zig icon indicating copy to clipboard operation
zig copied to clipboard

Segmentation fault when linking with shared library

Open hdante opened this issue 3 years ago • 9 comments

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
(...)

hdante avatar Jul 05 '22 22:07 hdante

should it not be $ zig build-exe -dynamic shared.zig -L. -ladd ?

nektro avatar Jul 05 '22 23:07 nektro

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.

hdante avatar Jul 06 '22 00:07 hdante