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