]> git.ipfire.org Git - thirdparty/gcc.git/blob - libphobos/libdruntime/rt/dmain2.d
e1cfb7c23aacd4dbc4d71207be7cc63774d04bcd
[thirdparty/gcc.git] / libphobos / libdruntime / rt / dmain2.d
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 version (GNU) {}
35 else pragma(lib, "shell32.lib"); // needed for CommandLineToArgvW
36 }
37
38 version (FreeBSD)
39 {
40 import core.stdc.fenv;
41 }
42 version (NetBSD)
43 {
44 import core.stdc.fenv;
45 }
46
47 extern (C) void _d_monitor_staticctor();
48 extern (C) void _d_monitor_staticdtor();
49 extern (C) void _d_critical_init();
50 extern (C) void _d_critical_term();
51 extern (C) void gc_init();
52 extern (C) void gc_term();
53 extern (C) void lifetime_init();
54 extern (C) void rt_moduleCtor();
55 extern (C) void rt_moduleTlsCtor();
56 extern (C) void rt_moduleDtor();
57 extern (C) void rt_moduleTlsDtor();
58 extern (C) void thread_joinAll();
59 extern (C) bool runModuleUnitTests();
60 extern (C) void _d_initMonoTime();
61
62 version (OSX)
63 {
64 // The bottom of the stack
65 extern (C) __gshared void* __osx_stack_end = cast(void*)0xC0000000;
66 }
67
68 version (CRuntime_Microsoft)
69 {
70 extern(C) void init_msvc();
71 }
72
73 /***********************************
74 * These are a temporary means of providing a GC hook for DLL use. They may be
75 * replaced with some other similar functionality later.
76 */
77 extern (C)
78 {
79 void* gc_getProxy();
80 void gc_setProxy(void* p);
81 void gc_clrProxy();
82
83 alias void* function() gcGetFn;
84 alias void function(void*) gcSetFn;
85 alias void function() gcClrFn;
86 }
87
88 version (Windows)
89 {
90 /*******************************************
91 * Loads a DLL written in D with the name 'name'.
92 * Returns:
93 * opaque handle to the DLL if successfully loaded
94 * null if failure
95 */
96 extern (C) void* rt_loadLibrary(const char* name)
97 {
98 return initLibrary(.LoadLibraryA(name));
99 }
100
101 extern (C) void* rt_loadLibraryW(const wchar_t* name)
102 {
103 return initLibrary(.LoadLibraryW(name));
104 }
105
106 void* initLibrary(void* mod)
107 {
108 // BUG: LoadLibrary() call calls rt_init(), which fails if proxy is not set!
109 // (What? LoadLibrary() is a Windows API call, it shouldn't call rt_init().)
110 if (mod is null)
111 return mod;
112 gcSetFn gcSet = cast(gcSetFn) GetProcAddress(mod, "gc_setProxy");
113 if (gcSet !is null)
114 { // BUG: Set proxy, but too late
115 gcSet(gc_getProxy());
116 }
117 return mod;
118 }
119
120 /*************************************
121 * Unloads DLL that was previously loaded by rt_loadLibrary().
122 * Input:
123 * ptr the handle returned by rt_loadLibrary()
124 * Returns:
125 * 1 succeeded
126 * 0 some failure happened
127 */
128 extern (C) int rt_unloadLibrary(void* ptr)
129 {
130 gcClrFn gcClr = cast(gcClrFn) GetProcAddress(ptr, "gc_clrProxy");
131 if (gcClr !is null)
132 gcClr();
133 return FreeLibrary(ptr) != 0;
134 }
135 }
136
137 /* To get out-of-band access to the args[] passed to main().
138 */
139
140 __gshared string[] _d_args = null;
141
142 extern (C) string[] rt_args()
143 {
144 return _d_args;
145 }
146
147 // make arguments passed to main available for being filtered by runtime initializers
148 extern(C) __gshared char[][] _d_main_args = null;
149
150 // This variable is only ever set by a debugger on initialization so it should
151 // be fine to leave it as __gshared.
152 extern (C) __gshared bool rt_trapExceptions = true;
153
154 alias void delegate(Throwable) ExceptionHandler;
155
156 /**
157 * Keep track of how often rt_init/rt_term were called.
158 */
159 shared size_t _initCount;
160
161 /**********************************************
162 * Initialize druntime.
163 * If a C program wishes to call D code, and there's no D main(), then it
164 * must call rt_init() and rt_term().
165 */
166 extern (C) int rt_init()
167 {
168 /* @@BUG 11380 @@ Need to synchronize rt_init/rt_term calls for
169 version (Shared) druntime, because multiple C threads might
170 initialize different D libraries without knowing about the
171 shared druntime. Also we need to attach any thread that calls
172 rt_init. */
173 if (atomicOp!"+="(_initCount, 1) > 1) return 1;
174
175 version (CRuntime_Microsoft)
176 init_msvc();
177
178 _d_monitor_staticctor();
179 _d_critical_init();
180
181 try
182 {
183 initSections();
184 // this initializes mono time before anything else to allow usage
185 // in other druntime systems.
186 _d_initMonoTime();
187 gc_init();
188 initStaticDataGC();
189 lifetime_init();
190 rt_moduleCtor();
191 rt_moduleTlsCtor();
192 return 1;
193 }
194 catch (Throwable t)
195 {
196 _initCount = 0;
197 _d_print_throwable(t);
198 }
199 _d_critical_term();
200 _d_monitor_staticdtor();
201 return 0;
202 }
203
204 /**********************************************
205 * Terminate use of druntime.
206 */
207 extern (C) int rt_term()
208 {
209 if (!_initCount) return 0; // was never initialized
210 if (atomicOp!"-="(_initCount, 1)) return 1;
211
212 try
213 {
214 rt_moduleTlsDtor();
215 thread_joinAll();
216 rt_moduleDtor();
217 gc_term();
218 return 1;
219 }
220 catch (Throwable t)
221 {
222 _d_print_throwable(t);
223 }
224 finally
225 {
226 finiSections();
227 _d_critical_term();
228 _d_monitor_staticdtor();
229 }
230 return 0;
231 }
232
233 /**********************************************
234 * Trace handler
235 */
236 alias Throwable.TraceInfo function(void* ptr) TraceHandler;
237 private __gshared TraceHandler traceHandler = null;
238
239
240 /**
241 * Overrides the default trace hander with a user-supplied version.
242 *
243 * Params:
244 * h = The new trace handler. Set to null to use the default handler.
245 */
246 extern (C) void rt_setTraceHandler(TraceHandler h)
247 {
248 traceHandler = h;
249 }
250
251 /**
252 * Return the current trace handler
253 */
254 extern (C) TraceHandler rt_getTraceHandler()
255 {
256 return traceHandler;
257 }
258
259 /**
260 * This function will be called when an exception is constructed. The
261 * user-supplied trace handler will be called if one has been supplied,
262 * otherwise no trace will be generated.
263 *
264 * Params:
265 * ptr = A pointer to the location from which to generate the trace, or null
266 * if the trace should be generated from within the trace handler
267 * itself.
268 *
269 * Returns:
270 * An object describing the current calling context or null if no handler is
271 * supplied.
272 */
273 extern (C) Throwable.TraceInfo _d_traceContext(void* ptr = null)
274 {
275 if (traceHandler is null)
276 return null;
277 return traceHandler(ptr);
278 }
279
280 /***********************************
281 * Provide out-of-band access to the original C argc/argv
282 * passed to this program via main(argc,argv).
283 */
284
285 struct CArgs
286 {
287 int argc;
288 char** argv;
289 }
290
291 __gshared CArgs _cArgs;
292
293 extern (C) CArgs rt_cArgs() @nogc
294 {
295 return _cArgs;
296 }
297
298 /***********************************
299 * Run the given main function.
300 * Its purpose is to wrap the D main()
301 * function and catch any unhandled exceptions.
302 */
303 private alias extern(C) int function(char[][] args) MainFunc;
304
305 extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
306 {
307 // Remember the original C argc/argv
308 _cArgs.argc = argc;
309 _cArgs.argv = argv;
310
311 int result;
312
313 version (OSX)
314 { /* OSX does not provide a way to get at the top of the
315 * stack, except for the magic value 0xC0000000.
316 * But as far as the gc is concerned, argv is at the top
317 * of the main thread's stack, so save the address of that.
318 */
319 __osx_stack_end = cast(void*)&argv;
320 }
321
322 version (FreeBSD) version (D_InlineAsm_X86)
323 {
324 /*
325 * FreeBSD/i386 sets the FPU precision mode to 53 bit double.
326 * Make it 64 bit extended.
327 */
328 ushort fpucw;
329 asm
330 {
331 fstsw fpucw;
332 or fpucw, 0b11_00_111111; // 11: use 64 bit extended-precision
333 // 111111: mask all FP exceptions
334 fldcw fpucw;
335 }
336 }
337 version (CRuntime_Microsoft)
338 {
339 // enable full precision for reals
340 version (Win64)
341 asm
342 {
343 push RAX;
344 fstcw word ptr [RSP];
345 or [RSP], 0b11_00_111111; // 11: use 64 bit extended-precision
346 // 111111: mask all FP exceptions
347 fldcw word ptr [RSP];
348 pop RAX;
349 }
350 else version (Win32)
351 {
352 asm
353 {
354 push EAX;
355 fstcw word ptr [ESP];
356 or [ESP], 0b11_00_111111; // 11: use 64 bit extended-precision
357 // 111111: mask all FP exceptions
358 fldcw word ptr [ESP];
359 pop EAX;
360 }
361 }
362 }
363
364 version (Windows)
365 {
366 /* Because we want args[] to be UTF-8, and Windows doesn't guarantee that,
367 * we ignore argc/argv and go get the Windows command line again as UTF-16.
368 * Then, reparse into wargc/wargs, and then use Windows API to convert
369 * to UTF-8.
370 */
371 const wchar_t* wCommandLine = GetCommandLineW();
372 immutable size_t wCommandLineLength = wcslen(wCommandLine);
373 int wargc;
374 wchar_t** wargs = CommandLineToArgvW(wCommandLine, &wargc);
375 // assert(wargc == argc); /* argc can be broken by Unicode arguments */
376
377 // Allocate args[] on the stack - use wargc
378 char[][] args = (cast(char[]*) alloca(wargc * (char[]).sizeof))[0 .. wargc];
379
380 // This is required because WideCharToMultiByte requires int as input.
381 assert(wCommandLineLength <= cast(size_t) int.max, "Wide char command line length must not exceed int.max");
382
383 immutable size_t totalArgsLength = WideCharToMultiByte(CP_UTF8, 0, wCommandLine, cast(int)wCommandLineLength, null, 0, null, null);
384 {
385 char* totalArgsBuff = cast(char*) alloca(totalArgsLength);
386 size_t j = 0;
387 foreach (i; 0 .. wargc)
388 {
389 immutable size_t wlen = wcslen(wargs[i]);
390 assert(wlen <= cast(size_t) int.max, "wlen cannot exceed int.max");
391 immutable int len = WideCharToMultiByte(CP_UTF8, 0, &wargs[i][0], cast(int) wlen, null, 0, null, null);
392 args[i] = totalArgsBuff[j .. j + len];
393 if (len == 0)
394 continue;
395 j += len;
396 assert(j <= totalArgsLength);
397 WideCharToMultiByte(CP_UTF8, 0, &wargs[i][0], cast(int) wlen, &args[i][0], len, null, null);
398 }
399 }
400 LocalFree(wargs);
401 wargs = null;
402 wargc = 0;
403 }
404 else version (Posix)
405 {
406 // Allocate args[] on the stack
407 char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc];
408
409 size_t totalArgsLength = 0;
410 foreach (i, ref arg; args)
411 {
412 arg = argv[i][0 .. strlen(argv[i])];
413 totalArgsLength += arg.length;
414 }
415 }
416 else
417 static assert(0);
418
419 /* Create a copy of args[] on the stack to be used for main, so that rt_args()
420 * cannot be modified by the user.
421 * Note that when this function returns, _d_args will refer to garbage.
422 */
423 {
424 _d_args = cast(string[]) args;
425 auto buff = cast(char[]*) alloca(args.length * (char[]).sizeof + totalArgsLength);
426
427 char[][] argsCopy = buff[0 .. args.length];
428 auto argBuff = cast(char*) (buff + args.length);
429 size_t j = 0;
430 foreach (arg; args)
431 {
432 if (arg.length < 6 || arg[0..6] != "--DRT-") // skip D runtime options
433 {
434 argsCopy[j++] = (argBuff[0 .. arg.length] = arg[]);
435 argBuff += arg.length;
436 }
437 }
438 args = argsCopy[0..j];
439 }
440
441 bool trapExceptions = rt_trapExceptions;
442
443 version (Windows)
444 {
445 if (IsDebuggerPresent())
446 trapExceptions = false;
447 version (GNU)
448 {
449 /* IsDebuggerPresent doesn't detect GDC. Would be nice to have
450 some way of detecting valid console output */
451 trapExceptions = true;
452 }
453 }
454
455 void tryExec(scope void delegate() dg)
456 {
457 if (trapExceptions)
458 {
459 try
460 {
461 dg();
462 }
463 catch (Throwable t)
464 {
465 _d_print_throwable(t);
466 result = EXIT_FAILURE;
467 }
468 }
469 else
470 {
471 dg();
472 }
473 }
474
475 // NOTE: The lifetime of a process is much like the lifetime of an object:
476 // it is initialized, then used, then destroyed. If initialization
477 // fails, the successive two steps are never reached. However, if
478 // initialization succeeds, then cleanup will occur even if the use
479 // step fails in some way. Here, the use phase consists of running
480 // the user's main function. If main terminates with an exception,
481 // the exception is handled and then cleanup begins. An exception
482 // thrown during cleanup, however, will abort the cleanup process.
483 void runAll()
484 {
485 if (rt_init() && runModuleUnitTests())
486 tryExec({ result = mainFunc(args); });
487 else
488 result = EXIT_FAILURE;
489
490 if (!rt_term())
491 result = (result == EXIT_SUCCESS) ? EXIT_FAILURE : result;
492 }
493
494 tryExec(&runAll);
495
496 // Issue 10344: flush stdout and return nonzero on failure
497 if (.fflush(.stdout) != 0)
498 {
499 .fprintf(.stderr, "Failed to flush stdout: %s\n", .strerror(.errno));
500 if (result == 0)
501 {
502 result = EXIT_FAILURE;
503 }
504 }
505
506 return result;
507 }
508
509 private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothrow sink)
510 {
511 for (; t; t = t.next)
512 {
513 t.toString(sink); sink("\n");
514
515 auto e = cast(Error)t;
516 if (e is null || e.bypassedException is null) continue;
517
518 sink("=== Bypassed ===\n");
519 for (auto t2 = e.bypassedException; t2; t2 = t2.next)
520 {
521 t2.toString(sink); sink("\n");
522 }
523 sink("=== ~Bypassed ===\n");
524 }
525 }
526
527 extern (C) void _d_print_throwable(Throwable t)
528 {
529 // On Windows, a console may not be present to print the output to.
530 // Show a message box instead. If the console is present, convert to
531 // the correct encoding.
532 version (Windows)
533 {
534 static struct WSink
535 {
536 wchar_t* ptr; size_t len;
537
538 void sink(in char[] s) scope nothrow
539 {
540 if (!s.length) return;
541 int swlen = MultiByteToWideChar(
542 CP_UTF8, 0, s.ptr, cast(int)s.length, null, 0);
543 if (!swlen) return;
544
545 auto newPtr = cast(wchar_t*)realloc(ptr,
546 (this.len + swlen + 1) * wchar_t.sizeof);
547 if (!newPtr) return;
548 ptr = newPtr;
549 auto written = MultiByteToWideChar(
550 CP_UTF8, 0, s.ptr, cast(int)s.length, ptr+len, swlen);
551 len += written;
552 }
553
554 wchar_t* get() { if (ptr) ptr[len] = 0; return ptr; }
555
556 void free() { .free(ptr); }
557 }
558
559 HANDLE windowsHandle(int fd)
560 {
561 version (CRuntime_Microsoft)
562 return cast(HANDLE)_get_osfhandle(fd);
563 else
564 return _fdToHandle(fd);
565 }
566
567 auto hStdErr = windowsHandle(fileno(stderr));
568 CONSOLE_SCREEN_BUFFER_INFO sbi;
569 bool isConsole = GetConsoleScreenBufferInfo(hStdErr, &sbi) != 0;
570
571 // ensure the exception is shown at the beginning of the line, while also
572 // checking whether stderr is a valid file
573 int written = fprintf(stderr, "\n");
574 if (written <= 0)
575 {
576 WSink buf;
577 formatThrowable(t, &buf.sink);
578
579 if (buf.ptr)
580 {
581 WSink caption;
582 if (t)
583 caption.sink(t.classinfo.name);
584
585 // Avoid static user32.dll dependency for console applications
586 // by loading it dynamically as needed
587 auto user32 = LoadLibraryW("user32.dll");
588 if (user32)
589 {
590 alias typeof(&MessageBoxW) PMessageBoxW;
591 auto pMessageBoxW = cast(PMessageBoxW)
592 GetProcAddress(user32, "MessageBoxW");
593 if (pMessageBoxW)
594 pMessageBoxW(null, buf.get(), caption.get(), MB_ICONERROR);
595 }
596 FreeLibrary(user32);
597 caption.free();
598 buf.free();
599 }
600 return;
601 }
602 else if (isConsole)
603 {
604 WSink buf;
605 formatThrowable(t, &buf.sink);
606
607 if (buf.ptr)
608 {
609 uint codepage = GetConsoleOutputCP();
610 int slen = WideCharToMultiByte(codepage, 0,
611 buf.ptr, cast(int)buf.len, null, 0, null, null);
612 auto sptr = cast(char*)malloc(slen * char.sizeof);
613 if (sptr)
614 {
615 WideCharToMultiByte(codepage, 0,
616 buf.ptr, cast(int)buf.len, sptr, slen, null, null);
617 WriteFile(hStdErr, sptr, slen, null, null);
618 free(sptr);
619 }
620 buf.free();
621 }
622 return;
623 }
624 }
625
626 void sink(in char[] buf) scope nothrow
627 {
628 fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr);
629 }
630 formatThrowable(t, &sink);
631 }