Pages

Wednesday, August 12, 2015

Let's run a c program by hand

Let’s compile a C program by hand. The aim is to compile the a.c program.

$cat a.h
#define AA 100
$cat a.c
#include “a.h”
main()
{
            printf(“%d apples”, AA);
            exit(19);
}
$

So, this is the program we really wanted to compile.
$gcc a.c –o a.exe
$./a.exe
100 apples$
$

To know what’s happening behind the scene, do this:
[kongkon@cadbury ~]$ gcc -v a.c
Reading specs from /usr/lib/gcc/i386-redhat-linux/3.4.3/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-java-awt=gtk --host=i386-redhat-linux
Thread model: posix
gcc version 3.4.3 20050227 (Red Hat 3.4.3-22.1)
 /usr/libexec/gcc/i386-redhat-linux/3.4.3/cc1 -quiet -v a.c -quiet -dumpbase a.c  -auxbase a -version -o /tmp/ccfWiYm9.s
ignoring nonexistent directory "/usr/lib/gcc/i386-redhat-linux/3.4.3/../../../../i386-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /usr/lib/gcc/i386-redhat-linux/3.4.3/include
 /usr/include
End of search list.
GNU C version 3.4.3 20050227 (Red Hat 3.4.3-22.1) (i386-redhat-linux)
compiled by GNU C version 3.4.3 20050227 (Red Hat 3.4.3-22.1).
GGC heuristics: --param ggc-min-expand=98 --param ggc-min-heapsize=128313
 as -V -Qy -o /tmp/ccyJwUTb.o /tmp/ccfWiYm9.s
GNU assembler version 2.15.92.0.2 (i386-redhat-linux) using BFD version 2.15.92.0.2 20040927
 /usr/libexec/gcc/i386-redhat-linux/3.4.3/collect2 --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 /usr/lib/gcc/i386-redhat-linux/3.4.3/../../../crt1.o /usr/lib/gcc/i386-redhat-linux/3.4.3/../../../crti.o /usr/lib/gcc/i386-redhat-linux/3.4.3/crtbegin.o -L/usr/lib/gcc/i386-redhat-linux/3.4.3 -L/usr/lib/gcc                                                                /i386-redhat-linux/3.4.3 -L/usr/lib/gcc/i386-redhat-linux/3.4.3/../../.. /tmp/cc yJwUTb.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s                                                                              --no-as-needed /usr/lib/gcc/i386-redhat-linux/3.4.3/crtend.o /usr/lib/gcc/i386-redhat-linux/3.4.3/../../../crtn.o
[kongkon@cadbury ~]$
Understood? It’s really hard to understand. Let me explain a little, and then things will look simpler. The –v switch to gcc explains what gcc has done with the compilation process. Now, first of all what is gcc? gcc is the compiler like everyone would say. Yes, it is the compiler. Actually it is a wrapper, which calls different programs to compile the C program: if you look at the –v output more closely you will see that, gcc calls "cpp", the C preprocessor first, then "cc1", the C compiler, and then "as", the assembler. As an output we get the object file, the same what we get by running “gcc –c a.c”. Here we take help of the linker, "ld", to link a.o with our library for printf, since we have not written code of printf() of our own. We need to link our a.o with libc.so for printf code, as specified by -lc. This outputs a.exe, out executable. Run this, and enjoy 100 apples.

Let’s do every thing by hand. Let’s call all of them one by one. First the C preprocessor, cpp.
[kongkon@cadbury ~]$ cpp a.c
# 1 "a.c"
# 1 ""
# 1 ""
# 1 "a.c"
# 1 "a.h" 1
# 2 "a.c" 2
main()
{
 printf("%d apples",100);
exit(19);
}
[kongkon@cadbury ~]$
cpp by default dumps the output in the console, redirect it to a file, call it a.i.
[kongkon@cadbury ~]$ cpp a.c -o a.i
[kongkon@cadbury ~]$
[kongkon@cadbury ~]$ ls a.*
a.c  a.h  a.i
[kongkon@cadbury ~]$

Next is the C compiler, cc1.
[kongkon@cadbury ~]$ cc1 a.i
-bash: cc1: command not found
[kongkon@cadbury ~]$
Oops! Bash did not find  cc1, he does not know where is cc1. Take the help of gcc itself to find where cc1 is. Do this:
[kongkon@cadbury ~]$ gcc -print-prog-name=cc1
/usr/libexec/gcc/i386-redhat-linux/3.4.3/cc1
[kongkon@cadbury ~]$
Next, compile a.i, not a.c.
[kongkon@cadbury ~]$ /usr/libexec/gcc/i386-redhat-linux/3.4.3/cc1 a.i
 main

Execution times (seconds)
 parser                :   0.01 (100%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall
 TOTAL                 :   0.01             0.00             0.01
[kongkon@cadbury ~]$
[kongkon@cadbury ~]$ ls a.*
a.c  a.h  a.i  a.s
This produces a.s.
Now, call the assembler, to create the object file, a.o.
[kongkon@cadbury ~]$ as a.s -o a.o
So, we have the object file here, a.o.
[kongkon@cadbury ~]$ ls a.*
a.c  a.h  a.i  a.o  a.s
[kongkon@cadbury ~]$
Now, call the linker program, ld.
[kongkon@cadbury ~]$ ld -o a.exe a.o -e main -lc -dynamic-linker /lib/ld-linux.so.2
[kongkon@cadbury ~]$ ls a.*
a.c  a.exe  a.h  a.i  a.o  a.s
[kongkon@cadbury ~]$
So, here we have the executable binary a.exe.
Run this and you will see the same output as we did in our first step.
[kongkon@cadbury ~]$ ./a.exe
100 apples[kongkon@cadbury ~]$
 Check whether the program exited successfully.
kongkon@cadbury ~]$ echo $?
19 
[kongkon@cadbury ~]$


So, we have successfully compiler a C program. Happy programming.

The thread ends here.