pop %esp和push %esp的研究

本文探讨了在x86-32架构下,pop %esp和push %esp指令在用户栈和内核栈跳转中的作用。pop %esp实现栈顶跳转,而push %esp则保存栈顶指针。同时,文章通过汇编代码示例验证了这两个指令的行为,并介绍了pusha和popa指令的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近看操作系统的代码,发现了利用pop %esp进行用户栈和内核栈之间的跳转,感觉挺有意思的,就研究了一下。其实这个问题本来不复杂,指令的设计方式蛮合理的,但是看了一下Intel的手册关于pop和push的描述反而把我弄晕了。
最后又仔细看了一下关于pop %esp和push %ebp的描述才明白,其实就是我一开始没看手册的时候想的那样。。。把intel手册上面对普通的pop和push的描述往push %esp和pop %esp上面套是我被弄晕的根本原因。(下面的叙述都基于x86-32)

  • 普通的popl DEST:
    DEST ← SS:ESP;
    ESP ← ESP + 4

  • 普通的pushl SRC
    ESP ← ESP – 4;
    Memory[SS:ESP] ← SRC

上面两个规则非常显然,根本不用解释,但是当你把DEST和SRC换成esp的时候,规则其实是不成立的。关于操作数是esp的情况,Intel手册上有特殊说明:
The POP ESP instruction increments the stack pointer (ESP) before data at the old top of stack is written into the destination.
The PUSH ESP instruction pushes the value of the ESP register as it existed before the instruction was executed. If a PUSH instruction uses a memory operand in which the ESP register is used for computing the operand address, the address of the operand is computed before the ESP register is decremented.
英文不好我就不翻译了,我觉得可以简单理解如下:
popl %esp=movl (%esp),%esp
pushl %esp=subl $4,%esp; leal 4(%esp),%eax; movl %eax,(%esp)**
这样一来,pop esp实现的是栈顶的跳转,push esp则相当于把新的栈顶变成了指向原来栈顶的指针。

另外还可以提一下pusha和popl指令,这两个指令分别把8个通用寄存器依次压栈和弹栈。其中特殊的还是esp寄存器。
对于pusha指令,相当于做了如下操作:
Temp ← (ESP);
Push(EAX);
Push(ECX);
Push(EDX);
Push(EBX);
Push(Temp);
Push(EBP);
Push(ESI);
Push(EDI);
需要注意的是轮到esp时,被压入的是pusha指令之前的栈顶地址。
对于popa指令,相当于做了如下操作:
EDI ← Pop();
ESI ← Pop();
EBP ← Pop();
Increment ESP by 4; (* Skip next 4 bytes of stack *)
EBX ← Pop();
EDX ← Pop();
ECX ← Pop();
EAX ← Pop();
需要注意的是轮到esp时,只是单纯的栈顶+4,对应内存里的数值并不会被弹出到esp中,否则地址会跳转。

下面写一段测试代码(AT&T汇编)验证一下:
64位环境下汇编和链接命令如下:
as -o push_pop_test.o push_pop_

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值