zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 04481c76cb5f2ec78bf4b10c0cdb090e8a64240e (tree)
parent 1f22b2cbb2d9cdc4b7e436328e7018f473a57388
Author: Andrew Kelley <andrew@ziglang.org>
Date:   Sat,  2 May 2026 20:06:56 +0200

Merge pull request 'Export wrapper around Zig DllMain function when linking libc + add tests' (#32179) from squeek502/zig:windows-dlls into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/32179
Reviewed-by: Andrew Kelley <andrew@ziglang.org>

Diffstat:
Mlib/std/start.zig | 14+++++++++++++-
Mtest/standalone/windows_entry_points/build.zig | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/standalone/windows_entry_points/dllmain.c | 9+++++++++
Atest/standalone/windows_entry_points/dllmain.zig | 18++++++++++++++++++
Atest/standalone/windows_entry_points/loaddll.zig | 13+++++++++++++
5 files changed, 124 insertions(+), 1 deletion(-)

diff --git a/lib/std/start.zig b/lib/std/start.zig @@ -23,6 +23,10 @@ comptime { const dll_main_crt_startup = if (builtin.abi.isGnu()) "DllMainCRTStartup" else "_DllMainCRTStartup"; if (native_os == .windows and !builtin.link_libc and !@hasDecl(root, dll_main_crt_startup)) { @export(&DllMainCRTStartup, .{ .name = dll_main_crt_startup }); + } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "DllMain")) { + if (!@typeInfo(@TypeOf(root.DllMain)).@"fn".calling_convention.eql(.winapi)) { + @export(&DllMain, .{ .name = "DllMain" }); + } } } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { if (builtin.link_libc and @hasDecl(root, "main")) { @@ -82,12 +86,20 @@ fn DllMainCRTStartup( } if (@hasDecl(root, "DllMain")) { - return root.DllMain(hinstDLL, fdwReason, lpReserved); + return root.DllMain(@ptrCast(hinstDLL), fdwReason, lpReserved); } return .TRUE; } +fn DllMain( + hinstDLL: std.os.windows.HINSTANCE, + fdwReason: std.os.windows.DWORD, + lpReserved: std.os.windows.LPVOID, +) callconv(.winapi) std.os.windows.BOOL { + return root.DllMain(@ptrCast(hinstDLL), fdwReason, lpReserved); +} + fn wasm_freestanding_start() callconv(.c) void { // This is marked inline because for some reason LLVM in // release mode fails to inline it, and we want fewer call frames in stack traces. diff --git a/test/standalone/windows_entry_points/build.zig b/test/standalone/windows_entry_points/build.zig @@ -139,4 +139,75 @@ pub fn build(b: *std.Build) void { _ = exe.getEmittedBin(); test_step.dependOn(&exe.step); } + + const load_dll_exe = b.addExecutable(.{ .name = "load_dll", .root_module = b.createModule(.{ + .root_source_file = b.path("loaddll.zig"), + .target = target, + .optimize = .Debug, + }) }); + + { + const dll = b.addLibrary(.{ + .name = "zig_dllmain", + .linkage = .dynamic, + .root_module = b.createModule(.{ + .root_source_file = b.path("dllmain.zig"), + .target = target, + .optimize = .Debug, + }), + }); + + const run = b.addRunArtifact(load_dll_exe); + run.addArtifactArg(dll); + run.expectStdErrEqual("hello from DllMain"); + run.expectStdOutEqual(""); + run.expectExitCode(0); + run.skip_foreign_checks = true; + + test_step.dependOn(&run.step); + } + + { + const dll = b.addLibrary(.{ + .name = "zig_dllmain_link_libc", + .linkage = .dynamic, + .root_module = b.createModule(.{ + .root_source_file = b.path("dllmain.zig"), + .target = target, + .optimize = .Debug, + .link_libc = true, + }), + }); + + const run = b.addRunArtifact(load_dll_exe); + run.addArtifactArg(dll); + run.expectStdErrEqual("hello from DllMain"); + run.expectStdOutEqual(""); + run.expectExitCode(0); + run.skip_foreign_checks = true; + + test_step.dependOn(&run.step); + } + + { + const dll = b.addLibrary(.{ + .name = "c_dllmain", + .linkage = .dynamic, + .root_module = b.createModule(.{ + .target = target, + .optimize = .Debug, + .link_libc = true, + }), + }); + dll.root_module.addCSourceFile(.{ .file = b.path("dllmain.c") }); + + const run = b.addRunArtifact(load_dll_exe); + run.addArtifactArg(dll); + run.expectStdErrEqual("hello from DllMain"); + run.expectStdOutEqual(""); + run.expectExitCode(0); + run.skip_foreign_checks = true; + + test_step.dependOn(&run.step); + } } diff --git a/test/standalone/windows_entry_points/dllmain.c b/test/standalone/windows_entry_points/dllmain.c @@ -0,0 +1,9 @@ +#include <windows.h> +#include <stdio.h> + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { + if (fdwReason == DLL_PROCESS_ATTACH) { + fprintf(stderr, "hello from DllMain"); + } + return TRUE; +} diff --git a/test/standalone/windows_entry_points/dllmain.zig b/test/standalone/windows_entry_points/dllmain.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const windows = std.os.windows; + +const DLL_PROCESS_ATTACH = 1; + +pub fn DllMain( + hinstDLL: windows.HINSTANCE, + fdwReason: windows.DWORD, + lpReserved: windows.LPVOID, +) windows.BOOL { + _ = hinstDLL; + _ = lpReserved; + switch (fdwReason) { + DLL_PROCESS_ATTACH => std.debug.print("hello from DllMain", .{}), + else => {}, + } + return .TRUE; +} diff --git a/test/standalone/windows_entry_points/loaddll.zig b/test/standalone/windows_entry_points/loaddll.zig @@ -0,0 +1,13 @@ +const std = @import("std"); +const windows = std.os.windows; + +extern "kernel32" fn LoadLibraryW(windows.LPCWSTR) callconv(.winapi) ?windows.HMODULE; + +pub fn main(init: std.process.Init) !void { + const arena = init.arena.allocator(); + const args = try init.minimal.args.toSlice(arena); + if (args.len < 2) return error.NoDllPathSpecified; + const dll_path = args[1]; + const dll_path_w = try std.unicode.wtf8ToWtf16LeAllocZ(arena, dll_path); + _ = LoadLibraryW(dll_path_w) orelse return error.FailedToLoadDll; +}