diff --git a/lldb/source/Plugins/ObjectFile/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/CMakeLists.txt --- a/lldb/source/Plugins/ObjectFile/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectFile/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(Breakpad) add_subdirectory(ELF) add_subdirectory(Mach-O) +add_subdirectory(PDB) add_subdirectory(PECOFF) add_subdirectory(JIT) add_subdirectory(wasm) diff --git a/lldb/source/Plugins/ObjectFile/PDB/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/PDB/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/PDB/CMakeLists.txt @@ -0,0 +1,11 @@ +add_lldb_library(lldbPluginObjectFilePDB PLUGIN + ObjectFilePDB.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbUtility + LINK_COMPONENTS + Support + ) \ No newline at end of file diff --git a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.h b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.h @@ -0,0 +1,107 @@ +//===-- ObjectFilePDB.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_PLUGINS_OBJECTFILE_PDB_OBJECTFILEPDB_H +#define LLDB_PLUGINS_OBJECTFILE_PDB_OBJECTFILEPDB_H + +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +namespace lldb_private { +namespace pdb { + +class ObjectFilePDB : public ObjectFile { +public: + // Static Functions + static void Initialize(); + static void Terminate(); + + static ConstString GetPluginNameStatic(); + static const char *GetPluginDescriptionStatic() { + return "PDB object file reader."; + } + + static bool MagicBytesMatch(lldb::DataBufferSP &data_sp); + + static ObjectFile * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length); + + static ObjectFile *CreateMemoryInstance(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP &data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr); + + static size_t GetModuleSpecifications(const FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + ModuleSpecList &specs); + + // PluginInterface protocol + ConstString GetPluginName() override { return GetPluginNameStatic(); } + + uint32_t GetPluginVersion() override { return 1; } + + // LLVM RTTI support + static char ID; + bool isA(const void *ClassID) const override { + return ClassID == &ID || ObjectFile::isA(ClassID); + } + static bool classof(const ObjectFile *obj) { return obj->isA(&ID); } + + // ObjectFile Protocol. + uint32_t GetAddressByteSize() const override { return 8; } + + Address GetBaseAddress() override; + + lldb::ByteOrder GetByteOrder() const override { + return lldb::eByteOrderLittle; + } + + bool ParseHeader() override { return true; } + + bool IsExecutable() const override { return false; } + + Symtab *GetSymtab() override { return nullptr; } + + bool IsStripped() override { return false; } + + void CreateSections(SectionList &unified_section_list) override; + + void Dump(Stream *s) override {} + + ArchSpec GetArchitecture() override { return m_arch; } + + UUID GetUUID() override { return m_uuid; } + + uint32_t GetDependentModules(FileSpecList &files) override { return 0; } + + Type CalculateType() override { return eTypeDebugInfo; } + + Strata CalculateStrata() override { return eStrataUser; } + + ObjectFilePDB(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + +private: + ArchSpec m_arch; + UUID m_uuid; + std::unique_ptr m_session; + + bool CreateSession(); +}; + +} // namespace pdb +} // namespace lldb_private +#endif // LLDB_PLUGINS_OBJECTFILE_PDB_OBJECTFILEPDB_H diff --git a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp @@ -0,0 +1,159 @@ +//===-- ObjectFilePDB.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 "ObjectFilePDB.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Utility/StreamString.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Support/BinaryByteStream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::pdb; +using namespace llvm::pdb; +using namespace llvm::codeview; + +LLDB_PLUGIN_DEFINE(ObjectFilePDB) + +struct CVInfoPdb70 { + // 16-byte GUID + struct _Guid { + llvm::support::ulittle32_t Data1; + llvm::support::ulittle16_t Data2; + llvm::support::ulittle16_t Data3; + uint8_t Data4[8]; + } Guid; + + llvm::support::ulittle32_t Age; +}; + +static UUID GetPDBUUID(InfoStream &IS) { + // This part is similar with what has done in ObjectFilePECOFF. + using llvm::support::endian::read16be; + using llvm::support::endian::read32; + using llvm::support::endian::read32be; + + GUID guid = IS.getGuid(); + const uint8_t *guid_p = guid.Guid; + struct CVInfoPdb70 info; + info.Guid.Data1 = read32be(guid_p); + guid_p += 4; + info.Guid.Data2 = read16be(guid_p); + guid_p += 2; + info.Guid.Data3 = read16be(guid_p); + guid_p += 2; + memcpy(info.Guid.Data4, guid_p, 8); + + // Return 20-byte UUID if the Age is not zero + uint32_t age = IS.getAge(); + if (age) { + info.Age = read32(&age, llvm::support::big); + return UUID::fromOptionalData(&info, sizeof(info)); + } + // Otherwise return 16-byte GUID + return UUID::fromOptionalData(&info.Guid, sizeof(info.Guid)); +} + +static std::unique_ptr GetNativeSession(const FileSpec *file) { + std::unique_ptr session; + if (auto E = + loadDataForPDB(PDB_ReaderType::Native, file->GetPath(), session)) { + llvm::consumeError(std::move(E)); + return nullptr; + } + return std::unique_ptr( + static_cast(session.release())); +} + +char ObjectFilePDB::ID; + +void ObjectFilePDB::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + CreateMemoryInstance, GetModuleSpecifications); +} + +void ObjectFilePDB::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ConstString ObjectFilePDB::GetPluginNameStatic() { + static ConstString g_name("pdb"); + return g_name; +} + +bool ObjectFilePDB::CreateSession() { + m_session = GetNativeSession(&m_file); + return (bool)m_session; +} + +ObjectFile * +ObjectFilePDB::CreateInstance(const ModuleSP &module_sp, DataBufferSP &data_sp, + offset_t data_offset, const FileSpec *file, + offset_t file_offset, offset_t length) { + auto objfile_up = std::make_unique( + module_sp, data_sp, data_offset, file, file_offset, length); + if (!objfile_up->CreateSession()) + return nullptr; + return objfile_up.release(); +} + +ObjectFile *ObjectFilePDB::CreateMemoryInstance(const ModuleSP &module_sp, + DataBufferSP &data_sp, + const ProcessSP &process_sp, + addr_t header_addr) { + return nullptr; +} + +size_t ObjectFilePDB::GetModuleSpecifications( + const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset, + offset_t file_offset, offset_t length, ModuleSpecList &specs) { + const size_t initial_count = specs.GetSize(); + ModuleSpec module_spec(file); + + std::unique_ptr NS = GetNativeSession(&file); + if (!NS) + return initial_count; + + PDBFile &pdb_file = NS->getPDBFile(); + auto stream = pdb_file.getPDBInfoStream(); + if (!stream) + return initial_count; + lldb_private::UUID &uuid = module_spec.GetUUID(); + if (!uuid.IsValid()) { + uuid = GetPDBUUID(*stream); + } + + specs.Append(module_spec); + return specs.GetSize() - initial_count; +} + +ObjectFilePDB::ObjectFilePDB(const ModuleSP &module_sp, DataBufferSP &data_sp, + offset_t data_offset, const FileSpec *file, + offset_t offset, offset_t length) + : ObjectFile(module_sp, file, offset, length, data_sp, data_offset) {} + +Address ObjectFilePDB::GetBaseAddress() { + return Address(GetSectionList()->GetSectionAtIndex(0), 0); +} + +void ObjectFilePDB::CreateSections(SectionList &unified_section_list) { + if (m_sections_up) + return; + m_sections_up = std::make_unique(); + for (auto s : unified_section_list) + m_sections_up->AddSection(s); +} diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -42,9 +42,11 @@ #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDB.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" #include "llvm/Demangle/MicrosoftDemangle.h" #include "llvm/Object/COFF.h" @@ -113,10 +115,11 @@ using namespace llvm::object; auto expected_binary = createBinary(exe_path); - // If the file isn't a PE/COFF executable, fail. + // If the file isn't a PE/COFF executable, it might be pdb file. if (!expected_binary) { llvm::consumeError(expected_binary.takeError()); - return nullptr; + std::unique_ptr pdb = loadPDBFile(exe_path, allocator); + return pdb; } OwningBinary binary = std::move(*expected_binary);