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