GCC在x86,win32上的空程序的汇编输出

Geo*_*rge 55 c x86 gcc gnu-assembler tdm-mingw

我编写空程序来惹恼stackoverflow程序员的地狱,不是.我正在探索gnu工具链.

现在以下对我来说可能太深了,但是为了继续执行空程序传奇,我已经开始检查C编译器的输出,GNU作为消耗的东西.

gcc version 4.4.0 (TDM-1 mingw32)
Run Code Online (Sandbox Code Playgroud)

test.c的:

int main()
{
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

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 
Run Code Online (Sandbox Code Playgroud)

你能解释一下这里发生的事吗?这是我努力理解它.我使用了as手册和我的最小x86 ASM知识:

  • .file "test.c" 是逻辑文件名的指令.
  • .def:根据文档"开始定义符号名称的调试信息".什么是符号(函数名称/变量?)以及什么样的调试信息?
  • .scl:docs说"存储类可以标记符号是静态的还是外部的".这是我从C中知道的静态外部吗?什么是'2'?
  • .type:存储参数"作为符号表条目的类型属性",我不知道.
  • .endef: 没问题.
  • .text:现在这是有问题的,它似乎是一个叫做section的东西,我已经读过它的代码所在,但是文档并没有告诉我太多.
  • .globl "使符号对ld可见." ,手册很清楚.
  • _main: 这可能是我的main函数的起始地址(?)
  • pushl_:长(32位)推送,将EBP放在堆栈上
  • movl:32位移动.伪C:EBP = ESP;
  • andl:逻辑和.伪C:ESP = -16 & ESP我真的不明白这是什么意思.
  • call:将IP推送到堆栈(因此被调用的过程可以找到它的方式)并继续在哪里__main.(什么是__main?)
  • movl:这个零必须是我在代码末尾返回的常量.MOV将此零置于EAX中.
  • leave:在ENTER指令(?)之后恢复堆栈.为什么?
  • ret:返回保存在堆栈中的指令地址

谢谢您的帮助!

nos*_*nos 56

.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
Run Code Online (Sandbox Code Playgroud)

开始一个文本部分 - 代码就在这里.

.globl _main

将_main符号定义为global,这将使链接器和链接的其他模块可见.

.def        _main;  .scl    2;      .type   32;     .endef
Run Code Online (Sandbox Code Playgroud)

与_main相同,创建调试符号,声明_main是一个函数.这可以由调试器使用.

_主要:

开始一个新标签(它最终会有一个地址).上面的.globl指令使该地址对其他实体可见.

pushl       %ebp
Run Code Online (Sandbox Code Playgroud)

将旧帧指针(ebp寄存器)保存在堆栈上(因此当此函数结束时可以将其放回原位)

movl        %esp, %ebp
Run Code Online (Sandbox Code Playgroud)

将堆栈指针移动到ebp寄存器.ebp通常被称为帧指针,它指向当前"帧"(通常是函数)内堆栈值的顶部,(通过ebp引用堆栈上的变量可以帮助调试器)

andl $ -16,%esp

使用fffffff0对堆栈进行修改,有效地将其与16字节边界对齐.访问堆栈上的对齐值比未对齐时快得多.所有这些前面的说明几乎都是标准的功能序言.

call        ___main
Run Code Online (Sandbox Code Playgroud)

调用___main函数,它将初始化gcc需要的东西.调用将推送堆栈上的当前指令指针并跳转到___main的地址

movl        $0, %eax
Run Code Online (Sandbox Code Playgroud)

将0移至eax寄存器(0返回0;)eax寄存器用于保存stdcall调用约定的函数返回值.

离开

离开指令非常简短

movl     ebp,esp
popl     ebp
Run Code Online (Sandbox Code Playgroud)

即它"取消"在函数开始时完成的操作 - 将帧指针和堆栈恢复到其以前的状态.

RET

返回调用此函数的人.它将从堆栈中弹出指令指针(相应的调用指令将放置在那里)并跳转到那里.


Geo*_*ips 12

这里概述了一个非常类似的练习:http: //en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax

你已经弄清楚了大部分内容 - 我只会为重点和补充做些补充说明.

__CODE__是GNU标准库中的一个子程序,它负责各种启动初始化.它对于C程序来说并不是绝对必要的,但是在C代码与C++链接的情况下是必需的.

__CODE__是你的主要子程序.由于这两个__CODE____CODE__的代码位置,他们具有相同的存储类和类型.我还没挖出了定义,__CODE____CODE__还.您可以通过定义一些全局变量来获得一些照明.

前三个指令是设置堆栈帧,这是子程序的工作存储的技术术语 - 大部分是本地和临时变量.推送会__CODE__保存呼叫者堆栈帧的基础.把__CODE____CODE__组我们的堆栈帧的基础.所述__CODE__对准堆栈帧为16字节的边界,以防万一在堆栈上的任何局部变量需要16字节对齐(用于x86 SIMD指令要求对准,但对准确实加快普通类型,如__CODE__S和__CODE__秒.

此时,您通常希望__CODE__在内存中向下移动以为局部变量分配堆栈空间.你__CODE__没有,所以gcc不打扰.

__CODE__对主入口点的调用是特殊的,通常不会出现在子例程中.

其余的就像你猜测的那样.Register __CODE__是在二进制规范中放置整数返回码的地方. __CODE__取消堆栈帧并__CODE__返回给调用者.在这种情况下,调用者是低级C运行时,它将执行额外的魔术(如调用__CODE__函数,设置进程的退出代码并要求操作系统终止进程).


Rob*_*ert 5

关于那个andl $ -16,%esp

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

因此它将屏蔽ESP的最后4位(btw:2**4等于16)并保留所有其他位(无论目标系统是32位还是64位).