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

简介: From: qf.hao@hotmail.comTo: quntmec@hotmail.comSubject: RE: 关于《UNIX技术内幕》的勘误及遇到的问题_20Date: Fri, 3 Feb 2012 09:01:06 +0800 Steve,对,这里是应该为10.



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号店


目录
相关文章
|
4月前
|
网络协议 Unix Go
Go unix domain socket通信
Go unix domain socket通信
|
Unix
《返璞归真--UNIX技术内幕》--第14章 多线程的实现
14.1  概述 现代操作系统引入了线程的概念。线程是CPU执行的最小单元,相对于进程而言,它具有轻捷、高效、开销小等优点。 14.2  线程和经典进程的比较 线程和经典进程的主要区别如下。
846 0
|
存储 缓存 Unix
《返璞归真--UNIX技术内幕》--第9章 字符设备驱动
本系统中的输入输出设备都是字符设备,它们包括:KL-11/DL-11A——电传串行接口、PC-11——纸带打孔机和LP-11——行打印机。其中KL-11用于连接终端(terminal),作为用户键盘输入和显示输出的交互接口。
915 0
|
Unix
《返璞归真--UNIX技术内幕》--源代码
本源代码经宁希波帮助整理,去除了''和'&'符号,在此表示诚挚的谢意!源码中除/usr/personal目录外,均由Bell Labs(贝尔实验室)开发。
656 0
|
Unix
《返璞归真--UNIX技术内幕》-- 第11章 UNIX可执行文件
11.1  .out文件 本版UNIX的可执行文件是.out格式,如果你在UNIX下用gcc编译程序,它默认会生成一个名为“a.out”的可执行文件。
839 0
|
Unix
《UNIX技术内幕》--第3章 虚拟内存
Normal 0 false false false MicrosoftInternetExplorer4 /* Style Definitions */ table.
594 0
|
Unix C语言
《返璞归真--UNIX技术内幕》--第6章 中断处理过程
6.4  PDP 11/40的中断类型 系统中的中断主要有下面几种。 6.4.1  电传终端接口输入中断 电传终端接口用于链接PDP 11/40的总线和主要的输入、输出终端,当时该终端是电传打字机,这也是UNIX中用tty(teletypewriter)表示进程所使用的终端类型的原因,事实上电传接口还可以连接彩色显示器(CRT)等。
875 0
|
4月前
|
缓存 网络协议 Unix
Linux(UNIX)五种网络I/O模型与IO多路复用
Linux(UNIX)五种网络I/O模型与IO多路复用
102 0
|
3月前
|
Unix Shell Linux
在Unix/Linux操作系统中,Shell脚本广泛用于自动化任务
在Unix/Linux操作系统中,Shell脚本广泛用于自动化任务
26 2
|
1月前
|
Oracle Ubuntu Unix
Unix与Linux区别
Unix: Unix是一个操作系统家族的名称,最早由贝尔实验室(Bell Labs)的肖像电机公司(AT&T)开发。最早的Unix版本是在1969年创建的。 Linux: Linux是由芬兰计算机科学家Linus Torvalds在1991年创建的。它是作为一个免费、开放源代码的Unix克隆而开始的。
19 1