Пример минимального запуска
Протестировано на виртуальной машине QEMU + Buildroot и хосте Ubuntu 16.04 с этим простым модулем принтера параметров .
Мы используем системные вызовы init_module
/ finit_module
и remove_module
Linux .
Ядро Linux предлагает два системных вызова для вставки модуля:
и
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 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
как побочный эффект.
, если это не удалось, malloc
файл в память с xmalloc_open_zipped_read_close
.
Эта функция может сначала разархивировать файл, если это zip-файл, и просто распределить его по другому.
Я не понимаю, почему этот бизнес на молнии таков, потому что мы даже не можем полагаться на него, потому что 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 для системных вызовов.