Index: lldb/include/lldb/Symbol/ObjectContainer.h =================================================================== --- lldb/include/lldb/Symbol/ObjectContainer.h +++ lldb/include/lldb/Symbol/ObjectContainer.h @@ -36,7 +36,7 @@ /// more than one architecture or object. ObjectContainer(const lldb::ModuleSP &module_sp, const FileSpec *file, lldb::offset_t file_offset, lldb::offset_t length, - lldb::DataBufferSP &data_sp, lldb::offset_t data_offset) + lldb::DataBufferSP data_sp, lldb::offset_t data_offset) : ModuleChild(module_sp), m_file(), // This file can be different than the module's file spec m_offset(file_offset), m_length(length) { Index: lldb/include/lldb/Symbol/ObjectFile.h =================================================================== --- lldb/include/lldb/Symbol/ObjectFile.h +++ lldb/include/lldb/Symbol/ObjectFile.h @@ -723,6 +723,8 @@ /// file when storing cached data. uint32_t GetCacheHash(); + static lldb::DataBufferSP MapFileData(const FileSpec &file, uint64_t Size, + uint64_t Offset); protected: // Member variables. @@ -764,9 +766,6 @@ /// The number of bytes to read when going through the plugins. static size_t g_initial_bytes_to_read; - static lldb::DataBufferSP MapFileData(const FileSpec &file, uint64_t Size, - uint64_t Offset); - private: ObjectFile(const ObjectFile &) = delete; const ObjectFile &operator=(const ObjectFile &) = delete; Index: lldb/source/Plugins/ObjectContainer/CMakeLists.txt =================================================================== --- lldb/source/Plugins/ObjectContainer/CMakeLists.txt +++ lldb/source/Plugins/ObjectContainer/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(BSD-Archive) add_subdirectory(Universal-Mach-O) +add_subdirectory(Mach-O-Fileset) Index: lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/CMakeLists.txt =================================================================== --- /dev/null +++ lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/CMakeLists.txt @@ -0,0 +1,10 @@ +add_lldb_library(lldbPluginObjectContainerMachOFileset PLUGIN + ObjectContainerMachOFileset.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbTarget + lldbUtility + ) Index: lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/ObjectContainerMachOFileset.h =================================================================== --- /dev/null +++ lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/ObjectContainerMachOFileset.h @@ -0,0 +1,95 @@ +//===-- ObjectContainerMachOFileset.h ---------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_MACH_O_FILESET_OBJECTCONTAINERMADCHOFILESET_H +#define LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_MACH_O_FILESET_OBJECTCONTAINERMADCHOFILESET_H + +#include "lldb/Host/SafeMachO.h" +#include "lldb/Symbol/ObjectContainer.h" +#include "lldb/Utility/FileSpec.h" + +namespace lldb_private { + +class ObjectContainerMachOFileset : public lldb_private::ObjectContainer { +public: + ObjectContainerMachOFileset(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + ObjectContainerMachOFileset(const lldb::ModuleSP &module_sp, + lldb::WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr); + + ~ObjectContainerMachOFileset() override; + + static void Initialize(); + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "mach-o-fileset"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "Mach-O Fileset container reader."; + } + + static lldb_private::ObjectContainer * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + static lldb_private::ObjectContainer *CreateMemoryInstance( + const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + + static size_t GetModuleSpecifications(const lldb_private::FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool MagicBytesMatch(const lldb_private::DataExtractor &data); + static bool MagicBytesMatch(lldb::DataBufferSP data_sp, + lldb::addr_t data_offset, + lldb::addr_t data_length); + + bool ParseHeader() override; + + size_t GetNumObjects() const override { return m_entries.size(); } + + lldb::ObjectFileSP GetObjectFile(const lldb_private::FileSpec *file) override; + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + struct Entry { + Entry(uint64_t vmaddr, uint64_t fileoff, std::string id) + : vmaddr(vmaddr), fileoff(fileoff), id(id) {} + uint64_t vmaddr; + uint64_t fileoff; + uint64_t slide; + std::string id; + }; + + Entry *FindEntry(llvm::StringRef id); + +private: + static bool ParseHeader(lldb_private::DataExtractor &data, + const lldb_private::FileSpec &file, + lldb::offset_t file_offset, + std::vector &entries); + + std::vector m_entries; + lldb::ProcessWP m_process_wp; + const lldb::addr_t m_memory_addr; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_MACH_O_FILESET_OBJECTCONTAINERMADCHOFILESET_H Index: lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/ObjectContainerMachOFileset.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/ObjectContainerMachOFileset.cpp @@ -0,0 +1,286 @@ +//===-- ObjectContainerMachOFileset.cpp -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ObjectContainerMachOFileset.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::MachO; + +LLDB_PLUGIN_DEFINE(ObjectContainerMachOFileset) + +void ObjectContainerMachOFileset::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + GetModuleSpecifications); +} + +void ObjectContainerMachOFileset::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ObjectContainerMachOFileset::ObjectContainerMachOFileset( + const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length) + : ObjectContainer(module_sp, file, offset, length, data_sp, data_offset), + m_memory_addr(LLDB_INVALID_ADDRESS) {} + +ObjectContainerMachOFileset::ObjectContainerMachOFileset( + const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) + : ObjectContainer(module_sp, nullptr, 0, 0, data_sp, 0), + m_memory_addr(header_addr) {} + +ObjectContainer *ObjectContainerMachOFileset::CreateInstance( + const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, + lldb::offset_t data_offset, const FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length) { + if (!data_sp) + return {}; + + DataExtractor data; + data.SetData(data_sp, data_offset, length); + if (!MagicBytesMatch(data)) + return {}; + + auto container_up = std::make_unique( + module_sp, data_sp, data_offset, file, file_offset, length); + if (!container_up->ParseHeader()) + return {}; + + return container_up.release(); +} + +ObjectContainer *ObjectContainerMachOFileset::CreateMemoryInstance( + const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { + if (!MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) + return {}; + + auto container_up = std::make_unique( + module_sp, data_sp, process_sp, header_addr); + if (!container_up->ParseHeader()) + return {}; + + return container_up.release(); +} + +ObjectContainerMachOFileset::~ObjectContainerMachOFileset() = default; + +static uint32_t MachHeaderSizeFromMagic(uint32_t magic) { + switch (magic) { + case MH_MAGIC: + case MH_CIGAM: + return sizeof(struct llvm::MachO::mach_header); + case MH_MAGIC_64: + case MH_CIGAM_64: + return sizeof(struct llvm::MachO::mach_header_64); + default: + return 0; + } +} + +static llvm::Optional +ParseMachOHeader(DataExtractor &data) { + lldb::offset_t offset = 0; + llvm::MachO::mach_header header; + header.magic = data.GetU32(&offset); + switch (header.magic) { + case MH_MAGIC: + data.SetByteOrder(endian::InlHostByteOrder()); + data.SetAddressByteSize(4); + break; + case MH_MAGIC_64: + data.SetByteOrder(endian::InlHostByteOrder()); + data.SetAddressByteSize(8); + break; + case MH_CIGAM: + data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig + ? eByteOrderLittle + : eByteOrderBig); + data.SetAddressByteSize(4); + break; + case MH_CIGAM_64: + data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig + ? eByteOrderLittle + : eByteOrderBig); + data.SetAddressByteSize(8); + break; + default: + return {}; + } + + header.cputype = data.GetU32(&offset); + header.cpusubtype = data.GetU32(&offset); + header.filetype = data.GetU32(&offset); + header.ncmds = data.GetU32(&offset); + header.sizeofcmds = data.GetU32(&offset); + return header; +} + +static bool +ParseFileset(DataExtractor &data, llvm::MachO::mach_header header, + std::vector &entries) { + lldb::offset_t offset = MachHeaderSizeFromMagic(header.magic); + ; + for (uint32_t i = 0; i < header.ncmds; ++i) { + const lldb::offset_t load_cmd_offset = offset; + llvm::MachO::load_command lc = {}; + if (data.GetU32(&offset, &lc.cmd, 2) == nullptr) + break; + + if (lc.cmd == LC_FILESET_ENTRY) { + llvm::MachO::fileset_entry_command entry; + data.CopyData(load_cmd_offset, sizeof(llvm::MachO::fileset_entry_command), + &entry); + lldb::offset_t entry_id_offset = load_cmd_offset + entry.entry_id; + const char *id = data.GetCStr(&entry_id_offset); + entries.emplace_back(entry.vmaddr, entry.fileoff, std::string(id)); + } + offset = load_cmd_offset + lc.cmdsize; + } + + return true; +} + +bool ObjectContainerMachOFileset::ParseHeader( + DataExtractor &data, const lldb_private::FileSpec &file, + lldb::offset_t file_offset, std::vector &entries) { + llvm::Optional header = ParseMachOHeader(data); + + if (!header) + return false; + + const size_t header_size = MachHeaderSizeFromMagic(header->magic); + const size_t header_and_lc_size = header_size + header->sizeofcmds; + + if (data.GetByteSize() < header_and_lc_size) { + DataBufferSP data_sp = + ObjectFile::MapFileData(file, header_and_lc_size, file_offset); + data.SetData(data_sp, file_offset, header_and_lc_size); + } + + return ParseFileset(data, *header, entries); +} + +bool ObjectContainerMachOFileset::ParseHeader() { + ModuleSP module_sp(GetModule()); + if (!module_sp) + return false; + + std::lock_guard guard(module_sp->GetMutex()); + + llvm::Optional header = ParseMachOHeader(m_data); + if (!header) + return false; + + const size_t header_size = MachHeaderSizeFromMagic(header->magic); + const size_t header_and_lc_size = header_size + header->sizeofcmds; + + if (m_data.GetByteSize() < header_and_lc_size) { + ProcessSP process_sp(m_process_wp.lock()); + DataBufferSP data_sp = + process_sp + ? ObjectFile::ReadMemory(process_sp, m_memory_addr, + header_and_lc_size) + : ObjectFile::MapFileData(m_file, header_and_lc_size, m_offset); + m_data.SetData(data_sp, m_offset, header_and_lc_size); + } + + return ParseFileset(m_data, *header, m_entries); +} + +size_t ObjectContainerMachOFileset::GetModuleSpecifications( + const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, lldb::offset_t file_offset, + lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) { + const size_t initial_count = specs.GetSize(); + + DataExtractor data; + data.SetData(data_sp, data_offset, data_sp->GetByteSize()); + + if (MagicBytesMatch(data)) { + std::vector entries; + if (ParseHeader(data, file, file_offset, entries)) { + for (const Entry &entry : entries) { + const lldb::offset_t entry_offset = entry.fileoff + file_offset; + if (ObjectFile::GetModuleSpecifications( + file, entry_offset, file_size - entry_offset, specs)) { + ModuleSpec &spec = specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1); + spec.GetObjectName() = ConstString(entry.id); + } + } + } + } + return specs.GetSize() - initial_count; +} + +bool ObjectContainerMachOFileset::MagicBytesMatch(DataBufferSP data_sp, + lldb::addr_t data_offset, + lldb::addr_t data_length) { + DataExtractor data; + data.SetData(data_sp, data_offset, data_length); + return MagicBytesMatch(data); +} + +bool ObjectContainerMachOFileset::MagicBytesMatch(const DataExtractor &data) { + lldb::offset_t offset = 0; + uint32_t magic = data.GetU32(&offset); + switch (magic) { + case MH_MAGIC: + case MH_CIGAM: + case MH_MAGIC_64: + case MH_CIGAM_64: + break; + default: + return false; + } + offset += 4; // cputype + offset += 4; // cpusubtype + uint32_t filetype = data.GetU32(&offset); + return filetype == llvm::MachO::MH_FILESET; +} + +ObjectFileSP +ObjectContainerMachOFileset::GetObjectFile(const lldb_private::FileSpec *file) { + ModuleSP module_sp(GetModule()); + if (!module_sp) + return {}; + + ConstString object_name = module_sp->GetObjectName(); + if (!object_name) + return {}; + + Entry *entry = FindEntry(object_name.GetCString()); + if (!entry) + return {}; + + DataBufferSP data_sp; + lldb::offset_t data_offset = 0; + return ObjectFile::FindPlugin(module_sp, file, m_offset + entry->fileoff, + m_data.GetByteSize() - entry->fileoff, data_sp, + data_offset); +} + +ObjectContainerMachOFileset::Entry * +ObjectContainerMachOFileset::FindEntry(llvm::StringRef id) { + for (Entry &entry : m_entries) { + if (entry.id == id) + return &entry; + } + return nullptr; +} Index: lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp =================================================================== --- lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -941,6 +941,17 @@ data.SetData(data_sp, data_offset, data_length); lldb::offset_t offset = 0; uint32_t magic = data.GetU32(&offset); + + offset += 4; // cputype + offset += 4; // cpusubtype + uint32_t filetype = data.GetU32(&offset); + + // A fileset has a Mach-O header but is not an + // individual file and must be handled via an + // ObjectContainer plugin. + if (filetype == llvm::MachO::MH_FILESET) + return false; + return MachHeaderSizeFromMagic(magic) != 0; }