gpt4 book ai didi

c - 如何从 C 代码加载 Linux 内核模块?

转载 作者:IT王子 更新时间:2023-10-29 00:19:28 32 4
gpt4 key购买 nike

我有一个应用程序,它有两个外部内核模块和一个用户空间守护进程。我想在启动时从用 C 编写的守护程序代码加载模块,并在干净退出时卸载它们。我能否以比执行 system("modprobe module"); 更简洁的方式加载它们并使用相应的 rmmod 卸载它们?

最佳答案

init_module/remove_module 最小可运行示例

在 QEMU + Buildroot VM 和 Ubuntu 16.04 主机上测试 with this simple parameter printer module .

我们使用init_module/finit_moduleremove_module Linux system calls .

Linux 内核提供了两种用于模块插入的系统调用:

  • 初始化模块
  • finit_module

和:

man init_module

记录:

The finit_module() system call is like init_module(), but reads the module to be loaded from the file descriptor fd. It is useful when the authenticity of a kernel module can be determined from its location in the filesystem; in cases where that is possible, the overhead of using cryptographically signed modules to determine the authenticity of a module can be avoided. The param_values argument is as for 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 upstream .

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 upstream .

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 尝试两件事:

  • mmap通过try_to_mmap_module将文件映射到内存。

    作为副作用,这总是将 image_size 设置为 .ko 文件的大小。

  • 如果失败,使用xmalloc_open_zipped_read_close将文件malloc 到内存。

    如果文件是 zip 文件,此函数可选择先解压缩文件,否则仅 mallocs。

    我不明白为什么要完成这个压缩业务,因为我们甚至不能依赖它,因为 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 所说:

The init_module() system call is not supported by glibc. No declaration is provided in glibc headers, but, through a quirk of history, glibc does export an ABI forthis system call. Therefore, in order to employ this system call, it is sufficient to manually declare the interface in your code; alternatively, you can invokethe system call using syscall(2).

BusyBox 明智地遵循了该建议并使用了 glibc 提供的 syscall,它为系统调用提供了 C API。

关于c - 如何从 C 代码加载 Linux 内核模块?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5947286/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com