系统调用(二)

系统调用(二)


SYSCALL_DEFINEx函数调用

文件syscall.h在include/linux/中.

在select和epoll的分析中, 出现了好几次SYSCALL_DEFINEx()的函数, 这些究竟是些什么呢? 现在我们就来探讨一下源码中他们是什么.

#define SYSCALL_DEFINE0(name)      asmlinkage long sys_##name(void)
#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__)

#define SYSCALL_DEFINEx(x, name, ...)                   \
    asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))

看了这几个宏定义, 在结合我们传入参数的个数, 大致也能想到x是代表传入参数的值.(如果对define的#, ## 符号的操作不了解, 可以先看下这篇)

#define SYSCALL_DEFINEx(x, name, ...)                   \
    asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__));       \
    static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__));   \
    asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__))        \
    {                               \
        __SC_TEST##x(__VA_ARGS__);              \
        return (long) SYSC##name(__SC_CAST##x(__VA_ARGS__));    \
    }                               \
    SYSCALL_ALIAS(sys##name, SyS##name);                \
    static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__))

这个函数宏定义看起来很不理解, 这里我们从后面开始看, 倒数第二排有一个SYSCALL_ALIAS(sys##name, SyS##name), 就是给sys##name取一个别名, 为SyS##name, 所以使用sys##name就相当调用SyS##name函数.

#ifdef CONFIG_PPC64
#define SYSCALL_ALIAS(alias, name)                  \
    asm ("\t.globl " #alias "\n\t.set " #alias ", " #name "\n"  \
         "\t.globl ." #alias "\n\t.set ." #alias ", ." #name)
#else
#define SYSCALL_ALIAS(alias, name)                  \
    asm ("\t.globl " #alias "\n\t.set " #alias ", " #name)
#endif

好了, 现在我们从

  • 第一行开始看, 现在执行的SYSCALL_DEFINEx也就是确认执行上面的7个宏定义函数.
  • 第二, 三行是函数的声明
  • 第四行开始就是SyS##name 的函数定义了
  • 最后一行是调用SYS##name 函数

__SC_DECL##x宏定义

我们再来看传入的参数.先来看参数__SC_DECL##x(__VA_ARGS__)的宏定义

#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__)

可以看出来该宏定义就是一层一层的将参数展开. 这里我用一个select的函数做例子来解释

SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp, fd_set __user *, exp, struct timeval __user *, tvp)

展开过程如下:

__SC_DECL5(int, n, fd_set user *, inp, fd_set user *, outp, fd_set user *, exp, struct timeval user *, tvp)     ->  

int n, __SC_DECL4(fd_set user *, inp, fd_set user *, outp, fd_set user *, exp, struct timeval user *, tvp)  ->  

int n, fd_set inp, __SC_DECL3(fd_set user *, outp, fd_set user *, exp, struct timeval __user *, tvp)    ->  

int n, fd_set inp, fd_set outp, __SC_DECL2(fd_set user *, outp, fd_set user *, exp, struct timeval __user *, tvp)   ->  

int n, fd_set inp, fd_set outp, fd_set exp, __SC_DECL1(truct timeval __user *, tvp)     ->  

int n, fd_set inp, fd_set outp, fd_set exp, struct timeval *, tvp

经过者一系列的转化, 终于将我们所有的参数都提取出来了.

同样的, 调用long SyS##name(__SC_LONG##x(__VA_ARGS__))也是一步一步的展开, 但是有一点不一样, 该调用会把参数都强行转化为long类型. 是因为不转为long类型, 会有一定的漏洞. 具体漏洞有兴趣的可以去查CVE-2009-2009.

#define __SC_LONG1(t1, a1)  long a1
#define __SC_LONG2(t2, a2, ...) long a2, __SC_LONG1(__VA_ARGS__)
#define __SC_LONG3(t3, a3, ...) long a3, __SC_LONG2(__VA_ARGS__)
#define __SC_LONG4(t4, a4, ...) long a4, __SC_LONG3(__VA_ARGS__)
#define __SC_LONG5(t5, a5, ...) long a5, __SC_LONG4(__VA_ARGS__)
#define __SC_LONG6(t6, a6, ...) long a6, __SC_LONG5(__VA_ARGS__)
#define __SC_CAST1(t1, a1)  (t1) a1
#define __SC_CAST2(t2, a2, ...) (t2) a2, __SC_CAST1(__VA_ARGS__)
#define __SC_CAST3(t3, a3, ...) (t3) a3, __SC_CAST2(__VA_ARGS__)
#define __SC_CAST4(t4, a4, ...) (t4) a4, __SC_CAST3(__VA_ARGS__)
#define __SC_CAST5(t5, a5, ...) (t5) a5, __SC_CAST4(__VA_ARGS__)
#define __SC_CAST6(t6, a6, ...) (t6) a6, __SC_CAST5(__VA_ARGS__)
#define __SC_TEST(type)     BUILD_BUG_ON(sizeof(type) > sizeof(long))
#define __SC_TEST1(t1, a1)  __SC_TEST(t1)
#define __SC_TEST2(t2, a2, ...) __SC_TEST(t2); __SC_TEST1(__VA_ARGS__)
#define __SC_TEST3(t3, a3, ...) __SC_TEST(t3); __SC_TEST2(__VA_ARGS__)
#define __SC_TEST4(t4, a4, ...) __SC_TEST(t4); __SC_TEST3(__VA_ARGS__)
#define __SC_TEST5(t5, a5, ...) __SC_TEST(t5); __SC_TEST4(__VA_ARGS__)
#define __SC_TEST6(t6, a6, ...) __SC_TEST(t6); __SC_TEST5(__VA_ARGS__)

总结

其实具体的宏定义流程还是很容易看懂的, 就只是体会到了宏定义的用法. 每个参数的读出都是一环叩一环的, 对于define的运用, 自己还要区总结一下, 然后在来重新为这篇加上几句. 看到这个源码我也明白为什么系统调用的参数不能超过6个, 毕竟宏定义也就只设置了6个, 也发现文件的函数也没有声明超过6个参数的函数, 同时通用寄存器也是6个, 每个寄存器存放一个数据. 不过不代表参数不能超过6个, 毕竟还可以用栈来传递参数, 这里只是说系统调用的函数, 大概收获的也就是这些吧.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值