Linux系统调用相关的宏

#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)

系统调用相关的函数经过这里被触发,慢慢惊醒扩展最终形成我们用户可见到的系统调用函数原型。这个宏首先将函数名称最前面加上下划线,然后给出函数的参数个数以及利用__VA_ARGS__将省略号中的参数接收。

#define SYSCALL_DEFINEx(x, sname, ...)				\
	SYSCALL_METADATA(sname, x, __VA_ARGS__)			\
	__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

接下来的宏展开,实际是为了加上跟踪信息,宏SYSCALL_METADATA(sname, x, __VA_ARGS__)在真正编译的时候会被定义为空。

#define __SYSCALL_DEFINEx(x, name, ...)					\
	asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))	\
		__attribute__((alias(__stringify(SyS##name))));		\
	static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));	\
	asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__));	\
	asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))	\
	{								\
		long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));	\
		__MAP(x,__SC_TEST,__VA_ARGS__);				\
		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));	\
		return ret;						\
	}								\
	static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))

首先分析__MAP(x,__SC_DECL,__VA_ARGS__),往下看可以知道__MAP根据不同的x组合成不同的宏,这里是__MAP5()由于##是连接两个字符串的意思)。

#define __MAP0(m,...)
#define __MAP1(m,t,a) m(t,a)
#define __MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__)
#define __MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__)
#define __MAP4(m,t,a,...) m(t,a), __MAP3(m,__VA_ARGS__)
#define __MAP5(m,t,a,...) m(t,a), __MAP4(m,__VA_ARGS__)
#define __MAP6(m,t,a,...) m(t,a), __MAP5(m,__VA_ARGS__)
#define __MAP(n,...) __MAP##n(__VA_ARGS__)

那么,接下来是__MAP5(____SC_DECL,__VA_ARGS__),接着展开就是__SC_DECL(t,a), __MAP4(__SC_DECL,__VA_ARGS__)

#define __SC_DECL(t, a) t a

通过__SC_DECL展开,我们可以看到,参数类型和形参被分离了。

SYSCALL_DEFINE5(rt_sigaction, int, sig, const struct sigaction __user *, act,

struct sigaction __user *, oact,

size_t, sigsetsize, void __user *, restorer)被展开成为:

asmlinkage long sys_rt_sigactionint sigconst struct sigaction __user * act,struct sigaction __user * oact,size_t sigsetsize, void __user * restorer

__attribute__((alias(__stringify(SyS##name))));这一句是为了给系统调用函数定义一个别名,经过宏展开,系统调用的别名是SyS_rt_sigaction

#define __stringify_1(x...) #x

#define __stringify(x...) __stringify_1(x)

接下来分析asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))。这里直接对__SC_LONG进行展开。

#define __TYPE_IS_LL(t) (__same_type((t)0, 0LL) || __same_type((t)0, 0ULL))
#define __SC_LONG(t, a) __typeof(__builtin_choose_expr(__TYPE_IS_LL(t), 0LL, 0L)) a
#define __SC_CAST(t, a)	(t) a
#define __SC_ARGS(t, a)	a
#define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(t) && sizeof(t) > sizeof(long))
# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))

其中__typeof类似于typedef用于定义一个类别,__builtin_choose_expr函数是编译器内置的,用于进行选择,函数原型为__builtin_choose_exprconst_expexp1exp2)。第一个参数必须是常量表达式,如果常量表达式非零,则生成exp1,否则生成exp2

接下来是对__same_type的展开,这里利用typeofab定义为类型,然后利用__builtin_types_compatible_p判断两个类型是否一致,一致则返回0,否则返回1。主要用于判断64位长整数和32为长整数,以保证系统的安全(具体参考CVE-2009-0029漏洞)。所以在这个函数里面进行一些检验,主要调用SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));这里通过强制转换将参数转化为系统调用的参数并进行真实的系统调用。

__MAP(x,__SC_TEST,__VA_ARGS__);

这一句主要是对参数进行验证,扩展之后如下面所示:

#define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(t) && sizeof(t) > sizeof(long))

#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))

其中当e为非0的时候,经过两次非操作得到的结果是1,加上符号就是-1,这样在编译的时候就会报错,因为int只有32位而-1明显大于32.反过来当e=0的时候,经过两次非操作仍然是0-0的结果还是0.这样可以在编译的时候发现错误。主要是保证t不是long long类型,这样的话t的大小不会大于long的大小。所以整体返回0值。接着对

__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));

整个宏并没有其他的行为,只是规定参数的输入以及返回值存放的位置。

#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__)
#define asmlinkage_protect(n, ret, args...) \
	__asmlinkage_protect##n(ret, ##args)
#define __asmlinkage_protect5(ret, arg1, arg2, arg3, arg4, arg5) \
	__asmlinkage_protect_n(ret, "m" (arg1), "m" (arg2), "m" (arg3), \
			      "m" (arg4), "m" (arg5))
#define __asmlinkage_protect_n(ret, args...) \
	__asm__ __volatile__ ("" : "=r" (ret) : "0" (ret), ##args)




你可能感兴趣的:(linux,内核,编译器)