JDevlieghere created this revision. JDevlieghere added a reviewer: jasonmolenda. Herald added a subscriber: mgorny. Herald added a project: All. JDevlieghere requested review of this revision.
https://reviews.llvm.org/D132433 Files: lldb/include/lldb/Symbol/ObjectFile.h lldb/source/Plugins/ObjectContainer/CMakeLists.txt lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/CMakeLists.txt lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/ObjectContainerMachOFileset.cpp lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/ObjectContainerMachOFileset.h lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
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; } Index: lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/ObjectContainerMachOFileset.h =================================================================== --- /dev/null +++ lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/ObjectContainerMachOFileset.h @@ -0,0 +1,78 @@ +//===-- 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() 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 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); + + bool ParseHeader() override; + + void Dump(lldb_private::Stream *s) const 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(); } + +private: + struct Entry { + Entry(uint64_t vmaddr, uint64_t fileoff, std::string id) + : vmaddr(vmaddr), fileoff(fileoff), id(id) {} + uint64_t vmaddr; + uint64_t fileoff; + std::string id; + }; + + static bool ParseHeader(lldb_private::DataExtractor &data, + std::vector<Entry> &entries); + + std::vector<Entry> m_entries; +}; + +} // 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,234 @@ +//===-- 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) {} + +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 {}; + + // We need the whole file in order to parse all the load commands. + if (data_sp->GetByteSize() < length) { + data_sp = ObjectFile::MapFileData(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + } + + auto container_up = std::make_unique<ObjectContainerMachOFileset>( + module_sp, data_sp, data_offset, file, file_offset, length); + if (!container_up->ParseHeader()) + return {}; + + return container_up.release(); +} + +ObjectContainerMachOFileset::~ObjectContainerMachOFileset() = default; + +bool ObjectContainerMachOFileset::ParseHeader() { + return ParseHeader(m_data, m_entries); +} + +bool ObjectContainerMachOFileset::ParseHeader(lldb_private::DataExtractor &data, + std::vector<Entry> &entries) { + llvm::MachO::mach_header header = {}; + + lldb::offset_t offset = 0; + size_t header_size = 0; + header.magic = data.GetU32(&offset); + + switch (header.magic) { + case MH_MAGIC: + data.SetByteOrder(endian::InlHostByteOrder()); + data.SetAddressByteSize(4); + header_size = sizeof(struct llvm::MachO::mach_header); + break; + case MH_MAGIC_64: + data.SetByteOrder(endian::InlHostByteOrder()); + data.SetAddressByteSize(8); + header_size = sizeof(struct llvm::MachO::mach_header_64); + break; + case MH_CIGAM: + data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig + ? eByteOrderLittle + : eByteOrderBig); + data.SetAddressByteSize(4); + header_size = sizeof(struct llvm::MachO::mach_header); + break; + case MH_CIGAM_64: + data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig + ? eByteOrderLittle + : eByteOrderBig); + data.SetAddressByteSize(8); + header_size = sizeof(struct llvm::MachO::mach_header_64); + break; + default: + return false; + } + + 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); + + assert(header.filetype == llvm::MachO::MH_FILESET && "Not a fileset?"); + assert(header.sizeofcmds <= data.GetByteSize() && + "Entire file must be mapped"); + + offset = header_size; + 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; +} + +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(); + + // We need the whole file in order to parse all the load commands. + if (data_sp->GetByteSize() < file_size) { + data_sp = ObjectFile::MapFileData(file, file_size, file_offset); + if (!data_sp) + return 0; + } + + DataExtractor data; + data.SetData(data_sp, data_offset, data_sp->GetByteSize()); + + if (MagicBytesMatch(data)) { + std::vector<Entry> entries; + if (ParseHeader(data, 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(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 {}; + + std::string object_name_str = object_name.GetCString(); + auto it = std::find_if(m_entries.begin(), m_entries.end(), + [object_name_str](const Entry &entry) { + return object_name_str == entry.id; + }); + if (it == m_entries.end()) + return {}; + + const Entry &entry = *it; + + 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); +} + +void ObjectContainerMachOFileset::Dump(Stream *s) const { + s->Printf("%p: ", static_cast<const void *>(this)); + s->Indent(); + + const size_t num_objects = GetNumObjects(); + s->Printf("ObjectContainerMachOFileset, num_objects = %zu", num_objects); + + for (size_t i = 0; i < num_objects; i++) { + s->Indent(); + s->Printf("object[%zu] = %s\n", i, m_entries[i].id.c_str()); + } + + s->IndentLess(); + s->EOL(); +} 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/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/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;
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits