Skip to content

《计算机系统基础》第4章 理论习题选解


习题解答仅供参考,如有问题或获取Markdown版本,请移步github仓库或联系转工管群ISGSNSG。


第3题

链接器在生成可执行文件时需要完成符号解析,符号解析需要使用符号表。根据《计算机系统基础》第193-194页的内容,对于某个C程序模块m,包含在符号表中的符号有三种类型:全局符号(在m中被定义,在其他模块中引用)、外部符号(其他模块定义,在m中引用)、本地符号(在m中被定义,在m中引用,包括带static属性的函数名和全局变量名)。 假设一个C语言程序有两个源文件main.c和test.c,对于编译生成的可重定位目标文件test.o,有如下符号情况分析: (1)extern int a[];指示编译器a是在其他地方(main)定义的且可以在当前文件中使用,属于外部符号因此存在于test.o的符号表中,定义模块为main.o,出现在test.o的.data节; (2)int val = 0;表明val是在test.o中被定义的,同时注意到main.c的第8行使用了val,因此属于全局符号,且val被初始化,则它位于.data 段(若未初始化会位于.bss段) (3)int sum()是一个函数名,目标代码位于.text节,且main.c调用了sum,因此属于全局符号 (4)i是test.o中的局部变量,是在运行时动态分配的,被分配在栈中,不位于test.o的符号表中 综上所述,表如下:

符号 是否在test.o的符号表中 定义模块 符号类型
a main.o 外部符号 .data
val test.o 全局符号 .data
sum test.o 全局符号 .text
i / / /

第4题

与第三题相类似,分析得到如下:

符号 是否在swap.o的符号表中 定义模块 符号类型
buf main.o 外部符号 .data
bufp0 swap.o 全局符号 .data
bufp1 swap.o 本地符号 .bss
incr swap.o 本地符号 .text
count swap.o 本地符号 .data
swap swap.o 全局符号 .text
temp / / /

其中需要注意的在上一题中没有出现之处有: (1)在上一题中两个全局符号均被main.o使用,但是本题中bufp0并未被其他模块引用,但是其不是在函数内部定义的且没有使用static关键字修饰,即使其他模块实际上并没有调用或引用这个符号,仍然视为一个全局符号。 (2)bufp1在定义时未初始化,,因此位于.bss节。根据《计算机系统基础》第188页,未初始化变量没有具体的值,无须在目标文件中分配用于保存值的空间(在目标文件中不占据实际磁盘空间,仅为一个占位符)。 (3)在函数中使用static int count = 0; 创建一个静态局部变量,其空间不被分配在栈中,是本地符号;在其被创建后,再次调用函数incr()时count不会再被初始化为0,因此可以达到incr()函数的计数效果。

第5题

(1)根据《计算机系统基础》第196页内容可知全局符号的强弱性:函数名和已初始化的全局变量为强符号,未初始化的全局变量名为弱符号。 由此可知,上述main.c中(unsigned)x、z、proc1、main为强符号,y为弱符号;proc1.c中proc1为强符号,(double)x为弱符号 (2)程序执行后的结果为x = 0, z = 0。 执行proc1函数前后在地址&x, &z存放的内容如下((double)-1.5的机器数为0xBFF8 0000 0000 0000)

地址 proc1执行前 proc1执行后
&x 01 01 00 00 00 00 00 00
&z 02 00   /   / 00 00 F8 BF

第三行修改为short y = 1, z = 2;后,执行proc1函数前后在地址&x, &y, &z存放的内容如下

地址 proc1执行前 proc1执行后
&x 01 01 00 00 00 00 00 00
&y, &z 01 00 02 00 00 00 F8 BF

因此程序执行后的结果为x = 0, z = -16392。 (3)一种可能的修改方式如下:

/*proc1.c*/
static double x;

void proc1()
{
    x = -1.5;
}

第6题(3)

①REF(m1.main)->DEF() 强符号main在m1中定义,m1中不存在对main函数的调用 ②REF(m2.main)->DEF(m1.main) ③REF(m1.p1)->DEF(m2.p1) ④REF(m1.x)->DEF() m1中x在main函数中定义,是局部符号,不存在关联 ⑤REF(m2.x)->DEF(m2.x)

第7题

