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