gcc编译c/c++程序为可执行文件的过程实际包括以下几步,假人我们要编译的文件为t.c:
1 预编译:
由预处理器cpp执行, 可以通过命令cpp -o t.i t.c 进行。 gcc -E t.c -o t.i
这一步预处理器把所有include的文件包括递归包含的文件的内容都展开到输出文件中,并且展开了所有的宏定义。
2 编译:
由编译器cc执行:cc1 t.i -o t.s gcc -S t.i -o t.s
编译的过程将预处理完的文件进行一系列的词法分析,语法分析,语义分析及优化后生成相应的汇编代码。在这一步中一般会进行优化,比如去除没有用到的类的声明和函数的声明,循环语句的优化等。
3 汇编:
as -o t.o t.s gcc -c t.s -o t.o
as汇编器会把汇编代码转换成机器指令,并以特定二进制格式输出保存在目标文件中
4 链接:
ld t.o -o t
ld链接器把程序的相关目标文件组合链接在一起,生成程序的可执行映像文件。这一步要解决的问题是:可能调用到了库函数,或者一个目标文件中调用了另外一个目标文件中的库函数,需要通过链接器建立对应的关系,使得程序能够正常的执行。
这里就有动态链接和静态链接两种方式:
静态链接: 将源代码从静态链接库中拷贝到最终的可执行程序中。这样可能会导致最终的目标文件很大。
动态链接: 需要调用的库函数以动态链接库的形式存在,多个进程之前共享。而链接的时候只需要知道要调用的函数的位置即可。在程序执行时,当需要调用某个动态链接库中的函数时,操作系统首先会查找所有正在运行的程序,看内存中是否已经有该库函数的拷贝了。如果有,则多进程之间可以共享该拷贝。否则才会将其载入到该进程的虚拟内存中。
linux下动态链接库的编译:
如果我们要将几个文件编译为动态链接库,gcc需要加的参数包括:
-shared 告诉编译器要建立动态链接库 (链接阶段命令)
-fPIC 编译时使用相对地址而不是绝对地址,这样在使用动态链接库时可以进行共享访问而不是拷贝。(编译阶段命令)
例如:
gcc t.c -fPIC -shared -o libt.so
linux动态链接库命名为xx.so
如果我们在文件main.c中调用了t.c中的函数,只需包含其头文件,指定动态链接库的位置,使得编译器可以找到动态链接库。指定的方法如下:
-L.:表示要连接的库在当前目录中
-lt:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称
LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。
所以这里的编译命令为:
gcc -L -lt main.c
静态链接库的编译:
通过ar命令进行,将.o文件生成为.a文件,为静态链接库文件。同样,使用动态链接库的时候需要加-llibname的选项,才会在该静态链接库中寻找函数。
ldd file_name可以看到程序用到的库。
下面是转载的一些方法:
一 例子详解
文件目录树如下:
1. libtest/
2. |-- myjob.c
3. |-- myjob.h
4. |-- test.c
静态库
A.做成静态库 libmyjob.a
1. $ gcc -c myjob.c -o myjob.o
2. $ ar -c -r -s libmyjob.a myjob.o
B.链接
1. $ gcc test.o libmyjob.a -o test
C.引用库情况(无所要信息)
1. $ ldd test
2. linux-gate.so.1 => (0xffffe000)
3. libc.so.6 => /lib/libc.so.6 (0xb7e29000)
4. /lib/ld-linux.so.2 (0xb7f6e000)
动态库
A.做成动态库 libmyjob.so
1. $ gcc -Wall –fPIC -c myjob.c -o myjob.o
2. $ gcc -shared -o libmyjob.so myjob.o
-shared: 该选项指定生成动态连接库(让连接器生成T 类型的导出符号表,有时候也生成弱连接W 类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件。
-fPIC: 表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
-L.: 表示要连接的库在当前目录中。
LD_LIBRARY_PATH: 这个环境变量指示动态连接器可以装载动态库的路径。
B.链接
链接方法I ,拷贝到系统库里再链接,让 gcc 自己查找:
1. $ cp libmyjob.so /usr/lib
2. $ gcc -o test test.o -lmyjob
这里我们可以看到了 -lmyjob 选项, -l[lib_name] 指定库名,他会主动搜索。 lib[lib_name].so 这个搜索的路径可以通过 gcc --print-search-dirs 来查找。
链接方法II ,手动指定库路径
1. $ gcc -o test test.o -lmyjob -B /path/to/lib
-B 选项就添加 /path/to/lib 到 gcc 搜索的路径之中。这样链接没有问题但是方法 II 中手动链接好的程序在 执行 时候仍旧需要指定库路径( 链接和执行是分开的 )。需要添加系统变量 LD_LIBRARY_PATH :
1. $ export LD_LIBRARY_PATH=/path/to/lib
这个时候再来检测一下test 程序的库链接状况 ( 方法 I 情况 )
1. $ ldd test
2. linux-gate.so.1 => (0xffffe000)
3. libmyjob.so => /usr/lib/ libmyjob .so (0xb7f58000)
4. libc.so.6 => /lib/libc.so.6 (0xb7e28000)
5. /lib/ld-linux.so.2 (0xb7f6f000)
是不是比静态链接的程序多了一个 libmyjob.so? 这就是静态与动态的最大区别,静态情况下,它把库直接加载到程序里,而在动态链接的时候,它只是保留接口,将动态库与程序代码独立。这样就可以提高代码的可复用度,和降低程序的耦合度。