RET - 從過程返回

操作碼

指令

說明

C3

RET

近返回到呼叫過程

CB

RET

遠返回到呼叫過程

C2 iw

RET imm16

近返回到呼叫過程,並從堆疊彈出 imm16 個位元組

CA iw

RET imm16

遠返回到呼叫過程,並從堆疊彈出 imm16 個位元組

說明

將程序控制權轉移到位於棧頂的返回地址。此地址通常由 CALL 指令放入堆疊,返回目標是 CALL 指令的下一條指令。

可選的源運算元指定彈出返回地址之後要釋放的堆疊位元組數;預設值是無。此運算元可用於從堆疊釋放傳遞到被呼叫過程並且不再需要的參數。用於切換到新過程的 CALL 指令使用帶非零字計數的呼叫門訪問新過程時,必須使用此運算元。這裡,RET 指令的源運算元指定的位元組數必須與呼叫門的字計數欄位中指定的相同。

RET 指令可用於執行三種不同型別的返回:

近返回 - 返回到目前程式碼段(CS 暫存器目前指向的段)中的呼叫過程,有時稱為段內返回。

遠返回 - 返回到目前程式碼段之外的段中的呼叫過程,有時稱為段間返回。

特權級別間遠返回 - 遠返回到與目前執行程式或過程不同的特權級別。

特權級別間返回型別只能在保護模式中執行。如需有關近呼叫、遠呼叫及特權級別間返回的詳細資訊,請參閱“IA-32 英特爾(R) 體系結構軟件開發人員手冊”第 1 卷第 6 章中標題為“使用 Call 與 RET 呼叫過程”的部分。

執行近返回時,處理器將返回指令指針(偏移量)從棧頂彈入 EIP 暫存器,然後在新的指令指針處開始程式執行。CS 暫存器保持不變。

執行遠返回時,處理器將返回指令指針從棧頂彈入 EIP 暫存器,並將段選擇器從棧頂彈入 CS 暫存器。接著,處理器在新程式碼段的指令指針處開始程式執行。

特權級別間遠返回的機制與段間返回類似,不同的是處理器會檢查要返回到的程式碼段與堆疊段的特權級別與訪問許可權,以確定是否允許轉移控制權。如果 DS、ES、FS 及 GS 段暫存器引用的段在新的特權級別下不允許進行訪問,則在執行特權級別間返回期間,RET 指令會清除它們。由於在執行特權級別間返回時還會發生堆疊切換,因此會從堆疊載入 ESP 與 SS 暫存器。

在執行特權級別間呼叫期間,如果將參數傳遞到被呼叫過程,則必須配合可選的源運算元使用 RET 指令,以便在返回時釋放參數。這裡,參數同時從被呼叫過程的堆疊與呼叫過程的堆疊(即返回到的堆疊)釋放。

操作

(* Near return *)
IF instruction near return
THEN;
IF OperandSize 32
THEN
IF top 12 bytes of stack not within stack limits THEN #SS(0); FI;
EIP Pop();
ELSE (* OperandSize 16 *)
IF top 6 bytes of stack not within stack limits
THEN #SS(0)
FI;
tempEIP Pop();
tempEIP tempEIP AND 0000FFFFH;
IF tempEIP not within code segment limits THEN #GP(0); FI;
EIP tempEIP;
FI;
IF instruction has immediate operand
THEN IF StackAddressSize=32
THEN
ESP ESP + SRC; (* release parameters from stack *)
ELSE (* StackAddressSize=16 *)
SP SP + SRC; (* release parameters from stack *)
FI;
FI;

(* Real-address mode or virtual-8086 mode *)
IF ((PE 0) OR (PE 1 AND VM 1)) AND instruction far return
THEN;
IF OperandSize 32
THEN
IF top 12 bytes of stack not within stack limits THEN #SS(0); FI;
EIP Pop();
CS Pop(); (* 32-bit pop, high-order 16 bits discarded *)
ELSE (* OperandSize 16 *)
IF top 6 bytes of stack not within stack limits THEN #SS(0); FI;
tempEIP Pop();
tempEIP tempEIP AND 0000FFFFH;
IF tempEIP not within code segment limits THEN #GP(0); FI;
EIP tempEIP;
CS Pop(); (* 16-bit pop *)
FI;
IF instruction has immediate operand
THEN
SP SP + (SRC AND FFFFH); (* release parameters from stack *)
FI;
FI;

(* Protected mode, not virtual-8086 mode *)
IF (PE 1 AND VM 0) AND instruction far RET
THEN
IF OperandSize 32
THEN
IF second doubleword on stack is not within stack limits THEN #SS(0); FI;
ELSE (* OperandSize 16 *)
IF second word on stack is not within stack limits THEN #SS(0); FI;
FI;
IF return code segment selector is null THEN GP(0); FI;
IF return code segment selector addresses descriptor beyond descriptor table limit
THEN GP(selector; FI;
Obtain descriptor to which return code segment selector points from descriptor table
IF return code segment descriptor is not a code segment THEN #GP(selector); FI;
if return code segment selector RPL < CPL THEN #GP(selector); FI;
IF return code segment descriptor is conforming
AND return code segment DPL > return code segment selector RPL
THEN #GP(selector); FI;
IF return code segment descriptor is not present THEN #NP(selector); FI:
IF return code segment selector RPL > CPL
THEN GOTO RETURN-OUTER-PRIVILEGE-LEVEL;
ELSE GOTO RETURN-TO-SAME-PRIVILEGE-LEVEL
FI;
END;FI;

RETURN-SAME-PRIVILEGE-LEVEL:
IF the return instruction pointer is not within then return code segment limit
THEN #GP(0);
FI;
IF OperandSize=32
THEN
EIP Pop();
CS Pop(); (* 32-bit pop, high-order 16 bits discarded *)
ESP ESP + SRC; (* release parameters from stack *)
ELSE (* OperandSize=16 *)
EIP Pop();
EIP EIP AND 0000FFFFH;
CS Pop(); (* 16-bit pop *)
ESP ESP + SRC; (* release parameters from stack *)
FI;

RETURN-OUTER-PRIVILEGE-LEVEL:
IF top (16 + SRC) bytes of stack are not within stack limits (OperandSize=32)
OR top (8 + SRC) bytes of stack are not within stack limits (OperandSize=16)
THEN #SS(0); FI;
FI;
Read return segment selector;
IF stack segment selector is null THEN #GP(0); FI;
IF return stack segment selector index is not within its descriptor table limits
THEN #GP(selector); FI;
Read segment descriptor pointed to by return segment selector;
IF stack segment selector RPL RPL of the return code segment selector
OR stack segment is not a writable data segment
OR stack segment descriptor DPL RPL of the return code segment selector
THEN #GP(selector); FI;
IF stack segment not present THEN #SS(StackSegmentSelector); FI;
IF the return instruction pointer is not within the return code segment limit THEN #GP(0); FI:
CPL ReturnCodeSegmentSelector(RPL);
IF OperandSize=32
THEN
EIP Pop();
CS Pop(); (* 32-bit pop, high-order 16 bits discarded *)
(* segment descriptor information also loaded *)
CS(RPL) CPL;
ESP ESP + SRC; (* release parameters from called procedure's stack *)
tempESP Pop();
tempSS Pop(); (* 32-bit pop, high-order 16 bits discarded *)
(* segment descriptor information also loaded *)
ESP tempESP;
SS tempSS;
ELSE (* OperandSize=16 *)
EIP Pop();
EIP EIP AND 0000FFFFH;
CS Pop(); (* 16-bit pop; segment descriptor information also loaded *)
CS(RPL) CPL;
ESP ESP + SRC; (* release parameters from called procedure's stack *)
tempESP Pop();
tempSS Pop(); (* 16-bit pop; segment descriptor information also loaded *)
(* segment descriptor information also loaded *)
ESP tempESP;
SS tempSS;
FI;
FOR each of segment register (ES, FS, GS, and DS)
DO;
IF segment register points to data or non-conforming code segment
AND CPL > segment descriptor DPL; (* DPL in hidden part of segment register *)
THEN (* segment register invalid *)
SegmentSelector 0; (* null segment selector *)
FI;
OD;
For each of ES, FS, GS, and DS
DO
IF segment selector index is not within descriptor table limits
OR segment descriptor indicates the segment is not a data or
readable code segment
OR if the segment is a data or non-conforming code segment and the segment
descriptor's DPL < CPL or RPL of code segment's segment selector
THEN
segment selector register null selector;
OD;
ESP ESP + SRC; (* release parameters from calling procedure's stack *)

影響的標誌

無。

保護模式異常

#GP(0) - 如果返回程式碼或堆疊段選擇器為空。如果返回指令指針不在返回程式碼段的限制範圍內

#GP(選擇器) - 如果返回程式碼段選擇器的 RPL 小於 CPL。如果返回程式碼或堆疊段選擇器的索引不在其描述符表格的限制範圍內。如果返回程式碼段描述符不指出它是程式碼段。如果返回程式碼段為非相容程式碼段,並且段選擇器的 DPL 不等於程式碼段的段選擇器的 RPL。如果返回程式碼段是相容程式碼段,並且段選擇器的 DPL 大於程式碼段的段選擇器的 RPL。如果堆疊段不是可寫的數據段。如果堆疊段選擇器 RPL 不等於返回程式碼段選擇器的 RPL。如果堆疊段描述符的 DPL 不等於返回程式碼段選擇器的 RPL。

#SS(0) - 如果堆疊的棧頂位元組超出堆疊限制。如果返回堆疊段不存在。

#NP(選擇器) - 如果返回程式碼段不存在。

#PF(錯誤程式碼) - 如果發生頁錯誤。

#AC(0) - 如果在 CPL 為 3 且啟用對齊檢查的情況下發生未對齊的記憶體訪問。

實地址模式異常

#GP - 如果返回指令指針不在返回程式碼段的限制範圍內

#SS - 如果堆疊的棧頂位元組超出堆疊限制。

虛 8086 模式異常

#GP(0) - 如果返回指令指針超出返回程式碼段限制。

#SS(0) - 如果堆疊的棧頂位元組超出堆疊限制。

#PF(錯誤程式碼) - 如果發生頁錯誤。

#AC(0) - 如果在啟用對齊檢查的情況下發生未對齊的記憶體訪問。