How to call exported kernel module functions from another module?
Asked Answered
S

3

29

I'm writing an API as a kernel module that provides device drivers with various functions. I wrote three functions in mycode.c. I then built and loaded the module, then copied mycode.h into < kernel >/include/linux. In a device driver, I have a #include < linux/mycode.h > and call those three functions. But when I build the driver module, I get three linker warnings saying that those functions are undefined.

Notes:

  • The functions are declared extern in mycode.h
  • The functions are exported using EXPORT_SYMBOL(func_name) in mycode.c
  • Running the command nm mycode.ko shows all three functions as being available in the symbol table (capital T next to them, meaning the symbols are found in the text (code) section)
  • After loading the module, the command grep func_name /proc/kallsyms shows all three functions as being loaded

So clearly the functions are being exported correctly and the kernel knows what and where they are. So why can't the driver see their definitions? Any idea what am I missing?


EDIT: I found some information about this here: http://www.kernel.org/doc/Documentation/kbuild/modules.txt

Sometimes, an external module uses exported symbols from another external module. kbuild needs to have full knowledge of all symbols to avoid spitting out warnings about undefined symbols. Three solutions exist for this situation.

NOTE: The method with a top-level kbuild file is recommended but may be impractical in certain situations.

Use a top-level kbuild file If you have two modules, foo.ko and bar.ko, where foo.ko needs symbols from bar.ko, you can use a common top-level kbuild file so both modules are compiled in the same build. Consider the following directory layout:

  ./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 knowledge of symbols from either module.

Use an extra Module.symvers file When an external module is built, a Module.symvers file is generated containing all exported symbols which are not defined in the kernel. To get access to symbols from bar.ko, copy the Module.symvers file from the compilation of bar.ko to the directory where foo.ko is built. During the module build, kbuild will read the Module.symvers file in the directory of the external module, and when the build is finished, a new Module.symvers file is created containing the sum of all symbols defined and not part of the kernel.

Use "make" variable KBUILD_EXTRA_SYMBOLS If it is impractical to copy Module.symvers from another module, you can assign a space separated list of files to KBUILD_EXTRA_SYMBOLS in your build file. These files will be loaded by modpost during the initialization of its symbol tables.

But with all three of these solutions, in order for any driver to use my API, it would have to either create a new Makefile or have direct access to my Module.symvers file? That seems a bit inconvenient. I was hoping they'd just be able to #include my header file and be good to go. Do no other alternatives exist?

Sexton answered 7/9, 2012 at 4:46 Comment(1)
Just in case anyone has trouble reading the block quote above; the three methods are: 1) Use a top-level kbuild file; 2) Use an extra Module.symvers file; and 3) Use a make variable KBUILD_EXTRA_SYMBOLS.Melchior
S
8

From my research, it seems that those are the only three ways to handle this situation, and I've gotten each of them to work, so I think I'll just pick my favorite out of those.

Sexton answered 8/9, 2012 at 14:34 Comment(2)
Do you have a package example? Could you share? Example is the best definition.Auriscope
I recently discovered that depmod automatically gets the symbols in /lib/modules/$(uname -r)/modules.symbols if you have modules_installed your module. To my great disappointment, modpost still doesn't see those symbols! I wonder why it doesn't complain about kernel's own functions but just the ones I installed.Gynous
S
4

Minimal QEMU + Buildroot example

I have tested the following in a fully reproducible QEMU + Buildroot environment, so maybe having this working version version will help you find out what is wong with your code.

GitHub upstream is centered on the files:

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)

And now you can do:

insmod dep.ko
insmod dep2.ko

With that Buildroot setup, things are already configuring depmod /lib/module/*/depmod with the dependency, so just this is enough to load both:

modprobe dep

Also, if you built your kernel with CONFIG_KALLSYMS_ALL=y, then the exported symbol can be seen with:

grep lkmc_dep /proc/kallsyms

see also: Does kallsyms have all the symbol of kernel functions?

Stephenstephenie answered 18/6, 2017 at 10:55 Comment(0)
L
0

OK: You have one module where the function is and one place what wants to import it right?

You must use "EXPORT_SYMBOL("name of the function") such as foo in the place where the function is. So in the "c" file have the function "foo" defined and put in: EXPORT_SYMBOL(foo)

Make sure you have the prototype for "foo" in a common header file (you can have it in separate places for each module and it will work but you are asking for trouble if the signatures change). So say: void foo(void *arg);

Then the other module that wants it just invoke "foo" and you are good.

Also: Make sure that you load the module with foo first. If you have cross dependencies like module2 needs foo from module1 and module1 needs bar from module2 you need to have one register functions with another. If you want to know please ask a separate Q.

Legendary answered 25/3, 2018 at 22:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.