linux下多个源文件的编译
(1)通过多个源文件编译,直接合成一个.o文件。
(2)通过创建静态链接库libmyhello.a,在调用函数的时候调用静态库。
(3)通过创建动态链接库libmyhello.so,在调用函数的时候调用动态库。
1.编译多个源文件
例子,在系统提示符下键入命令得到hello.o文件
#gcc -c hello.c -o hello.o
我们通常使用的gcc -c是将.c源文件编译成为一个可执行的二进制代码(-o选项其实是指定输出文件文件名,如果不加-c选项,gcc默认会编译链接生成可执行文件,文件的名称由-o选项指定),这包括调用作为gcc内的一部分真正的c编译器(ccl),以及调用gnu c编译器的输出中实际可执行代码的外部gnu汇编器(as)和连接器工具(ld)。
而gcc -c是使用GNU汇编器将源文件转化为目标代码之后就结束,在这种情况下,只调用了c编译器(ccl)和汇编器(as),而连接器(ld)并没有被执行,所以输出的目标文件不会包含作为Linux程序在被装载和执行时所必须的包含信息,但它可以在以后被连接到一个程序。
代码执行步骤
# gcc -c hello.c -o hello.o
# gcc -c main.c -o main.o
// 单个编译所有文件生成.o文件,然后链接为一个可执行文件
# gcc -o hello hello.o main.o
2.静态链接库
静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为myhello,则静态库文件名就是libmyhello.a。
创建静态库命令
# ar rcs libmyhello hello.o
静态库制作完成了,如何使用它内部的函数呢?只需要在使用到这些公有函数的源程序中包含这些公有函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从静态库中将公有函数连接到目标文件中。注意,gcc会在静态库名前加上前缀lib,然后追加扩展名.a得到的静态库文件名来查找静态库文件,因此,我们在写需要链接库时,只写名字就可以,如libmyhello.a,致谢-l myhello。
代码示例
# gcc -o hello main.c -static -L. -l myhello
通过测试可以发现,删除依赖的静态库程序依然可以运行,说明静态库的实现已被链接到可执行文件里了。
静态链接库的一个缺点是,如果我们同时运行了许多程序,并且他们使用了同一个库函数,这样,在内存中会大量拷贝同一函数。浪费存储空间,使用了共享链接库的linux就可以避免这个问题。
共享函数库和静态函数库很相似,只是后缀有所不同。比如,在一个典型的linux系统,标准的共享数序函数库是/usr/lib/libm.so。
当一个程序使用共享函数库时,在连接阶段并不把函数代码连接进来,而只是链接函数的一个引用。当最终的函数导入内存开始真正执行时,函数引用被解析,共享函数库的代码才真正导入到内存中。这样,共享链接库的函数就可以被许多程序同时共享,并且只需存储一次就可以了。共享函数库的另一个优点是,它可以独立更新,与调用它的函数毫不影响。
3.动态链接库(共享函数库)
编译生成动态链接库需要用到.o文件(注意编译生成.o文件的时候需要加入-fPIC选项,不然后面的指令会出错)
动态库文件名命名规范和静态库名命名规范类似,也是在动态库名增加前缀lib,但其文件扩展名为.so。例如:我们将创建的动态库名为myhell,则动态库文件名就是libmyhello.so。用gcc来创建动态库。
命令如下:
# gcc -shared -fPIC -o libmyhello.so hello.o
"PIC"命令行标记告诉gcc产尘的代码不要包含对函数和变量具体内存位置的引用,只是因为现在还无法知道使用该消息代码的应用程序会将它连接到哪一段内存地址空间。这样编译出来的hello.o可以被用于创建共享链接库。建立共享链接库只需要用gcc的"-shared"标记即可。
在程序中使用动态库和使用静态库完全一样,也是在使用到这些公有函数的源程序中包含这些公有函数的原型声明,然后在用gcc命令生成目标文件时指明动态库名进行编译。
执行命令如下:
gcc -o hello main.c -L. -l myhello
(使用"-l myhello"标记来告诉gcc驱动程序在连接阶段引用共享库函数libmyhello.so。"-L."标记告诉gcc函数库可能位于当前目录。否则,gnu连接器会查找标准系统函数目录:它先后搜索
1 .elf文件的DT_RPATH段
2 环境变量LID_LIBRARY_PATH
3 ./etc/ld.so.cache文件列表
4 ./lib,/usr/lib目录找到库文件后将其载入内存,但是我们生成的共享库在当前文件夹下并没有加到上述的4个路径的任何一个中,因此,执行后会出现错误)
# ./hello
./hello:error while loading shared
libraries:libmyhello.so:cannot open shared object file:No such
file or directory
#
错误提示,找不到动态库文件libmyhello.so。程序在运行时,会在/usr/lib和lib等目录中找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行。
解决方法:
(1)将文件libmyhello.so复制到目录/lib或者/usr/lib中。
(2)既然连接器会搜索LD_LIBRARY_PATH所指定的目录,那么我们可以将这个环境变量设置成当前目录:export LD_LIBRARY_PATH=$(pwd)。
(3)ldconfig /usr/zheng/lib 注:当用户在某个目录下面创建或拷贝了一个动态链接库,若想使其被系统共享,可以执行一下"ldconfig 目录名“这个命令。此命令的功能在于让ldconfig将指定目录下的动态链接库被系统共享起来,意即:在缓存文件 /etc/ld.so.cache中追加进指定目录下的共享库。本例让系统共享了/usr/zheng/lib目录下的动态链接库。该命令会重建/etc/ld.so.cache文件
编译动态库时遇到relocation R_x86_64_32 against a local symbol错误
编译代码
$ gcc -c hello.c
$ gcc -c main.c
$ gcc -shared -fPIC -o hello main.o hello.o
.. relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC
... could not read symbols: Bad value
解决办法编译器已经提示了:recompile with -fPIC
某些版本的gcc默认没加-fPIC参数
解决办法:保证你编译.o文件的时候,都加上-fPIC
$ gcc -fPIC -c hello.c
$ gcc -fPIC -c main.c
$ gcc -shared -fPIC -o hello mian.o hello.o
知识小结:
(1)ldd hello 可以看到执行时调用动态库的过程。
(2)编译参数解析
-shared:该选项指定生成动态链接库(让连接器生成T类型的导出符号表,有时候也可以生成 弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的,所以动态载入时通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
-L.:表示要连接的库在当前目录中
-l test:编译器查找动态链接库时由隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称。
LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。
调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录,通过"I"include进来了,库所在文件通过"-L"参数引导,并指定了”l”的库名,但通过ldd命令查看时,就是找不到指定链接的so文件,这时你要做的就是通过修改LD_LIBRARY_PATH或者/etc/ld.so.config文件来指定动态库的目录。
静态库链接是搜索路径顺序:
1.ld会去找gcc命令中的参数-L
2.再找gcc的环境变量LIBRARY_PAPTH
3.再找内定目录/lib /usr/lib/ /usr/local/lib这是当初compile gcc 时写在程序内的
动态链接时,执行时搜索路径顺序:
1.编译目标代码是指定的动态库搜索路径
2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径
3.配置文件/etc/ld.so.config中指定的动态库搜索路径
4.默认的动态库搜索路径/lib
5.默认的动态库搜索路径/usr/lib
有关环境变量:
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径
拓展知识
GNU
GNU不是一个公司名,而是一个软件项目名。它开发了许多应用程序。
GCC
GCC全称是 GNU C Compiler, 最早的时候就是一个c编译器。但是后来因为这个项目里边集成了更多其他不同语言的编译器,GCC就代表 the GNU Compiler Collection,所以表示一堆编译器的合集。
G++
G++则是GCC的c++编译器