2 * Contains druntime startup and shutdown routines.
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)
12 /* NOTE: This file has been patched from the original DMD distribution to
13 * work with the GDC compiler.
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;
31 private import core.stdc.wchar_;
32 private import core.sys.windows.windows;
35 else pragma(lib, "shell32.lib"); // needed for CommandLineToArgvW
40 import core.stdc.fenv;
44 import core.stdc.fenv;
47 extern (C) void _d_monitor_staticctor();
48 extern (C) void _d_monitor_staticdtor();
49 extern (C) void _d_critical_init();
50 extern (C) void _d_critical_term();
51 extern (C) void gc_init();
52 extern (C) void gc_term();
53 extern (C) void lifetime_init();
54 extern (C) void rt_moduleCtor();
55 extern (C) void rt_moduleTlsCtor();
56 extern (C) void rt_moduleDtor();
57 extern (C) void rt_moduleTlsDtor();
58 extern (C) void thread_joinAll();
59 extern (C) bool runModuleUnitTests();
60 extern (C) void _d_initMonoTime();
64 // The bottom of the stack
65 extern (C) __gshared void* __osx_stack_end = cast(void*)0xC0000000;
68 version (CRuntime_Microsoft)
70 extern(C) void init_msvc();
73 /***********************************
74 * These are a temporary means of providing a GC hook for DLL use. They may be
75 * replaced with some other similar functionality later.
80 void gc_setProxy(void* p);
83 alias void* function() gcGetFn;
84 alias void function(void*) gcSetFn;
85 alias void function() gcClrFn;
90 /*******************************************
91 * Loads a DLL written in D with the name 'name'.
93 * opaque handle to the DLL if successfully loaded
96 extern (C) void* rt_loadLibrary(const char* name)
98 return initLibrary(.LoadLibraryA(name));
101 extern (C) void* rt_loadLibraryW(const wchar_t* name)
103 return initLibrary(.LoadLibraryW(name));
106 void* initLibrary(void* mod)
108 // BUG: LoadLibrary() call calls rt_init(), which fails if proxy is not set!
109 // (What? LoadLibrary() is a Windows API call, it shouldn't call rt_init().)
112 gcSetFn gcSet = cast(gcSetFn) GetProcAddress(mod, "gc_setProxy");
114 { // BUG: Set proxy, but too late
115 gcSet(gc_getProxy());
120 /*************************************
121 * Unloads DLL that was previously loaded by rt_loadLibrary().
123 * ptr the handle returned by rt_loadLibrary()
126 * 0 some failure happened
128 extern (C) int rt_unloadLibrary(void* ptr)
130 gcClrFn gcClr = cast(gcClrFn) GetProcAddress(ptr, "gc_clrProxy");
133 return FreeLibrary(ptr) != 0;
137 /* To get out-of-band access to the args[] passed to main().
140 __gshared string[] _d_args = null;
142 extern (C) string[] rt_args()
147 // make arguments passed to main available for being filtered by runtime initializers
148 extern(C) __gshared char[][] _d_main_args = null;
150 // This variable is only ever set by a debugger on initialization so it should
151 // be fine to leave it as __gshared.
152 extern (C) __gshared bool rt_trapExceptions = true;
154 alias void delegate(Throwable) ExceptionHandler;
157 * Keep track of how often rt_init/rt_term were called.
159 shared size_t _initCount;
161 /**********************************************
162 * Initialize druntime.
163 * If a C program wishes to call D code, and there's no D main(), then it
164 * must call rt_init() and rt_term().
166 extern (C) int rt_init()
168 /* @@BUG 11380 @@ Need to synchronize rt_init/rt_term calls for
169 version (Shared) druntime, because multiple C threads might
170 initialize different D libraries without knowing about the
171 shared druntime. Also we need to attach any thread that calls
173 if (atomicOp!"+="(_initCount, 1) > 1) return 1;
175 version (CRuntime_Microsoft)
178 _d_monitor_staticctor();
184 // this initializes mono time before anything else to allow usage
185 // in other druntime systems.
197 _d_print_throwable(t);
200 _d_monitor_staticdtor();
204 /**********************************************
205 * Terminate use of druntime.
207 extern (C) int rt_term()
209 if (!_initCount) return 0; // was never initialized
210 if (atomicOp!"-="(_initCount, 1)) return 1;
222 _d_print_throwable(t);
228 _d_monitor_staticdtor();
233 /**********************************************
236 alias Throwable.TraceInfo function(void* ptr) TraceHandler;
237 private __gshared TraceHandler traceHandler = null;
241 * Overrides the default trace hander with a user-supplied version.
244 * h = The new trace handler. Set to null to use the default handler.
246 extern (C) void rt_setTraceHandler(TraceHandler h)
252 * Return the current trace handler
254 extern (C) TraceHandler rt_getTraceHandler()
260 * This function will be called when an exception is constructed. The
261 * user-supplied trace handler will be called if one has been supplied,
262 * otherwise no trace will be generated.
265 * ptr = A pointer to the location from which to generate the trace, or null
266 * if the trace should be generated from within the trace handler
270 * An object describing the current calling context or null if no handler is
273 extern (C) Throwable.TraceInfo _d_traceContext(void* ptr = null)
275 if (traceHandler is null)
277 return traceHandler(ptr);
280 /***********************************
281 * Provide out-of-band access to the original C argc/argv
282 * passed to this program via main(argc,argv).
291 __gshared CArgs _cArgs;
293 extern (C) CArgs rt_cArgs() @nogc
298 /***********************************
299 * Run the given main function.
300 * Its purpose is to wrap the D main()
301 * function and catch any unhandled exceptions.
303 private alias extern(C) int function(char[][] args) MainFunc;
305 extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
307 // Remember the original C argc/argv
314 { /* OSX does not provide a way to get at the top of the
315 * stack, except for the magic value 0xC0000000.
316 * But as far as the gc is concerned, argv is at the top
317 * of the main thread's stack, so save the address of that.
319 __osx_stack_end = cast(void*)&argv;
322 version (FreeBSD) version (D_InlineAsm_X86)
325 * FreeBSD/i386 sets the FPU precision mode to 53 bit double.
326 * Make it 64 bit extended.
332 or fpucw, 0b11_00_111111; // 11: use 64 bit extended-precision
333 // 111111: mask all FP exceptions
337 version (CRuntime_Microsoft)
339 // enable full precision for reals
344 fstcw word ptr [RSP];
345 or [RSP], 0b11_00_111111; // 11: use 64 bit extended-precision
346 // 111111: mask all FP exceptions
347 fldcw word ptr [RSP];
355 fstcw word ptr [ESP];
356 or [ESP], 0b11_00_111111; // 11: use 64 bit extended-precision
357 // 111111: mask all FP exceptions
358 fldcw word ptr [ESP];
366 /* Because we want args[] to be UTF-8, and Windows doesn't guarantee that,
367 * we ignore argc/argv and go get the Windows command line again as UTF-16.
368 * Then, reparse into wargc/wargs, and then use Windows API to convert
371 const wchar_t* wCommandLine = GetCommandLineW();
372 immutable size_t wCommandLineLength = wcslen(wCommandLine);
374 wchar_t** wargs = CommandLineToArgvW(wCommandLine, &wargc);
375 // assert(wargc == argc); /* argc can be broken by Unicode arguments */
377 // Allocate args[] on the stack - use wargc
378 char[][] args = (cast(char[]*) alloca(wargc * (char[]).sizeof))[0 .. wargc];
380 // This is required because WideCharToMultiByte requires int as input.
381 assert(wCommandLineLength <= cast(size_t) int.max, "Wide char command line length must not exceed int.max");
383 immutable size_t totalArgsLength = WideCharToMultiByte(CP_UTF8, 0, wCommandLine, cast(int)wCommandLineLength, null, 0, null, null);
385 char* totalArgsBuff = cast(char*) alloca(totalArgsLength);
387 foreach (i; 0 .. wargc)
389 immutable size_t wlen = wcslen(wargs[i]);
390 assert(wlen <= cast(size_t) int.max, "wlen cannot exceed int.max");
391 immutable int len = WideCharToMultiByte(CP_UTF8, 0, &wargs[i][0], cast(int) wlen, null, 0, null, null);
392 args[i] = totalArgsBuff[j .. j + len];
396 assert(j <= totalArgsLength);
397 WideCharToMultiByte(CP_UTF8, 0, &wargs[i][0], cast(int) wlen, &args[i][0], len, null, null);
406 // Allocate args[] on the stack
407 char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc];
409 size_t totalArgsLength = 0;
410 foreach (i, ref arg; args)
412 arg = argv[i][0 .. strlen(argv[i])];
413 totalArgsLength += arg.length;
419 /* Create a copy of args[] on the stack to be used for main, so that rt_args()
420 * cannot be modified by the user.
421 * Note that when this function returns, _d_args will refer to garbage.
424 _d_args = cast(string[]) args;
425 auto buff = cast(char[]*) alloca(args.length * (char[]).sizeof + totalArgsLength);
427 char[][] argsCopy = buff[0 .. args.length];
428 auto argBuff = cast(char*) (buff + args.length);
432 if (arg.length < 6 || arg[0..6] != "--DRT-") // skip D runtime options
434 argsCopy[j++] = (argBuff[0 .. arg.length] = arg[]);
435 argBuff += arg.length;
438 args = argsCopy[0..j];
441 bool trapExceptions = rt_trapExceptions;
445 if (IsDebuggerPresent())
446 trapExceptions = false;
449 /* IsDebuggerPresent doesn't detect GDC. Would be nice to have
450 some way of detecting valid console output */
451 trapExceptions = true;
455 void tryExec(scope void delegate() dg)
465 _d_print_throwable(t);
466 result = EXIT_FAILURE;
475 // NOTE: The lifetime of a process is much like the lifetime of an object:
476 // it is initialized, then used, then destroyed. If initialization
477 // fails, the successive two steps are never reached. However, if
478 // initialization succeeds, then cleanup will occur even if the use
479 // step fails in some way. Here, the use phase consists of running
480 // the user's main function. If main terminates with an exception,
481 // the exception is handled and then cleanup begins. An exception
482 // thrown during cleanup, however, will abort the cleanup process.
485 if (rt_init() && runModuleUnitTests())
486 tryExec({ result = mainFunc(args); });
488 result = EXIT_FAILURE;
491 result = (result == EXIT_SUCCESS) ? EXIT_FAILURE : result;
496 // Issue 10344: flush stdout and return nonzero on failure
497 if (.fflush(.stdout) != 0)
499 .fprintf(.stderr, "Failed to flush stdout: %s\n", .strerror(.errno));
502 result = EXIT_FAILURE;
509 private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothrow sink)
511 for (; t; t = t.next)
513 t.toString(sink); sink("\n");
515 auto e = cast(Error)t;
516 if (e is null || e.bypassedException is null) continue;
518 sink("=== Bypassed ===\n");
519 for (auto t2 = e.bypassedException; t2; t2 = t2.next)
521 t2.toString(sink); sink("\n");
523 sink("=== ~Bypassed ===\n");
527 extern (C) void _d_print_throwable(Throwable t)
529 // On Windows, a console may not be present to print the output to.
530 // Show a message box instead. If the console is present, convert to
531 // the correct encoding.
536 wchar_t* ptr; size_t len;
538 void sink(in char[] s) scope nothrow
540 if (!s.length) return;
541 int swlen = MultiByteToWideChar(
542 CP_UTF8, 0, s.ptr, cast(int)s.length, null, 0);
545 auto newPtr = cast(wchar_t*)realloc(ptr,
546 (this.len + swlen + 1) * wchar_t.sizeof);
549 auto written = MultiByteToWideChar(
550 CP_UTF8, 0, s.ptr, cast(int)s.length, ptr+len, swlen);
554 wchar_t* get() { if (ptr) ptr[len] = 0; return ptr; }
556 void free() { .free(ptr); }
559 HANDLE windowsHandle(int fd)
561 version (CRuntime_Microsoft)
562 return cast(HANDLE)_get_osfhandle(fd);
564 return _fdToHandle(fd);
567 auto hStdErr = windowsHandle(fileno(stderr));
568 CONSOLE_SCREEN_BUFFER_INFO sbi;
569 bool isConsole = GetConsoleScreenBufferInfo(hStdErr, &sbi) != 0;
571 // ensure the exception is shown at the beginning of the line, while also
572 // checking whether stderr is a valid file
573 int written = fprintf(stderr, "\n");
577 formatThrowable(t, &buf.sink);
583 caption.sink(t.classinfo.name);
585 // Avoid static user32.dll dependency for console applications
586 // by loading it dynamically as needed
587 auto user32 = LoadLibraryW("user32.dll");
590 alias typeof(&MessageBoxW) PMessageBoxW;
591 auto pMessageBoxW = cast(PMessageBoxW)
592 GetProcAddress(user32, "MessageBoxW");
594 pMessageBoxW(null, buf.get(), caption.get(), MB_ICONERROR);
605 formatThrowable(t, &buf.sink);
609 uint codepage = GetConsoleOutputCP();
610 int slen = WideCharToMultiByte(codepage, 0,
611 buf.ptr, cast(int)buf.len, null, 0, null, null);
612 auto sptr = cast(char*)malloc(slen * char.sizeof);
615 WideCharToMultiByte(codepage, 0,
616 buf.ptr, cast(int)buf.len, sptr, slen, null, null);
617 WriteFile(hStdErr, sptr, slen, null, null);
626 void sink(in char[] buf) scope nothrow
628 fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr);
630 formatThrowable(t, &sink);