The Kernel Newbie Corner: Kernel Symbols: What’s Available to Your Module, What Isn’t

13239

This week, let’s talk about what, out of all the symbols in kernel space, you’re allowed to access, and what you’re not. And while we’re at it, what symbols you can make available to other loadable modules from your code.

(The archive of all previous “Kernel Newbie Corner” articles can be found here.)

This is ongoing content from the Linux Foundation training program. If you want more content please, consider signing up for one of these classes.

Back to Basics

Let’s once again consider a fairly basic loadable module, which prints to /var/log/messages that it’s been loaded, and also prints the current value of the kernel space symbol jiffies, which is the number of kernel clock ticks in HZ since the kernel was booted (sort of). Here’s the sample module source file m1.c:

 #include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/jiffies.h> [for "jiffies" variable]

static int __init hi(void)
{
printk(KERN_INFO "module m1 being loaded.n");
printk("Current jiffies: %lu.n", jiffies);
return 0;
}

static void __exit bye(void)
{
printk(KERN_INFO "module m1 being unloaded.n");
}

module_init(hi);
module_exit(bye);

MODULE_AUTHOR("Robert P. J. Day");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Symbols, exported and otherwise.");

Run That Baby!

Given that the tick rate for my current kernel is 1000, if I load and unload that module once per second, I should expect to see the value of jiffies increasing by about 1000 each time. So let’s let the shell keep track of that by running the following at the command line until we tell it to stop:

 # while true ; do
> insmod m1.ko
> rmmod m1
> sleep 1
> done

at which point we see in /var/log/messages:

 ...
Jul 29 11:04:17 localhost kernel: module m1 being loaded.
Jul 29 11:04:17 localhost kernel: Current jiffies: 4297191451.
Jul 29 11:04:17 localhost kernel: module m1 being unloaded.
Jul 29 11:04:18 localhost kernel: module m1 being loaded.
Jul 29 11:04:18 localhost kernel: Current jiffies: 4297192470.
Jul 29 11:04:18 localhost kernel: module m1 being unloaded.
Jul 29 11:04:19 localhost kernel: module m1 being loaded.
Jul 29 11:04:19 localhost kernel: Current jiffies: 4297193488.
Jul 29 11:04:19 localhost kernel: module m1 being unloaded.
Jul 29 11:04:20 localhost kernel: module m1 being loaded.
Jul 29 11:04:20 localhost kernel: Current jiffies: 4297194508.
Jul 29 11:04:20 localhost kernel: module m1 being unloaded.
...

That looks good–the value of jiffies seems to be increasing about 1000 each time. Which brings us to our main point.

What Exactly Can Your Module Access After It’s Loaded?

As you can see, your module needed to be able to access a couple different symbols from kernel space: the routine printk(), and the variable jiffies. And why were those symbols available to your module? Because they were “exported”.

You can think of kernel symbols (either functions or data objects) as visible at three different levels in the kernel source code:

  • “static”, and therefore visible only within their own source file (just like standard user space programming),
  • “external”, and therefore potentially visible to any other code built into the kernel itself, and
  • “exported”, and therefore visible and available to any loadable module.

Exporting symbols for modules is typically done with one of:

  • EXPORT_SYMBOL(), which exports to any loadable module, or
  • EXPORT_SYMBOL_GPL(), which exports only to GPL-licensed modules.

Not surprisingly, then, since we could access those two symbols, we expect to find them exported somewhere in the source tree and, sure enough:

 kernel/printk.c:EXPORT_SYMBOL(printk);
kernel/time.c:EXPORT_SYMBOL(jiffies);

Let’s let kernel wizard Robert Love summarize this in his book, Linux Kernel Development (2nd ed.), p. 288:

When modules are loaded, they are dynamically linked into the kernel. As with userspace, dynamically linked binaries can call only into external functions that are explicitly exported for use. In the kernel, this is handled via special directives EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL().

