Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
PDBReader.cpp
Go to the documentation of this file.
1#include "PDBReader.h"
2
3#include <fstream>
4
5#include <Logger/Logger.h>
6#include <Tools.h>
7
8#include "../Private/Helpers.h"
9#include "../Private/Offsets.h"
10
11namespace API
12{
13 template <typename T>
15 {
16 public:
17 ScopedDiaType() : _sym(nullptr)
18 {
19 }
20
22 {
23 }
24
26 {
27 if (_sym != nullptr)
28 _sym->Release();
29 }
30
31 T** ref() { return &_sym; }
32 T** operator&() { return ref(); }
33 T* operator->() { return _sym; }
34 operator T*() { return _sym; }
35 void Attach(T* sym) { _sym = sym; }
36
37 private:
38 T* _sym;
39 };
40
41 template <typename T>
42 using CComPtr = ScopedDiaType<T>;
43
44 void PdbReader::Read(const std::wstring& path, std::unordered_map<std::string, intptr_t>* offsets_dump,
45 std::unordered_map<std::string, BitField>* bitfields_dump, const std::unordered_set<std::string> filter_set)
46 {
47 offsets_dump_ = offsets_dump;
48 bitfields_dump_ = bitfields_dump;
49 filter_set_ = filter_set;
50
51 offsets_dump_->reserve(550000);
52 bitfields_dump_->reserve(11000);
53
54 std::ifstream f{path};
55 if (!f.good())
56 throw std::runtime_error("Failed to open pdb file");
57
58 IDiaDataSource* data_source;
59 IDiaSession* dia_session;
60 IDiaSymbol* symbol;
61
62 try
63 {
64 LoadDataFromPdb(path, &data_source, &dia_session, &symbol);
65 }
66 catch (const std::runtime_error&)
67 {
68 Log::GetLog()->error("Failed to load data from pdb file ");
69 throw;
70 }
71
72 Log::GetLog()->info("Dumping structures..");
73 DumpStructs(symbol);
74
75 Log::GetLog()->info("Dumping functions..");
76 DumpFunctions(symbol);
77
78 Log::GetLog()->info("Dumping globals..");
79 DumpGlobalVariables(symbol);
80
81 Cleanup(symbol, dia_session, data_source);
82
83 Log::GetLog()->info("Successfully read information from PDB\n");
84 }
85
86 void PdbReader::LoadDataFromPdb(const std::wstring& path, IDiaDataSource** dia_source, IDiaSession** session,
87 IDiaSymbol** symbol)
88 {
89 const std::string current_dir = Tools::GetCurrentDir();
90
91 const std::string lib_path = current_dir + "\\msdia140.dll";
92 const HMODULE h_module = LoadLibraryA(lib_path.c_str());
93 if (h_module == nullptr)
94 {
95 throw std::runtime_error("Failed to load msdia140.dll. Error code - " + std::to_string(GetLastError()));
96 }
97
98 const auto dll_get_class_object = reinterpret_cast<HRESULT(WINAPI*)(REFCLSID, REFIID, LPVOID)>(GetProcAddress(
99 h_module, "DllGetClassObject"));
100 if (dll_get_class_object == nullptr)
101 {
102 throw std::runtime_error("Can't find DllGetClassObject. Error code - " + std::to_string(GetLastError()));
103 }
104
105 IClassFactory* class_factory;
106 HRESULT hr = dll_get_class_object(__uuidof(DiaSource), IID_IClassFactory, &class_factory);
107 if (FAILED(hr))
108 {
109 throw std::runtime_error("DllGetClassObject has failed. Error code - " + std::to_string(GetLastError()));
110 }
111
112 hr = class_factory->CreateInstance(nullptr, __uuidof(IDiaDataSource), reinterpret_cast<void**>(dia_source));
113 if (FAILED(hr))
114 {
115 class_factory->Release();
116 throw std::runtime_error("CreateInstance has failed. Error code - " + std::to_string(GetLastError()));
117 }
118
119 hr = (*dia_source)->loadDataFromPdb(path.c_str());
120 if (FAILED(hr))
121 {
122 class_factory->Release();
123 throw std::runtime_error("loadDataFromPdb has failed. HRESULT - " + std::to_string(hr));
124 }
125
126 // Open a session for querying symbols
127
128 hr = (*dia_source)->openSession(session);
129 if (FAILED(hr))
130 {
131 class_factory->Release();
132 throw std::runtime_error("openSession has failed. HRESULT - " + std::to_string(hr));
133 }
134
135 // Retrieve a reference to the global scope
136
137 hr = (*session)->get_globalScope(symbol);
138 if (hr != S_OK)
139 {
140 class_factory->Release();
141 throw std::runtime_error("get_globalScope has failed. HRESULT - " + std::to_string(hr));
142 }
143
144 class_factory->Release();
145 }
146
147 void PdbReader::DumpStructs(IDiaSymbol* g_symbol)
148 {
149 IDiaSymbol* symbol = nullptr;
150
151 CComPtr<IDiaEnumSymbols> enum_symbols;
152 if (FAILED(g_symbol->findChildren(SymTagUDT, nullptr, nsNone, &enum_symbols)))
153 throw std::runtime_error("Failed to find symbols");
154
155 ULONG celt = 0;
156 while (SUCCEEDED(enum_symbols->Next(1, &symbol, &celt)) && celt == 1)
157 {
158 CComPtr<IDiaSymbol> sym(symbol);
159
160 const uint32_t sym_id = GetSymbolId(symbol);
161 if (visited_.find(sym_id) != visited_.end())
162 return;
163
164 visited_.insert(sym_id);
165
166 std::string str_name = GetSymbolNameString(sym);
167
168 if (FilterSymbols(str_name))
169 continue;
170
171 DumpType(sym, str_name, 0);
172 }
173 }
174
175 void PdbReader::DumpFunctions(IDiaSymbol* g_symbol)
176 {
177 IDiaSymbol* symbol;
178
179 CComPtr<IDiaEnumSymbols> enum_symbols;
180 if (FAILED(g_symbol->findChildren(SymTagFunction, nullptr, nsNone, &enum_symbols)))
181 throw std::runtime_error("Failed to find symbols");
182
183 ULONG celt = 0;
184 std::stringstream ss;
185 while (SUCCEEDED(enum_symbols->Next(1, &symbol, &celt)) && celt == 1)
186 {
187 CComPtr<IDiaSymbol> sym(symbol);
188
189 DWORD sym_tag_type;
190 if (sym->get_symTag(&sym_tag_type) != S_OK)
191 continue;
192
193 std::string str_name = GetSymbolNameString(sym);
194
195 if (FilterSymbols(str_name))
196 continue;
197
198 const uint32_t sym_id = GetSymbolId(sym);
199
200 if (visited_.find(sym_id) != visited_.end())
201 continue;
202
203 visited_.insert(sym_id);
204
205 if (str_name.empty())
206 continue;
207
208 DWORD offset;
209 if (sym->get_addressOffset(&offset) != S_OK)
210 continue;
211
212 ss.clear();
213 ss.str(std::string());
214
215 // Check if it's a member function
216 if (str_name.find(':') != std::string::npos)
217 {
218 ss << ReplaceString(str_name, "::", ".") << "(" << GetFunctionSymbolParams(sym) << ")";
219 (*offsets_dump_)[ss.str()] = offset;
220 //const std::string new_str = ReplaceString(str_name, "::", ".") + "(" + GetFunctionSymbolParams(sym) + ")";
221 //(*offsets_dump_)[new_str] = offset;
222 }
223 else
224 {
225 ss << "Global." << str_name << "(" << GetFunctionSymbolParams(sym) << ")";
226 (*offsets_dump_)[ss.str()] = offset;
227 //(*offsets_dump_)["Global." + str_name + "(" + GetFunctionSymbolParams(sym) + ")"] = offset;
228 }
229 }
230 }
231
232 void PdbReader::DumpGlobalVariables(IDiaSymbol* g_symbol)
233 {
234 IDiaSymbol* symbol;
235
236 CComPtr<IDiaEnumSymbols> enum_symbols;
237 if (FAILED(g_symbol->findChildren(SymTagData, nullptr, nsNone, &enum_symbols)))
238 throw std::runtime_error("Failed to find symbols");
239
240 ULONG celt = 0;
241 while (SUCCEEDED(enum_symbols->Next(1, &symbol, &celt)) && celt == 1)
242 {
243 CComPtr<IDiaSymbol> sym(symbol);
244
245 const uint32_t sym_id = GetSymbolId(symbol);
246 if (visited_.find(sym_id) != visited_.end())
247 return;
248
249 visited_.insert(sym_id);
250
251 std::string str_name = GetSymbolNameString(sym);
252 if (FilterSymbols(str_name))
253 continue;
254
255 DWORD sym_tag;
256 if (sym->get_symTag(&sym_tag) != S_OK)
257 continue;
258
259 DWORD offset;
260 if (sym->get_addressOffset(&offset) != S_OK)
261 continue;
262
263 (*offsets_dump_)["Global." + str_name] = offset;
264 }
265 }
266
267 void PdbReader::DumpType(IDiaSymbol* symbol, const std::string& structure, int indent) const
268 {
269 CComPtr<IDiaEnumSymbols> enum_children;
270 IDiaSymbol* symbol_child;
271 DWORD sym_tag;
272 ULONG celt = 0;
273
274 if (indent > 5)
275 return;
276
277 if (symbol->get_symTag(&sym_tag) != S_OK)
278 return;
279
280 switch (sym_tag)
281 {
282 case SymTagData:
283 DumpData(symbol, structure);
284 break;
285 case SymTagEnum:
286 case SymTagUDT:
287 if (SUCCEEDED(symbol->findChildren(SymTagNull, nullptr, nsNone, &enum_children)))
288 {
289 while (SUCCEEDED(enum_children->Next(1, &symbol_child, &celt)) && celt == 1)
290 {
291 CComPtr<IDiaSymbol> sym_child(symbol_child);
292
293 DumpType(sym_child, structure, indent + 2);
294 }
295 }
296 break;
297 default:
298 break;
299 }
300 }
301
302 void PdbReader::DumpData(IDiaSymbol* symbol, const std::string& structure) const
303 {
304 DWORD loc_type;
305 if (symbol->get_locationType(&loc_type) != S_OK)
306 return;
307
308 if (loc_type != LocIsThisRel && loc_type != LocIsBitField)
309 return;
310
311 CComPtr<IDiaSymbol> type;
312 if (symbol->get_type(&type) != S_OK)
313 return;
314
315 if (type == nullptr)
316 return;
317
318 LONG offset;
319 if (symbol->get_offset(&offset) != S_OK)
320 return;
321
322 std::string str_name = GetSymbolNameString(symbol);
323 if (str_name.empty())
324 return;
325
326 if (loc_type == LocIsBitField)
327 {
328 DWORD bit_position;
329 if (symbol->get_bitPosition(&bit_position) != S_OK)
330 return;
331
332 ULONGLONG num_bits;
333 if (symbol->get_length(&num_bits) != S_OK)
334 return;
335
336 ULONGLONG length;
337 if (type->get_length(&length) != S_OK)
338 return;
339
340 const BitField bit_field{static_cast<DWORD64>(offset), bit_position, num_bits, length};
341
342 (*bitfields_dump_)[structure + "." + str_name] = bit_field;
343 }
344 else if (loc_type == LocIsThisRel)
345 {
346 (*offsets_dump_)[structure + "." + str_name] = offset;
347 }
348 }
349
350 bool PdbReader::FilterSymbols(const std::string input)
351 {
352 if (input.empty())
353 return true;
354
355 for (const auto& filter : filter_set_)
356 {
357 if (input.starts_with(filter))
358 return true;
359 }
360
361 if (input.find('`') != std::string::npos)
362 return true;
363
364 return false;
365 }
366
367 std::string PdbReader::GetSymbolNameString(IDiaSymbol* symbol)
368 {
369 BSTR str = nullptr;
370
371 std::string name;
372
373 HRESULT hr = symbol->get_name(&str);
374 if (hr != S_OK)
375 return name;
376
377 if (str != nullptr)
378 {
379 name = Tools::Utf8Encode(str);
380 }
381
382 SysFreeString(str);
383
384 return name;
385 }
386
387 uint32_t PdbReader::GetSymbolId(IDiaSymbol* symbol)
388 {
389 DWORD id;
390 symbol->get_symIndexId(&id);
391
392 return id;
393 }
394
395 void PdbReader::Cleanup(IDiaSymbol* symbol, IDiaSession* session, IDiaDataSource* source)
396 {
397 if (symbol != nullptr)
398 symbol->Release();
399 if (session != nullptr)
400 session->Release();
401 if (source != nullptr)
402 source->Release();
403
404 CoUninitialize();
405 }
406
407 std::string PdbReader::GetFunctionSymbolParams(IDiaSymbol* pFunction)
408 {
409 std::string parameterTypes;
410 BSTR undecorated = nullptr;
411 if (SUCCEEDED(pFunction->get_undecoratedNameEx(0x20000, &undecorated)) // 0x20000 - Don't include __ptr64 in output (just on the func sig, but the params can still output it)
412 && undecorated != nullptr)
413 {
414 parameterTypes = Tools::Utf8Encode(undecorated);
415 const size_t start = parameterTypes.find('(');
416 const size_t end = parameterTypes.find(')');
417 if (start != std::string::npos && end != std::string::npos)
418 {
419 parameterTypes = parameterTypes.substr(start + 1, end - start - 1);
420 parameterTypes = ReplaceString(parameterTypes, "struct ", "");
421 parameterTypes = ReplaceString(parameterTypes, "class ", "");
422 parameterTypes = ReplaceString(parameterTypes, "enum ", "");
423 //parameterTypes = ReplaceString(parameterTypes, "& __ptr64", "*"); // pointers
424 parameterTypes = ReplaceString(parameterTypes, "const ", "");
425 parameterTypes = ReplaceString(parameterTypes, " ", "");
426 parameterTypes = ReplaceString(parameterTypes, "__ptr64", "");
427 if (parameterTypes == "void")
428 parameterTypes.clear();
429 }
430 }
431
432 return parameterTypes;
433 }
434
435
436} // namespace API
#define WINAPI
void DumpType(IDiaSymbol *, const std::string &, int) const
void Read(const std::wstring &path, std::unordered_map< std::string, intptr_t > *offsets_dump, std::unordered_map< std::string, BitField > *bitfields_dump, const std::unordered_set< std::string > filter_set)
Definition PDBReader.cpp:44
bool FilterSymbols(const std::string input)
void DumpFunctions(IDiaSymbol *)
void DumpData(IDiaSymbol *, const std::string &) const
void DumpGlobalVariables(IDiaSymbol *)
void DumpStructs(IDiaSymbol *)
void Attach(T *sym)
Definition PDBReader.cpp:35
Definition IBaseApi.h:9
Definition json.hpp:4518