GNU/Linux >> Linux の 問題 >  >> Linux

CコードからLinuxカーネルモジュールをロードする方法は?

insmod/rmmod 関数 init_module を使用します と delete_module これを行うには、マンページも利用できます。どちらも関数を extern として宣言しています ヘッダーを含める代わりに、マンページには <linux/module.h> .


init_module / remove_module 最小限の実行可能な例

この単純なパラメーター プリンター モジュールを使用して、QEMU + Buildroot VM および Ubuntu 16.04 ホストでテスト済み。

init_module を使用します / finit_moduleremove_module Linux システム コール。

Linux カーネルは、モジュール挿入用に 2 つのシステム コールを提供します:

  • init_module
  • finit_module

そして:

man init_module

ドキュメント:

<ブロック引用>

finit_module() システム コールは init_module() に似ていますが、ロードするモジュールをファイル記述子 fd から読み取ります。ファイルシステム内の場所からカーネルモジュールの信頼性を判断できる場合に役立ちます。それが可能な場合は、モジュールの真正性を判断するために暗号で署名されたモジュールを使用するオーバーヘッドを回避できます。 param_values 引数は init_module() と同じです。

finit は新しく、v3.8 でのみ追加されました。さらなる根拠:https://lwn.net/Articles/519010/

glibc はそれらの C ラッパーを提供していないようなので、 syscall で独自に作成するだけです .

insmod.c

#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

#define init_module(module_image, len, param_values) syscall(__NR_init_module, module_image, len, param_values)
#define finit_module(fd, param_values, flags) syscall(__NR_finit_module, fd, param_values, flags)

int main(int argc, char **argv) {
    const char *params;
    int fd, use_finit;
    size_t image_size;
    struct stat st;
    void *image;

    /* CLI handling. */
    if (argc < 2) {
        puts("Usage ./prog mymodule.ko [args="" [use_finit=0]");
        return EXIT_FAILURE;
    }
    if (argc < 3) {
        params = "";
    } else {
        params = argv[2];
    }
    if (argc < 4) {
        use_finit = 0;
    } else {
        use_finit = (argv[3][0] != '0');
    }

    /* Action. */
    fd = open(argv[1], O_RDONLY);
    if (use_finit) {
        puts("finit");
        if (finit_module(fd, params, 0) != 0) {
            perror("finit_module");
            return EXIT_FAILURE;
        }
        close(fd);
    } else {
        puts("init");
        fstat(fd, &st);
        image_size = st.st_size;
        image = malloc(image_size);
        read(fd, image, image_size);
        close(fd);
        if (init_module(image, image_size, params) != 0) {
            perror("init_module");
            return EXIT_FAILURE;
        }
        free(image);
    }
    return EXIT_SUCCESS;
}

GitHub アップストリーム。

rmmod.c

#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

#define delete_module(name, flags) syscall(__NR_delete_module, name, flags)

int main(int argc, char **argv) {
    if (argc != 2) {
        puts("Usage ./prog mymodule");
        return EXIT_FAILURE;
    }
    if (delete_module(argv[1], O_NONBLOCK) != 0) {
        perror("delete_module");
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

GitHub アップストリーム。

Busybox ソースの解釈

Busybox は insmod を提供します 、そしてミニマリズムのために設計されているので、そこからどのように行われるかを推測することができます.

バージョン 1.24.2 では、エントリ ポイントは modutils/insmod.c にあります。 関数 insmod_main .

IF_FEATURE_2_4_MODULES は、古い Linux カーネル 2.4 モジュールのオプション サポートであるため、今のところ無視して構いません。

modutils.c に転送するだけです 関数 bb_init_module .

bb_init_module 2 つのことを試みます:

  • mmap try_to_mmap_module を介してファイルをメモリに .

    これは常に image_size を設定します .ko のサイズに

  • それが失敗した場合、malloc xmalloc_open_zipped_read_close でファイルをメモリに .

    この関数はオプションで、zip の場合は最初にファイルを解凍し、それ以外の場合は単に malloc します。

    try_to_mmap_module 物事を解凍していないようです.

最後に呼び出しが来ます:

init_module(image, image_size, options);

どこで image メモリに入れられた実行可能ファイルで、オプションは "" だけです insmod file.elf を呼び出す場合 それ以上の引数はありません。

init_module 上記は以下によって提供されます:

#ifdef __UCLIBC__
extern int init_module(void *module, unsigned long len, const char *options);
extern int delete_module(const char *module, unsigned int flags);
#else
# include <sys/syscall.h>
# define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
#endif

ulibc 組み込みの libc 実装であり、init_module を提供しているようです .

無ければglibcを想定していると思いますが、 man init_module のように 言います:

<ブロック引用>

init_module() システム コールは、glibc ではサポートされていません。 glibc ヘッダーでは宣言は提供されませんが、歴史の癖により、glibc はこのシステム コールの ABI をエクスポートします。したがって、このシステム コールを使用するには、コード内でインターフェイスを手動で宣言するだけで十分です。または、syscall(2) を使用してシステム コールを呼び出すこともできます。

BusyBox は賢明にもそのアドバイスに従い、syscall を使用します。 、glibc が提供し、システム コール用の C API を提供します。


Linux
  1. Linuxデスクトップでカーネルをアップグレードする方法

  2. Linux でソース コードからソフトウェアをコンパイルしてインストールする方法

  3. LinuxでCからIPアドレスを設定する方法

  1. LinuxにPerlモジュールをインストールする方法

  2. Linux カーネルはどうしてこんなに小さいのでしょうか?

  3. カーネルソースコードから最小限のブート可能な Linux (ターミナルのみ) を作成するには?

  1. Linux – Linuxからブートローダーを実行する方法は?

  2. Linux LKM ローダブル カーネル モジュールを作成、コンパイル、ロードする方法

  3. Linux カーネルモジュールのコーディング方法は?