Sometime back I got hold of a third-party library which had a file libsample.so (the shared library) and another named sample.h, the header containing the prototype declaration of the functions contained in the library. I put them both in my home directory /home/subhajit and started writing a program sampletest.c in my home directory to test it.
Once done, I start to put the code together. The first step would be to preprocess the code to expand all the header files and the preprocessor directives.
Whoa! I could very well see the file sample.h in the directory listing. Well, then gcc was never told to look for a header file in the current directory! We need to tell it now, and we do it like this
Now that the preprocessing succeeded, we can have a look into the output file sampletest.i.
Ok, so we can see the files stdio.h, stdlib.h and sample.h (and all the files they include) have been expanded (some of the prototype declarations are shown in gray). Also, the comment in our program has been removed.
Next, we need to compile the program to convert it from C to machine language, which in my case would be an x86 assembly program. we can do it like this:
Not again! Now, all these jargons of syntax and semantics ... So, the preprocessor can't detect this,rite? Alrite, we got the errors ... the missing semicolon at line 7 and the extra '+' in line number 9. Corrected them, preprocessed and compiled again, all to have a sampletest.s in my home directory. WHOOO!!!
With that, I was ready for assembling. So I ran the assembler, as.
Just what I was waiting for! Another error! And this time its the assembler. Which waits to catch errors in inline assembly codes or any inappropriate changes made to the generated .s file. In this case, the error is just that we can not move the value of eax into an immediate operand, while the reverse is true. So we change "mov %eax, $1\n\t" to "mov $1, eax\n\t" in line 14 of sampletest.c and obtain the binary file in machine code with all the symbol tables and relocation directives. This is what we usually do by
Well, well! All that is left is linking the object file to the shared libraries and get the binary executable. This time I will be extra cautious and will run ld, the linker.
so the linker command is quite long and is given below
1: #include <stdio.h> 2: #include <stdlib.h> 3: #include <sample.h> 4: #include <math.h> 5: 6: int main(){ 7: int var1=2 8: int var2=4; 9: int var3=var2++var3; 10: double var4; 11: 12:
/*this inline assembly is of no use.*/
13:
/*just added to have some flavour*/
14: __asm__("push %eax\n\t" "mov %eax,$1\n\t" "pop %eax"); 15: 16: var4=pow(add(2,6),0.25); 17: printf("%lf\n",var4); 18: }
Once done, I start to put the code together. The first step would be to preprocess the code to expand all the header files and the preprocessor directives.
Whoa! I could very well see the file sample.h in the directory listing. Well, then gcc was never told to look for a header file in the current directory! We need to tell it now, and we do it like this
Now that the preprocessing succeeded, we can have a look into the output file sampletest.i.
1: # 1 "sampletest.c"
2: # 1 "<built-in>"
3: # 1 "<command-line>"
4: # 1 "sampletest.c"
5: # 1 "/usr/include/stdio.h" 1 3 4
6:
7: ... Many Lines Removed ...
8:
9:
10: typedef _G_fpos_t fpos_t;
11:
12: ... Some Lines Removed ...
13:
14: extern struct _IO_FILE *stdin;
15: extern struct _IO_FILE *stdout;
16: extern struct _IO_FILE *stderr;
17:
18: ... Some Lines Removed ...
19:
20: extern int fprintf (FILE *__restrict __stream,
21: __const char *__restrict __format, ...);
22:
23: extern int printf (__const char *__restrict __format, ...);
24:
25: ... Many Lines Removed ...
26:
27: # 1 "/home/subhajit/sample.h" 1
28:
29: extern int add(int,int);
30: # 4 "sampletest.c" 2
31:
32: int main(){
33: int var1=2
34: int var2=4;
35: int var3=var2++var3;
36: double var4;
37:
38:
39:
40: __asm__("push eax\n\t" "mov %eax,$1\n\t" "pop %eax");
41:
42: var4=pow(add(2,6),0.25);
43: printf("%lf\n",var4);
44: }
Ok, so we can see the files stdio.h, stdlib.h and sample.h (and all the files they include) have been expanded (some of the prototype declarations are shown in gray). Also, the comment in our program has been removed.
Next, we need to compile the program to convert it from C to machine language, which in my case would be an x86 assembly program. we can do it like this:
Not again! Now, all these jargons of syntax and semantics ... So, the preprocessor can't detect this,rite? Alrite, we got the errors ... the missing semicolon at line 7 and the extra '+' in line number 9. Corrected them, preprocessed and compiled again, all to have a sampletest.s in my home directory. WHOOO!!!
With that, I was ready for assembling. So I ran the assembler, as.
Just what I was waiting for! Another error! And this time its the assembler. Which waits to catch errors in inline assembly codes or any inappropriate changes made to the generated .s file. In this case, the error is just that we can not move the value of eax into an immediate operand, while the reverse is true. So we change "mov %eax, $1\n\t" to "mov $1, eax\n\t" in line 14 of sampletest.c and obtain the binary file in machine code with all the symbol tables and relocation directives. This is what we usually do by
gcc -c -o sampletest.o sampletest.c
Well, well! All that is left is linking the object file to the shared libraries and get the binary executable. This time I will be extra cautious and will run ld, the linker.
so the linker command is quite long and is given below
ld -dynamic-linker /lib/ld-linux.so.2 -o sampletest sampletest.o /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o `gcc -print-file-name=`/crtbegin.o `gcc -print-file-name=`/crtend.o -L/home/subhajit -lc -lm -lsample -rpath=/home/subhajitSee how the linker options have been written in separate lines using '\'. Lets take the options one by one.
- -dynamic-linker /lib/ld-linux.so.2 is required to be linked to the executable, because this .so file loads the program into memory and takes care of the relocation of the sections when the program is executed.
- -o sampletest is the executable name to be created and sampletest.o is the source object file
- /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o are the C runtime object files which come with the routines _start, _init and _fini (actually a lot more functions, just do a objdump --disassemble /usr/lib/crt1.o to peek into its internals). These functions are the one which call main() and when main() returns, call _exit() to return control back to kernel.
- `gcc -print-file-name=`/crtbegin.o `gcc -print-file-name=`/crtend.o are required to link the program against gcc object files. `gcc -print-file-name` gives the absolute path to these objects (in my case it was /usr/lib/gcc/i486-linux-gnu/4.4.3/).
- -L/home/subhajit tells the linker to look for the libraries in /home/subhajit in addition to the default directory for libraries; and -lc -lm -lsample tell that the libc.a, libm.so (for pow() function) and libsample.so (for add() function) are the libraries to be linked.
- Finally, when the program will be executed, it will require to load libsample.so into memory which is not present in the standard library path (which may be /usr/lib or /lib). So, -rpath=/home/subhajit tells that the program can load libraries from /home/subhajit at runtime.
This hectic task of linking the sampletest.o to sampletest (with execute permission, verify that with ls -l sampletest) is generally done by
gcc sampletest.o -L/home/subhajit -lsample -lm -Wl,-rpath,/home/subhajit
So, all we can say after this, is that gcc takes care of all these task by preprocessing, compiling, assembling by internally calling as and linking through ld, and thereby saves the day for us. and all we care to do is:
No comments:
Post a Comment