1//===- Win32/Signals.cpp - Win32 Signals Implementation ---------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file provides the Win32 specific implementation of the Signals class. 11// 12//===----------------------------------------------------------------------===// 13 14#include "llvm/Support/FileSystem.h" 15#include <algorithm> 16#include <stdio.h> 17#include <vector> 18 19// The Windows.h header must be after LLVM and standard headers. 20#include "WindowsSupport.h" 21 22#ifdef __MINGW32__ 23 #include <imagehlp.h> 24#else 25 #include <dbghelp.h> 26#endif 27#include <psapi.h> 28 29#ifdef _MSC_VER 30 #pragma comment(lib, "psapi.lib") 31 #pragma comment(lib, "dbghelp.lib") 32#elif __MINGW32__ 33 #if ((HAVE_LIBIMAGEHLP != 1) || (HAVE_LIBPSAPI != 1)) 34 #error "libimagehlp.a & libpsapi.a should be present" 35 #endif 36 // The version of g++ that comes with MinGW does *not* properly understand 37 // the ll format specifier for printf. However, MinGW passes the format 38 // specifiers on to the MSVCRT entirely, and the CRT understands the ll 39 // specifier. So these warnings are spurious in this case. Since we compile 40 // with -Wall, this will generate these warnings which should be ignored. So 41 // we will turn off the warnings for this just file. However, MinGW also does 42 // not support push and pop for diagnostics, so we have to manually turn it 43 // back on at the end of the file. 44 #pragma GCC diagnostic ignored "-Wformat" 45 #pragma GCC diagnostic ignored "-Wformat-extra-args" 46 47 #if !defined(__MINGW64_VERSION_MAJOR) 48 // MinGW.org does not have updated support for the 64-bit versions of the 49 // DebugHlp APIs. So we will have to load them manually. The structures and 50 // method signatures were pulled from DbgHelp.h in the Windows Platform SDK, 51 // and adjusted for brevity. 52 typedef struct _IMAGEHLP_LINE64 { 53 DWORD SizeOfStruct; 54 PVOID Key; 55 DWORD LineNumber; 56 PCHAR FileName; 57 DWORD64 Address; 58 } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64; 59 60 typedef struct _IMAGEHLP_SYMBOL64 { 61 DWORD SizeOfStruct; 62 DWORD64 Address; 63 DWORD Size; 64 DWORD Flags; 65 DWORD MaxNameLength; 66 CHAR Name[1]; 67 } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64; 68 69 typedef struct _tagADDRESS64 { 70 DWORD64 Offset; 71 WORD Segment; 72 ADDRESS_MODE Mode; 73 } ADDRESS64, *LPADDRESS64; 74 75 typedef struct _KDHELP64 { 76 DWORD64 Thread; 77 DWORD ThCallbackStack; 78 DWORD ThCallbackBStore; 79 DWORD NextCallback; 80 DWORD FramePointer; 81 DWORD64 KiCallUserMode; 82 DWORD64 KeUserCallbackDispatcher; 83 DWORD64 SystemRangeStart; 84 DWORD64 KiUserExceptionDispatcher; 85 DWORD64 StackBase; 86 DWORD64 StackLimit; 87 DWORD64 Reserved[5]; 88 } KDHELP64, *PKDHELP64; 89 90 typedef struct _tagSTACKFRAME64 { 91 ADDRESS64 AddrPC; 92 ADDRESS64 AddrReturn; 93 ADDRESS64 AddrFrame; 94 ADDRESS64 AddrStack; 95 ADDRESS64 AddrBStore; 96 PVOID FuncTableEntry; 97 DWORD64 Params[4]; 98 BOOL Far; 99 BOOL Virtual; 100 DWORD64 Reserved[3]; 101 KDHELP64 KdHelp; 102 } STACKFRAME64, *LPSTACKFRAME64; 103 104typedef BOOL (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(HANDLE hProcess, 105 DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, 106 LPDWORD lpNumberOfBytesRead); 107 108typedef PVOID (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)( HANDLE ahProcess, 109 DWORD64 AddrBase); 110 111typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess, 112 DWORD64 Address); 113 114typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess, 115 HANDLE hThread, LPADDRESS64 lpaddr); 116 117typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, 118 PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, 119 PFUNCTION_TABLE_ACCESS_ROUTINE64, 120 PGET_MODULE_BASE_ROUTINE64, 121 PTRANSLATE_ADDRESS_ROUTINE64); 122static fpStackWalk64 StackWalk64; 123 124typedef DWORD64 (WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64); 125static fpSymGetModuleBase64 SymGetModuleBase64; 126 127typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64, 128 PDWORD64, PIMAGEHLP_SYMBOL64); 129static fpSymGetSymFromAddr64 SymGetSymFromAddr64; 130 131typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64, 132 PDWORD, PIMAGEHLP_LINE64); 133static fpSymGetLineFromAddr64 SymGetLineFromAddr64; 134 135typedef PVOID (WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64); 136static fpSymFunctionTableAccess64 SymFunctionTableAccess64; 137 138static bool load64BitDebugHelp(void) { 139 HMODULE hLib = ::LoadLibraryW(L"Dbghelp.dll"); 140 if (hLib) { 141 StackWalk64 = (fpStackWalk64) 142 ::GetProcAddress(hLib, "StackWalk64"); 143 SymGetModuleBase64 = (fpSymGetModuleBase64) 144 ::GetProcAddress(hLib, "SymGetModuleBase64"); 145 SymGetSymFromAddr64 = (fpSymGetSymFromAddr64) 146 ::GetProcAddress(hLib, "SymGetSymFromAddr64"); 147 SymGetLineFromAddr64 = (fpSymGetLineFromAddr64) 148 ::GetProcAddress(hLib, "SymGetLineFromAddr64"); 149 SymFunctionTableAccess64 = (fpSymFunctionTableAccess64) 150 ::GetProcAddress(hLib, "SymFunctionTableAccess64"); 151 } 152 return StackWalk64 != NULL; 153} 154 #endif // !defined(__MINGW64_VERSION_MAJOR) 155#endif // __MINGW32__ 156 157// Forward declare. 158static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep); 159static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType); 160 161// InterruptFunction - The function to call if ctrl-c is pressed. 162static void (*InterruptFunction)() = 0; 163 164static std::vector<std::string> *FilesToRemove = NULL; 165static std::vector<std::pair<void(*)(void*), void*> > *CallBacksToRun = 0; 166static bool RegisteredUnhandledExceptionFilter = false; 167static bool CleanupExecuted = false; 168static bool ExitOnUnhandledExceptions = false; 169static PTOP_LEVEL_EXCEPTION_FILTER OldFilter = NULL; 170 171// Windows creates a new thread to execute the console handler when an event 172// (such as CTRL/C) occurs. This causes concurrency issues with the above 173// globals which this critical section addresses. 174static CRITICAL_SECTION CriticalSection; 175 176namespace llvm { 177 178//===----------------------------------------------------------------------===// 179//=== WARNING: Implementation here must contain only Win32 specific code 180//=== and must not be UNIX code 181//===----------------------------------------------------------------------===// 182 183#ifdef _MSC_VER 184/// AvoidMessageBoxHook - Emulates hitting "retry" from an "abort, retry, 185/// ignore" CRT debug report dialog. "retry" raises an exception which 186/// ultimately triggers our stack dumper. 187static int AvoidMessageBoxHook(int ReportType, char *Message, int *Return) { 188 // Set *Return to the retry code for the return value of _CrtDbgReport: 189 // http://msdn.microsoft.com/en-us/library/8hyw4sy7(v=vs.71).aspx 190 // This may also trigger just-in-time debugging via DebugBreak(). 191 if (Return) 192 *Return = 1; 193 // Don't call _CrtDbgReport. 194 return TRUE; 195} 196 197#endif 198 199static void RegisterHandler() { 200#if __MINGW32__ && !defined(__MINGW64_VERSION_MAJOR) 201 // On MinGW.org, we need to load up the symbols explicitly, because the 202 // Win32 framework they include does not have support for the 64-bit 203 // versions of the APIs we need. If we cannot load up the APIs (which 204 // would be unexpected as they should exist on every version of Windows 205 // we support), we will bail out since there would be nothing to report. 206 if (!load64BitDebugHelp()) { 207 assert(false && "These APIs should always be available"); 208 return; 209 } 210#endif 211 212 if (RegisteredUnhandledExceptionFilter) { 213 EnterCriticalSection(&CriticalSection); 214 return; 215 } 216 217 // Now's the time to create the critical section. This is the first time 218 // through here, and there's only one thread. 219 InitializeCriticalSection(&CriticalSection); 220 221 // Enter it immediately. Now if someone hits CTRL/C, the console handler 222 // can't proceed until the globals are updated. 223 EnterCriticalSection(&CriticalSection); 224 225 RegisteredUnhandledExceptionFilter = true; 226 OldFilter = SetUnhandledExceptionFilter(LLVMUnhandledExceptionFilter); 227 SetConsoleCtrlHandler(LLVMConsoleCtrlHandler, TRUE); 228 229 // Environment variable to disable any kind of crash dialog. 230 if (getenv("LLVM_DISABLE_CRASH_REPORT")) { 231#ifdef _MSC_VER 232 _CrtSetReportHook(AvoidMessageBoxHook); 233#endif 234 SetErrorMode(SEM_FAILCRITICALERRORS | 235 SEM_NOGPFAULTERRORBOX | 236 SEM_NOOPENFILEERRORBOX); 237 ExitOnUnhandledExceptions = true; 238 } 239 240 // IMPORTANT NOTE: Caller must call LeaveCriticalSection(&CriticalSection) or 241 // else multi-threading problems will ensue. 242} 243 244// RemoveFileOnSignal - The public API 245bool sys::RemoveFileOnSignal(StringRef Filename, std::string* ErrMsg) { 246 RegisterHandler(); 247 248 if (CleanupExecuted) { 249 if (ErrMsg) 250 *ErrMsg = "Process terminating -- cannot register for removal"; 251 return true; 252 } 253 254 if (FilesToRemove == NULL) 255 FilesToRemove = new std::vector<std::string>; 256 257 FilesToRemove->push_back(Filename); 258 259 LeaveCriticalSection(&CriticalSection); 260 return false; 261} 262 263// DontRemoveFileOnSignal - The public API 264void sys::DontRemoveFileOnSignal(StringRef Filename) { 265 if (FilesToRemove == NULL) 266 return; 267 268 RegisterHandler(); 269 270 FilesToRemove->push_back(Filename); 271 std::vector<std::string>::reverse_iterator I = 272 std::find(FilesToRemove->rbegin(), FilesToRemove->rend(), Filename); 273 if (I != FilesToRemove->rend()) 274 FilesToRemove->erase(I.base()-1); 275 276 LeaveCriticalSection(&CriticalSection); 277} 278 279/// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or 280/// SIGSEGV) is delivered to the process, print a stack trace and then exit. 281void sys::PrintStackTraceOnErrorSignal() { 282 RegisterHandler(); 283 LeaveCriticalSection(&CriticalSection); 284} 285 286void llvm::sys::PrintStackTrace(FILE *) { 287 // FIXME: Implement. 288} 289 290 291void sys::SetInterruptFunction(void (*IF)()) { 292 RegisterHandler(); 293 InterruptFunction = IF; 294 LeaveCriticalSection(&CriticalSection); 295} 296 297 298/// AddSignalHandler - Add a function to be called when a signal is delivered 299/// to the process. The handler can have a cookie passed to it to identify 300/// what instance of the handler it is. 301void sys::AddSignalHandler(void (*FnPtr)(void *), void *Cookie) { 302 if (CallBacksToRun == 0) 303 CallBacksToRun = new std::vector<std::pair<void(*)(void*), void*> >(); 304 CallBacksToRun->push_back(std::make_pair(FnPtr, Cookie)); 305 RegisterHandler(); 306 LeaveCriticalSection(&CriticalSection); 307} 308} 309 310static void Cleanup() { 311 EnterCriticalSection(&CriticalSection); 312 313 // Prevent other thread from registering new files and directories for 314 // removal, should we be executing because of the console handler callback. 315 CleanupExecuted = true; 316 317 // FIXME: open files cannot be deleted. 318 319 if (FilesToRemove != NULL) 320 while (!FilesToRemove->empty()) { 321 llvm::sys::fs::remove(FilesToRemove->back()); 322 FilesToRemove->pop_back(); 323 } 324 325 if (CallBacksToRun) 326 for (unsigned i = 0, e = CallBacksToRun->size(); i != e; ++i) 327 (*CallBacksToRun)[i].first((*CallBacksToRun)[i].second); 328 329 LeaveCriticalSection(&CriticalSection); 330} 331 332void llvm::sys::RunInterruptHandlers() { 333 Cleanup(); 334} 335 336static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { 337 Cleanup(); 338 339 // Initialize the STACKFRAME structure. 340 STACKFRAME64 StackFrame; 341 memset(&StackFrame, 0, sizeof(StackFrame)); 342 343 DWORD machineType; 344#if defined(_M_X64) 345 machineType = IMAGE_FILE_MACHINE_AMD64; 346 StackFrame.AddrPC.Offset = ep->ContextRecord->Rip; 347 StackFrame.AddrPC.Mode = AddrModeFlat; 348 StackFrame.AddrStack.Offset = ep->ContextRecord->Rsp; 349 StackFrame.AddrStack.Mode = AddrModeFlat; 350 StackFrame.AddrFrame.Offset = ep->ContextRecord->Rbp; 351 StackFrame.AddrFrame.Mode = AddrModeFlat; 352#elif defined(_M_IX86) 353 machineType = IMAGE_FILE_MACHINE_I386; 354 StackFrame.AddrPC.Offset = ep->ContextRecord->Eip; 355 StackFrame.AddrPC.Mode = AddrModeFlat; 356 StackFrame.AddrStack.Offset = ep->ContextRecord->Esp; 357 StackFrame.AddrStack.Mode = AddrModeFlat; 358 StackFrame.AddrFrame.Offset = ep->ContextRecord->Ebp; 359 StackFrame.AddrFrame.Mode = AddrModeFlat; 360#endif 361 362 HANDLE hProcess = GetCurrentProcess(); 363 HANDLE hThread = GetCurrentThread(); 364 365 // Initialize the symbol handler. 366 SymSetOptions(SYMOPT_DEFERRED_LOADS|SYMOPT_LOAD_LINES); 367 SymInitialize(hProcess, NULL, TRUE); 368 369 while (true) { 370 if (!StackWalk64(machineType, hProcess, hThread, &StackFrame, 371 ep->ContextRecord, NULL, SymFunctionTableAccess64, 372 SymGetModuleBase64, NULL)) { 373 break; 374 } 375 376 if (StackFrame.AddrFrame.Offset == 0) 377 break; 378 379 // Print the PC in hexadecimal. 380 DWORD64 PC = StackFrame.AddrPC.Offset; 381#if defined(_M_X64) 382 fprintf(stderr, "0x%016llX", PC); 383#elif defined(_M_IX86) 384 fprintf(stderr, "0x%08lX", static_cast<DWORD>(PC)); 385#endif 386 387 // Print the parameters. Assume there are four. 388#if defined(_M_X64) 389 fprintf(stderr, " (0x%016llX 0x%016llX 0x%016llX 0x%016llX)", 390 StackFrame.Params[0], 391 StackFrame.Params[1], 392 StackFrame.Params[2], 393 StackFrame.Params[3]); 394#elif defined(_M_IX86) 395 fprintf(stderr, " (0x%08lX 0x%08lX 0x%08lX 0x%08lX)", 396 static_cast<DWORD>(StackFrame.Params[0]), 397 static_cast<DWORD>(StackFrame.Params[1]), 398 static_cast<DWORD>(StackFrame.Params[2]), 399 static_cast<DWORD>(StackFrame.Params[3])); 400#endif 401 // Verify the PC belongs to a module in this process. 402 if (!SymGetModuleBase64(hProcess, PC)) { 403 fputs(" <unknown module>\n", stderr); 404 continue; 405 } 406 407 // Print the symbol name. 408 char buffer[512]; 409 IMAGEHLP_SYMBOL64 *symbol = reinterpret_cast<IMAGEHLP_SYMBOL64 *>(buffer); 410 memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64)); 411 symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); 412 symbol->MaxNameLength = 512 - sizeof(IMAGEHLP_SYMBOL64); 413 414 DWORD64 dwDisp; 415 if (!SymGetSymFromAddr64(hProcess, PC, &dwDisp, symbol)) { 416 fputc('\n', stderr); 417 continue; 418 } 419 420 buffer[511] = 0; 421 if (dwDisp > 0) 422 fprintf(stderr, ", %s() + 0x%llX bytes(s)", symbol->Name, dwDisp); 423 else 424 fprintf(stderr, ", %s", symbol->Name); 425 426 // Print the source file and line number information. 427 IMAGEHLP_LINE64 line; 428 DWORD dwLineDisp; 429 memset(&line, 0, sizeof(line)); 430 line.SizeOfStruct = sizeof(line); 431 if (SymGetLineFromAddr64(hProcess, PC, &dwLineDisp, &line)) { 432 fprintf(stderr, ", %s, line %lu", line.FileName, line.LineNumber); 433 if (dwLineDisp > 0) 434 fprintf(stderr, " + 0x%lX byte(s)", dwLineDisp); 435 } 436 437 fputc('\n', stderr); 438 } 439 440 if (ExitOnUnhandledExceptions) 441 _exit(ep->ExceptionRecord->ExceptionCode); 442 443 // Allow dialog box to pop up allowing choice to start debugger. 444 if (OldFilter) 445 return (*OldFilter)(ep); 446 else 447 return EXCEPTION_CONTINUE_SEARCH; 448} 449 450static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType) { 451 // We are running in our very own thread, courtesy of Windows. 452 EnterCriticalSection(&CriticalSection); 453 Cleanup(); 454 455 // If an interrupt function has been set, go and run one it; otherwise, 456 // the process dies. 457 void (*IF)() = InterruptFunction; 458 InterruptFunction = 0; // Don't run it on another CTRL-C. 459 460 if (IF) { 461 // Note: if the interrupt function throws an exception, there is nothing 462 // to catch it in this thread so it will kill the process. 463 IF(); // Run it now. 464 LeaveCriticalSection(&CriticalSection); 465 return TRUE; // Don't kill the process. 466 } 467 468 // Allow normal processing to take place; i.e., the process dies. 469 LeaveCriticalSection(&CriticalSection); 470 return FALSE; 471} 472 473#if __MINGW32__ 474 // We turned these warnings off for this file so that MinGW-g++ doesn't 475 // complain about the ll format specifiers used. Now we are turning the 476 // warnings back on. If MinGW starts to support diagnostic stacks, we can 477 // replace this with a pop. 478 #pragma GCC diagnostic warning "-Wformat" 479 #pragma GCC diagnostic warning "-Wformat-extra-args" 480#endif 481