Wie rufe ich exportierte Kernelmodulfunktionen von einem anderen Modul aus auf?

Lesezeit: 6 Minuten

Benutzer-Avatar
Jacobsowls

Ich schreibe eine API als Kernelmodul, das Gerätetreibern verschiedene Funktionen zur Verfügung stellt. Ich habe drei Funktionen hineingeschrieben mycode.c. Ich habe dann das Modul gebaut und geladen und dann kopiert mycode.h hinein /include/linux. In einem Gerätetreiber habe ich eine #include und rufen Sie diese drei Funktionen auf. Aber wenn ich das Treibermodul baue, erhalte ich drei Linker-Warnungen, die besagen, dass these Funktionen sind undefiniert.

Anmerkungen:

  • Die Funktionen werden deklariert extern in mycode.h
  • Die Funktionen werden mit exportiert EXPORT_SYMBOL(Funktionsname) in mycode.c
  • Das Ausführen des Befehls nm mycode.ko zeigt alle drei Funktionen als verfügbar in der Symboltabelle an (großes T daneben, was bedeutet, dass die Symbole im Textabschnitt (Code) zu finden sind)
  • Nach dem Laden des Moduls wird der Befehl grep Funktionsname /proc/kallsyms zeigt alle drei Funktionen als geladen an

Die Funktionen werden also eindeutig korrekt exportiert und der Kernel weiß, was und wo sie sind. Warum kann der Fahrer ihre Definitionen nicht sehen? Irgendeine Idee, was mir fehlt?


EDIT: Ich habe hier einige Informationen dazu gefunden: http://www.kernel.org/doc/Documentation/kbuild/modules.txt

Manchmal verwendet ein externes Modul exportierte Symbole aus einem anderen externen Modul. kbuild muss alle Symbole vollständig kennen, um zu vermeiden, Warnungen über undefinierte Symbole auszuspucken. Für diese Situation gibt es drei Lösungen.

HINWEIS: Die Methode mit einer kbuild-Datei der obersten Ebene wird empfohlen, kann aber in bestimmten Situationen unpraktisch sein.

Verwenden Sie eine kbuild-Datei der obersten Ebene Wenn Sie zwei Module haben, foo.ko und bar.ko, wobei foo.ko Symbole von bar.ko benötigt, können Sie eine gemeinsame kbuild-Datei der obersten Ebene verwenden, sodass beide Module in derselben kompiliert werden bauen. Betrachten Sie das folgende Verzeichnislayout:

  ./foo/ <= contains foo.ko
  ./bar/ <= contains bar.ko

Die kbuild-Datei der obersten Ebene würde dann wie folgt aussehen:

  #./Kbuild (or ./Makefile): 
      obj-y := foo/ bar/

Und ausführen

  $ make -C $KDIR M=$PWD

wird dann das Erwartete tun und beide Module mit vollständiger Kenntnis der Symbole aus beiden Modulen kompilieren.

Verwenden Sie eine zusätzliche Module.symvers-Datei Wenn ein externes Modul erstellt wird, wird eine Module.symvers-Datei generiert, die alle exportierten Symbole enthält, die nicht im Kernel definiert sind. Um Zugriff auf Symbole von bar.ko zu erhalten, kopieren Sie die Datei Module.symvers aus der Zusammenstellung von bar.ko in das Verzeichnis, in dem foo.ko erstellt wird. Während der Modulerstellung liest kbuild die Datei Module.symvers im Verzeichnis des externen Moduls, und wenn die Erstellung abgeschlossen ist, wird eine neue Datei Module.symvers erstellt, die die Summe aller definierten Symbole enthält und nicht Teil des Kernels ist.

Verwenden Sie die “make”-Variable KBUILD_EXTRA_SYMBOLS Wenn es unpraktisch ist, Module.symvers von einem anderen Modul zu kopieren, können Sie KBUILD_EXTRA_SYMBOLS in Ihrer Erstellungsdatei eine durch Leerzeichen getrennte Liste von Dateien zuweisen. Diese Dateien werden von Modpost während der Initialisierung seiner Symboltabellen geladen.

Aber bei allen drei Lösungen müsste jeder Treiber, damit er meine API verwenden kann, entweder ein neues Makefile erstellen oder direkten Zugriff auf meine Module.symvers-Datei haben? Das wirkt etwas unbequem. Ich hatte gehofft, sie könnten einfach meine Header-Datei #einschließen und loslegen. Gibt es keine anderen Alternativen?

  • Nur für den Fall, dass jemand Probleme hat, das Blockzitat oben zu lesen; Die drei Methoden sind: 1) Verwenden Sie eine kbuild-Datei der obersten Ebene; 2) Verwenden Sie eine zusätzliche Module.symvers-Datei; und 3) Verwenden Sie a make Variable KBUILD_EXTRA_SYMBOLS.

    – Andi J

    20. November 2020 um 0:48 Uhr

