我读过这篇关于位置无关代码的文章,这就是我所得到的(专注于函数调用(:当一个共享库被构建和链接时,它将被加载到什么内存地址是未知的,这就是我们使用位置无关代码(PIC(的原因。
PIC中的一种机制是使用PLT(过程链接表(来调用函数,使我们的代码保持位置独立,它的工作原理基本上是这样的:对于库中我们调用的每个函数(func
(,都有一个func@plt
小过程,这就是实际被调用的过程。然后,该过程跳转到GOT中相应条目中存储的地址。首先,这个地址将指向PLT条目,然后PLT条目将调用动态链接器解析器来解析实际函数的地址。动态链接器现在将覆盖GOT中的条目,以指向正确的实际函数。
现在,所有这一切都是可能的,因为当链接共享库时,调用func
的指令和func@plt
之间的距离是已知的,并且到GOT中的条目的距离也是已知的。都是相对的,很棒!
我的问题是:我可以理解为什么要在链接共享库的文件中为函数调用提供这种机制:这是为了只在请求函数地址时解析它们。但我不明白为什么共享库本身需要这样做!
假设我们在共享库中有一个函数func
,调用func2
。func
和func2
之间的距离在静态链接阶段是已知的,因此我们可以计算func2
相对于指令指针的位置,就像我们计算func2@plt
一样。
PLT确实比简单的PIC调用效率低得多。
在共享库中,PLT仅用于外部(导入(函数或具有默认可见性的内部函数。这些函数可以在运行时由来自不同shlib的具有相同名称的函数插入,并且插入是通过PLT/GOT处理的。
不幸的是,默认情况下,在Linux系统上,所有函数都具有默认可见性,但用户可以使用-fvisibility=hidden
编译器标志或链接器版本脚本来更改此可见性(例如,有关详细信息,请参阅此问题(。