“.LC0”标记的内存位于应用程序的静态分配区域,这个区域在程序运行后即被分配,即“hello,world”作为一个C语言字符串常量被安排在静态分配区域。
还有一个非常重要内存分配区域就是堆栈,,堆栈是特殊的内存区域,用于程序中函数传递参数、数据的临时存取,通常是应用程序内存范围的结尾位置的内存区域,为了方便堆栈数据的存取,有一个堆栈指针(栈顶指针)指向堆栈中的下个内存位置,这意味着如何仅依靠栈顶指针不采用任意偏移地址机制(偏移地址可以以基地址为中心进行调整,比如说访问某个变量,该变量的基地址为0x400,偏移地址为0x16,则该变量的最终地址为0x416),则只能按照先进后出的顺序来访问堆栈内部存储的变量。
在汇编语言中将数据放入堆栈中,使用pushl助记符,而将数据从堆栈中弹出,使用popl助记符,每次对堆栈数据的放入与弹出都会导致栈顶指针的变化,因为栈顶指针永远指向堆栈中下一个可用的地址。下面这段汇编完成了将10压入堆栈,然后将10弹出到ebx寄存器中的过程。
pushl $10
popl %ebx
麦好的AI乐园博客所有内容是原创,如果转载请注明来源
http://blog.csdn.net/myhaspl/
(2)C程序执行
C语言的源代码被翻译成若干行汇编代码,由几个简单的指令组成的汇编代码生成二进制文件,执行这个二进制文件,完成了helloworld的执行。
汇编语言中用的较多助记符是movl、addl、subl
movl完成数据的复制,而addl完成数据的加法,subl完成数据的减法。
这3个助记符的语法格式是:
助记符 源数据 目标数据
比如,对于这段静态分配变量的汇编代码:
myvalue:
.long 190
mess:
.ascii “hello”
通过addl与movl可以完成将myvalue指示的long类型变量190加100,然后减20的功能。
movl myvalue,%ebx
addl $100,%ebx
subl $20,%ebx
movl %ebx,myvalue
汇编语言的代码放在了.text段中,分析上面的helloworld的反汇编代码中一段:
.text
.p2align 4,,15
.globl main
.type main, @function
globl 命令指定了main函数为入口函数(程序启动时执行的函数),然后接着在后面定义了main函数的组成:
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $4, %esp
movl $.LC0, (%esp)
call puts
movl $0, %eax
addl $4, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.2.1 20070831 patched [FreeBSD]"
.section .note.GNU-stack,"",@progbits
观察这些汇编代码,里面充斥着pushl、popl、movl、subl与addl等助记符,C程序最终就是通过复制、入栈、出栈、加法、减法等简单操作来完成执行的。注意观察这些代码中的如下几行:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $4, %esp
movl $.LC0, (%esp)
call puts
C语句的print(“helloworld”)输出字符串就是通过上述几行实现的,除开最后一行call puts(call指令完成调用C语言的puts函数输出字符串的功能,puts函数向终端输出一个字符串,其唯一的参数是char *str,str表示需要输出的字符串)外,其它行做的所有工作就是将调用puts函数的唯一参数(指向字符串”helloworld”地址的标示“.LC0”)的放入堆栈中,以供puts函数调用,倒数第二行将.LC0标记的地址复制到当前堆栈的栈顶,前面几行分配堆栈,调整栈顶指针,将需要保存的寄存器入栈(因为调用puts函数会破坏现有寄存器的值,称之为保存现场),当puts函数完成后,会将入栈的寄存器值弹回各自的寄存器中(称之为恢复现场)。