Lesson 10: The kernel symbol table, and why you should care

Printer-friendly versionPrinter-friendly version

What's all this "symbol" stuff, anyway?

By way of introduction, let's create a new module crash_syms to demonstrate how the kernel symbol table works, and how we can determine what symbols (variables, routines, etc.) we can and can't access in kernel space from our loadable modules.

Here's our new module source file:

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

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

static void __exit syms_out(void)
{
    printk(KERN_INFO "module crash_syms being unloaded.\n");
}

module_init(syms_in);
module_exit(syms_out);

MODULE_AUTHOR("Robert P. J. Day, http://crashcourse.ca");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Symbols, exported and otherwise.");

This module does little more than previous modules, except that it includes a header file that defines the jiffies variable, a counter that keeps track of the number of system ticks and is based on the frequency of the system timer. In our case, the tick rate is almost certainly 100 Hz but that's something we'll be able to verify shortly.

All we're doing here is verifying that we can write, build and load a module that can read the current value of that variable, then dump it to the log file. And if we test that module, sure enough, we see the following in the log file /var/log/messages:

...
... module crash_syms being loaded.
... Current jiffies: 4298098514.
... module crash_syms being unloaded.
...
... module crash_syms being loaded.
... Current jiffies: 4298099411.
... module crash_syms being unloaded.
...

And if we want to verify the system tick rate, we can type in the following infinite shell loop that loads and unloads the module every second, just so we can see the difference in jiffies every second:

$ while true ; do
> sudo insmod crash_syms.ko
> sudo rmmod crash_syms
> sleep 1
> done

at which point, we just let this run for a while as we tail the /var/log/messages file in real time to see (with all the extraneous output discarded):

...
Current jiffies: 4298244330.
Current jiffies: 4298244435.
Current jiffies: 4298244540.
Current jiffies: 4298244645.
Current jiffies: 4298244749.
...

And from the above, it's fairly clear that the system tick rate is 100 times per second. But that's not the point of this exercise.

What kernel symbols are available to your module?

If we look at the source for this module, it should be obvious that we needed access to two symbols in the kernel symbol space:

  • the jiffies variable, and
  • the printk() routine

which inspires the obvious question -- how did the kernel guarantee that those symbols (among many others, of course) were available and visible to your module? And the answer is -- because they were appropriately "exported."

You can think of kernel symbols (either functions or data objects) as being 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 kernel symbols so they're visible to loadable modules is typically done with one of:

  • EXPORT_SYMBOL(), which exports a given symbol to all loadable modules, or
  • EXPORT_SYMBOL_GPL(), which exports a given symbol to only those modules that have a GPL-compatible license. (The first variation is far more common.)

In other words, given that we could access those two symbols from our module, we can be fairly certain that they're being exported somewhere in the kernel source tree and, sure enough, they are:

  • 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":

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

Make sure you appreciate the significance of this sentence: "Core code can call any non-static interface in the kernel because all core source files are linked into a single base image.". That means that normal non-static, unexported symbols in kernel space are available to other routines that are built into the kernel, but are not available to loadable modules. In short, your modules are working with a more restricted kernel symbol table than other routines that are part of the kernel itself.

But wait -- there's more. (There always is.)

Exporting your own symbols

Now that you understand the concept of exporting symbols, you can use that knowledge to adjust the visibility in your code to determine what other modules might have access to in your module.

Let's create a test module with variables with different levels of visibility -- call it crash_syms2.c:

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

static int crash_syms2_static;
int        crash_syms2_external;
int        crash_syms2_exported;

EXPORT_SYMBOL(crash_syms2_exported);

static int __init syms_in(void)
{
    printk(KERN_INFO "module crash_syms2 being loaded.\n");
    return 0;
}

static void __exit syms_out(void)
{
    printk(KERN_INFO "module crash_syms2 being unloaded.\n");
}

module_init(syms_in);
module_exit(syms_out);

MODULE_AUTHOR("Robert P. J. Day, http://crashcourse.ca");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Exporting some of our own symbols.");

As you can see, we've created three different integer variables with allegedly different levels of visibility. And once we compile that module, if you're familiar with the nm utility, you can examine the module file to check its symbol table and see the visibility of various symbols (don't worry if you don't understand this output):

$ nm crash_syms2.ko
0000000000000000 r ____versions
0000000010382b3a A __crc_crash_syms2_exported
0000000000000000 r __kcrctab_crash_syms2_exported
0000000000000000 r __kstrtab_crash_syms2_exported
0000000000000000 r __ksymtab_crash_syms2_exported
0000000000000040 r __mod_author25
0000000000000000 r __mod_description27
000000000000002f r __mod_license26
0000000000000080 r __mod_srcversion31
00000000000000c0 r __mod_vermagic5
00000000000000a3 r __module_depends
0000000000000000 D __this_module
0000000000000000 T cleanup_module
0000000000000004 B crash_syms2_exported
0000000000000000 B crash_syms2_external
0000000000000000 T init_module
                 U mcount
                 U printk
