使用 int n 调用系统例程
来源:互联网 发布:网络不安全事件 编辑:程序博客网 时间:2024/05/19 22:25
转自:点击打开链接
IDT(Interrupt Descriptor Table)仅能存放 interrupt-gate、trap-gate 和 task-gate。
指令:
int 0x80
-----------------------------------
0x80 是 vector (中断向量号)
在 x86 下,gate-descriptor 是 8 个字节,所以:gate = IDTR.base + vector * 8,在 long mode 下,gate-descrptor 是 16 字节,所以:gate = IDTR.base + vector * 16。
在 real-mode 下,IVT(Interrupt Vector Table)是 IDT 在 real-mode 下的表现形式。IVT entry 是 4 个字节,构成 CS:offset 这种形式,offset 在低端,CS 在高端。所以: entry = IDTR.base + vector * 4。 IDTR.base 初始为 0。
1、索引 gate 和 descriptor
在 IDT 中查找 gate descriptor 是以 vector 为索引,不存在 selector,vector 从 0 ~ 255 共 1 个字节。
gate = IDTR.base + vector * 8;
selctor = gate.selector;
temp_descriptor = get_descriptor(selector, GDT /* or LDT);
用 IDTR.base + vector * 8 方法得取 gate-descriptor,再用 gate_descriptor 的 selector 来获取目 code segment descriptor。
2、权限 check
在访问 IDT 中不使用 selector,所以不存在 RPL 检查,仅需要检查 CPL 、DPLg 和 DPLs 就行了。DPLg 代表 gate-descriptor 的 DPL,DPLs 代表 code segment descriptor 的 DPL。
若 gate 是个 task-gate 仅需判断 CPL <= DPLg 就行了
if (gate.type == TASK_GATE) { /* task gate */
if (CPL <= DPLg) {
/* 通过,允许访问 */
} else {
/* 失败,#GP 异常 */
}
} else if (CPL <= DPLg && CPL >= DPLs) { /* interrupt / trap gate */
/* 通过,允许访问 */
} else {
/* 失败,#GP 异常 */
}
无论 code segment 是 conforming 还是 non-conforming 都是可以的。
3、stack 切换
interrupt / trap 服务例程必然在运行在 supervisor 权限下,若切入 interrupt/trap 例程的代码运行在 user 权限下,这将导致权限改变,stack 也发生切换。
DPL = temp_descriptor.DPL; /* code segment descriptor's DPL */
old_SS = SS;
old_ESP = ESP;
SS = TSS.stack[DPL].SS; /* 加载目标 stack */
ESP = TSS.stack[DPL].ESP;
push(old_SS);
push(old_ESP);
push(Eflags);
push(CS);
push(EIP);
if (ERROR_CODE) {
push(error_code);
}
stack 切换时,processor 做以下工作:
(1)以目标 code segment 的 DPL 为索引在 TSS 获取相应权限级别的 stack pointer 加载到 SS & ESP
(2)将旧的 SS & ESP 保存在当前 stack (新 stack) 中。
(3)EFLAGS 寄存器入栈。
(4)返回地址(CS & EIP)入栈。
(5)由异常引发的 interrupt/trap 例程,还将异常码入栈,以供 interrupt/trap 例程使用。
SS.RPL 会被更新为 DPL,表示当前 stack 的权限级别就是 SS.RPL。
当发生同级权限的转移时,不会发生 stack 切换,使用的是当前的 stack:
push(Eflags);
push(CS);
push(EIP);
if (ERROR_CDOE)
push(error_code)
4、Eflags 寄存器的处理
processor 将旧的 Eflags 入栈保存,将修改当 Eflags 的标志
if (gate == TASK_GATE) /* task gate */
Eflags.NT = 1;
else /* interrupt/trap gate */
Eflags.NT = 0;
Eflags.TF = 0;
Eflags.VM = 0;
Eflags.RF = 0;
if (gate == INTERRUPT_GATE) { /* interrupt gate */
Eflags.IF = 0;
} else if (gate == TRAP_GATE) { /* trap gate */
/* do nothing */
} /* task gate */
(1)若是 task gate,processor 将置 Eflags.NT = 1 进入 Nest Task 模式,在 Iret 指令返回时将检查 Eflags.NT = 1 进入 task 切换。若是 interrupt/trap 的话,将 Eflags.NT 清 0,防止 IRET 指令回时进行 task 切换。
(2)Eflags.TF 将被清 0,将 Eflags.TF = 1 将引发 #DB 异常,processor 将进入单步调试模式,在进入响应 #DB 异常的例程前,processor 会将 Eflags.TF 清 0。
(3)Eflags.RF 清 0,在 IRET 指令返回时,processor 将 Eflags.RF 置为 1,使得 #DB 异常断点下一条指令能顺利执行。
5、加载 code segment descriptor
同样,processor 会加载 code segment selector & descriptor 到 CS 寄存器
CS.selector = gate.selector;
CS.base = temp_descriptor.base;
CS.attribute = temp_descriptor.attribute;
CS.limit = temp_descriptor.limit;
加载后,CS.RPL 就是新的 CPL,
6、执行服务例程
processor 将加载 EIP = CS.base + gate.offset,然后执行 CS:EIP 处的中断服务例程。
7、中断例程返回
中断例程使用 iret 指令返回时,processor 同样会进行一系统的权限检查,中断例程不能向高权限的代码返回。
if (Eflags.NT == 1) {
/* 发生 task 切换 */
}
pop(temp_EIP);
pop(temp_CS);
if (temp_CS.RPL == CPL) { /* CPL == RPL */
goto return_same; /* 同级返回 */
} else if (temp_CS.RPL > CPL) /* CPL < RPL */
goto return_less; /* 向低权限代码返回
else { /* CPL > RPL */
/* 异常,#GP 异常 */
/* 拒绝向高权限代码返回 */
}
return_same:
CS = temp_CS;
EIP = temp_EIP;
pop(Eflags);
return;
return_less:
pop(temp_Eflags);
pop(temp_ESP);
pop(temp_SS);
if (temp_SS.RPL == temp_CS.RPL) {
Eflags = temp_Eflags;
SS = temp_SS;
ESP = temp_ESP;
CS = temp_CS;
EIP = temp_EIP;
} else {
/* 失败,#GP 异常 */
}
return;
(1)iret 指令检查 Eflags.NT 是否为 1,为 1 时,直接进行任务切换出去,这个 task 就是 TSS.link 里的 TSS selector。
(2)pop 出旧的 CS & EIP,并进行权限检查,temp_CS.RPL 代表原来的 CPL 运行级别,必须 CPL <= temp_CS.RPL,若 pop 出的 CS.RPL 是低于 CPL 的,则会产生 #GP 异常,processor 防止向高权限的代码返回。
当向同级的代码返回时,仅需加载回原来的 CS & EIP 以及 EFLAGS 寄存器就行了。
当向低权限代码返回时,processor 会检查 pop 出来的 SS.RPL 是否等于 temp_CS.RPL(原来的 CS.RPL),不相同则产生 #GP 异常,在 x86 下,无论什么情况 CPL.RPL 必须等于 SS.RPL。通过后,processor 相应地加载 SS & ESP、CS & EIP 以及 EFLAGS 寄存器,然后返回原程序。
- 使用 int n 调用系统例程
- 系统调用的封装例程
- 系统调用处理程序 系统调用服务例程 关系 区别
- 系统调用(int 0x80)
- Linux系统调用接口、系统调用例程和内核服务例程之间的关系
- Linux系统调用接口、 系统调用例程 和 内核服务例程之间的关系
- int *n;int &n;int *&n
- Linux系统调用 int 80h int 0x80
- DOS系统功能调用(INT 21H)
- read 系统调用为什么返回 int ?
- int $0x80系统调用的idea
- int $0x80系统调用的idea
- 系统调用(int 0x80)详解
- 王爽 汇编 实验16 增加键盘中断调用int 7ch中断例程
- int 10h中断例程
- 直接使用系统调用
- int *p[n]; int (*p)[n];
- int n与int... n的区别
- 整合IIS和Tomcat失败之后,原网站打不开,出现Service Unavailable 解决办法!!
- Cocos2d-x CCTMXTiledMap类
- 工作笔记1
- long mode 模式下的中断服务例程
- oracle10g init.ora参数文件内容
- 使用 int n 调用系统例程
- 使用 task gate 进行任务切换
- 使用 TSS selector 进行任务切换
- 选择 conforming 还是 non-conforming ?
- long mode 模式下 system/gate descriptor 的疑惑
- 通过 call gate 访问目标 code segment
- 【iOS-Cocos2d游戏开发之九】讲解CCSpriteBatchNode与TP工具的”.pvr.ccz”,”.plist”共用的终极精灵优化及注意事项!
- 使用 call/jmp 直接调用/跳转目标 code segment
- call/jmp offset 段内调用