Сбой 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