insmod/rmmod usa las funciones init_module
y delete_module
para hacer esto, que también tienen una página de manual disponible. Ambos declaran las funciones como extern
en lugar de incluir un encabezado, pero la página de manual dice que deberían estar en <linux/module.h>
.
init_module
/ remove_module
ejemplo ejecutable mínimo
Probado en un host QEMU + Buildroot VM y Ubuntu 16.04 con este módulo de impresora de parámetros simple.
Usamos el init_module
/ finit_module
y remove_module
Llamadas al sistema Linux.
El kernel de Linux ofrece dos llamadas al sistema para la inserción de módulos:
init_module
finit_module
y:
man init_module
documentos que:
La llamada al sistema finit_module() es como init_module(), pero lee el módulo que se va a cargar desde el descriptor de archivo fd. Es útil cuando la autenticidad de un módulo del kernel se puede determinar a partir de su ubicación en el sistema de archivos; en los casos en que eso sea posible, se puede evitar la sobrecarga de usar módulos firmados criptográficamente para determinar la autenticidad de un módulo. El argumento param_values es como para init_module().
finit
es más reciente y se agregó solo en v3.8. Más justificación:https://lwn.net/Articles/519010/
glibc no parece proporcionar un envoltorio C para ellos, por lo que simplemente creamos el nuestro 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 ascendente.
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 ascendente.
Interpretación de fuentes de Busybox
Busybox proporciona insmod
, y dado que está diseñado para el minimalismo, podemos intentar deducir cómo se hace a partir de ahí.
En la versión 1.24.2, el punto de entrada está en modutils/insmod.c
función insmod_main
.
El IF_FEATURE_2_4_MODULES
es un soporte opcional para módulos Linux kernel 2.4 más antiguos, por lo que podemos ignorarlo por ahora.
Eso solo reenvía a modutils.c
función bb_init_module
.
bb_init_module
intenta dos cosas:
-
mmap
el archivo a la memoria a través detry_to_mmap_module
.Esto siempre establece
image_size
al tamaño del.ko
archivo como efecto secundario. -
si eso falla,
malloc
el archivo a la memoria conxmalloc_open_zipped_read_close
.Esta función opcionalmente descomprime el archivo primero si se trata de un zip y, de lo contrario, simplemente lo almacena.
No entiendo por qué se hace este negocio de zipping, ya que ni siquiera podemos confiar en él porque el
try_to_mmap_module
no parece descomprimir las cosas.
Finalmente llega la llamada:
init_module(image, image_size, options);
donde image
es el ejecutable que se puso en la memoria, y las opciones son solo ""
si llamamos insmod file.elf
sin más argumentos.
init_module
se proporciona arriba por:
#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
es una implementación de libc incrustada y parece proporcionar init_module
.
Si no está presente, creo que se asume glibc, pero como man init_module
dice:
La llamada al sistema init_module() no es compatible con glibc. No se proporciona ninguna declaración en los encabezados de glibc, pero, a través de una peculiaridad del historial, glibc exporta una ABI para esta llamada al sistema. Por lo tanto, para emplear esta llamada al sistema, es suficiente declarar manualmente la interfaz en su código; alternativamente, puede invocar la llamada al sistema mediante syscall(2).
BusyBox sabiamente sigue ese consejo y usa syscall
, que proporciona glibc y que ofrece una API C para llamadas al sistema.