GNU/Linux >> Tutoriels Linux >  >> Linux

Comment charger les modules du noyau Linux à partir du code C ?

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 via try_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 avec xmalloc_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);

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.


Linux
  1. Linux - Comment exécuter un chargeur de démarrage à partir de Linux ?

  2. Comment créer, compiler, charger des modules de noyau chargeables Linux LKM

  3. Comment définir l'adresse IP de C sous Linux

  4. Comment coder un module du noyau Linux ?

  5. Comment puis-je réserver un bloc de mémoire à partir du noyau Linux ?

Comment charger et décharger les modules du noyau sous Linux

Comment obtenir des nouvelles instantanément à partir de la ligne de commande sous Linux

Comment construire le noyau Linux à partir de zéro

Comment compiler le noyau Linux à partir de la source pour créer un noyau personnalisé

Comment installer un logiciel à partir du code source dans votre système Linux

Comment Linux charge-t-il l'image 'initrd' ?