web-dev-qa-db-fra.com

Comment appeler les fonctions de module de noyau exportées depuis un autre module?

J'écris une API en tant que module de noyau qui fournit des pilotes de périphérique avec diverses fonctions. J'ai écrit trois fonctions dans mycode.c . J'ai ensuite construit et chargé le module, puis copié mycode.h dans <noyau>/include/linux . Dans un pilote de périphérique, j'ai un #include <linux/mycode.h> et j'appelle ces trois fonctions. Mais lorsque je construis le module du pilote, trois avertissements de l'éditeur de liens m'indiquent que ces fonctions ne sont pas définies .

Remarques:

  • Les fonctions sont déclarées extern dans mycode.h
  • Les fonctions sont exportées à l’aide de EXPORT_SYMBOL (func_name) in mycode.c
  • L'exécution de la commande nm mycode.ko indique que les trois fonctions sont disponibles dans la table des symboles (le symbole T majuscule à côté d'elles, ce qui signifie que les symboles se trouvent dans la section texte (code)).
  • Après avoir chargé le module, la commande grep nom_fonction/proc/kallsyms indique que les trois fonctions sont en cours de chargement.

Il est donc clair que les fonctions sont correctement exportées et le noyau sait quoi et où elles se trouvent. Alors, pourquoi le conducteur ne voit-il pas leurs définitions? Une idée de ce qui me manque?


EDIT: J'ai trouvé des informations à ce sujet ici: http://www.kernel.org/doc/Documentation/kbuild/modules.txt

Parfois, un module externe utilise les symboles exportés d'un autre module externe. kbuild doit avoir une connaissance complète de tous les symboles pour éviter de générer des avertissements concernant des symboles non définis. Trois solutions existent pour cette situation.

REMARQUE: la méthode avec un fichier kbuild de niveau supérieur est recommandée mais peut s'avérer peu pratique dans certaines situations.

Utiliser un fichier kbuild de niveau supérieur Si vous avez deux modules, foo.ko et bar.ko, où foo.ko a besoin des symboles de bar.ko, vous pouvez utiliser un fichier kbuild de niveau supérieur commun pour que les deux modules soient compilés de la même manière. construire. Considérez la disposition de répertoire suivante:

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

connaissance des symboles de l'un ou l'autre module.

Utiliser un fichier Module.symvers supplémentaire Lorsqu'un module externe est créé, un fichier Module.symvers contenant tous les symboles exportés non définis dans le noyau est généré. Pour accéder aux symboles de bar.ko, copiez le fichier Module.symvers de la compilation de bar.ko dans le répertoire où foo.ko est créé. Au cours de la construction du module, kbuild lira le fichier Module.symvers dans le répertoire du module externe et, une fois la construction terminée, un nouveau fichier Module.symvers contenant la somme de tous les symboles définis et ne faisant pas partie du noyau.

Utilisez la variable "make" KBUILD_EXTRA_SYMBOLS S'il n'est pas pratique de copier Module.symvers à partir d'un autre module, vous pouvez affecter une liste de fichiers séparés par des espaces à KBUILD_EXTRA_SYMBOLS dans votre fichier de construction. Ces fichiers seront chargés par modpost lors de l'initialisation de ses tables de symboles.

Mais avec ces trois solutions, pour qu'un pilote puisse utiliser mon API, il devrait créer un nouveau Makefile ou avoir un accès direct à mon fichier Module.symvers? Cela semble un peu gênant. J'espérais qu'ils pourraient juste inclure mon fichier d'en-tête et être prêts à partir. Est-ce qu'il n'y a pas d'autres alternatives?

24
jacobsowles

D'après mes recherches, il semble que ce soient les trois seules façons de gérer cette situation. Chacune d'entre elles fonctionnant bien, je pense donc que je vais choisir mon préféré parmi celles-ci.

6
jacobsowles

Exemple QEMU + Buildroot minimal

J'ai testé les éléments suivants dans un environnement QEMU + Buildroot entièrement reproductible. Par conséquent, le fait de disposer de cette version de travail vous aidera à savoir ce qui se passe avec votre code.

GitHub en amont est centré sur les fichiers: 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)

Maintenant, vous pouvez soit faire:

insmod dep.ko
insmod dep2.ko

mais Buildroot est également en train de configurer depmod /lib/module/*/depmod avec la dépendance, donc cela suffit pour charger les deux:

modprobe dep

Si vous utilisez CONFIG_KALLSYMS_ALL=y, alors le symbole peut être vu avec:

grep lkmc_dep /proc/kallsyms

voir aussi: Les kallsyms ont-ils tous le symbole des fonctions du noyau?

OK: Vous avez un module dans lequel la fonction est et un endroit où l’importation est correcte?

Vous devez utiliser "EXPORT_SYMBOL (" nom de la fonction ") tel que foo à la place de la fonction. Ainsi, dans le fichier" c ", définissez la fonction" foo "et insérez-la dans: EXPORT_SYMBOL (foo )

Assurez-vous que vous avez le prototype de "foo" dans un fichier d’en-tête commun (vous pouvez le placer à des emplacements distincts pour chaque module et cela fonctionnera, mais vous demandez des problèmes si les signatures changent). Alors dites: void foo (void * arg);

Ensuite, l’autre module qui le souhaite appelle simplement "foo" et vous êtes bon.

Aussi: Assurez-vous de charger le module avec foo en premier. Si vous avez des dépendances croisées, comme module2, il faut foo de module1 et module1 a besoin de barre de module2, vous devez disposer d’un registre avec l’autre. Si vous voulez savoir s'il vous plaît demander un autre Q.

0
user5078679