zig
zig copied to clipboard
Segmentation fault when linking with shared library
Hello, I'm getting a segfault when executing the following trivial program:
const std = @import("std");
const stdout = std.io.getStdOut().writer();
extern fn add(a: c_int, b: c_int) c_int;
pub fn main() !void {
const a: c_int = 1;
const b: c_int = 2;
const c = add(1, 2);
try stdout.print("{} + {} = {}\n", .{a, b, c});
}
int add(int a, int b)
{
return a+b;
}
$ cc -shared -nostdlib -o libadd.so add.c
$ zig build-exe -dynamic shared.zig -l ./libadd.so
$ ./shared
Segmentation fault at address 0x0
???:?:?: 0x0 in ??? (???)
/usr/lib/zig/std/start.zig:561:37: 0x22430a in std.start.callMain (shared)
const result = root.main() catch |err| {
^
/usr/lib/zig/std/start.zig:495:12: 0x205667 in std.start.callMainWithArgs (shared)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/usr/lib/zig/std/start.zig:409:17: 0x204764 in std.start.posixCallMainAndExit (shared)
std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
^
/usr/lib/zig/std/start.zig:322:5: 0x204571 in std.start._start (shared)
@call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
^
Aborted (core dumped)
Hint: the resulting executable does not have an entry for the dynamic linker on its program headers:
$ readelf -lW ./shared
(...)
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000200040 0x0000000000200040 0x0001f8 0x0001f8 R 0x8
LOAD 0x000000 0x0000000000200000 0x0000000000200000 0x002490 0x002490 R 0x1000
LOAD 0x002490 0x0000000000203490 0x0000000000203490 0x030bf0 0x030bf0 R E 0x1000
LOAD 0x033080 0x0000000000235080 0x0000000000235080 0x001480 0x001480 RW 0x1000
LOAD 0x034500 0x0000000000237500 0x0000000000237500 0x000020 0x003c64 RW 0x1000
TLS 0x033080 0x0000000000234080 0x0000000000234080 0x000000 0x000018 R 0x8
DYNAMIC 0x034428 0x0000000000236428 0x0000000000236428 0x0000d0 0x0000d0 RW 0x8
GNU_RELRO 0x033080 0x0000000000235080 0x0000000000235080 0x001480 0x001f80 R 0x1
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x1000000 RW 0
(...)
The current workaround seems to be linking with libc:
$ zig build-exe -dynamic shared.zig -lc -l ./libadd.so
$ ./shared
1 + 2 = 3
In this case, the executable contains an entry for the dynamic linker:
$ readelf -lW ./shared
(...)
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000200040 0x0000000000200040 0x000268 0x000268 R 0x8
INTERP 0x0002a8 0x00000000002002a8 0x00000000002002a8 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000200000 0x0000000000200000 0x002f6c 0x002f6c R 0x1000
LOAD 0x002f70 0x0000000000203f70 0x0000000000203f70 0x034d80 0x034d80 R E 0x1000
LOAD 0x037cf0 0x0000000000239cf0 0x0000000000239cf0 0x001468 0x001468 RW 0x1000
LOAD 0x039158 0x000000000023c158 0x000000000023c158 0x0000c8 0x0001a8 RW 0x1000
TLS 0x037cf0 0x0000000000238cf0 0x0000000000238cf0 0x000000 0x000018 R 0x8
DYNAMIC 0x038fd0 0x000000000023afd0 0x000000000023afd0 0x000170 0x000170 RW 0x8
GNU_RELRO 0x037cf0 0x0000000000239cf0 0x0000000000239cf0 0x001468 0x002310 R 0x1
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x1000000 RW 0
NOTE 0x0002c4 0x00000000002002c4 0x00000000002002c4 0x000020 0x000020 R 0x4
(...)
should it not be $ zig build-exe -dynamic shared.zig -L. -ladd ?
should it not be
$ zig build-exe -dynamic shared.zig -L. -ladd?
That should give an identical result when using these parameters with a C compiler, but unfortunately zig -L adds a RUNPATH entry on the binary, which is undesired:
0x000000000000001d (RUNPATH) Library runpath: [.]
For sample programs this should make no difference, but for production code, this makes a big difference (it changes the program behavior depending on the files in the named directory). This looks like another (usability) bug to me, so I'm using -l with the library path, at least for the sample program.
Anyway, using either syntax changes nothing in this bug report.