1//===- DIASession.cpp - DIA implementation of IPDBSession -------*- C++ -*-===//
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#include "llvm/DebugInfo/PDB/DIA/DIASession.h"
9#include "llvm/ADT/STLExtras.h"
10#include "llvm/DebugInfo/PDB/DIA/DIAEnumDebugStreams.h"
11#include "llvm/DebugInfo/PDB/DIA/DIAEnumFrameData.h"
12#include "llvm/DebugInfo/PDB/DIA/DIAEnumInjectedSources.h"
13#include "llvm/DebugInfo/PDB/DIA/DIAEnumLineNumbers.h"
14#include "llvm/DebugInfo/PDB/DIA/DIAEnumSectionContribs.h"
15#include "llvm/DebugInfo/PDB/DIA/DIAEnumSourceFiles.h"
16#include "llvm/DebugInfo/PDB/DIA/DIAEnumTables.h"
17#include "llvm/DebugInfo/PDB/DIA/DIAError.h"
18#include "llvm/DebugInfo/PDB/DIA/DIARawSymbol.h"
19#include "llvm/DebugInfo/PDB/DIA/DIASourceFile.h"
20#include "llvm/DebugInfo/PDB/DIA/DIASupport.h"
21#include "llvm/DebugInfo/PDB/GenericError.h"
22#include "llvm/DebugInfo/PDB/PDB.h"
23#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h"
24#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
25#include "llvm/Support/ConvertUTF.h"
26#include "llvm/Support/Format.h"
27#include "llvm/Support/FormatVariadic.h"
28#include "llvm/Support/raw_ostream.h"
29
30using namespace llvm;
31using namespace llvm::pdb;
32
33template <typename... Ts>
34static Error ErrorFromHResult(HRESULT Result, const char *Str, Ts &&... Args) {
35  SmallString<64> MessageStorage;
36  StringRef Context;
37  if (sizeof...(Args) > 0) {
38    MessageStorage = formatv(Str, std::forward<Ts>(Args)...).str();
39    Context = MessageStorage;
40  } else
41    Context = Str;
42
43  switch (Result) {
44  case E_PDB_NOT_FOUND:
45    return errorCodeToError(std::error_code(ENOENT, std::generic_category()));
46  case E_PDB_FORMAT:
47    return make_error<DIAError>(dia_error_code::invalid_file_format, Context);
48  case E_INVALIDARG:
49    return make_error<DIAError>(dia_error_code::invalid_parameter, Context);
50  case E_UNEXPECTED:
51    return make_error<DIAError>(dia_error_code::already_loaded, Context);
52  case E_PDB_INVALID_SIG:
53  case E_PDB_INVALID_AGE:
54    return make_error<DIAError>(dia_error_code::debug_info_mismatch, Context);
55  default: {
56    std::string S;
57    raw_string_ostream OS(S);
58    OS << "HRESULT: " << format_hex(static_cast<DWORD>(Result), 10, true)
59       << ": " << Context;
60    return make_error<DIAError>(dia_error_code::unspecified, OS.str());
61  }
62  }
63}
64
65static Error LoadDIA(CComPtr<IDiaDataSource> &DiaDataSource) {
66  if (SUCCEEDED(CoCreateInstance(CLSID_DiaSource, nullptr, CLSCTX_INPROC_SERVER,
67                                 IID_IDiaDataSource,
68                                 reinterpret_cast<LPVOID *>(&DiaDataSource))))
69    return Error::success();
70
71// If the CoCreateInstance call above failed, msdia*.dll is not registered.
72// Try loading the DLL corresponding to the #included DIA SDK.
73#if !defined(_MSC_VER)
74  return llvm::make_error<PDBError>(pdb_error_code::dia_failed_loading);
75#else
76  const wchar_t *msdia_dll = L"msdia140.dll";
77  HRESULT HR;
78  if (FAILED(HR = NoRegCoCreate(msdia_dll, CLSID_DiaSource, IID_IDiaDataSource,
79                                reinterpret_cast<LPVOID *>(&DiaDataSource))))
80    return ErrorFromHResult(HR, "Calling NoRegCoCreate");
81  return Error::success();
82#endif
83}
84
85DIASession::DIASession(CComPtr<IDiaSession> DiaSession) : Session(DiaSession) {}
86
87Error DIASession::createFromPdb(StringRef Path,
88                                std::unique_ptr<IPDBSession> &Session) {
89  CComPtr<IDiaDataSource> DiaDataSource;
90  CComPtr<IDiaSession> DiaSession;
91
92  // We assume that CoInitializeEx has already been called by the executable.
93  if (auto E = LoadDIA(DiaDataSource))
94    return E;
95
96  llvm::SmallVector<UTF16, 128> Path16;
97  if (!llvm::convertUTF8ToUTF16String(Path, Path16))
98    return make_error<PDBError>(pdb_error_code::invalid_utf8_path, Path);
99
100  const wchar_t *Path16Str = reinterpret_cast<const wchar_t *>(Path16.data());
101  HRESULT HR;
102  if (FAILED(HR = DiaDataSource->loadDataFromPdb(Path16Str))) {
103    return ErrorFromHResult(HR, "Calling loadDataFromPdb {0}", Path);
104  }
105
106  if (FAILED(HR = DiaDataSource->openSession(&DiaSession)))
107    return ErrorFromHResult(HR, "Calling openSession");
108
109  Session.reset(new DIASession(DiaSession));
110  return Error::success();
111}
112
113Error DIASession::createFromExe(StringRef Path,
114                                std::unique_ptr<IPDBSession> &Session) {
115  CComPtr<IDiaDataSource> DiaDataSource;
116  CComPtr<IDiaSession> DiaSession;
117
118  // We assume that CoInitializeEx has already been called by the executable.
119  if (auto EC = LoadDIA(DiaDataSource))
120    return EC;
121
122  llvm::SmallVector<UTF16, 128> Path16;
123  if (!llvm::convertUTF8ToUTF16String(Path, Path16))
124    return make_error<PDBError>(pdb_error_code::invalid_utf8_path, Path);
125
126  const wchar_t *Path16Str = reinterpret_cast<const wchar_t *>(Path16.data());
127  HRESULT HR;
128  if (FAILED(HR = DiaDataSource->loadDataForExe(Path16Str, nullptr, nullptr)))
129    return ErrorFromHResult(HR, "Calling loadDataForExe");
130
131  if (FAILED(HR = DiaDataSource->openSession(&DiaSession)))
132    return ErrorFromHResult(HR, "Calling openSession");
133
134  Session.reset(new DIASession(DiaSession));
135  return Error::success();
136}
137
138uint64_t DIASession::getLoadAddress() const {
139  uint64_t LoadAddress;
140  bool success = (S_OK == Session->get_loadAddress(&LoadAddress));
141  return (success) ? LoadAddress : 0;
142}
143
144bool DIASession::setLoadAddress(uint64_t Address) {
145  return (S_OK == Session->put_loadAddress(Address));
146}
147
148std::unique_ptr<PDBSymbolExe> DIASession::getGlobalScope() {
149  CComPtr<IDiaSymbol> GlobalScope;
150  if (S_OK != Session->get_globalScope(&GlobalScope))
151    return nullptr;
152
153  auto RawSymbol = std::make_unique<DIARawSymbol>(*this, GlobalScope);
154  auto PdbSymbol(PDBSymbol::create(*this, std::move(RawSymbol)));
155  std::unique_ptr<PDBSymbolExe> ExeSymbol(
156      static_cast<PDBSymbolExe *>(PdbSymbol.release()));
157  return ExeSymbol;
158}
159
160bool DIASession::addressForVA(uint64_t VA, uint32_t &Section,
161                              uint32_t &Offset) const {
162  DWORD ArgSection, ArgOffset = 0;
163  if (S_OK == Session->addressForVA(VA, &ArgSection, &ArgOffset)) {
164    Section = static_cast<uint32_t>(ArgSection);
165    Offset = static_cast<uint32_t>(ArgOffset);
166    return true;
167  }
168  return false;
169}
170
171bool DIASession::addressForRVA(uint32_t RVA, uint32_t &Section,
172                               uint32_t &Offset) const {
173  DWORD ArgSection, ArgOffset = 0;
174  if (S_OK == Session->addressForRVA(RVA, &ArgSection, &ArgOffset)) {
175    Section = static_cast<uint32_t>(ArgSection);
176    Offset = static_cast<uint32_t>(ArgOffset);
177    return true;
178  }
179  return false;
180}
181
182std::unique_ptr<PDBSymbol>
183DIASession::getSymbolById(SymIndexId SymbolId) const {
184  CComPtr<IDiaSymbol> LocatedSymbol;
185  if (S_OK != Session->symbolById(SymbolId, &LocatedSymbol))
186    return nullptr;
187
188  auto RawSymbol = std::make_unique<DIARawSymbol>(*this, LocatedSymbol);
189  return PDBSymbol::create(*this, std::move(RawSymbol));
190}
191
192std::unique_ptr<PDBSymbol>
193DIASession::findSymbolByAddress(uint64_t Address, PDB_SymType Type) const {
194  enum SymTagEnum EnumVal = static_cast<enum SymTagEnum>(Type);
195
196  CComPtr<IDiaSymbol> Symbol;
197  if (S_OK != Session->findSymbolByVA(Address, EnumVal, &Symbol)) {
198    ULONGLONG LoadAddr = 0;
199    if (S_OK != Session->get_loadAddress(&LoadAddr))
200      return nullptr;
201    DWORD RVA = static_cast<DWORD>(Address - LoadAddr);
202    if (S_OK != Session->findSymbolByRVA(RVA, EnumVal, &Symbol))
203      return nullptr;
204  }
205  auto RawSymbol = std::make_unique<DIARawSymbol>(*this, Symbol);
206  return PDBSymbol::create(*this, std::move(RawSymbol));
207}
208
209std::unique_ptr<PDBSymbol> DIASession::findSymbolByRVA(uint32_t RVA,
210                                                       PDB_SymType Type) const {
211  enum SymTagEnum EnumVal = static_cast<enum SymTagEnum>(Type);
212
213  CComPtr<IDiaSymbol> Symbol;
214  if (S_OK != Session->findSymbolByRVA(RVA, EnumVal, &Symbol))
215    return nullptr;
216
217  auto RawSymbol = std::make_unique<DIARawSymbol>(*this, Symbol);
218  return PDBSymbol::create(*this, std::move(RawSymbol));
219}
220
221std::unique_ptr<PDBSymbol>
222DIASession::findSymbolBySectOffset(uint32_t Sect, uint32_t Offset,
223                                   PDB_SymType Type) const {
224  enum SymTagEnum EnumVal = static_cast<enum SymTagEnum>(Type);
225
226  CComPtr<IDiaSymbol> Symbol;
227  if (S_OK != Session->findSymbolByAddr(Sect, Offset, EnumVal, &Symbol))
228    return nullptr;
229
230  auto RawSymbol = std::make_unique<DIARawSymbol>(*this, Symbol);
231  return PDBSymbol::create(*this, std::move(RawSymbol));
232}
233
234std::unique_ptr<IPDBEnumLineNumbers>
235DIASession::findLineNumbers(const PDBSymbolCompiland &Compiland,
236                            const IPDBSourceFile &File) const {
237  const DIARawSymbol &RawCompiland =
238      static_cast<const DIARawSymbol &>(Compiland.getRawSymbol());
239  const DIASourceFile &RawFile = static_cast<const DIASourceFile &>(File);
240
241  CComPtr<IDiaEnumLineNumbers> LineNumbers;
242  if (S_OK != Session->findLines(RawCompiland.getDiaSymbol(),
243                                 RawFile.getDiaFile(), &LineNumbers))
244    return nullptr;
245
246  return std::make_unique<DIAEnumLineNumbers>(LineNumbers);
247}
248
249std::unique_ptr<IPDBEnumLineNumbers>
250DIASession::findLineNumbersByAddress(uint64_t Address, uint32_t Length) const {
251  CComPtr<IDiaEnumLineNumbers> LineNumbers;
252  if (S_OK != Session->findLinesByVA(Address, Length, &LineNumbers)) {
253    ULONGLONG LoadAddr = 0;
254    if (S_OK != Session->get_loadAddress(&LoadAddr))
255      return nullptr;
256    DWORD RVA = static_cast<DWORD>(Address - LoadAddr);
257    if (S_OK != Session->findLinesByRVA(RVA, Length, &LineNumbers))
258      return nullptr;
259  }
260  return std::make_unique<DIAEnumLineNumbers>(LineNumbers);
261}
262
263std::unique_ptr<IPDBEnumLineNumbers>
264DIASession::findLineNumbersByRVA(uint32_t RVA, uint32_t Length) const {
265  CComPtr<IDiaEnumLineNumbers> LineNumbers;
266  if (S_OK != Session->findLinesByRVA(RVA, Length, &LineNumbers))
267    return nullptr;
268
269  return std::make_unique<DIAEnumLineNumbers>(LineNumbers);
270}
271
272std::unique_ptr<IPDBEnumLineNumbers>
273DIASession::findLineNumbersBySectOffset(uint32_t Section, uint32_t Offset,
274                                        uint32_t Length) const {
275  CComPtr<IDiaEnumLineNumbers> LineNumbers;
276  if (S_OK != Session->findLinesByAddr(Section, Offset, Length, &LineNumbers))
277    return nullptr;
278
279  return std::make_unique<DIAEnumLineNumbers>(LineNumbers);
280}
281
282std::unique_ptr<IPDBEnumSourceFiles>
283DIASession::findSourceFiles(const PDBSymbolCompiland *Compiland,
284                            llvm::StringRef Pattern,
285                            PDB_NameSearchFlags Flags) const {
286  IDiaSymbol *DiaCompiland = nullptr;
287  CComBSTR Utf16Pattern;
288  if (!Pattern.empty())
289    Utf16Pattern = CComBSTR(Pattern.data());
290
291  if (Compiland)
292    DiaCompiland = static_cast<const DIARawSymbol &>(Compiland->getRawSymbol())
293                       .getDiaSymbol();
294
295  Flags = static_cast<PDB_NameSearchFlags>(
296      Flags | PDB_NameSearchFlags::NS_FileNameExtMatch);
297  CComPtr<IDiaEnumSourceFiles> SourceFiles;
298  if (S_OK !=
299      Session->findFile(DiaCompiland, Utf16Pattern.m_str, Flags, &SourceFiles))
300    return nullptr;
301  return std::make_unique<DIAEnumSourceFiles>(*this, SourceFiles);
302}
303
304std::unique_ptr<IPDBSourceFile>
305DIASession::findOneSourceFile(const PDBSymbolCompiland *Compiland,
306                              llvm::StringRef Pattern,
307                              PDB_NameSearchFlags Flags) const {
308  auto SourceFiles = findSourceFiles(Compiland, Pattern, Flags);
309  if (!SourceFiles || SourceFiles->getChildCount() == 0)
310    return nullptr;
311  return SourceFiles->getNext();
312}
313
314std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>>
315DIASession::findCompilandsForSourceFile(llvm::StringRef Pattern,
316                                        PDB_NameSearchFlags Flags) const {
317  auto File = findOneSourceFile(nullptr, Pattern, Flags);
318  if (!File)
319    return nullptr;
320  return File->getCompilands();
321}
322
323std::unique_ptr<PDBSymbolCompiland>
324DIASession::findOneCompilandForSourceFile(llvm::StringRef Pattern,
325                                          PDB_NameSearchFlags Flags) const {
326  auto Compilands = findCompilandsForSourceFile(Pattern, Flags);
327  if (!Compilands || Compilands->getChildCount() == 0)
328    return nullptr;
329  return Compilands->getNext();
330}
331
332std::unique_ptr<IPDBEnumSourceFiles> DIASession::getAllSourceFiles() const {
333  CComPtr<IDiaEnumSourceFiles> Files;
334  if (S_OK != Session->findFile(nullptr, nullptr, nsNone, &Files))
335    return nullptr;
336
337  return std::make_unique<DIAEnumSourceFiles>(*this, Files);
338}
339
340std::unique_ptr<IPDBEnumSourceFiles> DIASession::getSourceFilesForCompiland(
341    const PDBSymbolCompiland &Compiland) const {
342  CComPtr<IDiaEnumSourceFiles> Files;
343
344  const DIARawSymbol &RawSymbol =
345      static_cast<const DIARawSymbol &>(Compiland.getRawSymbol());
346  if (S_OK !=
347      Session->findFile(RawSymbol.getDiaSymbol(), nullptr, nsNone, &Files))
348    return nullptr;
349
350  return std::make_unique<DIAEnumSourceFiles>(*this, Files);
351}
352
353std::unique_ptr<IPDBSourceFile>
354DIASession::getSourceFileById(uint32_t FileId) const {
355  CComPtr<IDiaSourceFile> LocatedFile;
356  if (S_OK != Session->findFileById(FileId, &LocatedFile))
357    return nullptr;
358
359  return std::make_unique<DIASourceFile>(*this, LocatedFile);
360}
361
362std::unique_ptr<IPDBEnumDataStreams> DIASession::getDebugStreams() const {
363  CComPtr<IDiaEnumDebugStreams> DiaEnumerator;
364  if (S_OK != Session->getEnumDebugStreams(&DiaEnumerator))
365    return nullptr;
366
367  return std::make_unique<DIAEnumDebugStreams>(DiaEnumerator);
368}
369
370std::unique_ptr<IPDBEnumTables> DIASession::getEnumTables() const {
371  CComPtr<IDiaEnumTables> DiaEnumerator;
372  if (S_OK != Session->getEnumTables(&DiaEnumerator))
373    return nullptr;
374
375  return std::make_unique<DIAEnumTables>(DiaEnumerator);
376}
377
378template <class T> static CComPtr<T> getTableEnumerator(IDiaSession &Session) {
379  CComPtr<T> Enumerator;
380  CComPtr<IDiaEnumTables> ET;
381  CComPtr<IDiaTable> Table;
382  ULONG Count = 0;
383
384  if (Session.getEnumTables(&ET) != S_OK)
385    return nullptr;
386
387  while (ET->Next(1, &Table, &Count) == S_OK && Count == 1) {
388    // There is only one table that matches the given iid
389    if (S_OK == Table->QueryInterface(__uuidof(T), (void **)&Enumerator))
390      break;
391    Table.Release();
392  }
393  return Enumerator;
394}
395std::unique_ptr<IPDBEnumInjectedSources>
396DIASession::getInjectedSources() const {
397  CComPtr<IDiaEnumInjectedSources> Files =
398      getTableEnumerator<IDiaEnumInjectedSources>(*Session);
399  if (!Files)
400    return nullptr;
401
402  return std::make_unique<DIAEnumInjectedSources>(Files);
403}
404
405std::unique_ptr<IPDBEnumSectionContribs>
406DIASession::getSectionContribs() const {
407  CComPtr<IDiaEnumSectionContribs> Sections =
408      getTableEnumerator<IDiaEnumSectionContribs>(*Session);
409  if (!Sections)
410    return nullptr;
411
412  return std::make_unique<DIAEnumSectionContribs>(*this, Sections);
413}
414
415std::unique_ptr<IPDBEnumFrameData>
416DIASession::getFrameData() const {
417  CComPtr<IDiaEnumFrameData> FD =
418      getTableEnumerator<IDiaEnumFrameData>(*Session);
419  if (!FD)
420    return nullptr;
421
422  return std::make_unique<DIAEnumFrameData>(FD);
423}
424