authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-02 20:06:56+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-02 20:06:56+02:00
log04481c76cb5f2ec78bf4b10c0cdb090e8a64240e
treeaed39038b525abe6d42341769a69e1b1826b4bb8
parent1f22b2cbb2d9cdc4b7e436328e7018f473a57388
parenta450016eb1816a8f0989626dde4b0c1865cba33d

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>

5 files changed, 124 insertions(+), 1 deletions(-)

lib/std/start.zig+13-1
......@@ -23,6 +23,10 @@ comptime {
2323 const dll_main_crt_startup = if (builtin.abi.isGnu()) "DllMainCRTStartup" else "_DllMainCRTStartup";
2424 if (native_os == .windows and !builtin.link_libc and !@hasDecl(root, dll_main_crt_startup)) {
2525 @export(&DllMainCRTStartup, .{ .name = dll_main_crt_startup });
26 } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "DllMain")) {
27 if (!@typeInfo(@TypeOf(root.DllMain)).@"fn".calling_convention.eql(.winapi)) {
28 @export(&DllMain, .{ .name = "DllMain" });
29 }
2630 }
2731 } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) {
2832 if (builtin.link_libc and @hasDecl(root, "main")) {
......@@ -82,12 +86,20 @@ fn DllMainCRTStartup(
8286 }
8387
8488 if (@hasDecl(root, "DllMain")) {
85 return root.DllMain(hinstDLL, fdwReason, lpReserved);
89 return root.DllMain(@ptrCast(hinstDLL), fdwReason, lpReserved);
8690 }
8791
8892 return .TRUE;
8993}
9094
95fn DllMain(
96 hinstDLL: std.os.windows.HINSTANCE,
97 fdwReason: std.os.windows.DWORD,
98 lpReserved: std.os.windows.LPVOID,
99) callconv(.winapi) std.os.windows.BOOL {
100 return root.DllMain(@ptrCast(hinstDLL), fdwReason, lpReserved);
101}
102
91103fn wasm_freestanding_start() callconv(.c) void {
92104 // This is marked inline because for some reason LLVM in
93105 // release mode fails to inline it, and we want fewer call frames in stack traces.
test/standalone/windows_entry_points/build.zig+71
......@@ -139,4 +139,75 @@ pub fn build(b: *std.Build) void {
139139 _ = exe.getEmittedBin();
140140 test_step.dependOn(&exe.step);
141141 }
142
143 const load_dll_exe = b.addExecutable(.{ .name = "load_dll", .root_module = b.createModule(.{
144 .root_source_file = b.path("loaddll.zig"),
145 .target = target,
146 .optimize = .Debug,
147 }) });
148
149 {
150 const dll = b.addLibrary(.{
151 .name = "zig_dllmain",
152 .linkage = .dynamic,
153 .root_module = b.createModule(.{
154 .root_source_file = b.path("dllmain.zig"),
155 .target = target,
156 .optimize = .Debug,
157 }),
158 });
159
160 const run = b.addRunArtifact(load_dll_exe);
161 run.addArtifactArg(dll);
162 run.expectStdErrEqual("hello from DllMain");
163 run.expectStdOutEqual("");
164 run.expectExitCode(0);
165 run.skip_foreign_checks = true;
166
167 test_step.dependOn(&run.step);
168 }
169
170 {
171 const dll = b.addLibrary(.{
172 .name = "zig_dllmain_link_libc",
173 .linkage = .dynamic,
174 .root_module = b.createModule(.{
175 .root_source_file = b.path("dllmain.zig"),
176 .target = target,
177 .optimize = .Debug,
178 .link_libc = true,
179 }),
180 });
181
182 const run = b.addRunArtifact(load_dll_exe);
183 run.addArtifactArg(dll);
184 run.expectStdErrEqual("hello from DllMain");
185 run.expectStdOutEqual("");
186 run.expectExitCode(0);
187 run.skip_foreign_checks = true;
188
189 test_step.dependOn(&run.step);
190 }
191
192 {
193 const dll = b.addLibrary(.{
194 .name = "c_dllmain",
195 .linkage = .dynamic,
196 .root_module = b.createModule(.{
197 .target = target,
198 .optimize = .Debug,
199 .link_libc = true,
200 }),
201 });
202 dll.root_module.addCSourceFile(.{ .file = b.path("dllmain.c") });
203
204 const run = b.addRunArtifact(load_dll_exe);
205 run.addArtifactArg(dll);
206 run.expectStdErrEqual("hello from DllMain");
207 run.expectStdOutEqual("");
208 run.expectExitCode(0);
209 run.skip_foreign_checks = true;
210
211 test_step.dependOn(&run.step);
212 }
142213}
test/standalone/windows_entry_points/dllmain.c created+9
......@@ -0,0 +1,9 @@
1#include <windows.h>
2#include <stdio.h>
3
4BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
5 if (fdwReason == DLL_PROCESS_ATTACH) {
6 fprintf(stderr, "hello from DllMain");
7 }
8 return TRUE;
9}
test/standalone/windows_entry_points/dllmain.zig created+18
......@@ -0,0 +1,18 @@
1const std = @import("std");
2const windows = std.os.windows;
3
4const DLL_PROCESS_ATTACH = 1;
5
6pub fn DllMain(
7 hinstDLL: windows.HINSTANCE,
8 fdwReason: windows.DWORD,
9 lpReserved: windows.LPVOID,
10) windows.BOOL {
11 _ = hinstDLL;
12 _ = lpReserved;
13 switch (fdwReason) {
14 DLL_PROCESS_ATTACH => std.debug.print("hello from DllMain", .{}),
15 else => {},
16 }
17 return .TRUE;
18}
test/standalone/windows_entry_points/loaddll.zig created+13
......@@ -0,0 +1,13 @@
1const std = @import("std");
2const windows = std.os.windows;
3
4extern "kernel32" fn LoadLibraryW(windows.LPCWSTR) callconv(.winapi) ?windows.HMODULE;
5
6pub fn main(init: std.process.Init) !void {
7 const arena = init.arena.allocator();
8 const args = try init.minimal.args.toSlice(arena);
9 if (args.len < 2) return error.NoDllPathSpecified;
10 const dll_path = args[1];
11 const dll_path_w = try std.unicode.wtf8ToWtf16LeAllocZ(arena, dll_path);
12 _ = LoadLibraryW(dll_path_w) orelse return error.FailedToLoadDll;
13}