I have a working position independent Linux freestanding x86_64 hello world:
main.S
.text
.global _start
_start:
asm_main_after_prologue:
/* Write */
mov $1, %rax /* syscall number */
mov $1, %rdi /* stdout */
lea msg(%rip), %rsi /* buffer */
mov $len, %rdx /* len */
syscall
/* Exit */
mov $60, %rax /* syscall number */
mov $0, %rdi /* exit status */
syscall
msg:
.ascii "hello\n"
len = . - msg
which I can assemble and run with:
as -o main.o main.S
ld -o main.out main.o
./main.out
Since it is position independent due to the RIP relative load, now I wanted to link it as a PIE and see it get loaded at random addresses every time to have some fun.
First I tried:
ld -pie -o main.out main.o
but then running it fails with:
-bash: ./main.out: No such file or directory
and readelf -Wa
says that a weird interpreter /lib/ld64.so.1
was used instead of the regular one /lib64/ld-linux-x86-64.so.2
for some reason.
I then learnt that his is actually the recommended System V AMD64 ABI interpreter name at 5.2.1 "Program Interpreter".
In any case, I then try to force matters with:
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o main.out main.o
and now it works: I get hello
and the executable gets loaded to a different address every time according to GDB.
Finally, as a final step, I wanted to also make that executable be statically linked to make things even more minimal, and possibly get rid of the explicit -dynamic-linker
.
That's what I could not do, and this is why I'm asking here.
If I try either of:
ld -static -pie -o main.out main.o
ld -static -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o main.out main.o
-static
does not seem to make any difference: I still get dynamic executables.
After quickly glancing at the kernel 5.0 source code in fs/binfmt_elf.c I saw this interesting comment:
* There are effectively two types of ET_DYN
* binaries: programs (i.e. PIE: ET_DYN with INTERP)
* and loaders (ET_DYN without INTERP, since they
* _are_ the ELF interpreter). The loaders must
so I guess when I achieve what I want, I will have a valid interpreter, and I'm so going to use my own minimal hello world as the interpreter of another program.
One thing I might try later on is see how some libc implementation compiles its loader and copy it.
Related question: Compile position-independent executable with statically linked library on 64 bit machine but that mentions an external library, so hopefully this is more minimal and answerable.
Tested in Ubuntu 18.10.