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@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 51static BOOL bSymInitialized = FALSE; 52 53static HMODULE hModule_Dbghelp = NULL; 54 55 56static 57FARPROC 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 81typedef BOOL (WINAPI *PFNSYMINITIALIZE)(HANDLE, LPSTR, BOOL); 82static PFNSYMINITIALIZE pfnSymInitialize = NULL; 83 84static 85BOOL 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 95typedef DWORD (WINAPI *PFNSYMSETOPTIONS)(DWORD); 96static PFNSYMSETOPTIONS pfnSymSetOptions = NULL; 97 98static 99DWORD 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 109typedef BOOL (WINAPI *PFNSYMGETSYMFROMADDR)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO); 110static PFNSYMGETSYMFROMADDR pfnSymFromAddr = NULL; 111 112static 113BOOL 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 124static INLINE void 125debug_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 */ 163static INLINE void 164debug_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 173void 174debug_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 192void 193debug_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 200struct util_hash_table* symbols_hash; 201pipe_static_mutex(symbols_mutex); 202 203static unsigned hash_ptr(void* p) 204{ 205 return (unsigned)(uintptr_t)p; 206} 207 208static 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 218const char* 219debug_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