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