Home | History | Annotate | Download | only in util
      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