1 /*
2 Copyright (c) 2004 Andrei Polushin
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22
23 #if !defined(_M_AMD64) && defined(_MSC_VER)
24
25 /* X86_64 is currently missing some machine-dependent code below. */
26
27 #define GC_BUILD
28 #include "private/msvc_dbg.h"
29 #include "gc.h"
30
31 #ifndef WIN32_LEAN_AND_MEAN
32 # define WIN32_LEAN_AND_MEAN 1
33 #endif
34 #define NOSERVICE
35 #include <windows.h>
36
37 #pragma pack(push, 8)
38 #include <imagehlp.h>
39 #pragma pack(pop)
40
41 #pragma comment(lib, "dbghelp.lib")
42 #pragma optimize("gy", off)
43
44 typedef GC_word word;
45 #define GC_ULONG_PTR word
46
47 #ifdef _WIN64
48 typedef GC_ULONG_PTR ULONG_ADDR;
49 #else
50 typedef ULONG ULONG_ADDR;
51 #endif
52
GetSymHandle(void)53 static HANDLE GetSymHandle(void)
54 {
55 static HANDLE symHandle = NULL;
56 if (!symHandle) {
57 BOOL bRet = SymInitialize(symHandle = GetCurrentProcess(), NULL, FALSE);
58 if (bRet) {
59 DWORD dwOptions = SymGetOptions();
60 dwOptions &= ~SYMOPT_UNDNAME;
61 dwOptions |= SYMOPT_LOAD_LINES;
62 SymSetOptions(dwOptions);
63 }
64 }
65 return symHandle;
66 }
67
FunctionTableAccess(HANDLE hProcess,ULONG_ADDR dwAddrBase)68 static void* CALLBACK FunctionTableAccess(HANDLE hProcess,
69 ULONG_ADDR dwAddrBase)
70 {
71 return SymFunctionTableAccess(hProcess, dwAddrBase);
72 }
73
GetModuleBase(HANDLE hProcess,ULONG_ADDR dwAddress)74 static ULONG_ADDR CALLBACK GetModuleBase(HANDLE hProcess, ULONG_ADDR dwAddress)
75 {
76 MEMORY_BASIC_INFORMATION memoryInfo;
77 ULONG_ADDR dwAddrBase = SymGetModuleBase(hProcess, dwAddress);
78 if (dwAddrBase) {
79 return dwAddrBase;
80 }
81 if (VirtualQueryEx(hProcess, (void*)(GC_ULONG_PTR)dwAddress, &memoryInfo,
82 sizeof(memoryInfo))) {
83 char filePath[_MAX_PATH];
84 char curDir[_MAX_PATH];
85 char exePath[_MAX_PATH];
86 DWORD size = GetModuleFileNameA((HINSTANCE)memoryInfo.AllocationBase,
87 filePath, sizeof(filePath));
88
89 /* Save and restore current directory around SymLoadModule, see KB */
90 /* article Q189780. */
91 GetCurrentDirectoryA(sizeof(curDir), curDir);
92 GetModuleFileNameA(NULL, exePath, sizeof(exePath));
93 #if defined(_MSC_VER) && _MSC_VER == 1200
94 /* use strcat for VC6 */
95 strcat(exePath, "\\..");
96 #else
97 strcat_s(exePath, sizeof(exePath), "\\..");
98 #endif /* _MSC_VER >= 1200 */
99 SetCurrentDirectoryA(exePath);
100 #ifdef _DEBUG
101 GetCurrentDirectoryA(sizeof(exePath), exePath);
102 #endif
103 SymLoadModule(hProcess, NULL, size ? filePath : NULL, NULL,
104 (ULONG_ADDR)(GC_ULONG_PTR)memoryInfo.AllocationBase, 0);
105 SetCurrentDirectoryA(curDir);
106 }
107 return (ULONG_ADDR)(GC_ULONG_PTR)memoryInfo.AllocationBase;
108 }
109
CheckAddress(void * address)110 static ULONG_ADDR CheckAddress(void* address)
111 {
112 ULONG_ADDR dwAddress = (ULONG_ADDR)(GC_ULONG_PTR)address;
113 GetModuleBase(GetSymHandle(), dwAddress);
114 return dwAddress;
115 }
116
GetStackFrames(size_t skip,void * frames[],size_t maxFrames)117 size_t GetStackFrames(size_t skip, void* frames[], size_t maxFrames)
118 {
119 HANDLE hProcess = GetSymHandle();
120 HANDLE hThread = GetCurrentThread();
121 CONTEXT context;
122 context.ContextFlags = CONTEXT_FULL;
123 if (!GetThreadContext(hThread, &context)) {
124 return 0;
125 }
126 /* GetThreadContext might return invalid context for the current thread. */
127 #if defined(_M_IX86)
128 __asm mov context.Ebp, ebp
129 #endif
130 return GetStackFramesFromContext(hProcess, hThread, &context, skip + 1,
131 frames, maxFrames);
132 }
133
GetStackFramesFromContext(HANDLE hProcess,HANDLE hThread,CONTEXT * context,size_t skip,void * frames[],size_t maxFrames)134 size_t GetStackFramesFromContext(HANDLE hProcess, HANDLE hThread,
135 CONTEXT* context, size_t skip,
136 void* frames[], size_t maxFrames)
137 {
138 size_t frameIndex;
139 DWORD machineType;
140 STACKFRAME stackFrame = { 0 };
141 stackFrame.AddrPC.Mode = AddrModeFlat;
142 #if defined(_M_IX86)
143 machineType = IMAGE_FILE_MACHINE_I386;
144 stackFrame.AddrPC.Offset = context->Eip;
145 stackFrame.AddrStack.Mode = AddrModeFlat;
146 stackFrame.AddrStack.Offset = context->Esp;
147 stackFrame.AddrFrame.Mode = AddrModeFlat;
148 stackFrame.AddrFrame.Offset = context->Ebp;
149 #elif defined(_M_MRX000)
150 machineType = IMAGE_FILE_MACHINE_R4000;
151 stackFrame.AddrPC.Offset = context->Fir;
152 #elif defined(_M_ALPHA)
153 machineType = IMAGE_FILE_MACHINE_ALPHA;
154 stackFrame.AddrPC.Offset = (unsigned long)context->Fir;
155 #elif defined(_M_PPC)
156 machineType = IMAGE_FILE_MACHINE_POWERPC;
157 stackFrame.AddrPC.Offset = context->Iar;
158 #elif defined(_M_IA64)
159 machineType = IMAGE_FILE_MACHINE_IA64;
160 stackFrame.AddrPC.Offset = context->StIIP;
161 #elif defined(_M_ALPHA64)
162 machineType = IMAGE_FILE_MACHINE_ALPHA64;
163 stackFrame.AddrPC.Offset = context->Fir;
164 #elif !defined(CPPCHECK)
165 # error Unknown CPU
166 #endif
167 for (frameIndex = 0; frameIndex < maxFrames; ) {
168 BOOL bRet = StackWalk(machineType, hProcess, hThread, &stackFrame,
169 &context, NULL, FunctionTableAccess, GetModuleBase, NULL);
170 if (!bRet) {
171 break;
172 }
173 if (skip) {
174 skip--;
175 } else {
176 frames[frameIndex++] = (void*)(GC_ULONG_PTR)stackFrame.AddrPC.Offset;
177 }
178 }
179 return frameIndex;
180 }
181
GetModuleNameFromAddress(void * address,char * moduleName,size_t size)182 size_t GetModuleNameFromAddress(void* address, char* moduleName, size_t size)
183 {
184 if (size) *moduleName = 0;
185 {
186 const char* sourceName;
187 IMAGEHLP_MODULE moduleInfo = { sizeof (moduleInfo) };
188 if (!SymGetModuleInfo(GetSymHandle(), CheckAddress(address),
189 &moduleInfo)) {
190 return 0;
191 }
192 sourceName = strrchr(moduleInfo.ImageName, '\\');
193 if (sourceName) {
194 sourceName++;
195 } else {
196 sourceName = moduleInfo.ImageName;
197 }
198 if (size) {
199 strncpy(moduleName, sourceName, size)[size - 1] = 0;
200 }
201 return strlen(sourceName);
202 }
203 }
204
GetModuleNameFromStack(size_t skip,char * moduleName,size_t size)205 size_t GetModuleNameFromStack(size_t skip, char* moduleName, size_t size)
206 {
207 void* address = NULL;
208 GetStackFrames(skip + 1, &address, 1);
209 if (address) {
210 return GetModuleNameFromAddress(address, moduleName, size);
211 }
212 return 0;
213 }
214
215 union sym_namebuf_u {
216 IMAGEHLP_SYMBOL sym;
217 char symNameBuffer[sizeof(IMAGEHLP_SYMBOL) + MAX_SYM_NAME];
218 };
219
GetSymbolNameFromAddress(void * address,char * symbolName,size_t size,size_t * offsetBytes)220 size_t GetSymbolNameFromAddress(void* address, char* symbolName, size_t size,
221 size_t* offsetBytes)
222 {
223 if (size) *symbolName = 0;
224 if (offsetBytes) *offsetBytes = 0;
225 __try {
226 ULONG_ADDR dwOffset = 0;
227 union sym_namebuf_u u;
228
229 u.sym.SizeOfStruct = sizeof(u.sym);
230 u.sym.MaxNameLength = sizeof(u.symNameBuffer) - sizeof(u.sym);
231
232 if (!SymGetSymFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset,
233 &u.sym)) {
234 return 0;
235 } else {
236 const char* sourceName = u.sym.Name;
237 char undName[1024];
238 if (UnDecorateSymbolName(u.sym.Name, undName, sizeof(undName),
239 UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS)) {
240 sourceName = undName;
241 } else if (SymUnDName(&u.sym, undName, sizeof(undName))) {
242 sourceName = undName;
243 }
244 if (offsetBytes) {
245 *offsetBytes = dwOffset;
246 }
247 if (size) {
248 strncpy(symbolName, sourceName, size)[size - 1] = 0;
249 }
250 return strlen(sourceName);
251 }
252 } __except (EXCEPTION_EXECUTE_HANDLER) {
253 SetLastError(GetExceptionCode());
254 }
255 return 0;
256 }
257
GetSymbolNameFromStack(size_t skip,char * symbolName,size_t size,size_t * offsetBytes)258 size_t GetSymbolNameFromStack(size_t skip, char* symbolName, size_t size,
259 size_t* offsetBytes)
260 {
261 void* address = NULL;
262 GetStackFrames(skip + 1, &address, 1);
263 if (address) {
264 return GetSymbolNameFromAddress(address, symbolName, size, offsetBytes);
265 }
266 return 0;
267 }
268
GetFileLineFromAddress(void * address,char * fileName,size_t size,size_t * lineNumber,size_t * offsetBytes)269 size_t GetFileLineFromAddress(void* address, char* fileName, size_t size,
270 size_t* lineNumber, size_t* offsetBytes)
271 {
272 if (size) *fileName = 0;
273 if (lineNumber) *lineNumber = 0;
274 if (offsetBytes) *offsetBytes = 0;
275 {
276 char* sourceName;
277 IMAGEHLP_LINE line = { sizeof (line) };
278 GC_ULONG_PTR dwOffset = 0;
279 if (!SymGetLineFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset,
280 &line)) {
281 return 0;
282 }
283 if (lineNumber) {
284 *lineNumber = line.LineNumber;
285 }
286 if (offsetBytes) {
287 *offsetBytes = dwOffset;
288 }
289 sourceName = line.FileName;
290 /* TODO: resolve relative filenames, found in 'source directories' */
291 /* registered with MSVC IDE. */
292 if (size) {
293 strncpy(fileName, sourceName, size)[size - 1] = 0;
294 }
295 return strlen(sourceName);
296 }
297 }
298
GetFileLineFromStack(size_t skip,char * fileName,size_t size,size_t * lineNumber,size_t * offsetBytes)299 size_t GetFileLineFromStack(size_t skip, char* fileName, size_t size,
300 size_t* lineNumber, size_t* offsetBytes)
301 {
302 void* address = NULL;
303 GetStackFrames(skip + 1, &address, 1);
304 if (address) {
305 return GetFileLineFromAddress(address, fileName, size, lineNumber,
306 offsetBytes);
307 }
308 return 0;
309 }
310
GetDescriptionFromAddress(void * address,const char * format,char * buffer,size_t size)311 size_t GetDescriptionFromAddress(void* address, const char* format,
312 char* buffer, size_t size)
313 {
314 char*const begin = buffer;
315 char*const end = buffer + size;
316 size_t line_number = 0;
317
318 (void)format;
319 if (size) {
320 *buffer = 0;
321 }
322 buffer += GetFileLineFromAddress(address, buffer, size, &line_number, NULL);
323 size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
324
325 if (line_number) {
326 char str[128];
327
328 wsprintf(str, "(%d) : ", (int)line_number);
329 if (size) {
330 strncpy(buffer, str, size)[size - 1] = 0;
331 }
332 buffer += strlen(str);
333 size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
334 }
335
336 if (size) {
337 strncpy(buffer, "at ", size)[size - 1] = 0;
338 }
339 buffer += sizeof("at ") - 1;
340 size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
341
342 buffer += GetSymbolNameFromAddress(address, buffer, size, NULL);
343 size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
344
345 if (size) {
346 strncpy(buffer, " in ", size)[size - 1] = 0;
347 }
348 buffer += sizeof(" in ") - 1;
349 size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
350
351 buffer += GetModuleNameFromAddress(address, buffer, size);
352 return buffer - begin;
353 }
354
GetDescriptionFromStack(void * const frames[],size_t count,const char * format,char * description[],size_t size)355 size_t GetDescriptionFromStack(void* const frames[], size_t count,
356 const char* format, char* description[],
357 size_t size)
358 {
359 const GC_ULONG_PTR begin = (GC_ULONG_PTR)description;
360 const GC_ULONG_PTR end = begin + size;
361 GC_ULONG_PTR buffer = begin + (count + 1) * sizeof(char*);
362 size_t i;
363
364 for (i = 0; i < count; ++i) {
365 if (description)
366 description[i] = (char*)buffer;
367 buffer += 1 + GetDescriptionFromAddress(frames[i], format, (char*)buffer,
368 end < buffer ? 0 : end - buffer);
369 }
370 if (description)
371 description[count] = NULL;
372 return buffer - begin;
373 }
374
375 /* Compatibility with <execinfo.h> */
376
backtrace(void * addresses[],int count)377 int backtrace(void* addresses[], int count)
378 {
379 return GetStackFrames(1, addresses, count);
380 }
381
backtrace_symbols(void * const * addresses,int count)382 char** backtrace_symbols(void*const* addresses, int count)
383 {
384 size_t size = GetDescriptionFromStack(addresses, count, NULL, NULL, 0);
385 char** symbols = (char**)malloc(size);
386 if (symbols != NULL)
387 GetDescriptionFromStack(addresses, count, NULL, symbols, size);
388 return symbols;
389 }
390
391 #else
392
393 extern int GC_quiet;
394 /* ANSI C does not allow translation units to be empty. */
395
396 #endif /* _M_AMD64 */
397