一.总体分析
二.代码分析
2.1 系统调用的初始化
在start_kernel-->trap_init
-
void __init trap_init(void)
-
{
-
set_system_gate(SYSCALL_VECTOR,&system_call); //SYSCALL_VECTOR=0x80
-
}
其中在include/asm/hw_irq.h中,定义了#define SYSCALL_VECTOR 0x80
2.2内核空间: start_kernel --> rest_init --> kernel_thread --> 线程函数init中调用了exec
-
static int init(void * unused)
-
{
-
......
-
if (execute_command) //这个execute_command=/bin/sh
-
execve(execute_command,argv_init,envp_init);
-
...
-
}
-
2.3
内核空间:这个execve是一个宏定义
在include/asm/unistd.h中有execve的定义
即:
static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp)
-
#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
-
type name(type1 arg1,type2 arg2,type3 arg3) \
-
{ \
-
long __res; \
-
__asm__ volatile ("int $0x80" \
-
: "=a" (__res) \
-
: "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
-
"d" ((long)(arg3))); \
-
__syscall_return(type,__res); \
-
}
展开后:
-
int execve(const char * file,char ** argv,char ** envp)
-
{
-
long __res;
-
__asm__ volatile ("int $0x80"
-
: "=a" (__res)
-
: "0" (__NR_execve),"b" ((long)(file)),"c" ((long)(argv)), "d" ((long)(envp)));
-
if ((unsigned long)__res >= (unsigned long)(-125))
-
{
-
errno = -(__res);
-
__res = -1;
-
}
-
return int __res;
-
}
2.4
内核空间: 进入system_call中
在arch/i386/kernel/entry.S中L194
-
ENTRY(system_call)
-
pushl %eax //2.4.1关于系统调用号
-
SAVE_ALL //2.4.2保存所有的寄存器
-
GET_CURRENT(%ebx) //2.4.3获取current
-
testb $0x02,tsk_ptrace(%ebx)
-
jne tracesys
-
cmpl $(NR_syscalls),%eax //检查系统调用号是不是越界
-
jae badsys
-
call *SYMBOL_NAME(sys_call_table)(,%eax,4) //2.5这儿是调用sys_execve
-
movl %eax,EAX(%esp) //将返回值保存在esp+6th
-
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 //2.6这儿
2.4.1 关于系统调用号
在 arch/i386/kernel/entry.S中
-
.data
-
ENTRY(sys_call_table)
-
...
-
.long SYMBOL_NAME(sys_unlink) /* 10 */
-
.long SYMBOL_NAME(sys_execve) //sys_execve的系统调用号是11
在linux-2.4.18/include/asm-i386/unistd.h中
2.4.2 关于寄存器
只有ebx ecx edx esi, edi这5个寄存器可以用作参数传递
-
struct pt_regs {
-
long ebx; //参数1,c函数从左向右数的第1个参数
-
long ecx; //参数2,c函数从左向右数的第2个参数
-
long edx; //参数3,c函数从左向右数的第3个参数
-
long esi; //参数4,c函数从左向右数的第4个参数
-
long edi; //参数5,c函数从左向右数的第5个参数
-
long ebp; //不能用作参数
-
long eax; //这儿的eax是系统调用号
-
int xds;
-
int xes;
-
long orig_eax;
-
long eip;
-
int xcs;
-
long eflags;
-
long esp;
-
int xss;
-
};
在linux-2.4.18/arch/i386/kernel/entry.S中
-
#define SAVE_ALL \
-
cld; \
-
pushl %es; \
-
pushl %ds; \
-
pushl %eax; \
-
pushl %ebp; \
-
pushl %edi; \
-
pushl %esi; \
-
pushl %edx; \
-
pushl %ecx; \
-
pushl %ebx; \
-
movl $(__KERNEL_DS),%edx; \
-
movl %edx,%ds; \
-
movl %edx,%es
2.4.3 获取current
-
#define GET_CURRENT(reg) \
-
movl $-8192, reg; \
-
andl %esp, reg
则GET_CURRENT(%ebx) 就是
movl $-8192, %ebx
andl %esp, %ebx
-
语句的意思是将ESP的内容与8191UL的反码按位进行与操作,之后再把结果赋值给current,
-
其中8191UL=8192-1=2^13-1,计算过程如下:
-
8192UL=2^13 --> 0000 0000 0000 0000 0010 0000 0000 0000
-
8191UL --> 0000 0000 0000 0000 0001 1111 1111 1111
-
~8191UL(反码) 1111 1111 1111 1111 1110 0000 0000 0000
-
0xc2343ffe --> 1100 0010 0011 0100 0011 1111 1111 1110
-
andl结果: --> 1100 0010 0011 0100 0010 0000 0000 0000
-
0xc 2 3 4 2 0 0 0
-
即,将esp的后12位清0,页对齐
-
-
转自《解析Linux内核获取当前进程指针的方法》
http://blog.sina.com.cn/s/blog_630ebdb50100pg8r.html
2.5系统调用sys_execve
-
asmlinkage int sys_execve(struct pt_regs regs)
-
{
-
int error;
-
char * filename;
-
//用户空间的调用-->execve(execute_command,argv_init,envp_init);
-
filename = getname((char *) regs.ebx); //ebx是用户空间的c函数从左向右数第1个参数execute_command="/bin/sh"
-
error = PTR_ERR(filename);
-
if (IS_ERR(filename))
-
goto out;
-
error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, ®s);
-
if (error == 0)
-
current->ptrace &= ~PT_DTRACE;
-
putname(filename);
-
out:
-
return error;
-
}
2.6关于RESTORE_ALL
-
#define RESTORE_ALL \
-
popl %ebx; \ //将SAVE_ALL中压栈的寄存器恢复
-
popl %ecx; \
-
popl %edx; \
-
popl %esi; \
-
popl %edi; \
-
popl %ebp; \
-
popl %eax; \
-
1: popl %ds; \
-
2: popl %es; \
-
addl $4,%esp; \ //system_call一开始压栈的eax直接加4丢掉了
-
3: iret; \ //从内核空间返回用户空间,iret用到的寄存器是自动压栈的
-
.section .fixup,"ax"; \
-
4: movl $0,(%esp); \
-
jmp 1b; \
-
5: movl $0,(%esp); \
-
jmp 2b; \
-
6: pushl %ss; \
-
popl %ds; \
-
pushl %ss; \
-
popl %es; \
-
pushl $11; \
-
call do_exit; \
-
.previous; \
-
.section __ex_table,"a";\
-
.align 4; \
-
.long 1b,4b; \
-
.long 2b,5b; \
-
.long 3b,6b; \
-
.previous
要理解
RESTORE_ALL,需在先知道当前的堆栈中的数据,如下所示:
出自arch/i386/kernel/entry.S的注释
-
* Stack layout in 'ret_from_system_call':
-
* ptrace needs to have all regs on the stack.
-
* if the order here is changed, it needs to be
-
* updated in fork.c:copy_process, signal.c:do_signal,
-
* ptrace.c and ptrace.h
-
*
-
* 0(%esp) - %ebx -->红色部分是在SAVE_ALL中压栈的
-
* 4(%esp) - %ecx
-
* 8(%esp) - %edx
-
* C(%esp) - %esi
-
* 10(%esp) - %edi
-
* 14(%esp) - %ebp
-
* 18(%esp) - %eax
-
* 1C(%esp) - %ds
-
* 20(%esp) - %es
-
* 24(%esp) - orig_eax -->绿色部分是system_call的一开始压栈的
-
* 28(%esp) - %eip -->蓝色部分是中断自动压栈的
-
* 2C(%esp) - %cs
-
* 30(%esp) - %eflags
-
* 34(%esp) - %oldesp
-
* 38(%esp) - %oldss
关于上述蓝色部分如下图所示:
上图出自《Linux内核完全注释》P117
阅读(2123) | 评论(0) | 转发(1) |