insmod/rmmod verwenden die Funktionen init_module
und delete_module
um dies zu tun, die auch eine Manpage zur Verfügung haben. Beide deklarieren die Funktionen als extern
anstatt einen Header einzufügen, aber die Manpage sagt, dass sie in <linux/module.h>
sein sollten .
init_module
/ remove_module
Minimales lauffähiges Beispiel
Getestet auf einer QEMU + Buildroot VM und einem Ubuntu 16.04-Host mit diesem einfachen Parameterdruckermodul .
Wir verwenden den init_module
/ finit_module
und remove_module
Linux-Systemaufrufe.
Der Linux-Kernel bietet zwei Systemaufrufe zum Einfügen von Modulen:
init_module
finit_module
und:
man init_module
Dokumente, die:
Der Systemaufruf finit_module() ist wie init_module(), liest aber das zu ladende Modul aus dem Dateideskriptor fd. Es ist nützlich, wenn die Authentizität eines Kernelmoduls anhand seiner Position im Dateisystem bestimmt werden kann; In Fällen, in denen dies möglich ist, kann der Mehraufwand für die Verwendung kryptografisch signierter Module zur Bestimmung der Authentizität eines Moduls vermieden werden. Das Argument param_values ist wie bei init_module().
finit
ist neuer und wurde erst in v3.8 hinzugefügt. Weitere Begründung:https://lwn.net/Articles/519010/
glibc scheint keinen C-Wrapper für sie bereitzustellen, also erstellen wir einfach unseren eigenen mit 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-Quelleninterpretation
Busybox stellt insmod
bereit , und da es auf Minimalismus ausgelegt ist, können wir versuchen, daraus abzuleiten, wie es gemacht wird.
Bei Version 1.24.2 ist der Einstiegspunkt bei modutils/insmod.c
Funktion insmod_main
.
Die IF_FEATURE_2_4_MODULES
ist eine optionale Unterstützung für ältere Linux-Kernel-2.4-Module, daher können wir sie vorerst einfach ignorieren.
Das leitet einfach zu modutils.c
weiter Funktion bb_init_module
.
bb_init_module
versucht zwei Dinge:
-
mmap
die Datei in den Speicher durchtry_to_mmap_module
.Dadurch wird immer
image_size
gesetzt auf die Größe von.ko
Datei als Nebeneffekt. -
wenn das fehlschlägt,
malloc
die Datei mitxmalloc_open_zipped_read_close
in den Speicher .Diese Funktion entpackt optional zuerst die Datei, wenn es sich um eine ZIP-Datei handelt, und blockiert sie ansonsten einfach.
Ich verstehe nicht, warum dieses Zipping-Geschäft gemacht wird, da wir uns nicht einmal darauf verlassen können, weil der
try_to_mmap_module
scheint die Dinge nicht zu entpacken.
Schließlich kommt der Aufruf:
init_module(image, image_size, options);
wobei image
ist die ausführbare Datei, die in den Speicher gestellt wurde, und Optionen sind nur ""
wenn wir insmod file.elf
anrufen ohne weitere Argumente.
init_module
wird oben bereitgestellt von:
#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
ist eine eingebettete libc-Implementierung und scheint init_module
bereitzustellen .
Wenn es nicht vorhanden ist, wird glibc angenommen, aber als man init_module
sagt:
Der Systemaufruf init_module() wird von glibc nicht unterstützt. In Glibc-Headern wird keine Deklaration bereitgestellt, aber aufgrund einer Eigenart der Geschichte exportiert Glibc eine ABI für diesen Systemaufruf. Um diesen Systemaufruf zu verwenden, reicht es daher aus, die Schnittstelle manuell in Ihrem Code zu deklarieren; Alternativ können Sie den Systemaufruf mit syscall(2) aufrufen.
BusyBox befolgt diesen Rat klug und verwendet syscall
, die glibc bereitstellt, und die eine C-API für Systemaufrufe bietet.