-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
Zig Version
0.14.1
Steps to Reproduce and Observed Behavior
You have the following files that will be built into static libraries:
first.c -> libfirst.a
int someFunction(int in)
{
return in + 4;
}
second.c -> libsecond.a
int someFunction(int in);
int anotherOne(int a)
{
return 70 + someFunction(a);
}
You have the following files that get built into an application:
other.h
#pragma once
int someFunction(int val);
other.c
#include "other.h"
int someFunction(int val)
{
return val * 99;
}
main.c
#include "other.h"
#include <stdio.h>
int main(void)
{
int v = someFunction(1);
printf("SomeFunction: %d\n", v);
return 0;
}
Then, finally, a build.zig
that compiles said static libraries, compiles the application, and links in the two compiled static libraries as "system libraries":
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Build libfirst.a and libsecond.a
const libfirst_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
libfirst_mod.addCSourceFiles(.{
.files = &.{"lib/first.c"},
.flags = &.{},
});
const libfirst_lib = b.addLibrary(.{
.name = "first",
.linkage = .static,
.root_module = libfirst_mod,
});
b.installArtifact(libfirst_lib);
const libsecond_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
libsecond_mod.addCSourceFiles(.{
.files = &.{"lib/second.c"},
.flags = &.{},
});
const libsecond_lib = b.addLibrary(.{
.name = "second",
.linkage = .static,
.root_module = libsecond_mod,
});
b.installArtifact(libsecond_lib);
const exe_mod = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
});
exe_mod.addCSourceFiles(.{
.files = &.{ "src/main.c", "src/other.c" },
.flags = &.{},
});
exe_mod.addLibraryPath(b.path("zig-out/lib"));
exe_mod.linkSystemLibrary("first", .{
.needed = true,
.preferred_link_mode = .static,
.use_pkg_config = .no,
});
exe_mod.linkSystemLibrary("second", .{
.needed = true,
.preferred_link_mode = .static,
.use_pkg_config = .no,
});
const exe = b.addExecutable(.{
.name = "system_lib_bug",
.root_module = exe_mod,
});
exe.step.dependOn(&libfirst_lib.step);
exe.step.dependOn(&libsecond_lib.step);
exe.verbose_link = true;
b.installArtifact(exe);
}
You will then observe after a zig build
you get a duplicate symbol error from the linker:
error: ld.lld: duplicate symbol: someFunction
note: defined at first.c:2 (lib/first.c:2)
note: /home/hayden/Documents/zig/system-lib-bug/.zig-cache/o/69cb02e8173c3d8f38a48464beadae4e/first.o:(someFunction) in archive /home/hayden/Documents/zig/system-lib-bug/zig-out/lib/libfirst.a
note: defined at other.c:4 (src/other.c:4)
note: /home/hayden/Documents/zig/system-lib-bug/.zig-cache/o/d8b8c19561fd3c4a8ee0ccc2ed542ebd/other.o:(.text+0x0)
Examining the call to the ld.lld
linker, we see that the static library files are listed before the application object files:
ld.lld --error-limit=0 -mllvm -float-abi=hard --entry _start -z stack-size=16777216 --build-id=none --image-base=16777216 --eh-frame-hdr -znow -m elf_x86_64 -o /home/hayden/Documents/zig/system-lib-bug/.zig-cache/o/6b4482bbff8fb40762452bcb96604ef7/system_lib_bug /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crti.o -rpath /home/hayden/Documents/zig/system-lib-bug/zig-out/lib -L /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /home/hayden/Documents/zig/system-lib-bug/zig-out/lib/libfirst.a /home/hayden/Documents/zig/system-lib-bug/zig-out/lib/libsecond.a /home/hayden/Documents/zig/system-lib-bug/.zig-cache/o/76131da7b5c1ad982a5a2281b4b71714/main.o /home/hayden/Documents/zig/system-lib-bug/.zig-cache/o/d8b8c19561fd3c4a8ee0ccc2ed542ebd/other.o /home/hayden/.cache/zig/o/ee9c7c2323795433f5cfe8074ac46f67/libubsan_rt.a --as-needed -lm -lpthread -lc -ldl -lrt -lutil /home/hayden/.cache/zig/o/198003ee6be6fa4a77cf81686a4c740a/libcompiler_rt.a /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crtn.o
Changing the order so that these libraries instead come after the application objects (like the other system libraries -lc, -ldl -lrt ...
):
zig ld.lld --error-limit=0 -mllvm -float-abi=hard --entry _start -z stack-size=16777216 --build-id=none --image-base=16777216 --eh-frame-hdr -znow -m elf_x86_64 -o /home/hayden/Documents/zig/system-lib-bug/.zig-cache/o/6b4482bbff8fb40762452bcb96604ef7/system_lib_bug /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crti.o -rpath /home/hayden/Documents/zig/system-lib-bug/zig-out/lib -L /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /home/hayden/Documents/zig/system-lib-bug/.zig-cache/o/76131da7b5c1ad982a5a2281b4b71714/main.o /home/hayden/Documents/zig/system-lib-bug/.zig-cache/o/d8b8c19561fd3c4a8ee0ccc2ed542ebd/other.o /home/hayden/.cache/zig/o/ee9c7c2323795433f5cfe8074ac46f67/libubsan_rt.a /home/hayden/Documents/zig/system-lib-bug/zig-out/lib/libfirst.a /home/hayden/Documents/zig/system-lib-bug/zig-out/lib/libsecond.a --as-needed -lm -lpthread -lc -ldl -lrt -lutil /home/hayden/.cache/zig/o/198003ee6be6fa4a77cf81686a4c740a/libcompiler_rt.a /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crtn.o
Link is successful, and the expected behavior of the application's implementation of someFunction
effectively "overriding" the system libraries implementation holds true:
/home/hayden/Documents/zig/system-lib-bug/.zig-cache/o/6b4482bbff8fb40762452bcb96604ef7/system_lib_bug
SomeFunction: 99
Expected Behavior
I would expect "system" library archives to come after application object files in the linker invocation. This article goes into pretty good detail about linker behavior in regards to ordering static libraries.
For context, this came about because of the following situation:
- I'm building for a freestanding Cortex-M target
- I'm porting to Zig's compiler from the arm-none-eabi-gcc compiler
- This compiler ships with pre-compiled
.a
files for the C standard library for any given target - I'm now using Zig (LLVM backend) to compile this project, however I'm linking in this pre-compiled C standard library
- I'm running into a duplicate symbol error, since application code provides an implementation of
_sbrk
- However, providing your own implementation for a standard library function and relying on link order to "take your implementation first" is a valid use case and relatively common in my world where the C standard library is exclusively statically linked into the binary. This isn't just me trying something hacky either, this comes from the chip vendor's HAL code.