1 //===-- ObjectFilePDB.cpp -------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "ObjectFilePDB.h"
10 #include "lldb/Core/Module.h"
11 #include "lldb/Core/ModuleSpec.h"
12 #include "lldb/Core/PluginManager.h"
13 #include "lldb/Core/Section.h"
14 #include "lldb/Utility/StreamString.h"
15 #include "llvm/BinaryFormat/Magic.h"
16 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
17 #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
18 #include "llvm/DebugInfo/PDB/Native/NativeSession.h"
19 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
20 #include "llvm/DebugInfo/PDB/PDB.h"
21 #include "llvm/Support/BinaryByteStream.h"
22 
23 using namespace lldb;
24 using namespace lldb_private;
25 using namespace llvm::pdb;
26 using namespace llvm::codeview;
27 
28 LLDB_PLUGIN_DEFINE(ObjectFilePDB)
29 
30 struct CVInfoPdb70 {
31   // 16-byte GUID
32   struct _Guid {
33     llvm::support::ulittle32_t Data1;
34     llvm::support::ulittle16_t Data2;
35     llvm::support::ulittle16_t Data3;
36     uint8_t Data4[8];
37   } Guid;
38 
39   llvm::support::ulittle32_t Age;
40 };
41 
42 static UUID GetPDBUUID(InfoStream &IS) {
43   // This part is similar with what has done in ObjectFilePECOFF.
44   using llvm::support::endian::read16be;
45   using llvm::support::endian::read32;
46   using llvm::support::endian::read32be;
47 
48   GUID guid = IS.getGuid();
49   const uint8_t *guid_p = guid.Guid;
50   struct CVInfoPdb70 info;
51   info.Guid.Data1 = read32be(guid_p);
52   guid_p += 4;
53   info.Guid.Data2 = read16be(guid_p);
54   guid_p += 2;
55   info.Guid.Data3 = read16be(guid_p);
56   guid_p += 2;
57   memcpy(info.Guid.Data4, guid_p, 8);
58 
59   // Return 20-byte UUID if the Age is not zero
60   uint32_t age = IS.getAge();
61   if (age) {
62     info.Age = read32(&age, llvm::support::big);
63     return UUID::fromOptionalData(&info, sizeof(info));
64   }
65   // Otherwise return 16-byte GUID
66   return UUID::fromOptionalData(&info.Guid, sizeof(info.Guid));
67 }
68 
69 char ObjectFilePDB::ID;
70 
71 void ObjectFilePDB::Initialize() {
72   PluginManager::RegisterPlugin(GetPluginNameStatic(),
73                                 GetPluginDescriptionStatic(), CreateInstance,
74                                 CreateMemoryInstance, GetModuleSpecifications);
75 }
76 
77 void ObjectFilePDB::Terminate() {
78   PluginManager::UnregisterPlugin(CreateInstance);
79 }
80 
81 ConstString ObjectFilePDB::GetPluginNameStatic() {
82   static ConstString g_name("pdb");
83   return g_name;
84 }
85 
86 ArchSpec ObjectFilePDB::GetArchitecture() {
87   auto dbi_stream = m_file_up->getPDBDbiStream();
88   if (!dbi_stream) {
89     llvm::consumeError(dbi_stream.takeError());
90     return ArchSpec();
91   }
92 
93   PDB_Machine machine = dbi_stream->getMachineType();
94   switch (machine) {
95   default:
96     break;
97   case PDB_Machine::Amd64:
98   case PDB_Machine::x86:
99   case PDB_Machine::PowerPC:
100   case PDB_Machine::PowerPCFP:
101   case PDB_Machine::Arm:
102   case PDB_Machine::ArmNT:
103   case PDB_Machine::Thumb:
104   case PDB_Machine::Arm64:
105     ArchSpec arch;
106     arch.SetArchitecture(eArchTypeCOFF, static_cast<int>(machine),
107                          LLDB_INVALID_CPUTYPE);
108     return arch;
109   }
110   return ArchSpec();
111 }
112 
113 bool ObjectFilePDB::initPDBFile() {
114   m_file_up = loadPDBFile(m_file.GetPath(), m_allocator);
115   if (!m_file_up)
116     return false;
117   auto info_stream = m_file_up->getPDBInfoStream();
118   if (!info_stream) {
119     llvm::consumeError(info_stream.takeError());
120     return false;
121   }
122   m_uuid = GetPDBUUID(*info_stream);
123   return true;
124 }
125 
126 ObjectFile *
127 ObjectFilePDB::CreateInstance(const ModuleSP &module_sp, DataBufferSP &data_sp,
128                               offset_t data_offset, const FileSpec *file,
129                               offset_t file_offset, offset_t length) {
130   auto objfile_up = std::make_unique<ObjectFilePDB>(
131       module_sp, data_sp, data_offset, file, file_offset, length);
132   if (!objfile_up->initPDBFile())
133     return nullptr;
134   return objfile_up.release();
135 }
136 
137 ObjectFile *ObjectFilePDB::CreateMemoryInstance(const ModuleSP &module_sp,
138                                                 DataBufferSP &data_sp,
139                                                 const ProcessSP &process_sp,
140                                                 addr_t header_addr) {
141   return nullptr;
142 }
143 
144 size_t ObjectFilePDB::GetModuleSpecifications(
145     const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset,
146     offset_t file_offset, offset_t length, ModuleSpecList &specs) {
147   const size_t initial_count = specs.GetSize();
148   ModuleSpec module_spec(file);
149   llvm::BumpPtrAllocator allocator;
150   std::unique_ptr<PDBFile> pdb_file = loadPDBFile(file.GetPath(), allocator);
151   if (!pdb_file)
152     return initial_count;
153 
154   auto info_stream = pdb_file->getPDBInfoStream();
155   if (!info_stream) {
156     llvm::consumeError(info_stream.takeError());
157     return initial_count;
158   }
159   auto dbi_stream = pdb_file->getPDBDbiStream();
160   if (!dbi_stream) {
161     llvm::consumeError(dbi_stream.takeError());
162     return initial_count;
163   }
164 
165   lldb_private::UUID &uuid = module_spec.GetUUID();
166   uuid = GetPDBUUID(*info_stream);
167 
168   ArchSpec &module_arch = module_spec.GetArchitecture();
169   switch (dbi_stream->getMachineType()) {
170   case PDB_Machine::Amd64:
171     module_arch.SetTriple("x86_64-pc-windows");
172     specs.Append(module_spec);
173     break;
174   case PDB_Machine::x86:
175     module_arch.SetTriple("i386-pc-windows");
176     specs.Append(module_spec);
177     module_arch.SetTriple("i686-pc-windows");
178     specs.Append(module_spec);
179     break;
180   case PDB_Machine::ArmNT:
181     module_arch.SetTriple("armv7-pc-windows");
182     specs.Append(module_spec);
183     break;
184   case PDB_Machine::Arm64:
185     module_arch.SetTriple("aarch64-pc-windows");
186     specs.Append(module_spec);
187     break;
188   default:
189     break;
190   }
191 
192   return specs.GetSize() - initial_count;
193 }
194 
195 ObjectFilePDB::ObjectFilePDB(const ModuleSP &module_sp, DataBufferSP &data_sp,
196                              offset_t data_offset, const FileSpec *file,
197                              offset_t offset, offset_t length)
198     : ObjectFile(module_sp, file, offset, length, data_sp, data_offset) {}
199 
200 std::unique_ptr<PDBFile>
201 ObjectFilePDB::loadPDBFile(std::string PdbPath,
202                            llvm::BumpPtrAllocator &Allocator) {
203   llvm::file_magic magic;
204   auto ec = llvm::identify_magic(PdbPath, magic);
205   if (ec || magic != llvm::file_magic::pdb)
206     return nullptr;
207   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ErrorOrBuffer =
208       llvm::MemoryBuffer::getFile(PdbPath, /*FileSize=*/-1,
209                                   /*RequiresNullTerminator=*/false);
210   if (!ErrorOrBuffer)
211     return nullptr;
212   std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(*ErrorOrBuffer);
213 
214   llvm::StringRef Path = Buffer->getBufferIdentifier();
215   auto Stream = std::make_unique<llvm::MemoryBufferByteStream>(
216       std::move(Buffer), llvm::support::little);
217 
218   auto File = std::make_unique<PDBFile>(Path, std::move(Stream), Allocator);
219   if (auto EC = File->parseFileHeaders()) {
220     llvm::consumeError(std::move(EC));
221     return nullptr;
222   }
223   if (auto EC = File->parseStreamData()) {
224     llvm::consumeError(std::move(EC));
225     return nullptr;
226   }
227 
228   return File;
229 }
230