I'm working a few linux kernel modules and have a question related to a circular loading problem.
Module A loads first and exports many symbols for module B or C's use. Module B or C then get loaded afterwards and the symbols exist for their use.
However, I now find that module A requires a symbol from either module B or C, but only during runtime, and not needed to initialize the module. So of course when A loads, it find that the symbol does not yet exist. I've even had the symbol marked as extern in module A but that didn't work either.
Is it possible to delay the loading of a symbol after module A has been loaded, though it does not yet exist till B or C has been loaded?
Exporting a symbol means "advertising" its existence in your object file/library and where it is, so that it could be imported (=linked to) by other modules.
The EXPORT_SYMBOL() mechanism allows us to export a symbol for use by loadable modules as well. An interesting thing is that a symbol thus exported by one module becomes accessible to another module that may depend on it! To summarise, extern is not kernel specific.
Such situations are often resolved using callbacks.
Suppose module A exports the functions to register/unregister the callbacks. B and/or C use these functions and provide the appropriate callbacks to A. When it is needed, A checks if the callbacks are set and calls them.
Something like this (without error handling and locking for simplicity):
/* Module A */
struct a_ops /* Better to define struct a_ops in a header file */
{
void (*needed_func)(void);
void (*another_needed_func)(void);
};
...
struct a_ops ops = {
.needed_func = NULL;
.another_needed_func = NULL;
};
...
int a_register_needed_funcs(struct a_ops *a_ops)
{
ops.needed_func = a_ops->needed_func;
ops.another_needed_func = a_ops->another_needed_func;
}
EXPORT_SYMBOL(a_register_needed_funcs);
void a_unregister_needed_funcs()
{
ops.needed_func = NULL;
ops.another_needed_func = NULL;
}
EXPORT_SYMBOL(a_unregister_needed_funcs);
...
/* Call the specified callbacks when needed: */
void do_something(void)
{
if (ops.needed_func != NULL) {
ops.needed_func();
}
else {
/* the callback is not set, handle this: report error, ignore it or
* do something else */
...
}
}
...
/* Modules B and C */
/* Their code #includes the file where struct a_ops is defined.
* The module registers the callbacks, for example, in its init function
* and unregister in exit function. */
...
static void func(void)
{
...
}
static void another_func(void)
{
...
}
struct a_ops my_funcs = {
.needed_func = func;
.another_needed_func = another_func;
};
int __init my_module_init(void)
{
...
result = a_register_needed_funcs(&my_funcs);
...
}
void __exit my_module_exit(void)
{
...
a_unregister_needed_funcs();
...
}
This is similar to file operations and many other callback operations in the kernel. Suppose a user wants to read from, say, a character device maintained by a custom driver. The kernel proper (VFS, to be exact) receives the request but cannot handle it itself. It forwards the request to that custom driver that has registered its file operation callbacks for that device. In turn, the driver uses the functions exported by the kernel proper, like cdev_add()
, etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With