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 
     44 #if defined(PIPE_OS_WINDOWS)
     45 
     46 #include <windows.h>
     47 #include <stddef.h>
     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 <execinfo.h>
    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 }
    319