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 <jfonseca (at) vmware.com> 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 #if defined(PIPE_OS_WINDOWS) && defined(PIPE_ARCH_X86) 44 45 #include <windows.h> 46 #include <stddef.h> 47 48 #include "dbghelp.h" 49 50 51 static BOOL bSymInitialized = FALSE; 52 53 static HMODULE hModule_Dbghelp = NULL; 54 55 56 static 57 FARPROC WINAPI __GetProcAddress(LPCSTR lpProcName) 58 { 59 #ifdef PIPE_CC_GCC 60 if (!hModule_Dbghelp) { 61 /* 62 * bfdhelp.dll is a dbghelp.dll look-alike replacement, which is able to 63 * understand MinGW symbols using BFD library. It is available from 64 * http://people.freedesktop.org/~jrfonseca/bfdhelp/ for now. 65 */ 66 hModule_Dbghelp = LoadLibraryA("bfdhelp.dll"); 67 } 68 #endif 69 70 if (!hModule_Dbghelp) { 71 hModule_Dbghelp = LoadLibraryA("dbghelp.dll"); 72 if (!hModule_Dbghelp) { 73 return NULL; 74 } 75 } 76 77 return GetProcAddress(hModule_Dbghelp, lpProcName); 78 } 79 80 81 typedef BOOL (WINAPI *PFNSYMINITIALIZE)(HANDLE, LPSTR, BOOL); 82 static PFNSYMINITIALIZE pfnSymInitialize = NULL; 83 84 static 85 BOOL WINAPI j_SymInitialize(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess) 86 { 87 if( 88 (pfnSymInitialize || (pfnSymInitialize = (PFNSYMINITIALIZE) __GetProcAddress("SymInitialize"))) 89 ) 90 return pfnSymInitialize(hProcess, UserSearchPath, fInvadeProcess); 91 else 92 return FALSE; 93 } 94 95 typedef DWORD (WINAPI *PFNSYMSETOPTIONS)(DWORD); 96 static PFNSYMSETOPTIONS pfnSymSetOptions = NULL; 97 98 static 99 DWORD WINAPI j_SymSetOptions(DWORD SymOptions) 100 { 101 if( 102 (pfnSymSetOptions || (pfnSymSetOptions = (PFNSYMSETOPTIONS) __GetProcAddress("SymSetOptions"))) 103 ) 104 return pfnSymSetOptions(SymOptions); 105 else 106 return FALSE; 107 } 108 109 typedef BOOL (WINAPI *PFNSYMGETSYMFROMADDR)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO); 110 static PFNSYMGETSYMFROMADDR pfnSymFromAddr = NULL; 111 112 static 113 BOOL WINAPI j_SymFromAddr(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol) 114 { 115 if( 116 (pfnSymFromAddr || (pfnSymFromAddr = (PFNSYMGETSYMFROMADDR) __GetProcAddress("SymFromAddr"))) 117 ) 118 return pfnSymFromAddr(hProcess, Address, Displacement, Symbol); 119 else 120 return FALSE; 121 } 122 123 124 static INLINE void 125 debug_symbol_name_dbghelp(const void *addr, char* buf, unsigned size) 126 { 127 HANDLE hProcess; 128 BYTE symbolBuffer[1024]; 129 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO) symbolBuffer; 130 DWORD64 dwDisplacement = 0; /* Displacement of the input address, relative to the start of the symbol */ 131 132 hProcess = GetCurrentProcess(); 133 134 memset(pSymbol, 0, sizeof *pSymbol); 135 pSymbol->SizeOfStruct = sizeof(symbolBuffer); 136 pSymbol->MaxNameLen = sizeof(symbolBuffer) - offsetof(SYMBOL_INFO, Name); 137 138 if(!bSymInitialized) { 139 j_SymSetOptions(/* SYMOPT_UNDNAME | */ SYMOPT_LOAD_LINES); 140 if(j_SymInitialize(hProcess, NULL, TRUE)) 141 bSymInitialized = TRUE; 142 } 143 144 if(!j_SymFromAddr(hProcess, (DWORD64)(uintptr_t)addr, &dwDisplacement, pSymbol)) 145 buf[0] = 0; 146 else 147 { 148 strncpy(buf, pSymbol->Name, size); 149 buf[size - 1] = 0; 150 } 151 } 152 #endif 153 154 #ifdef __GLIBC__ 155 #ifndef __UCLIBC__ 156 #include <execinfo.h> 157 #endif 158 159 /* This can only provide dynamic symbols, or binary offsets into a file. 160 * 161 * To fix this, post-process the output with tools/addr2line.sh 162 */ 163 static INLINE void 164 debug_symbol_name_glibc(const void *addr, char* buf, unsigned size) 165 { 166 char** syms = backtrace_symbols((void**)&addr, 1); 167 strncpy(buf, syms[0], size); 168 buf[size - 1] = 0; 169 free(syms); 170 } 171 #endif 172 173 void 174 debug_symbol_name(const void *addr, char* buf, unsigned size) 175 { 176 #if defined(PIPE_OS_WINDOWS) && defined(PIPE_ARCH_X86) 177 debug_symbol_name_dbghelp(addr, buf, size); 178 if(buf[0]) 179 return; 180 #endif 181 182 #ifdef __GLIBC__ 183 debug_symbol_name_glibc(addr, buf, size); 184 if(buf[0]) 185 return; 186 #endif 187 188 util_snprintf(buf, size, "%p", addr); 189 buf[size - 1] = 0; 190 } 191 192 void 193 debug_symbol_print(const void *addr) 194 { 195 char buf[1024]; 196 debug_symbol_name(addr, buf, sizeof(buf)); 197 debug_printf("\t%s\n", buf); 198 } 199 200 struct util_hash_table* symbols_hash; 201 pipe_static_mutex(symbols_mutex); 202 203 static unsigned hash_ptr(void* p) 204 { 205 return (unsigned)(uintptr_t)p; 206 } 207 208 static int compare_ptr(void* a, void* b) 209 { 210 if(a == b) 211 return 0; 212 else if(a < b) 213 return -1; 214 else 215 return 1; 216 } 217 218 const char* 219 debug_symbol_name_cached(const void *addr) 220 { 221 const char* name; 222 #ifdef PIPE_SUBSYSTEM_WINDOWS_USER 223 static boolean first = TRUE; 224 225 if (first) { 226 pipe_mutex_init(symbols_mutex); 227 first = FALSE; 228 } 229 #endif 230 231 pipe_mutex_lock(symbols_mutex); 232 if(!symbols_hash) 233 symbols_hash = util_hash_table_create(hash_ptr, compare_ptr); 234 name = util_hash_table_get(symbols_hash, (void*)addr); 235 if(!name) 236 { 237 char buf[1024]; 238 debug_symbol_name(addr, buf, sizeof(buf)); 239 name = strdup(buf); 240 241 util_hash_table_set(symbols_hash, (void*)addr, (void*)name); 242 } 243 pipe_mutex_unlock(symbols_mutex); 244 return name; 245 } 246