JZ是不是在栈里放了一个返回地址?

ADP*_*ADP 2 x86 assembly

我正在阅读汇编代码优化手册第 2.3 节常见编码陷阱 - 第 9 页

  1. 无与伦比的 PUSH 和 POP 指令。对于通过函数的所有可能路径,PUSH 和 POP 指令的数量必须相等。例子:

    push ebx
    test ecx, ecx
    jz   Finished
    ...
    pop  ebx
    Finished:       ; Wrong! Label should be before pop ebx
    ret
    
    Run Code Online (Sandbox Code Playgroud)

这里,如果 ECX 为零,则不会再次弹出推送的 EBX 值。结果是RET指令会弹出EBX之前的值并跳转到错误的地址。

我的疑问是:jz指令不是将返回地址存储在堆栈中吗?什么其他指令一样jmpjgjgejljle等?

Nat*_*dge 5

不,它没有。 调用指令将返回地址推入堆栈,跳转指令(包括条件跳转)不会。这就是调用和跳转之间的根本区别。

如果您想知道指令的作用,您应该始终参考官方文档中的说明,例如https://www.felixcloutier.com/x86/,这是英特尔手册的 HTML 副本。 (包括)的描述jccje没有提到推送返回地址,它告诉你它没有这样做。另一方面,的描述call明确指出返回地址被推送,并详细解释了它是如何完成的:

执行近调用时,处理器将 EIP 寄存器的值(包含 CALL 指令之后的指令的偏移量)压入堆栈(稍后用作返回指令指针)。处理器然后分支到目标操作数指定的当前代码段中的地址。

跳转指令通常用于在函数内转移控制,例如循环 ( while)、条件执行 ( if) 等结构。在大多数此类情况下,您永远不想返回跳转位置,因此推送返回地址会没用,只需要你浪费额外的指令来从堆栈中删除它。