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,106 @@ +//===-- 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; + + 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: + 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,217 @@ +//===-- 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/DbiStream.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; +} + +ArchSpec ObjectFilePDB::GetArchitecture() { + auto dbi_stream = m_session->getPDBFile().getPDBDbiStream(); + if (!dbi_stream) { + llvm::consumeError(dbi_stream.takeError()); + return ArchSpec(); + } + + PDB_Machine machine = dbi_stream->getMachineType(); + switch (machine) { + default: + break; + case PDB_Machine::Amd64: + case PDB_Machine::x86: + case PDB_Machine::PowerPC: + case PDB_Machine::PowerPCFP: + case PDB_Machine::Arm: + case PDB_Machine::ArmNT: + case PDB_Machine::Thumb: + case PDB_Machine::Arm64: + ArchSpec arch; + arch.SetArchitecture(eArchTypeCOFF, static_cast(machine), + LLDB_INVALID_CPUTYPE); + return arch; + } + return ArchSpec(); +} + +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 info_stream = pdb_file.getPDBInfoStream(); + if (!info_stream) { + llvm::consumeError(info_stream.takeError()); + return initial_count; + } + auto dbi_stream = pdb_file.getPDBDbiStream(); + if (!dbi_stream) { + llvm::consumeError(dbi_stream.takeError()); + return initial_count; + } + + lldb_private::UUID &uuid = module_spec.GetUUID(); + if (!uuid.IsValid()) + uuid = GetPDBUUID(*info_stream); + + ArchSpec &spec = module_spec.GetArchitecture(); + switch (dbi_stream->getMachineType()) { + case PDB_Machine::Amd64: + spec.SetTriple("x86_64-pc-windows"); + specs.Append(module_spec); + break; + case PDB_Machine::x86: + spec.SetTriple("i386-pc-windows"); + specs.Append(module_spec); + spec.SetTriple("i686-pc-windows"); + specs.Append(module_spec); + break; + case PDB_Machine::ArmNT: + spec.SetTriple("armv7-pc-windows"); + specs.Append(module_spec); + break; + case PDB_Machine::Arm64: + spec.SetTriple("aarch64-pc-windows"); + specs.Append(module_spec); + break; + default: + break; + } + + 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/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -142,7 +142,6 @@ // Cache coff binary. if (!objfile_up->CreateBinary()) return nullptr; - return objfile_up.release(); } @@ -852,7 +851,6 @@ if (m_sections_up) return; m_sections_up = std::make_unique(); - ModuleSP module_sp(GetModule()); if (module_sp) { std::lock_guard guard(module_sp->GetMutex()); 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); diff --git a/lldb/test/Shell/SymbolFile/NativePDB/load-pdb.cpp b/lldb/test/Shell/SymbolFile/NativePDB/load-pdb.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/NativePDB/load-pdb.cpp @@ -0,0 +1,23 @@ +// clang-format off +// REQUIRES: lld, x86 + +// Test that lldb load PDB file by command `target symbols add` + +// RUN: mkdir -p %t/executable +// RUN: rm -f %t/executable/foo.exe %t/executable/bar.pdb +// RUN: %clang_cl --target=x86_64-windows-msvc -Od -Z7 -c /Fo%t/executable/foo.obj -- %s +// RUN: lld-link -debug:full -nodefaultlib -entry:main %t/executable/foo.obj \ +// RUN: -out:%t/executable/foo.exe -pdb:%t/executable/foo.pdb +// Rename the PDB file so that the name is different from the name inside the executable (foo.exe). +// RUN: mv %t/executable/foo.pdb %t/executable/bar.pdb +// Create lldbinit +// RUN: echo -e "target symbols add %t/executable/bar.pdb\nquit" > %t/load-pdb.lldbinit +// RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t/executable/foo.exe -s \ +// RUN: %t/load-pdb.lldbinit | FileCheck %s + +int main(int argc, char** argv) { + return 0; +} + +// CHECK: (lldb) target symbols add {{.*}}bar.pdb +// CHECK: symbol file