1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // A smaller wrapper around the dbghelp symbol resolution routines. 6 // For example: 7 // SymResolver resolver("ntdll.dll"); 8 // resolver.Resolve("ntdll!NtBlahBlah"); 9 10 #ifndef TRACELINE_SYM_RESOLVER_H_ 11 #define TRACELINE_SYM_RESOLVER_H_ 12 13 #include <windows.h> 14 #include <dbghelp.h> 15 16 #include <vector> 17 #include <string> 18 #include <map> 19 20 static BOOL CALLBACK SymEnumer(PCSTR name, DWORD64 base, PVOID context) { 21 reinterpret_cast<std::vector<DWORD64>*>(context)->push_back(base); 22 return TRUE; 23 } 24 25 class SymResolver { 26 public: 27 28 // Constructor to load a single DLL. 29 SymResolver(const char* dllname, HANDLE proc = ::GetCurrentProcess()) 30 : proc_(proc) { 31 32 // TODO(deanm): Would be nice to get this from WinDBG, but it's buried 33 // in the workspace data blob... _NT_SYMBOL_PATH is not usually set... 34 static char* kSymbolPath = 35 "C:\\Program Files\\Debugging Tools for Windows (x86)\\sym;" 36 "C:\\Program Files\\Debugging Tools for Windows\\sym"; 37 38 // If we want to load a specific DLL, or we want to load all. 39 if (::SymInitialize(proc_, kSymbolPath, dllname ? FALSE : TRUE) != TRUE) { 40 NOTREACHED("SymInitialize failed: %d", GetLastError()); 41 } 42 43 base_ = 0; 44 45 if (dllname) { 46 base_ = ::SymLoadModuleEx(proc_, 47 NULL, 48 const_cast<char*>(dllname), 49 NULL, 50 reinterpret_cast<DWORD64>( 51 GetModuleHandleA(dllname)), 52 0, 53 NULL, 54 0); 55 if (base_ == 0) { 56 NOTREACHED("SymLoadModuleEx(%s) failed: %d", dllname, GetLastError()); 57 } 58 } 59 60 std::vector<DWORD64> bases; 61 // The name returned from SymEnumerateModules64 doesn't include the ext, 62 // so we can't differentiate between a dll and exe of the same name. So 63 // collect all of the base addresses and query for more info. 64 // The prototype changed from PSTR to PCSTR, so in order to support older 65 // SDKs we have to cast SymEnumer. 66 PSYM_ENUMMODULES_CALLBACK64 enumer = 67 reinterpret_cast<PSYM_ENUMMODULES_CALLBACK64>(&SymEnumer); 68 if (SymEnumerateModules64(proc_, enumer, &bases) != TRUE) { 69 NOTREACHED("SymEnumerateModules64 failed: %d\n", GetLastError()); 70 } 71 for (size_t i = 0; i < bases.size(); ++i) { 72 // This was failing, turns out I was just using the system32 73 // dbghelp.dll which is old, use the one from windbg :( 74 IMAGEHLP_MODULE64 info; 75 info.SizeOfStruct = sizeof(info); 76 if (SymGetModuleInfo64(proc_, bases[i], &info) != TRUE) { 77 NOTREACHED("SymGetModuleInfo64 failed: %d\n", GetLastError()); 78 } 79 std::string filename(info.ImageName); 80 size_t last_slash = filename.find_last_of('\\'); 81 if (last_slash != std::string::npos) 82 filename = filename.substr(filename.find_last_of('\\') + 1); 83 84 // Map the base address to the image name... 85 dlls_[static_cast<int>(bases[i])] = filename; 86 } 87 88 // TODO(deanm): check the symbols are rad and stuff... 89 } 90 91 char* Resolve(const char* name) { 92 // The API writes to the space after SYMBOL_INFO... 93 struct { 94 SYMBOL_INFO info; 95 char buf[128]; 96 } info = {0}; 97 98 info.info.SizeOfStruct = sizeof(info.info); 99 info.info.ModBase = base_; 100 info.info.MaxNameLen = 127; 101 102 if (SymFromName(proc_, const_cast<char*>(name), &info.info) != TRUE) { 103 NOTREACHED("SymFromName(%s) failed: %d", name, GetLastError()); 104 } 105 106 return reinterpret_cast<char*>(info.info.Address); 107 } 108 109 std::string Unresolve(int ptr) { 110 // The API writes to the space after SYMBOL_INFO... 111 struct { 112 SYMBOL_INFO info; 113 char buf[128]; 114 } info = {0}; 115 116 info.info.SizeOfStruct = sizeof(info.info); 117 info.info.ModBase = base_; 118 info.info.MaxNameLen = 127; 119 if (!::SymFromAddr(proc_, static_cast<DWORD64>(ptr), NULL, &info.info)) { 120 return std::string("failed"); 121 } 122 123 std::string name; 124 int addr = static_cast<int>(info.info.Address); 125 int base = static_cast<int>(info.info.ModBase); 126 127 if (dlls_.count(base) == 1) { 128 name.append(dlls_[base]); 129 } else { 130 name.append("unknown_mod"); 131 } 132 name.push_back('!'); 133 name.append(info.info.Name); 134 135 char buf[32]; 136 _itoa_s(ptr - addr, buf, sizeof(buf), 16); 137 name.append("+0x"); 138 name.append(buf); 139 140 DWORD disp; 141 IMAGEHLP_LINE64 line; 142 if (::SymGetLineFromAddr64( 143 proc_, static_cast<DWORD64>(ptr), &disp, &line)) { 144 name.append(" [ "); 145 name.append(line.FileName); 146 name.append(":"); 147 _itoa_s(line.LineNumber, buf, sizeof(buf), 10); 148 name.append(buf); 149 name.append(" ]"); 150 } 151 152 return name; 153 } 154 155 ~SymResolver() { 156 if (::SymCleanup(proc_) != TRUE) { 157 NOTREACHED("SymCleanup failed: %d", GetLastError()); 158 } 159 } 160 161 private: 162 HANDLE proc_; 163 ULONG64 base_; 164 std::map<int, std::string> dlls_; 165 }; 166 167 #endif // TRACELINE_SYM_RESOLVER_H_ 168