ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.45k stars 2.52k forks source link

[zig cc] unable to dynamically link musl executables #11909

Open motiejus opened 2 years ago

motiejus commented 2 years ago

Zig Version

0.10.0-dev.2473+e498fb155

Like the title says: zig cc frontend cannot compile a dynaimcally-linked musl binary. Created a separate issue as requested by @ifreund in https://github.com/ziglang/zig/issues/5364#issuecomment-1163025810

Full repro

main.c

#include <stdio.h>
#include <features.h>

int main() {
    #ifdef __GLIBC__
    printf("glibc_%d.%d\n", __GLIBC__, __GLIBC_MINOR__);
    #else
    printf("non-glibc\n");
    #endif
    return 0;
}

Compiling:

$ zig cc -v -target x86_64-linux-musl main.c -fPIE -lc -dynamic -o main
<...>
LLD Link... ld.lld -error-limit=0 -O0 -z stack-size=16777216 --gc-sections --eh-frame-hdr -znow -m elf_x86_64 -static -pie -o /home/motiejus/.cache/zig/o/503c6bf2ee43b2d33014a8d71f6bc1d2/main /home/motiejus/.cache/zig/o/60b545a849e93516862f973801e6307b/rcrt1.o /home/motiejus/.cache/zig/o/1f9266dba05df20689bccc6b184442ec/crti.o /home/motiejus/.cache/zig/o/6471a2fd822202eae06273e85f08f5c6/main.o /home/motiejus/.cache/zig/o/baf786ce8c76ce866d19a60d75637d88/libcompiler_rt.a --as-needed /home/motiejus/.cache/zig/o/a111f160cb9815a5d7c8128bcf0c7af8/libc.a /home/motiejus/.cache/zig/o/c8866bc9544d3faae13fa04a4728ac91/crtn.o --allow-shlib-undefined

Inspect the resulting main. It is static:

$ objdump --dynamic-syms main
main:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
no symbols

$ file main
main: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), static-pie linked, with debug_info, not stripped

Looking at the linker line above, you can see that -static is passed to the linker, and at least --dynamic-linker option is missing.

ghost commented 2 years ago

As a first-time contributor to zig, I want to try to do this. Where is the code for zig cc?

Edit: think I found it ;)

ghost commented 2 years ago

Hm, after building Zig from source, version 0.10.0-dev.2997+1653a9b25, I can no longer reproduce the issue.

My OS is Artix Linux and I have LLVM 14.0.6.

ghost commented 2 years ago

I notice the version you used expected LLVM 13, which I can't seem to easily get my system to use. Could it have been fixed by this update?

andrewrk commented 2 years ago

The resolution to this issue was reverted in 4f9345d20b3b95a1ccfe894bddd8ba5879c10c31.

andrewrk commented 2 years ago

note that the proper flag is not -dynamic but -shared.

iacore commented 1 year ago

It ignores -Wl,--dynamic-linker=x as well

vazub commented 10 months ago

0.12.0-dev.1718+3acb0e30a

I've been trying to build a dynamic executable for Python interpreter, with zig cc / musl, and can confirm that it is not working - the resulting binary is always statically linked, no matter extra -dynamic flags or whatnot.

Unless I am doing something wrong, I would say this issue is a showstopper for zig cc usage with any big and complex projects requiring a dynamic executable, where setting up own build.zig flow from scratch is prohibitively tedious.

vazub commented 10 months ago

After some poking around and testing, I've found a temporary mitigation option. It appears, that if you directly pass LDFLAGS="-dynamic-linker <path-to-interpreter>" along with compiler invocation, zig will pick it up and command the linker to build a dynamic executable.

However, there is a caveat to keep in mind: a linker path/name you pass there is not honored, and it defaults to /lib/ld-musl-x86_64.so.1 in the final linker invocation no matter what, so if you have one in some other place - you would have to use patchelf --set-interpreter <whatever-pathname-you-have> on the resulting executable to fix it.

alexrp commented 5 months ago

Just noting for any future readers that this seems to only affect executables, not shared libraries:

❯ bat main.c
       File: main.c
   1   #include <stdio.h>
   2   
   3   int main()
   4   {
   5       printf("hello world\n");
   6   }
❯ zig cc main.c -target x86_64-linux-musl -o main
❯ readelf -d main

There is no dynamic section in this file.
❯ ldd main
    not a dynamic executable
❯ file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped
❯ bat test.c
       File: test.c
   1   #include <stdio.h>
   2   
   3   void hello_world()
   4   {
   5       printf("hello world\n");
   6   }
❯ zig cc test.c -target aarch64-linux-musl -shared -fPIC -o libtest.so
❯ readelf -d libtest.so

Dynamic section at offset 0x3f0 contains 16 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so]
 0x000000000000001e (FLAGS)              BIND_NOW
 0x000000006ffffffb (FLAGS_1)            Flags: NOW
 0x0000000000000017 (JMPREL)             0x2f8
 0x0000000000000002 (PLTRELSZ)           24 (bytes)
 0x0000000000000003 (PLTGOT)             0x204f0
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000006 (SYMTAB)             0x200
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000005 (STRTAB)             0x2d0
 0x000000000000000a (STRSZ)              40 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x278
 0x0000000000000004 (HASH)               0x2a0
 0x000000000000000c (INIT)               0x10398
 0x000000000000000d (FINI)               0x103a8
 0x0000000000000000 (NULL)               0x0
❯ musl-ldd libtest.so
    musl-ldd (0x7c907ad8d000)
    libc.so => musl-ldd (0x7c907ad8d000)
Error relocating libtest.so: unsupported relocation type 1026
❯ file libtest.so
libtest.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, with debug_info, not stripped
fidgetingbits commented 5 months ago

After some poking around and testing, I've found a temporary mitigation option. It appears, that if you directly pass LDFLAGS="-dynamic-linker <path-to-interpreter>" along with compiler invocation, zig will pick it up and command the linker to build a dynamic executable.

However, there is a caveat to keep in mind: a linker path/name you pass there is not honored, and it defaults to /lib/ld-musl-x86_64.so.1 in the final linker invocation no matter what, so if you have one in some other place - you would have to use patchelf --set-interpreter <whatever-pathname-you-have> on the resulting executable to fix it.

Do you have a more exact example of how you managed to do this? I've been trying various incantations for awhile, but no luck yet. I always end up with a static binary. I am working on a project that uses zig to build some C stuff and I need to dynamically link against musl, so would be useful.