assembly 为什么Eax在“离开”之前就被清除了?

jq6vz3qz  于 2022-11-13  发布在  其他
关注(0)|答案(1)|浏览(221)

我已经用compiler explorer生成了一些简单的程序。为什么在堆栈弹出rbp之前eax总是被设置为零?在退出之前清除寄存器是标准的吗?

main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 1
        mov     DWORD PTR [rbp-8], 1
        add     DWORD PTR [rbp-4], 1
        mov     eax, DWORD PTR [rbp-4]
        mov     DWORD PTR [rbp-8], eax
        mov     eax, 0 <--------------------------?
        pop     rbp
        ret
  • 作为参考,我使用的是x86-64 gcc 12.1*
kqlmhetl

kqlmhetl1#

不,这不是标准的。你不“清除”寄存器本身,你总是把它们设置为某个值。有时这个值是0。
在C和C99中,main()的底部有一个隐式的return 0;。EAX是所有主流x86调用约定中的返回值寄存器,包括x86-64 System V。清零EAX实现了return 0;,如果函数没有被调用main,它就不会存在。
(Or如果你的源代码包含一个显式的return 0;你忘了在你的问题中包括这一点,甚至把它作为你的 lightning 链接的一部分;使用“共享”按钮创建一个短链接或更好的完整链接。)
在C
中,在没有return语句的情况下,从非void函数的末尾落下是未定义的行为。在main末尾的隐式返回0计数,因此重命名它将使它成为UB,以便到达执行路径,即,以便调用函数。
在C语言中,只有当调用者 * 使用 * 一个从函数末尾掉下来的返回值时,它才是UB,这是因为C语言的历史:在添加void之前,它已经存在了一段时间,因此历史代码中声明了类似foo(int x)的函数,并带有隐式int返回值,但实际上它们没有返回任何内容,调用者也没有读取返回值。
因此,无论如何,在不添加return语句的情况下将int main()重命名为int foo(),将使您的函数在C++中具有未定义的行为,并且在启用优化的情况下,GCC根本不会为它发出任何指令,甚至不会发出ret
(It当然,我也会打印一个警告,至少对于-Wall是这样的。在处理编译器输出时,您一定要启用-Wall,因为编译器认为值得警告的事情通常与奇怪的asm有关。在Godbolt上,在asm输出窗格左下角寻找任何红色,显示警告的数量。单击它可以打开一个新的窗格。)

相关问题