Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 为什么在使用返回值时将0移动到堆栈中?_C_Assembly_Clang_Disassembly_Calling Convention - Fatal编程技术网

C 为什么在使用返回值时将0移动到堆栈中?

C 为什么在使用返回值时将0移动到堆栈中?,c,assembly,clang,disassembly,calling-convention,C,Assembly,Clang,Disassembly,Calling Convention,我正在试验如何分解简单C程序(用-O0编译)的clang二进制文件,我对生成的某条指令感到困惑 以下是两个带有标准参数的空main函数,其中一个返回值,另一个不返回值: // return_void.c void main(int argc, char** argv) { } // return_0.c int main(int argc, char** argv) { return 0; } 现在,当我拆卸它们的总成时,它们看起来相当不同,但有一行我不明白: return_void

我正在试验如何分解简单C程序(用
-O0
编译)的
clang
二进制文件,我对生成的某条指令感到困惑

以下是两个带有标准参数的空
main
函数,其中一个返回值,另一个不返回值:

// 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    $0x0, -0x4(%rbp)    # What does this mean?
0000000100000f8d    movl    %edi, -0x8(%rbp)
0000000100000f90    movq    %rsi, -0x10(%rbp)
0000000100000f94    popq    %rbp
0000000100000f95    retq
它只在我使用函数is not void时生成,因此我认为这可能是返回0的另一种方式,但当我更改返回的常量时,这一行根本没有更改:

// 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    $0x1, %eax           # Return value modified
0000000100000f89    movl    $0x0, -0x4(%rbp)    # This value is not modified
0000000100000f90    movl    %edi, -0x8(%rbp)
0000000100000f93    movq    %rsi, -0x10(%rbp)
0000000100000f97    popq    %rbp
0000000100000f98    retq
为什么会产生这一行?它的目的是什么

movl   $0x0,-0x4(%rbp)
此指令将
0
存储在
%rbp-4
中。似乎clang为main的隐式返回值分配了一个隐藏的局部变量

从clang邮件列表中:

对。我们分配一个隐式局部变量来保存返回值; return语句然后只初始化返回槽并跳转到 尾声,加载并返回插槽。我们不使用phi 因为到达尾声的控制流不是 由于本地的清理,必须像简单的分支一样简单 作用域(如C++析构函数) 隐式返回值(如main)由隐式存储处理 在序言中


来源:

根据标准(对于托管环境),
main
需要重新输入
int
结果。因此,如果违反这一点,不要期望定义的行为


此外,
main
实际上不需要显式返回
0
;如果到达函数的末尾,则隐式返回。(注意这仅适用于
main
,它也没有原型。

clang在堆栈上为参数(寄存器
edi
rsi
)留出空间,出于某种原因,也将值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    $0, -4(%rbp)
那么,早期返回如下所示

callq   rand
cmpl    $42, %eax
jne .LBB0_2
movl    $1, -4(%rbp)
jmp .LBB0_3
然后到了最后,它确实做到了

.LBB0_3:
movl    -4(%rbp), %eax
addq    $32, %rsp
popq    %rbp
retq

因此,这个区域确实是为存储函数返回值而保留的。它似乎不是非常必要,也不是在优化代码中使用的,而是在
-O0
模式下使用的。您是否用普通函数(即未调用main的函数)测试过这个问题?
main
没有
int
返回类型是一个错误违反托管环境的标准。不要期望有任何特定行为。您禁止clang删除无用代码(使用
-O0
)。你为什么要询问程序集中的奇怪代码?
eax
确实有返回值,但不确定你是从哪里想到要将其存储在堆栈上的,尤其是在被调用方的框架中,调用方获得检查返回值的控件时,被调用方的框架将被破坏。你似乎是那个将其与返回地址混淆的人。Beside的事实是,无论什么系统,这都是32位ABI,而OP显然使用64位系统,返回值存储在
EAX/RAX
中。您正在混淆:),也许通过名称返回地址,OP问题确实是一个很好的问题。你们都是对的,我很抱歉我犯了一个错误,我正在更新答案。感谢you@GiuseppePes:在x86平台上,随着堆栈的增长,堆栈指针向下移动(到较低的内存地址)。这意味着返回地址始终存储在函数启动时与
rsp
值的正偏移量处<代码>-0x4(%rbp)在这种情况下不可能是返回地址。很高兴您正在尝试改进您的答案,但事实是它与此无关。OP清楚地知道堆栈和函数调用是如何工作的,它只是想知道(和我一样)为什么在体中有0的移动。如果你知道答案,我认为最好删除它并写一个新的:)这与问题有什么关系?@fuzzxl:在讨论生成的代码时,签名
main
不符合标准与讨论该代码的问题无关?啊,是的,我错过了OP使用的伪返回类型。