我对汇编很新,而且我不明白它是什么意思,当在proc结束时,你用ret
语句写一个数字。
像这样:
Function Proc
push ax cx
.
...body...
.
pop cx ax
ret 2
Function endp
我理解它与函数末尾的堆栈指针应返回的位置有关?
如果你能轻易解释,请帮助我。
答案 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]
地址word2
和word1
。
ret 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)
“用于返回的可选数字(16位或32位)参数指定从堆栈弹出返回地址后要释放的堆栈字节或字的数量。通常,这些字节或字是用作被调用过程的输入参数。“-https://docs.oracle.com/cd/E19455-01/806-3773/instructionset-67/index.html
因此,它首先弹出返回地址,然后可选数字是指在弹出返回地址之后要增加堆栈的字节数。
ret 4
在32位模式下总计为esp+=8
,包括弹出4字节的返回地址和呼叫者已推送的4字节。