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