Comment appeler les fonctions de module de kernel exscopes depuis un autre module?

J’écris une API sous la forme d’un module de kernel fournissant 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 / include / linux . Dans un pilote de périphérique, j’ai un #include et j’appelle ces trois fonctions. Mais lorsque je construis le module de 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 exscopes à l’aide de EXPORT_SYMBOL (nom_fonction) dans 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’eux, ce qui signifie que les symboles se trouvent dans la section texte (code)).
  • Après avoir chargé le module, la commande grep nom_du_fonction / proc / kallsyms indique que les trois fonctions sont en cours de chargement.

Il est donc clair que les fonctions sont correctement exscopes et le kernel 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 est généré, lequel contient tous les symboles exportés qui ne sont pas définis dans le kernel. 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 sum de tous les symboles définis et ne faisant pas partie du kernel.

Utilisez la variable “make” KBUILD_EXTRA_SYMBOLS S’il est impossible de copier Module.symvers à partir d’un autre module, vous pouvez affecter une liste de fichiers séparés par un espace à 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 access 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?

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.

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  /* usleep_range */ #include  #include  #include  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  /* usleep_range */ #include  #include  #include  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 aussi 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: kallsyms a-t-il tout le symbole des fonctions du kernel?

OK: Vous avez un module dans lequel se trouve la fonction et un endroit où importer le bon?

Vous devez utiliser “EXPORT_SYMBOL (” nom de la fonction “) tel que foo à l’endroit où se trouve la fonction. Ainsi, dans le fichier” c “, définissez la fonction” foo “et mettez-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.