接前面两篇继续,这篇讲swi(software_interrupt,软中断)异常处理
在上一篇的源码中我们看到swi异常用的是ldr指令绝对跳转,其他异常用的是b指令相对跳转
W(b) vector_und + stubs_offset
W(ldr) pc, .LCvswi + stubs_offset
为什么会这样呢?
猜想因为swi的异常处理代码的入口并不在__stubs_start~~~__stubs_end之间,其他几种异常的处理代码都在
__stubs_start~~~__stubs_end之间,而相对跳转的寻址范围是正负32M
在之前讲系统调用那篇博文中,我们知道系统调用在glibc库的源码中最后会产生一个swi异常
处理器检测到这个异常会跳到异常向量表,接着会从异常向量表取出跳转指令(绝对跳转)执行:
W(ldr) pc, .LCvswi + stubs_offset
swi异常处理的入口在:
linux-3.3\arch\arm\kernel\entry-common.S 347行
ENTRY(vector_swi)
……
……
……
代码比较多,就不贴出来了,很多汇编自己也不会,根据代码和注释说一下这段代码的主要工作:
首先就是保存cpu现场,这样在执行了系统调用后,才能顺利恢复cpu用户态进程;
切换处理器工作模式。
获取系统调用号scno。
根据调用号在系统调用跳转表中索引出实际要执行的系统调用函数入口
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
并进入执行;tbl中存放的即为系统调用跳转表起始地址,根据EABI,OABI配置的不同会有不同的跳转表
sys_call_table或sys_oabi_call_table,我们这里是前者。
ENTRY(sys_call_table)
#include "calls.S"
#undef ABI
#undef OBSOLETE
可以看到跳转表就是calls.S的内容,根据系统调用号选择表中的一项,贴出的部分代码
/* 0 */ CALL(sys_restart_syscall)
CALL(sys_exit)
CALL(sys_fork_wrapper)
CALL(sys_read)
CALL(sys_write)
/* 5 */ CALL(sys_open)
CALL实际是宏定义,在arch/arm/kernel/entry-common.S中,如下:
#define CALL(x) .equ NR_syscalls,NR_syscalls+1
#include "calls.S"
#undef CALL
#define CALL(x) .long x
在定义宏CALL()的地方,我们看到calls.S已经被包含了一次,只不过在这里,不是为了建立系统调用表,
而仅仅是为了获得系统的系统调用的数量,并保存在宏NR_syscalls中。在SWI异常处理中,我们也看到,
是使用了这个宏NR_syscalls来检查用户空间传进的调用号是否有效;
从上面我们可以看到最后有效的宏定义是#define CALL(x) .long x,前面的只是用来计算系统调用的
个数(用法很妙),因此CALL(sys_open)就是.long sys_open,也就是sys_open函数的入口地址,其他一样;
下面我们以open系统调用进行分析,其系统调用号存放在linux/arch/arm/include/asm/unistd.h
文件中,如下:
#if defined(__thumb__) || defined(__ARM_EABI__)
#define __NR_SYSCALL_BASE 0
#else
#define __NR_SYSCALL_BASE __NR_OABI_SYSCALL_BASE
#endif
#define __NR_open (__NR_SYSCALL_BASE+ 5)
但是,我们在内核源码中搜索sys_open时,并未找到其函数实例,只找到其系统调用原型如下:
/include/linux/syscalls.h 521行
asmlinkage long sys_open(const char __user *filename,
int flags, umode_t mode);
那么sys_open到底在哪里定义?
这里首先给出sys_open的定义,在\linux-3.3\fs\open.c中 997行
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}
为什么sys_open函数会是上面的这个函数呢?
首先来解释一下SYSCALL_DEFINEx 中的x表示上层应用函数的参数个数,比如open()函数有3个参数,
因此会对应到SYSCALL_DEFINE3。
下面我们来分析SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
SYSCALL_DEFINE3是个宏,定义在linux-3.3\include\linux\Syscalls.h 196行
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
继续分解 226行
#define SYSCALL_DEFINEx(x, sname, ...) \
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
进一步分解 248行
#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
由此再分解为如下:
asmlinkage long sys####open(__SC_DECL##3(__VA_ARGS__))
即asmlinkage long sys_open(__SC_DECL3(__VA_ARGS__))
__SC_DECL3也是宏,它的作用就是分解出形参
#define __SC_DECL1(t1, a1) t1 a1
#define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__)
#define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__)
#define __SC_DECL4(t4, a4, ...) t4 a4, __SC_DECL3(__VA_ARGS__)
#define __SC_DECL5(t5, a5, ...) t5 a5, __SC_DECL4(__VA_ARGS__)
#define __SC_DECL6(t6, a6, ...) t6 a6, __SC_DECL5(__VA_ARGS__)
到这里已经很清楚了,SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
解析出来就是sys_open,open系统调用最后调用的就是它,然后它在调用do_sys_open。其他系统调用是一样的原理。
关于do_sys_open本篇暂不分析
阅读(2923) | 评论(0) | 转发(3) |