Aus meiner Recherche geht hervor, dass dies die einzigen drei Möglichkeiten sind, mit dieser Situation umzugehen, und ich habe jede von ihnen zum Laufen gebracht, also denke ich, dass ich einfach meinen Favoriten aus diesen auswählen werde.

  • Haben Sie ein Verpackungsbeispiel? Könntest du teilen? Beispiel ist die beste Definition.

    – Moirisa Dikaiosýni

    13. Februar 2014 um 4:56 Uhr

  • Das habe ich kürzlich entdeckt depmod erhält automatisch die Symbole /lib/modules/$(uname -r)/modules.symbols Wenn Sie haben modules_installed Ihr Modul. Zu meiner großen Enttäuschung sieht Modpost diese Symbole immer noch nicht! Ich frage mich, warum es sich nicht über Kernel-eigene Funktionen beschwert, sondern nur über die, die ich installiert habe.

    – Shahbaz

    20. Februar 2015 um 14:34 Uhr

Benutzer-Avatar
Ciro Santilli OurBigBook.com

Minimales QEMU + Buildroot-Beispiel

Ich habe Folgendes in einer vollständig reproduzierbaren QEMU + Buildroot-Umgebung getestet. Vielleicht hilft Ihnen diese funktionierende Version dabei, herauszufinden, was mit Ihrem Code nicht stimmt.

GitHub Upstream konzentriert sich auf die Dateien:

abh.c

#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

int lkmc_dep = 0;
EXPORT_SYMBOL(lkmc_dep);
static struct task_struct *kthread;

static int work_func(void *data)
{
    while (!kthread_should_stop()) {
        printk(KERN_INFO "%d\n", lkmc_dep);
        usleep_range(1000000, 1000001);
    }
    return 0;
}

static int myinit(void)
{
    kthread = kthread_create(work_func, NULL, "mykthread");
    wake_up_process(kthread);
    return 0;
}

static void myexit(void)
{
    kthread_stop(kthread);
}

module_init(myinit)
module_exit(myexit)

dep2.c

#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

extern int lkmc_dep;
static struct task_struct *kthread;

static int work_func(void *data)
{
    while (!kthread_should_stop()) {
        usleep_range(1000000, 1000001);
        lkmc_dep++;
    }
    return 0;
}

static int myinit(void)
{
    kthread = kthread_create(work_func, NULL, "mykthread");
    wake_up_process(kthread);
    return 0;
}

static void myexit(void)
{
    kthread_stop(kthread);
}

module_init(myinit)
module_exit(myexit)

Und jetzt können Sie Folgendes tun:

insmod dep.ko
insmod dep2.ko

Mit diesem Buildroot-Setup konfigurieren die Dinge bereits depmod /lib/module/*/depmod mit der Abhängigkeit, also reicht das aus, um beide zu laden:

modprobe dep

Auch, wenn Sie Ihren Kernel mit gebaut haben CONFIG_KALLSYMS_ALL=ydann ist das exportierte Symbol zu sehen mit:

grep lkmc_dep /proc/kallsyms

siehe auch: Hat kallsyms alle Symbole der Kernelfunktionen?

OK: Sie haben ein Modul, wo die Funktion ist und ein Ort, was es importieren will, richtig?

Sie müssen “EXPORT_SYMBOL(“Name der Funktion”) wie foo an der Stelle verwenden, an der sich die Funktion befindet. Also müssen Sie in der “c”-Datei die Funktion “foo” definieren und einfügen: EXPORT_SYMBOL(foo)

Stellen Sie sicher, dass Sie den Prototyp für „foo“ in einer gemeinsamen Header-Datei haben (Sie können ihn an separaten Stellen für jedes Modul haben und es wird funktionieren, aber Sie fragen nach Ärger, wenn sich die Signaturen ändern). Sagen Sie also: void foo(void *arg);

Dann ruft das andere Modul, das es haben möchte, einfach “foo” auf und gut ist.

Außerdem: Stellen Sie sicher, dass Sie das Modul zuerst mit foo laden. Wenn Sie Querabhängigkeiten haben, z. B. Modul2 benötigt foo von Modul1 und Modul1 benötigt bar von Modul2, müssen Sie ein Register mit einem anderen verwenden. Wenn Sie es wissen möchten, fragen Sie bitte ein separates Q.

1366750cookie-checkWie rufe ich exportierte Kernelmodulfunktionen von einem anderen Modul aus auf?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy