GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo cargar módulos del kernel de Linux desde el código C?

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 de try_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 con xmalloc_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.


Linux
  1. Linux:¿cómo ejecutar un gestor de arranque desde Linux?

  2. Cómo crear, compilar y cargar módulos de kernel cargables LKM de Linux

  3. Cómo configurar la dirección IP desde C en Linux

  4. ¿Cómo codificar un módulo del kernel de Linux?

  5. ¿Cómo puedo reservar un bloque de memoria del kernel de Linux?

Cómo cargar y descargar módulos del kernel en Linux

Cómo obtener noticias al instante desde la línea de comandos en Linux

Cómo construir el kernel de Linux desde cero

Cómo compilar el kernel de Linux desde el origen para crear un kernel personalizado

Cómo instalar software desde el código fuente en su sistema Linux

¿Cómo carga Linux la imagen 'initrd'?