]> git.ipfire.org Git - thirdparty/gcc.git/blob - libphobos/libdruntime/rt/dmain2.d
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
[thirdparty/gcc.git] / libphobos / libdruntime / rt / dmain2.d
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(const scope 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(t.classinfo.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(const scope char[] buf) scope nothrow
671 {
672 fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr);
673 }
674 formatThrowable(t, &sink);
675 }