地址无关代码(PIC)是不依赖固定加载地址、能在内存任意位置执行的二进制形式,核心用于共享库以支持多进程共用物理代码页;通过PLT/GOT机制实现相对寻址与间接跳转,需编译时加-fPIC并链接时用-shared。

地址无关代码(PIC,Position-Independent Code)是指编译后不依赖固定加载地址、能在内存任意位置正确执行的机器码。在 C++ 中,它不是语言特性,而是编译器和链接器协同生成的一种二进制形式,核心目标是支持共享库(如 Linux 的 .so 文件)被多个进程以不同基地址映射到各自虚拟内存中,同时共用同一份物理代码页。
为什么动态库必须用 PIC?
普通可执行文件有固定入口和地址布局,而共享库可能被加载到任意地址(ASLR 安全机制也会随机化)。如果库中直接写死全局变量或函数的绝对地址(比如 call 0x4005a0),一旦加载位置偏移,调用就会跳错。PIC 通过相对寻址 + 间接跳转绕过这个问题:
- 函数调用走 PLT(Procedure Linkage Table),实际跳转由 GOT(Global Offset Table)中的指针间接完成
- 访问全局变量时,先通过 PC 相对寻址拿到 GOT 表项地址,再从 GOT 中读取真实地址
- 所有指令都不含硬编码的绝对地址,只依赖当前指令位置(%rip 或 %pc)做偏移计算
如何生成 PIC 代码?
不是默认行为,需显式启用:
- GCC/Clang:加 -fPIC(通用 PIC,适用于 64 位和大多数 32 位)或 -fpic(更紧凑,但有平台限制,如 GOT 条目数上限)
- 链接共享库时必须用 -shared,且所有目标文件都得是 PIC(否则报错:relocation R_X86_64_32 against `xxx' can not be used when making a shared object)
- C++ 模板、内联函数、constexpr 不影响 PIC 属性,但 静态局部变量 和 全局对象构造函数 需要运行时重定位,仍依赖 GOT/PLT 机制
PIC 在运行时怎么工作?
加载器(如 Linux 的 ld-linux.so)把 .so 映射到某个虚拟地址后,并不修改代码段(只读),而是填充数据段里的 GOT:
标签: linux go windows 编码 虚拟内存 ai c++ win 为什么 red
还木有评论哦,快来抢沙发吧~