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