linux平台学x86汇编(七):堆栈的使用

【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途】

        堆栈是内存中用于存放数据的专门保留的区域,该区域的数据存放和删除方式比较特殊。一般内存区域数据元素按照连续的方式存放到数据段,在数据段中最低内存开始存放,然后向更高的内存位置依次存放。而堆栈保留在内存区域的末尾位置,并且在当数据存放在堆栈中时,它向下增长。程序运行时使用的任何命令行参数都被送入堆栈中,并且堆栈指针被设置为指向数据元素的底部。

        当每个数据被添加到堆栈数据区域中时,使用一个指针跟踪堆栈的开始位置。寄存器esp包含堆栈起始位置的内存地址,所以在编写程序时,不允许把esp寄存器用作其它用途,如果堆栈起始位置丢失了,程序运行将出现意想不到的结果。在编写汇编语言时,主要工作时跟踪堆栈中的数据,不需要手动去设置堆栈指针,IA-32指令集包含指令用于访问堆栈数据的指令。
        把新的数据放到堆栈中称为入栈,其对应指令为push,格式为:
pushx source
x是一个字符,表示数据长度,类似mov指令,但只能是l(32位)和w(16位)。source为push操作的数据元素,可以是16位或32位寄存器、16或32位内存值、16位段寄存器、8位或16位或32位立即数值。
        从堆栈中获取数据使用pop命令,其格式为:
popx dest
x表示数据长度,dest为接受数据的目标,可以是16位或32为寄存器、16或32为内存位置,16位段寄存器。
如下一个示例可以帮助理解堆栈是如何工作的:
#pushpop.s
.section .data
data:
    .int 111

.section .text
.globl _start
_start:
    nop
    movl $91, %eax
    movw $45, %bx
    movb $22, %cl

    pushl %eax
    pushw %bx
    pushl %ecx
    pushl data
    pushl $data

    popl %edx
    popl %edx
    popl %edx
    popw %dx
    popl %edx

    movl $0, %ebx
    movl $1, %eax

    int $0x80
使用上一节Makefile编译链接程序,在调试器中运行该程序,并且监视esp寄存器的值。
(gdb) b _start
Breakpoint 1 at 0x8048074: file pushpop.s, line 9.
(gdb) r
Starting program: /home/allen/as/3_pushpop/pushpop

Breakpoint 1, _start () at pushpop.s:9
9    nop
(gdb) info registers
eax            0x0 0
ecx            0x0 0
edx            0x0 0
ebx            0x0 0
esp            0xbffff3c0 0xbffff3c0
ebp            0x0 0x0
esi            0x0 0
edi            0x0 0
eip            0x8048074 0x8048074 <_start>
eflags         0x212 [ AF IF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x0 0
(gdb) 
然后单步执行,查看寄存器值:
(gdb) print/x $esp
$3 = 0xbffff3c0
(gdb) s
12    movb $22, %cl
(gdb) print/x $esp
$4 = 0xbffff3c0
(gdb) s
14    pushl %eax
(gdb) print/x $esp
$5 = 0xbffff3c0
(gdb) s
15    pushw %bx
(gdb) print/x $esp
$6 = 0xbffff3bc
(gdb) s
16    pushl %ecx
(gdb) print/x $esp
$7 = 0xbffff3ba
(gdb) s
17    pushl data
(gdb) print/x $esp
$8 = 0xbffff3b6
(gdb) s
18    pushl $data
(gdb) print/x $esp
$9 = 0xbffff3b2
(gdb)
可以发现esp指针移动了,没push一次,esp寄存器递减了,指向新的堆栈起始位置。这说明堆栈在内存中确实向下扩展了。
执行完所有pop命令:
(gdb) s
24    popl %edx
(gdb) s
26    movl $0, %ebx
(gdb) print/x $esp
$12 = 0xbffff3c0
(gdb)
发现在所有数据出栈之后,堆栈其实位置恢复到之前数据进栈的地址了。pop命令每弹出一个数据,esp寄存器递增,显示堆栈长度在内存中向上扩展了。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。