Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6269230
  • 博文数量: 2759
  • 博客积分: 1021
  • 博客等级: 中士
  • 技术积分: 4091
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-11 14:14
文章分类

全部博文(2759)

文章存档

2019年(1)

2017年(84)

2016年(196)

2015年(204)

2014年(636)

2013年(1176)

2012年(463)

分类: LINUX

2016-12-07 02:02:34

一.总体分析


二.代码分析
2.1 系统调用的初始化
在start_kernel-->trap_init
  1. void __init trap_init(void)
  2. {
  3.     set_system_gate(SYSCALL_VECTOR,&system_call);   //SYSCALL_VECTOR=0x80
  4. }
其中在include/asm/hw_irq.h中,定义了#define SYSCALL_VECTOR 0x80
2.2内核空间: start_kernel --> rest_init --> kernel_thread --> 线程函数init中调用了exec
  1. static int init(void * unused)
  2. {
  3. ......
  4.     if (execute_command)    //这个execute_command=/bin/sh
  5.         execve(execute_command,argv_init,envp_init);
  6. ...   
  7. }
2.3 内核空间:这个execve是一个宏定义
在include/asm/unistd.h中有execve的定义
即:
static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp)
  1. #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
  2. type name(type1 arg1,type2 arg2,type3 arg3) \
  3. { \
  4. long __res; \
  5. __asm__ volatile ("int $0x80" \
  6.     : "=a" (__res) \
  7.     : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
  8.          "d" ((long)(arg3))); \
  9. __syscall_return(type,__res); \
  10. }
展开后:
  1. int execve(const char * file,char ** argv,char ** envp) 
  2. {
  3.    long __res;
  4.    __asm__ volatile ("int $0x80"
  5.             : "=a" (__res)
  6.             : "0" (__NR_execve),"b" ((long)(file)),"c" ((long)(argv)), "d" ((long)(envp)));
  7.     if ((unsigned long)__res >= (unsigned long)(-125)) 
  8.     {
  9.         errno = -(__res);
  10.         __res = -1;
  11.     }
  12.     return int __res;    
  13. }
2.4内核空间: 进入system_call中
在arch/i386/kernel/entry.S中L194
  1. ENTRY(system_call)
  2.     pushl %eax                      //2.4.1关于系统调用号
  3.     SAVE_ALL                        //2.4.2保存所有的寄存器
  4.     GET_CURRENT(%ebx)               //2.4.3获取current
  5.     testb $0x02,tsk_ptrace(%ebx)    
  6.     jne tracesys
  7.     cmpl $(NR_syscalls),%eax        //检查系统调用号是不是越界
  8.     jae badsys
  9.     call *SYMBOL_NAME(sys_call_table)(,%eax,4)  //2.5这儿是调用sys_execve
  10.     movl %eax,EAX(%esp)             //将返回值保存在esp+6th
  11. ENTRY(ret_from_sys_call)
  12.     cli                             # need_resched and signals atomic test
  13.     cmpl $0,need_resched(%ebx)
  14.     jne reschedule
  15.     cmpl $0,sigpending(%ebx)
  16.     jne signal_return
  17. restore_all:
  18.     RESTORE_ALL                      //2.6这儿
2.4.1 关于系统调用号
在 arch/i386/kernel/entry.S中
  1. .data
  2. ENTRY(sys_call_table)
  3. ...
  4.     .long SYMBOL_NAME(sys_unlink)      /* 10 */
  5.     .long SYMBOL_NAME(sys_execve)      //sys_execve的系统调用号是11
linux-2.4.18/include/asm-i386/unistd.h中
  1.  #define __NR_execve 11
2.4.2 关于寄存器
只有ebx ecx edx esi, edi这5个寄存器可以用作参数传递
  1. struct pt_regs {
  2.     long ebx;      //参数1,c函数从左向右数的第1个参数
  3.     long ecx;      //参数2,c函数从左向右数的第2个参数
  4.     long edx;      //参数3,c函数从左向右数的第3个参数
  5.     long esi;      //参数4,c函数从左向右数的第4个参数
  6.     long edi;      //参数5,c函数从左向右数的第5个参数
  7.     long ebp;      //不能用作参数
  8.     long eax;      //这儿的eax是系统调用号
  9.     int xds;
  10.     int xes;
  11.     long orig_eax;
  12.     long eip;
  13.     int xcs;
  14.     long eflags;
  15.     long esp;
  16.     int xss;
  17. };
在linux-2.4.18/arch/i386/kernel/entry.S中
  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
