web-dev-qa-db-ja.com

Linuxシステムコールにパラメーターを渡す方法は?

私はOSを勉強している大学生です。

Linuxカーネルに独自のシステムコールを追加しようとしていますが、問題が発生しています。

私の環境は以下のとおりです。

  • Linuxカーネルv.4.19.1
  • Oracle VirtualBox 5.2.18上のIntel Core i5-4210M CPUを搭載した64ビットUbuntu LTS 18.04.1
  • ホストマシンとしての64ビットWindows 10 Home 1803


x86_64マシンで作業しているので、 Arch/x86/entry/syscalls/syscall_64.tbl。 Linuxカーネルv.4.19.1では、最後のエントリは

334     common     rseq             __x64_sys_rseq

その下に3行追加しました。

335     common     my_syscall_0     sys_my_syscall_0
336     common     my_syscall_1     sys_my_syscall_1
337     common     my_syscall_2     sys_my_syscall_2


2番目に、関数のプロトタイプを追加しました include/linux/syscalls.h

asmlinkage int sys_my_syscall_0(void);
asmlinkage int sys_my_syscall_1(int);
asmlinkage int sys_my_syscall_2(int, int);


3番目に、新しいファイルを作成しました kernel/my_syscall.c 関数の実装が追加されました。

asmlinkage int sys_my_syscall_0(void)
{
    printk("my_syscall_0\n");
    return 0;
}

asmlinkage int sys_my_syscall_1(int a)
{
    printk("my_syscall_1 : %d\n", a);
    return 0;
}

asmlinkage int sys_my_syscall_0(int a, int b)
{
    printk("my_syscall_2 : %d, %d\n", a, b);
    return b;
}


その後、my_syscall.oを カーネル/ Makefile コンパイルする kernel/my_syscall.c 同じように。

obj-y = fork.o exec_domain.o panic.o \
        cpu.o exit.o softirq.o resource.o \
        sysctl.o sysctl_binary.o capability.o ptrace.o user.o \
        signal.o sys.o umh.o workqueue.o pid.o task_work.o \
        extable.o params.o \
        kthread.o sys_ni.o nsproxy.o \
        notifier.o ksysfs.o cred.o reboot.o \
        async.o range.o smpboot.o ucount.o \
        my_syscall.o


コンパイル後 make-kpkg そして dpkg コマンド、テストプログラムを作りました。

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
int main()
{
    printf("1 : %d\n", syscall(335));
    printf("2 : %d\n", syscall(336, 1));
    printf("3 : %d\n", syscall(337, 2, 3));
    return 0;
}


しかし、結果は奇妙です。 dmesg まったく意味をなさない膨大な数を示しています。

# dmesg
...
my_syscall_0
my_syscall_1 : 1111490392
my_syscall_2 : 1111490392, 1111490392

プログラムを実行するたびに変化しているようです。


明らかに、パラメーターの受け渡しに問題があります。しかしながら、 strace コマンドは、値がうまく渡されていることを示しています。

# strace ./syscall
...
syscall_0x14F(0x7ffd21866538, 0x7ffd21866548, ....) = 0
...
syscall_0x150(0x1, 0, 0, 0, 0x7f9e1562f3a0, ....) = 0
...
syscall_0x14F(0x2, 0x3, 0x7f9e1562f3a0, ....) = 0
....

簡単

  1. 簡単なシステムコールを行いました。

  2. それらにパラメーターを渡すことは、本来あるべき姿で機能していません。

  3. 質問:新しいシステムコールを追加するために、上記の手順に問題はありますか?

  4. 質問:システムコールにパラメーターを渡すときに注意すべき問題はありますか?

前もって感謝します。

3
P. Heekang

私は解決策を見つけました。 @Ajay Brahmakshatriyaが答えたように、SYSCALL_DEFINExマクロを使用する必要があります。また、私は変更する必要があります Arch/x86/entry/syscalls/syscall_64.tbl 同じように。

これが最終的な要約です。

新しいシステムコールを追加する方法

まず、変更 Arch/x86/entry/syscalls/syscall_64.tbl :以下の行を追加します。

335     common     my_syscall_0     __x64_sys_my_syscall_0
336     common     my_syscall_1     __x64_sys_my_syscall_1
337     common     my_syscall_2     __x64_sys_my_syscall_2

第二に、変更 include/linux/syscalls.h :以下の行を追加します。

asmlinkage long sys_my_syscall_0(void);
asmlinkage long sys_my_syscall_1(int);
asmlinkage long sys_my_syscall_2(int, int);

3番目に、実装用の新しいファイルを作成します。私の場合、 kernel/my_syscall.c

#include <linux/syscalls.h>
#include <linux/kernel.h>

SYSCALL_DEFINE0(my_syscall_0)
{
    printk("my_syscall_0\n");
    return 0;
}

SYSCALL_DEFINE1(my_syscall_1, int, a)
{
    printk("my_syscall_1 : %d\n", a);
    return 0;
}

SYSCALL_DEFINE2(my_syscall_2, int, a, int, b)
{
    printk("my_syscall_2 : %d, %d\n", a, b);
    return b;
}

4番目に、作成したファイルをディレクトリのMakefileに追加します。私の場合、 カーネル/ Makefile

...
obj-y = fork.o exec_domain.o panic.o \
        cpu.o exit.o softirq.o resource.o \
        sysctl.o sysctl_binary.o capability.o ptrace.o user.o \
        signal.o sys.o umh.o workqueue.o pid.o task_work.o \
        extable.o params.o \
        kthread.o sys_ni.o nsproxy.o \
        notifier.o ksysfs.o cred.o reboot.o \
        async.o range.o smpboot.o ucount.o \
        my_syscall.o
...

最後に、カーネルをコンパイルしてインストールします。これで、新しいシステムコールが適切に機能していることがわかります。

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>

int main()
{
    printf("1 : %d\n", syscall(335));
    printf("2 : %d\n", syscall(336, 1));
    printf("3 : %d\n", syscall(337, 2, 3));
    return 0;
}

dmesg コマンドは、システムコールがうまく機能していることを示しています。

# dmesg
my_syscall_0
my_syscall_1 : 1
my_syscall_2 : 2, 3
4
P. Heekang

システムコールには2つの引数が必要であり、それらはint型であることをビルドシステムに通知する必要があります。これは、ビルドシステムの一部であるスクリプトが、引数を必要な型にキャストするための適切なラッパーを生成するためです。あなたがしたように実際のハンドラを定義する代わりに、あなたは使うべきです-

SYSCALL_DEFINE2(my_syscall_2, int, a, int, b) // Yes, there is a comma between the types and the argument names
{
    printk("my_syscall_2 : %d, %d\n", a, b);
    return b;
}

SYSCALL_DEFINExlinux/include/linux/syscalls.h で定義されています。

linux/fs/read_write.c で例を見ることができます