Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zig cc: detect -static and treat -l options as static library names #4986

Open
ghost opened this issue Apr 9, 2020 · 9 comments
Open

zig cc: detect -static and treat -l options as static library names #4986

ghost opened this issue Apr 9, 2020 · 9 comments
Labels
enhancement Solving this issue will likely involve adding new logic or components to the codebase. zig cc Zig as a drop-in C compiler feature
Milestone

Comments

@ghost
Copy link

ghost commented Apr 9, 2020

So I'm not sure if I'm doing something incorrectly, but I can't really figure out, so I came with this reproducible example.
Consider we have a C file:

#include <stdio.h>
int main()
{
    printf("Hello World!\n");
}

We want to build it for arm-linux-musleabi as a static binary and link to libressl, so we do this:

wget https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.1.0.tar.gz
tar xvf libressl-3.1.0.tar.gz
cd libressl-3.1.0

export CC="zig cc -target arm-linux-musleabi -static"

./configure --host=arm-linux-musleabi --disable-shared --prefix=/some/dir

make -j16 -C crypto
make -j16 -C ssl
make -j16 -C crypto install
make -j16 -C ssl install

After that we build our C file:

zig cc -target arm-linux-musleabi -I/path/to/libressl-3.1.0/include/openssl \
-L/some/dir/lib -lssl -lcrypto -static hello.c

ldd tells that it's in fact not a dynamic binary:

$ ldd hello
        not a dynamic executable

But if we try to run file on it:

$ file hello
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-arm.so.1, not stripped

Or even if we do strings hello, /lib/ld-musl-arm.so.1 will be there too.
And we won't be able to run it on any ARM-based machine which doesn't have that file:

$ qemu-arm ./hello
/lib/ld-musl-arm.so.1: No such file or directory

