RET在装配中的意义

时间:2013-07-13 09:36:45

标签: assembly x86

我对汇编很新,而且我不明白它是什么意思,当在proc结束时,你用ret语句写一个数字。

像这样:

Function Proc
push ax cx
.
...body...
.
pop cx ax
ret 2 
Function endp

我理解它与函数末尾的堆栈指针应返回的位置有关?

如果你能轻易解释,请帮助我。

6 个答案:

答案 0 :(得分:21)

是的,但是ret 2也会从堆栈中删除2个字节的参数。据推测,你的功能被称为:

push some_parameter
call Function

此时,cdecl函数 - “调用者清理”函数(通常由C使用) - 需要add sp, 2来“清理堆栈”,删除参数。这样的函数将以普通ret结束。

一个stdcall函数,就是你所拥有的,是一个“被调用者清理”函数(例如,由Windows API使用),不需要add sp, 2 - 它已经被由ret 2完成。

如果您不了解它,call会将返回地址放在堆栈上(并ret将其弹出),因此您不能只pop获取函数内的参数。

答案 1 :(得分:18)

假设我有一个程序来添加两个单词并将总和保留在EAX中。单词是我想传递给堆栈上的过程的参数。即:

push word1
push word2
call addtwob

程序看起来像:

addtwob proc

push ebp
mov  ebp,esp
mov  eax, [ebp+6]    
add  eax, [ebp+8]
pop ebp
ret 4

Endp
堆栈上的

[ebp+6][ebp+8]地址word2word1ret 4只是像往常一样返回但是然后将4添加到堆栈指针(esp),这样在从调用返回后,您不必从堆栈中pop word2 pop word1,因此它可以清理/平衡堆栈而无需弹出之前的推送。

答案 2 :(得分:8)

正如亚历克斯所说,这意味着回归。 在x86程序集中,当编译器到达此行时(例如,在子例程的末尾),它会从堆栈中弹出 last 值,该值应该是返回的地址,并为其分配到IP注册。您可以通过编写简单的汇编代码并使用Turbo Debugger进行编译来更好地理解这一点。如果你是新手,那么有一个汇编程序的GUI。您可以找到GUI here

当您在子例程中弹出并向堆栈推送值时,应该存储返回的地址,因为在子路径的末尾需要将其推回到{{{ 1}}行。

祝你好运!

答案 3 :(得分:3)

这意味着返回,就像高级语言中的return一样。

在大多数机器上,它会弹出程序计数器的先前值,然后再从堆栈中输入子程序并将其复制到PC的寄存器中。

对于x86,参数是堆栈上的参数数量。仅当使用的约定是让子例程处理重置堆栈时才适用。

答案 4 :(得分:1)

您似乎在询问有关x86_64 的操作数的附近返回。处理器遇到RET附近的硬件后跟的算法如Intel instruction set reference manual所示,如下所示;

(* Near return *)
IF instruction = near return
    THEN;
    IF OperandSize = 32
        THEN
        IF top 4 bytes of stack not within stack limits
            THEN #SS(0); FI; //throw protected mode exception
        EIP ← Pop(); 
        ELSE
        IF OperandSize = 64
            THEN
            IF top 8 bytes of stack not within stack limits
                THEN #SS(0); FI; //throw protected mode exception
            RIP ← Pop();
            ELSE (* OperandSize = 16 *)
            IF top 2 bytes of stack not within stack limits
                THEN #SS(0); FI; //throw protected mode exception
            tempEIP ← Pop();
            tempEIP ← tempEIP AND 0000FFFFH;
            IF tempEIP not within code segment limits
                THEN #GP(0); FI; //throw protected mode exception
            EIP ← tempEIP;
        FI;
    FI;
    IF instruction has immediate operand
        THEN (* Release parameters from stack *)
        IF StackAddressSize = 32
            THEN
            ESP ← ESP + SRC;
            ELSE
            IF StackAddressSize = 64
                THEN
                RSP ← RSP + SRC;
                ELSE (* StackAddressSize = 16 *)
            SP ← SP + SRC;
            FI;
        FI;
    FI;
FI;
  • 根据此算法,只要遇到近似返回,就会检查返回地址是否在SS限制内。如果返回地址有效,则根据操作数大小将堆栈顶部弹出到RIP或EIP中。

  • 如果操作数大小为16位,则临时位置保存弹出的返回地址,该地址与值0x0000FFFF进行AND运算,并在检查CS限制后加载到EIP中。

  • 正如您的问题询问如果有近RET指令操作码的操作数会发生什么。它取决于堆栈地址大小。根据该大小,RSP ESP或SP由操作数增加,并且在所有近似RET指令完成后在硬件上执行。

答案 5 :(得分:1)

对于x86 32位,

“用于返回的可选数字(16位或32位)参数指定从堆栈弹出返回地址后要释放的堆栈字节或字的数量。通常,这些字节或字是用作被调用过程的输入参数。“-https://docs.oracle.com/cd/E19455-01/806-3773/instructionset-67/index.html

因此,它首先弹出返回地址,然后可选数字是指在弹出返回地址之后要增加堆栈的字节数。

ret 4在32位模式下总计为esp+=8,包括弹出4字节的返回地址和呼叫者已推送的4字节。