一些与编译,链接相关的问题(-fPIC)

本文详细探讨了在64位系统下编译动态库时遇到的-fPIC错误,解释了地址无关代码(Position Independent Code, PIC)的概念及其重要性。在64位环境下,由于CPU指令限制,编译动态库必须使用-fPIC选项。文章还介绍了装载时重定位、全局偏移表(GOT)以及-fPIC与-fpic的区别。此外,讨论了共享库与静态库的编译选择、静态链接与动态链接的优缺点,以及如何控制链接器选择动态库或静态库。最后,文章提到了动态库运行时的符号定位问题和解决方法。" 53088950,1952619,UML设计模式解析:应用与实践,"['设计模式', '面向对象', 'UML']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一些与编译,链接相关的问题(zz)
 地址无关代码,在64位下编译动态库的时候,经常会遇到下面的错误 


/usr/bin/ld: /tmp/ccQ1dkqh.o: relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC


提示说需要-fPIC编译,然后在链接动态库的地方加上-fPIC的参数编译结果还是报错,需要把共享库所用到的所有静态库都采用-fPIC编译一边才可以成功的在64位环境下编译出动态库。


这里的-fPIC指的是地址无关代码


这里首先先说明一下装载时重定位的问题,一个程序如果没有用到任何动态库,那么由于已经知道了所有的代码,
那么装载器在把程序载入内存的过程中就可以直接安装静态库在链接的时候定好的代码段位置直接加载进内存中的对应位置就可以了。
但是在面对动态的库的时候 ,这种方式就不行了。假设需要载入共享库A,但是在编译链接的时候使用的共享库和最后运行的不一定是同一个库,
在编译期就没办法知道具体的库长度,在链接的时候就没办法确定它或者其他动态库的具体位置。另一个方面动态库中也会用到一些全局的符号,
这些符号可能是来自其他的动态库,这在编译器是没办法假设的(如果可以假设那就全是静态库了)


基于上面的原因,就要求在载入动态库的时候对于使用到的符号地址实现重定位。在实现上在编译链接的时候不做重定位操作,地址都采用相对地址,
一但到了需要载入的时候,根据相对地址的偏移计算出最后的绝对地址载入内存中。


但是这种采用装载时重定位的方式存在一个问题就是相同的库代码(不包括数据部分)不能在多个进程间共享(每个代码都放到了它自己的进程空间中),
这个失去了动态库节省内存的优势。


为了解决这个问题,ELF中的做法是在数据段中建立一个指向那些需要被使用(内部的位置无关简单采用相对地址访问就可以实现)的地址列表(也被称为全局偏移表,Global offset table, GOT). 可以通过GOT相对应的位置进行间接引用.


对于我们的32位环境来说, 编译时是否加上-fPIC, 都不会对链接产生影响, 只是一份代码的在内存中有几个副本的问题(而且对于静态库而言结果都是一样的).但在64位的环境下装载时重定位的方式存在一个问题就是在我们的64位环境下用来进行位置偏移定位的cpu指令只支持32位的偏移, 但实际中位置的偏移是完全可能超过64位的,所以在这种情况下编译器要求用户必须采用fPIC的方式进行编译的程序才可以在共享库中使用


从理论上来说-fPIC由于多一次内存取址的调用,在性能上会有所损失.不过从目前的一些测试中还无法明显的看出加上-fPIC后对库的性能有多大的损失,这个可能和我们现在使用的机器缓存以及大量寄存器的存在相关.




小提示:


-fPIC与-fpic 上面的介绍可以看到,gcc要使用地址无关代码加上-fPIC即可,但是在gcc的手册中我们可以看到一个-fpic(区别在一个大写一个小写)的参数,从功能上来说它们都是一样的。-fpic在一些特定的环境中(包括硬件环境)可以有针对性的进行优化,产生更小更快的代码, 但是由于受到平台的限制,像我们的编译环境,开发环境,运行环境都不完全统一的情况下面使用fpic有一定未知的风险,所有决大多数情况下我们使用 -fPIC来产生地址无关代码。
共享内存效率


共享内存在只读的情况下性能和读普通内存是一样的(如果不算第一载入的消耗),而且由于是多个进程共享对cpu cache还显的相对友好。


同时存在静态库和动态库


