insmod/rmmod utiliser les fonctions init_module
et delete_module
pour ce faire, qui ont également une page de manuel disponible. Ils déclarent tous les deux les fonctions comme extern
au lieu d'inclure un en-tête, mais la page de manuel dit qu'ils devraient être en <linux/module.h>
.
init_module
/ remove_module
exemple exécutable minimal
Testé sur une machine virtuelle QEMU + Buildroot et un hôte Ubuntu 16.04 avec ce module d'imprimante à paramètres simples.
Nous utilisons le init_module
/ finit_module
et remove_module
Appels système Linux.
Le noyau Linux propose deux appels système pour l'insertion de modules :
init_module
finit_module
et :
man init_module
documents qui :
L'appel système finit_module() est comme init_module(), mais lit le module à charger à partir du descripteur de fichier fd. C'est utile lorsque l'authenticité d'un module du noyau peut être déterminée à partir de son emplacement dans le système de fichiers; dans les cas où cela est possible, la surcharge liée à l'utilisation de modules signés de manière cryptographique pour déterminer l'authenticité d'un module peut être évitée. L'argument param_values est comme pour init_module().
finit
est plus récent et n'a été ajouté que dans la v3.8. Plus de justification :https://lwn.net/Articles/519010/
glibc ne semble pas leur fournir de wrapper C, nous créons donc le nôtre avec 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 en amont.
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 en amont.
Interprétation de la source Busybox
Busybox fournit insmod
, et puisqu'il est conçu pour le minimalisme, on peut essayer d'en déduire comment c'est fait.
Sur la version 1.24.2, le point d'entrée est à modutils/insmod.c
fonction insmod_main
.
Le IF_FEATURE_2_4_MODULES
est un support facultatif pour les anciens modules du noyau Linux 2.4, nous pouvons donc simplement l'ignorer pour l'instant.
Cela transmet juste à modutils.c
fonction bb_init_module
.
bb_init_module
tente deux choses :
-
mmap
le fichier en mémoire viatry_to_mmap_module
.Cela définit toujours
image_size
à la taille du.ko
fichier comme effet secondaire. -
si cela échoue,
malloc
le fichier en mémoire avecxmalloc_open_zipped_read_close
.Cette fonction décompresse éventuellement le fichier en premier s'il s'agit d'un zip, et le malloc sinon.
Je ne comprends pas pourquoi cette entreprise de fermeture éclair est terminée, car nous ne pouvons même pas nous y fier car le
try_to_mmap_module
ne semble pas décompresser les choses.
Vient enfin l'appel :
init_module(image, image_size, options);
où image
est l'exécutable qui a été mis en mémoire, et les options ne sont que ""
si nous appelons insmod file.elf
sans autre argument.
init_module
est fourni ci-dessus par :
#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
est une implémentation libc intégrée, et elle semble fournir init_module
.
S'il n'est pas présent, je pense que glibc est supposé, mais comme man init_module
dit :
L'appel système init_module() n'est pas pris en charge par la glibc. Aucune déclaration n'est fournie dans les en-têtes de la glibc, mais, par une bizarrerie historique, la glibc exporte une ABI pour cet appel système. Par conséquent, pour utiliser cet appel système, il suffit de déclarer manuellement l'interface dans votre code; alternativement, vous pouvez invoquer l'appel système en utilisant syscall(2).
BusyBox suit sagement ce conseil et utilise syscall
, fourni par glibc, et qui propose une API C pour les appels système.