Note
Search…
系统调用

int 0x80方式

eax寄存器中为调用号,ebx、ecx、edx、esi等寄存器则依次为参数
(1) 由于INT指令发生了不同优先级之间的控制转移,所以首先从TSS(任务状态段)中获取高优先级的核心堆栈信息(SS和ESP);
(2) 把低优先级堆栈信息(SS和ESP)保留到高优先级堆栈(即核心栈)中;
(3) 把EFLAGS,外层CS,EIP推入高优先级堆栈(核心栈)中。
(4) 通过IDT表项的0x80处,加载CS,EIP(控制转移至中断处理函数)
1
STACK
2
----------------------
3
返回用户态的EIP
4
用户态的CS
5
用户态的EFLAGS
6
用户态的ESP
7
用户态的SS(和DS相同)
Copied!
然后就进入了中断0x80的处理函数system_call了,在该函数中首先使用了一个宏SAVE_ALL,该宏的定义如下所示:
1
#define SAVE_ALL /
2
cld; /
3
pushl %es; /
4
pushl %ds; /
5
pushl %eax; /
6
pushl %ebp; /
7
pushl %edi; /
8
pushl %esi; /
9
pushl %edx; /
10
pushl %ecx; /
11
pushl %ebx; /
12
movl $(__KERNEL_DS),%edx; /
13
movl %edx,%ds; /
14
movl %edx,%es;
Copied!
该宏的功能一方面是将寄存器上下文压入到核心栈中,对于系统调用,同时也是系统调用参数的传入过程。 因为在不同特权级之间控制转换时,INT指令不同于CALL指令,它不会将外层堆栈的参数自动拷贝到内层堆栈中。所以在调用系统调用时,必须先象前面的例子里提到的那样,把参数指定到各个寄存器中,然后在陷入核心之后使用SAVE_ALL把这些保存在寄存器中的参数依次压入核心栈,这样核心才能使用用户传入的参数。
接着调用system_call_table
1
call *SYMBOL_NAME(sys_call_table)(,%eax,4)
Copied!
这里是以EAX(即前面提到的系统调用号)作为偏移,在系统调用表sys_call_table中查找处理函数入口地址,并跳转到该入口地址。

sysenter 方式

在新版本的内核中,加入了sysenter方式,sysenter 指令之前有一个 mov edx,esp的操作,后续进入Ring0通过edx定位参数
关键是在 Ring3 的代码调用了 sysenter 指令之后,CPU 会做出如下的操作:
1. 将 SYSENTER_CS_MSR 的值装载到 cs 寄存器
2. 将 SYSENTER_EIP_MSR 的值装载到 eip 寄存器
3. 将 SYSENTER_CS_MSR 的值加 8(Ring0 的堆栈段描述符)装载到 ss 寄存器。
4. 将 SYSENTER_ESP_MSR 的值装载到 esp 寄存器
5. 将特权级切换到 Ring0
6. 如果 EFLAGS 寄存器的 VM 标志被置位,则清除该标志
7. 开始执行指定的 Ring0 代码 ,同上int方式的接口函数 system_call
在 Ring0 代码执行完毕,调用 sysexit 指令退回 Ring3 时,CPU 会做出如下操作:
1. 将 SYSENTER_CS_MSR 的值加 16(Ring3 的代码段描述符)装载到 cs 寄存器
2. 将寄存器 edx 的值装载到 eip 寄存器
3. 将 SYSENTER_CS_MSR 的值加 24(Ring3 的堆栈段描述符)装载到 ss 寄存器
4. 将寄存器 ecx 的值装载到 esp 寄存器
5. 将特权级切换到 Ring3
6. 继续执行 Ring3 的代码