'relocation error when implementing my own elf loader
I am currently implementing an elf loader to emulate binaries with the unicorn engine. To avoid implementing my own dynamic linker, I load the ld-linux-x86-64.so.2 (into the unicorn engine) and execute it with the path to my dynamic linked executable. Now here comes my problem:
I load the dynamic linker binary (/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2) into the memory and launch it with the path to my executable (basically printf("Hello World")). After the loader loads the executable and the libc the following gets printed to the stdout:
./executable: ./executable: no version information available (required by ./executable)
./executable: relocation error: ./executable: symbol __libc_start_main, version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference
I copied the binaries (ld and libc) from the system I compiled the hello_world program. It works when I execute the binary there:
$ /lib/x86_64-linux-gnu/ld-2.21.so ./executable
Hello World
Any idea why this should not work? Any idea where I should look for my error? (Note that I am using my custom loader)
Here is the 'strace' of the loader running in the unicorn-engine that is hopefully correctly executed (I need to simulate the kernel).
sys_brk(0x0)
sys_open(./executable)
sys_read(3, 0x7fff6c844868, 832)
sys_fstat(3, 0x7fff6c844718)
sys_getcwd(0x8888888800225738, 128)
sys_mmap2(0x400000, 4096, 5, 2066, 3, 0)
sys_mmap2(0x600000, 8192, 3, 2066, 3, 0)
sys_close(3)
sys_uname(0x7fff6c844a22)
sys_access(/etc/ld.so.nohwcap, 0)
sys_mmap2(0x0, 8192, 3, 34, -1, 0)
sys_access(/etc/ld.so.preload, 4)
sys_open(/etc/ld.so.cache)
sys_open(/lib/x86_64-linux-gnu/tls/x86_64/libc.so.6)
sys_stat(/lib/x86_64-linux-gnu/tls/x86_64, 140735013995640)
sys_open(/lib/x86_64-linux-gnu/tls/libc.so.6)
sys_stat(/lib/x86_64-linux-gnu/tls, 140735013995640)
sys_open(/lib/x86_64-linux-gnu/x86_64/libc.so.6)
sys_stat(/lib/x86_64-linux-gnu/x86_64, 140735013995640)
sys_open(/lib/x86_64-linux-gnu/libc.so.6)
sys_read(4, 0x7fff6c8445c8, 832)
sys_fstat(4, 0x7fff6c844478)
sys_close(4)
sys_writev(2, 7fff6c844538, 6)
=> ./executable: ./executable: no version information available (required by ./executable)
sys_mmap2(0x0, 4096, 3, 34, -1, 0)
sys_arch_prctl(4098, 0x6555555500001fc0)
sys_mprotect(0x600000, 4096, 1)
sys_writev(2, 7fff6c844220, 10)
=> ./executable: relocation error: ./executable: symbol __libc_start_main, version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference
sys_exit(127)
And the strace on the machine I compiled the binaries:
$ strace /lib/x86_64-linux-gnu/ld-2.21.so ./dynamic_x86_64_print_args
execve("/lib/x86_64-linux-gnu/ld-2.21.so", ["/lib/x86_64-linux-gnu/ld-2.21.so", "./dynamic_x86_64_print_args"], [/* 23 vars */]) = 0
brk(0) = 0x5636dd9f3000
open("./dynamic_x86_64_print_args", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\2\0>\0\1\0\0\0@\4@\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=8528, ...}) = 0
getcwd("/vagrant/tdb/static", 128) = 20
mmap(0x400000, 4096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0x400000
mmap(0x600000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0x600000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc3a45e1000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
// ...
// SNIP 100 tries to load the libc.so.6 that is not there
// ...
open("/home/vagrant/villoc/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/home/vagrant/villoc", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=55962, ...}) = 0
mmap(NULL, 55962, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fc3a45d3000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\v\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1869392, ...}) = 0
mmap(NULL, 3972864, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc3a4209000
mprotect(0x7fc3a43c9000, 2097152, PROT_NONE) = 0
mmap(0x7fc3a45c9000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7fc3a45c9000
mmap(0x7fc3a45cf000, 16128, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fc3a45cf000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc3a4208000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc3a4207000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc3a4206000
arch_prctl(ARCH_SET_FS, 0x7fc3a4207700) = 0
mprotect(0x7fc3a45c9000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x5636dba3b000, 4096, PROT_READ) = 0
munmap(0x7fc3a45d3000, 55962) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc3a45e0000
write(1, "arg 0 ./dynamic_x86_64_print_arg"..., 34arg 0 ./dynamic_x86_64_print_args
) = 34
exit_group(0) = ?
+++ exited with 0 +++
Solution 1:[1]
Looking at your syscalls...
sys_mmap2(0x400000, 4096, 5, 2066, 3, 0)
sys_mmap2(0x600000, 8192, 3, 2066, 3, 0)
You appear to have MAP_FIXED
bit set.
MAP_FIXED
is a dangerous tool - it will quite happily overwrite whatever else is at that address. Chances are, your loader is using those addresses. I know that /lib64/ld-linux-x86-64.so.2
uses MAP_FIXED
- but...
> readelf --segments /lib64/ld-linux-x86-64.so.2
Elf file type is DYN (Shared object file)
Entry point 0x1140
There are 7 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000021960 0x0000000000021960 R E 200000
LOAD 0x0000000000021b40 0x0000000000221b40 0x0000000000221b40
0x0000000000001438 0x0000000000001610 RW 200000
DYNAMIC 0x0000000000021e00 0x0000000000221e00 0x0000000000221e00
0x0000000000000190 0x0000000000000190 RW 8
NOTE 0x00000000000001c8 0x00000000000001c8 0x00000000000001c8
0x0000000000000024 0x0000000000000024 R 4
GNU_EH_FRAME 0x000000000001eda4 0x000000000001eda4 0x000000000001eda4
0x0000000000000674 0x0000000000000674 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x0000000000021b40 0x0000000000221b40 0x0000000000221b40
0x00000000000004c0 0x00000000000004c0 R 1
Section to Segment mapping:
Segment Sections...
00 .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_d .rela.dyn .rela.plt .plt .text .rodata .stapsdt.base .eh_frame_hdr .eh_frame
01 .data.rel.ro .dynamic .got .data .bss
02 .dynamic
03 .note.gnu.build-id
04 .eh_frame_hdr
05
06 .data.rel.ro .dynamic .got
...it doesn't appear to be using the addresses it is using MAP_FIXED
for. It's a clever beast.
> strace /lib64/ld-linux-x86-64.so.2 `which readelf` |& grep mmap
mmap(0x400000, 503808, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0x400000
mmap(0x67a000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x7a000) = 0x67a000
mmap(0x67d000, 10248, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x67d000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0af3a38000
mmap(NULL, 313165, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f0af39eb000
mmap(NULL, 2109744, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f0af3614000
mmap(0x7f0af3816000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f0af3816000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0af39ea000
mmap(NULL, 3985920, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f0af3246000
mmap(0x7f0af3609000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7f0af3609000
mmap(0x7f0af360f000, 16896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0af360f000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0af39e8000
mmap(NULL, 106172832, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f0aecd04000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0af3a37000
If I use MAP_FIXED
in a Bad Way™ on my own system, I get a somewhat similar message:
./myloader: symbol lookup error: ./myloader: undefined symbol:
But given we're just smashing memory up here, any number of things could happen.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |