web-dev-qa-db-fra.com

insmod échoue avec "Symbole inconnu dans le module" pour un symbole défini dans un autre module

Je travaille dans Ubuntu. J'essaie de créer deux modules de noyau qui utilisent mutuellement des fonctions. Mon problème est que j'ai correctement compilé les modules, mais le symbole n'est pas résolu pour l'un d'eux.

Pour simplifier les choses, appelons ces modules comme m1 Et m2.

m2 exporte la fonction void func_m2(void). Le m1 Appelle cette fonction. Les deux modules compilent correctement.

Une fois la compilation terminée, je dois d'abord charger le module m2 (Car il a exporté la fonction func_m2) Et ensuite le module m1. Alors, faisons-le:

volodymyr@sv1:~/development/kmodules/m2$ Sudo insmod ./m2.ko

Maintenant, chargeons le module m1 Qui essaie d'utiliser func_m2:

volodymyr@sv1:~/development/kmodules/m1$ Sudo insmod ./m1.ko
insmod: error inserting './m1.ko': -1 Unknown symbol in module

Voici ce que je vois dans les journaux:

volodymyr@sv1:~/development/kmodules/m1$ dmesg | tail
[ 3938.166616] Loading m2 module ...
[ 3963.078055] m1: no symbol version for func_m2
[ 3963.078059] m1: Unknown symbol func_m2

Il semble donc que les références au symbole func_m2 Ne soient pas résolues. Intéressant. Vérifions s'il est présent dans la table des symboles:

volodymyr@sv1:~/development/kmodules$ cat /proc/kallsyms | grep 'func_m2'
ffffffffa00530d0 r __ksymtab_func_m2    [m2]
ffffffffa00530e8 r __kstrtab_func_m2    [m2]
ffffffffa00530e0 r __kcrctab_func_m2    [m2]
ffffffffa0053000 T func_m2      [m2]
000000004edd543f a __crc_func_m2        [m2]

Comme vous pouvez le voir, le func_m2 Est en fait présent dans la table des symboles. Alors pourquoi m1 Ne peut pas être chargé?

J'ai installé correctement les en-têtes Linux pour mon noyau et les sources Linux. Je n'ai fait aucune modification dans le noyau, il est intact et sa version est: 2.6.31-16-generic (je lance x64)

Maintenant, pour vous montrer l'image complète, je mets ici le code source et le Makefile que j'ai utilisés pour ce test pour les modules m1 Et m2.

module m1:

m1.c:

#include <linux/module.h>
#include <linux/kernel.h>

extern void func_m2(void);

int hello_start(void)
{
    printk(KERN_INFO "Loading m1 module ...\n");

    func_m2();

    return 0;
 }

 void hello_end(void)
 {
    printk(KERN_INFO "Unloading m1 ...\n");
 }

module_init(hello_start);
module_exit(hello_end);

MODULE_LICENSE("GPL");

Makefile:

obj-m := m1.o

all:
    make -C /lib/modules/$(Shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(Shell uname -r)/build M=$(PWD) clean

module m2:

m2.c:

#include <linux/module.h>
#include <linux/kernel.h>

int hello_start(void)
{
    printk(KERN_INFO "Loading m2 module ...\n");

    return 0;
}

void hello_end(void)
{
    printk(KERN_INFO "Unloading m2 ...\n");
}

void func_m2(void)
{
    printk(KERN_INFO "This a function in m2\n");
}

module_init(hello_start);
module_exit(hello_end);

MODULE_LICENSE("GPL");
EXPORT_SYMBOL(func_m2);

Makefile:

obj-m := m2.o
export-objs := m2.o

all:
    make -C /lib/modules/$(Shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(Shell uname -r)/build M=$(PWD) clean

Fondamentalement, ma question est: Pourquoi ne peut-on pas charger m1?

Il serait utile que quelqu'un puisse répondre.

16
user5485048

Voici quelques problèmes que j'ai trouvés avec votre code:

(une). Vos fonctions d'initialisation et de terminaison doivent être déclarées statiques et correctement identifiées. Par exemple, dans m1.c -

static int __init hello_start(void)
{
     printk(KERN_INFO "Loading m1 module ...\n");

    func_m2();

    return 0;
}

static void __exit hello_end(void)
{
    printk(KERN_INFO "Unloading m1 ...\n");
}

Répétez cette opération pour m2.c

(b). Construisez vos deux modules ensemble, en utilisant le même Makefile. Je parie que si vous regardez attentivement la sortie de votre Makefile existant pour m1.c, vous verrez un avertissement indiquant que func_m2 () n'est pas défini. Quoi qu'il en soit, le Makefile consolidé devrait ressembler à -

SRCS   = m1.c m2.c
OBJS   = $(SRCS:.c=.o)

obj-m += $(OBJS)

EXTRA_CFLAGS = -O2


all:
    $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) modules

clean:
    $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) clean
    $(RM) Module.markers modules.order

Une fois les deux modules construits, exécutez insmod sur 'm2.ko' avant d'émettre l'insmod pour 'm1.ko'. Vérifiez les résultats via dmesg.

De plus, ici, je suppose que m1.c et m2.c sont dans le même répertoire. Même s'ils se trouvent dans des répertoires différents, cette technique fonctionnera, mais ce sera compliqué. S'ils se trouvent dans des répertoires différents, procédez comme suit.

