1 /* BOOTSTRAP.C  (c) Copyright Ivan Warren, 2003-2009                 */
2 /*              (c) Copyright "Fish" (David B. Trout), 2005-2009     */
3 /*              Hercules executable main module                      */
4 
5 /*-------------------------------------------------------------------*/
6 /* This module is the initial entry point of the Hercules emulator.  */
7 /* The main() function performs platform-specific functions before   */
8 /* calling the impl function which launches the emulator.            */
9 /*-------------------------------------------------------------------*/
10 
11 #include "hstdinc.h"
12 #include "hercules.h"
13 #if defined(HDL_USE_LIBTOOL)
14 #include "ltdl.h"
15 #endif
16 
17 #if !defined( _MSVC_ )
18 /*-------------------------------------------------------------------*/
19 /* For Unix-like platforms, the main() function:                     */
20 /* - sets the privilege level                                        */
21 /* - initializes the LIBTOOL environment                             */
22 /* - passes control to the impl() function in impl.c                 */
23 /*-------------------------------------------------------------------*/
main(int ac,char * av[])24 int main(int ac,char *av[])
25 {
26     DROP_PRIVILEGES(CAP_SYS_NICE);
27     SET_THREAD_NAME("bootstrap");
28 
29 #if defined( OPTION_DYNAMIC_LOAD ) && defined( HDL_USE_LIBTOOL )
30     LTDL_SET_PRELOADED_SYMBOLS();
31 #endif
32     exit(impl(ac,av));
33 }
34 
35 #else // defined( _MSVC_ )
36 /*-------------------------------------------------------------------*/
37 /* For Windows platforms, the main() function:                       */
38 /* - disables the standard CRT invalid parameter handler             */
39 /* - requests a minimum resolution for periodic timers               */
40 /* - sets up an exception trap                                       */
41 /* - passes control to the impl() function in impl.c                 */
42 /*                                                                   */
43 /* The purpose of the exception trap is to call a function which     */
44 /* will write a minidump file in the event of a Hercules crash.      */
45 /*-------------------------------------------------------------------*/
46 
47 #pragma optimize( "", off )
48 
49 typedef BOOL (MINIDUMPWRITEDUMPFUNC)
50 (
51     HANDLE                             hProcess,
52     DWORD                              ProcessId,
53     HANDLE                             hDumpFile,
54     MINIDUMP_TYPE                      DumpType,
55     PMINIDUMP_EXCEPTION_INFORMATION    ExceptionParam,
56     PMINIDUMP_USER_STREAM_INFORMATION  UserStreamParam,
57     PMINIDUMP_CALLBACK_INFORMATION     CallbackParam
58 );
59 
60 static MINIDUMPWRITEDUMPFUNC*  g_pfnMiniDumpWriteDumpFunc  = NULL;
61 static HMODULE                 g_hDbgHelpDll               = NULL;
62 
63 // Global string buffers to prevent C4748 warning: "/GS can not protect
64 // parameters and local variables from local buffer overrun because
65 // optimizations are disabled in function"
66 
67 static WCHAR  g_wszHercDrive [ 4 * _MAX_DRIVE ]  = {0};
68 static WCHAR  g_wszHercDir   [ 4 * _MAX_DIR   ]  = {0};
69 static WCHAR  g_wszFileDir   [ 4 * _MAX_DIR   ]  = {0};
70 static WCHAR  g_wszHercPath  [ 4 * _MAX_PATH  ]  = {0};
71 static WCHAR  g_wszDumpPath  [ 4 * _MAX_PATH  ]  = {0};
72 static WCHAR  g_wszFileName  [ 4 * _MAX_FNAME ]  = {0};
73 
74 static TCHAR    g_szSaveTitle[ 512 ] = {0};
75 static LPCTSTR  g_pszTempTitle = _T("{98C1C303-2A9E-11d4-9FF5-0060677l8D04}");
76 
77 // (forward reference)
78 
79 static void ProcessException( EXCEPTION_POINTERS* pExceptionPtrs );
80 static HWND FindConsoleHandle();
81 
82 // (helper macro)
83 
84 #ifndef ARRAYSIZE
85 #define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
86 #endif
87 
88 ///////////////////////////////////////////////////////////////////////////////
89 
90 #include <Mmsystem.h>
91 #pragma comment( lib, "Winmm" )
92 
main(int ac,char * av[])93 int main(int ac,char *av[])
94 {
95     int rc = 0;
96 
97     SET_THREAD_NAME("bootstrap");
98 
99     // Disable default invalid crt parameter handling
100 
101     DISABLE_CRT_INVALID_PARAMETER_HANDLER();
102 
103     // Request the highest possible time-interval accuracy...
104 
105     timeBeginPeriod( 1 );   // (one millisecond time interval accuracy)
106 
107     EnableMenuItem( GetSystemMenu( FindConsoleHandle(), FALSE ),
108                     SC_CLOSE, MF_BYCOMMAND | MF_GRAYED );
109 
110     // If we're being debugged, then let the debugger
111     // catch the exception. Otherwise, let our exception
112     // handler catch it...
113 
114     if ( IsDebuggerPresent() )      // (are we being debugged?)
115     {
116         rc = impl(ac,av);           // (yes, let debugger catch the exception)
117     }
118     else // (not being debugged; use our exception handler)
119     {
120         if (1
121             && (g_hDbgHelpDll = LoadLibrary(_T("DbgHelp.dll")))
122             && (g_pfnMiniDumpWriteDumpFunc = (MINIDUMPWRITEDUMPFUNC*)
123                 GetProcAddress( g_hDbgHelpDll, _T("MiniDumpWriteDump")))
124         )
125         {
126             GetModuleFileNameW( NULL, g_wszHercPath, ARRAYSIZE(g_wszHercPath) );
127             _wsplitpath( g_wszHercPath, g_wszHercDrive, g_wszHercDir, NULL, NULL );
128         }
129 
130         SetErrorMode( SEM_NOGPFAULTERRORBOX );
131 
132         __try
133         {
134             rc = impl(ac,av);  // (Hercules, do your thing!)
135         }
136         __except
137         (
138             fflush(stdout),
139             fflush(stderr),
140             _ftprintf( stderr, _T("]!OOPS!\n") ),
141             fflush(stdout),
142             fflush(stderr),
143             Sleep(10),
144             _tprintf( _T("\n\n") ),
145             _tprintf( _T("                   ***************\n") ),
146             _tprintf( _T("                   *    OOPS!    *\n") ),
147             _tprintf( _T("                   ***************\n") ),
148             _tprintf( _T("\n") ),
149             _tprintf( _T("                 Hercules has crashed!\n") ),
150             _tprintf( _T("\n") ),
151             _tprintf( _T("(you may need to press ENTER if no 'oops!' dialog-box appears)\n") ),
152             _tprintf( _T("\n") ),
153             ProcessException( GetExceptionInformation() ),
154             EXCEPTION_EXECUTE_HANDLER
155         )
156         {
157             rc = -1; // (indicate error)
158         }
159     }
160 
161     // Each call to "timeBeginPeriod" must be matched with a call to "timeEndPeriod"
162 
163     timeEndPeriod( 1 );     // (no longer care about accurate time intervals)
164 
165     EnableMenuItem( GetSystemMenu( FindConsoleHandle(), FALSE ),
166                 SC_CLOSE, MF_BYCOMMAND | MF_ENABLED );
167 
168     return rc;
169 }
170 
171 ///////////////////////////////////////////////////////////////////////////////
172 
FindConsoleHandle()173 static HWND FindConsoleHandle()
174 {
175     HWND hWnd;
176     if (!GetConsoleTitle(g_szSaveTitle,ARRAYSIZE(g_szSaveTitle)))
177         return NULL;
178     if (!SetConsoleTitle(g_pszTempTitle))
179         return NULL;
180     Sleep(20);
181     hWnd = FindWindow(NULL,g_pszTempTitle);
182     SetConsoleTitle(g_szSaveTitle);
183     return hWnd;
184 }
185 
186 ///////////////////////////////////////////////////////////////////////////////
187 
188 static BOOL CreateMiniDump( EXCEPTION_POINTERS* pExceptionPtrs );
189 
ProcessException(EXCEPTION_POINTERS * pExceptionPtrs)190 static void ProcessException( EXCEPTION_POINTERS* pExceptionPtrs )
191 {
192     UINT uiMBFlags =
193         0
194         | MB_SYSTEMMODAL
195         | MB_TOPMOST
196         | MB_SETFOREGROUND
197         ;
198 
199     HWND hwndMBOwner = FindConsoleHandle();
200 
201     if (!hwndMBOwner || !IsWindowVisible(hwndMBOwner))
202         hwndMBOwner = GetDesktopWindow();
203 
204     if ( !g_pfnMiniDumpWriteDumpFunc )
205     {
206         MessageBox
207         (
208             hwndMBOwner,
209             _T("The creation of a crash dump for analysis by the Hercules ")
210             _T("development team is NOT possible\nbecause the required 'DbgHelp.dll' ")
211             _T("is missing or is not installed or was otherwise not located.")
212             ,_T("OOPS!  Hercules has crashed!"),
213             uiMBFlags
214                 | MB_ICONERROR
215                 | MB_OK
216         );
217 
218         return;
219     }
220 
221     if ( IDYES == MessageBox
222     (
223         hwndMBOwner,
224         _T("The creation of a crash dump for further analysis by ")
225         _T("the Hercules development team is strongly suggested.\n\n")
226         _T("Would you like to create a crash dump for ")
227         _T("the Hercules development team to analyze?")
228         ,_T("OOPS!  Hercules has crashed!"),
229         uiMBFlags
230             | MB_ICONERROR
231             | MB_YESNO
232     ))
233     {
234         if ( CreateMiniDump( pExceptionPtrs ) )
235         {
236             MessageBox
237             (
238                 hwndMBOwner,
239                 _T("Please send the dump to the Hercules development team for analysis.")
240                 ,_T("Dump Complete"),
241                 uiMBFlags
242                     | MB_ICONEXCLAMATION
243                     | MB_OK
244             );
245         }
246     }
247 }
248 
249 ///////////////////////////////////////////////////////////////////////////////
250 // The following CreateMiniDump functions are based on
251 // Oleg Starodumov's sample at http://www.debuginfo.com
252 
253 static void BuildUserStreams( MINIDUMP_USER_STREAM_INFORMATION* pMDUSI );
254 
255 static BOOL CALLBACK MyMiniDumpCallback  // (fwd ref)
256 (
257     PVOID                            pParam,
258     const PMINIDUMP_CALLBACK_INPUT   pInput,
259     PMINIDUMP_CALLBACK_OUTPUT        pOutput
260 );
261 
CreateMiniDump(EXCEPTION_POINTERS * pExceptionPtrs)262 static BOOL CreateMiniDump( EXCEPTION_POINTERS* pExceptionPtrs )
263 {
264     BOOL bSuccess = FALSE;
265     HANDLE hDumpFile;
266 
267     _wmakepath( g_wszDumpPath, g_wszHercDrive, g_wszHercDir, L"Hercules", L".dmp" );
268 
269     _tprintf( _T("Creating crash dump \"%ls\"...\n"), g_wszDumpPath );
270     _tprintf( _T("Please wait; this may take a few minutes...\n") );
271     _tprintf( _T("(another message will appear when the dump is complete)\n") );
272 
273     hDumpFile = CreateFileW
274     (
275         g_wszDumpPath,
276         GENERIC_WRITE,
277         0, NULL, CREATE_ALWAYS,
278         FILE_ATTRIBUTE_NORMAL, NULL
279     );
280 
281     if ( hDumpFile && INVALID_HANDLE_VALUE != hDumpFile )
282     {
283         // Create the minidump
284 
285         MINIDUMP_EXCEPTION_INFORMATION    mdei;
286         MINIDUMP_USER_STREAM_INFORMATION  mdusi;
287         MINIDUMP_CALLBACK_INFORMATION     mci;
288         MINIDUMP_TYPE                     mdt;
289 
290         BuildUserStreams( &mdusi );
291 
292         mdei.ThreadId           = GetCurrentThreadId();
293         mdei.ExceptionPointers  = pExceptionPtrs;
294         mdei.ClientPointers     = FALSE;
295 
296         mci.CallbackRoutine     = (MINIDUMP_CALLBACK_ROUTINE) MyMiniDumpCallback;
297         mci.CallbackParam       = 0;
298 
299         mdt = (MINIDUMP_TYPE)
300         (0
301             | MiniDumpWithPrivateReadWriteMemory
302             | MiniDumpWithDataSegs
303             | MiniDumpWithHandleData
304 //          | MiniDumpWithFullMemoryInfo
305 //          | MiniDumpWithThreadInfo
306             | MiniDumpWithUnloadedModules
307         );
308 
309         bSuccess = g_pfnMiniDumpWriteDumpFunc( GetCurrentProcess(), GetCurrentProcessId(),
310             hDumpFile, mdt, (pExceptionPtrs != 0) ? &mdei : 0, &mdusi, &mci );
311 
312         CloseHandle( hDumpFile );
313 
314         if ( bSuccess )
315         {
316             _tprintf( _T("Dump \"%ls\" created.\n"), g_wszDumpPath );
317         }
318         else
319             _tprintf( _T("MiniDumpWriteDump failed! Error: %u\n"), GetLastError() );
320     }
321     else
322     {
323         _tprintf( _T("CreateFile failed! Error: %u\n"), GetLastError() );
324     }
325 
326     return bSuccess;
327 }
328 
329 ///////////////////////////////////////////////////////////////////////////////
330 // Build User Stream Arrays...
331 
332 #define MAX_MINIDUMP_USER_STREAMS  (64)
333 
334 static  char                  g_host_info_str [ 1024 ];
335 static  MINIDUMP_USER_STREAM  UserStreamArray [ MAX_MINIDUMP_USER_STREAMS ];
336 
BuildUserStreams(MINIDUMP_USER_STREAM_INFORMATION * pMDUSI)337 static void BuildUserStreams( MINIDUMP_USER_STREAM_INFORMATION* pMDUSI )
338 {
339     const char** ppszBldInfoStr;
340     int nNumBldInfoStrs;
341     ULONG UserStreamCount;
342 
343     _ASSERTE( pMDUSI );
344 
345     get_hostinfo_str( NULL, g_host_info_str, sizeof(g_host_info_str) );
346     nNumBldInfoStrs = get_buildinfo_strings( &ppszBldInfoStr );
347 
348     UserStreamCount = min( (3+nNumBldInfoStrs), MAX_MINIDUMP_USER_STREAMS );
349 
350     pMDUSI->UserStreamCount = UserStreamCount;
351     pMDUSI->UserStreamArray = UserStreamArray;
352 
353     UserStreamCount = 0;
354 
355     if ( UserStreamCount < pMDUSI->UserStreamCount )
356     {
357         UserStreamArray[UserStreamCount].Type       = CommentStreamA;
358         UserStreamArray[UserStreamCount].Buffer     =        VERSION;
359         UserStreamArray[UserStreamCount].BufferSize = sizeof(VERSION);
360         UserStreamCount++;
361     }
362 
363     if ( UserStreamCount < pMDUSI->UserStreamCount )
364     {
365         UserStreamArray[UserStreamCount].Type       = CommentStreamA;
366         UserStreamArray[UserStreamCount].Buffer     =        HERCULES_COPYRIGHT;
367         UserStreamArray[UserStreamCount].BufferSize = sizeof(HERCULES_COPYRIGHT);
368         UserStreamCount++;
369     }
370 
371     if ( UserStreamCount < pMDUSI->UserStreamCount )
372     {
373         UserStreamArray[UserStreamCount].Type       = CommentStreamA;
374         UserStreamArray[UserStreamCount].Buffer     =        g_host_info_str;
375         UserStreamArray[UserStreamCount].BufferSize = strlen(g_host_info_str)+1;
376         UserStreamCount++;
377     }
378 
379     for (; nNumBldInfoStrs && UserStreamCount < pMDUSI->UserStreamCount;
380         nNumBldInfoStrs--, UserStreamCount++, ppszBldInfoStr++ )
381     {
382         UserStreamArray[UserStreamCount].Type       = CommentStreamA;
383         UserStreamArray[UserStreamCount].Buffer     = (PVOID)*ppszBldInfoStr;
384         UserStreamArray[UserStreamCount].BufferSize = strlen(*ppszBldInfoStr)+1;
385     }
386 }
387 
388 ///////////////////////////////////////////////////////////////////////////////
389 // Custom minidump callback
390 
391 static BOOL IsDataSectionNeeded( const WCHAR* pwszModuleName );  // (fwd ref)
392 
MyMiniDumpCallback(PVOID pParam,const PMINIDUMP_CALLBACK_INPUT pInput,PMINIDUMP_CALLBACK_OUTPUT pOutput)393 static BOOL CALLBACK MyMiniDumpCallback
394 (
395     PVOID                            pParam,
396     const PMINIDUMP_CALLBACK_INPUT   pInput,
397     PMINIDUMP_CALLBACK_OUTPUT        pOutput
398 )
399 {
400     BOOL bRet = FALSE;
401 
402     if ( !pInput || !pOutput )
403         return FALSE;
404 
405     switch ( pInput->CallbackType )
406     {
407         case IncludeModuleCallback:
408         {
409             // Include the module into the dump
410             bRet = TRUE;
411         }
412         break;
413 
414         case IncludeThreadCallback:
415         {
416             // Include the thread into the dump
417             bRet = TRUE;
418         }
419         break;
420 
421         case ModuleCallback:
422         {
423             // Are data sections available for this module ?
424 
425             if ( pOutput->ModuleWriteFlags & ModuleWriteDataSeg )
426             {
427                 // Yes, but do we really need them?
428 
429                 if ( !IsDataSectionNeeded( pInput->Module.FullPath ) )
430                     pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);
431             }
432 
433             bRet = TRUE;
434         }
435         break;
436 
437         case ThreadCallback:
438         {
439             // Include all thread information into the minidump
440             bRet = TRUE;
441         }
442         break;
443 
444         case ThreadExCallback:
445         {
446             // Include this information
447             bRet = TRUE;
448         }
449         break;
450 
451 /* NOTE About MemoryCallback :
452  * This is defined for DbgHelp > 6.1..
453  * Since "false" is returned, it has been commented out.
454  *
455  * Additionally, false is now returned by default. This
456  * ensures that the callback function will operate correctly
457  * even with future versions of the DbhHelp DLL.
458  * -- Ivan
459  */
460 //        case MemoryCallback:
461 //        {
462 //            // We do not include any information here -> return FALSE
463 //            bRet = FALSE;
464 //        }
465 //        break;
466 
467 // Following default block added by ISW 2005/05/06
468           default:
469             {
470                 // Do not return any information for unrecognized
471                 // callback types.
472                 bRet=FALSE;
473             }
474             break;
475 
476 //      case CancelCallback:
477 //          break;
478     }
479 
480     return bRet;
481 }
482 
483 ///////////////////////////////////////////////////////////////////////////////
484 // This function determines whether we need data sections of the given module
485 
IsDataSectionNeeded(const WCHAR * pwszModuleName)486 static BOOL IsDataSectionNeeded( const WCHAR* pwszModuleName )
487 {
488     BOOL bNeeded = FALSE;
489 
490     _ASSERTE( pwszModuleName );
491 
492     _wsplitpath( pwszModuleName, NULL, g_wszFileDir, g_wszFileName, NULL );
493 
494     if ( _wcsicmp( g_wszFileName, L"ntdll" ) == 0 )
495     {
496         bNeeded = TRUE;
497     }
498     else if ( _wcsicmp( g_wszFileDir, g_wszHercDir ) == 0 )
499     {
500         bNeeded = TRUE;
501     }
502 
503     return bNeeded;
504 }
505 
506 ///////////////////////////////////////////////////////////////////////////////
507 
508 #pragma optimize( "", on )
509 
510 #endif // !defined( _MSVC_ )
511