From 9be3a3cefb8b3fc0574b2906f9c60fa803e20574 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Sat, 8 Nov 2025 01:39:15 -0800 Subject: [PATCH] Let CRT take care of the entry point for wWinMain if libc is linked Fixes #7852 Before, the modified test would fail with: ``` error: lld-link: undefined symbol: wWinMain note: referenced by C:\Users\Ryan\Programming\Zig\zig-x86_64-windows-0.15.1\lib\libc\mingw\crt\crtexewin.c:66 note: libmingw32.lib(ucrtexewin.obj):(wmain) ``` --- lib/std/start.zig | 8 +++ .../standalone/windows_entry_points/build.zig | 62 +++++++++++++++++++ test/standalone/windows_entry_points/main.zig | 5 ++ .../windows_entry_points/wwinmain.zig | 15 +++++ 4 files changed, 90 insertions(+) create mode 100644 test/standalone/windows_entry_points/main.zig create mode 100644 test/standalone/windows_entry_points/wwinmain.zig diff --git a/lib/std/start.zig b/lib/std/start.zig index c22a36f24fd6..98f8e43b8a8c 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -61,6 +61,10 @@ comptime { } else if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) { @export(&main, .{ .name = "main" }); } + } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "wWinMain")) { + if (!@typeInfo(@TypeOf(root.wWinMain)).@"fn".calling_convention.eql(.c)) { + @export(&wWinMain, .{ .name = "wWinMain" }); + } } else if (native_os == .windows) { if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) @@ -527,6 +531,10 @@ fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn { std.os.windows.ntdll.RtlExitUserProcess(@as(std.os.windows.UINT, @bitCast(result))); } +fn wWinMain(hInstance: *anyopaque, hPrevInstance: ?*anyopaque, pCmdLine: [*:0]u16, nCmdShow: c_int) callconv(.c) c_int { + return root.wWinMain(@ptrCast(hInstance), @ptrCast(hPrevInstance), pCmdLine, @intCast(nCmdShow)); +} + fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn { // We're not ready to panic until thread local storage is initialized. @setRuntimeSafety(false); diff --git a/test/standalone/windows_entry_points/build.zig b/test/standalone/windows_entry_points/build.zig index c77a48ecd836..236ebe95c632 100644 --- a/test/standalone/windows_entry_points/build.zig +++ b/test/standalone/windows_entry_points/build.zig @@ -77,4 +77,66 @@ pub fn build(b: *std.Build) void { _ = exe.getEmittedBin(); test_step.dependOn(&exe.step); } + + { + const exe = b.addExecutable(.{ + .name = "zig_main", + .root_module = b.createModule(.{ + .root_source_file = b.path("main.zig"), + .target = target, + .optimize = .Debug, + }), + }); + + _ = exe.getEmittedBin(); + test_step.dependOn(&exe.step); + } + + { + const exe = b.addExecutable(.{ + .name = "zig_main_link_libc", + .root_module = b.createModule(.{ + .root_source_file = b.path("main.zig"), + .target = target, + .optimize = .Debug, + .link_libc = true, + }), + }); + + _ = exe.getEmittedBin(); + test_step.dependOn(&exe.step); + } + + { + const exe = b.addExecutable(.{ + .name = "zig_wwinmain", + .root_module = b.createModule(.{ + .root_source_file = b.path("wwinmain.zig"), + .target = target, + .optimize = .Debug, + }), + }); + exe.mingw_unicode_entry_point = true; + // Note: `exe.subsystem = .windows;` is not necessary + + _ = exe.getEmittedBin(); + test_step.dependOn(&exe.step); + } + + { + const exe = b.addExecutable(.{ + .name = "zig_wwinmain_link_libc", + .root_module = b.createModule(.{ + .root_source_file = b.path("wwinmain.zig"), + .target = target, + .optimize = .Debug, + .link_libc = true, + }), + }); + exe.mingw_unicode_entry_point = true; + // Note: `exe.subsystem = .windows;` is not necessary + + _ = exe.getEmittedBin(); + test_step.dependOn(&exe.step); + } } diff --git a/test/standalone/windows_entry_points/main.zig b/test/standalone/windows_entry_points/main.zig new file mode 100644 index 000000000000..a3a4fae0d439 --- /dev/null +++ b/test/standalone/windows_entry_points/main.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub fn main() void { + std.debug.print("hello from Zig main\n", .{}); +} diff --git a/test/standalone/windows_entry_points/wwinmain.zig b/test/standalone/windows_entry_points/wwinmain.zig new file mode 100644 index 000000000000..d6b897385308 --- /dev/null +++ b/test/standalone/windows_entry_points/wwinmain.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +pub fn wWinMain( + inst: std.os.windows.HINSTANCE, + prev: ?std.os.windows.HINSTANCE, + cmd_line: std.os.windows.LPWSTR, + cmd_show: c_int, +) std.os.windows.INT { + _ = inst; + _ = prev; + _ = cmd_line; + _ = cmd_show; + std.debug.print("hello from Zig wWinMain\n", .{}); + return 0; +}