J'ai fait peu de recherches et trouvé un moyen de construire des modules dans des répertoires séparés. L'exemple que j'ai utilisé est beaucoup plus simple que ce que vous avez, mais il est peut-être adaptable.

J'ai le manifeste suivant de fichiers dans un répertoire appelé ExportSymbol ...

$ ls -CFR
.:
include/  Makefile  mod1/  mod2/

./include:
m2_func.h

./mod1:
Makefile  module1.c

./mod2:
Makefile  module2.c

Le m2_func.h apparaît comme:

#ifndef M2_FUNC_H
#define M2_FUNC_H

void m2_func(void);

#endif

Le Makefile de niveau supérieur apparaît comme:

obj-y := mod1/ mod2/

all:
    $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) modules

clean:
    $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) clean
    $(RM) Module.markers modules.order

Le Makefile et module1.c, qui sont en mod1 /, apparaissent comme:

SRCS   = module1.c
OBJS   = $(SRCS:.c=.o)

obj-m += $(OBJS)

EXTRA_CFLAGS += -I${PWD}/include

all:
    $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) modules

clean:
    $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) clean
    $(RM) Module.markers modules.order

#include <linux/module.h>
#include <linux/kernel.h>

static int __init hello_start(void)
{
 printk(KERN_INFO "Loading m1 module ...\n");

 m2_func();

 return 0;
}

static void __exit hello_end(void)
{
 printk(KERN_INFO "Unloading m1 ...\n");
}

module_init(hello_start);
module_exit(hello_end);

MODULE_LICENSE("GPL");

Le Makefile et module2.c, qui sont en mod2 /, apparaissent comme:

SRCS   = module2.c
OBJS   = $(SRCS:.c=.o)

obj-m += $(OBJS)

EXTRA_CFLAGS += -I${PWD}/include

all:
    $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) modules

clean:
    $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) clean
    $(RM) Module.markers modules.order

#include "m2_func.h"
#include <linux/module.h>
#include <linux/kernel.h>

static int __init hello_start(void)
{
 printk(KERN_INFO "Loading m2 module ...\n");

 return 0;
}

static void __exit hello_end(void)
{
 printk(KERN_INFO "Unloading m2 ...\n");
}

void m2_func(void)
{
 printk(KERN_INFO "This a function in m2\n");
} 

module_init(hello_start);
module_exit(hello_end);

MODULE_LICENSE("GPL");
EXPORT_SYMBOL(m2_func);

NOTE: Je ne peux pas utiliser votre makefile car il génère * .ko pour chaque fichier c. Le Makefile fait son travail. Un fichier 'ko' est un fichier objet du noyau; vous en aurez un pour chaque fichier source .c. Il n'y a aucun moyen de contourner cela. Si vous ne voulez pas plusieurs fichiers Ko, alors mettez tout votre code dans un seul fichier source.

3
Ashish Ahuja

Lorsque vous construisez m2, il crée un Module.symvers fichier.

Copiez ce fichier là où vous construisez m1. Ensuite, faites m1 et insmodez-le.

Vous aviez probablement un avertissement lorsque vous construisiez m1 auparavant, quelque chose comme:

AVERTISSEMENT: "func_m2" [/tmp/m1/m1.ko] non défini!

Cela devrait disparaître une fois que vous utilisez le Module.symvers du module m2.

De http://www.kernel.org/doc/Documentation/kbuild/modules.txt :

--- 6.2 Symboles et modules externes

Lors de la construction d'un module externe, le système de génération doit avoir accès aux symboles à partir du noyau pour vérifier si tous les symboles externes sont définis. Cela se fait à l'étape MODPOST. modpost obtient les symboles en lisant Module.symvers dans l'arborescence des sources du noyau. Si un fichier Module.symvers est présent dans le répertoire où le module externe est en cours de construction, ce fichier sera également lu. Au cours de l'étape MODPOST, un nouveau fichier Module.symvers sera écrit contenant tous les symboles exportés qui n'ont pas été définis dans le noyau.

Et cela vaut également la peine d'être lu, à partir du même fichier:

--- 6.3 Symboles d'un autre module externe

Parfois, un module externe utilise des symboles exportés d'un autre module externe. kbuild doit avoir une connaissance complète de tous les symboles pour éviter de cracher des avertissements sur les 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 ne pas être 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 de symboles de bar.ko, vous pouvez utiliser un fichier kbuild de niveau supérieur commun afin que les deux modules soient compilés dans le même construire. Considérez la disposition de répertoire suivante:

./foo/ <= contient foo.ko ./bar/ <= contient bar.ko

Le fichier kbuild de niveau supérieur ressemblerait alors à:

$ ./Kbuild (ou ./Makefile): obj-y: = foo/bar /

Et l'exécution

$ make -C $ KDIR M = $ PWD

fera ensuite ce qui est attendu et compilera les deux modules avec une connaissance complète des symboles des deux modules.

Utiliser un fichier Module.symvers supplémentaire Lorsqu'un module externe est construit, un fichier Module.symvers est généré contenant tous les symboles exportés qui ne sont pas définis dans le noyau. 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 construit. Pendant la construction du module, kbuild lira le fichier Module.symvers dans le répertoire du module externe, et lorsque la construction est terminée, un nouveau fichier Module.symvers est créé 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 génération. Ces fichiers seront chargés par modpost lors de l'initialisation de ses tables de symboles.

6
Michael