ARM64内核系统调用添加方法(基于kernel-4.9)

既上一篇介绍了ARM64内核系统调用详解之后,本篇文章来介绍如何添加自己的系统调用到内核中。

根据上一篇我们知道如下关键的几点:

(1)ARM64的系统调用分为32-bit模式和64-bit模式。

(2)32-bit模式的系统调用syscall定义在头文件arch/arm64/include/asm/unistd32.h

(3)64-bit模式的系统调用syscall定义在头文件include/uapi/asm-generic/unistd.h

(4)默认系统为定义的系统调用号会直接执行一个no implement handler,也就是do_ni_syscall

下面我们就来介绍两种添加自定义系统调用的方法。

第一种方式:

我们以系统调用getpid为例进行讲解。

(1)实现sys_getpid函数体

kernel/sys.c:
 /**
  * sys_getpid - return the thread group id of the current process
  *
  * Note, despite the name, this returns the tgid not the pid.  The tgid and
  * the pid are identical unless CLONE_THREAD was specified on clone() in
  * which case the tgid is the same in all threads of the same group.
  *
  * This is SMP safe as current->tgid does not change.
  */
 SYSCALL_DEFINE0(getpid)
 {
     return task_tgid_vnr(current);
 }

SYSCALL_DEFINE0的定义如下:

 #define SYSCALL_DEFINE0(sname)                  \
     SYSCALL_METADATA(_##sname, 0);              \
     asmlinkage long sys_##sname(void)

(2)头文件中声明系统调用函数

include/linux/syscalls.h:
asmlinkage long sys_getpid(void);

(3)把系统调用函数加入到syscall数组中

64-bit模式系统调用数组添加:

include/uapi/asm-generic/unistd.h:
#define __NR_getpid 172
__SYSCALL(__NR_getpid, sys_getpid)

32-bit模式系统调用数组添加:

arch/arm64/include/asm/unistd32.h:
#define __NR_getpid 20
__SYSCALL(__NR_getpid, sys_getpid)

至此我们就完成了一个系统调用的添加了。

第二种方式:

通过前面的介绍我们知道Linux中没有意义的系统调用号都会调用到arch/arm64/kernel/trap.c:do_ni_syscall函数。

那么我们可以考虑在此函数中实现我们新添加的系统调用,使用这种方式添加系统调用就不用再去更改系统调用数组了。

arch/arm64/kernel/trap.c:
 asmlinkage long do_ni_syscall(struct pt_regs *regs)
 {
 #ifdef CONFIG_COMPAT
     long ret;
     if (is_compat_task()) {
         ret = compat_arm_syscall(regs);
         if (ret != -ENOSYS)
             return ret;
     }
 #endif

     if (show_unhandled_signals_ratelimited()) {
         pr_info("%s[%d]: syscall %d\n", current->comm,
             task_pid_nr(current), (int)regs->syscallno);
         dump_instr("", regs);
         if (user_mode(regs))
             __show_regs(regs);
     }

     return sys_ni_syscall();
 }

我们可以在此函数的起始处加入一个我们自己的syscall handle函数,并在此函数中处理所有我们要加入的系统调用。

比如:

long custom_add_syscall(struct pt_regs *regs)
{
     unsigned int no;
    if (is_compat_task()) {
      no = regs->regs[7]; // regs[7] :32bit program
    else
      no =  regs->regs[8]; //regs[8] :64bit program
    return handle_all_add_syscall(regs, no);
}   

注意一定要区分32-bit和64-bit模式,可以使用is_compat_task()函数来判断是否处于32-bit运行模式。对于32-bit程序,regs[7]中是syscall number,而对于64-bit程序,regs[8]中才是syscall number。得到了syscall number之后,我们可以进一步处理我们要加入的新的syscall了。

整个代码执行流程如下:

查找系统调用表—>表中未定义实际的处理函数—>执行默认的处理函数do_ni_syscall—>执行我们新添加的custom_add_syscall—>在我们的函数中根据系统调用号进一步处理

猜你喜欢

转载自blog.csdn.net/rikeyone/article/details/79929032