12: tbl .req r8 @ syscall table pointer
13: why .req r8 @ Linux syscall (!= 0)
14: tsk .req r9 @ current thread_info
.req 是伪汇编,以 scno .req r7 为例,表示scno是寄存器r7的别名。
get_thread_info tsk
其中,tsk是寄存器r9的别名,get_thread_info是一个宏定义,如下:
1: .macro get_thread_info, rd
2: mov rd, sp, lsr #13
3: mov rd, rd, lsl #13
4: .endm
即:将sp进行8KB对齐后的值赋给寄存器r9,什么意思?
这个就涉及到Linux的内核栈了。Linux为每个进程都分配了一个8KB的内核栈,在内核栈的尾端存放有关于这个进程的struct therad_info结构:
1: struct thread_info {
2: unsigned long flags; /* low level flags */
3: int preempt_count; /* 0 => preemptable, <0 => bug */
4: mm_segment_t addr_limit; /* address limit */
5: struct task_struct *task; /* main task structure */
6: struct exec_domain *exec_domain; /* execution domain */
7: __u32 cpu; /* cpu */
8: __u32 cpu_domain; /* cpu domain */
9: struct cpu_context_save cpu_context; /* cpu context */
10: __u32 syscall; /* syscall number */
11: __u8 used_cp[16]; /* thread used copro */
12: unsigned long tp_value;
13: struct crunch_state crunchstate;
14: union fp_state fpstate __attribute__((aligned(8)));
15: union vfp_state vfpstate;
16: #ifdef CONFIG_ARM_THUMBEE
17: unsigned long thumbee_state; /* ThumbEE Handler Base register */
18: #endif
19: struct restart_block restart_block;
20: };
通过上面的操作,寄存器r9中就是这个进程的thread_info结构的起始地址。
sys_call_table
entry-common.S (archarmkernel)
1: .type sys_call_table, #object
2: ENTRY(sys_call_table)
3: #include "calls.S"
4: #undef ABI
5: #undef OBSOLETE
其中,calls.S的内容如下:
1: /*
2: * linux/arch/arm/kernel/calls.S
3: *
4: * Copyright (C) 1995-2005 Russell King
5: *
6: * This program is free software; you can redistribute it and/or modify
7: * it under the terms of the GNU General Public License version 2 as
8: * published by the Free Software Foundation.
9: *
10: * This file is included thrice in entry-common.S
11: */
12: /* 0 */ CALL(sys_restart_syscall)
13: CALL(sys_exit)
14: CALL(sys_fork_wrapper)
15: CALL(sys_read)
16: CALL(sys_write)
17: /* 5 */ CALL(sys_open)
18: CALL(sys_close)
19: CALL(sys_ni_syscall) /* was sys_waitpid */
20: CALL(sys_creat)
21: CALL(sys_link)
22: /* 10 */ CALL(sys_unlink)
23: CALL(sys_execve_wrapper)
24: CALL(sys_chdir)
25: CALL(OBSOLETE(sys_time)) /* used by libc4 */
26: CALL(sys_mknod)
27: ......
28: /* 355 */ CALL(sys_signalfd4)
29: CALL(sys_eventfd2)
30: CALL(sys_epoll_create1)
31: CALL(sys_dup3)
32: CALL(sys_pipe2)
33: /* 360 */ CALL(sys_inotify_init1)
34: CALL(sys_preadv)
35: CALL(sys_pwritev)
36: #ifndef syscalls_counted
37: .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
38: #define syscalls_counted
39: #endif
40: .rept syscalls_padding
41: CALL(sys_ni_syscall)
42: .endr
关于这个部分的更多介绍参见:
http://www.cnblogs.com/pengdonglin137/p/3714981.html
bics r10, r10, #0xff000000
执行这个操作的时候,r10中存放的是SWI instruction,在我们的例子中就是(a.dis):
即:r10 为 0xEF000000
显然,bics这条指令下面的两个语句由于条件不成立,无法获得执行。这条指令的作用是获得系统调用号
可以参考这个手册,看一下svc执行的格式:
http://files.cnblogs.com/pengdonglin137/DUI0203IC_rvct_developer_guide.pdf
可以看到,[23:0]存放的就是svc指令后面的那个立即数,也即系统调用号。
不过需要注意的是:我们这里并没有这样做,我们的做法是(a.dis中可以看到):
使用的是svc 0,后面跟的并不是系统调用号,而是0,这里把系统调用号存放在了寄存器r7中(a.dis中):
可以看到,由于使用的sys_open系统调用,所以把它的系统调用号5存放到了寄存器r7当中
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
这里的scno是就是寄存器r7的别名,它的值是sys_open的系统调用号5,由于在calls.S中每个系统调用标号占用4个字节,所以这个将scno的值乘以4然后再加上tbl,tbl是系统调用表sys_call_table的基地址。然后就跳入开始执行sys_open了。
asmlinkage long sys_open(const char __user *filename,
int flags, int mode);
那么sys_open在哪呢?在内核源码中直接搜索sys_open,无法搜到它的实现代码,实际上它是在fs/open.c中实现的:
1: SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
2: {
3: long ret;
4:
5: if (force_o_largefile())
6: flags |= O_LARGEFILE;
7:
8: ret = do_sys_open(AT_FDCWD, filename, flags, mode);
9: /* avoid REGPARM breakage on x86: */
10: asmlinkage_protect(3, ret, filename, flags, mode);
11: return ret;
12: }
其中SYSCALL_DEFINE3是一个宏:
syscalls.h (includelinux)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
SYSCALL_DEFINEx也是一个宏:
syscalls.h (includelinux)
#define SYSCALL_DEFINEx(x, sname, ...)
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
__SYSCALL_DEFINEx仍然是个宏:
syscalls.h (includelinux)
#define __SYSCALL_DEFINEx(x, name, ...)
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
所以展开后的结果就是:
asmlinkage long sys_open(__SC_DECL3(__VA_ARGS__))
其中,__SC_DECL3定义如下:
syscalls.h (includelinux)
1: #define __SC_DECL1(t1, a1) t1 a1
2: #define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__)
3: #define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__)
所以最终的结果如下:
1: asmlinkage long sys_open(const char __user *filename, int flags, int mode)
2: {
3: long ret;
4:
5: if (force_o_largefile())
6: flags |= O_LARGEFILE;
7:
8: ret = do_sys_open(AT_FDCWD, filename, flags, mode);
9: /* avoid REGPARM breakage on x86: */
10: asmlinkage_protect(3, ret, filename, flags, mode);
11: return ret;
12:
13: }
关于sys_open本身的实现这里就不深入分析了。
接下来看一下返回。
adr lr, ret_fast_syscall @ return address
当sys_open中return后,便跳入ret_fast_syscall处开始执行:
1: /*
2: * This is the fast syscall return path. We do as little as
3: * possible here, and this includes saving r0 back into the SVC
4: * stack.
5: */
6: ret_fast_syscall:
7: UNWIND(.fnstart )
8: UNWIND(.cantunwind )
9: disable_irq @ disable interrupts
10:
ldr r1, [tsk, #TI_FLAGS] @将thread_info中的flags成员存放到r1中
11: tst r1, #_TIF_WORK_MASK
12: bne fast_work_pending
13:
14: /* perform architecture specific actions before user return */
15: arch_ret_to_user r1, lr
16:
17: @ fast_restore_user_regs
18: ldr r1, [sp, #S_OFF + S_PSR] @ get calling cpsr
19: ldr lr, [sp, #S_OFF + S_PC]! @ get pc
[1] [2] [3] [4]