在两个目标模块m1和m2中,出现了两处关于全局符号main的定义。其中m1中main为强符号,符号类型为函数;m2中main为弱符号,符号类型为变量。根据《计算机系统基础》书第196页链接器处理多重定义符号的规则: (1)强符号不能被多次定义,否则链接错误 (2)若一个符号被说明为一次强符号定义和多次弱符号定义,按强符号定义为准 (3)若有多个弱符号定义,则任选其中一个 则依据第二条规则,main会按照m1中的强符号定义处理。且函数初始指令一般如下(引用了Lab1-5的反汇编结果)

0000000000400900 <seek>:
  400900:   55                  push   %rbp
  400901:   48 89 e5            mov    %rsp,%rbp
  400904:   48 83 ec 10         sub    $0x10,%rsp
其中48是REX前缀。考虑到lab实验为64位,则main函数前两条指令为
0000000000000000 <main>:
  000000:   55                  push   %ebp
  000001:   89 e5               mov    %esp,%ebp
则会输出0x5589\n的结果。

第8题

使用objdump显示可执行目标文件程序头表的可读写数据段,发现可执行目标文件中的数据长度和虚拟地址空间中的存储区大小之间相差了28字节,可能的原因如下: 在可执行目标文件中描述的可读写数据段是由所有可重定位目标文件中的.data段合并生产的.data节、所有可重定位目标文件中的.bss段合并生产的.bss节组成。 根据《计算机系统基础》第188页,未初始化变量没有具体的值,无须在目标文件中分配用于保存值的空间(在目标文件中不占据实际磁盘空间,仅为一个占位符)。 因此,虚拟存储空间长度为0x104字节的可读写数据段中,.data节长度为0xe8,为已初始化值的存储空间;后28字节为未初始化变量所在区域,因此使可执行目标文件中的数据长度和虚拟地址空间中的存储区大小之间相差了28字节。

第9题

根据《计算机系统基础》第198-201页的内容,符号解析过程维护了三个集合:E(将被合并到一起组成可执行文件的所有目标文件的集合)、U(未解析符号的集合)、D(已被加入E所有目标文件中定义符号的集合)。对于扫描的.o文件,将其加入E,把其中未解析符号加入U,把定义符号加入D(把默认静态链接库加入到输入文件列表末尾)。对于扫描的静态库文件,将当前U中所有符号与静态库中所有目标模块依次匹配,并将匹配到的对应模块加入E,将匹配到的符号从U转移到D,同时将本模块中未定义的符号加入到U把默认静态链接库加入到输入文件列表末尾),迭代匹配至U和D不再变化。 特别地,书中给出了如下两个静态库目标模块有相互引用关系的例子:func.o调用了静态库libx.a中的函数,libx.a中调用了liby.a中的函数,且liby.a中调用了libx.a中的函数,可以通过在命令行中重复静态库文件名链接,如下:

gcc -static -o myfunc func.o libx.a liby.a libx.a
同时,命令中使用-static选项指示链接器生成一个完全链接的可执行目标文件(能直接加载到存储器执行,不需要再动态链接到其他应用或模块)。 综上所述, (1)一个可能符合要求的命令行如下:
gcc -static -o p p.o libx.a liby.a
(2)一个可能符合要求的命令行如下:
gcc -static -o p p.o libx.a liby.a libx.a
(3)一个可能符合要求的命令行如下:
gcc -static -o p p.o libx.a liby.a libx.a libz.a

第10题

我们指出:main.o的.text节中需重定位的符号名有swap,相对于.text节起始位置偏移量为0x7,所在指令行号为6(按照图4.17所标示的行号)。 根据第7行,重定位方式为R_386_PC32(PC相对地址方式)。重定位前内容对应偏移量0x7-0xa四字节内容,为0xfffffffc(-4)。重定位后,指向swap函数的起始地址,其中重定位值的计算过程如下: 首先,可以计算出main最后一条指令地址: $$ 0x8048386 + 0x12 = 0x8048398 $$ 由题目知,swap函数紧跟在main后,首地址按4字节边界对齐,则swap函数起始地址为\(0x8048398\)。 根据《计算机系统基础》第203页的内容,IA-32中转移目标地址(有效地址)计算公式为: $$ 转移目标地址 = PC + 偏移地址 $$ call指令中重定位值为偏移地址,因此重定位值: $$ 0x8048398 - (0x8048386 + 0x7 + 0x4) = 0x7 $$ 于是重定位后内容应为0x00000007,即第6行指令变为

6     6:  e8 07 00 00 00        call 0x8048398 <swap>


2024年11月30日