1bdd1243dSDimitry Andric //===-- ObjectContainerMachOFileset.cpp -----------------------------------===// 2bdd1243dSDimitry Andric // 3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bdd1243dSDimitry Andric // 7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8bdd1243dSDimitry Andric 9bdd1243dSDimitry Andric #include "ObjectContainerMachOFileset.h" 10bdd1243dSDimitry Andric #include "lldb/Core/Module.h" 11bdd1243dSDimitry Andric #include "lldb/Core/ModuleSpec.h" 12bdd1243dSDimitry Andric #include "lldb/Core/PluginManager.h" 13bdd1243dSDimitry Andric #include "lldb/Symbol/ObjectFile.h" 14bdd1243dSDimitry Andric #include "lldb/Target/Target.h" 15bdd1243dSDimitry Andric #include "lldb/Utility/ArchSpec.h" 16bdd1243dSDimitry Andric #include "lldb/Utility/DataBuffer.h" 17bdd1243dSDimitry Andric #include "lldb/Utility/Stream.h" 18bdd1243dSDimitry Andric #include <optional> 19bdd1243dSDimitry Andric 20bdd1243dSDimitry Andric using namespace lldb; 21bdd1243dSDimitry Andric using namespace lldb_private; 22bdd1243dSDimitry Andric using namespace llvm::MachO; 23bdd1243dSDimitry Andric 24bdd1243dSDimitry Andric LLDB_PLUGIN_DEFINE(ObjectContainerMachOFileset) 25bdd1243dSDimitry Andric 26bdd1243dSDimitry Andric void ObjectContainerMachOFileset::Initialize() { 27bdd1243dSDimitry Andric PluginManager::RegisterPlugin(GetPluginNameStatic(), 28bdd1243dSDimitry Andric GetPluginDescriptionStatic(), CreateInstance, 29bdd1243dSDimitry Andric GetModuleSpecifications, CreateMemoryInstance); 30bdd1243dSDimitry Andric } 31bdd1243dSDimitry Andric 32bdd1243dSDimitry Andric void ObjectContainerMachOFileset::Terminate() { 33bdd1243dSDimitry Andric PluginManager::UnregisterPlugin(CreateInstance); 34bdd1243dSDimitry Andric } 35bdd1243dSDimitry Andric 36bdd1243dSDimitry Andric ObjectContainerMachOFileset::ObjectContainerMachOFileset( 37bdd1243dSDimitry Andric const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, 38bdd1243dSDimitry Andric lldb::offset_t data_offset, const lldb_private::FileSpec *file, 39bdd1243dSDimitry Andric lldb::offset_t offset, lldb::offset_t length) 40bdd1243dSDimitry Andric : ObjectContainer(module_sp, file, offset, length, data_sp, data_offset), 41bdd1243dSDimitry Andric m_memory_addr(LLDB_INVALID_ADDRESS) {} 42bdd1243dSDimitry Andric 43bdd1243dSDimitry Andric ObjectContainerMachOFileset::ObjectContainerMachOFileset( 44bdd1243dSDimitry Andric const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, 45bdd1243dSDimitry Andric const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) 46bdd1243dSDimitry Andric : ObjectContainer(module_sp, nullptr, 0, data_sp->GetByteSize(), data_sp, 47bdd1243dSDimitry Andric 0), 48bdd1243dSDimitry Andric m_process_wp(process_sp), m_memory_addr(header_addr) {} 49bdd1243dSDimitry Andric 50bdd1243dSDimitry Andric ObjectContainer *ObjectContainerMachOFileset::CreateInstance( 51bdd1243dSDimitry Andric const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, 52bdd1243dSDimitry Andric lldb::offset_t data_offset, const FileSpec *file, 53bdd1243dSDimitry Andric lldb::offset_t file_offset, lldb::offset_t length) { 54bdd1243dSDimitry Andric if (!data_sp) 55bdd1243dSDimitry Andric return {}; 56bdd1243dSDimitry Andric 57bdd1243dSDimitry Andric DataExtractor data; 58bdd1243dSDimitry Andric data.SetData(data_sp, data_offset, length); 59bdd1243dSDimitry Andric if (!MagicBytesMatch(data)) 60bdd1243dSDimitry Andric return {}; 61bdd1243dSDimitry Andric 62bdd1243dSDimitry Andric auto container_up = std::make_unique<ObjectContainerMachOFileset>( 63bdd1243dSDimitry Andric module_sp, data_sp, data_offset, file, file_offset, length); 64bdd1243dSDimitry Andric if (!container_up->ParseHeader()) 65bdd1243dSDimitry Andric return {}; 66bdd1243dSDimitry Andric 67bdd1243dSDimitry Andric return container_up.release(); 68bdd1243dSDimitry Andric } 69bdd1243dSDimitry Andric 70bdd1243dSDimitry Andric ObjectContainer *ObjectContainerMachOFileset::CreateMemoryInstance( 71bdd1243dSDimitry Andric const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, 72bdd1243dSDimitry Andric const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { 73bdd1243dSDimitry Andric if (!MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) 74bdd1243dSDimitry Andric return {}; 75bdd1243dSDimitry Andric 76bdd1243dSDimitry Andric auto container_up = std::make_unique<ObjectContainerMachOFileset>( 77bdd1243dSDimitry Andric module_sp, data_sp, process_sp, header_addr); 78bdd1243dSDimitry Andric if (!container_up->ParseHeader()) 79bdd1243dSDimitry Andric return {}; 80bdd1243dSDimitry Andric 81bdd1243dSDimitry Andric return container_up.release(); 82bdd1243dSDimitry Andric } 83bdd1243dSDimitry Andric 84bdd1243dSDimitry Andric ObjectContainerMachOFileset::~ObjectContainerMachOFileset() = default; 85bdd1243dSDimitry Andric 86bdd1243dSDimitry Andric static uint32_t MachHeaderSizeFromMagic(uint32_t magic) { 87bdd1243dSDimitry Andric switch (magic) { 88bdd1243dSDimitry Andric case MH_MAGIC: 89bdd1243dSDimitry Andric case MH_CIGAM: 90bdd1243dSDimitry Andric return sizeof(struct mach_header); 91bdd1243dSDimitry Andric case MH_MAGIC_64: 92bdd1243dSDimitry Andric case MH_CIGAM_64: 93bdd1243dSDimitry Andric return sizeof(struct mach_header_64); 94bdd1243dSDimitry Andric default: 95bdd1243dSDimitry Andric return 0; 96bdd1243dSDimitry Andric } 97bdd1243dSDimitry Andric } 98bdd1243dSDimitry Andric 99bdd1243dSDimitry Andric static std::optional<mach_header> ParseMachOHeader(DataExtractor &data) { 100bdd1243dSDimitry Andric lldb::offset_t offset = 0; 101bdd1243dSDimitry Andric mach_header header; 102bdd1243dSDimitry Andric header.magic = data.GetU32(&offset); 103bdd1243dSDimitry Andric switch (header.magic) { 104bdd1243dSDimitry Andric case MH_MAGIC: 105bdd1243dSDimitry Andric data.SetByteOrder(endian::InlHostByteOrder()); 106bdd1243dSDimitry Andric data.SetAddressByteSize(4); 107bdd1243dSDimitry Andric break; 108bdd1243dSDimitry Andric case MH_MAGIC_64: 109bdd1243dSDimitry Andric data.SetByteOrder(endian::InlHostByteOrder()); 110bdd1243dSDimitry Andric data.SetAddressByteSize(8); 111bdd1243dSDimitry Andric break; 112bdd1243dSDimitry Andric case MH_CIGAM: 113bdd1243dSDimitry Andric data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig 114bdd1243dSDimitry Andric ? eByteOrderLittle 115bdd1243dSDimitry Andric : eByteOrderBig); 116bdd1243dSDimitry Andric data.SetAddressByteSize(4); 117bdd1243dSDimitry Andric break; 118bdd1243dSDimitry Andric case MH_CIGAM_64: 119bdd1243dSDimitry Andric data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig 120bdd1243dSDimitry Andric ? eByteOrderLittle 121bdd1243dSDimitry Andric : eByteOrderBig); 122bdd1243dSDimitry Andric data.SetAddressByteSize(8); 123bdd1243dSDimitry Andric break; 124bdd1243dSDimitry Andric default: 125bdd1243dSDimitry Andric return {}; 126bdd1243dSDimitry Andric } 127bdd1243dSDimitry Andric 128bdd1243dSDimitry Andric header.cputype = data.GetU32(&offset); 129bdd1243dSDimitry Andric header.cpusubtype = data.GetU32(&offset); 130bdd1243dSDimitry Andric header.filetype = data.GetU32(&offset); 131bdd1243dSDimitry Andric header.ncmds = data.GetU32(&offset); 132bdd1243dSDimitry Andric header.sizeofcmds = data.GetU32(&offset); 133bdd1243dSDimitry Andric return header; 134bdd1243dSDimitry Andric } 135bdd1243dSDimitry Andric 136bdd1243dSDimitry Andric static bool 137bdd1243dSDimitry Andric ParseFileset(DataExtractor &data, mach_header header, 138bdd1243dSDimitry Andric std::vector<ObjectContainerMachOFileset::Entry> &entries, 139bdd1243dSDimitry Andric std::optional<lldb::addr_t> load_addr = std::nullopt) { 140bdd1243dSDimitry Andric lldb::offset_t offset = MachHeaderSizeFromMagic(header.magic); 141bdd1243dSDimitry Andric lldb::offset_t slide = 0; 142bdd1243dSDimitry Andric for (uint32_t i = 0; i < header.ncmds; ++i) { 143bdd1243dSDimitry Andric const lldb::offset_t load_cmd_offset = offset; 144bdd1243dSDimitry Andric load_command lc = {}; 145bdd1243dSDimitry Andric if (data.GetU32(&offset, &lc.cmd, 2) == nullptr) 146bdd1243dSDimitry Andric break; 147bdd1243dSDimitry Andric 148bdd1243dSDimitry Andric // If we know the load address we can compute the slide. 149bdd1243dSDimitry Andric if (load_addr) { 150bdd1243dSDimitry Andric if (lc.cmd == llvm::MachO::LC_SEGMENT_64) { 151bdd1243dSDimitry Andric segment_command_64 segment; 152bdd1243dSDimitry Andric data.CopyData(load_cmd_offset, sizeof(segment_command_64), &segment); 153bdd1243dSDimitry Andric if (llvm::StringRef(segment.segname) == "__TEXT") 154bdd1243dSDimitry Andric slide = *load_addr - segment.vmaddr; 155bdd1243dSDimitry Andric } 156bdd1243dSDimitry Andric } 157bdd1243dSDimitry Andric 158bdd1243dSDimitry Andric if (lc.cmd == LC_FILESET_ENTRY) { 159bdd1243dSDimitry Andric fileset_entry_command entry; 160bdd1243dSDimitry Andric data.CopyData(load_cmd_offset, sizeof(fileset_entry_command), &entry); 1615f757f3fSDimitry Andric lldb::offset_t entry_id_offset = load_cmd_offset + entry.entry_id.offset; 162*0fca6ea1SDimitry Andric if (const char *id = data.GetCStr(&entry_id_offset)) 163bdd1243dSDimitry Andric entries.emplace_back(entry.vmaddr + slide, entry.fileoff, 164bdd1243dSDimitry Andric std::string(id)); 165bdd1243dSDimitry Andric } 166bdd1243dSDimitry Andric 167bdd1243dSDimitry Andric offset = load_cmd_offset + lc.cmdsize; 168bdd1243dSDimitry Andric } 169bdd1243dSDimitry Andric 170bdd1243dSDimitry Andric return true; 171bdd1243dSDimitry Andric } 172bdd1243dSDimitry Andric 173bdd1243dSDimitry Andric bool ObjectContainerMachOFileset::ParseHeader( 174bdd1243dSDimitry Andric DataExtractor &data, const lldb_private::FileSpec &file, 175bdd1243dSDimitry Andric lldb::offset_t file_offset, std::vector<Entry> &entries) { 176bdd1243dSDimitry Andric std::optional<mach_header> header = ParseMachOHeader(data); 177bdd1243dSDimitry Andric 178bdd1243dSDimitry Andric if (!header) 179bdd1243dSDimitry Andric return false; 180bdd1243dSDimitry Andric 181bdd1243dSDimitry Andric const size_t header_size = MachHeaderSizeFromMagic(header->magic); 182bdd1243dSDimitry Andric const size_t header_and_lc_size = header_size + header->sizeofcmds; 183bdd1243dSDimitry Andric 184bdd1243dSDimitry Andric if (data.GetByteSize() < header_and_lc_size) { 185bdd1243dSDimitry Andric DataBufferSP data_sp = 186bdd1243dSDimitry Andric ObjectFile::MapFileData(file, header_and_lc_size, file_offset); 187bdd1243dSDimitry Andric data.SetData(data_sp); 188bdd1243dSDimitry Andric } 189bdd1243dSDimitry Andric 190bdd1243dSDimitry Andric return ParseFileset(data, *header, entries); 191bdd1243dSDimitry Andric } 192bdd1243dSDimitry Andric 193bdd1243dSDimitry Andric bool ObjectContainerMachOFileset::ParseHeader() { 194bdd1243dSDimitry Andric ModuleSP module_sp(GetModule()); 195bdd1243dSDimitry Andric if (!module_sp) 196bdd1243dSDimitry Andric return false; 197bdd1243dSDimitry Andric 198bdd1243dSDimitry Andric std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex()); 199bdd1243dSDimitry Andric 200bdd1243dSDimitry Andric std::optional<mach_header> header = ParseMachOHeader(m_data); 201bdd1243dSDimitry Andric if (!header) 202bdd1243dSDimitry Andric return false; 203bdd1243dSDimitry Andric 204bdd1243dSDimitry Andric const size_t header_size = MachHeaderSizeFromMagic(header->magic); 205bdd1243dSDimitry Andric const size_t header_and_lc_size = header_size + header->sizeofcmds; 206bdd1243dSDimitry Andric 207bdd1243dSDimitry Andric if (m_data.GetByteSize() < header_and_lc_size) { 208bdd1243dSDimitry Andric ProcessSP process_sp(m_process_wp.lock()); 209bdd1243dSDimitry Andric DataBufferSP data_sp = 210bdd1243dSDimitry Andric process_sp 211bdd1243dSDimitry Andric ? ObjectFile::ReadMemory(process_sp, m_memory_addr, 212bdd1243dSDimitry Andric header_and_lc_size) 213bdd1243dSDimitry Andric : ObjectFile::MapFileData(m_file, header_and_lc_size, m_offset); 214bdd1243dSDimitry Andric m_data.SetData(data_sp); 215bdd1243dSDimitry Andric } 216bdd1243dSDimitry Andric 217bdd1243dSDimitry Andric return ParseFileset(m_data, *header, m_entries, m_memory_addr); 218bdd1243dSDimitry Andric } 219bdd1243dSDimitry Andric 220bdd1243dSDimitry Andric size_t ObjectContainerMachOFileset::GetModuleSpecifications( 221bdd1243dSDimitry Andric const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, 222bdd1243dSDimitry Andric lldb::offset_t data_offset, lldb::offset_t file_offset, 223bdd1243dSDimitry Andric lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) { 224bdd1243dSDimitry Andric const size_t initial_count = specs.GetSize(); 225bdd1243dSDimitry Andric 226bdd1243dSDimitry Andric DataExtractor data; 227bdd1243dSDimitry Andric data.SetData(data_sp, data_offset, data_sp->GetByteSize()); 228bdd1243dSDimitry Andric 229bdd1243dSDimitry Andric if (MagicBytesMatch(data)) { 230bdd1243dSDimitry Andric std::vector<Entry> entries; 231bdd1243dSDimitry Andric if (ParseHeader(data, file, file_offset, entries)) { 232bdd1243dSDimitry Andric for (const Entry &entry : entries) { 233bdd1243dSDimitry Andric const lldb::offset_t entry_offset = entry.fileoff + file_offset; 234bdd1243dSDimitry Andric if (ObjectFile::GetModuleSpecifications( 235bdd1243dSDimitry Andric file, entry_offset, file_size - entry_offset, specs)) { 236bdd1243dSDimitry Andric ModuleSpec &spec = specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1); 237bdd1243dSDimitry Andric spec.GetObjectName() = ConstString(entry.id); 238bdd1243dSDimitry Andric } 239bdd1243dSDimitry Andric } 240bdd1243dSDimitry Andric } 241bdd1243dSDimitry Andric } 242bdd1243dSDimitry Andric return specs.GetSize() - initial_count; 243bdd1243dSDimitry Andric } 244bdd1243dSDimitry Andric 245bdd1243dSDimitry Andric bool ObjectContainerMachOFileset::MagicBytesMatch(DataBufferSP data_sp, 246bdd1243dSDimitry Andric lldb::addr_t data_offset, 247bdd1243dSDimitry Andric lldb::addr_t data_length) { 248bdd1243dSDimitry Andric DataExtractor data; 249bdd1243dSDimitry Andric data.SetData(data_sp, data_offset, data_length); 250bdd1243dSDimitry Andric return MagicBytesMatch(data); 251bdd1243dSDimitry Andric } 252bdd1243dSDimitry Andric 253bdd1243dSDimitry Andric bool ObjectContainerMachOFileset::MagicBytesMatch(const DataExtractor &data) { 254bdd1243dSDimitry Andric lldb::offset_t offset = 0; 255bdd1243dSDimitry Andric uint32_t magic = data.GetU32(&offset); 256bdd1243dSDimitry Andric switch (magic) { 257bdd1243dSDimitry Andric case MH_MAGIC: 258bdd1243dSDimitry Andric case MH_CIGAM: 259bdd1243dSDimitry Andric case MH_MAGIC_64: 260bdd1243dSDimitry Andric case MH_CIGAM_64: 261bdd1243dSDimitry Andric break; 262bdd1243dSDimitry Andric default: 263bdd1243dSDimitry Andric return false; 264bdd1243dSDimitry Andric } 265bdd1243dSDimitry Andric offset += 4; // cputype 266bdd1243dSDimitry Andric offset += 4; // cpusubtype 267bdd1243dSDimitry Andric uint32_t filetype = data.GetU32(&offset); 268bdd1243dSDimitry Andric return filetype == MH_FILESET; 269bdd1243dSDimitry Andric } 270bdd1243dSDimitry Andric 271bdd1243dSDimitry Andric ObjectFileSP 272bdd1243dSDimitry Andric ObjectContainerMachOFileset::GetObjectFile(const lldb_private::FileSpec *file) { 273bdd1243dSDimitry Andric ModuleSP module_sp(GetModule()); 274bdd1243dSDimitry Andric if (!module_sp) 275bdd1243dSDimitry Andric return {}; 276bdd1243dSDimitry Andric 277bdd1243dSDimitry Andric ConstString object_name = module_sp->GetObjectName(); 278bdd1243dSDimitry Andric if (!object_name) 279bdd1243dSDimitry Andric return {}; 280bdd1243dSDimitry Andric 281bdd1243dSDimitry Andric Entry *entry = FindEntry(object_name.GetCString()); 282bdd1243dSDimitry Andric if (!entry) 283bdd1243dSDimitry Andric return {}; 284bdd1243dSDimitry Andric 285bdd1243dSDimitry Andric DataBufferSP data_sp; 286bdd1243dSDimitry Andric lldb::offset_t data_offset = 0; 287bdd1243dSDimitry Andric return ObjectFile::FindPlugin(module_sp, file, m_offset + entry->fileoff, 288bdd1243dSDimitry Andric m_data.GetByteSize() - entry->fileoff, data_sp, 289bdd1243dSDimitry Andric data_offset); 290bdd1243dSDimitry Andric } 291bdd1243dSDimitry Andric 292bdd1243dSDimitry Andric ObjectContainerMachOFileset::Entry * 293bdd1243dSDimitry Andric ObjectContainerMachOFileset::FindEntry(llvm::StringRef id) { 294bdd1243dSDimitry Andric for (Entry &entry : m_entries) { 295bdd1243dSDimitry Andric if (entry.id == id) 296bdd1243dSDimitry Andric return &entry; 297bdd1243dSDimitry Andric } 298bdd1243dSDimitry Andric return nullptr; 299bdd1243dSDimitry Andric } 300