From 6d52678a6cfee1bc05d995bef8d963a5921ed8ea Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 12 Feb 2026 13:13:48 -0800 Subject: [PATCH] zig libc malloc: skip export when unit testing These functions can only be exported when external libc components are available due to the errno location dependency. Note that even when zig libc is complete, on Windows, errno location will always be external (in ucrtbase.dll). --- build.zig | 2 +- lib/c.zig | 35 +++++++++++++++++++++++++---------- lib/c/malloc.zig | 23 +++++++++++++---------- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/build.zig b/build.zig index 5eba8f82db9fe1567cf81dae1495499b8e627bf3..ed3c764a24e566b12321711da6178db3616b958e 100644 --- a/build.zig +++ b/build.zig @@ -518,7 +518,7 @@ pub fn build(b: *std.Build) !void { .test_extra_targets = test_extra_targets, .root_src = "lib/c.zig", .name = "zigc", - .desc = "Run the zigc tests", + .desc = "Run the zig libc implementation unit tests", .optimize_modes = optimization_modes, .include_paths = &.{}, .skip_single_threaded = true, diff --git a/lib/c.zig b/lib/c.zig index b9f2a27eb7065ca5023f9d45c3e9e20fe30dbe99..2e21581588be05490b621a9525de0019e7c3fa42 100644 --- a/lib/c.zig +++ b/lib/c.zig @@ -15,17 +15,32 @@ pub const panic = if (builtin.is_test) else std.debug.no_panic; -/// Determines the symbol's visibility to other objects. -/// For WebAssembly this allows the symbol to be resolved to other modules, but will not -/// export it to the host runtime. -pub const visibility: std.builtin.SymbolVisibility = .hidden; - +/// It is possible that this libc is being linked into a different test +/// compilation, as opposed to being tested itself. In such case, +/// `builtin.link_libc` will be `true` along with `builtin.is_test`. +/// +/// When we don't have a complete libc, `builtin.link_libc` will be `false` and +/// we will be missing externally provided symbols, such as `_errno` from +/// ucrtbase.dll. In such case, we must avoid analyzing otherwise exported +/// functions because it would cause undefined symbol usage. +/// +/// Unfortunately such logic cannot be automatically done in this function body +/// since `func` will always be analyzed by the time we get here, so `comptime` +/// blocks will need to each check for `builtin.link_libc` and skip exports +/// when the exported functions have libc dependencies not provided by this +/// compilation unit. pub inline fn symbol(comptime func: *const anyopaque, comptime name: []const u8) void { - // Normally, libc goes into a static archive, making all symbols - // overridable. However, Zig supports including the libc functions as part - // of the Zig Compilation Unit, so to support this use case we make all - // symbols weak. - @export(func, .{ .name = name, .linkage = .weak, .visibility = visibility }); + @export(func, .{ + .name = name, + // Normally, libc goes into a static archive, making all symbols + // overridable. However, Zig supports including the libc functions as part + // of the Zig Compilation Unit, so to support this use case we make all + // symbols weak. + .linkage = .weak, + // For WebAssembly, hidden visibility allows the symbol to be resolved to + // other modules, but will not export it to the host runtime. + .visibility = .hidden, + }); } /// Given a low-level syscall return value, sets errno and returns `-1`, or on diff --git a/lib/c/malloc.zig b/lib/c/malloc.zig index 5ddae3776754882ad722b63543ae1285a55ee268..4ef795c278716763b40507431c5513979f0bf2d3 100644 --- a/lib/c/malloc.zig +++ b/lib/c/malloc.zig @@ -23,17 +23,20 @@ const alignment: Alignment = .fromByteUnits(alignment_bytes); const symbol = @import("../c.zig").symbol; comptime { - symbol(&malloc, "malloc"); - symbol(&aligned_alloc, "aligned_alloc"); - symbol(&posix_memalign, "posix_memalign"); - symbol(&calloc, "calloc"); - symbol(&realloc, "realloc"); - symbol(&reallocarray, "reallocarray"); - symbol(&free, "free"); - symbol(&malloc_usable_size, "malloc_usable_size"); + // Dependency on external errno location. + if (builtin.link_libc) { + symbol(&malloc, "malloc"); + symbol(&aligned_alloc, "aligned_alloc"); + symbol(&posix_memalign, "posix_memalign"); + symbol(&calloc, "calloc"); + symbol(&realloc, "realloc"); + symbol(&reallocarray, "reallocarray"); + symbol(&free, "free"); + symbol(&malloc_usable_size, "malloc_usable_size"); - symbol(&valloc, "valloc"); - symbol(&memalign, "memalign"); + symbol(&valloc, "valloc"); + symbol(&memalign, "memalign"); + } } const no_context: *anyopaque = undefined; -- 2.54.0