it-swarm.com.de

Wie rufe ich exportierte Kernelmodul-Funktionen von einem anderen Modul aus auf?

Ich schreibe eine API als Kernelmodul, die Gerätetreibern verschiedene Funktionen bietet. Ich habe drei Funktionen in mycode.c geschrieben. Ich habe dann das Modul erstellt und geladen und dann mycode.h in <Kernel>/include/linux kopiert. In einem Gerätetreiber habe ich ein #include <linux/mycode.h> und rufe diese drei Funktionen auf. Aber wenn ich das Treibermodul erstelle, erhalte ich drei Linkerwarnungen, die besagen, dass diese Funktionen undefiniert sind .

Anmerkungen:

  • Die Funktionen sind in mycode.h extern deklariert
  • Die Funktionen werden mit EXPORT_SYMBOL (func_name) in mycode.c exportiert
  • Wenn Sie den Befehl nm mycode.ko ausführen, werden alle drei Funktionen in der Symboltabelle als verfügbar angezeigt (Großbuchstabe T daneben, dh die Symbole befinden sich im Textabschnitt (Code)).
  • Nach dem Laden des Moduls zeigt der Befehl grep func_name/proc/kallsyms alle drei Funktionen als geladen an

So werden die Funktionen eindeutig korrekt exportiert und der Kernel weiß, was und wo sie sind. Warum kann der Fahrer die Definitionen nicht sehen? Irgendeine Idee, was ich vermisse?


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

Manchmal verwendet ein externes Modul exportierte Symbole von einem anderen externen Modul. kbuild muss alle Symbole genau kennen, um nicht vor undefinierten Symbolen gewarnt zu werden. Für diese Situation gibt es drei Lösungen.

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

Verwenden einer kbuild-Datei der obersten Ebene Wenn Sie zwei Module haben, foo.ko und bar.ko, für die foo.ko Symbole aus 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

  The top-level kbuild file would then look like:

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

  And executing

      $ make -C $KDIR M=$PWD

  will then do the expected and compile both modules with         full

kenntnis der Symbole aus beiden Modulen.

Zusätzliche Module.symvers-Datei verwenden Wenn ein externes Modul erstellt wird, wird eine Module.symvers-Datei generiert, die alle exportierten Symbole enthält, die nicht im Kernel definiert sind. Kopieren Sie die Datei Module.symvers aus der Kompilierung von bar.ko in das Verzeichnis, in dem foo.ko erstellt wird, um auf Symbole von bar.ko zuzugreifen. Während der Modulerstellung liest kbuild die Datei Module.symvers im Verzeichnis des externen Moduls. Wenn die Erstellung abgeschlossen ist, wird eine neue Datei Module.symvers erstellt, die die Summe aller definierten Symbole enthält, die nicht Teil des Kernels sind.

Verwenden Sie die Variable "make" KBUILD_EXTRA_SYMBOLS. Wenn es nicht praktikabel ist, Module.symvers aus einem anderen Modul zu kopieren, können Sie KBUILD_EXTRA_SYMBOLS in Ihrer Build-Datei eine durch Leerzeichen getrennte Liste von Dateien zuweisen. Diese Dateien werden von modpost während der Initialisierung seiner Symboltabellen geladen.

Bei allen drei dieser Lösungen müsste ein Treiber jedoch entweder ein neues Makefile erstellen oder direkten Zugriff auf meine Module.symvers-Datei haben, damit er meine API verwenden kann. Das scheint ein bisschen unpraktisch. Ich hatte gehofft, sie könnten einfach meine Header-Datei einbinden und loslegen. Gibt es keine anderen Alternativen?

24
jacobsowles

Nach meinen Recherchen scheint es, dass dies die einzigen drei Wege sind, um mit dieser Situation umzugehen, und ich habe jeden davon zum Laufen gebracht. Ich denke, ich werde einfach meinen Favoriten daraus auswählen.

6
jacobsowles

Minimales QEMU + Buildroot-Beispiel

Ich habe Folgendes in einer vollständig reproduzierbaren QEMU + Buildroot-Umgebung getestet. Wenn Sie diese funktionierende Version verwenden, können Sie vielleicht herausfinden, was mit Ihrem Code funktioniert.

GitHub-Upstream basiert auf den Dateien: dep.c | dep2.c | Makefile

dep.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)

Jetzt können Sie entweder tun:

insmod dep.ko
insmod dep2.ko

buildroot konfiguriert jedoch bereits depmod /lib/module/*/depmod mit der Abhängigkeit, also reicht dies aus, um beide zu laden:

modprobe dep

Wenn Sie CONFIG_KALLSYMS_ALL=y verwenden, kann das Symbol folgendermaßen angezeigt werden:

grep lkmc_dep /proc/kallsyms

siehe auch: Verfügt Kallsyms über das Symbol für Kernfunktionen?

OK: Sie haben ein Modul, in dem die Funktion vorhanden ist, und einen Ort, an dem sie richtig importiert werden soll.

Sie müssen "EXPORT_SYMBOL (" Name der Funktion ") wie foo an der Stelle verwenden, an der sich die Funktion befindet. In der" c "-Datei muss die Funktion" foo "definiert und eingefügt werden: EXPORT_SYMBOL (foo )

Vergewissern Sie sich, dass Sie den Prototyp für "foo" in einer gemeinsamen Header-Datei haben (Sie können es für jedes Modul an unterschiedlichen Stellen haben und es funktioniert, aber Sie fragen nach Problemen, wenn sich die Signaturen ändern). Also sag: void foo (void * arg);

Dann ruft das andere Modul, das es will, einfach "foo" auf und du bist gut.

Außerdem: Stellen Sie sicher, dass Sie zuerst das Modul mit foo laden. Wenn Sie Querabhängigkeiten haben, wie Modul2 foo von Modul1 benötigt und module1 Bar von Modul2 benötigt, müssen Sie eine Registerfunktion mit einer anderen haben. Wenn Sie möchten, fragen Sie bitte ein separates Q.

0
user5078679