GNU/Linux >> Linux Esercitazione >  >> Linux

Come caricare i moduli del kernel Linux dal codice C?

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 tramite try_to_mmap_module .

    Questo imposta sempre image_size alla dimensione del .ko file come effetto collaterale.

  • se fallisce, malloc il file in memoria con xmalloc_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.


Linux
  1. Come installare i moduli Perl su Linux

  2. Come aggiornare il kernel su desktop Linux

  3. Come aggiungere la funzione poll al codice del modulo del kernel?

  4. Come posso riservare un blocco di memoria dal kernel Linux?

  5. Come creare un Linux avviabile minimo (solo con terminale) dal codice sorgente del kernel?

Come caricare e scaricare i moduli del kernel in Linux

Come ottenere notizie istantaneamente dalla riga di comando in Linux

Come costruire il kernel Linux da zero

Come compilare il kernel Linux dal sorgente per creare un kernel personalizzato

Come installare il software dal codice sorgente nel tuo sistema Linux

In che modo Linux carica l'immagine "initrd"?