编译器通过运用以下这个有趣的事实来生成对全局变量的 PIC 引用
无论我们在内存中的何处加载一个目标模块(包括共享目标模块),数据段与代码段的距离总是保持不变
Note
他们在内存映像图中相对位置是固定的
因此,代码段中任何指令和数据段中任何变量之间的距离都是一个运行时常量,与代码段和数据段的绝对内存位置是无关的
[!note]也就是说我们只需要直接访问 “我楼上数3楼那户人家里的去拿东西”,而不关心它放的是什么,放的东西的绝对位置,如何拿到这个东西,其实有点那个内存的意思,读内存的特定区域就好,调度来的资源不是我们关心的
想要生成对全局变量 PIC 引用的编译器利用了这个事实,它在数据段开始的地方创建了一个表
叫做全局偏移量表(Global Offset Table,GOT)
在 GOT 中,每个被这个目标模块引用的全局数据目标(过程或全局变量)都有一个 8 字节条目
编译器还为 GOT 中每个条目生成一个重定位记录
在加载时,动态链接器会重定位 GOT 中的每个条目,使得它包含目标的正确的绝对地址
每个引用全局目标的目标模块都有自己的 GOT
下图展示了示例 libvector.so 共享模块的 GOT。addvec 例程通过 GOT[3] 间接地加载全局变量 addcnt 的地址,然后把 addcnt 在内存中加 1。这里的关键思想是对 GOTQ[3] 的 PC 相对引用中的偏移量是一个运行时常量

Note
这也就所谓的,我们只用去楼上去,关注相对位置,条目中存着对应调度资源的绝对位置,我们只访问相对位置,这好像一个指针,我们读取指针,指针保存内容
这里读的就是距离是固定的GOT[3]
因为 addcnt 是由 libvector.so 模块定义的,编译器可以利用代码段和数据段之间不变的距离,产生对 addcnt 的直接 PC 相对引用,并增加一个重定位,让链接器在构造这个共享模块时解析它。不过,如果 addcnt 是由另一个共享模块定义的,那么就需要通过 GOT 进行间接访问。在这里,编译器选择采用最通用的解决方案,为所有的引用使用 GOT