在x86程序集中的过程中调用ret指令的位置是否重要?

Eng*_*999 0 x86 assembly stack call

我目前正在学习x86程序集.但是当我使用堆栈进行函数调用时,我仍然不清楚某些事情.我知道调用指令将涉及在堆栈上推送返回地址,然后加载程序计数器和要调用的函数的地址.ret指令将该地址加载回程序计数器.

我的困惑是,在过程/函数中调用ret指令时是否重要?它是否总能找到存储在堆栈中的正确返回地址,或者堆栈指针当前是否必须指向存储返回地址的位置?如果是这种情况,我们不能只使用push和pop而不是call和ret吗?

例如,下面的代码可能是进入函数的第一个代码,如果我们在堆栈上推送不同的寄存器,则必须在以相反的顺序弹出寄存器之后才调用ret指令,以便在pop%ebp指令之后,堆栈指针将指向返回地址所在的堆栈上的正确位置,或者无论它在何处被调用,它仍会找到它?提前致谢

push %ebp
mov %ebp, %esp
//push other registers

...
//pop other registers
mov %esp, %ebp
(could ret instruction go here for example and still pop the correct return address?)
pop %ebp
ret
Run Code Online (Sandbox Code Playgroud)

J..*_*... 5

您必须在找到它们时保留堆栈和非易失性寄存器.调用函数不知道你可能用它们做了什么 - 否则调用函数将继续执行下一条指令ret.只有ret在你完成清理之后.

ret将始终查看堆栈顶部的返回地址并将pop其转换为EIP.如果它ret是一个"远"返回,那么它也pop将代码段放入CS寄存器(也可以被call"远"调用推送).由于这些是推动的第一件事call,它们必须是最后的东西ret.否则你最终会在ret某个地方未定义.


Ped*_*d7g 5

CPU不知道什么是函数/ etc ... ret指令将从esp跳转指向的内存中获取值.例如,您可以执行以下操作(以说明CPU对您在结构上组织源代码的方式不感兴趣):

   ; slow alternative to "jmp continue_there_address"
   push continue_there_address
   ret
continue_there_address:
   ...
Run Code Online (Sandbox Code Playgroud)

您也不需要从堆栈中恢复寄存器,(甚至不将它们恢复到原始寄存器),只要esp指向返回地址ret执行时,它将被使用:

    call SomeFunction
    ...

SomeFunction:
    push eax
    push ebx
    push ecx
    add  esp,8   ; forget about last 2 push
    pop  ecx     ; ecx = original eax
    ret          ; returns back after call
Run Code Online (Sandbox Code Playgroud)

如果您的函数应该可以与代码的其他部分互操作,您可能仍然希望按照您正在编程的平台的调用约定来存储/恢复寄存器,因此从调用者的角度来看,您不会修改某些寄存器值应该保留,等等...但没有一个困扰CPU和执行指令ret,CPU只是从stack([esp])加载值,并跳转到那里.

此外,当返回地址存储到堆栈时,它与以任何方式推送到堆栈的其他值没有区别,所有这些都只是写入内存中的值,因此ret没有机会以某种方式在堆栈中找到"返回地址"并跳过"值",对于CPU,内存中的值看起来相同,每个32位值是32位值.无论是存储的call,push,mov,还是别的什么,无所谓,该信息(价值原点)不存储,唯一的价值.

如果是这种情况,我们不能只使用push和pop而不是call和ret吗?

您当然可以push首选返回堆栈(我的第一个例子).但你做不到pop eip,没有这样的指示.其实这就是ret 做,所以pop eip实际上是同样的事情,但没有x86汇编程序员使用这样的助记符,以及操作码与其它不同pop的指令.您当然可以pop将返回地址放入不同的寄存器中,eax然后执行jmp eax,以便有慢速ret替代(也可以修改eax).

也就是说,复杂的现代x86 CPU确实保留了一些call/ret配对跟踪(以预测下一个ret将返回的位置,因此它可以快速预取代码),所以如果您将使用其中一种替代的非标准方式,在某些时候CPU将意识到它的返回地址的预测系统不在真实状态,它将不得不丢弃所有这些缓存/预加载并从实际eip值重新获取所有内容,因此您可能会因混淆它而支付性能损失.