How to Export Symbol from Linux Kernel Module in This Case

How to export symbol from Linux kernel module in this case?

You may provide the callback function from module A. In that case you don't need to export each function you need to the kernel namespace. I presume you just could supply some structure to the B. Something like:

internal header:

struct possible_ops {
int (*xmit)(...);
};

A:

struct private {
struct possible_ops *ops;
};
...
ops = kzalloc(sizeof(*ops));
ops->xmit = xmit;

B:

whatever(struct possible_ops *ops) {
if (ops && ops->xmit) {
ret = ops->xmit();
...
}
}

How to export a symbol from an external module?

Add this line at the very top of your Makefile for your hello module:

KBUILD_EXTRA_SYMBOLS := /home/your-user/path/to/printt/Module.symvers

(be sure to put in the correct path to your printt module).

Now rebuild your hello module and it will be loaded just fine.

For details see Documentation/kbuild/modules.txt, "6.3 Symbols From Another External Module".

How to call exported kernel module functions from another module?

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.

Yocto Patch Linux Kernel In-Tree-Module with extern symbol exported from Out-Of-Tree Module

There are several ways to achieve what you want (taking into account different aspects, like module can be compiled in or be a module).

  1. Convert Out-Of-Tree module to be In-Tree one (in your custom kernel build). This will require simple export and import as you basically done and nothing special is required, just maybe providing a header with the symbol and depmod -a run after module installation. Note, you have to use modprobe in-tree which reads and satisfies dependencies.
  2. Turn other way around, i.e. export symbol from in-tree module and file it in the out-of-tree. In this case you simply have to check if it has been filed or not (since it's a MAC address the check against all 0's will work, no additional flags needed)

BUT, these ways are simply wrong. The driver and even your patch clearly show that it supports OF (Device Tree) and your board has support of it. So, this is a first part of the solution, you may provide correct MAC to the network card using Device Tree.

In the case you want to change it runtime the procfs approach is very strange to begin with. Network device interface in Linux has all means to update MAC from user space at any time user wants to do it. Just use ip command, like /sbin/ip link set <$ETH> addr <$MACADDR>, where <$ETH> is a network interface, for example, eth0 and <$MACADDR> is a desired address to set.

So, if this question rather about module symbols, you need to find better example for it because it's really depends to use case. You may consider to read How to export symbol from Linux kernel module in this case? as an alternative way to exporting. Another possibility how to do it right is to use software nodes (it's a new concept in recent Linux kernel).

Proper way of getting the address of non-exported kernel symbols in a Linux kernel module

Disclaimer: using non-exported symbols is in general not a good idea, so you should only do it for educational purposes, not for production-ready modules/drivers.

Before Linux v5.7, you indeed would have used kallsyms_lookup_name() to look-up non-exported kernel symbols from a module. See How do I access any kernel symbol in a kernel module? if you want to know how.

However, the symbol stopped being exported in v5.7 because nobody was using it outside of core kernel code, and it was just there to be abused by modules to find and use other non-exported symbols. Here's also a relevant LWN article on this. Nowadays there isn't really a "proper way" to work around this problem, but there a number of different "hacks" that you could consider.

The following approaches cover both kernel functions and global objects (i.e. global variables):

  1. If you are already compiling the kernel, you can add EXPORT_SYMBOL() after the definition of the symbol(s) you are interested in. This is the simplest option given you are willing to modify the kernel and build a custom one. You could also export kallsyms_lookup_name() in kernel/kallsyms.c and then use that, if you really want.

  2. You can use an unsigned long module parameter, passing the needed symbol address (taken from /proc/kallsyms) when loading the module, and then cast it to the appropriate type:

    static unsigned long addr;
    module_param_named(addr, addr, ulong, 0);
    MODULE_PARM_DESC(addr, "Address of the `foo` symbol");

    static <type_of_foo_here> *foo_ptr;
    // Examples:
    // int foo(char *) -> int (*foo_ptr)(char *)
    // unsigned long foo -> unsigned long *foo_ptr

    static int __init mymodule_init(void)
    {
    foo_ptr = (typeof(foo_ptr))addr;
    // ...
    return 0;
    }

    Then you'd be able to do something like this:

    sudo insmod mymodule.ko addr=0x$(sudo grep ' some_symbol_name' /proc/kallsyms | cut -d' ' -f1)
  3. If your kernel supports kprobes, you can [ab]use a kprobe to make the kernel lookup a symbol for you through kprobe_register(). This approach is detailed in this other answer. Due to the intended usage of kprobes, this will only work for functions, however you can simply find kallsyms_lookup_name() first, and then use that to lookup any other symbol.

    In order for this to work, your kernel needs to be configured with CONFIG_KPROBES=y as well as CONFIG_KALLSYMS=y (and possibly also CONFIG_KALLSYMS_ALL=y), since register_kprobe() uses exactly kallsyms_lookup_name() under the hood. Automatic symbol address resolution for kprobes has been supported since Linux v2.6.16.

  4. For functions only, you can also consider re-implementing the functionality in your module. For example, task_statm() implemented in fs/proc/task_mmu.c is a rather small function that only uses other exported functions, so "borrowing" it for use in your module would be rather straightforward.

    Chances are that you want to call some non-exported function for a more specific purpose than what it was designed for. In such case, a good idea would be to look at the kernel source to understand how it works, and only re-implement the bare minimum needed for your module.

  5. Finally, you could technically open and read /proc/kallsyms from kernel space using filp_open() + kernel_read() from <linux/fs.h>, though this would probably be the objectively worst solution overall.



Related Topics



Leave a reply



Submit