Is the binary executable compiled by Rust defautl to static link glibc?

The environment of my server is Ubuntu, and the version of the glibc is 2.27. If I default to compile the code by using cross, then the binary can successfully run on my server. However, if I set the following configuration

strip = true  # Automatically strip symbols from the binary.
opt-level = "z"  # Optimize for size.
lto = true
panic = "abort"

Then, if I run the binary executable file, the terminal will report /lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.28' not found (required by ./test-file`). What makes the difference here? Why is the binary cannot run on the server if I give it the configuration? Does the default configuration link glibc statically such that the result binary can be run on the server that has a lower version of glibc?

The binary that is fault to run has the following information

// ldd test-file
./test-file: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by ./test-file)
        linux-vdso.so.1 (0x00007fff0cbb3000)
        libssl.so.1.1 => /usr/lib/x86_64-linux-gnu/libssl.so.1.1 (0x00007f7e6a7ea000)
        libcrypto.so.1.1 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 (0x00007f7e6a31e000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f7e6a106000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f7e69ee7000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f7e69b49000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f7e69945000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7e69554000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f7e6b10c000)

// strings ./test-file  | grep GLIBC_2.28
GLIBC_2.28

The binary that is successful to run has the following information

root@ubuntu:/home# ldd ./test-file-ok
        linux-vdso.so.1 (0x00007ffda2b1f000)
        libssl.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007f99d6a92000)
        libcrypto.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007f99d664e000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f99d6436000)
        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f99d622e000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f99d600f000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f99d5c71000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f99d5a6d000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f99d567c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f99d7a15000)

// strings ./test-file-ok  | grep GLIBC_2.28

The *-linux-gnu architecture targets, one of which I assume you're using, do dynamic linking by default.

This means that the glibc version on your build machine or container affects the libc symbols linked to in your final binary. Your build environment has a glibc version >= 2.28, and the linker invoked by the Rust compiler has used a symbol that requires at least this version of glibc, which means your server can't run the program.

I suspect lto = true is the culprit, since Link-Time Optimizations can affect the symbols linked. You can try disabling LTO or switch to a *-linux-musl target that statically links the musl library, a drop-in replacement for glibc.

Incidentally, you can try running objdump -T your_binary_file on the working and non-working builds to dump out the dynamic symbol tables and compare them.

fault.txt

ok.txt

Here are the dump files, I'm not sure what the culprit is. I also find that it even cannot run a simple hello word program that is compiled without LTO, on my server, which reports the Glibc 2.28 error. However, the complex program(i.e. test-ok, which uses tokio, reqwest, and so on) that is compiled without LTO instead can directly run on my server.

In the faulty symbol table, you have this symbol:

0000000000000000  w   DF *UND*	0000000000000000  GLIBC_2.28  statx

whereas in the working program it's:

0000000000000000  w   D  *UND*	0000000000000000              statx

Also, the working symbol table has no version requirements higher than GLIBC_2.18.

Edit: the OpenSSL versions linked are different too (1.1 vs 1.0).

Are you sure you're compiling the two programs using the same cross toolchain and target? It looks like the working program is being compiled on an older build environment, but I could be mistaken.

You can try setting your build target to x86_64-unknown-linux-gnu:centos (see instructions here), as this will do dynamic linking on an older build container, so it shouldn't cause problems when you run the executable file.

1 Like

They are compiled in the same environment, merely, one is opened LTO another is default configuration(i.e. no LOT).

Are you sure? They both link to different versions of OpenSSL at least, which LTO can't have an influence on.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.