2014-01-21 49 views
0

像这样但没有CALL指令。我想我应该使用JMP以及其他可能的说明。如何使用JMP模拟CALL指令?

PUSH 5 
PUSH 4 
CALL Function 
+0

阅读手册描述呼叫的功能,然后创建一个指令列表,以跳转完成相同的事情,这是呼叫最后一个操作。 –

+1

这个问题似乎是无关紧要的,因为你没有描述你到目前为止所尝试过的,以及为什么这些尝试与你的期望相符或不相符。解决这个问题需要对问题有一个最基本的理解。 –

回答

12

这是很容易做到的。将返回地址推入堆栈,然后跳转到子例程。 最终的代码如下所示:

PUSH 5 
    PUSH 4 
    PUSH offset label1 
    jmp Function 
label1: ; returns here 
    leas esp, 8[esp] 

Function: 
    ... 
    ret 

虽然这工作,你真的不希望这样做。在大多数现代处理器上,保持片上调用堆栈返回地址缓存,这会在调用时推送返回地址,并在RET上弹出返回地址。在处理器上,这具有极短的更新/访问时间,这意味着RET指令可以使用调用栈高速缓存弹出的值来预测PC下一步应该去的位置,而不是等待从实际指向的存储器位置读取实际存储器到ESP。如果你执行“PUSH offset label1”技巧, 这个缓存没有被更新,因此RET分支预测是错误的,并且处理器流水线被烧毁,对性能产生严重的负面影响。 (我认为IBM在特殊指令方面拥有专利,这些指令本质上是“PUSHRETURNADDRESS k”和“POPRETURNADDESS”,允许在他们的某些CPU上使用这种技巧。唉,不是在x86上。

+0

+1用于解释片上调用堆栈。 – xis

4

这取决于情况。如果返回之前,你的函数做的最后一件事是调用另一个函数,你可以简单地跳转到函数这就是所谓的尾部调用消除,并且是许多编译器进行优化举例:

foo: 
    call B 
    call A 
    ret 

尾调用消除用单跳指令代替最后两行:

foo: 
    call B 
    jmp A 

这是有效的,因为堆栈包含foo的调用者的返回地址。所以当函数A返回时,它将返回到调用foo的函数。

你想要执行到跳转到后恢复,跳跃前推该地址到堆栈中:

foo: 
    call B 
    push offset bar 
    jmp A 
bar: 

不过,我能想到的任何理由为什么有人会想这样做。

+0

感谢您解释这一点。 +1 – cdonts