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:
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;
+}