Подтвердить что ты не робот

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

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

Чтобы сделать все просто, позвоните этим модулям как m1 и m2.

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

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

[email protected]:~/development/kmodules/m2$ sudo insmod ./m2.ko

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

[email protected]:~/development/kmodules/m1$ sudo insmod ./m1.ko
insmod: error inserting './m1.ko': -1 Unknown symbol in module

Ниже приводится то, что я вижу в журналах:

[email protected]:~/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 не разрешены. Интересно. Проверьте, присутствует ли в таблице символов:

[email protected]:~/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?

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

4b9b3361

Ответ 1

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

(а). Ваши функции инициализации и завершения должны быть объявлены статическими и правильно идентифицированными. Например, в 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() - undefined. Во всяком случае, консолидированный 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. Нет никакого способа обойти это. Если вам не нужны несколько ko файлов, тогда поместите весь свой код в один исходный файл.

Ответ 2

Когда вы создаете m2, он создает файл Module.symvers.

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

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

ПРЕДУПРЕЖДЕНИЕ: "func_m2" [/tmp/m1/m1.ko] undefined!

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

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

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

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

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

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

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

ПРИМЕЧАНИЕ. Рекомендуется использовать метод с файлом 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 is встроенный. Во время сборки модуля kbuild будет читать Файл Module.symvers в каталоге внешнего модуль, и когда сборка завершена, новый Создается файл Module.symvers, содержащий сумму все символы определены, а не являются частью ядра.

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