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
23using namespace lldb;
24using namespace lldb_private;
25using namespace llvm::pdb;
26using namespace llvm::codeview;
27
28LLDB_PLUGIN_DEFINE(ObjectFilePDB)
29
30static UUID GetPDBUUID(InfoStream &IS, DbiStream &DS) {
31  UUID::CvRecordPdb70 debug_info;
32  memcpy(&debug_info.Uuid, IS.getGuid().Guid, sizeof(debug_info.Uuid));
33  debug_info.Age = DS.getAge();
34  return UUID(debug_info);
35}
36
37char ObjectFilePDB::ID;
38
39void ObjectFilePDB::Initialize() {
40  PluginManager::RegisterPlugin(GetPluginNameStatic(),
41                                GetPluginDescriptionStatic(), CreateInstance,
42                                CreateMemoryInstance, GetModuleSpecifications);
43}
44
45void ObjectFilePDB::Terminate() {
46  PluginManager::UnregisterPlugin(CreateInstance);
47}
48
49ArchSpec ObjectFilePDB::GetArchitecture() {
50  auto dbi_stream = m_file_up->getPDBDbiStream();
51  if (!dbi_stream) {
52    llvm::consumeError(dbi_stream.takeError());
53    return ArchSpec();
54  }
55
56  PDB_Machine machine = dbi_stream->getMachineType();
57  switch (machine) {
58  default:
59    break;
60  case PDB_Machine::Amd64:
61  case PDB_Machine::x86:
62  case PDB_Machine::PowerPC:
63  case PDB_Machine::PowerPCFP:
64  case PDB_Machine::Arm:
65  case PDB_Machine::ArmNT:
66  case PDB_Machine::Thumb:
67  case PDB_Machine::Arm64:
68    ArchSpec arch;
69    arch.SetArchitecture(eArchTypeCOFF, static_cast<int>(machine),
70                         LLDB_INVALID_CPUTYPE);
71    return arch;
72  }
73  return ArchSpec();
74}
75
76bool ObjectFilePDB::initPDBFile() {
77  m_file_up = loadPDBFile(m_file.GetPath(), m_allocator);
78  if (!m_file_up)
79    return false;
80  auto info_stream = m_file_up->getPDBInfoStream();
81  if (!info_stream) {
82    llvm::consumeError(info_stream.takeError());
83    return false;
84  }
85  auto dbi_stream = m_file_up->getPDBDbiStream();
86  if (!dbi_stream) {
87    llvm::consumeError(dbi_stream.takeError());
88    return false;
89  }
90  m_uuid = GetPDBUUID(*info_stream, *dbi_stream);
91  return true;
92}
93
94ObjectFile *
95ObjectFilePDB::CreateInstance(const ModuleSP &module_sp, DataBufferSP data_sp,
96                              offset_t data_offset, const FileSpec *file,
97                              offset_t file_offset, offset_t length) {
98  auto objfile_up = std::make_unique<ObjectFilePDB>(
99      module_sp, data_sp, data_offset, file, file_offset, length);
100  if (!objfile_up->initPDBFile())
101    return nullptr;
102  return objfile_up.release();
103}
104
105ObjectFile *ObjectFilePDB::CreateMemoryInstance(const ModuleSP &module_sp,
106                                                WritableDataBufferSP data_sp,
107                                                const ProcessSP &process_sp,
108                                                addr_t header_addr) {
109  return nullptr;
110}
111
112size_t ObjectFilePDB::GetModuleSpecifications(
113    const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset,
114    offset_t file_offset, offset_t length, ModuleSpecList &specs) {
115  const size_t initial_count = specs.GetSize();
116  ModuleSpec module_spec(file);
117  llvm::BumpPtrAllocator allocator;
118  std::unique_ptr<PDBFile> pdb_file = loadPDBFile(file.GetPath(), allocator);
119  if (!pdb_file)
120    return initial_count;
121
122  auto info_stream = pdb_file->getPDBInfoStream();
123  if (!info_stream) {
124    llvm::consumeError(info_stream.takeError());
125    return initial_count;
126  }
127  auto dbi_stream = pdb_file->getPDBDbiStream();
128  if (!dbi_stream) {
129    llvm::consumeError(dbi_stream.takeError());
130    return initial_count;
131  }
132
133  lldb_private::UUID &uuid = module_spec.GetUUID();
134  uuid = GetPDBUUID(*info_stream, *dbi_stream);
135
136  ArchSpec &module_arch = module_spec.GetArchitecture();
137  switch (dbi_stream->getMachineType()) {
138  case PDB_Machine::Amd64:
139    module_arch.SetTriple("x86_64-pc-windows");
140    specs.Append(module_spec);
141    break;
142  case PDB_Machine::x86:
143    module_arch.SetTriple("i386-pc-windows");
144    specs.Append(module_spec);
145    break;
146  case PDB_Machine::ArmNT:
147    module_arch.SetTriple("armv7-pc-windows");
148    specs.Append(module_spec);
149    break;
150  case PDB_Machine::Arm64:
151    module_arch.SetTriple("aarch64-pc-windows");
152    specs.Append(module_spec);
153    break;
154  default:
155    break;
156  }
157
158  return specs.GetSize() - initial_count;
159}
160
161ObjectFilePDB::ObjectFilePDB(const ModuleSP &module_sp, DataBufferSP &data_sp,
162                             offset_t data_offset, const FileSpec *file,
163                             offset_t offset, offset_t length)
164    : ObjectFile(module_sp, file, offset, length, data_sp, data_offset) {}
165
166std::unique_ptr<PDBFile>
167ObjectFilePDB::loadPDBFile(std::string PdbPath,
168                           llvm::BumpPtrAllocator &Allocator) {
169  llvm::file_magic magic;
170  auto ec = llvm::identify_magic(PdbPath, magic);
171  if (ec || magic != llvm::file_magic::pdb)
172    return nullptr;
173  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ErrorOrBuffer =
174      llvm::MemoryBuffer::getFile(PdbPath, /*IsText=*/false,
175                                  /*RequiresNullTerminator=*/false);
176  if (!ErrorOrBuffer)
177    return nullptr;
178  std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(*ErrorOrBuffer);
179
180  llvm::StringRef Path = Buffer->getBufferIdentifier();
181  auto Stream = std::make_unique<llvm::MemoryBufferByteStream>(
182      std::move(Buffer), llvm::endianness::little);
183
184  auto File = std::make_unique<PDBFile>(Path, std::move(Stream), Allocator);
185  if (auto EC = File->parseFileHeaders()) {
186    llvm::consumeError(std::move(EC));
187    return nullptr;
188  }
189  if (auto EC = File->parseStreamData()) {
190    llvm::consumeError(std::move(EC));
191    return nullptr;
192  }
193
194  return File;
195}
196