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