authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-26 10:08:51+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-26 18:57:55+02:00
log62f13ef4efe5aab5030ea56442b552ad0c5fec42
treefcdd607d4f2f01deb47c15c24df72b4a27ea13cd
parentf94185e67749c9cfcf21be5cf9c03baf8d2ac025

Zcu: fix overriding weak export with strong

Our "multiple definitions" check in the frontend was a little overzealous! The only case which should actually be an error is when there are multiple *strong* definitions. In the future we will eliminate this frontend check entirely, because it's redundant with the check the linker will perform---but for now it's worth keeping since our linkers do not yet print useful source location information for Zig exports. Resolves: https://codeberg.org/ziglang/zig/issues/36678

2 files changed, 20 insertions(+), 0 deletions(-)

src/Zcu/PerThread.zig+4
......@@ -3655,6 +3655,10 @@ pub fn processExports(pt: Zcu.PerThread) (Allocator.Error || Io.Cancelable)!void
36553655
36563656 for (alive_exports.items) |export_index| {
36573657 const exp = export_index.ptr(zcu);
3658 switch (exp.opts.linkage) {
3659 .strong => {},
3660 .weak, .internal, .link_once => continue,
3661 }
36583662 const gop = exports_by_name.getOrPutAssumeCapacity(exp.opts.name);
36593663 if (gop.found_existing) {
36603664 const existing_exp = gop.value_ptr.*.ptr(zcu);
test/cases/export_override_libc.zig created+16
......@@ -0,0 +1,16 @@
1/// This implementation is intentionally incorrect so we can check that it is
2/// called instead of the libc symbol.
3export fn isdigit(c: c_int) c_int {
4 return @intFromBool(c == '0');
5}
6
7pub fn main() void {
8 const static = struct {
9 extern fn isdigit(c_int) c_int;
10 };
11 if (static.isdigit('0') == 0) @panic("override failed: isdigit('0') == 0");
12 if (static.isdigit('1') != 0) @panic("override failed: isdigit('1') != 0");
13}
14
15// run
16// link_libc=true