I have built a custom version of glibc. It introduces some new symbols that I use with a custom shared library. For this I add a new version: SHIM
I use gcc -g -o my_test my_test.c -l my_so.so -Wl,-dynamic-linker=/local/home/me/glibc-build/ld-linux-x86-64.so.2
to build a test executable. It needs to use the runtime linker of my custom glibc build so it can link the custom glibc symbols used in my_so.so. This actually works.
But when I use gdb
to debug the executable, I find that the default runtime linker is used. I get the following error:
/bin/bash: /lib64/ld-linux-x86-64.so.2: version `SHIM' not found (required by /local/home/me/glibc-build/libc.so.6)
How can I have gdb use my custom runtime linker? I tried to use gdb --args /local/home/me/glibc-build/ld-linux-x86-64.so.2 ./my_test
, but I end up with the same error
I recently faced the same issue in a cross compilation environment, and could not find any solution to change the dynamic linker after compilation. This post is a bit old but still very relevant, given that I had to spend some time before figuring out a solution, so I decided to contribute with my approach. I hope it will be helpful for some people.
My use case was a bit awkward as I needed to run a program that was compiled with a new cross-compiler, and using the associated new libgcc.so, and a custom glibc version (hence the new dynamic linker).
Short answer:
I passed this linker option -Wl,-dynamic-linker,custom_path_to_dynamic_linker
to GCC.
Details:
https://sourceware.org/binutils/docs-2.42/ld/Options.html
--dynamic-linker=file
Set the name of the dynamic linker. This is only meaningful when generating dynamically linked ELF executables. The default dynamic linker is normally correct; don’t use this unless you know what you are doing.
From my experience, the dynamic linker cannot be changed easily after compilation, unless you edit the ELF program header manually. You can visualize its content as follows:
$ aarch64-none-linux-gnu-readelf -e ./a.out
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R 0x8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x0000000000000070 0x0000000000000070 R 0x1
[Requesting program interpreter: ${target_runtime_sys_root}/libc/lib/ld-linux-aarch64.so.1]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000001e8b4 0x000000000001e8b4 R E 0x10000
LOAD 0x000000000001fd30 0x000000000042fd30 0x000000000042fd30
0x0000000000000458 0x0000000000000470 RW 0x10000
DYNAMIC 0x000000000001fd90 0x000000000042fd90 0x000000000042fd90
0x0000000000000220 0x0000000000000220 RW 0x8
NOTE 0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
0x0000000000000020 0x0000000000000020 R 0x4
GNU_EH_FRAME 0x000000000001c58c 0x000000000041c58c 0x000000000041c58c
0x0000000000000734 0x0000000000000734 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x000000000001fd30 0x000000000042fd30 0x000000000042fd30
0x00000000000002d0 0x00000000000002d0 R 0x1
...
https://docs.kernel.org/userspace-api/ELF.html
PT_INTERP
First
PT_INTERP
program header is used to locate the filename of ELF interpreter. OtherPT_INTERP
headers are ignored (since Linux 2.4.11).
The value of PT_INTERP
usually points ld.so.1
corresponding to your platform.
You can change its value by passing this linker option -Wl,-dynamic-linker,custom_path_to_dynamic_linker
to GCC.
Build:
aarch64-none-linux-gnu-g++ -march=armv9.4-a -O0 -g3 -std=c++23 \
-o ./a.out \
test.cpp \
-Wl,-L${cross_gcc_sys_root}/lib/gcc/aarch64-none-linux-gnu/15.0.0 \
-Wl,-lbacktrace \
-Wl,-dynamic-linker,${target_runtime_sys_root}/libc/lib/ld-linux-aarch64.so.1
In a VM, with a recent GDB installed from the distribution's repo:
$ gdb ./a.out
# Set the environment variables
(gdb) python gdb.execute("set environment LD_LIBRARY_PATH=${target_runtime_sys_root}/lib64:${target_runtime_sys_root}/libc/lib64")
# Run the program
(gdb) run [arg1 arg2 ...]
# That's all! It should work now :)
Note: Please ignore all the VM and cross-compilation details if you do everything on the same host :)