理解空main()的转换为汇编

unj*_*nj2 15 c compiler-construction assembly gcc

有人可以解释GCC正在为这段代码做些什么吗?什么是初始化?原始代码是:

#include <stdio.h>
int main()
{

}
Run Code Online (Sandbox Code Playgroud)

它被翻译成:

    .file   "test1.c"
    .def    ___main;    .scl    2;  .type   32; .endef
    .text
.globl _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $8, %esp
    andl    $-16, %esp
    movl    $0, %eax
    addl    $15, %eax
    addl    $15, %eax
    shrl    $4, %eax
    sall    $4, %eax
    movl    %eax, -4(%ebp)
    movl    -4(%ebp), %eax
    call    __alloca
    call    ___main
    leave
    ret
Run Code Online (Sandbox Code Playgroud)

如果编译器/汇编大师通过解释堆栈,寄存器和段初始化来启动我,我将不胜感激.我无法从代码中删除头部或尾部.

编辑:我正在使用gcc 3.4.5.命令行参数是gcc -S test1.c

谢谢,kunjaan.

Eth*_*man 14

我应该在我的所有评论前面说,我仍在学习.

我会忽略部分初始化.关于部分初始化的解释以及基本上我覆盖的其他所有内容都可以在这里找到:http: //en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax

ebp 寄存器堆栈帧基指针,因此是BP.它存储指向当前堆栈开头的指针.

esp寄存器是堆栈指针.它保存堆栈顶部的内存位置.每当我们在堆栈上推送某些内容时,esp就会更新,以便始终指向堆栈顶部的地址.

所以ebp指向基础,esp指向顶部.所以堆栈看起来像:

esp -----> 000a3   fa
           000a4   21
           000a5   66
           000a6   23
esb -----> 000a7   54
Run Code Online (Sandbox Code Playgroud)

如果你在堆栈上推e4,这就是:

esp -----> 000a2   e4
           000a3   fa
           000a4   21
           000a5   66
           000a6   23
esb -----> 000a7   54
Run Code Online (Sandbox Code Playgroud)

请注意,堆栈会向较低的地址增长,这一点在下面很重要.

前两个步骤称为过程序言或更常见的函数序言,它们准备堆栈以供局部变量使用.请参阅底部的程序prolog引用.

在步骤1中,我们通过调用pushl%ebp将指针保存到堆栈上的旧堆栈帧.由于main是第一个被调用的函数,我不知道%ebp的前一个值是什么.

第2步,我们正在输入一个新的堆栈帧,因为我们正在输入一个新函数(main).因此,我们必须设置一个新的堆栈帧基指针.我们使用esp中的值作为堆栈帧的开头.

步骤3.在堆栈上分配8个字节的空间.正如我们上面提到的,堆栈向低地址增长,因此,减去8,将堆栈顶部移动8个字节.

第4步; 分配堆栈,我发现了不同的意见.我真的不确定这是做什么的.我怀疑这样做是为了允许在堆栈上分配大指令(SIMD),

http://gcc.gnu.org/ml/gcc/2008-01/msg00282.html

此代码"和"ESP与0xFFFF0000,使堆栈与下一个最低16字节边界对齐.对Mingw源代码的检查表明,这可能是出现在"_main"例程中的SIMD指令,它只在对齐的地址上运行.由于我们的例程不包含SIMD指令,因此不需要此行.

http://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax

步骤5到11似乎对我没有任何意义.我在谷歌上找不到任何解释.真正了解这些东西的人能否提供更深入的了解.我听说有传言说这些东西用于C的异常处理.

步骤5,将主0的返回值存储在eax中.

步骤6和7我们在未知原因的情况下将15 in hex添加到eax中.eax = 01111 + 01111 = 11110

步骤8我们将eax 4位向右移位.eax = 00001因为最后一位是00001 |的结束 111.

步骤9我们将eax 4位向左移位,eax = 10000.

步骤10和11将堆栈中前4个分配字节中的值移动到eax中,然后将其从eax移回.

步骤12和13设置c库.

我们已经达到了功能结局.也就是说,函数的一部分将堆栈指针esp和ebp返回到调用此函数之前的状态.

步骤14,将esp设置为ebp的值,将堆栈顶部移动到main调用之前的地址.然后它将ebp设置为指向我们在步骤1中保存在堆栈顶部的地址.

可以使用以下说明替换休假:

mov  %ebp, %esp
pop  %ebp
Run Code Online (Sandbox Code Playgroud)

步骤15,返回并退出该功能.

1.    pushl       %ebp
2.    movl        %esp, %ebp
3.    subl        $8, %esp
4.    andl        $-16, %esp
5.    movl        $0, %eax
6.    addl        $15, %eax
7.    addl        $15, %eax
8.    shrl        $4, %eax
9.    sall        $4, %eax
10.   movl        %eax, -4(%ebp)
11.   movl        -4(%ebp), %eax
12.   call        __alloca
13.   call        ___main
14.   leave
15.   ret
Run Code Online (Sandbox Code Playgroud)

程序Prolog:

函数必须做的第一件事就是程序prolog.它首先用指令pushl%ebp保存当前的基指针(ebp)(记住ebp是用于访问函数参数和局部变量的寄存器).现在它使用指令movl%esp,%ebp将堆栈指针(esp)复制到基指针(ebp).这允许您从基指针访问函数参数作为索引.局部变量总是从ebp中减去,例如-4(%ebp)或(%ebp)-4对于第一个局部变量,返回值始终为4(%ebp)或(%ebp)+4,每个参数或参数为N*4 + 4(%ebp),例如第一个参数为8(%ebp),而旧ebp为(%ebp).

http://www.milw0rm.com/papers/52

存在一个非常好的堆栈溢出线程,它回答了大部分问题. 为什么我的gcc输出中有额外的说明?

有关x86机器代码指令的详细参考,请访问:http: //programminggroundup.blogspot.com/2007/01/appendix-b-common-x86-instructions.html

这个讲座包含以下使用的一些想法:http: //csc.colstate.edu/bosworth/cpsc5155/Y2006_TheFall/MySlides/CPSC5155_L23.htm

以下是回答您的问题的另一种看法:http: //www.phiral.net/linuxasmone.htm

这些来源都没有解释一切.


Cha*_*lie 9

这是main()GCC编译的一个简单函数的一个很好的逐步细分,包含大量详细信息:GAS语法(维基百科)

对于您粘贴的代码,说明细分如下:

  • 前四条指令(pushl through andl):设置一个新的堆栈帧
  • 接下来的五条指令(movl to sall):为eax生成一个奇怪的值,它将成为返回值(我不知道它是如何决定这样做的)
  • 接下来的两条指令(均为movl):将计算出的返回值存储在堆栈上的临时变量中
  • 接下来的两条指令(都调用):调用C库的init函数
  • leave 说明:撕下堆栈框架
  • ret 指令:返回调用者(外部运行时函数,或者调用程序的内核函数)