学习笔记
《x86汇编语言:从实模式到保护模式》
https://www.jianshu.com/p/d481cb547e9f
处理器用以下四种方法将控制转移到其他任务:
- 1、当前程序、任务、或者过程执行一个将控制转移到GDT内某个TSS描述符的jmp或者call指令;
举例,内核程序 通过TSS 切换任务
;32位间接远调用指令CALL
call far [es:ecx+0x14] ;执行任务切换
;ECX指向要切换任务(用户程序)的TCB
;从TCB中取出TSS基地址、TSS选择子
;CPU发现这是TSS选择子,就知道要执行任务切换
;新任务的TSS完整内容在子程序load_relocate_program中已经填写完毕
- 2、当前程序、任务、或者过程执行一个将控制转移到GDT或者当前LDT内某个任务门描述符的jmp或者call指令;
举例,用户程序 通过 调用门 显示 字符串
call far [fs:PrintString]
- 3、 一个异常或者中断发生时,中断号指向中断描述表内的任务门;
- 4、在EFLAGS寄存器的NT位置位的情况下,当前任务执行了一个iret指令;
CALL 与 栈的示意图
- 32位相对近调用,只压入EIP寄存器
| |
|--------|
| EIP |
|--------|
| |
栈的示意图
- 32位远调用,要入依次压入CS和EIP(通过调用门实施的控制转移一定是远转移,要压入CS和EIP)
| |
|---------|
| 0 | CS |
|---------|
| EIP |
|-------- |
| |
栈的示意图
- 假设还带参数,栈的示意图
PUSH 参数1
PUSH 参数2
PUSH 参数3
CALL 调用门
这四条语句执行完后的栈的示意图:
| |
|---------|
| 参数1 |
|---------|
| 参数2 |
|---------|
| 参数3 |
|---------|
| 0 | CS |
|---------|
| EIP |
|-------- |
| |
P.248 14.5.1 通过调用门转移控制的完整过程
CALL
不同特权级间控制转移,要实施栈切换:
- 1、分配内存给特权栈(程序员自己编程实现);
- 2、使用目标代码段的DPL(新的CPL,假设为 n),选择特权级栈,栈段选择子
SSn
,栈指针ESPn
; - 3、栈的切换由处理器固件自动完成;
PUSH 参数1
PUSH 参数2
PUSH 参数3
CALL 调用门
这四条语句执行完后的栈的示意图:
|---------|
| 0 | SS |
|---------|
| ESP |
|-------- |
| 参数1 |
|---------|
| 参数2 |
|---------|
| 参数3 |
|---------|
| 0 | CS |
|---------|
| EIP |
|-------- |
| |
注意:这个示意图本质上是新栈的示意图,
SS 以及ESP压入这个新栈之后,
3个参数才会依次从旧栈被复制到新栈,
紧接着才是CALL指令隐含的CS以及EIP的压入。
相同特权级间的控制转移
PUSH 参数1
PUSH 参数2
PUSH 参数3
CALL 调用门
这四条语句执行完后的栈的示意图:
|-------- |
| 参数1 |
|---------|
| 参数2 |
|---------|
| 参数3 |
|---------|
| 0 | CS |
|---------|
| EIP |
|-------- |
| |
注意:因为这里是相同特权级之间的转移控制,
所以不存在新栈旧栈的问题,
就只有一个栈,
CALL隐含的压入CS以及EIP而已。
call 返回
- 近返回
ret 参数个数*4
(丢弃相应个数的参数)
举例:子程序load_relocate_program
load_relocate_program: ;加载并重定位用户程序
;PUSH 参数1
;PUSH 参数2
;PUSH 参数3
.... ....
.... ....
ret 3*4 ;丢弃调用本过程前压入的参数
- 远返回
iretd
: 32位模式下,iret
与iretd
是相同的
举例:子程序 terminate_current_task
terminate_current_task:
...
.b1:
...
iretd
JMP
- 如果通过
JMP FAR
发起调用门控制转移,那么既没有特权级的变化,也不需要切换栈。
P.297 15.5 处理器在实施任务切换时的操作
任务切换过程中NT位、B位以及TSS指针域变化规则
将控制转移到GDT内某个 TSS描述符 的jmp或者call指令
293页
- 当执行任务切换时,处理器用得到的选择子访问GDT,一旦处理器发现那是一个TSS描述符,就知道应该执行任务切换的操作;
- 首先,因为当前正在执行的任务是由任务寄存器TR指示的,所以,处理器要把每个寄存器的“快照”保存到由TR指向的TSS中;
- 然后,处理器用指令中给出的TSS选择子访问GDT,取得新任务的TSS描述符,并从该TSS中恢复各个寄存器的内容,包括通用寄存器、标志寄存器EFLAGS、段寄存器、指令指针寄存器EIP、栈指针寄存器ESP,以及局部描述符表寄存器LDTR等等;
- 最终,任务寄存器TR指向新任务的TSS,而处理器旋即开始执行新的任务。
CALL
- 内核程序 切换到 用户程序(位于内核程序
c15_core.asm
)
call far [es:ecx+0x14]
- 用户程序 切换回 内核程序(位于用户程序
c15.asm
)
call far [fs:TerminateProgram]
-
CALL指令切换任务过程中:TSS示意图
使用CALL指令 从 程序管理器 切换到 用户程序 后的TSS示意图.png
JMP
- 内核程序 切换到 用户程序(位于内核程序
c15_core.asm
)
jmp far [es:ecx+0x14]
- 用户程序 切换回 内核程序(位于用户程序
c15.asm
)
call far [fs:TerminateProgram]
-
JMP指令切换任务过程中:TSS示意图
使用 JMP 指令 从 程序管理器 切换到 用户程序 后的TSS 示意图.png