和Steve之间的通信--RE: 关于《UNIX技术内幕》的勘误及遇到的问题_20

  1. 云栖社区>
  2. 博客>
  3. 正文

和Steve之间的通信--RE: 关于《UNIX技术内幕》的勘误及遇到的问题_20

罗宾豪 2012-09-26 23:13:19 浏览708



From: qf.hao@hotmail.com
To: quntmec@hotmail.com
Subject: RE: 关于《UNIX技术内幕》的勘误及遇到的问题_20
Date: Fri, 3 Feb 2012 09:01:06 +0800

Steve,
对,这里是应该为10.谢谢!


From: quntmec@hotmail.com
To: qf.hao@hotmail.com
Subject: RE: 关于《UNIX技术内幕》的勘误及遇到的问题_20
Date: Wed, 11 Jan 2012 14:08:59 +0800

郝先生,

关于“勘误“里,你提到为何a的 r_address 应为10(而不是8),我认为如下:

根据433页里r_address的定义,以及440页图11-11里最上面的set函数指令列表(与下面“程序重定向表“是关联的),可知 a 偏离 set 函数首指令 10 个字节。所以a 的 r_address 应该是 10。


From: qf.hao@hotmail.com
To: quntmec@hotmail.com
Subject: RE: 关于《UNIX技术内幕》的勘误及遇到的问题_20
Date: Tue, 10 Jan 2012 22:57:20 +0800




From: quntmec@hotmail.com
To: qf.hao@hotmail.com
Subject: 关于《UNIX技术内幕》的勘误及遇到的问题_20
Date: Thu, 5 Jan 2012 15:51:12 +0800

郝先生,

关于“unix可执行文件“一章,我的疑问如下:

1、434页,倒数第8行,公式:n_name = pstr +sizeof(int) + n_strx + sizeof(int),能解释一下吗?最好可以举例说明,如图11-3(字符串表)里的xx。
[郝]:这里排版有问题,应该是:n_name = pstr +sizeof(int) + n_strx ,“+ sizeof(int)”是因为字符串表起始4或2个字节记录表的总长度。

2、436页,图11-6,yy的 n_value 为 0x2002,这个数字是如何得出来的?同理,441页里“图11-13”里面的xx,yy,a的 n_value(分别为0x2002,0x2004,0x2006)又是怎样算出来的?
[郝]:xx,yy都在BSS段,而BSS段的起始地址是0x2000,所以xx在0x2000处,yy在0x2002处(xx占了2个字节)。
3、对于11.2.4“动态加载过程分析“,感觉内容有些乱,我的理解如下:

首 先,这里应该涉及到2个GOT,一个是“进程的GOT“,这里简称“main的GOT“(只为下面描述方便而已);另一个是“libh.so的GOT”。 同时这里也涉及2个重定向表,一个是“进程的重定向表“,这里简称“main的重定向表“;另一个是"libh.so的重定向表".同理,也涉及到2个 PLT0&1、符号表及字符串表。

其次,对于1~7步,我的理解如下:

1)加载 a.out 后,“main的GOT“内的GOT2被改为 0x1E00。具体参考 451页 图11-29

2)执行到main函数时,调用increase时,它跳转至"main的.PLT1“,即跳转至“main的GOT“中的GOT3,即“Main_addr+0x32",也就是:pushl $offset处,最后跳至 ”main的.PLT0“

3).PLT0 的第一条指令将“main的GOT“中的GOT1压栈,然后跳转至GOT2,即0x1E00处(动态连接器地址)。由于 2)里将 $offset 压栈,该值为increase相对于“main的重定向表“的偏移,因此,动态链接器可以从“main的重定向表“里找到increase的记录。在找到 该记录后,发现器 r_extern 被设,则从“main的重定向表“(450页,图11-27)中找到 increase 对应的 r_symbolnum。根据该值并结合“main的字符串表“和“main的符号表“(450页,图11-26),得知调用的函数名为 “increase"。然后搜索各个动态库符号表及其对应的字符串表,以查找包含 increase 定义的库。找到后(libh.so),加载该库,即这里是加载 libh.so。加载后的libh.so如图11-31(452页)所示。 libh.so处于 0x1400 ~ 0x14A8 之间,它含有自己的代码段(0x1400~0x1493)和自己的数据段(0x1494~14A8)。其中,数据段对应的是“libh.so的GOT”。

