Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a statically linked position independent executable ELF in Linux?

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.


People also ask

What is a static linked program in Linux?

By statically linked I mean a program that does not require any shared objects to run, even the ubiquitous libc. In reality, most programs one encounters on Linux aren't statically linked, and do require one or more shared objects to run.

How does a kernel execute an ELF program?

The kernel then goes on mapping the program's segments into memory, according to the information contained in the ELF program headers. Finally, it passes the execution, by directly modifying the IP register, to the entry address read from the ELF header of the program ( e_entry ).

What are position Independent Executables (PIE)?

I recently learned that (at least on Fedora and Red Hat Enterprise Linux), executable programs that are compiled as Position Independent Executables (PIE) receive stronger address space randomization (ASLR) protection. So: How do I test whether a particular executable was compiled as a Position Independent Executable, on Linux?

Is it possible to compile position independent executable on 64 bit machine?

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. Show activity on this post.


Video Answer


1 Answers

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.

like image 88
Employed Russian Avatar answered Oct 18 '22 08:10

Employed Russian