Zig 0.16 cross-compiling with external uClibc toolchain: sysroot/libc linking and ELF interpreter issues

Hello,

zig 0.16

I want to build an app that uses a library which links against the libc provided by the arm-buildroot-linux-uclibcgnueabihf-4.9.4 toolchain. I can compile my code with Zig, but I’m having trouble linking to that library because it depends on the toolchain libc.

When I build, either it links against my system libc or it fails to link against the toolchain libc. I tried using setLibCFile() but ldd reports:

error: ld.lld: cannot open /lib/libc.so.1: No such file or directory
error: 1 compilation errors

The linker is searching my system lib instead of the toolchain sysroot. I tried setting the sysroot with b.sysroot =, but the toolchain is downloaded and extracted by build.zig, so b.sysroot would need to be a lazy path to work. If I extract the toolchain outside of build.zig and set b.sysroot, the linker then cannot find Zig’s libraries. (like undefined getauxval)

I thought it might be a bug, so I tried Zig 0.17.0-dev.958+c5d6277ac, but b.sysroot no longer exists there and I still have the same libc problem.

I considered the hack ln -s /mytoolchain/sysroot/lib/libc.so.1 /lib/libc.so.1, but that didn’t work either. (and it’s awful…)

If I didn’t have to use this librairie, everything would be fine, but I need it for DTLS.
I’ve been working on this for three days and have tried many approaches, but I’m stuck. Any ideas how to fix this?


Another issue: for code that links against shared libraries with no dependencies, I must manually set the interpreter using patchelf. The dynamic-linker field does not work with shared libraries (and I don’t really understand what it does :eyes:), so I have to do this:

const patchelf = b.addSystemCommand(&.{
    "patchelf",
    "--set-interpreter",
    "/lib/ld-uClibc.so.0",
});
patchelf.addArg("--output");
const output = patchelf.addOutputFileArg("patched.elf");
patchelf.addFileArg(exe.getEmittedBin());
patchelf.step.dependOn(&exe.step);
b.getInstallStep().dependOn(&b.addInstallFileWithDir(output, .bin, "patched").step);

Now I can’t build on Windows and my team uses Windows. I’m the only one on Linux.
Is there a way to set the interpreter for the executable without using patchelf?

I made it work by reversing the build pipeline. I built the Zig code as a static library without libc and then linked everything with the toolchain (arm-buildroot-linux-uclibcgnueabihf-4.9.4).

I had some issues with getauxval and memory corruption related to TLS. But I made it work by implementing my own posixCallMainAndExit with some adjustments. I learned a lot and it was fun x)

edit: I also had to add a function to link against the other functions. Zig refuses to build with unresolved external functions, even if it is built as a library. Maybe there are better ways to do this, but I still have a lot to learn haha.

export fn register_api(
    sys_int_: ?*const fn (...) callconv(.c) c_int,
    sys_uninit_: ?*const fn (...) callconv(.c) void,
    set_eventNotify_cb_: ?*const fn (cb: cbNotify) callconv(.c) void,
    set_commonCmd_cb_: ?*const fn (cb: cbOemCmd) callconv(.c) void,
    cgi_cmd_: ?*const fn (cgiStr: [*c]const u8, cgiResult: [*c]u8, len: c_int, timeout: c_int) callconv(.c) c_int,
    getOemUsrAccount_: ?*const fn (user: [*c]u8, password: [*c]u8) callconv(.c) void,
    setOemUsrAccount_: ?*const fn (user: [*c]u8, password: [*c]u8) callconv(.c) void,
    start_: ?*const fn (mediaPara_: *mediaPara) callconv(.c) c_int,
    stop_: ?*const fn () callconv(.c) c_int,
    getVideoFrame_: ?*const fn (frame: [*c]u8, len: *c_int) callconv(.c) c_int,
    getAudioFrame_: ?*const fn (frame: [*c]u8, len: *c_int) callconv(.c) c_int,
) callconv(.c) void {
    Logger.debug("Registering libFosOem API functions", .{});
    __sys_int = sys_int_;
    __sys_uninit = sys_uninit_;
    __set_eventNotify_cb = set_eventNotify_cb_;
    __set_commonCmd_cb = set_commonCmd_cb_;
    __cgi_cmd = cgi_cmd_;
    __getOemUsrAccount = getOemUsrAccount_;
    __setOemUsrAccount = setOemUsrAccount_;
    __start = start_;
    __stop = stop_;
    __getVideoFrame = getVideoFrame_;
    __getAudioFrame = getAudioFrame_;
}