7.6
buf 有 m.o 外部 .data
bufp0 有 swap.o 全局 .data
bufp1 有 swap.o 局部 .bss
incr 有 swap.o 局部 .text
count 有 swap.o 局部 .bss
swap 有 swap.o 全局 .text
temp 无
7.7

在bar5.c中声明x的时候使用static ,使其链接为内部链接:
/* bar5.c */
static double x;
void f()
{
x = -0.0;
}
7.8
A.
(a) REF(main.1) --> DEF(main.1)
(b) REF(main.2) --> DEF(main.2)
B.
(a) REF(x.1) --> 未知
(b) REF(x.2) --> 未知
C.
(a) REF(x.1) --> 错误
(b) REF(x.2) --> 错误
7.9
foo6.c中main符号是一个函数名,是强符号;bar6.c中main是一个未初始化的全局变量,是若符号。两者链接后链接器会选择强符号,也就是foo6.c中的main,这个main代表main函数的起始地址,因此会打印这个地址的数据,也就是main函数中第一条指令的机器码。
7.10
ld p.o libx.a p.o
ld p.o libx.a liby.a libx.a
ld p.o libx.a liby.a libx.a libz.a
7.11

未初始化的全局变量在可执行文件的数据段中不占用空间,但是当程序装载到内存后会占用空间。因此多出的一个字节可能是全局变量占用的。
7.12
0x4004e0+0xa=0x4004ea
0x4004ea e8 00 00 00 00 callq e
0x4004ef …
因此执行callq时rip中的值为0x4004ef
所以call的偏移=0x4004f8-0x4004ef=0x9
上面是错误的做法!!
正确的做法是,利用下面的公式

首先,重定位类型中包含PC32,因此这个重定位采用PC相对寻址。
先计算出引用所在的内存地址:
refaddr = ADDR(.text) + r.offset = 0x4004e0 + 0xa = 0x4004ea
注意,上面这个refaddr并不是call指令所在的内存地址!
实际编译后的代码段是这样的:
0x4004e9 e8 00 00 00 00 callq e
call指令机器码的第一个字节的地址为0x4004e9,而它之后的一个字节(也就是"引用"的所在点)才是0x4004ea。也就是说,上面计算出的refaddr并不是call指令的操作码所在地址,而是"操作数"所在的地址。这部分内容很难理解,也很容易出错,但是只要按照上面的公式来计算就没有问题。
然后计算偏移:
refptr = (unsigned)(ADDR(r.symbol) + r.addend - refaddr) = 0x4004f8 + (-4) - 0x4004ea = 0xa
因此第一问答案是0xa
同样的方法计算第二问:
refaddr = 0x4004d0 + 0xa = 0x4004da
refptr = 0x400500 + (-4) - 0x4004da =0x22
7.13
不会
本文深入探讨了程序链接过程中的内部与外部链接,以及静态变量的作用域。讲解了全局变量、局部变量、未初始化全局变量在不同阶段的内存占用情况,并通过实例解析了链接器如何处理符号冲突。同时,详细阐述了call指令的重定位过程,解释了内存中call指令的实际布局和计算偏移的方法。最后,讨论了可执行文件与内存空间的关系,揭示了多出字节的原因。
4773

被折叠的 条评论
为什么被折叠?



