为什么 rsp 寄存器在叶函数中没有像任何其他函数开始时那样递减?[复制]

Why is it that the rsp register is not decremented in a leaf function as it is at the beginning of any other functions? [duplicate]

提问人:alessio solari 提问时间:8/28/2023 最后编辑:Peter Cordesalessio solari 更新时间:8/29/2023 访问量:86

问:

以下是有关我的系统的一些信息:

  • Ubuntu 22.04.3 在 Intel x86_64上运行;
  • ggc 版本 11.4.0;

我注意到 rsp 寄存器在叶函数中没有像在非叶函数中那样递减。

例如,在文件 test.c 中考虑这个 C 程序:

int fx(){
     
     int x = 30;
     int y = 34;
     int z = 45;
     return 30;  
}

int main(){    
    
    int a = 10;
    int b = 20;
    int c = fx();
     
}

使用“gcc test.c -fno-stack-protector -o test”编译后,我运行“objdump -dw -M suffix test”,得到:

0000000000001129 <fx>:
    1129:   f3 0f 1e fa             endbr64 
    112d:   55                      pushq  %rbp
    112e:   48 89 e5                movq   %rsp,%rbp
    1131:   c7 45 fc 1e 00 00 00    movl   $0x1e,-0x4(%rbp)
    1138:   c7 45 f8 22 00 00 00    movl   $0x22,-0x8(%rbp)
    113f:   c7 45 f4 2d 00 00 00    movl   $0x2d,-0xc(%rbp)
    1146:   b8 1e 00 00 00          movl   $0x1e,%eax
    114b:   5d                      popq   %rbp
    114c:   c3                      retq   

000000000000114d <main>:
    114d:   f3 0f 1e fa             endbr64 
    1151:   55                      pushq  %rbp
    1152:   48 89 e5                movq   %rsp,%rbp
    1155:   48 83 ec 10             subq   $0x10,%rsp
    1159:   c7 45 fc 0a 00 00 00    movl   $0xa,-0x4(%rbp)
    1160:   c7 45 f8 14 00 00 00    movl   $0x14,-0x8(%rbp)
    1167:   b8 00 00 00 00          movl   $0x0,%eax
    116c:   e8 b8 ff ff ff          callq  1129 <fx>
    1171:   89 45 f4                movl   %eax,-0xc(%rbp)
    1174:   b8 00 00 00 00          movl   $0x0,%eax
    1179:   c9                      leaveq 
    117a:   c3                      retq  

正如你所看到的,我们主要有,但这不会发生。subq $0x10,%rspfx

我的问题:

  1. 它是否与 gcc 遵循的 System V ABI 有关,或者它只是编译器执行的优化?

  2. 这种行为的原因是什么?

  3. 有没有办法告诉编译器我不希望叶函数出现这种行为?

C x86-64 堆栈内存 ABI 红色区域

评论

0赞 Jabberwocky 8/28/2023
@Frankie_C哪些参数?函数中没有参数。fx
2赞 BoP 8/28/2023
x86-64 上的红色区域究竟在哪里?
1赞 0___________ 8/28/2023
无需保留堆栈帧,因为您不调用任何其他函数。
0赞 alessio solari 8/28/2023
@0___________是否可以在 Ubuntu x86_64 系统中编译带有“-mno-red-zone”标志的 C 程序,例如“gcc file.c -fno-stack-protector -mno-red-zone -o file”?此选项是否会破坏某些东西或导致一些不兼容,或者它只是一个优化?

答:

2赞 0___________ 8/28/2023 #1

由于您不通过寄存器传递任何参数,并且不调用任何其他函数,因此保留堆栈空间毫无意义。

void nn(int x);

int fx(void){     
     volatile int x = 30;

     nn(x);
     return 30;  
}

https://godbolt.org/z/7qE4nbdcn

是否可以在 Ubuntu x86_64 系统中编译 C 程序,使用 标记“-mno-red-zone”,如“gcc file.c -fno-stack-protector” -mno-red-zone -o 文件“?此选项是否会破坏某些东西或导致一些不兼容,或者它只是一个优化?

这是安全的。基本上,它在用户空间代码中毫无意义。是优化吗?- 是的,有点。保留空间是毫无意义的,除非你的堆栈大于红色区域。

int fx(void){     
     volatile int x[100] = {30,};

     return 30;  
}

它将保留堆栈减去红色区域的大小。

https://godbolt.org/z/8qb191x9E

我读到System V ABI“强制”红色区域。这样做 意味着即使我们使用 -mno-red-zone,这个红色区域也确实存在 选择?我的意思是,如果我们使用此选项,我们不会使用红色区域,而是 它仍然存在吗?

它与您的代码无关。信号和中断处理程序会将堆栈指针从堆栈指针上移开。要更改它,您需要重新编译内核并修改其某些代码。

评论

0赞 alessio solari 8/28/2023
我读到System V ABI“强制”红色区域。这是否意味着即使我们使用 -mno-red-zone 选项,这个红色区域也确实存在?我的意思是,如果我们使用此选项,我们不会使用红色区域,但它仍然存在?
0赞 alessio solari 8/28/2023
@ 0___________://stackoverflow.com/questions/76993711/does-the-red-zone-still-exist-even-if-if-we-use-the-mno-red-zone-flag-in-gcc?noredirect=1#comment135729735_76993711
1赞 0___________ 8/28/2023
@alessiosolari是的,它与操作系统有关。你的代码与它无关。
1赞 Peter Cordes 8/29/2023
@alessiosolari:是的,内核的信号传递代码和其他任何代码仍将保留 RSP 下方 128 字节的红色区域。它不知道也不关心当前正在执行的机器代码不会重新加载该数据(在这种情况下,因为编译器选择不保留任何内容)。尽管它是一个选项,但它不必更改 ABI,只是不要以利用 ABI 的那部分的方式进行优化。但是,内核代码通常不能使用红色区域,因此必须进行编译以匹配该 ABI。-m-mno-red-zone