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