tsdtas
October 10, 2024, 8:01am
1
I am currently in the process of trying out zig build for compiling an application for an embedded linux platform. No problems compiling at all (and loving how easy Zig makes cross compilation ), but I can’t seem to wrap my head around linking shared libraries.
The application relies on a closed-source shared library.
With Cmake I can do this:
set(TARGET_SYSROOT "/path/to/sysroot")
set(CMAKE_C_FLAGS "--sysroot=${TARGET_SYSROOT}")
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
[...]
target_link_libraries( ${X} sharedlib )
Which will:
Make the compiler look for the library in the sysroot (e.g., /path/to/sysroot/lib/libsharedlib.so), and
Make the application load the library from its usual location (e.g. /lib/libsharedlib.so)
The equivalent zig seems to be invoking
zig build --sysroot "/path/to/sysroot" --search-prefix "/path/to/sysroot"
and then
exe.linkSystemLibrary("sharedlib");
in the build script.
This compiles just fine, but when I load it onto the device, it looks for the shared library in “/path/to/sysroot”, so it fails to run:
error while loading shared libraries: /path/to/sysroot/lib/libsharedlib.so: cannot open shared object file: No such file or directory
Considering how nice everything else regarding cross compilation is in Zig, I’m surprised I can’t seem to get this working.
What am I doing wrong?
(I can share more information about the target and build process if needed)
It’s a bug, check out this
opened 05:44AM - 26 Dec 23 UTC
bug
### Zig Version
0.12.0-dev.1849+bb0f7d55e
### Steps to Reproduce and Observed … Behavior
I have a project where I need to cross-compile to RPi using a 3rd party library that I don't have access to the source code only the shared object (libEDSDK.so). The project compiles fine but it generate invalid runtime paths in the executable.
```bash
$ readelf -d zig-shared-obj
Dynamic section at offset 0xa2cf4 contains 26 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [/Users/marrony/Development/zig-shared-obj/lib/libEDSDK.so]
0x00000001 (NEEDED) Shared library: [libpthread.so.0]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x00000001 (NEEDED) Shared library: [ld-linux-armhf.so.3]
....
```
**build.zig**
```zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const default_target = std.zig.CrossTarget.parse(.{
.arch_os_abi = "arm-linux-gnueabihf",
}) catch @panic("unknown target");
const target = b.standardTargetOptions(.{
.default_target = default_target,
});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "zig-shared-obj",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
exe.addLibraryPath(.{ .path = "lib" });
exe.addIncludePath(.{ .path = "include" });
exe.defineCMacro("TARGET_OS_LINUX", null);
exe.linkLibC();
// how to properly link lib/libEDSDK.so ?
exe.linkSystemLibrary("EDSDK");
b.installArtifact(exe);
}
```
**src/main.zig**
```zig
const std = @import("std");
const c = @cImport({
@cInclude("stdbool.h");
@cInclude("EDSDK.h");
});
pub fn main() void {
if (c.EdsInitializeSDK() != c.EDS_ERR_OK) {
std.debug.print("Error initializing the library\n", .{});
}
std.debug.print("Hello World\n", .{});
if (c.EdsTerminateSDK() != c.EDS_ERR_OK) {
std.debug.print("Error terminating the library\n", .{});
}
}
```
Here is a reproducible example: https://github.com/marrony/zig-shared-obj
Related issues?
* https://github.com/ziglang/zig/issues/15849
* https://github.com/ziglang/zig/issues/17373
### Expected Behavior
I expect the final executable to not have fullpath of the linked shared objects.
ziglang:master
← marrony:master
opened 05:11AM - 27 Dec 23 UTC
Solves https://github.com/ziglang/zig/issues/18373
This PR allow users to do … `exe.linkSystemLibrary(":libEDSDK.so")` and `exe.addRPathSpecial("$ORIGIN")` preserving the library name.
`zig build-exe` already support this notation:
```bash
$ zig build-exe src/main.zig -I ./include -DTARGET_OS_LINUX -L ./lib -l:libEDSDK.so -lc -rpath \$ORIGIN -target arm-linux-gnueabihf --name zig-shared-obj
```
Trying with `zig build`
**build.zig**
```zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const default_target = std.zig.CrossTarget.parse(.{
.arch_os_abi = "arm-linux-gnueabihf",
}) catch @panic("unknown target");
const target = b.standardTargetOptions(.{
.default_target = default_target,
});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "zig-shared-obj",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
exe.addLibraryPath(.{ .path = "lib" });
exe.addIncludePath(.{ .path = "include" });
exe.defineCMacro("TARGET_OS_LINUX", null);
exe.linkLibC();
exe.linkSystemLibrary(":libEDSDK.so");
exe.addRPathSpecial("$ORIGIN");
b.installArtifact(exe);
}
```
**src/main.zig**
```zig
const std = @import("std");
const c = @cImport({
@cInclude("stdbool.h");
@cInclude("EDSDK.h");
});
pub fn main() void {
if (c.EdsInitializeSDK() != c.EDS_ERR_OK) {
std.debug.print("Error initializing the library\n", .{});
}
std.debug.print("Hello World\n", .{});
if (c.EdsTerminateSDK() != c.EDS_ERR_OK) {
std.debug.print("Error terminating the library\n", .{});
}
}
```
Both `zig build` and `zig build-exe` produced the same result below:
```bash
$ readelf -d zig-shared-obj
Dynamic section at offset 0x894dc contains 26 entries:
Tag Type Name/Value
0x0000001d (RUNPATH) Library runpath: [$ORIGIN]
0x00000001 (NEEDED) Shared library: [libEDSDK.so]
0x00000001 (NEEDED) Shared library: [libpthread.so.0]
0x00000001 (NEEDED) Shared library: [libc.so.6]
....
```
tsdtas
October 10, 2024, 10:54am
3
Ah, a shame. Thanks for the links though. Appreciated.
For now I’ve fixed it by patching the files as a post-build step.
It’s not blocking, but it is a little annoying.
What is the expected correct behaviour, then?
zig --sysroot "whatever"
Searching “./whatever” on the compiler-host and root dir on target?
Edit : Marking this solved.
tsdtas
October 11, 2024, 9:17am
4
In case anyone is interested, here’s what I did as a temporary fix:
readelf -d
will show you the incorrectly assigned library paths:
readelf -d /path/to/binary
Tag Type Name/Value
...
0x00000001 (NEEDED) Shared library: [/path/to/sysroot/lib/libsharedlib.so]
...
then patchelf --replace-needed
can patch the library path.
Patchelf is a NixOS system utility available here .
patchelf --replace-needed /path/to/sysroot/lib/libsharedlib.so libsharedlib.so /path/to/binary
now the path is correctly assigned:
readelf -d /path/to/binary
Tag Type Name/Value
...
0x00000001 (NEEDED) Shared library: [libsharedlib.so]
...
A pure zig solution is welcome if one is available.
2 Likes