gpt4 book ai didi

assembly - 为什么 x86-64 GCC 函数序言分配的堆栈比局部变量少?

转载 作者:行者123 更新时间:2023-12-04 00:39:11 25 4
gpt4 key购买 nike

考虑以下简单程序:

int main(int argc, char **argv)
{
char buffer[256];

buffer[0] = 0x41;
buffer[128] = 0x41;
buffer[255] = 0x41;

return 0;
}

在 x86-64 机器上用 GCC 4.7.0 编译。用 GDB 反汇编 main() 给出:
0x00000000004004cc <+0>:     push   rbp
0x00000000004004cd <+1>: mov rbp,rsp
0x00000000004004d0 <+4>: sub rsp,0x98
0x00000000004004d7 <+11>: mov DWORD PTR [rbp-0x104],edi
0x00000000004004dd <+17>: mov QWORD PTR [rbp-0x110],rsi
0x00000000004004e4 <+24>: mov BYTE PTR [rbp-0x100],0x41
0x00000000004004eb <+31>: mov BYTE PTR [rbp-0x80],0x41
0x00000000004004ef <+35>: mov BYTE PTR [rbp-0x1],0x41
0x00000000004004f3 <+39>: mov eax,0x0
0x00000000004004f8 <+44>: leave
0x00000000004004f9 <+45>: ret

当缓冲区为 256 字节时,为什么它只用 0x98 = 152d 代替 rsp?当我将数据移动到缓冲区 [0] 时,它似乎只是使用分配的堆栈帧之外的数据并使用 rbp 进行引用,那么 sub rsp,0x98 的意义何在?

另一个问题,这些线有什么作用?
0x00000000004004d7 <+11>:    mov    DWORD PTR [rbp-0x104],edi
0x00000000004004dd <+17>: mov QWORD PTR [rbp-0x110],rsi

为什么需要保存 EDI 而不是 RDI?但是,我看到它将它移到 C 代码中分配的缓冲区的最大范围之外。同样令人感兴趣的是为什么两个变量之间的增量如此之大。既然EDI只有4个字节,那为什么两个变量需要12个字节的分隔呢?

最佳答案

x86-64 ABI used by Linux (和其他一些操作系统,虽然不是 Windows,它有自己不同的 ABI)在堆栈指针下方定义了一个 128 字节的“红色区域”,保证不会被信号或中断处理程序触及。 (参见图 3.3 和 §3.2.2。)

因此,叶函数(即不调用任何其他函数的函数)可以将这个区域用于它想要的任何地方——它不像 call 那样做任何事情。这会将数据放置在堆栈指针处;并且任何信号或中断处理程序都将遵循 ABI 并在存储任何内容之前将堆栈指针至少再增加 128 个字节。

(较短的指令编码可用于带符号的 8 位位移,因此红色区域的重点是它增加了叶函数可以使用这些较短的指令访问的本地数据量。)

这就是这里发生的事情。

但是......这段代码没有使用那些较短的编码(它使用来自 rbp 而不是 rsp 的偏移量)。为什么不?它还在节省 edirsi完全没有必要——你问为什么要节省 edi而不是 rdi ,但为什么要保存它呢?

答案是编译器正在生成非常糟糕的代码,因为没有启用优化。如果您启用任何优化,您的整个功能可能会折叠为:

mov eax, 0
ret

因为这就是它真正需要做的: buffer[]是本地的,因此对其所做的更改永远不会被其他任何东西看到,因此可以优化掉;除此之外,所有函数需要做的就是返回 0。

所以,这里有一个更好的例子。这个函数完全是胡说八道,但使用了一个类似的数组,同时做了足够的工作以确保事情不会全部被优化:
$ cat test.c
int foo(char *bar)
{
char tmp[256];
int i;

for (i = 0; bar[i] != 0; i++)
tmp[i] = bar[i] + i;

return tmp[1] + tmp[200];
}

编译进行了一些优化,可以看到红色区域的类似使用,
除了这次它确实使用了来自 rsp 的偏移量:
$ gcc -m64 -O1 -c test.c
$ objdump -Mintel -d test.o

test.o: file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <foo>:
0: 53 push rbx
1: 48 81 ec 88 00 00 00 sub rsp,0x88
8: 0f b6 17 movzx edx,BYTE PTR [rdi]
b: 84 d2 test dl,dl
d: 74 26 je 35 <foo+0x35>
f: 4c 8d 44 24 88 lea r8,[rsp-0x78]
14: 48 8d 4f 01 lea rcx,[rdi+0x1]
18: 4c 89 c0 mov rax,r8
1b: 89 c3 mov ebx,eax
1d: 44 28 c3 sub bl,r8b
20: 89 de mov esi,ebx
22: 01 f2 add edx,esi
24: 88 10 mov BYTE PTR [rax],dl
26: 0f b6 11 movzx edx,BYTE PTR [rcx]
29: 48 83 c0 01 add rax,0x1
2d: 48 83 c1 01 add rcx,0x1
31: 84 d2 test dl,dl
33: 75 e6 jne 1b <foo+0x1b>
35: 0f be 54 24 50 movsx edx,BYTE PTR [rsp+0x50]
3a: 0f be 44 24 89 movsx eax,BYTE PTR [rsp-0x77]
3f: 8d 04 02 lea eax,[rdx+rax*1]
42: 48 81 c4 88 00 00 00 add rsp,0x88
49: 5b pop rbx
4a: c3 ret

现在让我们稍微调整一下,通过插入对另一个函数的调用,
以便 foo()不再是叶函数:
$ cat test.c
extern void dummy(void); /* ADDED */

int foo(char *bar)
{
char tmp[256];
int i;

for (i = 0; bar[i] != 0; i++)
tmp[i] = bar[i] + i;

dummy(); /* ADDED */

return tmp[1] + tmp[200];
}

现在红色区域无法使用,所以你会看到更像你的东西
原本预期:
$ gcc -m64 -O1 -c test.c
$ objdump -Mintel -d test.o

test.o: file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <foo>:
0: 53 push rbx
1: 48 81 ec 00 01 00 00 sub rsp,0x100
8: 0f b6 17 movzx edx,BYTE PTR [rdi]
b: 84 d2 test dl,dl
d: 74 24 je 33 <foo+0x33>
f: 49 89 e0 mov r8,rsp
12: 48 8d 4f 01 lea rcx,[rdi+0x1]
16: 48 89 e0 mov rax,rsp
19: 89 c3 mov ebx,eax
1b: 44 28 c3 sub bl,r8b
1e: 89 de mov esi,ebx
20: 01 f2 add edx,esi
22: 88 10 mov BYTE PTR [rax],dl
24: 0f b6 11 movzx edx,BYTE PTR [rcx]
27: 48 83 c0 01 add rax,0x1
2b: 48 83 c1 01 add rcx,0x1
2f: 84 d2 test dl,dl
31: 75 e6 jne 19 <foo+0x19>
33: e8 00 00 00 00 call 38 <foo+0x38>
38: 0f be 94 24 c8 00 00 movsx edx,BYTE PTR [rsp+0xc8]
3f: 00
40: 0f be 44 24 01 movsx eax,BYTE PTR [rsp+0x1]
45: 8d 04 02 lea eax,[rdx+rax*1]
48: 48 81 c4 00 01 00 00 add rsp,0x100
4f: 5b pop rbx
50: c3 ret

(请注意, tmp[200] 在第一种情况下处于有符号的 8 位位移范围内,但不在这种情况下。)

关于assembly - 为什么 x86-64 GCC 函数序言分配的堆栈比局部变量少?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13201644/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com