master
  1// This file is included in the compilation unit when exporting an executable.
  2
  3const root = @import("root");
  4const std = @import("std.zig");
  5const builtin = @import("builtin");
  6const assert = std.debug.assert;
  7const uefi = std.os.uefi;
  8const elf = std.elf;
  9const native_arch = builtin.cpu.arch;
 10const native_os = builtin.os.tag;
 11
 12const start_sym_name = if (native_arch.isMIPS()) "__start" else "_start";
 13
 14// The self-hosted compiler is not fully capable of handling all of this start.zig file.
 15// Until then, we have simplified logic here for self-hosted. TODO remove this once
 16// self-hosted is capable enough to handle all of the real start.zig logic.
 17pub const simplified_logic = switch (builtin.zig_backend) {
 18    .stage2_aarch64,
 19    .stage2_arm,
 20    .stage2_powerpc,
 21    .stage2_sparc64,
 22    .stage2_spirv,
 23    .stage2_x86,
 24    => true,
 25    else => false,
 26};
 27
 28comptime {
 29    // No matter what, we import the root file, so that any export, test, comptime
 30    // decls there get run.
 31    _ = root;
 32
 33    if (simplified_logic) {
 34        if (builtin.output_mode == .Exe) {
 35            if ((builtin.link_libc or builtin.object_format == .c) and @hasDecl(root, "main")) {
 36                if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) {
 37                    @export(&main2, .{ .name = "main" });
 38                }
 39            } else if (builtin.os.tag == .windows) {
 40                if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) {
 41                    @export(&wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
 42                }
 43            } else if (builtin.os.tag == .opencl or builtin.os.tag == .vulkan) {
 44                if (@hasDecl(root, "main"))
 45                    @export(&spirvMain2, .{ .name = "main" });
 46            } else {
 47                if (!@hasDecl(root, "_start")) {
 48                    @export(&_start2, .{ .name = "_start" });
 49                }
 50            }
 51        }
 52    } else {
 53        if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) {
 54            if (native_os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) {
 55                @export(&_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" });
 56            }
 57        } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) {
 58            if (builtin.link_libc and @hasDecl(root, "main")) {
 59                if (native_arch.isWasm()) {
 60                    @export(&mainWithoutEnv, .{ .name = "__main_argc_argv" });
 61                } else if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) {
 62                    @export(&main, .{ .name = "main" });
 63                }
 64            } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "wWinMain")) {
 65                if (!@typeInfo(@TypeOf(root.wWinMain)).@"fn".calling_convention.eql(.c)) {
 66                    @export(&wWinMain, .{ .name = "wWinMain" });
 67                }
 68            } else if (native_os == .windows) {
 69                if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and
 70                    !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))
 71                {
 72                    @export(&WinStartup, .{ .name = "wWinMainCRTStartup" });
 73                } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and
 74                    !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))
 75                {
 76                    @compileError("WinMain not supported; declare wWinMain or main instead");
 77                } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and
 78                    !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup"))
 79                {
 80                    @export(&wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" });
 81                }
 82            } else if (native_os == .uefi) {
 83                if (!@hasDecl(root, "EfiMain")) @export(&EfiMain, .{ .name = "EfiMain" });
 84            } else if (native_os == .wasi) {
 85                const wasm_start_sym = switch (builtin.wasi_exec_model) {
 86                    .reactor => "_initialize",
 87                    .command => "_start",
 88                };
 89                if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) {
 90                    // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which
 91                    // case it's not required to provide an entrypoint such as main.
 92                    @export(&wasi_start, .{ .name = wasm_start_sym });
 93                }
 94            } else if (native_arch.isWasm() and native_os == .freestanding) {
 95                // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which
 96                // case it's not required to provide an entrypoint such as main.
 97                if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(&wasm_freestanding_start, .{ .name = start_sym_name });
 98            } else switch (native_os) {
 99                .other, .freestanding, .@"3ds", .vita => {},
100                else => if (!@hasDecl(root, start_sym_name)) @export(&_start, .{ .name = start_sym_name }),
101            }
102        }
103    }
104}
105
106// Simplified start code for stage2 until it supports more language features ///
107
108fn main2() callconv(.c) c_int {
109    return callMain();
110}
111
112fn _start2() callconv(.withStackAlign(.c, 1)) noreturn {
113    std.posix.exit(callMain());
114}
115
116fn spirvMain2() callconv(.kernel) void {
117    root.main();
118}
119
120fn wWinMainCRTStartup2() callconv(.c) noreturn {
121    std.posix.exit(callMain());
122}
123
124////////////////////////////////////////////////////////////////////////////////
125
126fn _DllMainCRTStartup(
127    hinstDLL: std.os.windows.HINSTANCE,
128    fdwReason: std.os.windows.DWORD,
129    lpReserved: std.os.windows.LPVOID,
130) callconv(.winapi) std.os.windows.BOOL {
131    if (!builtin.single_threaded and !builtin.link_libc) {
132        _ = @import("os/windows/tls.zig");
133    }
134
135    if (@hasDecl(root, "DllMain")) {
136        return root.DllMain(hinstDLL, fdwReason, lpReserved);
137    }
138
139    return std.os.windows.TRUE;
140}
141
142fn wasm_freestanding_start() callconv(.c) void {
143    // This is marked inline because for some reason LLVM in
144    // release mode fails to inline it, and we want fewer call frames in stack traces.
145    _ = @call(.always_inline, callMain, .{});
146}
147
148fn wasi_start() callconv(.c) void {
149    // The function call is marked inline because for some reason LLVM in
150    // release mode fails to inline it, and we want fewer call frames in stack traces.
151    switch (builtin.wasi_exec_model) {
152        .reactor => _ = @call(.always_inline, callMain, .{}),
153        .command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{})),
154    }
155}
156
157fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv(.c) usize {
158    uefi.handle = handle;
159    uefi.system_table = system_table;
160
161    switch (@typeInfo(@TypeOf(root.main)).@"fn".return_type.?) {
162        noreturn => {
163            root.main();
164        },
165        void => {
166            root.main();
167            return 0;
168        },
169        uefi.Status => {
170            return @intFromEnum(root.main());
171        },
172        uefi.Error!void => {
173            root.main() catch |err| switch (err) {
174                error.Unexpected => @panic("EfiMain: unexpected error"),
175                else => {
176                    const status = uefi.Status.fromError(@errorCast(err));
177                    return @intFromEnum(status);
178                },
179            };
180
181            return 0;
182        },
183        else => @compileError(
184            "expected return type of main to be 'void', 'noreturn', " ++
185                "'uefi.Status', or 'uefi.Error!void'",
186        ),
187    }
188}
189
190fn _start() callconv(.naked) noreturn {
191    // TODO set Top of Stack on non x86_64-plan9
192    if (native_os == .plan9 and native_arch == .x86_64) {
193        // from /sys/src/libc/amd64/main9.s
194        std.os.plan9.tos = asm volatile (""
195            : [tos] "={rax}" (-> *std.os.plan9.Tos),
196        );
197    }
198
199    // This is the first userspace frame. Prevent DWARF-based unwinders from unwinding further. We
200    // prevent FP-based unwinders from unwinding further by zeroing the register below.
201    if (builtin.unwind_tables != .none or !builtin.strip_debug_info) asm volatile (switch (native_arch) {
202            .aarch64, .aarch64_be => ".cfi_undefined lr",
203            .alpha => ".cfi_undefined $26",
204            .arc, .arceb => ".cfi_undefined blink",
205            .arm, .armeb, .thumb, .thumbeb => "", // https://github.com/llvm/llvm-project/issues/115891
206            .csky => ".cfi_undefined lr",
207            .hexagon => ".cfi_undefined r31",
208            .kvx => ".cfi_undefined r14",
209            .loongarch32, .loongarch64 => ".cfi_undefined 1",
210            .m68k => ".cfi_undefined %%pc",
211            .microblaze, .microblazeel => ".cfi_undefined r15",
212            .mips, .mipsel, .mips64, .mips64el => ".cfi_undefined $ra",
213            .or1k => ".cfi_undefined r9",
214            .powerpc, .powerpcle, .powerpc64, .powerpc64le => ".cfi_undefined lr",
215            .riscv32, .riscv32be, .riscv64, .riscv64be => if (builtin.zig_backend == .stage2_riscv64)
216                ""
217            else
218                ".cfi_undefined ra",
219            .s390x => ".cfi_undefined %%r14",
220            .sh, .sheb => ".cfi_undefined pr",
221            .sparc, .sparc64 => ".cfi_undefined %%i7",
222            .x86 => ".cfi_undefined %%eip",
223            .x86_64 => ".cfi_undefined %%rip",
224            else => @compileError("unsupported arch"),
225        });
226
227    // Move this to the riscv prong below when this is resolved: https://github.com/ziglang/zig/issues/20918
228    if (builtin.cpu.arch.isRISCV() and builtin.zig_backend != .stage2_riscv64) asm volatile (
229        \\ .weak __global_pointer$
230        \\ .hidden __global_pointer$
231        \\ .option push
232        \\ .option norelax
233        \\ lla gp, __global_pointer$
234        \\ .option pop
235    );
236
237    // Note that we maintain a very low level of trust with regards to ABI guarantees at this point.
238    // We will redundantly align the stack, clear the link register, etc. While e.g. the Linux
239    // kernel is usually good about upholding the ABI guarantees, the same cannot be said of dynamic
240    // linkers; musl's ldso, for example, opts to not align the stack when invoking the dynamic
241    // linker explicitly.
242    asm volatile (switch (native_arch) {
243            .x86_64 =>
244            \\ xorl %%ebp, %%ebp
245            \\ movq %%rsp, %%rdi
246            \\ andq $-16, %%rsp
247            \\ callq %[posixCallMainAndExit:P]
248            ,
249            .x86 =>
250            \\ xorl %%ebp, %%ebp
251            \\ movl %%esp, %%eax
252            \\ andl $-16, %%esp
253            \\ subl $12, %%esp
254            \\ pushl %%eax
255            \\ calll %[posixCallMainAndExit:P]
256            ,
257            .aarch64, .aarch64_be =>
258            \\ mov fp, #0
259            \\ mov lr, #0
260            \\ mov x0, sp
261            \\ and sp, x0, #-16
262            \\ b %[posixCallMainAndExit]
263            ,
264            .alpha =>
265            // $15 = FP, $26 = LR, $29 = GP, $30 = SP
266            \\ br $29, 1f
267            \\1:
268            \\ ldgp $29, 0($29)
269            \\ mov 0, $15
270            \\ mov 0, $26
271            \\ mov $30, $16
272            \\ ldi $1, -16
273            \\ and $30, $30, $1
274            \\ jsr $26, %[posixCallMainAndExit]
275            ,
276            .arc, .arceb =>
277            // ARC v1 and v2 had a very low stack alignment requirement of 4; v3 increased it to 16.
278            \\ mov fp, 0
279            \\ mov blink, 0
280            \\ mov r0, sp
281            \\ and sp, sp, -16
282            \\ b %[posixCallMainAndExit]
283            ,
284            .arm, .armeb, .thumb, .thumbeb =>
285            // Note that this code must work for Thumb-1.
286            // r7 = FP (local), r11 = FP (unwind)
287            \\ movs v1, #0
288            \\ mov r7, v1
289            \\ mov r11, v1
290            \\ mov lr, v1
291            \\ mov a1, sp
292            \\ subs v1, #16
293            \\ ands v1, a1
294            \\ mov sp, v1
295            \\ b %[posixCallMainAndExit]
296            ,
297            .csky =>
298            // The CSKY ABI assumes that `gb` is set to the address of the GOT in order for
299            // position-independent code to work. We depend on this in `std.pie` to locate
300            // `_DYNAMIC` as well.
301            // r8 = FP
302            \\ grs t0, 1f
303            \\ 1:
304            \\ lrw gb, 1b@GOTPC
305            \\ addu gb, t0
306            \\ movi r8, 0
307            \\ movi lr, 0
308            \\ mov a0, sp
309            \\ andi sp, sp, -8
310            \\ jmpi %[posixCallMainAndExit]
311            ,
312            .hexagon =>
313            // r29 = SP, r30 = FP, r31 = LR
314            \\ r30 = #0
315            \\ r31 = #0
316            \\ r0 = r29
317            \\ r29 = and(r29, #-8)
318            \\ memw(r29 + #-8) = r29
319            \\ r29 = add(r29, #-8)
320            \\ call %[posixCallMainAndExit]
321            ,
322            .kvx =>
323            \\ make $fp = 0
324            \\ ;;
325            \\ set $ra = $fp
326            \\ copyd $r0 = $sp
327            \\ andd $sp = $sp, -32
328            \\ ;;
329            \\ goto %[posixCallMainAndExit]
330            ,
331            .loongarch32, .loongarch64 =>
332            \\ move $fp, $zero
333            \\ move $ra, $zero
334            \\ move $a0, $sp
335            \\ bstrins.d $sp, $zero, 3, 0
336            \\ b %[posixCallMainAndExit]
337            ,
338            .or1k =>
339            // r1 = SP, r2 = FP, r9 = LR
340            \\ l.ori r2, r0, 0
341            \\ l.ori r9, r0, 0
342            \\ l.ori r3, r1, 0
343            \\ l.andi r1, r1, -4
344            \\ l.jal %[posixCallMainAndExit]
345            ,
346            .riscv32, .riscv32be, .riscv64, .riscv64be =>
347            \\ li fp, 0
348            \\ li ra, 0
349            \\ mv a0, sp
350            \\ andi sp, sp, -16
351            \\ tail %[posixCallMainAndExit]@plt
352            ,
353            .m68k =>
354            // Note that the - 8 is needed because pc in the jsr instruction points into the middle
355            // of the jsr instruction. (The lea is 6 bytes, the jsr is 4 bytes.)
356            \\ suba.l %%fp, %%fp
357            \\ move.l %%sp, %%a0
358            \\ move.l %%a0, %%d0
359            \\ and.l #-4, %%d0
360            \\ move.l %%d0, %%sp
361            \\ move.l %%a0, -(%%sp)
362            \\ lea %[posixCallMainAndExit] - . - 8, %%a0
363            \\ jsr (%%pc, %%a0)
364            ,
365            .microblaze, .microblazeel =>
366            // r1 = SP, r15 = LR, r19 = FP, r20 = GP
367            \\ ori r15, r0, r0
368            \\ ori r19, r0, r0
369            \\ mfs r20, rpc
370            \\ addik r20, r20, _GLOBAL_OFFSET_TABLE_ + 8
371            \\ ori r5, r1, r0
372            \\ andi r1, r1, -4
373            \\ brlid r15, %[posixCallMainAndExit]
374            ,
375            .mips, .mipsel =>
376            \\ move $fp, $zero
377            \\ bal 1f
378            \\ .gpword .
379            \\ .gpword %[posixCallMainAndExit]
380            \\1:
381            // The `gp` register on MIPS serves a similar purpose to `r2` (ToC pointer) on PPC64.
382            \\ lw $gp, 0($ra)
383            \\ nop
384            \\ subu $gp, $ra, $gp
385            \\ lw $t9, 4($ra)
386            \\ nop
387            \\ addu $t9, $t9, $gp
388            \\ move $ra, $zero
389            \\ move $a0, $sp
390            \\ and $sp, -8
391            \\ subu $sp, $sp, 16
392            \\ jalr $t9
393            ,
394            .mips64, .mips64el => switch (builtin.abi) {
395                .gnuabin32, .muslabin32 =>
396                \\ move $fp, $zero
397                \\ bal 1f
398                \\ .gpword .
399                \\ .gpword %[posixCallMainAndExit]
400                \\1:
401                // The `gp` register on MIPS serves a similar purpose to `r2` (ToC pointer) on PPC64.
402                \\ lw $gp, 0($ra)
403                \\ subu $gp, $ra, $gp
404                \\ lw $t9, 4($ra)
405                \\ addu $t9, $t9, $gp
406                \\ move $ra, $zero
407                \\ move $a0, $sp
408                \\ and $sp, -8
409                \\ subu $sp, $sp, 16
410                \\ jalr $t9
411                ,
412                else =>
413                \\ move $fp, $zero
414                // This is needed because early MIPS versions don't support misaligned loads. Without
415                // this directive, the hidden `nop` inserted to fill the delay slot after `bal` would
416                // cause the two doublewords to be aligned to 4 bytes instead of 8.
417                \\ .balign 8
418                \\ bal 1f
419                \\ .gpdword .
420                \\ .gpdword %[posixCallMainAndExit]
421                \\1:
422                // The `gp` register on MIPS serves a similar purpose to `r2` (ToC pointer) on PPC64.
423                \\ ld $gp, 0($ra)
424                \\ dsubu $gp, $ra, $gp
425                \\ ld $t9, 8($ra)
426                \\ daddu $t9, $t9, $gp
427                \\ move $ra, $zero
428                \\ move $a0, $sp
429                \\ and $sp, -16
430                \\ dsubu $sp, $sp, 16
431                \\ jalr $t9
432                ,
433            },
434            .powerpc, .powerpcle =>
435            // Set up the initial stack frame, and clear the back chain pointer.
436            // r1 = SP, r31 = FP
437            \\ mr 3, 1
438            \\ clrrwi 1, 1, 4
439            \\ li 0, 0
440            \\ stwu 1, -16(1)
441            \\ stw 0, 0(1)
442            \\ li 31, 0
443            \\ mtlr 0
444            \\ b %[posixCallMainAndExit]
445            ,
446            .powerpc64, .powerpc64le =>
447            // Set up the ToC and initial stack frame, and clear the back chain pointer.
448            // r1 = SP, r2 = ToC, r31 = FP
449            \\ addis 2, 12, .TOC. - %[_start]@ha
450            \\ addi 2, 2, .TOC. - %[_start]@l
451            \\ mr 3, 1
452            \\ clrrdi 1, 1, 4
453            \\ li 0, 0
454            \\ stdu 0, -32(1)
455            \\ li 31, 0
456            \\ mtlr 0
457            \\ b %[posixCallMainAndExit]
458            \\ nop
459            ,
460            .s390x =>
461            // Set up the stack frame (register save area and cleared back-chain slot).
462            // r11 = FP, r14 = LR, r15 = SP
463            \\ lghi %%r11, 0
464            \\ lghi %%r14, 0
465            \\ lgr %%r2, %%r15
466            \\ lghi %%r0, -16
467            \\ ngr %%r15, %%r0
468            \\ aghi %%r15, -160
469            \\ lghi %%r0, 0
470            \\ stg  %%r0, 0(%%r15)
471            \\ jg %[posixCallMainAndExit]
472            ,
473            .sh, .sheb =>
474            // r14 = FP, r15 = SP, pr = LR
475            \\ mov #0, r0
476            \\ lds r0, pr
477            \\ mov r0, r14
478            \\ mov r15, r4
479            \\ mov #-4, r0
480            \\ and r0, r15
481            \\ mov.l 2f, r1
482            \\1:
483            \\ bsrf r1
484            \\2:
485            \\ .balign 4
486            \\ .long %[posixCallMainAndExit]@PCREL - (1b + 4 - .)
487            ,
488            .sparc =>
489            // argc is stored after a register window (16 registers * 4 bytes).
490            // i7 = LR
491            \\ mov %%g0, %%fp
492            \\ mov %%g0, %%i7
493            \\ add %%sp, 64, %%o0
494            \\ and %%sp, -8, %%sp
495            \\ ba,a %[posixCallMainAndExit]
496            ,
497            .sparc64 =>
498            // argc is stored after a register window (16 registers * 8 bytes) plus the stack bias
499            // (2047 bytes).
500            // i7 = LR
501            \\ mov %%g0, %%fp
502            \\ mov %%g0, %%i7
503            \\ add %%sp, 2175, %%o0
504            \\ add %%sp, 2047, %%sp
505            \\ and %%sp, -16, %%sp
506            \\ sub %%sp, 2047, %%sp
507            \\ ba,a %[posixCallMainAndExit]
508            ,
509            else => @compileError("unsupported arch"),
510        }
511        :
512        : [_start] "X" (&_start),
513          [posixCallMainAndExit] "X" (&posixCallMainAndExit),
514    );
515}
516
517fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn {
518    // Switch from the x87 fpu state set by windows to the state expected by the gnu abi.
519    if (builtin.cpu.arch.isX86() and builtin.abi == .gnu) asm volatile ("fninit");
520
521    if (!builtin.single_threaded and !builtin.link_libc) {
522        _ = @import("os/windows/tls.zig");
523    }
524
525    std.debug.maybeEnableSegfaultHandler();
526
527    std.os.windows.ntdll.RtlExitUserProcess(callMain());
528}
529
530fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn {
531    // Switch from the x87 fpu state set by windows to the state expected by the gnu abi.
532    if (builtin.cpu.arch.isX86() and builtin.abi == .gnu) asm volatile ("fninit");
533
534    if (!builtin.single_threaded and !builtin.link_libc) {
535        _ = @import("os/windows/tls.zig");
536    }
537
538    std.debug.maybeEnableSegfaultHandler();
539
540    const result: std.os.windows.INT = call_wWinMain();
541    std.os.windows.ntdll.RtlExitUserProcess(@as(std.os.windows.UINT, @bitCast(result)));
542}
543
544fn wWinMain(hInstance: *anyopaque, hPrevInstance: ?*anyopaque, pCmdLine: [*:0]u16, nCmdShow: c_int) callconv(.c) c_int {
545    return root.wWinMain(@ptrCast(hInstance), @ptrCast(hPrevInstance), pCmdLine, @intCast(nCmdShow));
546}
547
548fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn {
549    // We're not ready to panic until thread local storage is initialized.
550    @setRuntimeSafety(false);
551    // Code coverage instrumentation might try to use thread local variables.
552    @disableInstrumentation();
553    const argc = argc_argv_ptr[0];
554    const argv: [*][*:0]u8 = @ptrCast(argc_argv_ptr + 1);
555
556    const envp_optional: [*:null]?[*:0]u8 = @ptrCast(@alignCast(argv + argc + 1));
557    var envp_count: usize = 0;
558    while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
559    const envp = @as([*][*:0]u8, @ptrCast(envp_optional))[0..envp_count];
560
561    // Find the beginning of the auxiliary vector
562    const auxv: [*]elf.Auxv = @ptrCast(@alignCast(envp.ptr + envp_count + 1));
563
564    var at_hwcap: usize = 0;
565    const phdrs = init: {
566        var i: usize = 0;
567        var at_phdr: usize = 0;
568        var at_phnum: usize = 0;
569        while (auxv[i].a_type != elf.AT_NULL) : (i += 1) {
570            switch (auxv[i].a_type) {
571                elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val,
572                elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val,
573                elf.AT_HWCAP => at_hwcap = auxv[i].a_un.a_val,
574                else => continue,
575            }
576        }
577        break :init @as([*]elf.Phdr, @ptrFromInt(at_phdr))[0..at_phnum];
578    };
579
580    // Apply the initial relocations as early as possible in the startup process. We cannot
581    // make calls yet on some architectures (e.g. MIPS) *because* they haven't been applied yet,
582    // so this must be fully inlined.
583    if (builtin.link_mode == .static and builtin.position_independent_executable) {
584        @call(.always_inline, std.pie.relocate, .{phdrs});
585    }
586
587    if (native_os == .linux) {
588        // This must be done after PIE relocations have been applied or we may crash
589        // while trying to access the global variable (happens on MIPS at least).
590        std.os.linux.elf_aux_maybe = auxv;
591
592        if (!builtin.single_threaded) {
593            // ARMv6 targets (and earlier) have no support for TLS in hardware.
594            // FIXME: Elide the check for targets >= ARMv7 when the target feature API
595            // becomes less verbose (and more usable).
596            if (comptime native_arch.isArm()) {
597                if (at_hwcap & std.os.linux.HWCAP.TLS == 0) {
598                    // FIXME: Make __aeabi_read_tp call the kernel helper kuser_get_tls
599                    // For the time being use a simple trap instead of a @panic call to
600                    // keep the binary bloat under control.
601                    @trap();
602                }
603            }
604
605            // Initialize the TLS area.
606            std.os.linux.tls.initStatic(phdrs);
607        }
608
609        // The way Linux executables represent stack size is via the PT_GNU_STACK
610        // program header. However the kernel does not recognize it; it always gives 8 MiB.
611        // Here we look for the stack size in our program headers and use setrlimit
612        // to ask for more stack space.
613        expandStackSize(phdrs);
614    }
615
616    const opt_init_array_start = @extern([*]const *const fn () callconv(.c) void, .{
617        .name = "__init_array_start",
618        .linkage = .weak,
619    });
620    const opt_init_array_end = @extern([*]const *const fn () callconv(.c) void, .{
621        .name = "__init_array_end",
622        .linkage = .weak,
623    });
624    if (opt_init_array_start) |init_array_start| {
625        const init_array_end = opt_init_array_end.?;
626        const slice = init_array_start[0 .. init_array_end - init_array_start];
627        for (slice) |func| func();
628    }
629
630    std.posix.exit(callMainWithArgs(argc, argv, envp));
631}
632
633fn expandStackSize(phdrs: []elf.Phdr) void {
634    for (phdrs) |*phdr| {
635        switch (phdr.p_type) {
636            elf.PT_GNU_STACK => {
637                if (phdr.p_memsz == 0) break;
638                assert(phdr.p_memsz % std.heap.page_size_min == 0);
639
640                // Silently fail if we are unable to get limits.
641                const limits = std.posix.getrlimit(.STACK) catch break;
642
643                // Clamp to limits.max .
644                const wanted_stack_size = @min(phdr.p_memsz, limits.max);
645
646                if (wanted_stack_size > limits.cur) {
647                    std.posix.setrlimit(.STACK, .{
648                        .cur = wanted_stack_size,
649                        .max = limits.max,
650                    }) catch {
651                        // Because we could not increase the stack size to the upper bound,
652                        // depending on what happens at runtime, a stack overflow may occur.
653                        // However it would cause a segmentation fault, thanks to stack probing,
654                        // so we do not have a memory safety issue here.
655                        // This is intentional silent failure.
656                        // This logic should be revisited when the following issues are addressed:
657                        // https://github.com/ziglang/zig/issues/157
658                        // https://github.com/ziglang/zig/issues/1006
659                    };
660                }
661                break;
662            },
663            else => {},
664        }
665    }
666}
667
668inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {
669    std.os.argv = argv[0..argc];
670    std.os.environ = envp;
671
672    std.debug.maybeEnableSegfaultHandler();
673
674    return callMain();
675}
676
677fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) callconv(.c) c_int {
678    var env_count: usize = 0;
679    while (c_envp[env_count] != null) : (env_count += 1) {}
680    const envp = @as([*][*:0]u8, @ptrCast(c_envp))[0..env_count];
681
682    if (builtin.os.tag == .linux) {
683        const at_phdr = std.c.getauxval(elf.AT_PHDR);
684        const at_phnum = std.c.getauxval(elf.AT_PHNUM);
685        const phdrs = (@as([*]elf.Phdr, @ptrFromInt(at_phdr)))[0..at_phnum];
686        expandStackSize(phdrs);
687    }
688
689    return callMainWithArgs(@as(usize, @intCast(c_argc)), @as([*][*:0]u8, @ptrCast(c_argv)), envp);
690}
691
692fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int {
693    std.os.argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@intCast(c_argc)];
694    return callMain();
695}
696
697// General error message for a malformed return type
698const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'";
699
700pub inline fn callMain() u8 {
701    const ReturnType = @typeInfo(@TypeOf(root.main)).@"fn".return_type.?;
702
703    switch (ReturnType) {
704        void => {
705            root.main();
706            return 0;
707        },
708        noreturn, u8 => {
709            return root.main();
710        },
711        else => {
712            if (@typeInfo(ReturnType) != .error_union) @compileError(bad_main_ret);
713
714            const result = root.main() catch |err| {
715                switch (builtin.zig_backend) {
716                    .stage2_powerpc,
717                    .stage2_riscv64,
718                    => {
719                        _ = std.posix.write(std.posix.STDERR_FILENO, "error: failed with error\n") catch {};
720                        return 1;
721                    },
722                    else => {},
723                }
724                std.log.err("{s}", .{@errorName(err)});
725                switch (native_os) {
726                    .freestanding, .other => {},
727                    else => if (@errorReturnTrace()) |trace| {
728                        std.debug.dumpStackTrace(trace);
729                    },
730                }
731                return 1;
732            };
733
734            return switch (@TypeOf(result)) {
735                void => 0,
736                u8 => result,
737                else => @compileError(bad_main_ret),
738            };
739        },
740    }
741}
742
743pub fn call_wWinMain() std.os.windows.INT {
744    const peb = std.os.windows.peb();
745    const MAIN_HINSTANCE = @typeInfo(@TypeOf(root.wWinMain)).@"fn".params[0].type.?;
746    const hInstance: MAIN_HINSTANCE = @ptrCast(peb.ImageBaseAddress);
747    const lpCmdLine: [*:0]u16 = @ptrCast(peb.ProcessParameters.CommandLine.Buffer);
748
749    // There are various types used for the 'show window' variable through the Win32 APIs:
750    // - u16 in STARTUPINFOA.wShowWindow / STARTUPINFOW.wShowWindow
751    // - c_int in ShowWindow
752    // - u32 in PEB.ProcessParameters.dwShowWindow
753    // Since STARTUPINFO is the bottleneck for the allowed values, we use `u16` as the
754    // type which can coerce into i32/c_int/u32 depending on how the user defines their wWinMain
755    // (the Win32 docs show wWinMain with `int` as the type for nShowCmd).
756    const nShowCmd: u16 = nShowCmd: {
757        // This makes Zig match the nShowCmd behavior of a C program with a WinMain symbol:
758        // - With STARTF_USESHOWWINDOW set in STARTUPINFO.dwFlags of the CreateProcess call:
759        //   - nShowCmd is STARTUPINFO.wShowWindow from the parent CreateProcess call
760        // - With STARTF_USESHOWWINDOW unset:
761        //   - nShowCmd is always SW_SHOWDEFAULT
762        const SW_SHOWDEFAULT = 10;
763        const STARTF_USESHOWWINDOW = 1;
764        if (peb.ProcessParameters.dwFlags & STARTF_USESHOWWINDOW != 0) {
765            break :nShowCmd @truncate(peb.ProcessParameters.dwShowWindow);
766        }
767        break :nShowCmd SW_SHOWDEFAULT;
768    };
769
770    // second parameter hPrevInstance, MSDN: "This parameter is always NULL"
771    return root.wWinMain(hInstance, null, lpCmdLine, nShowCmd);
772}