JZ不会在堆栈中放置一个return地址吗?

Does JZ not place a return address in the stack?

我正在阅读 assembly code optimization manual 第 2.3 节常见编码陷阱 - 第 9 页

  1. Unmatched PUSH and POP instructions. The number of PUSH and POP instructions must be equal for all possible paths through a function. Example:

    push ebx
    test ecx, ecx
    jz   Finished
    ...
    pop  ebx
    Finished:       ; Wrong! Label should be before pop ebx
    ret
    

Here, the value of EBX that is pushed is not popped again if ECX is zero. The result is that the RET instruction will pop the former value of EBX and jump to a wrong address.

我的疑惑是:jz指令不是把return地址存入栈了吗? jmpjgjgejljle 等其他指令呢?

不,不是。 Call 指令将 return 地址压入堆栈,jump 指令(包括条件跳转)则不会。这就是调用和跳转的根本区别。

如果你想知道一条指令的作用,你应该总是参考它在官方文档中的描述,例如https://www.felixcloutier.com/x86/ which is an HTML copy of Intel's manuals. The description of jcc (which includes je) makes no mention of pushing a return address, which tells you that it doesn't do it. On the other hand, the description of call 明确指出 return 地址被推送,并详细解释了它是如何完成的:

When executing a near call, the processor pushes the value of the EIP register (which contains the offset of the instruction following the CALL instruction) on the stack (for use later as a return-instruction pointer). The processor then branches to the address in the current code segment specified by the target operand.

跳转指令通常用于在函数内转移控制,用于循环 (while)、条件执行 (if) 等结构。在大多数情况下,您永远不想到 return 到跳转的站点,因此推送 return 地址将毫无用处,只会需要您浪费额外的指令才能将其从堆栈中删除。