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 ++静态初始化器和其他所需的内务处理之类的事情).
Run Code Online (Sandbox Code Playgroud).text
开始一个文本部分 - 代码就在这里.
.globl _main
将_main符号定义为global,这将使链接器和链接的其他模块可见.
Run Code Online (Sandbox Code Playgroud).def _main; .scl 2; .type 32; .endef
与_main相同,创建调试符号,声明_main是一个函数.这可以由调试器使用.
_主要:
开始一个新标签(它最终会有一个地址).上面的.globl指令使该地址对其他实体可见.
Run Code Online (Sandbox Code Playgroud)pushl %ebp
将旧帧指针(ebp寄存器)保存在堆栈上(因此当此函数结束时可以将其放回原位)
Run Code Online (Sandbox Code Playgroud)movl %esp, %ebp
将堆栈指针移动到ebp寄存器.ebp通常被称为帧指针,它指向当前"帧"(通常是函数)内堆栈值的顶部,(通过ebp引用堆栈上的变量可以帮助调试器)
andl $ -16,%esp
使用fffffff0对堆栈进行修改,有效地将其与16字节边界对齐.访问堆栈上的对齐值比未对齐时快得多.所有这些前面的说明几乎都是标准的功能序言.
call ___main
Run Code Online (Sandbox Code Playgroud)
调用___main函数,它将初始化gcc需要的东西.调用将推送堆栈上的当前指令指针并跳转到___main的地址
Run Code Online (Sandbox Code Playgroud)movl $0, %eax
将0移至eax寄存器(0返回0;)eax寄存器用于保存stdcall调用约定的函数返回值.
离开
离开指令非常简短
Run Code Online (Sandbox Code Playgroud)movl ebp,esp popl ebp
即它"取消"在函数开始时完成的操作 - 将帧指针和堆栈恢复到其以前的状态.
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__函数,设置进程的退出代码并要求操作系统终止进程).
关于那个andl $ -16,%esp
因此它将屏蔽ESP的最后4位(btw:2**4等于16)并保留所有其他位(无论目标系统是32位还是64位).
| 归档时间: |
|
| 查看次数: |
5594 次 |
| 最近记录: |