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}