(Maybe I just used some compiler options incorrectly, but I'm not really experienced with C compiler arguments)

@ghost
Copy link
Author

ghost commented Apr 9, 2020

Also, on an unrelated note - is it possible to do "--strip" (like in zig build-exe) with zig cc?

@Vexu Vexu added the zig cc Zig as a drop-in C compiler feature label Apr 9, 2020
@andrewrk
Copy link
Member

andrewrk commented Apr 9, 2020

Also, on an unrelated note - is it possible to do "--strip" (like in zig build-exe) with zig cc?

--strip (like in zig build-exe) is enabled by default for zig cc, and turns off if you pass -g or other similar debug options.

For the other problem-

I looked through the code a bit and I believe the problem is -lssl and -lcrypto making zig think you are dynamic linking against those libraries. So this is a missing feature of zig cc, to detect -static and make it look for .a files instead.

If you are looking for a workaround in the meantime, it is to pass the .a files as positional arguments instead of -l parameters.

@andrewrk andrewrk added the enhancement Solving this issue will likely involve adding new logic or components to the codebase. label Apr 9, 2020
@andrewrk andrewrk added this to the 0.7.0 milestone Apr 9, 2020
@andrewrk andrewrk changed the title zig cc makes a dynamic library even when told not to zig cc: detect -static and treat -l options as static library names Apr 9, 2020
@ghost
Copy link
Author

ghost commented Apr 10, 2020

@andrewrk I'm not sure if I'm doing something wrong but it doesn't seem like strip is enabled by default, with the same C file:

#include <stdio.h>
int main()
{
    printf("Hello World!\n");
}
$ zig cc -target x86_64-linux-musl -static -O3 hello.c

$ file file hello                                                                                                                                                                           
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

$ du -sh hello
12K     hello

$ strip hello

$ du -sh hello
8.0K    hello

$ file hello                                                                                                                                                                           
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped

@andrewrk
Copy link
Member

andrewrk commented Apr 10, 2020

Is that different than zig build-exe though? The strip functionality is not fully implemented; currently it only omits debug info.

@andrewrk andrewrk modified the milestones: 0.7.0, 0.8.0 Oct 14, 2020
@andrewrk andrewrk modified the milestones: 0.8.0, 0.9.0 Nov 6, 2020
@andrewrk andrewrk modified the milestones: 0.9.0, 0.10.0 May 19, 2021
@makew0rld
Copy link

makew0rld commented Sep 14, 2021

Not sure if this is the same issue, but zig cc has the same problem with -Wl,-Bstatic which sets all the -l libs after it as statically linked. They end up being dynamically linked anyway. This happens without -static being specified. Running the same command with clang doesn't have this issue.

Edit: Also, Zig doesn't even seem to know the options:

warning: unsupported linker arg: -Bstatic
warning: unsupported linker arg: -Bdynamic

@SoftwareApe
Copy link

Facing the same issue as @makeworld-the-better-one, also doesn't work when using lld as a linker.

@nuald
Copy link
Contributor

nuald commented Oct 23, 2021

As @andrewrk suggested, there is a workaround by using object files (addObjectFile method), i.e.

const exe = b.addExecutable("exe", null);
exe.addObjectFile("/path/to/a-file");

@YugoCode
Copy link

YugoCode commented Jul 2, 2023

Could someone please provide a workaround for using zig cc instead of zig build?

I tried following command, but still get error: undefined symbol for every libcurl function:

zig cc main.c web/https.c helper/helper.c utils/json/cJSON.c -I. -L/usr/local/lib /usr/local/lib/libcurl.a /usr/local/lib/libmbedcrypto.a /usr/local/lib/libmbedtls.a /usr/local/lib/libmbedx509.a -static

This command builds without a problem:

gcc main.c web/https.c helper/helper.c utils/json/cJSON.c -I. -L/usr/local/lib -lcurl -lmbedcrypto -lmbedtls -lmbedx509

@polarathene
Copy link

polarathene commented Mar 7, 2024

UPDATE: I misunderstood this issue:

  • -static is not recognized by zig cc, it doesn't complain about it when provided like some other options do with error: Unknown Clang option. This issue is asking for zig cc to detect and handle -static properly.
  • As shown below when providing the static *.a libraries explicitly as a suggested workaround above, Zig is still configuring a dynamic linker / interpreter which is causing the segfault?

I looked through the code a bit and I believe the problem is -lssl and -lcrypto making zig think you are dynamic linking against those libraries. So this is a missing feature of zig cc, to detect -static and make it look for .a files instead.

If you are looking for a workaround in the meantime, it is to pass the .a files as positional arguments instead of -l parameters.

Just building the hello world example without any -l parameters still ignores -static for a glibc (-gnu) target:

hello.c:

#include <stdio.h>
int main()
{
    printf("Hello World!\n");
}
# zig 0.11.0 (Aug 2023) via Fedora 40 Docker image:

$ zig cc -static -o hello hello.c
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, with debug_info, not stripped
$ ldd hello
        linux-vdso.so.1 (0x00007fff22ad0000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f59ddc91000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f59dde83000)

# Explicit target, same output except for `GNU/Linux 2.0.0`:
$ zig cc -static -target x86_64-linux-gnu -o hello hello.c
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.0.0, with debug_info, not stripped

# Musl target is static, unlike glibc/gnu:
$ zig cc -static -target x86_64-linux-musl -o hello hello.c
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped
$ ldd hello
        not a dynamic executable

I imagine that's the actual issue here?


UPDATE: Nevermind, I understand the advice/discussion now 😅

To successfully static link the hello world on a -gnu target, you need to reference the absolute path for libc.a, which then fails with:

LLD Link... ld.lld: error: undefined symbol: _Unwind_Resume
>>> referenced by iofputs.o:(_IO_fputs.cold) in archive /usr/lib64/libc.a
>> referenced by fileops.o:(_IO_new_file_underflow.cold) in archive /usr/lib64/libc.a
>> referenced by wfileops.o:(_IO_wfile_underflow.cold) in archive /usr/lib64/libc.a
>> referenced 4 more times

ld.lld: error: undefined symbol: __gcc_personality_v0
>>> referenced by iofputs.o:(DW.ref.__gcc_personality_v0) in archive /usr/lib64/libc.a

So you need to also reference libgcc_eh.a explicitly too.

On fedora these can be found:

# Find a package that provides this file, in this case it's `gcc`:
$ dnf provides *gcc_eh.a
gcc-14.0.1-0.7.fc40.x86_64 : Various compilers (C, C++, Objective-C, ...)
Repo        : @System
Matched from:
Other       : *gcc_eh.a

# Query the package for the actual install path:
$ dnf repoquery -l gcc | grep gcc_eh.a
/usr/lib/gcc/x86_64-redhat-linux/14/32/libgcc_eh.a
/usr/lib/gcc/x86_64-redhat-linux/14/libgcc_eh.a

# Same process for `libc.a`:
$ dnf provides *libc.a
glibc-static-2.39-2.fc40.x86_64 : C library static libraries for -static linking.
Repo        : @System
Matched from:
Other       : *libc.a

$ dnf repoquery -l glibc-static | grep libc.a
/usr/lib/libc.a
/usr/lib64/libc.a

So until this issue is fixed, it'll vary based on build host:

# Split to multi-line for readability:
$ zig cc -static -target x86_64-linux-gnu -o hello \
  hello.c \
  /usr/lib64/libc.a \
  /usr/lib/gcc/x86_64-redhat-linux/14/libgcc_eh.a

$ ldd hello
        statically linked

# Despite that, seems to still be dynamically linked?
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.0.0, with debug_info, not stripped

$ nm -an hello | grep dl_open
0000000000290e60 t dl_open_worker
0000000000291190 t _dl_open
00000000002914c0 t dl_open_worker_begin

Perhaps it needs something else for file to not treated it as dynamically linked?

  • I assume that's due to the _dl_open. I'm aware that glibc can be statically linked and still have _dl_open, so it may be due to that.
  • I'm more familiar with Rust where the equivalent glibc static link build does not have the extra dynamic output from file / nm commands.
  • EDIT: I probably should have run it first, the above approach segfaults 😂 (despite being run on the same build host) so I'm definitely missing something else, or doing something wrong?

Related issues:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Solving this issue will likely involve adding new logic or components to the codebase. zig cc Zig as a drop-in C compiler feature
Projects
None yet
Development

No branches or pull requests

7 participants