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