4) 动态链接器重新设置“libh.so的GOT”(原图在448页,图11-21),将其GOT2设置为0x1E00,g_count设为 0x2014(因为假设在32位机上运行,所以 a 占用4个字节,g_count 紧跟在a后面,因而 g_count 为 a+4即,0x2010+4=0x014),abs设置为0x148C(根据448页图11-21,abs=.PLT1+4, 且.PLT1=0x1470+0x18=0x1488,所以,abs=0x1488+4=0x148C).

5)动态链接器重新设置 “main的GOT“里的GOT3(increase项)。根据“libh.so的符号表”(448页,图11-22),可知 increase 的 n_value 为0x20,则 increase 的绝对地址为:0x1400+0x20=0x1420.更新后的“main的GOT“如图11-32。

回到 2),现在已知 increase 的绝对地址,则动态链接器跳转至 0x1420。

6)increase->judge->abs,程序跳转至 “libh.so的.PLT1“中(问题2:程序是如何获知要跳转至的是"libh.so的.PLT1",而不是"main的.PLT1"?)。
[郝]:因为libh.so中的代码编译器生成后是位置无关代码PIC,所以judge调用的时候,它调用的是.PLT1相对于它的偏移量,这样,不管libh.so被加载到何处,judge总能调到.PLT1。
重 复 2)~5)中的GOT(libh.so的GOT)操作,可知 abs 的绝对地址为 0x1000(因为在“libh.so的符号表“中abs为 undefine,所以动态加载器会去查找其他符号表,最终在“main的符号表”里得到 abs 的绝对地址为 0x1000。动态加载器将 0x1000 写入 “libh.so的GOT” 中的 GOT3,以更换 4)中得到的 0x148C。更新后如图11-33所示(453页)。

7) 在找到 abs 绝对地址后,上接 6),动态加载器执行完 abs 后,返回至 judge,完后再返回至 increase,最后访问 g_count。由于 g_count 的 r_extern 位未被设("libh.so的重定向表“,449页,图11-24),所以动态加载器查找“libh.so的GOT”,得 g_count 的绝对地址 0x2014。接着,动态加载器根据该地址访问 g_count 的内容,程序往下执行至完。(问题3:根据448页,图11-22,g_count 的 n_value 为 0x8,即偏移“libh.so的GOT”0x8。那按理 g_count 应该是 0x2008 而不是 0x2014。是这样吗?
[郝]:g_count所在的绝对地址是0x2014,而它在libh.so的GOT表中的偏移量是0x8,也就是说,g_count在libh.so的GOT中的第4个条目GOT4----参照图11-33.
问题1:对于上面 1)~7)的理解对吗?
[郝]:正确。其实,GOT表就有点类似于C++中的VTABLE-- 虚函数表,调用虚函数时,也是取出虚函数表中固定偏移量(条目)处的地址,然后调用,这也就是为什么它能够实现多态的原因。而PLT表就类似于取虚函数表 中条目的那段代码。GOT表内容的生成和VTABLE有点相似但更为复杂的是,GOT表中的内容是动态链接器在运行时通过查找进程符号表而设定的;而 VTABLE的内容是类构造函数在运行时设定的,而构造函数的这一段代码比较固定简单,在编译时就生成好了

问题2:在6)中。

问题3:在7)中。



此外,勘误如下:
440

图11-11里的“程序重定向表“,里面,a的 r_address 应为10(而不是8)

[郝]: 为什么?
Steve





















《返璞归真--UNIX技术内幕》在全国各大书店及网城均有销售:
京东
亚马逊                          China pub
上学吧                          1号店