Running gcc's steps manually, compiling, assembling, linking

If you have a simple C program, like

int main(void) {return 0;}

It can be compiled with gcc -o test test.c.

As I understand, gcc performs compiling, assembling then linking. The latter two steps are achieved by it running as and ld.

I can generate the assembly code by using gcc -S test.c.

What would you type into a terminal, to convert the assembly code into an executable?

(the reason for doing so is to learn assembly)


Solution 1:

These are the different stages using gcc

gcc -E  --> Preprocessor, but don't compile
gcc -S  --> Compile but don't assemble
gcc -c  --> Preprocess, compile, and assemble, but don't link
gcc with no switch will link your object files and generate the executable

Solution 2:

// main.c
#include <stdio.h>

int main(void)
{
        printf("Hello World !\n");
        return 0;
}

For preprocessing, compiling, assembling and then finally linking the simple aforementioned hello world program, follow the steps below:

Step 1/4) Preprocess main.c to generate main.i:

$: gcc -E main.c -o main.i

NOTE: You could call the C preprocessor directly as well:

$: cpp main.c -o main.i

Step 2/4) Compile main.i to generate main.s:

$: gcc -S main.i -o main.s

Step 3/4) Assemble main.s to generate main.o:

$: as main.s -o main.o

NOTE: You can combine the aforementioned steps 1, 2 and 3 by using the -c (small C) flag of gcc:

$: gcc -c main.s -o main.o // OR $: gcc -c main.c -o main.o

Step 4/4) Link main.o with other necessary object files namely, crti.o & crtn.o (they define function prologs & epilogs, respectively), crt1.o (contains _start symbol for bootstrapping the initial execution of the program), libc.so path or -lc flag for libc and then finally set the name of the dynamic linker, to generate a dynamically linked ELF executable:

On x86_64:

$: ld /usr/lib/x86_64-linux-gnu/crti.o /usr/lib/x86_64-linux-gnu/crtn.o /usr/lib/x86_64-linux-gnu/crt1.o -lc main.o -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o main_ELF_executable

OR (if you'd like to specify path to libc.so)

$: ld /usr/lib/x86_64-linux-gnu/crti.o /usr/lib/x86_64-linux-gnu/crtn.o /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/libc.so main.o -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o main_ELF_executable

On 32-bit ARM:

$: ld /usr/lib/arm-linux-gnueabihf/crti.o /usr/lib/arm-linux-gnueabihf/crtn.o /usr/lib/arm-linux-gnueabihf/crt1.o -lc main.o -dynamic-linker /lib/ld-linux.so.3 -o main_ELF_executable

OR (if you'd like to specify path to libc.so)

$: ld /usr/lib/arm-linux-gnueabihf/crti.o /usr/lib/arm-linux-gnueabihf/crtn.o /usr/lib/arm-linux-gnueabihf/crt1.o /usr/lib/arm-linux-gnueabihf/libc.so main.o -dynamic-linker /lib/ld-linux-armhf.so.3 -o main_ELF_executable

You can then run the ELF executable 'main_ELF_executable':

$: ./main_ELF_executable

Hello World !

Sources:

https://linux.die.net/man/1/gcc

https://linux.die.net/man/1/ld

https://dev.gentoo.org/~vapier/crt.txt