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