Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
5564 serge 1
/**************************************************************************
2
 *
3
 * Copyright 2009 VMware, Inc.
4
 * All Rights Reserved.
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a
7
 * copy of this software and associated documentation files (the
8
 * "Software"), to deal in the Software without restriction, including
9
 * without limitation the rights to use, copy, modify, merge, publish,
10
 * distribute, sub license, and/or sell copies of the Software, and to
11
 * permit persons to whom the Software is furnished to do so, subject to
12
 * the following conditions:
13
 *
14
 * The above copyright notice and this permission notice (including the
15
 * next paragraph) shall be included in all copies or substantial portions
16
 * of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21
 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
 *
26
 **************************************************************************/
27
 
28
/**
29
 * @file
30
 * Symbol lookup.
31
 *
32
 * @author Jose Fonseca 
33
 */
34
 
35
#include "pipe/p_compiler.h"
36
#include "os/os_thread.h"
37
#include "u_string.h"
38
 
39
#include "u_debug.h"
40
#include "u_debug_symbol.h"
41
#include "u_hash_table.h"
42
 
43
 
44
#if defined(PIPE_OS_WINDOWS)
45
 
46
#include 
47
#include 
48
 
49
#include "dbghelp.h"
50
 
51
 
52
/**
53
 * SymInitialize() must be called once for each process (in this case, the
54
 * current process), before any of the other functions can be called.
55
 */
56
static BOOL g_bSymInitialized = FALSE;
57
 
58
 
59
/**
60
 * Lookup the address of a DbgHelp function.
61
 */
62
static FARPROC WINAPI
63
getDbgHelpProcAddress(LPCSTR lpProcName)
64
{
65
   static HMODULE hModule = NULL;
66
 
67
   if (!hModule) {
68
      static boolean bail = FALSE;
69
 
70
      if (bail) {
71
         return NULL;
72
      }
73
 
74
#ifdef PIPE_CC_GCC
75
      /*
76
       * DbgHelp does not understand the debug information generated by MinGW toolchain.
77
       *
78
       * mgwhelp.dll is a dbghelp.dll look-alike replacement, which is able to
79
       * understand MinGW symbols, including on 64-bit builds.
80
       */
81
      if (!hModule) {
82
         hModule = LoadLibraryA("mgwhelp.dll");
83
         if (!hModule) {
84
            _debug_printf("warning: mgwhelp.dll not found: symbol names will not be resolved\n"
85
                          "warning: download it from https://github.com/jrfonseca/drmingw/#mgwhelp\n");
86
         }
87
      }
88
#endif
89
 
90
      /*
91
       * Fallback to the real DbgHelp.
92
       */
93
      if (!hModule) {
94
         hModule = LoadLibraryA("dbghelp.dll");
95
      }
96
 
97
      if (!hModule) {
98
         bail = TRUE;
99
         return NULL;
100
      }
101
   }
102
 
103
   return GetProcAddress(hModule, lpProcName);
104
}
105
 
106
 
107
/**
108
 * Generic macro to dispatch a DbgHelp functions.
109
 */
110
#define DBGHELP_DISPATCH(_name, _ret_type, _ret_default, _arg_types, _arg_names) \
111
   static _ret_type WINAPI \
112
   j_##_name _arg_types \
113
   { \
114
      typedef BOOL (WINAPI *PFN) _arg_types; \
115
      static PFN pfn = NULL; \
116
      if (!pfn) { \
117
         pfn = (PFN) getDbgHelpProcAddress(#_name); \
118
         if (!pfn) { \
119
            return _ret_default; \
120
         } \
121
      } \
122
      return pfn _arg_names; \
123
   }
124
 
125
DBGHELP_DISPATCH(SymInitialize,
126
                 BOOL, 0,
127
                 (HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess),
128
                 (hProcess, UserSearchPath, fInvadeProcess))
129
 
130
DBGHELP_DISPATCH(SymSetOptions,
131
                 DWORD, FALSE,
132
                 (DWORD SymOptions),
133
                 (SymOptions))
134
 
135
DBGHELP_DISPATCH(SymFromAddr,
136
                 BOOL, FALSE,
137
                 (HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol),
138
                 (hProcess, Address, Displacement, Symbol))
139
 
140
DBGHELP_DISPATCH(SymGetLineFromAddr64,
141
                 BOOL, FALSE,
142
                 (HANDLE hProcess, DWORD64 dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line),
143
                 (hProcess, dwAddr, pdwDisplacement, Line))
144
 
145
 
146
#undef DBGHELP_DISPATCH
147
 
148
 
149
static INLINE boolean
150
debug_symbol_name_dbghelp(const void *addr, char* buf, unsigned size)
151
{
152
   DWORD64 dwAddr = (DWORD64)(uintptr_t)addr;
153
   HANDLE hProcess = GetCurrentProcess();
154
 
155
   /* General purpose buffer, to back pSymbol and other temporary stuff.
156
    * Must not be too memory hungry here to avoid stack overflows.
157
    */
158
   CHAR buffer[512];
159
 
160
   PSYMBOL_INFO pSymbol = (PSYMBOL_INFO) buffer;
161
   DWORD64 dwDisplacement = 0;  /* Displacement of the input address, relative to the start of the symbol */
162
   DWORD dwLineDisplacement = 0;
163
   IMAGEHLP_LINE64 Line;
164
 
165
   memset(pSymbol, 0, sizeof *pSymbol);
166
   pSymbol->SizeOfStruct = sizeof buffer;
167
   pSymbol->MaxNameLen = sizeof buffer - offsetof(SYMBOL_INFO, Name);
168
 
169
   if (!g_bSymInitialized) {
170
      j_SymSetOptions(/* SYMOPT_UNDNAME | */ SYMOPT_LOAD_LINES);
171
      if (j_SymInitialize(hProcess, NULL, TRUE)) {
172
         g_bSymInitialized = TRUE;
173
      }
174
   }
175
 
176
   /* Lookup symbol name */
177
   if (!g_bSymInitialized ||
178
       !j_SymFromAddr(hProcess, dwAddr, &dwDisplacement, pSymbol)) {
179
      /*
180
       * We couldn't obtain symbol information.  At least tell which module the address belongs.
181
       */
182
 
183
      HMODULE hModule = NULL;
184
 
185
      if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
186
                             (LPCTSTR)addr,
187
                             &hModule)) {
188
         return FALSE;
189
      }
190
 
191
      if (GetModuleFileNameA(hModule, buffer, sizeof buffer) == sizeof buffer) {
192
         return FALSE;
193
      }
194
      util_snprintf(buf, size, "%p at %s+0x%lx",
195
                    addr, buffer,
196
                    (unsigned long)((uintptr_t)addr - (uintptr_t)hModule));
197
 
198
      return TRUE;
199
   }
