main.c
#include <stdio.h>
void hello();
int main() {
hello();
}
hello.c
// hello.c
#include <stdio.h>
void hello() {
printf("Hello World\n");
}
gcc main.c hello.c
Hello World
gcc -c main.c
得到 main.o
objdump -d main.o
0000000000000000 <main>:
0: f3 0f 1e fa endbr64
4: 48 83 ec 08 sub $0x8,%rsp
8: 31 c0 xor %eax,%eax
a: e8 00 00 00 00 callq ????????
f: 31 c0 xor %eax,%eax
11: 48 83 c4 08 add $0x8,%rsp
15: c3 retq
但有些地址编译的时候不知道啊 (比如 hello), 就先填个0吧
filled = *refptr 为运行时被填入 00 00 00 00 的值。 计算公式如下:
S = ADDR(r.symbol)
A = -4
P = ADDR(s) + r.offset
PC = ADDR(s) + r.offset - r.addend
= P - A
*refptr = S + A - P
= S - PC
所以 S = *refptr + PC 成立
可以验证:
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
int main();
void hello() {
char *refptr = (char *)main + 0xa + 1;
int32_t filled = *(int32_t *)refptr;
// 必须满足以下条件
assert((char *)main + 0xf + filled == (char *)hello);
}
// 其中 PC = (char *)main + 0xf 为下一条指令的地址。
根据CSAPP, ADDR(s)的值存在GOT里面。如果是静态链接,在生成可执行目标文件后,这个值就确定不变了。如果是动态链接,第一次调用的时候,会由动态链接器算出这个值(之后就不会变了),然后存在GOT里面,第二次以及之后的调用就直接从GOT里面拿了。