blob bca0922f (24401B) - Raw
1 // This file is included in the compilation unit when exporting an executable. 2 3 const root = @import("root"); 4 const std = @import("std.zig"); 5 const builtin = @import("builtin"); 6 const assert = std.debug.assert; 7 const uefi = std.os.uefi; 8 const elf = std.elf; 9 const native_arch = builtin.cpu.arch; 10 const native_os = builtin.os.tag; 11 12 const 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. 17 pub const simplified_logic = 18 builtin.zig_backend == .stage2_x86 or 19 builtin.zig_backend == .stage2_aarch64 or 20 builtin.zig_backend == .stage2_arm or 21 builtin.zig_backend == .stage2_sparc64 or 22 builtin.cpu.arch == .spirv32 or 23 builtin.cpu.arch == .spirv64; 24 25 comptime { 26 // No matter what, we import the root file, so that any export, test, comptime 27 // decls there get run. 28 _ = root; 29 30 if (simplified_logic) { 31 if (builtin.output_mode == .Exe) { 32 if ((builtin.link_libc or builtin.object_format == .c) and @hasDecl(root, "main")) { 33 if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { 34 @export(main2, .{ .name = "main" }); 35 } 36 } else if (builtin.os.tag == .windows) { 37 if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) { 38 @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" }); 39 } 40 } else if (builtin.os.tag == .opencl) { 41 if (@hasDecl(root, "main")) 42 @export(spirvMain2, .{ .name = "main" }); 43 } else { 44 if (!@hasDecl(root, "_start")) { 45 @export(_start2, .{ .name = "_start" }); 46 } 47 } 48 } 49 } else { 50 if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) { 51 if (native_os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { 52 @export(_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" }); 53 } 54 } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { 55 if (builtin.link_libc and @hasDecl(root, "main")) { 56 if (native_arch.isWasm()) { 57 @export(mainWithoutEnv, .{ .name = "main" }); 58 } else if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { 59 @export(main, .{ .name = "main" }); 60 } 61 } else if (native_os == .windows) { 62 if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and 63 !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) 64 { 65 @export(WinStartup, .{ .name = "wWinMainCRTStartup" }); 66 } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and 67 !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) 68 { 69 @compileError("WinMain not supported; declare wWinMain or main instead"); 70 } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and 71 !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) 72 { 73 @export(wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" }); 74 } 75 } else if (native_os == .uefi) { 76 if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" }); 77 } else if (native_os == .wasi) { 78 const wasm_start_sym = switch (builtin.wasi_exec_model) { 79 .reactor => "_initialize", 80 .command => "_start", 81 }; 82 if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) { 83 // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which 84 // case it's not required to provide an entrypoint such as main. 85 @export(wasi_start, .{ .name = wasm_start_sym }); 86 } 87 } else if (native_arch.isWasm() and native_os == .freestanding) { 88 // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which 89 // case it's not required to provide an entrypoint such as main. 90 if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(wasm_freestanding_start, .{ .name = start_sym_name }); 91 } else if (native_os != .other and native_os != .freestanding) { 92 if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name }); 93 } 94 } 95 } 96 } 97 98 // Simplified start code for stage2 until it supports more language features /// 99 100 fn main2() callconv(.C) c_int { 101 root.main(); 102 return 0; 103 } 104 105 fn _start2() callconv(.C) noreturn { 106 callMain2(); 107 } 108 109 fn callMain2() noreturn { 110 @setAlignStack(16); 111 root.main(); 112 exit2(0); 113 } 114 115 fn spirvMain2() callconv(.Kernel) void { 116 root.main(); 117 } 118 119 fn wWinMainCRTStartup2() callconv(.C) noreturn { 120 root.main(); 121 exit2(0); 122 } 123 124 fn exit2(code: usize) noreturn { 125 switch (native_os) { 126 .linux => switch (builtin.cpu.arch) { 127 .x86_64 => { 128 asm volatile ("syscall" 129 : 130 : [number] "{rax}" (231), 131 [arg1] "{rdi}" (code), 132 : "rcx", "r11", "memory" 133 ); 134 }, 135 .arm => { 136 asm volatile ("svc #0" 137 : 138 : [number] "{r7}" (1), 139 [arg1] "{r0}" (code), 140 : "memory" 141 ); 142 }, 143 .aarch64 => { 144 asm volatile ("svc #0" 145 : 146 : [number] "{x8}" (93), 147 [arg1] "{x0}" (code), 148 : "memory", "cc" 149 ); 150 }, 151 .sparc64 => { 152 asm volatile ("ta 0x6d" 153 : 154 : [number] "{g1}" (1), 155 [arg1] "{o0}" (code), 156 : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" 157 ); 158 }, 159 else => @compileError("TODO"), 160 }, 161 // exits(0) 162 .plan9 => std.os.plan9.exits(null), 163 .windows => { 164 std.os.windows.ntdll.RtlExitUserProcess(@as(u32, @truncate(code))); 165 }, 166 else => @compileError("TODO"), 167 } 168 unreachable; 169 } 170 171 //////////////////////////////////////////////////////////////////////////////// 172 173 fn _DllMainCRTStartup( 174 hinstDLL: std.os.windows.HINSTANCE, 175 fdwReason: std.os.windows.DWORD, 176 lpReserved: std.os.windows.LPVOID, 177 ) callconv(std.os.windows.WINAPI) std.os.windows.BOOL { 178 if (!builtin.single_threaded and !builtin.link_libc) { 179 _ = @import("start_windows_tls.zig"); 180 } 181 182 if (@hasDecl(root, "DllMain")) { 183 return root.DllMain(hinstDLL, fdwReason, lpReserved); 184 } 185 186 return std.os.windows.TRUE; 187 } 188 189 fn wasm_freestanding_start() callconv(.C) void { 190 // This is marked inline because for some reason LLVM in 191 // release mode fails to inline it, and we want fewer call frames in stack traces. 192 _ = @call(.always_inline, callMain, .{}); 193 } 194 195 fn wasi_start() callconv(.C) void { 196 // The function call is marked inline because for some reason LLVM in 197 // release mode fails to inline it, and we want fewer call frames in stack traces. 198 switch (builtin.wasi_exec_model) { 199 .reactor => _ = @call(.always_inline, callMain, .{}), 200 .command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{})), 201 } 202 } 203 204 fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv(.C) usize { 205 uefi.handle = handle; 206 uefi.system_table = system_table; 207 208 switch (@typeInfo(@TypeOf(root.main)).Fn.return_type.?) { 209 noreturn => { 210 root.main(); 211 }, 212 void => { 213 root.main(); 214 return 0; 215 }, 216 usize => { 217 return root.main(); 218 }, 219 uefi.Status => { 220 return @intFromEnum(root.main()); 221 }, 222 else => @compileError("expected return type of main to be 'void', 'noreturn', 'usize', or 'std.os.uefi.Status'"), 223 } 224 } 225 226 fn _start() callconv(.Naked) noreturn { 227 // TODO set Top of Stack on non x86_64-plan9 228 if (native_os == .plan9 and native_arch == .x86_64) { 229 // from /sys/src/libc/amd64/main9.s 230 std.os.plan9.tos = asm volatile ("" 231 : [tos] "={rax}" (-> *std.os.plan9.Tos), 232 ); 233 } 234 235 // Note that we maintain a very low level of trust with regards to ABI guarantees at this point. 236 // We will redundantly align the stack, clear the link register, etc. While e.g. the Linux 237 // kernel is usually good about upholding the ABI guarantees, the same cannot be said of dynamic 238 // linkers; musl's ldso, for example, opts to not align the stack when invoking the dynamic 239 // linker explicitly. 240 asm volatile (switch (native_arch) { 241 .x86_64 => 242 \\ xorl %%ebp, %%ebp 243 \\ movq %%rsp, %%rdi 244 \\ andq $-16, %%rsp 245 \\ callq %[posixCallMainAndExit:P] 246 , 247 .x86 => 248 \\ xorl %%ebp, %%ebp 249 \\ movl %%esp, %%eax 250 \\ andl $-16, %%esp 251 \\ subl $12, %%esp 252 \\ pushl %%eax 253 \\ calll %[posixCallMainAndExit:P] 254 , 255 .aarch64, .aarch64_be => 256 \\ mov fp, #0 257 \\ mov lr, #0 258 \\ mov x0, sp 259 \\ and sp, x0, #-16 260 \\ b %[posixCallMainAndExit] 261 , 262 .arm, .armeb, .thumb, .thumbeb => 263 \\ mov fp, #0 264 \\ mov lr, #0 265 \\ mov a1, sp 266 \\ and sp, #-16 267 \\ b %[posixCallMainAndExit] 268 , 269 .loongarch32, .loongarch64 => 270 \\ move $fp, $zero 271 \\ move $a0, $sp 272 \\ bstrins.d $sp, $zero, 3, 0 273 \\ b %[posixCallMainAndExit] 274 , 275 .riscv32, .riscv64 => 276 \\ li s0, 0 277 \\ li ra, 0 278 \\ mv a0, sp 279 \\ andi sp, sp, -16 280 \\ tail %[posixCallMainAndExit]@plt 281 , 282 .m68k => 283 // Note that the - 8 is needed because pc in the jsr instruction points into the middle 284 // of the jsr instruction. (The lea is 6 bytes, the jsr is 4 bytes.) 285 \\ suba.l %%fp, %%fp 286 \\ move.l %%sp, -(%%sp) 287 \\ lea %[posixCallMainAndExit] - . - 8, %%a0 288 \\ jsr (%%pc, %%a0) 289 , 290 .mips, .mipsel => 291 \\ move $fp, $0 292 \\ bal 1f 293 \\ .gpword . 294 \\ .gpword %[posixCallMainAndExit] 295 \\ 1: 296 // The `gp` register on MIPS serves a similar purpose to `r2` (ToC pointer) on PPC64. 297 \\ lw $gp, 0($ra) 298 \\ subu $gp, $ra, $gp 299 \\ lw $25, 4($ra) 300 \\ addu $25, $25, $gp 301 \\ move $ra, $0 302 \\ move $a0, $sp 303 \\ and $sp, -8 304 \\ subu $sp, $sp, 16 305 \\ jalr $25 306 , 307 .mips64, .mips64el => 308 \\ move $fp, $0 309 // This is needed because early MIPS versions don't support misaligned loads. Without 310 // this directive, the hidden `nop` inserted to fill the delay slot after `bal` would 311 // cause the two doublewords to be aligned to 4 bytes instead of 8. 312 \\ .balign 8 313 \\ bal 1f 314 \\ .gpdword . 315 \\ .gpdword %[posixCallMainAndExit] 316 \\ 1: 317 // The `gp` register on MIPS serves a similar purpose to `r2` (ToC pointer) on PPC64. 318 \\ ld $gp, 0($ra) 319 \\ dsubu $gp, $ra, $gp 320 \\ ld $25, 8($ra) 321 \\ daddu $25, $25, $gp 322 \\ move $ra, $0 323 \\ move $a0, $sp 324 \\ and $sp, -16 325 \\ dsubu $sp, $sp, 16 326 \\ jalr $25 327 , 328 .powerpc, .powerpcle => 329 // Set up the initial stack frame, and clear the back chain pointer. 330 \\ mr 3, 1 331 \\ clrrwi 1, 1, 4 332 \\ li 0, 0 333 \\ stwu 1, -16(1) 334 \\ stw 0, 0(1) 335 \\ mtlr 0 336 \\ b %[posixCallMainAndExit] 337 , 338 .powerpc64, .powerpc64le => 339 // Set up the ToC and initial stack frame, and clear the back chain pointer. 340 \\ addis 2, 12, .TOC. - %[_start]@ha 341 \\ addi 2, 2, .TOC. - %[_start]@l 342 \\ mr 3, 1 343 \\ clrrdi 1, 1, 4 344 \\ li 0, 0 345 \\ stdu 0, -32(1) 346 \\ mtlr 0 347 \\ b %[posixCallMainAndExit] 348 , 349 .s390x => 350 // Set up the stack frame (register save area and cleared back-chain slot). 351 \\ lgr %%r2, %%r15 352 \\ lghi %%r0, -16 353 \\ ngr %%r15, %%r0 354 \\ aghi %%r15, -160 355 \\ lghi %%r0, 0 356 \\ stg %%r0, 0(%%r15) 357 \\ jg %[posixCallMainAndExit] 358 , 359 .sparc64 => 360 // argc is stored after a register window (16 registers * 8 bytes) plus the stack bias 361 // (2047 bytes). 362 \\ mov %%g0, %%fp 363 \\ add %%sp, 2175, %%o0 364 \\ add %%sp, 2047, %%sp 365 \\ and %%sp, -16, %%sp 366 \\ sub %%sp, 2047, %%sp 367 \\ ba,a %[posixCallMainAndExit] 368 , 369 else => @compileError("unsupported arch"), 370 } 371 : 372 : [_start] "X" (_start), 373 [posixCallMainAndExit] "X" (&posixCallMainAndExit), 374 ); 375 } 376 377 fn WinStartup() callconv(std.os.windows.WINAPI) noreturn { 378 @setAlignStack(16); 379 if (!builtin.single_threaded and !builtin.link_libc) { 380 _ = @import("start_windows_tls.zig"); 381 } 382 383 std.debug.maybeEnableSegfaultHandler(); 384 385 std.os.windows.ntdll.RtlExitUserProcess(callMain()); 386 } 387 388 fn wWinMainCRTStartup() callconv(std.os.windows.WINAPI) noreturn { 389 @setAlignStack(16); 390 if (!builtin.single_threaded and !builtin.link_libc) { 391 _ = @import("start_windows_tls.zig"); 392 } 393 394 std.debug.maybeEnableSegfaultHandler(); 395 396 const result: std.os.windows.INT = call_wWinMain(); 397 std.os.windows.ntdll.RtlExitUserProcess(@as(std.os.windows.UINT, @bitCast(result))); 398 } 399 400 fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.C) noreturn { 401 // We're not ready to panic until thread local storage is initialized. 402 @setRuntimeSafety(false); 403 // Code coverage instrumentation might try to use thread local variables. 404 @disableInstrumentation(); 405 const argc = argc_argv_ptr[0]; 406 const argv = @as([*][*:0]u8, @ptrCast(argc_argv_ptr + 1)); 407 408 const envp_optional: [*:null]?[*:0]u8 = @ptrCast(@alignCast(argv + argc + 1)); 409 var envp_count: usize = 0; 410 while (envp_optional[envp_count]) |_| : (envp_count += 1) {} 411 const envp = @as([*][*:0]u8, @ptrCast(envp_optional))[0..envp_count]; 412 413 if (native_os == .linux) { 414 // Find the beginning of the auxiliary vector 415 const auxv: [*]elf.Auxv = @ptrCast(@alignCast(envp.ptr + envp_count + 1)); 416 417 var at_hwcap: usize = 0; 418 const phdrs = init: { 419 var i: usize = 0; 420 var at_phdr: usize = 0; 421 var at_phnum: usize = 0; 422 while (auxv[i].a_type != elf.AT_NULL) : (i += 1) { 423 switch (auxv[i].a_type) { 424 elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val, 425 elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val, 426 elf.AT_HWCAP => at_hwcap = auxv[i].a_un.a_val, 427 else => continue, 428 } 429 } 430 break :init @as([*]elf.Phdr, @ptrFromInt(at_phdr))[0..at_phnum]; 431 }; 432 433 // Apply the initial relocations as early as possible in the startup process. We cannot 434 // make calls yet on some architectures (e.g. MIPS) *because* they haven't been applied yet, 435 // so this must be fully inlined. 436 if (builtin.position_independent_executable) { 437 @call(.always_inline, std.os.linux.pie.relocate, .{phdrs}); 438 } 439 440 // This must be done after PIE relocations have been applied or we may crash 441 // while trying to access the global variable (happens on MIPS at least). 442 std.os.linux.elf_aux_maybe = auxv; 443 444 if (!builtin.single_threaded) { 445 // ARMv6 targets (and earlier) have no support for TLS in hardware. 446 // FIXME: Elide the check for targets >= ARMv7 when the target feature API 447 // becomes less verbose (and more usable). 448 if (comptime native_arch.isARM()) { 449 if (at_hwcap & std.os.linux.HWCAP.TLS == 0) { 450 // FIXME: Make __aeabi_read_tp call the kernel helper kuser_get_tls 451 // For the time being use a simple trap instead of a @panic call to 452 // keep the binary bloat under control. 453 @trap(); 454 } 455 } 456 457 // Initialize the TLS area. 458 std.os.linux.tls.initStaticTLS(phdrs); 459 } 460 461 // The way Linux executables represent stack size is via the PT_GNU_STACK 462 // program header. However the kernel does not recognize it; it always gives 8 MiB. 463 // Here we look for the stack size in our program headers and use setrlimit 464 // to ask for more stack space. 465 expandStackSize(phdrs); 466 } 467 468 std.posix.exit(callMainWithArgs(argc, argv, envp)); 469 } 470 471 fn expandStackSize(phdrs: []elf.Phdr) void { 472 for (phdrs) |*phdr| { 473 switch (phdr.p_type) { 474 elf.PT_GNU_STACK => { 475 assert(phdr.p_memsz % std.mem.page_size == 0); 476 477 // Silently fail if we are unable to get limits. 478 const limits = std.posix.getrlimit(.STACK) catch break; 479 480 // Clamp to limits.max . 481 const wanted_stack_size = @min(phdr.p_memsz, limits.max); 482 483 if (wanted_stack_size > limits.cur) { 484 std.posix.setrlimit(.STACK, .{ 485 .cur = wanted_stack_size, 486 .max = limits.max, 487 }) catch { 488 // Because we could not increase the stack size to the upper bound, 489 // depending on what happens at runtime, a stack overflow may occur. 490 // However it would cause a segmentation fault, thanks to stack probing, 491 // so we do not have a memory safety issue here. 492 // This is intentional silent failure. 493 // This logic should be revisited when the following issues are addressed: 494 // https://github.com/ziglang/zig/issues/157 495 // https://github.com/ziglang/zig/issues/1006 496 }; 497 } 498 break; 499 }, 500 else => {}, 501 } 502 } 503 } 504 505 inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { 506 std.os.argv = argv[0..argc]; 507 std.os.environ = envp; 508 509 std.debug.maybeEnableSegfaultHandler(); 510 maybeIgnoreSigpipe(); 511 512 return callMain(); 513 } 514 515 fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) callconv(.C) c_int { 516 var env_count: usize = 0; 517 while (c_envp[env_count] != null) : (env_count += 1) {} 518 const envp = @as([*][*:0]u8, @ptrCast(c_envp))[0..env_count]; 519 520 if (builtin.os.tag == .linux) { 521 const at_phdr = std.c.getauxval(elf.AT_PHDR); 522 const at_phnum = std.c.getauxval(elf.AT_PHNUM); 523 const phdrs = (@as([*]elf.Phdr, @ptrFromInt(at_phdr)))[0..at_phnum]; 524 expandStackSize(phdrs); 525 } 526 527 return callMainWithArgs(@as(usize, @intCast(c_argc)), @as([*][*:0]u8, @ptrCast(c_argv)), envp); 528 } 529 530 fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.C) c_int { 531 std.os.argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@as(usize, @intCast(c_argc))]; 532 return callMain(); 533 } 534 535 // General error message for a malformed return type 536 const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'"; 537 538 pub inline fn callMain() u8 { 539 const ReturnType = @typeInfo(@TypeOf(root.main)).Fn.return_type.?; 540 541 switch (ReturnType) { 542 void => { 543 root.main(); 544 return 0; 545 }, 546 noreturn, u8 => { 547 return root.main(); 548 }, 549 else => { 550 if (@typeInfo(ReturnType) != .ErrorUnion) @compileError(bad_main_ret); 551 552 const result = root.main() catch |err| { 553 if (builtin.zig_backend == .stage2_riscv64) { 554 std.debug.print("error: failed with error\n", .{}); 555 return 1; 556 } 557 std.log.err("{s}", .{@errorName(err)}); 558 if (@errorReturnTrace()) |trace| { 559 std.debug.dumpStackTrace(trace.*); 560 } 561 return 1; 562 }; 563 564 return switch (@TypeOf(result)) { 565 void => 0, 566 u8 => result, 567 else => @compileError(bad_main_ret), 568 }; 569 }, 570 } 571 } 572 573 pub fn call_wWinMain() std.os.windows.INT { 574 const peb = std.os.windows.peb(); 575 const MAIN_HINSTANCE = @typeInfo(@TypeOf(root.wWinMain)).Fn.params[0].type.?; 576 const hInstance = @as(MAIN_HINSTANCE, @ptrCast(peb.ImageBaseAddress)); 577 const lpCmdLine: [*:0]u16 = @ptrCast(peb.ProcessParameters.CommandLine.Buffer); 578 579 // There are various types used for the 'show window' variable through the Win32 APIs: 580 // - u16 in STARTUPINFOA.wShowWindow / STARTUPINFOW.wShowWindow 581 // - c_int in ShowWindow 582 // - u32 in PEB.ProcessParameters.dwShowWindow 583 // Since STARTUPINFO is the bottleneck for the allowed values, we use `u16` as the 584 // type which can coerce into i32/c_int/u32 depending on how the user defines their wWinMain 585 // (the Win32 docs show wWinMain with `int` as the type for nCmdShow). 586 const nCmdShow: u16 = nCmdShow: { 587 // This makes Zig match the nCmdShow behavior of a C program with a WinMain symbol: 588 // - With STARTF_USESHOWWINDOW set in STARTUPINFO.dwFlags of the CreateProcess call: 589 // - Compiled with subsystem:console -> nCmdShow is always SW_SHOWDEFAULT 590 // - Compiled with subsystem:windows -> nCmdShow is STARTUPINFO.wShowWindow from 591 // the parent CreateProcess call 592 // - With STARTF_USESHOWWINDOW unset: 593 // - nCmdShow is always SW_SHOWDEFAULT 594 const SW_SHOWDEFAULT = 10; 595 const STARTF_USESHOWWINDOW = 1; 596 // root having a wWinMain means that std.builtin.subsystem will always have a non-null value. 597 if (std.builtin.subsystem.? == .Windows and peb.ProcessParameters.dwFlags & STARTF_USESHOWWINDOW != 0) { 598 break :nCmdShow @truncate(peb.ProcessParameters.dwShowWindow); 599 } 600 break :nCmdShow SW_SHOWDEFAULT; 601 }; 602 603 // second parameter hPrevInstance, MSDN: "This parameter is always NULL" 604 return root.wWinMain(hInstance, null, lpCmdLine, nCmdShow); 605 } 606 607 fn maybeIgnoreSigpipe() void { 608 const have_sigpipe_support = switch (builtin.os.tag) { 609 .linux, 610 .plan9, 611 .solaris, 612 .netbsd, 613 .openbsd, 614 .haiku, 615 .macos, 616 .ios, 617 .watchos, 618 .tvos, 619 .visionos, 620 .dragonfly, 621 .freebsd, 622 => true, 623 624 else => false, 625 }; 626 627 if (have_sigpipe_support and !std.options.keep_sigpipe) { 628 const posix = std.posix; 629 const act: posix.Sigaction = .{ 630 // Set handler to a noop function instead of `SIG.IGN` to prevent 631 // leaking signal disposition to a child process. 632 .handler = .{ .handler = noopSigHandler }, 633 .mask = posix.empty_sigset, 634 .flags = 0, 635 }; 636 posix.sigaction(posix.SIG.PIPE, &act, null); 637 } 638 } 639 640 fn noopSigHandler(_: i32) callconv(.C) void {}