下面是具体步骤:
.file "test.c"
原始源文件名称(用于调试器)。
.section .rodata
.LC0:
.string "Hello world!"
在“.rodata”段中包含了一个以零结尾的字符串(“ro”代表“只读”:应用程序可以读取数据,但对它进行任何写入尝试都会触发异常)。
.text
现在,我们将东西写入“ .text”部分,这是代码所在的位置。
.globl main
.type main, @function
main:
我们定义了一个名为“main”的函数,并使其在全局可见(其他对象文件将能够调用它)。
leal 4(%esp), %ecx
我们将值4+%esp
(其中%esp
是栈指针)存储在寄存器%ecx
中。
andl $-16, %esp
为了使内存访问地址是16的倍数,%esp
被轻微修改。 对于一些数据类型(对应于C语言中的double
和long double
浮点格式),当内存访问在16的倍数地址上时,性能更好。 这在这里并不是真正必要的,但如果没有使用优化标志(例如-O2
...),编译器往往会产生相当多的通用无用代码(即在某些情况下可能有用但在这里不是)。
pushl -4(%ecx)
这个有点奇怪:此时,地址-4(%ecx)
处的单词是在andl
操作之前位于堆栈顶部的单词。 代码检索该单词(顺便说一句,它应该是返回地址),并再次将其推送。 这样有点类似于从具有16字节对齐堆栈的函数调用获取的内容。 我猜这个push
是参数复制序列的遗留物。 由于函数已经调整了堆栈指针,因此必须复制函数参数,这些参数可以通过旧的堆栈指针值访问。 在这里,除了函数返回地址外,没有其他参数。 请注意,此单词不会被使用(再次强调,这是没有进行优化的代码)。
pushl %ebp
movl %esp, %ebp
这是标准的函数序言:我们保存%ebp
(因为我们即将修改它),然后设置%ebp
指向堆栈帧。此后,%ebp
将用于访问函数参数,使%esp
再次可用。(是的,没有参数,所以对该函数无用。)
pushl %ecx
我们保存了 %ecx
寄存器(在函数退出时我们需要它,以恢复 andl
执行之前的 %esp
值)。
subl $20, %esp
我们在栈上保留了32个字节(记住栈是向下增长的)。这些空间将用于存储printf()
函数的参数(这是过度设计,因为只有一个参数,它将使用4个字节[即指针大小])。
movl $.LC0, (%esp)
call printf
我们“推送”参数给 printf()
函数(即确保 %esp
指向一个包含参数的单词,这里是常量字符串在 rodata 部分中的地址 $.LC0
)。然后我们调用 printf()
函数。
addl $20, %esp
printf()
返回时,我们会释放为参数分配的空间。这个 addl
与上面的 subl
相互抵消。
popl %ecx
我们恢复了%ecx
寄存器(它被压在上面);printf()
可能已经修改了它(调用约定描述函数可以在退出时不还原哪些寄存器;%ecx
是其中一个寄存器)。
popl %ebp
函数结尾:这将恢复%ebp
(对应于上面的pushl %ebp
)。
leal -4(%ecx), %esp
我们将%esp
恢复到其初始值。该操作码的效果是将值%ecx-4
存储在%esp
中。%ecx
在第一个函数操作码中设置。这将取消对%esp
的任何更改,包括andl
。
ret
函数退出。
.size main, .-main
这将设置main()
函数的大小:在汇编过程中的任何时刻,“.
”是“我们当前添加内容的地址”的别名。如果在此处添加了另一条指令,它将放在由“.
”指定的地址上。因此,在此处,“.-main
”是函数main()
代码的确切大小。 .size
指令指示汇编器将该信息写入对象文件。
.ident "GCC: (GNU) 4.3.0 20080428 (Red Hat 4.3.0-8)"
GCC喜欢留下其操作的痕迹。这个字符串最终会成为目标文件中的一种注释。连接器将删除它。
.section .note.GNU-stack,"",@progbits
一段特殊的代码区域,在这里GCC写入的代码可以适用于一个非可执行堆栈。这是正常情况。可执行堆栈只在某些特殊用途中需要(不是标准C语言)。在现代处理器上,内核可以创建一个非可执行堆栈(堆栈上的数据如果被尝试作为代码执行将触发异常)。对于某些人来说,这被视为“安全功能”,因为把代码放在堆栈上是利用缓冲区溢出的常见方式。通过这个代码区域,可执行文件将被标记为“兼容非可执行堆栈”,内核会愉快地提供这样的服务。
subl $20, %esp
我们在堆栈上保留了32字节(请记住,堆栈向“下”增长)。 难道不应该是20字节而不是32字节吗? - Ragtime