]>
Commit | Line | Data |
---|---|---|
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 | */ | |
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 | ||
22163f0d | 34 | pragma(lib, "shell32.lib"); // needed for CommandLineToArgvW |
03385ed3 | 35 | } |
36 | ||
37 | version (FreeBSD) | |
38 | { | |
39 | import core.stdc.fenv; | |
40 | } | |
41 | version (NetBSD) | |
42 | { | |
43 | import core.stdc.fenv; | |
44 | } | |
45 | ||
46 | extern (C) void _d_monitor_staticctor(); | |
47 | extern (C) void _d_monitor_staticdtor(); | |
48 | extern (C) void _d_critical_init(); | |
49 | extern (C) void _d_critical_term(); | |
50 | extern (C) void gc_init(); | |
51 | extern (C) void gc_term(); | |
52 | extern (C) void lifetime_init(); | |
53 | extern (C) void rt_moduleCtor(); | |
54 | extern (C) void rt_moduleTlsCtor(); | |
55 | extern (C) void rt_moduleDtor(); | |
56 | extern (C) void rt_moduleTlsDtor(); | |
57 | extern (C) void thread_joinAll(); | |
58 | extern (C) bool runModuleUnitTests(); | |
59 | extern (C) void _d_initMonoTime(); | |
60 | ||
61 | version (OSX) | |
62 | { | |
63 | // The bottom of the stack | |
64 | extern (C) __gshared void* __osx_stack_end = cast(void*)0xC0000000; | |
65 | } | |
66 | ||
67 | version (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 | */ | |
76 | extern (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 | ||
87 | version (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 | ||
141 | extern (C) string[] rt_args() | |
142 | { | |
143 | return _d_args; | |
144 | } | |
145 | ||
146 | // make arguments passed to main available for being filtered by runtime initializers | |
147 | extern(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. | |
151 | extern (C) __gshared bool rt_trapExceptions = true; | |
152 | ||
153 | alias void delegate(Throwable) ExceptionHandler; | |
154 | ||
155 | /** | |
156 | * Keep track of how often rt_init/rt_term were called. | |
157 | */ | |
158 | shared 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 | */ | |
165 | extern (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 | */ | |
206 | extern (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 | */ | |
235 | alias Throwable.TraceInfo function(void* ptr) TraceHandler; | |
236 | private __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 | */ | |
245 | extern (C) void rt_setTraceHandler(TraceHandler h) | |
246 | { | |
247 | traceHandler = h; | |
248 | } | |
249 | ||
250 | /** | |
251 | * Return the current trace handler | |
252 | */ | |
253 | extern (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 | */ | |
272 | extern (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 | ||
284 | struct CArgs | |
285 | { | |
286 | int argc; | |
287 | char** argv; | |
288 | } | |
289 | ||
290 | __gshared CArgs _cArgs; | |
291 | ||
292 | extern (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 | */ | |
302 | private alias extern(C) int function(char[][] args) MainFunc; | |
303 | ||
304 | extern (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 | ||
508 | private 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 | ||
526 | extern (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 | } |