How to link a gas assembly program that uses the C

2019-01-31 14:08发布

问题:

As an exercise to learn more precisely how c programs work and what minimum level of content must exist for a program to be able to use libc, I've taken it upon myself to attempt to program primarily in x86 assembly using gas and ld.

As a fun little challenge, I've successfully assembled and linked several programs linked to different self-made dynamic libraries, but I have failed to be able to code a program from scratch to use libc function calls without directly using gcc.

I understand the calling conventions of individual c library functions, and have thoroughly inspected programs compiled out of gcc through use of objdump and readelf, but haven't gotten anywhere as far as what information to include in a gas assembly file and what parameters to invoke in ld to successfully link to libc. Anyone have any insight to this?

I'm running Linux, on an x86 machine.

回答1:

There are at least three things that you need to do to successfully use libc with dynamic linking:

  1. Link /usr/lib/crt1.o, which contains _start, which will be the entry point for the ELF binary;
  2. Link /usr/lib/crti.o (before libc) and /usr/lib/crtn.o (after), which provide some initialisation and finalisation code;
  3. 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!
$


回答2:

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?



回答3:

I think something like this should work:

  1. make a simple C program
  2. gcc -S file.c
  3. edit file.s
  4. gas file.s
  5. ld file.o -lc crt1.o -o myprog