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>
This commit is contained in:
Andrew Kelley
2026-05-02 20:06:56 +02:00
5 changed files with 124 additions and 1 deletions
+13 -1
View File
@@ -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.
@@ -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);
}
}
@@ -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;
}
@@ -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;
}
@@ -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;
}