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