5

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.

  • 1
    Related: [What's the difference between "statically linked" and "not a dynamic executable" from Linux ldd?](https://stackoverflow.com/q/61553723) re: static-PIE vs. a regular PIE that uses an ELF interpreter but has no shared libs. – Peter Cordes Jun 11 '20 at 20:26

1 Answers1

6

You want to add --no-dynamic-linker to your link command:

$ ld main.o -o main.out -pie --no-dynamic-linker

$ file main.out
main.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, not stripped

$ ./main.out
hello

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.

I am not sure I understood what you are saying correctly. If you meant that main.out would have itself as its interpreter, that's wrong.

P.S. GLIBC-2.27 added support for -static-pie, so you no longer have to resort to assembly to get a statically linked PIE binary. But you'll have to use very recent GCC and GLIBC.

Employed Russian
  • 182,696
  • 29
  • 267
  • 329
  • Awesome, thanks! I wonder why `file` says it is dynamically linked, it seems to check for `PT_DYNAMIC` instead of `PT_INTERP` https://stackoverflow.com/questions/8040631/checking-if-a-binary-compiled-with-static/55664341#55664341 By "use my own minimal hello world as the interpreter of another program" I meant something like take a normal C hello world and compile it pointing to my own `gcc -Wl,--dynamic-linker=` to see what args Linux kernel passes to linker. It worked when I use the asm hello world, but segfaulted when I tried `-static-pie` on a C hello world (Ubuntu 18.10 has it). – Ciro Santilli Путлер Капут 六四事 Apr 13 '19 at 20:32
  • 1
    @CiroSantilli新疆改造中心996ICU六四事件"to see what args ..." the answer is: none (see https://stackoverflow.com/a/55564759/50617). I also reproduced the crash when `-static-pie` `ld.so` is used as an interpreter, but have not understood why it's crashing. Worth a separate question. – Employed Russian Apr 14 '19 at 00:23
  • 1
    @CiroSantilli新疆改造中心996ICU六四事件 Ok, I figured out the crash. It's complicated :-( – Employed Russian Apr 14 '19 at 19:51
  • Cool! I was about to ask the question but got distracted with other stuff. Is it a bug or just behavior? Should I ask a question so you can share findings? – Ciro Santilli Путлер Капут 六四事 Apr 14 '19 at 19:54
  • 1
    @CiroSantilli新疆改造中心996ICU六四事件 https://stackoverflow.com/questions/55679589/why-does-a-standalone-hello-program-crash-when-used-as-a-dynamic-linker – Employed Russian Apr 14 '19 at 20:11