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 */ 15 module rt.dmain2; 16 17 private 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 29 version (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 37 version (FreeBSD) 38 { 39 import core.stdc.fenv; 40 } 41 version (NetBSD) 42 { 43 import core.stdc.fenv; 44 } 45 version (DragonFlyBSD) 46 { 47 import core.stdc.fenv; 48 } 49 50 extern (C) void _d_monitor_staticctor(); 51 extern (C) void _d_monitor_staticdtor(); 52 extern (C) void _d_critical_init(); 53 extern (C) void _d_critical_term(); 54 extern (C) void gc_init(); 55 extern (C) void gc_term(); 56 extern (C) void lifetime_init(); 57 extern (C) void rt_moduleCtor(); 58 extern (C) void rt_moduleTlsCtor(); 59 extern (C) void rt_moduleDtor(); 60 extern (C) void rt_moduleTlsDtor(); 61 extern (C) void thread_joinAll(); 62 extern (C) bool runModuleUnitTests(); 63 extern (C) void _d_initMonoTime(); 64 65 version (OSX) 66 { 67 // The bottom of the stack 68 extern (C) __gshared void* __osx_stack_end = cast(void*)0xC0000000; 69 } 70 71 version (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 */ 80 extern (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 91 version (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 145 extern (C) string[] rt_args() 146 { 147 return _d_args; 148 } 149 150 // make arguments passed to main available for being filtered by runtime initializers 151 extern(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. 155 extern (C) __gshared bool rt_trapExceptions = true; 156 157 alias void delegate(Throwable) ExceptionHandler; 158 159 /** 160 * Keep track of how often rt_init/rt_term were called. 161 */ 162 shared 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 */ 169 extern (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 */ 210 extern (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 */ 239 alias Throwable.TraceInfo function(void* ptr) TraceHandler; 240 private __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 */ 249 extern (C) void rt_setTraceHandler(TraceHandler h) 250 { 251 traceHandler = h; 252 } 253 254 /** 255 * Return the current trace handler 256 */ 257 extern (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 */ 276 extern (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 288 struct CArgs 289 { 290 int argc; 291 char** argv; 292 } 293 294 __gshared CArgs _cArgs; 295 296 extern (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 */ 306 private alias extern(C) int function(char[][] args) MainFunc; 307 308 extern (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 (Win64) 344 asm 345 { 346 push RAX; 347 fstcw word ptr [RSP]; 348 or [RSP], 0b11_00_111111; // 11: use 64 bit extended-precision 349 // 111111: mask all FP exceptions 350 fldcw word ptr [RSP]; 351 pop RAX; 352 } 353 else version (Win32) 354 { 355 asm 356 { 357 push EAX; 358 fstcw word ptr [ESP]; 359 or [ESP], 0b11_00_111111; // 11: use 64 bit extended-precision 360 // 111111: mask all FP exceptions 361 fldcw word ptr [ESP]; 362 pop EAX; 363 } 364 } 365 } 366 367 version (Windows) 368 { 369 /* Because we want args[] to be UTF-8, and Windows doesn't guarantee that, 370 * we ignore argc/argv and go get the Windows command line again as UTF-16. 371 * Then, reparse into wargc/wargs, and then use Windows API to convert 372 * to UTF-8. 373 */ 374 const wchar_t* wCommandLine = GetCommandLineW(); 375 immutable size_t wCommandLineLength = wcslen(wCommandLine); 376 int wargc; 377 wchar_t** wargs = CommandLineToArgvW(wCommandLine, &wargc); 378 // assert(wargc == argc); /* argc can be broken by Unicode arguments */ 379 380 // Allocate args[] on the stack - use wargc 381 char[][] args = (cast(char[]*) alloca(wargc * (char[]).sizeof))[0 .. wargc]; 382 383 // This is required because WideCharToMultiByte requires int as input. 384 assert(wCommandLineLength <= cast(size_t) int.max, "Wide char command line length must not exceed int.max"); 385 386 immutable size_t totalArgsLength = WideCharToMultiByte(CP_UTF8, 0, wCommandLine, cast(int)wCommandLineLength, null, 0, null, null); 387 { 388 char* totalArgsBuff = cast(char*) alloca(totalArgsLength); 389 size_t j = 0; 390 foreach (i; 0 .. wargc) 391 { 392 immutable size_t wlen = wcslen(wargs[i]); 393 assert(wlen <= cast(size_t) int.max, "wlen cannot exceed int.max"); 394 immutable int len = WideCharToMultiByte(CP_UTF8, 0, &wargs[i][0], cast(int) wlen, null, 0, null, null); 395 args[i] = totalArgsBuff[j .. j + len]; 396 if (len == 0) 397 continue; 398 j += len; 399 assert(j <= totalArgsLength); 400 WideCharToMultiByte(CP_UTF8, 0, &wargs[i][0], cast(int) wlen, &args[i][0], len, null, null); 401 } 402 } 403 LocalFree(wargs); 404 wargs = null; 405 wargc = 0; 406 } 407 else version (Posix) 408 { 409 // Allocate args[] on the stack 410 char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc]; 411 412 size_t totalArgsLength = 0; 413 foreach (i, ref arg; args) 414 { 415 arg = argv[i][0 .. strlen(argv[i])]; 416 totalArgsLength += arg.length; 417 } 418 } 419 else 420 static assert(0); 421 422 /* Create a copy of args[] on the stack to be used for main, so that rt_args() 423 * cannot be modified by the user. 424 * Note that when this function returns, _d_args will refer to garbage. 425 */ 426 { 427 _d_args = cast(string[]) args; 428 auto buff = cast(char[]*) alloca(args.length * (char[]).sizeof + totalArgsLength); 429 430 char[][] argsCopy = buff[0 .. args.length]; 431 auto argBuff = cast(char*) (buff + args.length); 432 size_t j = 0; 433 foreach (arg; args) 434 { 435 if (arg.length < 6 || arg[0..6] != "--DRT-") // skip D runtime options 436 { 437 argsCopy[j++] = (argBuff[0 .. arg.length] = arg[]); 438 argBuff += arg.length; 439 } 440 } 441 args = argsCopy[0..j]; 442 } 443 444 bool trapExceptions = rt_trapExceptions; 445 446 version (Windows) 447 { 448 if (IsDebuggerPresent()) 449 trapExceptions = false; 450 version (GNU) 451 { 452 /* IsDebuggerPresent doesn't detect GDC. Would be nice to have 453 some way of detecting valid console output */ 454 trapExceptions = true; 455 } 456 } 457 458 void tryExec(scope void delegate() dg) 459 { 460 if (trapExceptions) 461 { 462 try 463 { 464 dg(); 465 } 466 catch (Throwable t) 467 { 468 _d_print_throwable(t); 469 result = EXIT_FAILURE; 470 } 471 } 472 else 473 { 474 dg(); 475 } 476 } 477 478 // NOTE: The lifetime of a process is much like the lifetime of an object: 479 // it is initialized, then used, then destroyed. If initialization 480 // fails, the successive two steps are never reached. However, if 481 // initialization succeeds, then cleanup will occur even if the use 482 // step fails in some way. Here, the use phase consists of running 483 // the user's main function. If main terminates with an exception, 484 // the exception is handled and then cleanup begins. An exception 485 // thrown during cleanup, however, will abort the cleanup process. 486 void runAll() 487 { 488 if (rt_init() && runModuleUnitTests()) 489 tryExec({ result = mainFunc(args); }); 490 else 491 result = EXIT_FAILURE; 492 493 if (!rt_term()) 494 result = (result == EXIT_SUCCESS) ? EXIT_FAILURE : result; 495 } 496 497 tryExec(&runAll); 498 499 // Issue 10344: flush stdout and return nonzero on failure 500 if (.fflush(.stdout) != 0) 501 { 502 .fprintf(.stderr, "Failed to flush stdout: %s\n", .strerror(.errno)); 503 if (result == 0) 504 { 505 result = EXIT_FAILURE; 506 } 507 } 508 509 return result; 510 } 511 512 private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothrow sink) 513 { 514 for (; t; t = t.next) 515 { 516 t.toString(sink); sink("\n"); 517 518 auto e = cast(Error)t; 519 if (e is null || e.bypassedException is null) continue; 520 521 sink("=== Bypassed ===\n"); 522 for (auto t2 = e.bypassedException; t2; t2 = t2.next) 523 { 524 t2.toString(sink); sink("\n"); 525 } 526 sink("=== ~Bypassed ===\n"); 527 } 528 } 529 530 extern (C) void _d_print_throwable(Throwable t) 531 { 532 // On Windows, a console may not be present to print the output to. 533 // Show a message box instead. If the console is present, convert to 534 // the correct encoding. 535 version (Windows) 536 { 537 static struct WSink 538 { 539 wchar_t* ptr; size_t len; 540 541 void sink(in char[] s) scope nothrow 542 { 543 if (!s.length) return; 544 int swlen = MultiByteToWideChar( 545 CP_UTF8, 0, s.ptr, cast(int)s.length, null, 0); 546 if (!swlen) return; 547 548 auto newPtr = cast(wchar_t*)realloc(ptr, 549 (this.len + swlen + 1) * wchar_t.sizeof); 550 if (!newPtr) return; 551 ptr = newPtr; 552 auto written = MultiByteToWideChar( 553 CP_UTF8, 0, s.ptr, cast(int)s.length, ptr+len, swlen); 554 len += written; 555 } 556 557 wchar_t* get() { if (ptr) ptr[len] = 0; return ptr; } 558 559 void free() { .free(ptr); } 560 } 561 562 HANDLE windowsHandle(int fd) 563 { 564 version (CRuntime_Microsoft) 565 return cast(HANDLE)_get_osfhandle(fd); 566 else 567 return _fdToHandle(fd); 568 } 569 570 auto hStdErr = windowsHandle(fileno(stderr)); 571 CONSOLE_SCREEN_BUFFER_INFO sbi; 572 bool isConsole = GetConsoleScreenBufferInfo(hStdErr, &sbi) != 0; 573 574 // ensure the exception is shown at the beginning of the line, while also 575 // checking whether stderr is a valid file 576 int written = fprintf(stderr, "\n"); 577 if (written <= 0) 578 { 579 WSink buf; 580 formatThrowable(t, &buf.sink); 581 582 if (buf.ptr) 583 { 584 WSink caption; 585 if (t) 586 caption.sink(t.classinfo.name); 587 588 // Avoid static user32.dll dependency for console applications 589 // by loading it dynamically as needed 590 auto user32 = LoadLibraryW("user32.dll"); 591 if (user32) 592 { 593 alias typeof(&MessageBoxW) PMessageBoxW; 594 auto pMessageBoxW = cast(PMessageBoxW) 595 GetProcAddress(user32, "MessageBoxW"); 596 if (pMessageBoxW) 597 pMessageBoxW(null, buf.get(), caption.get(), MB_ICONERROR); 598 } 599 FreeLibrary(user32); 600 caption.free(); 601 buf.free(); 602 } 603 return; 604 } 605 else if (isConsole) 606 { 607 WSink buf; 608 formatThrowable(t, &buf.sink); 609 610 if (buf.ptr) 611 { 612 uint codepage = GetConsoleOutputCP(); 613 int slen = WideCharToMultiByte(codepage, 0, 614 buf.ptr, cast(int)buf.len, null, 0, null, null); 615 auto sptr = cast(char*)malloc(slen * char.sizeof); 616 if (sptr) 617 { 618 WideCharToMultiByte(codepage, 0, 619 buf.ptr, cast(int)buf.len, sptr, slen, null, null); 620 WriteFile(hStdErr, sptr, slen, null, null); 621 free(sptr); 622 } 623 buf.free(); 624 } 625 return; 626 } 627 } 628 629 void sink(in char[] buf) scope nothrow 630 { 631 fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr); 632 } 633 formatThrowable(t, &sink); 634 } 635