0000000000000000 t syms_in
0000000000000000 t syms_out
$

Without getting into frightening detail, it should be obvious from the above that, in terms of the module's symbol table:

  • crash_syms2_static is nowhere to be seen,
  • crash_syms2_external clearly has some of visibility, and
  • crash_syms2_exported has far more visibility

But the obviousness of the above shows after you load the module and scan through the /proc/kallsyms file:

$ grep crash_syms2 /proc/kallsyms
ffffffffa0692000 t syms_out	[crash_syms2]
ffffffffa0692090 r __ksymtab_crash_syms2_exported	[crash_syms2]
ffffffffa06920a8 r __kstrtab_crash_syms2_exported	[crash_syms2]
ffffffffa06920a0 r __kcrctab_crash_syms2_exported	[crash_syms2]
ffffffffa06920c0 d __this_module	[crash_syms2]
ffffffffa0692000 t cleanup_module	[crash_syms2]
ffffffffa0692310 b crash_syms2_external	[crash_syms2]
$

Again, without getting into the gory details of that output, it should be obvious that your three variables have three different levels of visibility with respect to other loadable modules.

And in conclusion ...

All of the above was just a long-winded way of saying that for a loadable module to have access to a kernel symbol, that symbol must be exported in some way.

Exercise for the student: By far, the most common export directive is EXPORT_SYMBOL. If you know how to use grep, scan the kernel source tree to see how many times someone is very carefully exporting a symbol only to GPL-compatible modules with EXPORT_SYMBOL_GPL.

Comments

EXPORT_SYMBOL_GPL count

I count 5342 times as of .35 release.
Almost 2700 of them in ./drivers

thanks so far, it's fun!

well some thing went wrong :(


#include
#include
#include
#include
static int __init syms_in (void)
{
printk(KERN_INFO "module crash_syms being loaded\n");
printk("Current jiffies: %lu \n",jiffies);
return 0;
}
static void __exit syms_out(void)
{
printk(KERNE_INFO "module crash_syms being unloaded\n");
}
module_init(syms_in);
module_init(syms_out);

MODULE_AUTHOR("HA HA ");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Symbols exported otherwise");

and the Makefile

ifeq ($(KERNRELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
.PHONY: build clean
build:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c
rm -rf modules.order Module.symvers
else
$(info Building with KERNELRELEASE =${KERNELRELEASE})
obj-m := crash5.o

endif

When I did make no errors at all
but did not had any module such as *.ko ?
What went wrong here?

Solved:

Ok was able to solve all
Mistakes
1) used KERNE_INFO instead correct thing is KERN_INFO
2) module_init repeated 2 times no module_exit
3) In Makefile variable KERNELRELEASE is written as KERNRELRELEASE

doubt 1

I am not able to understand the following sentence above
"Core code can call any non-static interface in the kernel because all core source files are linked into a single base image."

Cannot find symbols in /proc/kallsyms

Hi,
I compiled the sample and i cannot find the symbols in the /proc/kallsyms.

The "nm" looks ok , but the variables are not in the /proc/kallsyms

If it matters - I'm using a customized kernel (2.6.37 , x86_64)...

Of course, it's no big-deal, but it would be nice to know why it's not working for me

tnx

Solved

In my customized kernel, the parameter "CONFIG_KALLSYMS_ALL" wasn't defined.
when i added "CONFIG_KALLSYMS_ALL=y" i was able to see all the symbols.

Hope it helps someone...

crash_syms2_exported: Wonder what I'm missing ...

I did all the above with success.

"Why not try using crash_syms2_exported in my original crash_syms module?" I thought.

Easy enough. First, mirror the jiffies declaration:

extern unsigned long volatile __jiffy_data crash_syms2_exported;

Then, later, use it:

printk("crash_syms: Imported jiffies: %lu.\n", crash_syms2_exported);

However, when I try building that, here's what I see:

WARNING: "crash_syms2_exported" [/home/jsh/Crashcourse/crash_syms/crash_syms.ko] undefined!

(A subsequent load fails, even if I've loaded crash_syms2 first and redefined/redeclared crash_syms2_exported in it appropriately.)

Ideas?

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <p> <br> <pre> <h1> <h2> <h3> <h4>
  • Lines and paragraphs break automatically.

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.