一个空的程序的GCC的程序集输出在x86,Win32的(GCC's assembly out

2019-08-02 04:13发布

我写空的程序惹恼了地狱的计算器编码器,而不是。 我只是在探索的GNU工具链。

现在以下可能是太深的我,但到continuie我已经开始研究C编译器的输出,东西GNU作为消耗空程序传奇。

gcc version 4.4.0 (TDM-1 mingw32)

test.c的:

int main()
{
    return 0;
}

GCC -S test.c的

    .file   "test.c"
    .def    ___main;    .scl    2;  .type   32; .endef
    .text
.globl _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    call    ___main
    movl    $0, %eax
    leave
    ret 

你能解释这里发生了什么? 这是我努力去理解它。 我已经使用as手册和我最小的x86 ASM知识:

  • .file "test.c"是逻辑文件名的指令。
  • .def :根据文档“开始定义一个符号名调试信息”。 什么是调试信息的符号(函数名/变量?)什么样的?
  • .scl :文件说“存储类型可以表明一个符号是静态的还是外部”。 这是相同的静态外部 I由C知道吗? 什么是“2”?
  • .type :存储参数“作为一个符号表项的类型属性”,我不知道。
  • .endef :没问题。
  • .text :现在,这是有问题的,这似乎是一种叫节,我已阅读,它的代码的地方,但该文档没有告诉我太多。
  • .globl “使符号LD可见。” ,该手册是对这个很清楚。
  • _main:这可能会为我的主要功能是起始地址(?)
  • pushl_ :一个长(32位)推,这使在堆栈上EBP
  • movl :32位举动。 伪C: EBP = ESP;
  • andl :逻辑和。 伪C: ESP = -16 & ESP ,我实在不明白这什么地步。
  • call :推动IP堆栈(所以被调用过程可以找到它的归途),并继续在那里__main是。 (什么是__main?)
  • movl :这个零点必须是恒定的我在我的代码的最后返回。 该MOV地方这个零到EAX。
  • leave :还原一个ENTER指令后堆栈(?)。 为什么?
  • ret :返回到保存在栈上的指令地址

谢谢您的帮助!

Answer 1:

.file “test.c的”

命令开始。 是指令来汇编。 这只是说,这是“file.c”,这些信息可以导出exe文件的调试信息。

.DEF ___main; .scl伪2; .TYPE 32; .endef伪

.DEF指令限定了调试符号。 SCL 2表示存储类2(外部存储类).TYPE 32表示,这是sumbol的函数。 这些数字将被PE-COFF EXE格式的定义

___main是一个叫做函数,它引导的是gcc的需求(它会做的事情一样运行C ++静态初始化,需要其他管家)的照顾。

.text 

开头的文字部分 - 代码住在这里。

Kglobl _man

定义_main符号作为全球性的,这将使它在连接器和该公司在连接其它模块可见。

.def        _main;  .scl    2;      .type   32;     .endef 

同样的事情_main,创建调试符号,说明_main是一个函数。 这可以被调试器使用。

_主要:

启动一个新的标签(这将最终的地址)。 上述.globl指令,使这个地址设置为向其他实体。

pushl       %ebp 

保存在堆栈上旧的帧指针(EBP寄存器)(这样它可以在适当位置时,这个函数结束被放回)

movl        %esp, %ebp 

移动堆栈指针到EBP寄存器。 EBP通常被称为帧指针,它指向在当前“帧”内的所述堆栈的所述最高(函数通常),(经由EBP指栈中的变量可以帮助调试器)

和L $ -16,ESP%

ANDS与FFFFFFF0堆栈其用途不同对准它一个16字节边界上。 访问对齐值在栈上比,如果他们不对齐快得多。 所有这些先行指令都已相当多的标准功能序幕。

call        ___main

调用___main功能,会做初始化的东西,GCC的需求。 通话将推动在堆栈上当前指令指针,并跳转到___main的地址

movl        $0, %eax 

(在返回0 0)移动0〜eax寄存器,EAX寄存器被用来保存函数返回值的stdcall调用约定。

离开

leave指令是非常速记

 movl ebp,esp popl ebp 

即它“索马里发展事务处”的功能开始做的东西 - 恢复帧指针和堆栈到以前的状态。

返回到谁调用该函数。 它会从弹出堆栈指令指针(其对应的调用指令将已经摆在那里),并跳转到那里。



Answer 2:

这里有一个概述非常类似的工作: http://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax

你已经想通了大部分 - 我就做了强调和补充附加注释。

__main是在GNU标准库中的子程序,它采取各种启动初始化工作。 这不是绝对必要的C程序,但需要公正的情况下C代码使用C ++链接。

_main是你的主要子程序。 由于两个_main__main是代码位置,他们具有相同的存储类和类型。 我还没挖出的定义.scl.type呢。 您可以通过定义几个全局变量得到一些照明。

前三个指令设置堆栈帧这是一个子程序的工作存储一个技术术语 - 大部分地方和临时变量。 推ebp保存调用者的堆栈帧的基础。 把espebp将我们的堆栈帧的基础。 的andl对准堆栈帧为16字节的边界,以防万一在堆栈上的任何局部变量需要16字节对齐(用于x86 SIMD指令要求对准,但对准并加快普通类型,如int S和float秒。

此时,您通常会想到esp在内存中得到下移分配堆栈空间的局部变量。 你的main现在没有这样的gcc不打扰。

要调用__main是特殊的主入口点,并且通常不会出现在子程序。

其余部分则作为你推测。 注册eax是把整数返回代码的二进制规范的地方。 leave撤消堆栈帧和ret返回给调用者。 在这种情况下,主叫方是低级别的C运行时将做额外的魔法(比如调用atexit()函数,设置进程的退出代码,并要求操作系统终止进程。



Answer 3:

至于说和L $ -16,ESP%

  • 32位:-16十进制等于十六进制表示,以0xfffffff0
  • 64位:-16十进制等于十六进制表示,以0xfffffffffffffff0

因此,将屏蔽掉ESP的最后4位(顺便说一句:2 ** 4等于16)和(如果目标系统是32或64位无论)将保留所有其他位。



Answer 4:

andl $-16,%esp这工作,因为低位设置为零将随时调节%esp价值下降 ,并在栈上的x86向下增长。



Answer 5:

我不知道所有的答案,但我可以解释我所知道的。

ebp使用由函数来存储的初始状态esp其流动过程中,到了那里被传递给功能的参数,并且其中一个参考是它自己的局部变量。 功能做的第一件事是保存在给定的状态ebppushl %ebp ,它是使呼叫的功能是至关重要的,而不是其自己的当前堆栈中的位置将其替换espmovl %esp, %ebp 。 归零的最后4位ebp在这一点上是GCC的特殊,我不知道为什么这个编译器做到这一点。 它的工作没有做。 现在,我们终于进入业务, call ___main ,谁是__main? 我也不知道......也许多个GCC具体的程序,最后的唯一的事情你的main()那样,设置返回值为0与movl $0, %eaxleave是一样的做movl %ebp, %esp; popl %ebp movl %ebp, %esp; popl %ebp恢复ebp状态,然后ret完成。 ret弹出eip ,并继续从该点线流动,无论它是(作为其主要的(),这可能沤导致其处理程序结束一些内核程序)。

大部分是所有关于管理堆栈。 我写了一篇关于如何叠前一段时间使用,这将是有益的解释为什么所有这些事情都做了详细的教程。 但其在葡萄牙...



文章来源: GCC's assembly output of an empty program on x86, win32