Сбой insmod с "Неизвестным символом в модуле" для символа, определенного в другом модуле

Я работаю в Ubuntu. Я пытаюсь сделать два модуля ядра, которые используют функции друг друга. Моя проблема в том, что я правильно скомпилировал модули, но символ не разрешен для одного из них.

Для простоты давайте назовем эти модули m1 а также m2,

м2 экспортирует функцию void func_m2(void), m1 вызывает эту функцию. Оба модуля правильно компилируются.

После того, как все это скомпилируется, мне нужно сначала загрузить m2 модуль (потому что он экспортировал func_m2 функция), а потом m1 модуль. Итак, давайте сделаем это:

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

Теперь давайте загрузим m1 модуль, который пытается использовать func_m2:

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

Вот что я вижу в логах:

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

Итак, похоже, что ссылки на символ func_m2 не разрешено Интересно. Давайте проверим, присутствует ли он в таблице символов:

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]

Как видите, func_m2 на самом деле присутствует в таблице символов. Так почему m1 не может быть загружен?

Я правильно установил заголовки Linux для своего ядра и исходников Linux. Я не делал никаких изменений в ядре, он не тронут, и его версия: 2.6.31-16-generic (я запускаю x64)

Теперь, чтобы показать вам полную картину, я помещаю сюда исходный код и Makefile, которые я использовал для этого теста для обоих m1 а также m2 модули.

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

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

В основном мой вопрос: почему не может m1 быть загруженным?

Было бы полезно, если бы кто-то мог ответить.

3 ответа

Решение

Вот некоторые проблемы, которые я нашел с вашим кодом:

(А). Ваши функции инициализации и завершения должны быть объявлены статическими и должным образом идентифицированы. Например, в 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");
}

Повторите это для m2.c

(Б). Соберите оба своих модуля вместе, используя один и тот же Makefile. Бьюсь об заклад, если вы внимательно посмотрите на вывод из вашего существующего Makefile для m1.c, вы увидите предупреждение о том, что func_m2() не определена. В любом случае, сводный Makefile должен выглядеть так:

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

После того, как оба модуля собраны, запустите insmod на m2.ko, прежде чем выдавать insmod для m1.ko. Проверьте результаты через dmesg.

Также здесь я предполагаю, что и m1.c, и m2.c находятся в одном каталоге. Даже если они находятся в разных каталогах, этот метод будет работать, но он будет грязным. Если они находятся в разных каталогах, сделайте следующее.

Я провел небольшое исследование и нашел способ создавать модули в отдельных каталогах. Пример, который я использовал, намного проще, чем у вас, но, возможно, его можно адаптировать.

У меня есть следующий манифест файлов в каталоге с именем ExportSymbol...

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

./include:
m2_func.h

./mod1:
Makefile  module1.c

./mod2:
Makefile  module2.c

M2_func.h выглядит как:

#ifndef M2_FUNC_H
#define M2_FUNC_H

void m2_func(void);

#endif

Makefile верхнего уровня выглядит так:

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

Makefile и module1.c, которые находятся в mod1/, выглядят как:

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");

Makefile и module2.c, которые находятся в mod2/, выглядят как:

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);

ПРИМЕЧАНИЕ: я не могу использовать ваш make-файл, так как он генерирует *.ko для каждого файла c. Makefile делает свою работу. Файл 'ko' - это объектный файл ядра; у вас будет один для каждого исходного файла.c. Там нет никакого способа обойти это. Если вам не нужны несколько ко-файлов, поместите весь код в один исходный файл.

Когда вы строите м2, он создает Module.symvers файл.

Скопируйте этот файл туда, где вы строите m1. Затем сделайте m1 и вставьте его.

У вас, вероятно, было предупреждение, когда вы собирали m1 раньше, что-то вроде:

ВНИМАНИЕ: "func_m2" [/tmp/m1/m1.ko] не определено!

Это должно исчезнуть, как только вы используете Module.symvers из модуля м2.

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

--- 6.2 Символы и внешние модули

При сборке внешнего модуля системе сборки необходим доступ к символам из ядра, чтобы проверить, определены ли все внешние символы. Это делается на шаге MODPOST. Modpost получает символы, читая Module.symvers из дерева исходных текстов ядра. Если файл Module.symvers присутствует в каталоге, где создается внешний модуль, этот файл также будет прочитан. На этапе MODPOST будет записан новый файл Module.symvers, содержащий все экспортированные символы, которые не были определены в ядре.

И это тоже стоит прочитать из того же файла:

--- 6.3 Символы от другого внешнего модуля

Иногда внешний модуль использует экспортированные символы из другого внешнего модуля. kbuild должен иметь полное знание всех символов, чтобы избежать появления предупреждений о неопределенных символах. Три решения существуют для этой ситуации.

ПРИМЕЧАНИЕ. Рекомендуется использовать метод с файлом верхнего уровня kbuild, но в некоторых ситуациях он может быть непрактичным.

Использование файла kbuild верхнего уровня Если у вас есть два модуля, foo.ko и bar.ko, где foo.ko нужны символы из bar.ko, вы можете использовать общий файл kbuild верхнего уровня, чтобы оба модуля компилировались в одном и том же строить. Рассмотрим следующую схему каталогов:

./foo/ <= содержит foo.ko ./bar/ <= содержит bar.ko

Файл верхнего уровня kbuild будет выглядеть так:

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

И выполнение

$ make -C $KDIR M=$PWD

Затем выполним ожидаемое и скомпилируем оба модуля с полным знанием символов из любого модуля.

Использование дополнительного файла Module.symvers При сборке внешнего модуля создается файл Module.symvers, содержащий все экспортированные символы, которые не определены в ядре. Чтобы получить доступ к символам из bar.ko, скопируйте файл Module.symvers из компиляции bar.ko в каталог, где собран foo.ko. Во время сборки модуля kbuild будет читать файл Module.symvers в каталоге внешнего модуля, а когда сборка завершится, будет создан новый файл Module.symvers, содержащий сумму всех определенных символов, а не часть ядра.

Используйте переменную "make" KBUILD_EXTRA_SYMBOLS Если копировать Module.symvers из другого модуля нецелесообразно, вы можете назначить разделенный пробелами список файлов для KBUILD_EXTRA_SYMBOLS в вашем файле сборки. Эти файлы будут загружены modpost во время инициализации его таблиц символов.

Самый простой способ - создать Makefile для двух модулей одновременно.

пример :

       obj-m += hello.o
 obj-m += world.o
 
 all:
      make -C /lib/modules/`uname -r`/build M=$(PWD) modules
install: 
      make -C /lib/modules/`uname -r`/build M=$(PWD) modules_install
clean:
      make -C /lib/modules/`uname -r`/build M=$(PWD) clean

Первый модуль hello.c

      #inluce  <linux/module.h>
#include <linux/init.h>

int hello_print(){
 printk("<0> Hello -> %s : %d",__FUNCTION__,__LINE__);
return 0;
}
EXPORT_SYMBOL(hello_print),

static int __init open(void)
{
    printk("<0> Module Hello Start!");
    return 0;
}

static void __exit close(void) {
    printk("<0> Module Hello Stop!");
}

module_init(open);
module_exit(close);
MODULE_LICENSE("GPL v2");

Второй модуль world.c

      #inluce  <linux/module.h>
#include <linux/init.h>

extern int hello_print();

static int __init open(void)
{
    printk("<0> Init World Module");
    printk("<0> Call hello_print() from Hello Module");
    hello_print();
    return 0;
}

static void __exit close(void) {
    printk("<0> Module Hello Stop!");
}

module_init(open);
module_exit(close);
MODULE_LICENSE("GPL v2");

тогда

      $ make 
$ insmod hello.ko
$ insmod world.ko
Другие вопросы по тегам