Linux kernel module symbols

Linux Kernel Symbols

Kernel symbols are names of functions and variables. Global symbols are those
which are available outside the file they are declared in. Global symbols
in the Linux kernel currently running on a system are available through
`/proc/kallsyms` file. This includes symbols defined inside kernel modules
currently loaded.

Global symbols are of two types:

  1. those explicitly exported through EXPORT_SYMBOL_GPL and EXPORT_SYMBOL
    macros, and
  2. those which are not declared with `static` C keyword and hence visible to
    code which is statically linked with the kernel itself and may be available
    outside the kernel image.

The first type, explicitly exported ones, are denoted with capital letter in
output of `cat /proc/kallsyms` – e.g. T if the symbol is in text section, i.e.
a function name. The second type are denoted with small letter – e.g. t for a
function which isn’t exported via EXPORT_SYMBOL_GPL or EXPORT_SYMBOL.

Inside kernel code, we can access symbols which are exported explicity by
simply using them like other variables, e.g. by calling printk() function.

For global symbols which aren’t explicitly exported, but are still available,
we can attempt to access them by calling kallsyms_lookup_name() function,
defined in kernel/kallsyms.c:

unsigned long kallsyms_lookup_name(const char *name);

This takes symbol name as argument and returns its address in memory, i.e. a
pointer to it. The calling code can dereference the pointer to make use of that
symbol. If the symbol isn’t found, the function returns NULL.

Источник

Linux kernel module symbols

We’ve seen how insmod resolves undefined symbols against the table of public kernel symbols. The table contains the addresses of global kernel items—functions and variables—that are needed to implement modularized drivers. When a module is loaded, any symbol exported by the module becomes part of the kernel symbol table. In the usual case, a module implements its own functionality without the need to export any symbols at all. You need to export symbols, however, whenever other modules may benefit from using them.

New modules can use symbols exported by your module, and you can stack new modules on top of other modules. Module stacking is implemented in the mainstream kernel sources as well: the msdos filesystem relies on symbols exported by the fat module, and each input USB device module stacks on the usbcore and input modules.

Module stacking is useful in complex projects. If a new abstraction is implemented in the form of a device driver, it might offer a plug for hardware-specific implementations. For example, the video-for-linux set of drivers is split into a generic module that exports symbols used by lower-level device drivers for specific hardware. According to your setup, you load the generic video module and the specific module for your installed hardware. Support for parallel ports and the wide variety of attachable devices is handled in the same way, as is the USB kernel subsystem. Stacking in the parallel port subsystem is shown in Figure 2-2; the arrows show the communications between the modules and with the kernel programming interface.

Читайте также:  Распределение дискового пространства linux

Figure 2-2. Stacking of parallel port driver modules

When using stacked modules, it is helpful to be aware of the modprobe utility. As we described earlier, modprobe functions in much the same way as insmod , but it also loads any other modules that are required by the module you want to load. Thus, one modprobe command can sometimes replace several invocations of insmod (although you’ll still need insmod when loading your own modules from the current directory, because modprobe looks only in the standard installed module directories).

Using stacking to split modules into multiple layers can help reduce development time by simplifying each layer. This is similar to the separation between mechanism and policy that we discussed in Chapter 1.

The Linux kernel header files provide a convenient way to manage the visibility of your symbols, thus reducing namespace pollution (filling the namespace with names that may conflict with those defined elsewhere in the kernel) and promoting proper information hiding. If your module needs to export symbols for other modules to use, the following macros should be used.

EXPORT_SYMBOL(name); EXPORT_SYMBOL_GPL(name);

Either of the above macros makes the given symbol available outside the module. The _GPL version makes the symbol available to GPL-licensed modules only. Symbols must be exported in the global part of the module’s file, outside of any function, because the macros expand to the declaration of a special-purpose variable that is expected to be accessible globally. This variable is stored in a special part of the module executible (an «ELF section») that is used by the kernel at load time to find the variables exported by the module. (Interested readers can look at for the details, even though the details are not needed to make things work.)

Источник

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

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  
#include
#include
#include [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.

Читайте также:  Ошибка при установке arch linux

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.

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  
#include
#include

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.
Читайте также:  Linux установка драйвера через терминал

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.

Источник

Оцените статью
Adblock
detector