Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5771855
  • 博文数量: 675
  • 博客积分: 20301
  • 博客等级: 上将
  • 技术积分: 7671
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-31 16:15
文章分类

全部博文(675)

文章存档

2012年(1)

2011年(20)

2010年(14)

2009年(63)

2008年(118)

2007年(141)

2006年(318)

分类: LINUX

2007-12-19 12:36:16

发信人: luohandsome (苹果王子), 信区: KernelTech
标  题: [yc]读核感悟-伪装现场-系统调用参数
发信站: 水木社区 (Wed Dec 19 10:05:16 2007), 转信

        内核支配了整个计算机的硬件资源,好像一位独裁者,高高在上。他有时候必须
像法官一样公正,有时候则必须像狐狸一样狡猾。伪装现场就是他的拿手好戏。
        系统调用是很特别的函数,因为它里面实现了用户态到内核态的转换。应用程序
要创建新进程,不可能在用户态直接调用sys_fork()。这就需要内核为sys_fork()伪
装一下调用现场。
        比如fork()系统调用,它有一个简洁得不能再简洁的接口。不过它在内核中的对
应函数中的声明却是:
asmlinkage int sys_fork(struct pt_regs regs)
如果有兴趣看早期的Linux源代码版本(0.95),它的声明是这样的:
int sys_fork(long ebx,long ecx,long edx,

long esi, long edi, long ebp, long eax, long ds,

long es, long fs, long gs, long orig_eax,

long eip,long cs,long eflags,long esp,long ss)
这些参数是如何来的呢。为什么用户态参数与内核态参数不一致?
又如clone(),vfork()这几个系统调用在用户态的参数个数和类型都不一样。但在
内核态都致。
asmlinkage int sys_clone(struct pt_regs regs)
asmlinkage int sys_vfork(struct pt_regs regs)
接下去,我们可以看到,内核是多么巧妙地设置堆栈,让内核的函数感觉就好像上
层函数在调用它一样。

        我们很容易得知,这些参数是在系统调用进入内核时由int 0x80指令和SAVE_ALL
宏把一些寄存器压入内核堆栈的。压入的寄存器数量和顺序是一致的,它们恰好与s
truct pt_regs一一对应。从这个角度讲,所有的系统调用获得的参数是形式是一样
的。
026 struct pt_regs {

027         long ebx;

028         long ecx;

029         long edx;

030         long esi;

031         long edi;

032         long ebp;

033         long eax;

034         int  xds;

035         int  xes;

036         long orig_eax;

037         long eip;

038         int  xcs;

039         long eflags;

040         long esp;

041         int  xss;

042 };
其中xss到eip由int指令压入内核堆栈。orig_eax到ebx为ENTRY(system_call)压入
堆栈。orig_eax为系统调用号。eax作为返回值。ebp~ebx作为系统调用的参数。

        linux的系统调用在内核中对应函数如sys_fork()的声明前都有一个asmlinage的
宏。它被定义为:
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
        也就是说,所有的参数都通过堆栈传递。注意,这里的堆栈传递已经在内核态了
,与系统调用参数通过寄存器传递并不矛盾(那个是之前在用户态到内核态的切换过
程中)。
        这样就意味着我们只要巧妙地设置好堆栈,让sys_fork()产生一种错觉,好像是
上层的一个函数直接调用sys_fork()一样。事实上sys_fork()也的确可以在内核态直
接调用。
        我们又观察到:内核中_syscall0,_syscall1,_syscall2,。。。这些宏用来封
装系统调用(只是内核自己用,glibc不用这些宏而已。)传递参数的方式是这样的
:参数1~参数6正好放在ebx ecx edx esi edi ebp中,这恰好与pt_regs中的顺序相
对应。为什么采用这样的一种顺序呢?
        1.这与编译器的编译规则有关。标准的C函数编译时,总是从右往左依次把参数压
入堆栈。执行到函数内部时,就根据这样的规则依次从堆栈中取出参数。所以在内核
中也不例外。当应用程序执行系统调用,进入到内核态中的ENTRY(system_call)调用
        call *sys_call_table(,%eax,4)
        时,所有的参数都在堆栈中准备就绪。
        具体这些参数怎么理解,就取决于函数的定义了。
        如果认为内核堆栈中放的是个结构体pt_regs,就可以定义为
        asmlinkage int sys_fork(struct pt_regs regs)
        如果认为内核堆栈中放的是一个个整数,就可以定义为
        asmlinkage int sys_fork(long ebx,long ecx,long edx,

long esi, long edi, long ebp, long eax, long ds,

long es, long fs, long gs, long orig_eax,

long eip,long cs,long eflags,long esp,long ss)
        其它的系统调用也类似。
        通过巧妙的构造堆栈,达到调用内核函数的目的。
        除了系统调用,还有其它函数如do_page_fault()等等,也是用类似的手段。
--
联系方式:
QQ:61347014(Linux下上不了。。。)
MSN:luohandsome@hotmail.com(经常挂着)


※ 来源:·水木社区 newsmth.net·[FROM: 60.176.204.*]

===================================================
发信人: fishhunter (fishhunter), 信区: KernelTech
标  题: Re: [yc]读核感悟-伪装现场-系统调用参数
发信站: 水木社区 (Wed Dec 19 11:44:06 2007), 转信

流程是不是这个样子的:
用户空间程序将系统调用的参数,存入寄存器中。
int 0x80指令陷入到内核态,并将保存参数的这几个寄存器压栈(内核栈);
ENTRY(system_call)调用SAVE_ALL,将其他的一些寄存器压栈。
从而构建了系统调用的内核栈的映像。

还有一篇文章可以参考:
linux内核分析---系统调用实现代码分析

阅读(1154) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~