I have compiled a Rust program for armv7-unknown-linux-gnueabihf
, and I want it to run on a system that has glibc 2.16 installed. Unfortunately when running it I get this error:
./foo: /lib/libc.so.6: version `GLIBC_2.18' not found (required by ./foo)
Running objdump -T foo
reveals that the only symbol needed from glibc 2.18 is:
00000000 w DF *UND* 00000000 GLIBC_2.18 __cxa_thread_atexit_impl
Rust makes __cxa_thread_atexit_impl
a weak symbol (as seen by the little w
flag from objdump
), however GCC is apparently stupid and even though all the symbols from GLIBC_2.18 are weak it still makes GLIBC_2.18 itself a strong requirement. You can see that with readelf
:
$ readelf -V foo
...
Version needs section '.gnu.version_r' contains 5 entries:
Addr: 0x0000000000001e4c Offset: 0x001e4c Link: 6 (.dynstr)
000000: Version: 1 File: ld-linux-armhf.so.3 Cnt: 1
0x0010: Name: GLIBC_2.4 Flags: none Version: 9
0x0020: Version: 1 File: librt.so.1 Cnt: 1
0x0030: Name: GLIBC_2.4 Flags: none Version: 5
0x0040: Version: 1 File: libgcc_s.so.1 Cnt: 4
0x0050: Name: GCC_4.3.0 Flags: none Version: 10
0x0060: Name: GCC_3.0 Flags: none Version: 7
0x0070: Name: GCC_3.5 Flags: none Version: 6
0x0080: Name: GCC_3.3.1 Flags: none Version: 4
0x0090: Version: 1 File: libc.so.6 Cnt: 2
0x00a0: Name: GLIBC_2.18 Flags: none Version: 8
0x00b0: Name: GLIBC_2.4 Flags: none Version: 3
0x00c0: Version: 1 File: libpthread.so.0 Cnt: 1
0x00d0: Name: GLIBC_2.4 Flags: none Version: 2
Notice that GLIBC_2.18
says Flags: none
. It should say Flags: WEAK
. Fortunately I found an amazing page where someone shows how to fix this. Unfortunately it involves hex editing the binary!
Take the offset of that .gnu.version_r
table (0x001e4c
), add the entry offset for GLIBC_2.18
(0x00a0
), then add an offset for the flags field of the struct at that address (0x04
). That gives 0x001EF0
. At that address there should be two zero bytes: 0x0000
. Change them to 0x0200
.
Verify with readelf
:
Version needs section '.gnu.version_r' contains 5 entries:
Addr: 0x0000000000001e4c Offset: 0x001e4c Link: 6 (.dynstr)
000000: Version: 1 File: ld-linux-armhf.so.3 Cnt: 1
0x0010: Name: GLIBC_2.4 Flags: none Version: 9
0x0020: Version: 1 File: librt.so.1 Cnt: 1
0x0030: Name: GLIBC_2.4 Flags: none Version: 5
0x0040: Version: 1 File: libgcc_s.so.1 Cnt: 4
0x0050: Name: GCC_4.3.0 Flags: none Version: 10
0x0060: Name: GCC_3.0 Flags: none Version: 7
0x0070: Name: GCC_3.5 Flags: none Version: 6
0x0080: Name: GCC_3.3.1 Flags: none Version: 4
0x0090: Version: 1 File: libc.so.6 Cnt: 2
0x00a0: Name: GLIBC_2.18 Flags: WEAK Version: 8
0x00b0: Name: GLIBC_2.4 Flags: none Version: 3
0x00c0: Version: 1 File: libpthread.so.0 Cnt: 1
0x00d0: Name: GLIBC_2.4 Flags: none Version: 2
Success! Except it still doesn't work:
./foo: /lib/libc.so.6: weak version `GLIBC_2.18' not found (required by ./foo)
./foo: relocation error: ./foo: symbol __cxa_thread_atexit_impl, version GLIBC_2.18 not defined in file libc.so.6 with link time reference
How is the weak version still required?! I can't wait for glibc to die.
Is there any way to get Rust to build the program without using this symbol?
You need a Rust toolchain which was compiled for glibc 2.16 or earlier. glibc 2.17 likely works as well because it lacks __cxa_thread_atexit_impl
, so that it will not carry a GLIBC_2.18
symbol version in the binary.
The use of the weak symbol in the Rust code is not particularly useful because GNU's particular version of ELF symbol versioning does not have weak symbol versions. We might change that eventually, but right now, the best way to deal with this is to compile with a sufficiently old toolchain.
Another option is to backport the symbol into the glibc you use. This should be a fairly isolated backport, probably consisting of these commits:
(I have not attempted the backport to glibc 2.16, but as far as such things go, it does not look particularly difficult.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With