Ark Server API (ASE) - 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)
46 {
47 offsets_dump_ = offsets_dump;
48 bitfields_dump_ = bitfields_dump;
49
50 std::ifstream f{path};
51 if (!f.good())
52 throw std::runtime_error("Failed to open pdb file");
53
54 IDiaDataSource* data_source;
55 IDiaSession* dia_session;
56 IDiaSymbol* symbol;
57
58 try
59 {
60 LoadDataFromPdb(path, &data_source, &dia_session, &symbol);
61 }
62 catch (const std::runtime_error&)
63 {
64 Log::GetLog()->error("Failed to load data from pdb file ");
65 throw;
66 }
67
68 Log::GetLog()->info("Dumping structures..");
69 DumpStructs(symbol);
70
71 Log::GetLog()->info("Dumping functions..");
72 DumpFunctions(symbol);
73
74 Log::GetLog()->info("Dumping globals..");
75 DumpGlobalVariables(symbol);
76
77 Cleanup(symbol, dia_session, data_source);
78
79 Log::GetLog()->info("Successfully read information from PDB\n");
80 }
81
82 void PdbReader::LoadDataFromPdb(const std::wstring& path, IDiaDataSource** dia_source, IDiaSession** session,
83 IDiaSymbol** symbol)
84 {
85 const std::string current_dir = Tools::GetCurrentDir();
86
87 const std::string lib_path = current_dir + "\\msdia140.dll";
88 const HMODULE h_module = LoadLibraryA(lib_path.c_str());
89 if (h_module == nullptr)
90 {
91 throw std::runtime_error("Failed to load msdia140.dll. Error code - " + std::to_string(GetLastError()));
92 }
93
94 const auto dll_get_class_object = reinterpret_cast<HRESULT(WINAPI*)(REFCLSID, REFIID, LPVOID)>(GetProcAddress(
95 h_module, "DllGetClassObject"));
96 if (dll_get_class_object == nullptr)
97 {
98 throw std::runtime_error("Can't find DllGetClassObject. Error code - " + std::to_string(GetLastError()));
99 }
100
101 IClassFactory* class_factory;
102 HRESULT hr = dll_get_class_object(__uuidof(DiaSource), IID_IClassFactory, &class_factory);
103 if (FAILED(hr))
104 {
105 throw std::runtime_error("DllGetClassObject has failed. Error code - " + std::to_string(GetLastError()));
106 }
107
108 hr = class_factory->CreateInstance(nullptr, __uuidof(IDiaDataSource), reinterpret_cast<void**>(dia_source));
109 if (FAILED(hr))
110 {
111 class_factory->Release();
112 throw std::runtime_error("CreateInstance has failed. Error code - " + std::to_string(GetLastError()));
113 }
114
115 hr = (*dia_source)->loadDataFromPdb(path.c_str());
116 if (FAILED(hr))
117 {
118 class_factory->Release();
119 throw std::runtime_error("loadDataFromPdb has failed. HRESULT - " + std::to_string(hr));
120 }
121
122 // Open a session for querying symbols
123
124 hr = (*dia_source)->openSession(session);
125 if (FAILED(hr))
126 {
127 class_factory->Release();
128 throw std::runtime_error("openSession has failed. HRESULT - " + std::to_string(hr));
129 }
130
131 // Retrieve a reference to the global scope
132
133 hr = (*session)->get_globalScope(symbol);
134 if (hr != S_OK)
135 {
136 class_factory->Release();
137 throw std::runtime_error("get_globalScope has failed. HRESULT - " + std::to_string(hr));
138 }
139
140 class_factory->Release();
141 }
142
143 void PdbReader::DumpStructs(IDiaSymbol* g_symbol)
144 {
145 IDiaSymbol* symbol = nullptr;
146
147 CComPtr<IDiaEnumSymbols> enum_symbols;
148 if (FAILED(g_symbol->findChildren(SymTagUDT, nullptr, nsNone, &enum_symbols)))
149 throw std::runtime_error("Failed to find symbols");
150
151 ULONG celt = 0;
152 while (SUCCEEDED(enum_symbols->Next(1, &symbol, &celt)) && celt == 1)
153 {
154 CComPtr<IDiaSymbol> sym(symbol);
155
156 const uint32_t sym_id = GetSymbolId(symbol);
157 if (visited_.find(sym_id) != visited_.end())
158 return;
159
160 visited_.insert(sym_id);
161
162 std::string str_name = GetSymbolNameString(sym);
163 if (str_name.empty())
164 continue;
165
166 DumpType(sym, str_name, 0);
167 }
168 }
169
170 void PdbReader::DumpFunctions(IDiaSymbol* g_symbol)
171 {
172 IDiaSymbol* symbol;
173
174 CComPtr<IDiaEnumSymbols> enum_symbols;
175 if (FAILED(g_symbol->findChildren(SymTagFunction, nullptr, nsNone, &enum_symbols)))
176 throw std::runtime_error("Failed to find symbols");
177
178 ULONG celt = 0;
179 while (SUCCEEDED(enum_symbols->Next(1, &symbol, &celt)) && celt == 1)
180 {
181 CComPtr<IDiaSymbol> sym(symbol);
182
183 DWORD sym_tag_type;
184 if (sym->get_symTag(&sym_tag_type) != S_OK)
185 continue;
186
187 const uint32_t sym_id = GetSymbolId(sym);
188 if (visited_.find(sym_id) != visited_.end())
189 continue;
190
191 visited_.insert(sym_id);
192
193 std::string str_name = GetSymbolNameString(sym);
194 if (str_name.empty())
195 continue;
196
197 DWORD offset;
198 if (sym->get_addressOffset(&offset) != S_OK)
199 continue;
200
201 // Filter out some useless functions
202 if (str_name.find('`') != std::string::npos)
203 continue;
204
205 // Check if it's a member function
206 if (str_name.find(':') != std::string::npos)
207 {
208 const std::string new_str = ReplaceString(str_name, "::", ".");
209
210 (*offsets_dump_)[new_str] = offset;
211 }
212 else
213 {
214 (*offsets_dump_)["Global." + str_name] = offset;
215 }
216 }
217 }
218
219 void PdbReader::DumpGlobalVariables(IDiaSymbol* g_symbol)
220 {
221 IDiaSymbol* symbol;
222
223 CComPtr<IDiaEnumSymbols> enum_symbols;
224 if (FAILED(g_symbol->findChildren(SymTagData, nullptr, nsNone, &enum_symbols)))
225 throw std::runtime_error("Failed to find symbols");
226
227 ULONG celt = 0;
228 while (SUCCEEDED(enum_symbols->Next(1, &symbol, &celt)) && celt == 1)
229 {
230 CComPtr<IDiaSymbol> sym(symbol);
231
232 const uint32_t sym_id = GetSymbolId(symbol);
233 if (visited_.find(sym_id) != visited_.end())
234 return;
235
236 visited_.insert(sym_id);
237
238 std::string str_name = GetSymbolNameString(sym);
239 if (str_name.empty())
240 continue;
241
242 DWORD sym_tag;
243 if (sym->get_symTag(&sym_tag) != S_OK)
244 continue;
245
246 DWORD offset;
247 if (sym->get_addressOffset(&offset) != S_OK)
248 continue;
249
250 (*offsets_dump_)["Global." + str_name] = offset;
251 }
252 }
253
254 void PdbReader::DumpType(IDiaSymbol* symbol, const std::string& structure, int indent) const
255 {
256 CComPtr<IDiaEnumSymbols> enum_children;
257 IDiaSymbol* symbol_child;
258 DWORD sym_tag;
259 ULONG celt = 0;
260
261 if (indent > 5)
262 return;
263
264 if (symbol->get_symTag(&sym_tag) != S_OK)
265 return;
266
267 switch (sym_tag)
268 {
269 case SymTagData:
270 DumpData(symbol, structure);
271 break;
272 case SymTagEnum:
273 case SymTagUDT:
274 if (SUCCEEDED(symbol->findChildren(SymTagNull, nullptr, nsNone, &enum_children)))
275 {
276 while (SUCCEEDED(enum_children->Next(1, &symbol_child, &celt)) && celt == 1)
277 {
278 CComPtr<IDiaSymbol> sym_child(symbol_child);
279
280 DumpType(sym_child, structure, indent + 2);
281 }
282 }
283 break;
284 default:
285 break;
286 }
287 }
288
289 void PdbReader::DumpData(IDiaSymbol* symbol, const std::string& structure) const
290 {
291 DWORD loc_type;
292 if (symbol->get_locationType(&loc_type) != S_OK)
293 return;
294
295 if (loc_type != LocIsThisRel && loc_type != LocIsBitField)
296 return;
297
298 CComPtr<IDiaSymbol> type;
299 if (symbol->get_type(&type) != S_OK)
300 return;
301
302 if (type == nullptr)
303 return;
304
305 LONG offset;
306 if (symbol->get_offset(&offset) != S_OK)
307 return;
308
309 std::string str_name = GetSymbolNameString(symbol);
310 if (str_name.empty())
311 return;
312
313 if (loc_type == LocIsBitField)
314 {
315 DWORD bit_position;
316 if (symbol->get_bitPosition(&bit_position) != S_OK)
317 return;
318
319 ULONGLONG num_bits;
320 if (symbol->get_length(&num_bits) != S_OK)
321 return;
322
323 ULONGLONG length;
324 if (type->get_length(&length) != S_OK)
325 return;
326
327 const BitField bit_field{static_cast<DWORD64>(offset), bit_position, num_bits, length};
328
329 (*bitfields_dump_)[structure + "." + str_name] = bit_field;
330 }
331 else if (loc_type == LocIsThisRel)
332 {
333 (*offsets_dump_)[structure + "." + str_name] = offset;
334 }
335 }
336
337 std::string PdbReader::GetSymbolNameString(IDiaSymbol* symbol)
338 {
339 BSTR str = nullptr;
340
341 std::string name;
342
343 HRESULT hr = symbol->get_name(&str);
344 if (hr != S_OK)
345 return name;
346
347 if (str != nullptr)
348 {
349 name = Tools::Utf8Encode(str);
350 }
351
352 SysFreeString(str);
353
354 return name;
355 }
356
357 uint32_t PdbReader::GetSymbolId(IDiaSymbol* symbol)
358 {
359 DWORD id;
360 symbol->get_symIndexId(&id);
361
362 return id;
363 }
364
365 void PdbReader::Cleanup(IDiaSymbol* symbol, IDiaSession* session, IDiaDataSource* source)
366 {
367 if (symbol != nullptr)
368 symbol->Release();
369 if (session != nullptr)
370 session->Release();
371 if (source != nullptr)
372 source->Release();
373
374 CoUninitialize();
375 }
376} // namespace API
void DumpType(IDiaSymbol *, const std::string &, int) const
void DumpFunctions(IDiaSymbol *)
void DumpData(IDiaSymbol *, const std::string &) const
void DumpGlobalVariables(IDiaSymbol *)
void Read(const std::wstring &path, std::unordered_map< std::string, intptr_t > *offsets_dump, std::unordered_map< std::string, BitField > *bitfields_dump)
Definition PDBReader.cpp:44
void DumpStructs(IDiaSymbol *)
void Attach(T *sym)
Definition PDBReader.cpp:35
Definition IBaseApi.h:9
Definition json.hpp:4518