在x86程序集中,ESP在调用之后递减两次,然后在数据保存到堆栈之前推送?

s0s*_*s0s 2 x86 assembly stack push call

简而言之,我正在研究Singh和Triebel的一本名为"8088和8086微处理器"的书,以学习那些特定CPU的组件.现在,我正在练习的计算机是我最近构建的主计算机,因此寄存器更大.

也就是说,这本书(我觉得非常有用)说调用标签操作数会导致调用后的指令地址被放在堆栈上,然后THEN SP递减2(ESP并在我的CPU上递减4) .在我正在研究的一些代码中,一个调用操作数后面紧跟一个push.当CPU遇到a时push,书中说明SP会减2(再次,ESP在我的CPU上减4).

; ESP=0xffffd840 right now
call iprint
mov eax, 0Ah

iprint:
push eax ; say eax contains 1
Run Code Online (Sandbox Code Playgroud)

现在,ESP=0xffffd840在通话前说.地址EIP保存在堆栈中(CALL操作数后面的指令地址).然后ESP减少4.此时,ESP=0xffffd83c.然后遇到推送操作数.按照本书所说的,首先递减堆栈指针,然后将寄存器的内容压入堆栈.所以现在ESP=0xffffd838,1被推入堆栈.

If it helps:
Stack addr  Contents
********** ********
0xffffd840  address of mov eax, 0Ah
0xffffd83c  ?
0xffffd838  1
Run Code Online (Sandbox Code Playgroud)

现在,我的问题是,0xffffd83c跳过了吗?根据这本书,ESP在保存下一条指令之后递减call,然后在数据从堆栈中放入之前push,它再次递减.

我一直在调试类似的情况一段时间,密切注意寄存器的值,但我无法判断调试器是否符合本书所说的内容(在执行操作之前或之后递减).

这是因为在某些情况下,RET在子程序之后给出一个参数,导致堆栈指针递增?如果堆栈指针在放入数据之前确实递减了两次,这是我能看到的唯一原因.

如果我有这个错误,有人可以确认或解释一下吗?

谢谢

Ped*_*d7g 5

call <address>就像:push eip jmp <address>,所以在你的情况下,如果esp0xffffd840提前call,下一条指令的返回地址被推送到0xffffd83c(因为伪" push eip"将首先递减esp以创建新的堆栈顶部,然后它将存储当前值的eip那里(顺便说一下,eip已经指向下一条指令,因为fetch + decode指令阶段call已经完成,所以它实际上是需要的值ret).

您也可以在调试器中查看内存.而"堆叠"只是普通的记忆.因此,如果你有esp相同的0xffffd840,你可以打开例如内存视图0xffffd824,你将看到32字节的堆栈内存,28字节未使用,最后4字节是当前"堆栈顶部".

我在任何地方都使用4个字节的组,因为这是在32b保护模式下CPU"字"的本机大小(dword在x86术语中,word仅为16位).IIRC你仍然可以强制CPU执行push ax或使用sub/add esp,immediate甚至通过单字节移动它,但通常它涉及性能损失,并且在64b模式下几个调用约定甚至需要16字节对齐,所以我建议坚持+ -4 esp操作在32b模式下.

但是如果你的书大概是8086,你可能想用来dosbox模拟旧的DOS 16位环境,以便在开始时为你节省一些特定于平台的问题.虽然你可能应该为你的操作系统找到一些32/64位的最新书,因为x86上的32b保护模式更容易学习(只有图形输出不像DOS时代那样简单,但如果你愿意的话将你的asm文件与C++"loader"混合,例如将一些窗口表面初始化为ARGB内存数组,你可以将该指针传递给asm例程并用像素玩具,用同样简单的方法,如何旧的320x200"模式13h"在DOS中工作.更容易(没有调色板,没有64k段限制).

  • @ S0S有疑问时,就看过原著英特尔文档......因为我总是懒链接到那些大PDF文件,我只想把这里的在线简化副本(有时含有微小的不准确之处,但在这种情况下,更准确比你的书):http://x86.renejeschke.de/html/file_module_x86_id_26.html我相信这本书的作者可能没有给予足够的重视配方,因为它还挺明显的值是如何新的压入堆栈,因此,对于`call`来说,以其他方式做这件事是没有意义的.所以可能他们没有意识到描述的顺序错误. (2认同)