'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