Static compilation fails inexplicably

Hi guys, I have a code work pretty good and now it just can not compile as static file since today I modified it (only add the clap lib to my program). I use the command

RUSTFLAGS="-C target-feature=+crt-static" cargo build --target x86_64-unknown-linux-gnu --release

to build my program, that's work fine before, but now I can not get the x86_64-unknown-linux-gnu static file, but still get the x86_64-unknown-linux-musl static file as usual...

file target/x86_64-unknown-linux-gnu/release/watchdog-client
target/x86_64-unknown-linux-gnu/release/watchdog-client: ELF 64-bit LSB pie executable, x86-64, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=0df4df6cd4cebdd7cab25c3867899c9a5dbea53d, for GNU/Linux 3.2.0, with debug_info, not stripped

I was confused, and I want to know why...

You are showing that file in the code block below, right?

Command line parameters and environment variables are features of the C runtime. The glibc may use dlopen internally to handle those features.

Rust supports statically linking glibc. Sometimes that's the desirable path, even given the limitations.

1 Like

Can you post the output of ldd and objdump -x on that same binary?

Statically linking glibc doesn't make the glibc itself fully statically linked. Binaries with statically linked glibc still requires dynamically linked dependencies of the glibc itself, which make the whole binary non-static. I guess this is the problem OP have.

Binaries statically linked to glibc show up as statically linked, not dynamically linked. glibc accesses libraries like nss using dlopen.

A real elf file dynamically linked to glibc have a interpreter.

ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=.... , for GNU/Linux 3.2.0, with debug_info, not stripped

A statically linked PIE elf will also show as "dynamically linked", but don't have interpreter.

ELF 64-bit LSB pie executable, x86-64, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=..., for GNU/Linux 3.2.0, with debug_info, not stripped

You can test this beheiver using gcc or clang:

> gcc test.c && file a.out
a.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=..., for GNU/Linux 3.2.0, not stripped
> gcc -static test.c && file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=..., for GNU/Linux 3.2.0, not stripped
> gcc -static-pie test.c && file a.out
a.out: ELF 64-bit LSB pie executable, x86-64, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=..., for GNU/Linux 3.2.0, not stripped

You can also test the file using readelf:

> readelf -d a.out

Dynamic section at offset 0xb5d58 contains 23 entries:
  Tag        Type                         Name/Value
 0x000000000000000c (INIT)               0x9000
 0x000000000000000d (FINI)               0x899f8
 0x0000000000000019 (INIT_ARRAY)         0xb3ee0
 0x000000000000001b (INIT_ARRAYSZ)       16 (bytes)
 0x000000000000001a (FINI_ARRAY)         0xb3ef0
 0x000000000000001c (FINI_ARRAYSZ)       16 (bytes)
 0x0000000000000004 (HASH)               0x2b8
 0x000000006ffffef5 (GNU_HASH)           0x2c8
 0x0000000000000005 (STRTAB)             0x300
 0x0000000000000006 (SYMTAB)             0x2e8
 0x000000000000000a (STRSZ)              1 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0xb7000
 0x0000000000000002 (PLTRELSZ)           576 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x7e08
 0x0000000000000007 (RELA)               0x308
 0x0000000000000008 (RELASZ)             31488 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffb (FLAGS_1)            Flags: PIE
 0x000000006ffffff9 (RELACOUNT)          1312
 0x0000000000000000 (NULL)               0x0

A statically linked PIE elf file don't have NEEDED entry.

See also: x86_64-unknown-linux-musl binaries have a DYNAMIC segment · Issue #80000 · rust-lang/rust · GitHub

Hi Josh, here is output.

ldd target/x86_64-unknown-linux-gnu/release/watchdog-client
        /lib64/ld-linux-x86-64.so.2 (0x00007f1291572000)
        linux-vdso.so.1 (0x00007ffd737e6000)

But the objdump -x get a lot of output so I redirect it to a file, here is link (objdump - Google Drive) to download it.

Yes, before I edited it, the code can compile as static and file command show it was statically not dynamically, while I send it to my Linux server without any environment, it's work good.

OMG, I understand why this is causing the problem,thank you very much, you solved a problem that has been bugging me for days :kissing_heart: