读书人

IA-32的调用栈治理

发布时间: 2013-03-28 10:20:24 作者: rapoo

IA-32的调用栈管理

本文用于汇总整理IA-32架构调用栈管理的基本知识,
根据:
《Intel? 64 and IA-32 Architectures Software Developer Manuals》
整理



1 调用栈的基本操作

调用栈被容纳在由SS寄存器指定的段里面,最大4G Bytes。
当执行PUSH指令时向调用栈内添加元素并递减ESP寄存器取值,当执行POP指令时从调用栈中移除元素,并递增寄存器取值,调用栈是一个向下增长的栈结构(向低地址增长)。
一个程序可以维护多个调用栈,但是在同一时刻只有一个调用栈有效。
基于调用栈,IA-32支持两种类型的PROCEDURE CALL:
使用CALL-RET指令使用ENTER-LEAVE指令(也需要配合CALL-RET指令)


2 使用CALL-RET指令的PROCEDURE CALL

通过CALL指令实现同一代码段内的控制流转移称为Near Call,跨代码段的控制流转移称为Far Call。


2.1 Near Call

当执行Near Call指令时:
把EIP寄存器的内容压栈把要调用的procedure指令偏移量载入EIP寄存器开始执行被调用的Prodedure
当执行相应的RET指令时:
把栈顶元素载入EIP寄存器If the RET instruction has an optional n argument, increments the stack pointer by the number of bytes specified with the n operand to release parameters from the stack.恢复Calling Procedure的执行


2.2 Far Call



2.2.1 CPU运行级别

IA-32处理器提高4种运行级别:Level 0 ~ Level 3,其中Level 0优先级最高,Level 3优先级最低。低优先级的代码段中的控制流转移到高优先级代码段中时,必须通过gate进行优先级判断,否则将会产生general-protection exception (#GP)。
The segment selector provided in the CALL instruction references a special data structure called a call gate descriptor. call gate descriptor提供以下信息:
访问权限信息called procedure的代码段的segment selectorcalled procedure的instruction pointer在代码段中的偏移量
TSS: The segment selectors and stack pointers for the privilege level 2, 1, and 0 stacks are stored in a system segment called the task state segment (TSS).


2.2.2 同一处理器运行级内的Far Call

执行Call时:
把CS寄存器压入调用栈把EIP寄存器压入调用栈把Called Procedure的代码段selector载入CS寄存器把Called Procedure的Instruction Pointer在代码段中的偏移量载入EIP寄存器开始执行called procedure
执行RET时:
弹出调用栈顶,并载入EIP寄存器弹出调用栈顶,并载入CS寄存器If the RET instruction has an optional n argument, increments the stack pointer by the number of bytes继续执行 calling procedure


2.2.3 试图提高运行级的Far Call

执行CALL时:
权限检查保存:SS, ESP, CS和EIP寄存器从TSS中载入新运行级对应的segment selector和stack pointer进入SS和ESP寄存器,由此开始使用新的调用栈向新的调用栈内压入刚刚保存的Calling Procedure的SS和ESP寄存器取值Copies the parameters from the calling procedure’s stack to the new stack. A value in the call gate descriptordetermines how many parameters to copy to the new stack.向新的调用栈内压入刚刚保存的Calling Procedure的CS和EIP寄存器取值从Call Gate中载入新的code segment和instruction pointer到CS和EIP寄存器开始执行高运行级called procedure


执行RET时:
权限检查Restores the CS and EIP registers to their values prior to the call.If the RET instruction has an optional n argument, increments the stack pointer by the number of bytes specified with the n operand to release parameters from the stack. If the call gate descriptor specifies that one or more parameters be copied from one stack to the other, a RET n instruction must be used to release the parameters from both stacks. Here, the n operand specifies the number of bytes occupied on each stack by the parameters. On a return, the processor increments ESP by n for each stack to step over (effectively remove)these parameters from the stacks.Restores the SS and ESP registers to their values prior to the call, which causes a switch back to the stack of the calling procedure.If the RET instruction has an optional n argument, increments the stack pointer by the number of bytes specified with the n operand to release parameters from the stack (see explanation in step 3).继续执行Calling Procedure


2.3 参数传递

有三种方式:
Through the General-Purpose Registers: 8个general-purpose registers中的6个(EAX、EBX、ECX、EDX、ESI、EDI,即除了:EBP、ESP)在PROCEDURE CALL的过程中没用,于是calling procedure可以通过general-purpose registers最多向called procedure传递6个参数
On the Stack:为了传递大量参数,可以把参数放到调用栈上,此时,可以通过EBP寄存器标明栈帧间的边界,方便参数的访问。
In an Argument List:把参数放到一个data segment内的argument list里面,然后通过 general-purpose register或调用栈传递指向argument list的指针。


2.4 现场保护

在Procedure Call的过程中,需要注意以下寄存器的现场保护:
general-purpose registersEFLAGSsegment registers
若在Procedure之间要保存general-purpose registers和EFLAGS寄存器的状态,可以使用处理器提供的以下机制:
PUSHA指令把所有general-purpose registers保存到调用栈上,按照顺序:EAX-ECX-EDX-EBX-ESP(the value prior to executing the PUSHA instruction)-EBP-ESI-EDIPOPA指令把PUSHA指令保存的所有general-purpose registers(except the ESP value)恢复到寄存器中PUSHF指令把EFLAGS的lower word压到栈上PUSHD指令把整个EFLAGS压到栈上POPF指令是PUSHF指令的反向操作POPD指令是PUSHD指令的反向操作


2 使用ENTER-LEAVE指令
(也需要配合CALL-RET指令)
的Procedure Call

暂略。


3 中断与异常

中断与异常是IA-32处理器提供的两种打断程序执行的方式:
中断是异步的,由设备I/O或中断指令触发异常是同步的,在处理器发现异常情况时发生
IA-32处理器提供18个预定义的中断/异常,和224个用户定义的中断:


处理器处理中断和异常的方式是一样的,有两种模式:
调用处理Procedure来处理调用处理Task来处理


3.1 调用Procedure来处理中断或异常

需要通过 interrupt gate或trap gate来进行,interrupt gate和trap gate的区别在于:如果通过interrupt gate调用,会关闭EFLAGS中的中断标志位,而trap gate不会。
若处理Procedure的运行级则不需要切换调用栈,否则要进行调用栈的切换。若不需要切换调用栈,则调用处理Procedure的过程如下:
Pushes the current contents of the EFLAGS, CS, and EIP registers (in that order) on the stack.Pushes an error code (if appropriate) on the stack.Loads the segment selector for the new code segment and the new instruction pointer (from the interrupt gate or trap gate) into the CS and EIP registers, respectively.If the call is through an interrupt gate, clears the IF flag in the EFLAGS register.Begins execution of the handler procedure.
返回时:
Restores the CS and EIP registers to their values prior to the interrupt or exception.Restores the EFLAGS register.Increments the stack pointer appropriately.Resumes execution of the interrupted procedure.
若需要切换调用栈,则调用处理Procedure的过程如下:
Temporarily saves (internally) the current contents of the SS, ESP, EFLAGS, CS, and EIP registers.Loads the segment selector and stack pointer for the new stack (that is, the stack for the privilege level being called) from the TSS into the SS and ESP registers and switches to the new stack.Pushes the temporarily saved SS, ESP, EFLAGS, CS, and EIP values for the interrupted procedure’s stack onto the new stack.Pushes an error code on the new stack (if appropriate)Loads the segment selector for the new code segment and the new instruction pointer (from the interrupt gate or trap gate) into the CS and EIP registers, respectively.If the call is through an interrupt gate, clears the IF flag in the EFLAGS register.Begins execution of the handler procedure at the new privilege level.
返回时:
Performs a privilege check.Restores the CS and EIP registers to their values prior to the interrupt or exception.Restores the EFLAGS register.Restores the SS and ESP registers to their values prior to the interrupt or exception, resulting in a stack switch back to the stack of the interrupted procedure.Resumes execution of the interrupted procedure.


3.2 调用Task来处理中断或异常

在一个单独的Task里执行exception handler,处理Task有自己的地址空间。需要通过一个task gate调用。


3.3 Real-Address模式下的中断/异常处理

通过一个隐式Far Call调用Handler。处理器使用interrupt/exception向量作为IDT(Interrupt Descriptor Table)的索引,IDT包含处理Procedure的instruction pointer。处理器在切换到处理Proceure之前会在栈上保存EFLAGS、EIP、CS寄存器 and an optional error code。返回时通过IRET指令。






读书人网 >操作系统

热点推荐