1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "precompiled.hpp" 26 #include "utilities/ostream.hpp" 27 #include "windbghelp.hpp" 28 29 #include <windows.h> 30 31 typedef DWORD (WINAPI *pfn_SymSetOptions)(DWORD); 32 typedef DWORD (WINAPI *pfn_SymGetOptions)(void); 33 typedef BOOL (WINAPI *pfn_SymInitialize)(HANDLE, PCTSTR, BOOL); 34 typedef BOOL (WINAPI *pfn_SymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64); 35 typedef DWORD (WINAPI *pfn_UnDecorateSymbolName)(const char*, char*, DWORD, DWORD); 36 typedef BOOL (WINAPI *pfn_SymSetSearchPath)(HANDLE, PCTSTR); 37 typedef BOOL (WINAPI *pfn_SymGetSearchPath)(HANDLE, PTSTR, int); 38 typedef BOOL (WINAPI *pfn_StackWalk64)(DWORD MachineType, 39 HANDLE hProcess, 40 HANDLE hThread, 41 LPSTACKFRAME64 StackFrame, 42 PVOID ContextRecord, 43 PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, 44 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, 45 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, 46 PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); 47 typedef PVOID (WINAPI *pfn_SymFunctionTableAccess64)(HANDLE hProcess, DWORD64 AddrBase); 48 typedef DWORD64 (WINAPI *pfn_SymGetModuleBase64)(HANDLE hProcess, DWORD64 dwAddr); 49 typedef BOOL (WINAPI *pfn_MiniDumpWriteDump) (HANDLE hProcess, DWORD ProcessId, HANDLE hFile, 50 MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 51 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 52 PMINIDUMP_CALLBACK_INFORMATION CallbackParam); 53 typedef BOOL (WINAPI *pfn_SymGetLineFromAddr64) (HANDLE hProcess, DWORD64 dwAddr, 54 PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line); 55 typedef LPAPI_VERSION (WINAPI *pfn_ImagehlpApiVersion)(void); 56 57 // Add functions as needed. 58 #define FOR_ALL_FUNCTIONS(DO) \ 59 DO(ImagehlpApiVersion) \ 60 DO(SymGetOptions) \ 61 DO(SymSetOptions) \ 62 DO(SymInitialize) \ 63 DO(SymGetSymFromAddr64) \ 64 DO(UnDecorateSymbolName) \ 65 DO(SymSetSearchPath) \ 66 DO(SymGetSearchPath) \ 67 DO(StackWalk64) \ 68 DO(SymFunctionTableAccess64) \ 69 DO(SymGetModuleBase64) \ 70 DO(MiniDumpWriteDump) \ 71 DO(SymGetLineFromAddr64) 72 73 74 #define DECLARE_FUNCTION_POINTER(functionname) \ 75 static pfn_##functionname g_pfn_##functionname; 76 77 FOR_ALL_FUNCTIONS(DECLARE_FUNCTION_POINTER) 78 79 80 static HMODULE g_dll_handle = NULL; 81 static DWORD g_dll_load_error = 0; 82 static API_VERSION g_version = { 0, 0, 0, 0 }; 83 84 static enum { 85 state_uninitialized = 0, 86 state_ready = 1, 87 state_error = 2 88 } g_state = state_uninitialized; 89 90 static void initialize() { 91 92 assert(g_state == state_uninitialized, "wrong sequence"); 93 g_state = state_error; 94 95 g_dll_handle = ::LoadLibrary("DBGHELP.DLL"); 96 if (g_dll_handle == NULL) { 97 g_dll_load_error = ::GetLastError(); 98 } else { 99 // Note: We loaded the DLL successfully. From here on we count 100 // initialization as success. We still may fail to load all of the 101 // desired function pointers successfully, but DLL may still be usable 102 // enough for our purposes. 103 g_state = state_ready; 104 105 #define DO_RESOLVE(functionname) \ 106 g_pfn_##functionname = (pfn_##functionname) ::GetProcAddress(g_dll_handle, #functionname); 107 108 FOR_ALL_FUNCTIONS(DO_RESOLVE) 109 110 // Retrieve version information. 111 if (g_pfn_ImagehlpApiVersion) { 112 const API_VERSION* p = g_pfn_ImagehlpApiVersion(); 113 memcpy(&g_version, p, sizeof(API_VERSION)); 114 } 115 } 116 117 } 118 119 120 ///////////////////// External functions ////////////////////////// 121 122 // All outside facing functions are synchronized. Also, we run 123 // initialization on first touch. 124 125 static CRITICAL_SECTION g_cs; 126 127 namespace { // Do not export. 128 class WindowsDbgHelpEntry { 129 public: 130 WindowsDbgHelpEntry() { 131 ::EnterCriticalSection(&g_cs); 132 if (g_state == state_uninitialized) { 133 initialize(); 134 } 135 } 136 ~WindowsDbgHelpEntry() { 137 ::LeaveCriticalSection(&g_cs); 138 } 139 }; 140 } 141 142 // Called at DLL_PROCESS_ATTACH. 143 void WindowsDbgHelp::pre_initialize() { 144 ::InitializeCriticalSection(&g_cs); 145 } 146 147 DWORD WindowsDbgHelp::symSetOptions(DWORD arg) { 148 WindowsDbgHelpEntry entry_guard; 149 if (g_pfn_SymSetOptions != NULL) { 150 return g_pfn_SymSetOptions(arg); 151 } 152 return 0; 153 } 154 155 DWORD WindowsDbgHelp::symGetOptions(void) { 156 WindowsDbgHelpEntry entry_guard; 157 if (g_pfn_SymGetOptions != NULL) { 158 return g_pfn_SymGetOptions(); 159 } 160 return 0; 161 } 162 163 BOOL WindowsDbgHelp::symInitialize(HANDLE hProcess, PCTSTR UserSearchPath, BOOL fInvadeProcess) { 164 WindowsDbgHelpEntry entry_guard; 165 if (g_pfn_SymInitialize != NULL) { 166 return g_pfn_SymInitialize(hProcess, UserSearchPath, fInvadeProcess); 167 } 168 return FALSE; 169 } 170 171 BOOL WindowsDbgHelp::symGetSymFromAddr64(HANDLE hProcess, DWORD64 the_address, 172 PDWORD64 Displacement, PIMAGEHLP_SYMBOL64 Symbol) { 173 WindowsDbgHelpEntry entry_guard; 174 if (g_pfn_SymGetSymFromAddr64 != NULL) { 175 return g_pfn_SymGetSymFromAddr64(hProcess, the_address, Displacement, Symbol); 176 } 177 return FALSE; 178 } 179 180 DWORD WindowsDbgHelp::unDecorateSymbolName(const char* DecoratedName, char* UnDecoratedName, 181 DWORD UndecoratedLength, DWORD Flags) { 182 WindowsDbgHelpEntry entry_guard; 183 if (g_pfn_UnDecorateSymbolName != NULL) { 184 return g_pfn_UnDecorateSymbolName(DecoratedName, UnDecoratedName, UndecoratedLength, Flags); 185 } 186 if (UnDecoratedName != NULL && UndecoratedLength > 0) { 187 UnDecoratedName[0] = '\0'; 188 } 189 return 0; 190 } 191 192 BOOL WindowsDbgHelp::symSetSearchPath(HANDLE hProcess, PCTSTR SearchPath) { 193 WindowsDbgHelpEntry entry_guard; 194 if (g_pfn_SymSetSearchPath != NULL) { 195 return g_pfn_SymSetSearchPath(hProcess, SearchPath); 196 } 197 return FALSE; 198 } 199 200 BOOL WindowsDbgHelp::symGetSearchPath(HANDLE hProcess, PTSTR SearchPath, int SearchPathLength) { 201 WindowsDbgHelpEntry entry_guard; 202 if (g_pfn_SymGetSearchPath != NULL) { 203 return g_pfn_SymGetSearchPath(hProcess, SearchPath, SearchPathLength); 204 } 205 return FALSE; 206 } 207 208 BOOL WindowsDbgHelp::stackWalk64(DWORD MachineType, 209 HANDLE hProcess, 210 HANDLE hThread, 211 LPSTACKFRAME64 StackFrame, 212 PVOID ContextRecord) { 213 WindowsDbgHelpEntry entry_guard; 214 if (g_pfn_StackWalk64 != NULL) { 215 return g_pfn_StackWalk64(MachineType, hProcess, hThread, StackFrame, 216 ContextRecord, 217 NULL, // ReadMemoryRoutine 218 g_pfn_SymFunctionTableAccess64, // FunctionTableAccessRoutine, 219 g_pfn_SymGetModuleBase64, // GetModuleBaseRoutine 220 NULL // TranslateAddressRoutine 221 ); 222 } 223 return FALSE; 224 } 225 226 PVOID WindowsDbgHelp::symFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase) { 227 WindowsDbgHelpEntry entry_guard; 228 if (g_pfn_SymFunctionTableAccess64 != NULL) { 229 return g_pfn_SymFunctionTableAccess64(hProcess, AddrBase); 230 } 231 return NULL; 232 } 233 234 DWORD64 WindowsDbgHelp::symGetModuleBase64(HANDLE hProcess, DWORD64 dwAddr) { 235 WindowsDbgHelpEntry entry_guard; 236 if (g_pfn_SymGetModuleBase64 != NULL) { 237 return g_pfn_SymGetModuleBase64(hProcess, dwAddr); 238 } 239 return 0; 240 } 241 242 BOOL WindowsDbgHelp::miniDumpWriteDump(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, 243 MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 244 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 245 PMINIDUMP_CALLBACK_INFORMATION CallbackParam) { 246 WindowsDbgHelpEntry entry_guard; 247 if (g_pfn_MiniDumpWriteDump != NULL) { 248 return g_pfn_MiniDumpWriteDump(hProcess, ProcessId, hFile, DumpType, 249 ExceptionParam, UserStreamParam, CallbackParam); 250 } 251 return FALSE; 252 } 253 254 BOOL WindowsDbgHelp::symGetLineFromAddr64(HANDLE hProcess, DWORD64 dwAddr, 255 PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line) { 256 WindowsDbgHelpEntry entry_guard; 257 if (g_pfn_SymGetLineFromAddr64 != NULL) { 258 return g_pfn_SymGetLineFromAddr64(hProcess, dwAddr, pdwDisplacement, Line); 259 } 260 return FALSE; 261 } 262 263 // Print one liner describing state (if library loaded, which functions are 264 // missing - if any, and the dbhelp API version) 265 void WindowsDbgHelp::print_state_on(outputStream* st) { 266 // Note: We should not lock while printing, but this should be 267 // safe to do without lock anyway. 268 st->print("dbghelp: "); 269 270 if (g_state == state_uninitialized) { 271 st->print("uninitialized."); 272 } else if (g_state == state_error) { 273 st->print("loading error: %u", g_dll_load_error); 274 } else { 275 st->print("loaded successfully "); 276 277 // We may want to print dll file name here - which may be interesting for 278 // cases where more than one version exists on the system, e.g. with a 279 // debugging sdk separately installed. But we get the file name in the DLL 280 // section of the hs-err file too, so this may be redundant. 281 282 // Print version. 283 st->print("- version: %u.%u.%u", 284 g_version.MajorVersion, g_version.MinorVersion, g_version.Revision); 285 286 // Print any functions which failed to load. 287 int num_missing = 0; 288 st->print(" - missing functions: "); 289 290 #define CHECK_AND_PRINT_IF_NULL(functionname) \ 291 if (g_pfn_##functionname == NULL) { \ 292 st->print("%s" #functionname, ((num_missing > 0) ? ", " : "")); \ 293 num_missing ++; \ 294 } 295 296 FOR_ALL_FUNCTIONS(CHECK_AND_PRINT_IF_NULL) 297 298 if (num_missing == 0) { 299 st->print("none"); 300 } 301 } 302 st->cr(); 303 } 304