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