200
 
201
   /*
202
    * Try to get filename and line number.
203
    */
204
   memset(&Line, 0, sizeof Line);
205
   Line.SizeOfStruct = sizeof Line;
206
   if (!j_SymGetLineFromAddr64(hProcess, dwAddr, &dwLineDisplacement, &Line)) {
207
      Line.FileName = NULL;
208
   }
209
 
210
   if (Line.FileName) {
211
      util_snprintf(buf, size, "%s at %s:%lu", pSymbol->Name, Line.FileName, Line.LineNumber);
212
   } else {
213
      util_snprintf(buf, size, "%s", pSymbol->Name);
214
   }
215
 
216
   return TRUE;
217
}
218
 
219
#endif /* PIPE_OS_WINDOWS */
220
 
221
 
222
#if defined(__GLIBC__) && !defined(__UCLIBC__)
223
 
224
#include 
225
 
226
/* This can only provide dynamic symbols, or binary offsets into a file.
227
 *
228
 * To fix this, post-process the output with tools/addr2line.sh
229
 */
230
static INLINE boolean
231
debug_symbol_name_glibc(const void *addr, char* buf, unsigned size)
232
{
233
   char** syms = backtrace_symbols((void**)&addr, 1);
234
   if (!syms) {
235
      return FALSE;
236
   }
237
   strncpy(buf, syms[0], size);
238
   buf[size - 1] = 0;
239
   free(syms);
240
   return TRUE;
241
}
242
 
243
#endif /* defined(__GLIBC__) && !defined(__UCLIBC__) */
244
 
245
 
246
void
247
debug_symbol_name(const void *addr, char* buf, unsigned size)
248
{
249
#if defined(PIPE_OS_WINDOWS)
250
   if (debug_symbol_name_dbghelp(addr, buf, size)) {
251
      return;
252
   }
253
#endif
254
 
255
#if defined(__GLIBC__) && !defined(__UCLIBC__)
256
   if (debug_symbol_name_glibc(addr, buf, size)) {
257
       return;
258
   }
259
#endif
260
 
261
   util_snprintf(buf, size, "%p", addr);
262
   buf[size - 1] = 0;
263
}
264
 
265
void
266
debug_symbol_print(const void *addr)
267
{
268
   char buf[1024];
269
   debug_symbol_name(addr, buf, sizeof(buf));
270
   debug_printf("\t%s\n", buf);
271
}
272
 
273
struct util_hash_table* symbols_hash;
274
pipe_static_mutex(symbols_mutex);
275
 
276
static unsigned hash_ptr(void* p)
277
{
278
   return (unsigned)(uintptr_t)p;
279
}
280
 
281
static int compare_ptr(void* a, void* b)
282
{
283
   if(a == b)
284
      return 0;
285
   else if(a < b)
286
      return -1;
287
   else
288
      return 1;
289
}
290
 
291
const char*
292
debug_symbol_name_cached(const void *addr)
293
{
294
   const char* name;
295
#ifdef PIPE_SUBSYSTEM_WINDOWS_USER
296
   static boolean first = TRUE;
297
 
298
   if (first) {
299
      pipe_mutex_init(symbols_mutex);
300
      first = FALSE;
301
   }
302
#endif
303
 
304
   pipe_mutex_lock(symbols_mutex);
305
   if(!symbols_hash)
306
      symbols_hash = util_hash_table_create(hash_ptr, compare_ptr);
307
   name = util_hash_table_get(symbols_hash, (void*)addr);
308
   if(!name)
309
   {
310
      char buf[1024];
311
      debug_symbol_name(addr, buf, sizeof(buf));
312
      name = strdup(buf);
313
 
314
      util_hash_table_set(symbols_hash, (void*)addr, (void*)name);
315
   }
316
   pipe_mutex_unlock(symbols_mutex);
317
   return name;
318
}