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.
 
    