Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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.  
  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.