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
不会