How to link a gas assembly program that uses the C standard library with ld without using gcc?
There are at least three things that you need to do to successfully use libc with dynamic linking:
- Link
/usr/lib/crt1.o
, which contains_start
, which will be the entry point for the ELF binary; - Link
/usr/lib/crti.o
(before libc) and/usr/lib/crtn.o
(after), which provide some initialisation and finalisation code; - Tell the linker that the binary will use the dynamic linker,
/lib/ld-linux.so
.
For example:
$ cat hello.s
.text
.globl main
main:
push %ebp
mov %esp, %ebp
pushl $hw_str
call puts
add $4, %esp
xor %eax, %eax
leave
ret
.data
hw_str:
.asciz "Hello world!"
$ as -o hello.o hello.s
$ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -lc hello.o /usr/lib/crtn.o
$ ./hello
Hello world!
$
If you define main
in assembly
Matthew's answer does a great job of telling you the minimum requirements.
Let me show you how how to find those paths in your system. Run:
gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'
and then pick up the files Matthew mentioned.
gcc -v
gives you the exact linker command GCC uses.
collect2 is the internal executable GCC uses as a linker front-end, which has a similar interface to ld
.
In Ubuntu 14.04 64-bit (GCC 4.8), I ended up with:
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
/usr/lib/x86_64-linux-gnu/crt1.o \
/usr/lib/x86_64-linux-gnu/crti.o \
-lc hello_world.o \
/usr/lib/x86_64-linux-gnu/crtn.o
You might also need -lgcc
and -lgcc_s
. See also: Do I really need libgcc?
If you define _start
in assembly
If I defined the _start
, the hello world from glibc worked with just:
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc hello_world.o
I'm not sure if this is robust, i.e. if the crt
initializations can be safely skipped to invoke glibc functions. See also: Why does an assembly program only work when linked with crt1.o crti.o and crtn.o?