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