我了解到,当前的gcc编译器默认会生成位置无关的代码。但是,为了了解位置相关代码的外观,我编译了此
int Add(int x, int y) {
return x+y;
}
int Subtract(int x, int y) {
return x-y;
}
int main() {
bool flag = false;
int x=10,y=5,z;
if (flag) {
z = Add(x,y);
}
else {
z = Subtract(x,y);
}
}
为g++ -c check.cpp -no-pie
。但是,无论是否带有-no-pie
标志,生成的代码都是相同的。 <main+0x34>
似乎是相对偏移量。
26: 55 push %rbp
27: 48 89 e5 mov %rsp,%rbp
2a: 48 83 ec 10 sub $0x10,%rsp
2e: c6 45 f3 00 movb $0x0,-0xd(%rbp)
32: c7 45 f4 0a 00 00 00 movl $0xa,-0xc(%rbp)
39: c7 45 f8 05 00 00 00 movl $0x5,-0x8(%rbp)
40: 80 7d f3 00 cmpb $0x0,-0xd(%rbp)
44: 74 14 je 5a <main+0x34>
46: 8b 55 f8 mov -0x8(%rbp),%edx
49: 8b 45 f4 mov -0xc(%rbp),%eax
4c: 89 d6 mov %edx,%esi
4e: 89 c7 mov %eax,%edi
50: e8 00 00 00 00 callq 55 <main+0x2f>
55: 89 45 fc mov %eax,-0x4(%rbp)
58: eb 12 jmp 6c <main+0x46>
5a: 8b 55 f8 mov -0x8(%rbp),%edx
5d: 8b 45 f4 mov -0xc(%rbp),%eax
60: 89 d6 mov %edx,%esi
62: 89 c7 mov %eax,%edi
64: e8 00 00 00 00 callq 69 <main+0x43>
69: 89 45 fc mov %eax,-0x4(%rbp)
6c: b8 00 00 00 00 mov $0x0,%eax
71: c9 leaveq
72: c3 retq
在两种情况下,都是objdump
,只是主要的。我使用的标志是否不正确,或者该代码块的PIC和非PIC的汇编代码应该相同?如果应该相同,请提供一个不是的摘要!
答案 0 :(得分:2)
您必须访问模块或部分之外的项目才能看到区别。
unsigned int x;
void fun ( void )
{
x = 5;
}
因此这将.text转换为.data。
取决于位置。
00000000 <fun>:
0: e3a02005 mov r2, #5
4: e59f3004 ldr r3, [pc, #4] ; 10 <fun+0x10>
8: e5832000 str r2, [r3]
c: e12fff1e bx lr
10: 00000000
与位置无关
00000000 <fun>:
0: e3a02005 mov r2, #5
4: e59f3010 ldr r3, [pc, #16] ; 1c <fun+0x1c>
8: e59f1010 ldr r1, [pc, #16] ; 20 <fun+0x20>
c: e08f3003 add r3, pc, r3
10: e7933001 ldr r3, [r3, r1]
14: e5832000 str r2, [r3]
18: e12fff1e bx lr
1c: 00000008
20: 00000000
在第一种情况下,链接器将地址填写到存储位置
8: e5832000 str r2, [r3]
c: e12fff1e bx lr
10: 00000000 <--- here
。pc相对地址从4:到10:在.text部分之内,因此可以依赖或独立。
4: e59f3004 ldr r3, [pc, #4] ; 10 <fun+0x10>
8: e5832000 str r2, [r3]
c: e12fff1e bx lr
10: 00000000
它获取到外部实体的地址,由链接器填写,然后直接在该地址访问该项目。
4: e59f3010 ldr r3, [pc, #16] ; 1c <fun+0x1c>
8: e59f1010 ldr r1, [pc, #16] ; 20 <fun+0x20>
c: e08f3003 add r3, pc, r3
10: e7933001 ldr r3, [r3, r1]
14: e5832000 str r2, [r3]
18: e12fff1e bx lr
1c: 00000008
20: 00000000
更容易看到链接(-Ttext = 0x1000 -Tdata = 0x2000)
00001000 <fun>:
1000: e3a02005 mov r2, #5
1004: e59f3010 ldr r3, [pc, #16] ; 101c <fun+0x1c>
1008: e59f1010 ldr r1, [pc, #16] ; 1020 <fun+0x20>
100c: e08f3003 add r3, pc, r3
1010: e7933001 ldr r3, [r3, r1]
1014: e5832000 str r2, [r3]
1018: e12fff1e bx lr
101c: 00010010
1020: 0000000c
Disassembly of section .got:
00011024 <_GLOBAL_OFFSET_TABLE_>:
...
11030: 00002000
Disassembly of section .bss:
00002000 <x>:
2000: 00000000
(显然,我还应该指定GOT的去向)。
尽管全局偏移表和.bss是链接在一起的不同部分,但它们彼此相对固定。位置无关性是使.bss(或.data等)相对于.text移动的能力。因此,如果您考虑位置相关的解决方案,如果要移动.data并说整个二进制文件中散布了1000个引用,则要移动.bss,则必须修补其中的每一个。
相反,全局偏移表在此处提供了变量x的地址所在的单个位置,并且对变量x的所有访问本质上都将使用双重间接访问。也许不是很明显,但是位置依赖的获取表的方式将是链接器填写其绝对地址,但这不是独立的,并且被编译为独立的,因此必须完成pc相对数学查找全局偏移量表,因此对于在0x100c处执行指令时的该指令集,程序计数器为0x100c + 8。
100c: e08f3003 add r3, pc, r3
因此,我们将0x100C + 8 + 0x00010010 = 0x11024加上0x0000000c到0x11030。因此,计算出GOT的地址,然后计算其中的偏移量,这便为我们提供了该商品的地址。 0x2000。因此,您需要在此处进行第二次间接获取。
如果将.text放置在0x1000以外的地址上,但不要移动.bss,那很好,这一切都将起作用,直到GOT从.text移到相同的相对偏移。如果要离开.text但移动.bss,则必须更新GOT,如果将.bss从0x2000移至0x3000,则相差+ 0x1000,因此您要遍历GOT并向每个项目添加0x1000弥补差异。
为了访问遥远的项目或相对于.text不依赖于位置的项目,位置独立性实质上必须执行两次间接访问,而不是单一的间接访问(或比依赖于位置的级别多一层)。这意味着更多的代码,更多的内存访问。代码更多,速度更慢。
要使其工作.text扩展到其他.text项目,不能使用固定地址,它必须使用间接/计算地址。同样,此处使用的GOT(由GNU使用)必须相对于.text处于固定的相对位置。然后,您可以从那里相对于代码移动数据,并且仍然可以访问它。因此,您必须有一些规则。 .text是代码,并且假定为只读,因此无法支持此偏移表,该偏移表必须位于ram中,因此不能简单地将其内置到.text节中。