前面提到编译动态库的时候有提到编译动态库可以像编译静态库那样采用-Lpath -lxx的方式进行, 但这里存在一个问题,如果在path目录下既有动态库又有静态库的时候的行为又是什么样地? 事实上在这种情下, 链接器优先选择采用动态库的方式进行编译.比如在同一目录下存在 libx.a 和 libx.so, 那么在链接的时候会优先选择libx.so进行链接. 这也是
<think>好的,用户现在问的是编译库时加-fPIC有什么区别。之前他们遇到了动态链接库的问题,可能是在编译过程中遇到的,所以现在他们想更深入地了解编译选项的影响。 首先,我需要确认用户对-fPIC的理解程度。他们可能知道这是一个编译选项,但不太清楚具体作用。需要解释清楚什么是位置无关代码,以及为什么在编译库的时候需要它。 接下来,要对比加和不加-fPIC的区别。重点应该放在代码生成的不同,比如是否使用绝对地址,对共享库和静态库的影响,以及可能出现的错误,比如文本重定位的问题。 然后,要说明什么时候必须使用-fPIC,例如在生成共享库的时候,或者某些架构要求。同时,也要提到可能的性能影响,虽然现代处理器优化后影响不大,但用户可能关心这点。 用户可能是在尝试自己编译库时遇到了问题,比如之前那个libc.so.6的警告,现在想优化编译选项,或者解决其他链接问题。需要确保解释清楚,避免他们重复犯错,比如在生成共享库时忘记加-fPIC,导致无法链接。 另外,用户可能对技术细节不太熟悉,所以需要用简单易懂的例子,比如解释共享库被多个进程加载的情况,这时候位置无关代码的重要性。同时,给出具体的编译命令示例,帮助他们实际操作。 最后,提醒用户注意不同编译阶段是否需要-fPIC,比如生成目标文件和链接时是否都需要。可能用户之前只在一个阶段使用了,导致问题出现。总结关键点,确保他们能记住何时使用这个选项。</think>在编译共享库(动态链接库)时,使用 `-fPIC`(Position-Independent Code)选项有**本质区别**,它直接影响代码能否被正确加载和复用。以下是详细对比: --- ### **1. `-fPIC` 的作用** - **核心目标**:生成**位置无关代码**(PIC),即代码中的函数和变量地址在加载到内存时由动态链接器动态解析,而非固定地址。 - **必要性**:共享库需要被多个进程同时加载到不同的内存位置,若代码依赖固定地址,会导致内存冲突。 --- ### **2. 加 `-fPIC` 和不加的区别** | **特性** | **使用 `-fPIC`** | **不使用 `-fPIC`** | |--------------------|-------------------------------------------|----------------------------------------| | **代码地址** | 通过 GOT/PLT 表动态解析地址 | 使用绝对地址或偏移量 | | **共享库兼容性** | ✅ 可编译为共享库(`.so`) | ❌ 只能用于静态库(`.a`) | | **内存占用** | 略高(需维护 GOT/PLT 表) | 略低 | | **性能** | 轻微下降(地址需动态解析) | 稍快(直接使用绝对地址) | | **安全性** | ✅ 支持 ASLR(地址空间随机化) | ❌ 破坏 ASLR 防护 | | **常见错误** | 无 | 链接时报 `text relocation` 错误 | --- ### **3. 必须使用 `-fPIC` 的场景** 1. **编译共享库**(`.so` 文件)时,所有目标文件(`.o`)必须用 `-fPIC` 编译。 2. **某些架构强制要求**(如 x86-64 的共享库通常必须使用 `-fPIC`)。 3. **链接时依赖其他 PIC 代码**(如静态库需被集成到共享库中时)。 --- ### **4. 典型错误示例** 若未对共享库使用 `-fPIC`: ```bash # 编译目标文件时未加 -fPIC gcc -c foo.c -o foo.o # 尝试生成共享库 gcc -shared foo.o -o libfoo.so # 报错示例: # relocation R_X86_64_PC32 against symbol `xxx' can not be used when making a shared object; # recompile with -fPIC ``` --- ### **5. 如何正确使用** - **编译共享库**: ```bash # 编译目标文件时加 -fPIC gcc -c -fPIC foo.c -o foo.o # 生成共享库 gcc -shared foo.o -o libfoo.so ``` - **静态库需被共享库引用时**: ```bash # 静态库的代码也需用 -fPIC 编译 gcc -c -fPIC bar.c -o bar.o ar rcs libbar.a bar.o # 再生成共享库 gcc -shared -fPIC foo.c -o libfoo.so -L. -lbar ``` --- ### **6. 性能权衡** - **影响**:现代 CPU 对 PIC 代码的性能损失已较小(约 1-5%),但对性能敏感的核心代码(如数学计算循环)需谨慎评估。 - **替代方案**:若仅用于静态库,可不加 `-fPIC`。 --- ### **总结** - ✅ **必须加 `-fPIC`**:共享库、强制要求的架构、跨库依赖。 - ❌ **可不加**:仅用于静态库且不涉及共享库的场景。 - **验证方法**: ```bash # 检查目标文件是否包含 PIC 代码 readelf -r foo.o | grep _GLOBAL_OFFSET_TABLE_ # 若有输出,则表示已启用 PIC ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值