2.4.3 获取current
  1. #define GET_CURRENT(reg) \
  2.     movl $-8192, reg; \
  3.     andl %esp, reg
则GET_CURRENT(%ebx) 就是
movl $-8192, %ebx
andl %esp, %ebx        
  1. 语句的意思是将ESP的内容与8191UL的反码按位进行与操作,之后再把结果赋值给current,
  2. 其中8191UL=8192-1=2^13-1,计算过程如下:
  3. 8192UL=2^13 --> 0000 0000 0000 0000 0010 0000 0000 0000
  4. 8191UL      --> 0000 0000 0000 0000 0001 1111 1111 1111
  5. ~8191UL(反码)    1111 1111 1111 1111 1110 0000 0000 0000
  6. 0xc2343ffe -->  1100 0010 0011 0100 0011 1111 1111 1110
  7. andl结果:  -->  1100 0010 0011 0100 0010 0000 0000 0000
  8.                  0xc  2    3     4    2   0    0     0
  9. 即,将esp的后12位清0,页对齐

  10. 转自《解析Linux内核获取当前进程指针的方法》
    http://blog.sina.com.cn/s/blog_630ebdb50100pg8r.html


2.5系统调用sys_execve
  1. asmlinkage int sys_execve(struct pt_regs regs)
  2. {
  3.     int error;
  4.     char * filename;
  5. //用户空间的调用-->execve(execute_command,argv_init,envp_init);
  6.     filename = getname((char *) regs.ebx);   //ebx是用户空间的c函数从左向右数第1个参数execute_command="/bin/sh"
  7.     error = PTR_ERR(filename);
  8.     if (IS_ERR(filename))
  9.         goto out;
  10.     error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
  11.     if (error == 0)
  12.         current->ptrace &= ~PT_DTRACE;
  13.     putname(filename);
  14. out:
  15.     return error;
  16. }



2.6关于RESTORE_ALL
  1. #define RESTORE_ALL    \
  2.     popl %ebx;    \       //将SAVE_ALL中压栈的寄存器恢复
  3.     popl %ecx;    \
  4.     popl %edx;    \
  5.     popl %esi;    \
  6.     popl %edi;    \
  7.     popl %ebp;    \
  8.     popl %eax;    \
  9. 1:  popl %ds;    \
  10. 2:  popl %es;    \
  11.     addl $4,%esp;    \      //system_call一开始压栈的eax直接加4丢掉了
  12. 3:    iret;        \        //从内核空间返回用户空间,iret用到的寄存器是自动压栈的
  13. .section .fixup,"ax";    \
  14. 4:    movl $0,(%esp);    \
  15.     jmp 1b;        \
  16. 5:    movl $0,(%esp);    \
  17.     jmp 2b;        \
  18. 6:    pushl %ss;    \
  19.     popl %ds;    \
  20.     pushl %ss;    \
  21.     popl %es;    \
  22.     pushl $11;    \
  23.     call do_exit;    \
  24. .previous;        \
  25. .section __ex_table,"a";\
  26.     .align 4;    \
  27.     .long 1b,4b;    \
  28.     .long 2b,5b;    \
  29.     .long 3b,6b;    \
  30. .previous
要理解RESTORE_ALL,需在先知道当前的堆栈中的数据,如下所示:
出自arch/i386/kernel/entry.S的注释

  1. * Stack layout in 'ret_from_system_call':
  2.  *     ptrace needs to have all regs on the stack.
  3.  *    if the order here is changed, it needs to be
  4.  *    updated in fork.c:copy_process, signal.c:do_signal,
  5.  *    ptrace.c and ptrace.h
  6.  *
  7.  *     0(%esp) - %ebx      -->红色部分是在SAVE_ALL中压栈的
  8.  *     4(%esp) - %ecx
  9.  *     8(%esp) - %edx
  10.  *     C(%esp) - %esi
  11.  *    10(%esp) - %edi
  12.  *    14(%esp) - %ebp
  13.  *    18(%esp) - %eax
  14.  *    1C(%esp) - %ds
  15.  *    20(%esp) - %es
  16.  *    24(%esp) - orig_eax   -->绿色部分是system_call的一开始压栈的
  17.  *    28(%esp) - %eip       -->蓝色部分是中断自动压栈的
  18.  *    2C(%esp) - %cs
  19.  *    30(%esp) - %eflags
  20.  *    34(%esp) - %oldesp
  21.  *    38(%esp) - %oldss
关于上述蓝色部分如下图所示:

上图出自《Linux内核完全注释》P117

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