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
SymEnumer(PCSTR name,DWORD64 base,PVOID context)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())
proc_(proc)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
Resolve(const char * name)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
Unresolve(int ptr)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
~SymResolver()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