/* BOOTSTRAP.C (c) Copyright Ivan Warren, 2003-2012 */ /* (c) Copyright "Fish" (David B. Trout), 2005-2012 */ /* Hercules executable main module */ /* */ /* Released under "The Q Public License Version 1" */ /* (http://www.hercules-390.org/herclic.html) as modifications to */ /* Hercules. */ /*-------------------------------------------------------------------*/ /* This module is the initial entry point of the Hercules emulator. */ /* The main() function performs platform-specific functions before */ /* calling the impl function which launches the emulator. */ /*-------------------------------------------------------------------*/ #include "hstdinc.h" #include "hercules.h" #if defined(HDL_USE_LIBTOOL) #include "ltdl.h" #endif #if !defined( _MSVC_ ) /*-------------------------------------------------------------------*/ /* For Unix-like platforms, the main() function: */ /* - sets the privilege level */ /* - initializes the LIBTOOL environment */ /* - passes control to the impl() function in impl.c */ /*-------------------------------------------------------------------*/ int main(int ac,char *av[]) { DROP_PRIVILEGES(CAP_SYS_NICE); SET_THREAD_NAME("bootstrap"); #if defined( OPTION_DYNAMIC_LOAD ) && defined( HDL_USE_LIBTOOL ) LTDL_SET_PRELOADED_SYMBOLS(); #endif exit(impl(ac,av)); } #else // defined( _MSVC_ ) /*-------------------------------------------------------------------*/ /* For Windows platforms, the main() function: */ /* - disables the standard CRT invalid parameter handler */ /* - requests a minimum resolution for periodic timers */ /* - sets up an exception trap */ /* - passes control to the impl() function in impl.c */ /* */ /* The purpose of the exception trap is to call a function which */ /* will write a minidump file in the event of a Hercules crash. */ /* */ /*-------------------------------------------------------------------*/ /* */ /* ****************************************** */ /* ** IMPORTANT! PLEASE NOTE! ** */ /* ****************************************** */ /* */ /* Without a compatible version of DBGHELP.dll to use, the below */ /* exception handling logic accomplishes absolutely *NOTHING*. */ /* */ /* The below exception handling requires DBGHELP.DLL version 6.1 */ /* or greater. Windows XP is known to ship a version of DbgHelp */ /* that is too old (version 5.1). Hercules *SHOULD* be shipping */ /* DbgHelp.DLL as part of its distribution. The latest version */ /* of the DbgHelp redistributable DLL can be downloaded as part */ /* of Microsoft's "Debugging Tools for Windows" package at the */ /* following URLs: */ /* */ /* http://msdn.microsoft.com/en-us/library/ms679294(VS.85).aspx */ /* http://go.microsoft.com/FWLink/?LinkId=84137 */ /* http://www.microsoft.com/whdc/devtools/debugging/default.mspx */ /* */ /* The DbgHelp.dll that ships in Windows is *NOT* redistributable. */ /* The "Debugging Tools for Windows" DbgHelp *IS* redistributable. */ /* */ /*-------------------------------------------------------------------*/ #pragma optimize( "", off ) // (this code should NOT be optimized!) typedef BOOL (MINIDUMPWRITEDUMPFUNC) ( HANDLE hProcess, DWORD ProcessId, HANDLE hDumpFile, MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam ); static MINIDUMPWRITEDUMPFUNC* g_pfnMiniDumpWriteDumpFunc = NULL; static HMODULE g_hDbgHelpDll = NULL; /////////////////////////////////////////////////////////////////////////////// // Global string buffers to prevent C4748 warning: "/GS can not protect // parameters and local variables from local buffer overrun because // optimizations are disabled in function" static WCHAR g_wszHercDrive [ 4 * _MAX_DRIVE ] = {0}; static WCHAR g_wszHercDir [ 4 * _MAX_DIR ] = {0}; static WCHAR g_wszFileDir [ 4 * _MAX_DIR ] = {0}; static WCHAR g_wszHercPath [ 4 * _MAX_PATH ] = {0}; static WCHAR g_wszDumpPath [ 4 * _MAX_PATH ] = {0}; static WCHAR g_wszFileName [ 4 * _MAX_FNAME ] = {0}; static TCHAR g_szSaveTitle[ 512 ] = {0}; static LPCTSTR g_pszTempTitle = _T("{98C1C303-2A9E-11d4-9FF5-0060677l8D04}"); /////////////////////////////////////////////////////////////////////////////// // (forward references) static LONG WINAPI HerculesUnhandledExceptionFilter( EXCEPTION_POINTERS* pExceptionPtrs ); static void ProcessException( EXCEPTION_POINTERS* pExceptionPtrs ); static BOOL CreateMiniDump( EXCEPTION_POINTERS* pExceptionPtrs ); static void BuildUserStreams( MINIDUMP_USER_STREAM_INFORMATION* pMDUSI ); static BOOL IsDataSectionNeeded( const WCHAR* pwszModuleName ); static BOOL CALLBACK MyMiniDumpCallback ( PVOID pParam, const PMINIDUMP_CALLBACK_INPUT pInput, PMINIDUMP_CALLBACK_OUTPUT pOutput ); static HWND FindConsoleHandle(); /////////////////////////////////////////////////////////////////////////////// // MessageBoxTimeout API support... #include // (need WINUSERAPI) #pragma comment(lib,"user32.lib") // (need user32.dll) #ifndef MB_TIMEDOUT #define MB_TIMEDOUT 32000 // (timed out return code) #endif #if defined( _UNICODE ) || defined( UNICODE ) int WINAPI MessageBoxTimeoutW ( IN HWND hWnd, IN LPCWSTR lpText, IN LPCWSTR lpCaption, IN UINT uType, IN WORD wLanguageId, IN DWORD dwMilliseconds ); #define MessageBoxTimeout MessageBoxTimeoutW #else // not unicode int WINAPI MessageBoxTimeoutA ( IN HWND hWnd, IN LPCSTR lpText, IN LPCSTR lpCaption, IN UINT uType, IN WORD wLanguageId, IN DWORD dwMilliseconds ); #define MessageBoxTimeout MessageBoxTimeoutA #endif // unicode or not /////////////////////////////////////////////////////////////////////////////// #include // (timeBeginPeriod, timeEndPeriod) #pragma comment( lib, "Winmm" ) // (timeBeginPeriod, timeEndPeriod) int main(int ac,char *av[]) { int rc = 0; SET_THREAD_NAME("bootstrap"); // Disable default invalid crt parameter handling DISABLE_CRT_INVALID_PARAMETER_HANDLER(); // Request the highest possible time-interval accuracy... timeBeginPeriod( 1 ); // (one millisecond time interval accuracy) // If we're being debugged, then let the debugger // catch the exception. Otherwise, let our exception // handler catch it... if (!IsDebuggerPresent()) { if (!sysblk.daemon_mode) // (normal panel mode?) { EnableMenuItem( GetSystemMenu( FindConsoleHandle(), FALSE ), SC_CLOSE, MF_BYCOMMAND | MF_GRAYED ); } if (1 && (g_hDbgHelpDll = LoadLibrary(_T("DbgHelp.dll"))) && (g_pfnMiniDumpWriteDumpFunc = (MINIDUMPWRITEDUMPFUNC*) GetProcAddress( g_hDbgHelpDll, _T("MiniDumpWriteDump"))) ) { GetModuleFileNameW( NULL, g_wszHercPath, _countof( g_wszHercPath ) ); _wsplitpath( g_wszHercPath, g_wszHercDrive, g_wszHercDir, NULL, NULL ); } SetUnhandledExceptionFilter( HerculesUnhandledExceptionFilter ); SetErrorMode( SEM_NOGPFAULTERRORBOX ); } rc = impl(ac,av); // (Hercules, do your thing!) // Each call to "timeBeginPeriod" must be matched with a call to "timeEndPeriod" timeEndPeriod( 1 ); // (no longer care about accurate time intervals) if (!IsDebuggerPresent()) { if (!sysblk.daemon_mode) // (normal panel mode?) { EnableMenuItem( GetSystemMenu( FindConsoleHandle(), FALSE ), SC_CLOSE, MF_BYCOMMAND | MF_ENABLED ); } } return rc; } /////////////////////////////////////////////////////////////////////////////// // Hercules unhandled exception filter... #include // (need SetConsoleMode, etc) #pragma optimize( "", off ) static LONG WINAPI HerculesUnhandledExceptionFilter( EXCEPTION_POINTERS* pExceptionPtrs ) { static BOOL bDidThis = FALSE; // (if we did this once already) if (bDidThis) return EXCEPTION_EXECUTE_HANDLER; // (quick exit to prevent loop) bDidThis = TRUE; SetErrorMode( 0 ); // (reset back to default handling) if (sysblk.daemon_mode) // (is an external GUI in control?) { fflush( stdout ); fflush( stderr ); _ftprintf( stderr, _T("]!OOPS!\n") ); // (external GUI pre-processing...) fflush( stdout ); fflush( stderr ); Sleep( 10 ); } else { // Normal panel mode: reset console mode and clear the screen... DWORD dwCellsWritten; CONSOLE_SCREEN_BUFFER_INFO csbi; HANDLE hStdIn, hStdErr; COORD ptConsole = { 0, 0 }; EnableMenuItem( GetSystemMenu( FindConsoleHandle(), FALSE ), SC_CLOSE, MF_BYCOMMAND | MF_ENABLED ); hStdIn = GetStdHandle( STD_INPUT_HANDLE ); hStdErr = GetStdHandle( STD_ERROR_HANDLE ); #define DEFAULT_CONSOLE_ATTRIBUTES (0 \ | FOREGROUND_RED \ | FOREGROUND_GREEN \ | FOREGROUND_BLUE \ ) /* FIXME these are defined in SDK V6+ */ #ifndef ENABLE_INSERT_MODE #define ENABLE_INSERT_MODE 0 #endif #ifndef ENABLE_QUICK_EDIT_MODE #define ENABLE_QUICK_EDIT_MODE 0 #endif #define DEFAULT_CONSOLE_INPUT_MODE (0 \ | ENABLE_ECHO_INPUT \ | ENABLE_INSERT_MODE \ | ENABLE_LINE_INPUT \ | ENABLE_MOUSE_INPUT \ | ENABLE_PROCESSED_INPUT \ | ENABLE_QUICK_EDIT_MODE \ ) #define DEFAULT_CONSOLE_OUTPUT_MODE (0 \ | ENABLE_PROCESSED_OUTPUT \ | ENABLE_WRAP_AT_EOL_OUTPUT \ ) SetConsoleTextAttribute( hStdErr, DEFAULT_CONSOLE_ATTRIBUTES ); SetConsoleMode ( hStdIn, DEFAULT_CONSOLE_INPUT_MODE ); SetConsoleMode ( hStdErr, DEFAULT_CONSOLE_OUTPUT_MODE ); GetConsoleScreenBufferInfo( hStdErr, &csbi ); FillConsoleOutputCharacter( hStdErr, ' ', csbi.dwSize.X * csbi.dwSize.Y, ptConsole, &dwCellsWritten ); GetConsoleScreenBufferInfo( hStdErr, &csbi ); FillConsoleOutputAttribute( hStdErr, csbi.wAttributes, csbi.dwSize.X * csbi.dwSize.Y, ptConsole, &dwCellsWritten ); SetConsoleCursorPosition ( hStdErr, ptConsole ); fflush( stdout ); fflush( stderr ); Sleep( 10 ); } _tprintf( _T("\n\n") ); _tprintf( _T(" ***************\n") ); _tprintf( _T(" * OOPS! *\n") ); _tprintf( _T(" ***************\n") ); _tprintf( _T("\n") ); _tprintf( _T(" Hercules has crashed!\n") ); _tprintf( _T("\n") ); _tprintf( _T("(you may or may not need to press ENTER if no 'oops!' dialog-box appears)\n") ); _tprintf( _T("\n") ); fflush( stdout ); fflush( stderr ); Sleep( 10 ); ProcessException( pExceptionPtrs ); // (create a minidump, if possible) fflush( stdout ); fflush( stderr ); Sleep( 10 ); timeEndPeriod( 1 ); // (reset to default time interval) return EXCEPTION_EXECUTE_HANDLER; // (quite likely exits the process) } /////////////////////////////////////////////////////////////////////////////// static HWND FindConsoleHandle() { HWND hWnd; if (!GetConsoleTitle( g_szSaveTitle, _countof( g_szSaveTitle ))) return NULL; if (!SetConsoleTitle( g_pszTempTitle )) return NULL; Sleep(20); hWnd = FindWindow( NULL, g_pszTempTitle ); SetConsoleTitle( g_szSaveTitle ); return hWnd; } /////////////////////////////////////////////////////////////////////////////// #define USE_MSGBOX_TIMEOUT // (to force default = yes take a minidump) #ifdef USE_MSGBOX_TIMEOUT #define MESSAGEBOX MessageBoxTimeout #else #define MESSAGEBOX MessageBox #endif /////////////////////////////////////////////////////////////////////////////// static inline int MsgBox( HWND hWnd, LPCTSTR pszText, LPCTSTR pszCaption, UINT uType, WORD wLanguageId, DWORD dwMilliseconds ) { return MESSAGEBOX( hWnd, pszText, pszCaption, uType #ifdef USE_MSGBOX_TIMEOUT , wLanguageId , dwMilliseconds #endif ); } /////////////////////////////////////////////////////////////////////////////// static void ProcessException( EXCEPTION_POINTERS* pExceptionPtrs ) { int rc; UINT uiMBFlags = 0 | MB_SYSTEMMODAL | MB_TOPMOST | MB_SETFOREGROUND ; HWND hwndMBOwner = FindConsoleHandle(); if (!hwndMBOwner || !IsWindowVisible(hwndMBOwner)) hwndMBOwner = GetDesktopWindow(); if ( !g_pfnMiniDumpWriteDumpFunc ) { MsgBox ( hwndMBOwner, _T("The creation of a crash dump for analysis by the Hercules ") _T("development team is NOT possible\nbecause the required 'DbgHelp.dll' ") _T("is missing or is not installed or was otherwise not located.") ,_T("OOPS! Hercules has crashed!"), uiMBFlags | MB_ICONERROR | MB_OK ,0 // (default/neutral language) ,(15 * 1000) // (timeout == 15 seconds) ); return; } if ( IDYES == ( rc = MsgBox ( hwndMBOwner, _T("The creation of a crash dump for further analysis by ") _T("the Hercules development team is strongly suggested.\n\n") _T("Would you like to create a crash dump for ") _T("the Hercules development team to analyze?") ,_T("OOPS! Hercules has crashed!"), uiMBFlags | MB_ICONERROR | MB_YESNO ,0 // (default/neutral language) ,(10 * 1000) // (timeout == 10 seconds) )) || MB_TIMEDOUT == rc ) { if ( CreateMiniDump( pExceptionPtrs ) && MB_TIMEDOUT != rc ) { MsgBox ( hwndMBOwner, _T("Please send the dump to the Hercules development team for analysis.") ,_T("Dump Complete"), uiMBFlags | MB_ICONEXCLAMATION | MB_OK ,0 // (default/neutral language) ,(5 * 1000) // (timeout == 5 seconds) ); } } } /////////////////////////////////////////////////////////////////////////////// // The following CreateMiniDump functions are based on // Oleg Starodumov's sample at http://www.debuginfo.com static BOOL CreateMiniDump( EXCEPTION_POINTERS* pExceptionPtrs ) { BOOL bSuccess = FALSE; HANDLE hDumpFile; _wmakepath( g_wszDumpPath, g_wszHercDrive, g_wszHercDir, L"Hercules", L".dmp" ); _tprintf( _T("Creating crash dump \"%ls\"...\n\n"), g_wszDumpPath ); _tprintf( _T("Please wait; this may take a few minutes...\n\n") ); _tprintf( _T("(another message will appear when the dump is complete)\n\n") ); hDumpFile = CreateFileW ( g_wszDumpPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if ( hDumpFile && INVALID_HANDLE_VALUE != hDumpFile ) { // Create the minidump MINIDUMP_EXCEPTION_INFORMATION mdei; MINIDUMP_USER_STREAM_INFORMATION mdusi; MINIDUMP_CALLBACK_INFORMATION mci; MINIDUMP_TYPE mdt; BuildUserStreams( &mdusi ); mdei.ThreadId = GetCurrentThreadId(); mdei.ExceptionPointers = pExceptionPtrs; mdei.ClientPointers = FALSE; mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE) MyMiniDumpCallback; mci.CallbackParam = 0; mdt = (MINIDUMP_TYPE) (0 | MiniDumpWithPrivateReadWriteMemory | MiniDumpWithDataSegs | MiniDumpWithHandleData // | MiniDumpWithFullMemoryInfo // | MiniDumpWithThreadInfo | MiniDumpWithUnloadedModules ); bSuccess = g_pfnMiniDumpWriteDumpFunc( GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, mdt, (pExceptionPtrs != 0) ? &mdei : 0, &mdusi, &mci ); CloseHandle( hDumpFile ); if ( bSuccess ) { _tprintf( _T("\nDump \"%ls\" created.\n\n" "Please forward the dump to the Hercules team for analysis.\n\n"), g_wszDumpPath ); } else _tprintf( _T("\nMiniDumpWriteDump failed! Error: %u\n\n"), GetLastError() ); } else { _tprintf( _T("\nCreateFile failed! Error: %u\n\n"), GetLastError() ); } return bSuccess; } /////////////////////////////////////////////////////////////////////////////// // Build User Stream Arrays... #define MAX_MINIDUMP_USER_STREAMS (64) static char g_host_info_str [ 1024 ]; static MINIDUMP_USER_STREAM UserStreamArray [ MAX_MINIDUMP_USER_STREAMS ]; static void BuildUserStreams( MINIDUMP_USER_STREAM_INFORMATION* pMDUSI ) { const char** ppszBldInfoStr; int nNumBldInfoStrs; ULONG UserStreamCount; _ASSERTE( pMDUSI ); get_hostinfo_str( NULL, g_host_info_str, sizeof(g_host_info_str) ); nNumBldInfoStrs = get_buildinfo_strings( &ppszBldInfoStr ); UserStreamCount = min( (3+nNumBldInfoStrs), MAX_MINIDUMP_USER_STREAMS ); pMDUSI->UserStreamCount = UserStreamCount; pMDUSI->UserStreamArray = UserStreamArray; UserStreamCount = 0; if ( UserStreamCount < pMDUSI->UserStreamCount ) { UserStreamArray[UserStreamCount].Type = CommentStreamA; UserStreamArray[UserStreamCount].Buffer = VERSION; UserStreamArray[UserStreamCount].BufferSize = sizeof(VERSION); UserStreamCount++; } if ( UserStreamCount < pMDUSI->UserStreamCount ) { UserStreamArray[UserStreamCount].Type = CommentStreamA; UserStreamArray[UserStreamCount].Buffer = HERCULES_COPYRIGHT; UserStreamArray[UserStreamCount].BufferSize = sizeof(HERCULES_COPYRIGHT); UserStreamCount++; } if ( UserStreamCount < pMDUSI->UserStreamCount ) { UserStreamArray[UserStreamCount].Type = CommentStreamA; UserStreamArray[UserStreamCount].Buffer = g_host_info_str; UserStreamArray[UserStreamCount].BufferSize = (ULONG)strlen(g_host_info_str)+1; UserStreamCount++; } for (; nNumBldInfoStrs && UserStreamCount < pMDUSI->UserStreamCount; nNumBldInfoStrs--, UserStreamCount++, ppszBldInfoStr++ ) { UserStreamArray[UserStreamCount].Type = CommentStreamA; UserStreamArray[UserStreamCount].Buffer = (PVOID)*ppszBldInfoStr; UserStreamArray[UserStreamCount].BufferSize = (ULONG)strlen(*ppszBldInfoStr)+1; } } /////////////////////////////////////////////////////////////////////////////// // Custom minidump callback static BOOL CALLBACK MyMiniDumpCallback ( PVOID pParam, const PMINIDUMP_CALLBACK_INPUT pInput, PMINIDUMP_CALLBACK_OUTPUT pOutput ) { BOOL bRet = FALSE; if ( !pInput || !pOutput ) return FALSE; switch ( pInput->CallbackType ) { case IncludeModuleCallback: { // Include the module into the dump bRet = TRUE; } break; case IncludeThreadCallback: { // Include the thread into the dump bRet = TRUE; } break; case ModuleCallback: { // Are data sections available for this module ? if ( pOutput->ModuleWriteFlags & ModuleWriteDataSeg ) { // Yes, but do we really need them? if ( !IsDataSectionNeeded( pInput->Module.FullPath ) ) pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg); } bRet = TRUE; } break; case ThreadCallback: { // Include all thread information into the minidump bRet = TRUE; } break; case ThreadExCallback: { // Include this information bRet = TRUE; } break; /* NOTE About MemoryCallback : * This is defined for DbgHelp > 6.1.. * Since "false" is returned, it has been commented out. * * Additionally, false is now returned by default. This * ensures that the callback function will operate correctly * even with future versions of the DbhHelp DLL. * -- Ivan */ // case MemoryCallback: // { // // We do not include any information here -> return FALSE // bRet = FALSE; // } // break; // Following default block added by ISW 2005/05/06 default: { // Do not return any information for unrecognized // callback types. bRet=FALSE; } break; // case CancelCallback: // break; } return bRet; } /////////////////////////////////////////////////////////////////////////////// // This function determines whether we need data sections of the given module static BOOL IsDataSectionNeeded( const WCHAR* pwszModuleName ) { BOOL bNeeded = FALSE; _ASSERTE( pwszModuleName ); _wsplitpath( pwszModuleName, NULL, g_wszFileDir, g_wszFileName, NULL ); if ( _wcsicmp( g_wszFileName, L"ntdll" ) == 0 ) { bNeeded = TRUE; } else if ( _wcsicmp( g_wszFileDir, g_wszHercDir ) == 0 ) { bNeeded = TRUE; } return bNeeded; } /////////////////////////////////////////////////////////////////////////////// #pragma optimize( "", on ) // (we're done; re-enable optimizations) #endif // !defined( _MSVC_ )