Functions that are exported are available for use by modules. Functions that are not exported cannot be invoked by modules. The linking and invoking rules are much more stringent for modules than code in the core kernel image. Core code can call any non-static interface in the kernel because all core source files are linked into a single base image. Exported symbols, of course, must be non-static, too.

The set of kernel symbols that are exported are known as the exported kernel interfaces or even (gasp) the kernel API.

But wait–there’s more.

Exporting Symbols of Your Own

Not surprisingly, you can use the above to export symbols from your loadable modules to make them available to other modules. Consider module source file m2.c:

 #include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>

static int rday_1 = 1;
int rday_2 = 2;
int rday_3 = 3;

EXPORT_SYMBOL(rday_3);

static int __init hi(void)
{
printk(KERN_INFO "module m2 being loaded.n");
return 0;
}

static void __exit bye(void)
{
printk(KERN_INFO "module m2 being unloaded.n");
}

module_init(hi);
module_exit(bye);

MODULE_AUTHOR("Robert P. J. Day");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Let's try some exporting.");

As you can see, there are three integer variables defined, only one of which should be visible to other modules after this one is loaded. To confirm that, once you compile that module, but before you load it, you can examine its symbol table thusly:

 $ nm m2.ko
0000000000000000 r __kstrtab_rday_3
0000000000000000 r __ksymtab_rday_3
0000000000000040 r __mod_author25
0000000000000000 r __mod_description27
0000000000000028 r __mod_license26
0000000000000060 r __mod_srcversion23
00000000000000a0 r __mod_vermagic5
0000000000000088 r __module_depends
0000000000000000 D __this_module
0000000000000000 t bye
0000000000000000 T cleanup_module
0000000000000000 t hi
0000000000000000 T init_module
U mcount
U printk
0000000000000000 D rday_2
0000000000000004 D rday_3

If you’re familiar with the nm utility, you’ll see that the variable rday_1 doesn’t show up in the symbol table at all (having been declared as “static”), while both rday_2 and rday_3 are flagged as global data objects but, additionally, you can see that rday_3 has an entry in the module string table and symbol table, meaning that when this module is loaded, that symbol will be made available to other loadable modules. And it’s easy enough to verify that by loading that module, then checking the contents of the dynamic kernel symbol table available in the file /proc/kallsyms:

 $ grep rday /proc/kallsyms
ffffffffa00f6080 r __ksymtab_rday_3 [m2]
ffffffffa00f6090 r __kstrtab_rday_3 [m2]
ffffffffa00f6570 D rday_3 [m2]
ffffffffa00f656c d rday_2 [m2]

What the above excerpt from /proc/kallsyms represents should be obvious:

  • The variable rday_1 shouldn’t show up anywhere in kernel space since it was declared as static.
  • The variable rday_2 exists in kernel space, but is considered “local” in that context.
  • The variable rday_3 exists in kernel space, and is considered “global” and available to other modules since it was exported.

In other words, you should now be able to write another module that tries to read rday_3–obviously an exercise for the reader.

A Couple More Details Regarding Exporting

Besides the standard export macros defined in the kernel header file linux/module.h, there are a couple more specialized ones:

  • EXPORT_UNUSED_SYMBOL() and EXPORT_UNUSED_SYMBOL_GPL(), which are used to identify symbols that are currently being exported but which are destined for unexporting some day, so you should try to avoid using them. The phrase “UNUSED” in this context is unfortunate, as it would have made much more sense to have used the phrase “DEPRECATED”.
  • EXPORT_SYMBOL_GPL_FUTURE(), which denotes a symbol that is currently available to all modules, but is planned for restriction to GPL-only modules in the future

It’s left as an exercise for the reader to see if these macros are currently being used anywhere in the current kernel source tree, and perhaps to figure out why.

Robert P. J. Day is a Linux consultant and long-time corporate trainer who lives in Waterloo, Ontario. He provides assistance to the Linux Foundation’s Training Program. Robert can be reached at
This e-mail address is being protected from spambots. You need JavaScript enabled to view it
, and can be followed at http://twitter.com/rpjday.