-ENOENT, but believe me, it's there

Almost every ELF-file in a Linux environment is dynamically linked, and the operating system has to locate all dynamic libraries in order to execute the file. To its help, it has the runtime dynamic linker, whose only job is to interpret the ELF file format, load the shared objects with unresolved references, and, at last, execute and pass over the control to the ELF file.

The expected path to the dynamic linker is hardcoded into the ELF itself, so if the linker is missing, it leads to an obvious but maybe confusing error.

If I want to run an executable that has its path to a dynamic linker that simple does not exists, we got this:

root@cgtqmx6:/# ./01_SimpleTriangle -sh: ./01_SimpleTriangle: No such file or directory

Hue? But the file does exist! OK, lets find out which system calls is made.:

root@cgtqmx6:/# strace ./01_SimpleTriangle

execve("./01_SimpleTriangle", ["./01_SimpleTriangle"], [/* 15 vars */]) = -1 ENOENT (No such file or directory)
write(2, "strace: exec: No such file or directory) = 40
exit_group(1) = ?+++ exited with 1 +++

Not much valuable information. Strace calls execve that says the file does not exist. We are talking about the file... but which file is it?

As told before, the runtime dynamic linker is always executed first to load libraries for unresolved symbols. So one qualified guess is that it's the dynamic linker that is missing.

Readelf is a handy tool to read out information from an ELF file such as sections, which architecture it's compiled for and so on. It also shows us the expected path to the dynamic linker.:

root@cgtqmx6:/# readelf -l 01_SimpleTriangle

Elf file type is EXEC (Executable file)
Entry point 0x9288There are 8 program headers, starting at offset 52
Program Headers:  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align  EXIDX          0x0022ec 0x0000a2ec 0x0000a2ec 0x00088 0x00088 R   0x4  PHDR           0x000034 0x00008034 0x00008034 0x00100 0x00100 R E 0x4  INTERP         0x000134 0x00008134 0x00008134 0x00013 0x00013 R   0x1    >>>>>>

[Requesting program interpreter: /lib/ld-linux.so.3] <<<<<<<  LOAD           0x000000 0x00008000 0x00008000 0x02378 0x02378 R E 0x8000         0x002378 0x00012378 0x00012378 0x002a0 0x00358 RW  0x8000  DYNAMIC        0x002384 0x00012384 0x00012384 0x00118 0x00118 RW  0x4  NOTE           0x000148 0x00008148 0x00008148 0x00020 0x00020 R   0x4  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4

 Section to Segment mapping:  Segment Sections...   00     .ARM.exidx    01        02     .interp    03     .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .ARM.extab .ARM.exidx .eh_frame    04     .init_array .fini_array .jcr .dynamic .got .data .bss    05     .dynamic    06     .note.ABI-tag    07

Here we go! The expected path is /lib/ld-linux.so.3. Our system is currently using /lib/ld-linux-armhf.so.3, so lets create a symbolic link for now.:

ln -s /lib/ld-linux-armhf.so.3 /lib/ld-linux.so.3

And finally, the program is now executing!:

root@cgtqmx6:/# ./01_SimpleTriangle root@cgtqmx6:/#

Anyway, who decide which runtime dynamic linker to use? It's decided in the linker-stage, usually by ld or gcc and may vary between different toolchains,