作者回复: 很好。看不懂的就大胆提问,不用不好意思,不存在愚蠢的问题。第一个大问题,在链接时,可执行程序的每一个符号都要被解决,有些是在其它中间文件中,这种就是静态链接;有些是在动态库里,这种就不需要解决了,只要在GOT表里留好位置就行了。所以编译一个依赖动态库的应用程序时,既有静态链接,也有动态链接。这不矛盾。第二个大问题的第1小问,两个因素都起作用的。首先,动态库之间的相对位移是在运行时才能知道的,如果整个程序中只有一个动态库,且这个动态库不再依赖其他库了,那显然不需要PIC。其次,如果不是多个进程需要共享代码的话,那我们把偏移放到代码里也没问题。这两个条件都要满足,这才是要使用PIC的原因。第2个问题,进程没有GOT表,GOT表都是跟着动态库的,你可以再读一个03,然后自己动手看看readelf -l的结果和运行起来以后smap的结果。所以第3个问题也就回答了。
作者回复: Good
作者回复: 注意看文章,我们讲了A.exe和B.exe的smap的结果,那里能看到动态库是独立的,它不会再和其他动态库合并了。这就意味着一个库的数据段和代码段在内存里也是靠在一起的。相对偏移和文件中的相对偏移是一样的。所以它就可以提前决定这个偏移是多少。
作者回复: 这节课的核心就是地址无关代码产生的动机是什么。我们学习计算机知识,一定不能死记硬背。你不必记忆got是什么,只要理解了pic产生的原因,got这种东西就是自然而然的。掌握了这个方案,你下次在工作中遇到类似的问题就知道往哪个方向去研究了。死记硬背的东西很快就忘了,那种学习方法我是非常反对的。
作者回复: 调用者和自己的GOT表的偏移是固定的。它们是同一个动态库,所以它们之间的偏移值就是确定的,不会因为进程的改变而改变。记住一点:调用的语句和GOT之间的偏移和进程无关,它们永远都和磁盘上的偏移是一样的;但是GOT里面的内容,却是和进程相关的,不同的进程里面放的值都不一样,这是因为被调用者的位置在不同的进程里各不相同。
作者回复: 我记得在原文中写过,动态库的代码段和数据段是靠在一起的,它们不会被分拆。也就是说,动态库被加载的时候,代码段不必与其他动态库合并。这是静态链接和动态链接比较重要的一个核心区别。
作者回复: Good,动手看一下最直接。不过,还是要稍想一下,为什么要这么做?
作者回复: OK,thanks
作者回复: .so文件的所有段都是自己独立的,不和其他文件的段合并。也就是说每个.so文件的GOT是独立的。正文中其实有讲,只是没有当做重点单独划出来。
作者回复: 不是。因为data和code的性质不同,loader在内存中不会把它们放到同一页的。所以这种情况也需要两个页,也就是8KB。