Как вызывать экспортированные функции модуля ядра из другого модуля?

Я пишу API как модуль ядра, который предоставляет драйверы устройств с различными функциями. Я написал три функции в mycode.c. Затем я построил и загрузил модуль, а затем скопировал mycode.h в <kernel> /include/linux. В драйвере устройства у меня есть #include < linux/mycode.h > и вызовите эти три функции. Но когда я создаю модуль драйвера, я получаю три предупреждения компоновщика, говорящие, что эти функции undefined.

Примечания:

  • Функции объявлены extern в mycode.h
  • Функции экспортируются с помощью EXPORT_SYMBOL (func_name) в mycode.c
  • Запуск команды nm mycode.ko показывает все три функции как доступные в таблице символов (рядом с ними находится столица T, то есть символы находятся в разделе текста (кода))
  • После загрузки модуля команда grep func_name/proc/kallsyms показывает все три функции как загружаемые

Так ясно, что функции экспортируются правильно, а ядро ​​знает, что и где они находятся. Так почему водитель не может видеть свои определения? Любая идея, что мне не хватает?


EDIT: я нашел здесь некоторую информацию: http://www.kernel.org/doc/Documentation/kbuild/modules.txt

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

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

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

  ./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

знание символов из любого модуля.

Используйте дополнительный файл 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 во время инициализации его таблицы символов.

Но со всеми тремя этими решениями, чтобы любой драйвер мог использовать мой API, ему пришлось бы либо создавать новый Makefile, либо иметь прямой доступ к моему файлу Module.symvers? Это кажется немного неудобным. Я надеялся, что они просто смогут # включить мой заголовочный файл и быть хорошим. Нет ли других альтернатив?

Ответ 1

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

Ответ 2

Минимальный пример QEMU + Buildroot

Я тестировал следующее в полностью воспроизводимой среде QEMU + Buildroot, поэтому, возможно, эта версия рабочей версии поможет вам узнать, что такое wro с вашим кодом.

GitHub upstream сосредоточен на файлах: dep.c | dep2.c | Makefile

dep.c:

#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>

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 <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>

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)

Теперь вы можете:

insmod dep.ko
insmod dep2.ko

но Buildroot уже настроил depmod /lib/module/*/depmod с зависимостью, поэтому этого достаточно, чтобы загрузить оба:

modprobe dep

Если вы используете CONFIG_KALLSYMS_ALL=y, тогда символ можно увидеть с помощью

grep lkmc_dep /proc/kallsyms

см. также: Имеются ли у kallsyms все символы функций ядра?