insmod/rmmod usa le funzioni init_module
e delete_module
per fare ciò, che hanno a disposizione anche una man-page. Entrambi dichiarano le funzioni come extern
invece di includere un'intestazione, ma la pagina man dice che dovrebbero essere in <linux/module.h>
.
init_module
/ remove_module
esempio eseguibile minimo
Testato su una VM QEMU + Buildroot e su un host Ubuntu 16.04 con questo semplice modulo stampante di parametri.
Usiamo il init_module
/ finit_module
e remove_module
Chiamate di sistema Linux.
Il kernel Linux offre due chiamate di sistema per l'inserimento di moduli:
init_module
finit_module
e:
man init_module
documenti che:
La chiamata di sistema finit_module() è come init_module(), ma legge il modulo da caricare dal descrittore di file fd. È utile quando l'autenticità di un modulo del kernel può essere determinata dalla sua posizione nel filesystem; nei casi in cui ciò sia possibile, è possibile evitare l'overhead dell'utilizzo di moduli con firma crittografica per determinare l'autenticità di un modulo. L'argomento param_values è come per init_module().
finit
è più recente ed è stato aggiunto solo nella v3.8. Più logica:https://lwn.net/Articles/519010/
glibc non sembra fornire un wrapper C per loro, quindi creiamo semplicemente il nostro con 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 a monte.
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 a monte.
Interpretazione della fonte di Busybox
Busybox fornisce insmod
, e poiché è progettato per il minimalismo, possiamo provare a dedurre come è fatto da lì.
Nella versione 1.24.2, il punto di ingresso è modutils/insmod.c
funzione insmod_main
.
Il IF_FEATURE_2_4_MODULES
è il supporto facoltativo per i vecchi moduli del kernel Linux 2.4, quindi per ora possiamo semplicemente ignorarlo.
Questo inoltra solo a modutils.c
funzione bb_init_module
.
bb_init_module
tenta due cose:
-
mmap
il file in memoria tramitetry_to_mmap_module
.Questo imposta sempre
image_size
alla dimensione del.ko
file come effetto collaterale. -
se fallisce,
malloc
il file in memoria conxmalloc_open_zipped_read_close
.Questa funzione facoltativamente decomprime prima il file se si tratta di un file zip, altrimenti lo sposta in malloc.
Non capisco perché questa faccenda dello zippare sia finita, dal momento che non possiamo nemmeno fare affidamento su di essa perché il
try_to_mmap_module
non sembra decomprimere le cose.
Finalmente arriva la chiamata:
init_module(image, image_size, options);
dove image
è l'eseguibile che è stato messo in memoria, e le opzioni sono solo ""
se chiamiamo insmod file.elf
senza ulteriori argomenti.
init_module
è fornito sopra da:
#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
è un'implementazione di libc incorporata e sembra fornire init_module
.
Se non è presente, penso che si presuma glibc, ma come man init_module
dice:
La chiamata di sistema init_module() non è supportata da glibc. Nessuna dichiarazione è fornita nelle intestazioni di glibc, ma, per uno scherzo della cronologia, glibc esporta un'ABI per questa chiamata di sistema. Pertanto, per utilizzare questa chiamata di sistema, è sufficiente dichiarare manualmente l'interfaccia nel codice; in alternativa, puoi invocare la chiamata di sistema usando syscall(2).
BusyBox segue saggiamente questo consiglio e usa syscall
, fornito da glibc e che offre un'API C per le chiamate di sistema.