g++ 链接、装载与库
前言
从代码到运行需要经历:编译、链接、装载、运行几大步骤。从编译后的.o文件到运行时的二进制文件很像,都接近于机器语言。他们在内存里可称为image。
链接:符号(如函数签名)和地址做一些调整
编译
include需要直接依赖的文件和间接依赖
链接
链接:组装相互引用的模块。 方式是:调整地址
具体包括:地址和空间分配(Address and Storage Allocation),符号决议(Symbol Resolution)和重定位(Relocation).
目标文件我们也称作模块。
比如main.c使用了fun.c中的foo()
静态链接
是archieve,实际就是把.o文件打个包。思想是把写好的代码拿来复用。静态链接时需要当前依赖的库是完备的。
动态链接
静态链接:浪费内存、更新困难
动态链接:运行时才链接、升级时更新某个so即可,不用全部重新链接一遍。
是shared,实际上是可执行程序。思想是调用对方的程序来执行一遍。需要当前依赖的库是完备的,否则装载不成功。所以动态链接库是一些函数的组成。
过程如下
1. 加载:加载访问了动态链接库的进程
2. 分配:系统为进程分配 4GB 的私有地址空间
3. 解析:系统就会分析这个可执行模块,找到调用的 DLL ,搜索这些 DLL
4. 加载分配:找到这些 DLL 后便将这些 DLL 加载到内存中,并为它们分配虚拟的内存空间,
5. 映射:最后将 DLL 的页面映射到调用进程的地址空间中,
6. 共享:DLL 的虚拟内存有代码页和数据页,它们被分别映射到 进程 A 的代码页面和数据页面,如果这时进程B 也启动了,并且 进程 B 也需要访问该 DLL ,这时,只需要将该 DLL 在虚拟内存中的代码页面和数据页面映射到第二个进程的地址空间即可。这也表明了在内存中,只需要存在一份 DLL 的代码和数据,多个进程共享 DLL 的同一份代码,很明显这样做可以节省内存空间的。
7. 隔离:应用程序之间还是不能够相互影响的,也就是说多个应用程序虽然是可以共享同一个 DLL 中的相同的代码的,但是 DLL 为每一个进程保存的数据都是不相同的,并且每一个进程都为 DLL 使用的全部数据分配了自己的地址空间。
参数
-o 输出
-g Produce debugging information in the operating system’s native format
-I 头文件 编译时用
-L 库搜索路径
-l
技术准备
查看依赖
readelf -d PyGalaxy.so
ldd PyGalaxy.so
load 动态库过程
基本的说就是符号重定位,然后合并到全局符号表。
链接
链接时对库的顺序要求
基本的意思就是从左向右查找,如果是链接成动态库会稍微不同一点。
1.4.1 对于library的查找
查找需要连接的符号名是从前向后找,根据-L指定的路径顺序查找;不同目录下的同名的库,只取第一个
1.4.2 对于符号的查找
从左向右查找,如果是主程序块和静态库,不能定位地址就报错: ‘undefined reference to: xxx’如果是链接成动态库,则假设该符号在load 的时候地址重定位。如果找不到对应的动态库,则会在load的时候报:“undefined symbol: xxx“这样的错误。
1.5.1 链接主程序模块或者是静态库的时的‘undefined reference to: xxx’
g++ -Wl,--as-needed -lGalaxyRT -lc -lm -ldl -lpthread -L/home/ocaml/lib/ -lrt -o mutex mutex.o
假设mutex依赖libGalaxyRT.so中的东西。想想,因为gcc对库的顺序要求 和–as-needed(因为libGalaxyRT.so在mutex.o的左边,所以gcc认为没有用到它,–as-needed将其忽略),ld忽略libGalaxyRT.so,定位mutex.o的 符号的时候当然会找不到符号的定义!所以‘undefined reference to’这个 错误是正常地!
正确的链接方式是:
g++ -Wl,--as-needed mutex.o -lGalaxyRT -lc -lm -ldl -lpthread -L/home/ocaml/lib/ -lrt -o mutex
参考资料
linux下C/C++编译时系统搜索 include 和 链接库 文件路径的指定
《程序员的自我修养—链接、装载与库》

浙公网安备 33010602011771号