C编译过程和动态库,静态库的生成

GCC编译过程:
1)预处理阶段

命令:gcc -E hello.c hello.i
生成.i文件
预处理器根据以字符#开头的命令修改原始的C程序,主要进行文本替换,宏展开,删除注释等简单操作

2)编译阶段

命令:gcc -S hello.c hello.s
生成.s文件
这个阶段编译器主要做词法分析,语法分析,语义分析,在检查无错误后,把代码翻译为汇编语言

3)汇编阶段

命令:gcc -c hello.c hello.o
生成.o文件
汇编器将汇编语言程序翻译为机器语言程序(二进制),编译成目标文件,做准备连接工作,将你使用的函数之类的,寻找它真正的存放地址

4)链接

命令:gcc -v hello.o -o hello
将程序所用到的C提供的库函数或者结构体从其他文件引入,链接器就是负责这样一个过程,最后得到一个可执行的目标文件

最终生成二进制可执行文件

链接的函数库一般分为静态库和动态库
linux系统有几个重要的目录存放系统的函数库,比如:/lib,/usr/lib
4.1)动态库
动态库在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销.
动态库的一般后缀为.so,gcc在编译时默认使用动态库.
4.2)静态库
静态库在编译链接的时候,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时就不需要库文件了.
静态库的一般后缀为.a.
4.3)静态库的制作
A:准备相应的操作的.c文件,然后将.c文件转换为汇编文件.o文件

命令:gcc -c test.c
生成test.o文件了

B:执行ar -cr libxxxx.a *.o命令

ar命令"
可以用来创建,删除库,也可以从苦衷提取出单个模块,ar是archiver的缩写
-r:将文件插入库文件中,如果已经存在则直接替换
-t:显示库文件中所包含的.o文件
-C:表示create,创建
xxx:生生成库的名称

命令:ar -cr libtest.a test.o
生成静态库libtest.a
到这一步静态库制作完成
main.c文件中调用了test.c中定义的函数,所以main.c文件需要链接libtest.a(静态库)来生成可执行文件

C:在编译main.c程序文件时,链接静态库

命令:gcc -o main main.c -ltest(只需要加上库文件名即可,lib和.a不需要)
gcc只会到系统默认的文件目录处搜索链接库,所以我们要将自定义的库文件拷贝到/lib目录下去
命令:cp libtest.a /lib/
将自定义的库文件拷贝到系统默认的库文件目录后,又带来一个问题---我们污染了系统库,所以我们一般在命令中指定自定义库文件的路径即可
命令:gcc main.c -L. -ltest (.代表当前目录,-L要放在-l命令选项之前)
----------------------
-l:告知gcc用户自定义头文件的存放路径,如果把制作的库文件放在系统库文件夹下,就不需要该选项
-L:告知gcc链接库的存放路径

4.4)动态库的建立
A:准备相应操作的.c文件,然后将.c文件转换至汇编文件.0文件

命令:gcc -fPIC test.c -c
-fPIC:产生位置无关代码
PIC告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行,这正是共享库所要求的,共享库被加载时,在内存的位置是不固定的

B:创建动态库

命令:gcc -shared -o libxxxx.so test.o
-shared:表示动态库(共享库)
xxxx:表示动态库的名字

可能出现的错误:
/usr/bin/ld: test.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: 最后的链结失败: 输出不可表示的节
collect2: 错误:ld 返回 1
解决方案:
gcc -fPIC -c hello.c -o hello.o
gcc -share hello.o -o libmyhello.so

第一步和第二步可以合成一步:
命令:gcc -fPIC -shared -o libxxxx.so test.c

C:编译main.c文件

命令:gcc -o main main.c -L. -lxxxx

D:执行./main
出错:(找不到运行时依赖的动态库)
请添加图片描述
需要注意的是:此时我们还不能直接执行此程序,我们需要在执行一步:
“export LD_LIBRARY_PATH=”.“”

export LD_LIBRARY_PATH="."

LD_LIBRARY_PATH是一个环境变量,告诉链接器 库文件的搜索路径,这里将库的搜索路径设置成当前目录
gcc默认只会到/lib,/usr/lib等目录去搜索库,除非我们把自己制作的库放到这些目录下,否则我们需要设置LD_LIBRARY_PATH环境变量,注意LD_LIBRARY_PATH指定的搜索路径优先于/lib等目录,库的搜索先后路径是很重要的,因为这决定了gcc最终链接到哪一个库.
完成此步,就可以执行了
段错误(吐核)
请添加图片描述

5)判断程序是否链接了动态库

file:用来判断文件的类型
ldd:看动态库,如果目标程序没有链接动态库,则打印"not a dynamic executable"(不是动态可执行文件)

6)-static选项

当同一个目录下既有动态库又有静态库,并且两个库的名字相同时(libtest.a/libtest.so),gcc编译时会默认选择动态库链接,如果要指定优先链接静态库,需要指定-static选项
命令:gcc main.c -L. -static -ltest

7)静态动态混合链接

默认情况下,GCC/G++链接时优先链接动态库,如果没有动态库,则链接相应的静态库.同时,GCC/G++也提供了链接选项,供用户指定静态库和动态库链接
-WI,-Bstatic:指示跟在后面的-lxxxx选项都是静态链接库
-WI,-Bdynamic:指示跟在后面的-lxxx选项都是动态链接库

8)静态链接库和动态链接库的对比
首先,静态库的嵌入造成了系统空间的浪费,并且如果多个程序链接了同一个静态库文件,那么每一个生成的可执行文件就都会有一个这个静态库的副本,浪费显而易见
其次,一旦发现静态库中有bug,必须把链接了该静态库的程序一一找出,注意修改,重新编译
动态库的出现弥补了以上的弊端,因为动态库是在程序运行时被链接的,所以磁盘上只需要保留一份副本即可,大大节省了磁盘空间.如果动态库中出现bug,只需要修改动态库即可.
但是,静态库也有优点:编译后的执行程序不需要外部的函数库的支持,即使删除了静态库的源文件程序依旧可以正常执行
请添加图片描述
参考文章:linux下静态库和动态库

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值