GNU/Linux >> Znalost Linux >  >> Linux

Jak načíst moduly jádra Linuxu z kódu C?

insmod/rmmod použijte funkce init_module a delete_module k tomu, které mají také k dispozici manuálovou stránku. Oba deklarují funkce jako extern místo zahrnutí záhlaví, ale manuálová stránka říká, že by měly být v <linux/module.h> .


init_module / remove_module minimální spustitelný příklad

Testováno na virtuálním počítači QEMU + Buildroot a hostiteli Ubuntu 16.04 s tímto jednoduchým modulem tiskárny parametrů.

Používáme init_module / finit_module a remove_module Systémová volání Linuxu.

Linuxové jádro nabízí dvě systémová volání pro vložení modulu:

  • init_module
  • finit_module

a:

man init_module

dokumenty, které:

Systémové volání finit_module() je jako init_module(), ale čte modul, který se má načíst, z deskriptoru souboru fd. Je to užitečné, když lze pravost modulu jádra určit z jeho umístění v souborovém systému; v případech, kdy je to možné, se lze vyhnout režii používání kryptograficky podepsaných modulů k určení pravosti modulu. Argument param_values ​​je stejný jako u init_module().

finit je novější a byl přidán až ve verzi 3.8. Více zdůvodnění:https://lwn.net/Articles/519010/

Zdá se, že glibc pro ně neposkytuje obal C, takže jsme si vytvořili vlastní s 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 upstream.

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 upstream.

Interpretace zdroje Busybox

Busybox poskytuje insmod , a protože je navržen pro minimalismus, můžeme se z toho pokusit odvodit, jak se to dělá.

Ve verzi 1.24.2 je vstupní bod na modutils/insmod.c funkce insmod_main .

IF_FEATURE_2_4_MODULES je volitelná podpora pro starší moduly linuxového jádra 2.4, takže ji zatím můžeme ignorovat.

To jen přesměruje na modutils.c funkce bb_init_module .

bb_init_module pokusí se o dvě věci:

  • mmap soubor do paměti přes try_to_mmap_module .

    Toto vždy nastaví image_size na velikost .ko soubor jako vedlejší efekt.

  • pokud to selže, malloc soubor do paměti s xmalloc_open_zipped_read_close .

    Tato funkce volitelně nejprve rozbalí soubor, pokud se jedná o zip, a jinak jej pouze mallocuje.

    Nechápu, proč se dělá toto zipování, protože se na to ani nemůžeme spolehnout, protože try_to_mmap_module nezdá se, že by věci rozepínal.

Konečně přichází hovor:

init_module(image, image_size, options);

kde image je spustitelný soubor, který byl vložen do paměti, a možnosti jsou pouze "" pokud zavoláme insmod file.elf bez dalších argumentů.

init_module poskytuje výše:

#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 je vestavěná implementace knihovny libc a zdá se, že poskytuje init_module .

Pokud není přítomen, myslím, že se předpokládá glibc, ale jako man init_module říká:

Systémové volání init_module() není podporováno glibc. V hlavičkách glibc není k dispozici žádná deklarace, ale díky historickému vtipu glibc exportuje ABI pro toto systémové volání. Proto, aby bylo možné použít toto systémové volání, stačí ručně deklarovat rozhraní ve vašem kódu; alternativně můžete systémové volání vyvolat pomocí syscall(2).

BusyBox moudře následuje tuto radu a používá syscall , který poskytuje glibc a který nabízí C API pro systémová volání.


Linux
  1. Linux – Jak spustit bootloader z Linuxu?

  2. Jak vytvořit, zkompilovat, načíst moduly jádra Linuxu LKM

  3. Jak nastavit IP adresu z C v linuxu

  1. Jak kódovat modul jádra Linuxu?

  2. Jak přidat funkci dotazování do kódu modulu jádra?

  3. Jak vytvořit minimální zaváděcí linux (pouze s terminálem) ze zdrojového kódu jádra?

  1. Jak nainstalovat moduly Perl na Linux

  2. Jak upgradovat jádro na ploše Linuxu

  3. Jak zkompilovat a nainstalovat software ze zdrojového kódu na Linuxu