1/** 2 * Contains druntime startup and shutdown routines. 3 * 4 * Copyright: Copyright Digital Mars 2000 - 2013. 5 * License: Distributed under the 6 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). 7 * (See accompanying file LICENSE) 8 * Authors: Walter Bright, Sean Kelly 9 * Source: $(DRUNTIMESRC src/rt/_dmain2.d) 10 */ 11 12/* NOTE: This file has been patched from the original DMD distribution to 13 * work with the GDC compiler. 14 */ 15module rt.dmain2; 16 17private 18{ 19 import rt.memory; 20 import rt.sections; 21 import core.atomic; 22 import core.stdc.stddef; 23 import core.stdc.stdlib; 24 import core.stdc.string; 25 import core.stdc.stdio; // for printf() 26 import core.stdc.errno : errno; 27} 28 29version (Windows) 30{ 31 private import core.stdc.wchar_; 32 private import core.sys.windows.windows; 33 34 pragma(lib, "shell32.lib"); // needed for CommandLineToArgvW 35} 36 37version (FreeBSD) 38{ 39 import core.stdc.fenv; 40} 41version (NetBSD) 42{ 43 import core.stdc.fenv; 44} 45version (DragonFlyBSD) 46{ 47 import core.stdc.fenv; 48} 49 50extern (C) void _d_monitor_staticctor(); 51extern (C) void _d_monitor_staticdtor(); 52extern (C) void _d_critical_init(); 53extern (C) void _d_critical_term(); 54extern (C) void gc_init(); 55extern (C) void gc_term(); 56extern (C) void lifetime_init(); 57extern (C) void rt_moduleCtor(); 58extern (C) void rt_moduleTlsCtor(); 59extern (C) void rt_moduleDtor(); 60extern (C) void rt_moduleTlsDtor(); 61extern (C) void thread_joinAll(); 62extern (C) bool runModuleUnitTests(); 63extern (C) void _d_initMonoTime(); 64 65version (OSX) 66{ 67 // The bottom of the stack 68 extern (C) __gshared void* __osx_stack_end = cast(void*)0xC0000000; 69} 70 71version (CRuntime_Microsoft) 72{ 73 extern(C) void init_msvc(); 74} 75 76/*********************************** 77 * These are a temporary means of providing a GC hook for DLL use. They may be 78 * replaced with some other similar functionality later. 79 */ 80extern (C) 81{ 82 void* gc_getProxy(); 83 void gc_setProxy(void* p); 84 void gc_clrProxy(); 85 86 alias void* function() gcGetFn; 87 alias void function(void*) gcSetFn; 88 alias void function() gcClrFn; 89} 90 91version (Windows) 92{ 93 /******************************************* 94 * Loads a DLL written in D with the name 'name'. 95 * Returns: 96 * opaque handle to the DLL if successfully loaded 97 * null if failure 98 */ 99 extern (C) void* rt_loadLibrary(const char* name) 100 { 101 return initLibrary(.LoadLibraryA(name)); 102 } 103 104 extern (C) void* rt_loadLibraryW(const wchar_t* name) 105 { 106 return initLibrary(.LoadLibraryW(name)); 107 } 108 109 void* initLibrary(void* mod) 110 { 111 // BUG: LoadLibrary() call calls rt_init(), which fails if proxy is not set! 112 // (What? LoadLibrary() is a Windows API call, it shouldn't call rt_init().) 113 if (mod is null) 114 return mod; 115 gcSetFn gcSet = cast(gcSetFn) GetProcAddress(mod, "gc_setProxy"); 116 if (gcSet !is null) 117 { // BUG: Set proxy, but too late 118 gcSet(gc_getProxy()); 119 } 120 return mod; 121 } 122 123 /************************************* 124 * Unloads DLL that was previously loaded by rt_loadLibrary(). 125 * Input: 126 * ptr the handle returned by rt_loadLibrary() 127 * Returns: 128 * 1 succeeded 129 * 0 some failure happened 130 */ 131 extern (C) int rt_unloadLibrary(void* ptr) 132 { 133 gcClrFn gcClr = cast(gcClrFn) GetProcAddress(ptr, "gc_clrProxy"); 134 if (gcClr !is null) 135 gcClr(); 136 return FreeLibrary(ptr) != 0; 137 } 138} 139 140/* To get out-of-band access to the args[] passed to main(). 141 */ 142 143__gshared string[] _d_args = null; 144 145extern (C) string[] rt_args() 146{ 147 return _d_args; 148} 149 150// make arguments passed to main available for being filtered by runtime initializers 151extern(C) __gshared char[][] _d_main_args = null; 152 153// This variable is only ever set by a debugger on initialization so it should 154// be fine to leave it as __gshared. 155extern (C) __gshared bool rt_trapExceptions = true; 156 157alias void delegate(Throwable) ExceptionHandler; 158 159/** 160 * Keep track of how often rt_init/rt_term were called. 161 */ 162shared size_t _initCount; 163 164/********************************************** 165 * Initialize druntime. 166 * If a C program wishes to call D code, and there's no D main(), then it 167 * must call rt_init() and rt_term(). 168 */ 169extern (C) int rt_init() 170{ 171 /* @@BUG 11380 @@ Need to synchronize rt_init/rt_term calls for 172 version (Shared) druntime, because multiple C threads might 173 initialize different D libraries without knowing about the 174 shared druntime. Also we need to attach any thread that calls 175 rt_init. */ 176 if (atomicOp!"+="(_initCount, 1) > 1) return 1; 177 178 version (CRuntime_Microsoft) 179 init_msvc(); 180 181 _d_monitor_staticctor(); 182 _d_critical_init(); 183 184 try 185 { 186 initSections(); 187 // this initializes mono time before anything else to allow usage 188 // in other druntime systems. 189 _d_initMonoTime(); 190 gc_init(); 191 initStaticDataGC(); 192 lifetime_init(); 193 rt_moduleCtor(); 194 rt_moduleTlsCtor(); 195 return 1; 196 } 197 catch (Throwable t) 198 { 199 _initCount = 0; 200 _d_print_throwable(t); 201 } 202 _d_critical_term(); 203 _d_monitor_staticdtor(); 204 return 0; 205} 206 207/********************************************** 208 * Terminate use of druntime. 209 */ 210extern (C) int rt_term() 211{ 212 if (!_initCount) return 0; // was never initialized 213 if (atomicOp!"-="(_initCount, 1)) return 1; 214 215 try 216 { 217 rt_moduleTlsDtor(); 218 thread_joinAll(); 219 rt_moduleDtor(); 220 gc_term(); 221 return 1; 222 } 223 catch (Throwable t) 224 { 225 _d_print_throwable(t); 226 } 227 finally 228 { 229 finiSections(); 230 _d_critical_term(); 231 _d_monitor_staticdtor(); 232 } 233 return 0; 234} 235 236/********************************************** 237 * Trace handler 238 */ 239alias Throwable.TraceInfo function(void* ptr) TraceHandler; 240private __gshared TraceHandler traceHandler = null; 241 242 243/** 244 * Overrides the default trace hander with a user-supplied version. 245 * 246 * Params: 247 * h = The new trace handler. Set to null to use the default handler. 248 */ 249extern (C) void rt_setTraceHandler(TraceHandler h) 250{ 251 traceHandler = h; 252} 253 254/** 255 * Return the current trace handler 256 */ 257extern (C) TraceHandler rt_getTraceHandler() 258{ 259 return traceHandler; 260} 261 262/** 263 * This function will be called when an exception is constructed. The 264 * user-supplied trace handler will be called if one has been supplied, 265 * otherwise no trace will be generated. 266 * 267 * Params: 268 * ptr = A pointer to the location from which to generate the trace, or null 269 * if the trace should be generated from within the trace handler 270 * itself. 271 * 272 * Returns: 273 * An object describing the current calling context or null if no handler is 274 * supplied. 275 */ 276extern (C) Throwable.TraceInfo _d_traceContext(void* ptr = null) 277{ 278 if (traceHandler is null) 279 return null; 280 return traceHandler(ptr); 281} 282 283/*********************************** 284 * Provide out-of-band access to the original C argc/argv 285 * passed to this program via main(argc,argv). 286 */ 287 288struct CArgs 289{ 290 int argc; 291 char** argv; 292} 293 294__gshared CArgs _cArgs; 295 296extern (C) CArgs rt_cArgs() @nogc 297{ 298 return _cArgs; 299} 300 301/*********************************** 302 * Run the given main function. 303 * Its purpose is to wrap the D main() 304 * function and catch any unhandled exceptions. 305 */ 306private alias extern(C) int function(char[][] args) MainFunc; 307 308extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) 309{ 310 // Remember the original C argc/argv 311 _cArgs.argc = argc; 312 _cArgs.argv = argv; 313 314 int result; 315 316 version (OSX) 317 { /* OSX does not provide a way to get at the top of the 318 * stack, except for the magic value 0xC0000000. 319 * But as far as the gc is concerned, argv is at the top 320 * of the main thread's stack, so save the address of that. 321 */ 322 __osx_stack_end = cast(void*)&argv; 323 } 324 325 version (FreeBSD) version (D_InlineAsm_X86) 326 { 327 /* 328 * FreeBSD/i386 sets the FPU precision mode to 53 bit double. 329 * Make it 64 bit extended. 330 */ 331 ushort fpucw; 332 asm 333 { 334 fstsw fpucw; 335 or fpucw, 0b11_00_111111; // 11: use 64 bit extended-precision 336 // 111111: mask all FP exceptions 337 fldcw fpucw; 338 } 339 } 340 version (CRuntime_Microsoft) 341 { 342 // enable full precision for reals 343 version (GNU) 344 { 345 size_t fpu_cw; 346 asm { "fstcw %0" : "=m" (fpu_cw); } 347 fpu_cw |= 0b11_00_111111; // 11: use 64 bit extended-precision 348 // 111111: mask all FP exceptions 349 asm { "fldcw %0" : "=m" (fpu_cw); } 350 } 351 else version (Win64) 352 asm 353 { 354 push RAX; 355 fstcw word ptr [RSP]; 356 or [RSP], 0b11_00_111111; // 11: use 64 bit extended-precision 357 // 111111: mask all FP exceptions 358 fldcw word ptr [RSP]; 359 pop RAX; 360 } 361 else version (Win32) 362 { 363 asm 364 { 365 push EAX; 366 fstcw word ptr [ESP]; 367 or [ESP], 0b11_00_111111; // 11: use 64 bit extended-precision 368 // 111111: mask all FP exceptions 369 fldcw word ptr [ESP]; 370 pop EAX; 371 } 372 } 373 } 374 375 version (Windows) 376 { 377 /* Because we want args[] to be UTF-8, and Windows doesn't guarantee that, 378 * we ignore argc/argv and go get the Windows command line again as UTF-16. 379 * Then, reparse into wargc/wargs, and then use Windows API to convert 380 * to UTF-8. 381 */ 382 const wchar_t* wCommandLine = GetCommandLineW(); 383 immutable size_t wCommandLineLength = wcslen(wCommandLine); 384 int wargc; 385 wchar_t** wargs = CommandLineToArgvW(wCommandLine, &wargc); 386 // assert(wargc == argc); /* argc can be broken by Unicode arguments */ 387 388 // Allocate args[] on the stack - use wargc 389 char[][] args = (cast(char[]*) alloca(wargc * (char[]).sizeof))[0 .. wargc]; 390 391 // This is required because WideCharToMultiByte requires int as input. 392 assert(wCommandLineLength <= cast(size_t) int.max, "Wide char command line length must not exceed int.max"); 393 394 immutable size_t totalArgsLength = WideCharToMultiByte(CP_UTF8, 0, wCommandLine, cast(int)wCommandLineLength, null, 0, null, null); 395 { 396 char* totalArgsBuff = cast(char*) alloca(totalArgsLength); 397 size_t j = 0; 398 foreach (i; 0 .. wargc) 399 { 400 immutable size_t wlen = wcslen(wargs[i]); 401 assert(wlen <= cast(size_t) int.max, "wlen cannot exceed int.max"); 402 immutable int len = WideCharToMultiByte(CP_UTF8, 0, &wargs[i][0], cast(int) wlen, null, 0, null, null); 403 args[i] = totalArgsBuff[j .. j + len]; 404 if (len == 0) 405 continue; 406 j += len; 407 assert(j <= totalArgsLength); 408 WideCharToMultiByte(CP_UTF8, 0, &wargs[i][0], cast(int) wlen, &args[i][0], len, null, null); 409 } 410 } 411 LocalFree(wargs); 412 wargs = null; 413 wargc = 0; 414 } 415 else version (Posix) 416 { 417 // Allocate args[] on the stack 418 char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc]; 419 420 size_t totalArgsLength = 0; 421 foreach (i, ref arg; args) 422 { 423 arg = argv[i][0 .. strlen(argv[i])]; 424 totalArgsLength += arg.length; 425 } 426 } 427 else 428 static assert(0); 429 430 /* Create a copy of args[] on the stack to be used for main, so that rt_args() 431 * cannot be modified by the user. 432 * Note that when this function returns, _d_args will refer to garbage. 433 */ 434 { 435 _d_args = cast(string[]) args; 436 auto buff = cast(char[]*) alloca(args.length * (char[]).sizeof + totalArgsLength); 437 438 char[][] argsCopy = buff[0 .. args.length]; 439 auto argBuff = cast(char*) (buff + args.length); 440 size_t j = 0; 441 foreach (arg; args) 442 { 443 if (arg.length < 6 || arg[0..6] != "--DRT-") // skip D runtime options 444 { 445 argsCopy[j++] = (argBuff[0 .. arg.length] = arg[]); 446 argBuff += arg.length; 447 } 448 } 449 args = argsCopy[0..j]; 450 } 451 452 bool trapExceptions = rt_trapExceptions; 453 454 version (Windows) 455 { 456 if (IsDebuggerPresent()) 457 trapExceptions = false; 458 version (GNU) 459 { 460 /* IsDebuggerPresent doesn't detect GDC. Would be nice to have 461 some way of detecting valid console output */ 462 trapExceptions = true; 463 } 464 } 465 466 void tryExec(scope void delegate() dg) 467 { 468 if (trapExceptions) 469 { 470 try 471 { 472 dg(); 473 } 474 catch (Throwable t) 475 { 476 _d_print_throwable(t); 477 result = EXIT_FAILURE; 478 } 479 } 480 else 481 { 482 dg(); 483 } 484 } 485 486 // NOTE: The lifetime of a process is much like the lifetime of an object: 487 // it is initialized, then used, then destroyed. If initialization 488 // fails, the successive two steps are never reached. However, if 489 // initialization succeeds, then cleanup will occur even if the use 490 // step fails in some way. Here, the use phase consists of running 491 // the user's main function. If main terminates with an exception, 492 // the exception is handled and then cleanup begins. An exception 493 // thrown during cleanup, however, will abort the cleanup process. 494 void runAll() 495 { 496 if (rt_init() && runModuleUnitTests()) 497 tryExec({ result = mainFunc(args); }); 498 else 499 result = EXIT_FAILURE; 500 501 if (!rt_term()) 502 result = (result == EXIT_SUCCESS) ? EXIT_FAILURE : result; 503 } 504 505 tryExec(&runAll); 506 507 // Issue 10344: flush stdout and return nonzero on failure 508 if (.fflush(.stdout) != 0) 509 { 510 .fprintf(.stderr, "Failed to flush stdout: %s\n", .strerror(.errno)); 511 if (result == 0) 512 { 513 result = EXIT_FAILURE; 514 } 515 } 516 517 return result; 518} 519 520private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothrow sink) 521{ 522 for (; t; t = t.next) 523 { 524 t.toString(sink); sink("\n"); 525 526 auto e = cast(Error)t; 527 if (e is null || e.bypassedException is null) continue; 528 529 sink("=== Bypassed ===\n"); 530 for (auto t2 = e.bypassedException; t2; t2 = t2.next) 531 { 532 t2.toString(sink); sink("\n"); 533 } 534 sink("=== ~Bypassed ===\n"); 535 } 536} 537 538extern (C) void _d_print_throwable(Throwable t) 539{ 540 // On Windows, a console may not be present to print the output to. 541 // Show a message box instead. If the console is present, convert to 542 // the correct encoding. 543 version (Windows) 544 { 545 static struct WSink 546 { 547 wchar_t* ptr; size_t len; 548 549 void sink(in char[] s) scope nothrow 550 { 551 if (!s.length) return; 552 int swlen = MultiByteToWideChar( 553 CP_UTF8, 0, s.ptr, cast(int)s.length, null, 0); 554 if (!swlen) return; 555 556 auto newPtr = cast(wchar_t*)realloc(ptr, 557 (this.len + swlen + 1) * wchar_t.sizeof); 558 if (!newPtr) return; 559 ptr = newPtr; 560 auto written = MultiByteToWideChar( 561 CP_UTF8, 0, s.ptr, cast(int)s.length, ptr+len, swlen); 562 len += written; 563 } 564 565 wchar_t* get() { if (ptr) ptr[len] = 0; return ptr; } 566 567 void free() { .free(ptr); } 568 } 569 570 HANDLE windowsHandle(int fd) 571 { 572 version (CRuntime_Microsoft) 573 return cast(HANDLE)_get_osfhandle(fd); 574 else 575 return _fdToHandle(fd); 576 } 577 578 auto hStdErr = windowsHandle(fileno(stderr)); 579 CONSOLE_SCREEN_BUFFER_INFO sbi; 580 bool isConsole = GetConsoleScreenBufferInfo(hStdErr, &sbi) != 0; 581 582 // ensure the exception is shown at the beginning of the line, while also 583 // checking whether stderr is a valid file 584 int written = fprintf(stderr, "\n"); 585 if (written <= 0) 586 { 587 WSink buf; 588 formatThrowable(t, &buf.sink); 589 590 if (buf.ptr) 591 { 592 WSink caption; 593 if (t) 594 caption.sink(t.classinfo.name); 595 596 // Avoid static user32.dll dependency for console applications 597 // by loading it dynamically as needed 598 auto user32 = LoadLibraryW("user32.dll"); 599 if (user32) 600 { 601 alias typeof(&MessageBoxW) PMessageBoxW; 602 auto pMessageBoxW = cast(PMessageBoxW) 603 GetProcAddress(user32, "MessageBoxW"); 604 if (pMessageBoxW) 605 pMessageBoxW(null, buf.get(), caption.get(), MB_ICONERROR); 606 } 607 FreeLibrary(user32); 608 caption.free(); 609 buf.free(); 610 } 611 return; 612 } 613 else if (isConsole) 614 { 615 WSink buf; 616 formatThrowable(t, &buf.sink); 617 618 if (buf.ptr) 619 { 620 uint codepage = GetConsoleOutputCP(); 621 int slen = WideCharToMultiByte(codepage, 0, 622 buf.ptr, cast(int)buf.len, null, 0, null, null); 623 auto sptr = cast(char*)malloc(slen * char.sizeof); 624 if (sptr) 625 { 626 WideCharToMultiByte(codepage, 0, 627 buf.ptr, cast(int)buf.len, sptr, slen, null, null); 628 WriteFile(hStdErr, sptr, slen, null, null); 629 free(sptr); 630 } 631 buf.free(); 632 } 633 return; 634 } 635 } 636 637 void sink(in char[] buf) scope nothrow 638 { 639 fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr); 640 } 641 formatThrowable(t, &sink); 642} 643