xref: /netbsd-src/external/gpl3/gcc.old/dist/libphobos/libdruntime/rt/dmain2.d (revision 924795e69c8bb3f17afd8fcbb799710cc1719dc4)
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 (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 
520 private 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 
538 extern (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