Posted on 2008-02-18 14:35
ZelluX 阅读(551)
评论(4) 编辑 收藏 所属分类:
Linux 、
System
_syscall 宏:
最简单的没有参数的系统调用的实现:
/**//* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name)); \
__syscall_return(type,__res); \
}
以getuid()为例,_syscall0(int, getuid)展开后就变成
int getuid(void)
{
long __res;
__asm__ volatile("int $0x80"
:"=a" (__res)
:"0" (__NR_getuid));
__syscall_return(int, __res);
}
程序把系统调用号__NR_getuid放入eax寄存器,然后调用软中断。
include/asm-i386/hw_irq.h中的定义:
00025 #define SYSCALL_VECTOR 0x80;
arch/i386/kernel/traps.c中把该中断号绑定到system_call函数:
00944 set_system_gate(SYSCALL_VECTOR,&system_call);
system_call函数在arch/i386/kernel/entry.S中:
ENTRY(system_call)
pushl %eax # save orig_eax
SAVE_ALL
GET_CURRENT(%ebx)
testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS
jne tracesys
cmpl $(NR_syscalls),%eax
jae badsys
call *SYMBOL_NAME(sys_call_table)(,%eax,4)
movl %eax,EAX(%esp) # save the return value
ENTRY(ret_from_sys_call)
cli # need_resched and signals atomic test
cmpl $0,need_resched(%ebx)
jne reschedule
cmpl $0,sigpending(%ebx)
jne signal_return
restore_all:
RESTORE_ALL 主要步骤:
1. 保留一份系统调用号的最初拷贝
2. 保存堆栈环境(SAVE_ALL)
3. 得到当前task_struct的地址,保存到ebx中
4. 检查系统调用号
5. 根据%eax调用号计算地址,调用相应函数
6. 在entry.S后面可以看到,
.data
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/
.long SYMBOL_NAME(sys_getuid16)
.long SYMBOL_NAME(sys_stime) /* 25 */
.long SYMBOL_NAME(sys_ptrace)
sys_call_table + %eax * 4是sys_getuid16地址,kernel/uid16.c中:
asmlinkage long sys_getuid16(void)
{
return high2lowuid(current->uid);
} 很简单的处理代码,返回当前进程的uid。这里asmlinkage修饰符表示函数必须从堆栈中,而不是从寄存器中拿参数(防止gcc用寄存器传参优化)。
7. 保存返回值eax到堆栈中的eax
8. RESTORE_ALL
另外这里再提一下GET_CURRENT的实现
#define GET_CURRENT(reg) \
movl $-8192, reg; \
andl %esp, reg 很巧妙的实现,把栈指针与掩码-8192做与操作,末尾13位清零,就是当前的进程的task_struct地址了。
接下来是利用内核模块动态添加一个系统调用的例程,由于2.4.20以后sys_call_table符号不再被导出了,要获得这个地址得手动hack。尚未成功,下次回过头来看看。