为什么在使用 return 值时将 0 移动到堆栈?
Why is 0 moved to stack when using return value?
我正在尝试反汇编 clang
简单 C 程序的二进制文件(用 -O0
编译),我对生成的某条指令感到困惑。
这里有两个带有标准参数的空 main
函数,其中一个 return 的值而另一个没有:
// return_void.c
void main(int argc, char** argv)
{
}
// return_0.c
int main(int argc, char** argv)
{
return 0;
}
现在,当我拆解它们的组件时,它们看起来相当不同,但有一行我不明白:
return_void.bin:
(__TEXT,__text) section
_main:
0000000000000000 pushq %rbp
0000000000000001 movq %rsp, %rbp
0000000000000004 movl %edi, -0x4(%rbp)
0000000000000007 movq %rsi, -0x10(%rbp)
000000000000000b popq %rbp
000000000000000c retq
return_0.bin:
(__TEXT,__text) section
_main:
0000000100000f80 pushq %rbp
0000000100000f81 movq %rsp, %rbp
0000000100000f84 xorl %eax, %eax # We return with EAX, so we clean it to return 0
0000000100000f86 movl [=12=]x0, -0x4(%rbp) # What does this mean?
0000000100000f8d movl %edi, -0x8(%rbp)
0000000100000f90 movq %rsi, -0x10(%rbp)
0000000100000f94 popq %rbp
0000000100000f95 retq
它只有在我使用函数时才会生成 not void,所以我认为这可能是 return 0 的另一种方式,但是当我更改 returned 常量时,这一行完全没有改变:
// return_1.c
int main(int argc, char** argv)
{
return 1;
}
empty_return_1.bin:
(__TEXT,__text) section
_main:
0000000100000f80 pushq %rbp
0000000100000f81 movq %rsp, %rbp
0000000100000f84 movl [=13=]x1, %eax # Return value modified
0000000100000f89 movl [=13=]x0, -0x4(%rbp) # This value is not modified
0000000100000f90 movl %edi, -0x8(%rbp)
0000000100000f93 movq %rsi, -0x10(%rbp)
0000000100000f97 popq %rbp
0000000100000f98 retq
为什么会生成这条线,它的用途是什么?
movl [=10=]x0,-0x4(%rbp)
该指令将0
存储在%rbp - 4
。 clang 似乎为来自 main.
的隐式 return 值分配了一个隐藏的局部变量
来自 clang 邮件列表:
Yes. We allocate an implicit local variable to hold the return value;
return statements then just initialize the return slot and jump to the
epilogue, where the slot is loaded and returned. We don't use a phi
because the control flow for getting to the epilogue is not
necessarily as simple as a simple branch, due to cleanups in local
scopes (like C++ destructors).
Implicit return values like main's are handled with an implicit store
in the prologue.
来源:http://lists.cs.uiuc.edu/pipermail/cfe-dev/2012-February/019767.html
根据标准(对于托管环境),需要 5.1.2.2.1、main
才能返回 int
结果。因此,如果违反此规定,不要指望定义的行为。
此外,main
实际上_不需要显式 return 0
;如果到达函数末尾,则隐式 returned。 (请注意,这仅适用于 main
,它也没有原型。
clang 正在为参数(寄存器 edi
和 rsi
)在堆栈上创建 space 并且出于某种原因也将值 0 放在堆栈上。我假设 clang 将您的代码编译为这样的 SSA 表示形式:
int main(int argc, char** argv)
{
int a;
a = 0;
return a;
}
这可以解释为什么要分配堆栈槽。如果 clang 也进行持续传播,这就可以解释为什么 eax
被清零而不是从 -4(%rbp)
加载。一般来说,不要过多考虑 未优化 程序集中的可疑构造。毕竟,您禁止编译器删除无用代码。
该区域的用途由以下代码揭示
int main(int argc, char** argv)
{
if (rand() == 42)
return 1;
printf("Helo World!\n");
return 0;
}
一开始是
movl [=11=], -4(%rbp)
然后早期的return看起来如下
callq rand
cmpl , %eax
jne .LBB0_2
movl , -4(%rbp)
jmp .LBB0_3
然后在最后
.LBB0_3:
movl -4(%rbp), %eax
addq , %rsp
popq %rbp
retq
所以,这个区域确实是预留的,用来存放函数return的值。它似乎不是非常必要,也没有在优化代码中使用,但在 -O0
模式下它就是这样工作的。
我正在尝试反汇编 clang
简单 C 程序的二进制文件(用 -O0
编译),我对生成的某条指令感到困惑。
这里有两个带有标准参数的空 main
函数,其中一个 return 的值而另一个没有:
// return_void.c
void main(int argc, char** argv)
{
}
// return_0.c
int main(int argc, char** argv)
{
return 0;
}
现在,当我拆解它们的组件时,它们看起来相当不同,但有一行我不明白:
return_void.bin:
(__TEXT,__text) section
_main:
0000000000000000 pushq %rbp
0000000000000001 movq %rsp, %rbp
0000000000000004 movl %edi, -0x4(%rbp)
0000000000000007 movq %rsi, -0x10(%rbp)
000000000000000b popq %rbp
000000000000000c retq
return_0.bin:
(__TEXT,__text) section
_main:
0000000100000f80 pushq %rbp
0000000100000f81 movq %rsp, %rbp
0000000100000f84 xorl %eax, %eax # We return with EAX, so we clean it to return 0
0000000100000f86 movl [=12=]x0, -0x4(%rbp) # What does this mean?
0000000100000f8d movl %edi, -0x8(%rbp)
0000000100000f90 movq %rsi, -0x10(%rbp)
0000000100000f94 popq %rbp
0000000100000f95 retq
它只有在我使用函数时才会生成 not void,所以我认为这可能是 return 0 的另一种方式,但是当我更改 returned 常量时,这一行完全没有改变:
// return_1.c
int main(int argc, char** argv)
{
return 1;
}
empty_return_1.bin:
(__TEXT,__text) section
_main:
0000000100000f80 pushq %rbp
0000000100000f81 movq %rsp, %rbp
0000000100000f84 movl [=13=]x1, %eax # Return value modified
0000000100000f89 movl [=13=]x0, -0x4(%rbp) # This value is not modified
0000000100000f90 movl %edi, -0x8(%rbp)
0000000100000f93 movq %rsi, -0x10(%rbp)
0000000100000f97 popq %rbp
0000000100000f98 retq
为什么会生成这条线,它的用途是什么?
movl [=10=]x0,-0x4(%rbp)
该指令将0
存储在%rbp - 4
。 clang 似乎为来自 main.
来自 clang 邮件列表:
Yes. We allocate an implicit local variable to hold the return value; return statements then just initialize the return slot and jump to the epilogue, where the slot is loaded and returned. We don't use a phi because the control flow for getting to the epilogue is not necessarily as simple as a simple branch, due to cleanups in local scopes (like C++ destructors).
Implicit return values like main's are handled with an implicit store in the prologue.
来源:http://lists.cs.uiuc.edu/pipermail/cfe-dev/2012-February/019767.html
根据标准(对于托管环境),需要 5.1.2.2.1、main
才能返回 int
结果。因此,如果违反此规定,不要指望定义的行为。
此外,main
实际上_不需要显式 return 0
;如果到达函数末尾,则隐式 returned。 (请注意,这仅适用于 main
,它也没有原型。
clang 正在为参数(寄存器 edi
和 rsi
)在堆栈上创建 space 并且出于某种原因也将值 0 放在堆栈上。我假设 clang 将您的代码编译为这样的 SSA 表示形式:
int main(int argc, char** argv)
{
int a;
a = 0;
return a;
}
这可以解释为什么要分配堆栈槽。如果 clang 也进行持续传播,这就可以解释为什么 eax
被清零而不是从 -4(%rbp)
加载。一般来说,不要过多考虑 未优化 程序集中的可疑构造。毕竟,您禁止编译器删除无用代码。
该区域的用途由以下代码揭示
int main(int argc, char** argv)
{
if (rand() == 42)
return 1;
printf("Helo World!\n");
return 0;
}
一开始是
movl [=11=], -4(%rbp)
然后早期的return看起来如下
callq rand
cmpl , %eax
jne .LBB0_2
movl , -4(%rbp)
jmp .LBB0_3
然后在最后
.LBB0_3:
movl -4(%rbp), %eax
addq , %rsp
popq %rbp
retq
所以,这个区域确实是预留的,用来存放函数return的值。它似乎不是非常必要,也没有在优化代码中使用,但在 -O0
模式下它就是这样工作的。