Kernel Symbols and CONFIG_MODVERSIONS
 

Note this was written with the linux-2.4.2 source in front of me so some of this info may only apply to that version.

The text file is available here

               -------------------------------------
               Kernel Symbols and CONFIG_MODVERSIONS
               -------------------------------------
               
                       Mark McLoughlin
                        mark@skynet.ie
                       Tue Mar 13, 2001

=================
Exporting Symbols
=================

   By default, any global variables or functions defined in a module
are exported to the kernel symbol table when the module is loaded.
However, there are ways which you may control which symbols are
exported.

   If you only require that none of the module's symbols are exported
you can use the EXPORT_NO_SYMBOLS macro.

   If however, you require that only some of your global symbols
are exported you will need to use the EXPORT_SYMBOL macro to export it.
If CONFIG_MODVERSIONS is turned on a further step is required in the
build process, but that will be explained later.

======================
So How Does This Work?
======================

   A kernel module that explicitly exports symbols will have two
special sections in its object file: the symbol table '__ksymtab' and
the string table '.kstrtab'. When a symbol is exported by a module
using EXPORT_SYMBOL, two things happen:

      o a string, that is either the symbol name or, in the case that
        CONFIG_MODVERSIONS is turned on, the symbol name with some
        extra versioning info attached, is defined in the string table. 
      o a module_symbol structure is defined in the symbol table. This
        structure contains a pointer to the symbol itself and a
        pointer to the entry in the string table.

   When a module is loaded this info is added to the kernels symbol
table and these symbols are now treated like any of the kernel's
exported symbols.

   To take a peek at your module's symbol table do

$> objdump --disassemble -j __ksymtab sunrpc.o

   or the string table do

$> objdump --disassemble -j .kstrtab sunrpc.o

========================
CONFIG_MODVERSIONS et al.
========================

   CONFIG_MODVERSIONS is a notion thought up to make people's lives
easier. In essence, what it is meant to achieve is that if you have a
module you can attempt to load that module into any kernel, safe in the
knowledge that it will fail to load if any of the kernel data
structures, types or functions that the module uses have changed.

   If your kernel is not compiled with CONFIG_MODVERSIONS enabled you
will only be able to load modules that were compiled specifically for
that kernel version and that were also compiled without MODVERSIONS
enabled.

   However, if your kernel is compiled with CONFIG_MODVERSIONS enabled
you will be able to load a module that was compiled for the same
kernel version with MODVERSIONS turned off. But - here's the important
part folks - you will also be able to load any modules compiled with
MDOVERSIONS turned on, as long as the kernel API that the module uses
hasn't changed.

======================
So How Does This Work?
======================

   When CONFIG_MODVERSIONS is turned on the kernel then a special
piece of versioning info is appended to every symbol exported using
EXPORT_SYMBOL.

   This versioning info is calculated using the genksyms command whose
man page has this to say about how the info is calculated :

       When a symbol table is found in the source, the symbol will be
       expanded to its full definition, where  all  struct's,  unions,
       enums  and  typedefs will be expanded down to their basic part,
       recursively.  This final string will then be used as input to a
       CRC algorithm that will give an integer that will change as
       soon as any of the included definitions changes, for this symbol.

       The  version information in the kernel normally looks like:
       symbol_R12345678, where 12345678 is the hexadecimal
       representation of the CRC.

   What this means is that the versioning info is calculated in such a
way as that it will only change when the definition of that symbol
changes.

   The versioning string is 'appended' by the use of a #define in
linux/modversions.h for every exported symbol. The #define usually
winds up looking something like this (simplified):

       #define printk printk_R1b7d4074

   What this does is effectively get rid of the function 'printk' -
alas, poor printk - and replace it with the much more handsome
'printk_R1b7d4074'.

   If you have a look at linux/modversions.h you'll notice that it
just includes loads of .ver files. These are generated using a command
similar to 

$> gcc -E -D__GENKSYMS__ ${c-file} | genksyms -k ${kernel-ver} > ${ver-file}

   Notice that the c file if first passed through the c preprocessor
before being passed to genksyms. This is to collapse all macros and
stuff beforehand.

================================
What does this mean for modules?
================================

   When modules are being compiled for a kernel with
CONFIG_MODVERSIONS turned on, the header file linux/modversions.h must
be included at the top of every c file. This you be an awful pain to
do, so we just do it with the gcc flag '-include'.

       ifdef CONFIG_MODULES
       ifdef CONFIG_MODVERSIONS
       MODFLAGS += -DMODVERSIONS -include $(HPATH)/linux/modversions.h
       endif

   The extra MODVERSIONS flag is used to indicate that this is a
module being compiled with CONFIG_MODVERSIONS turned on as opposed to
the kernel being compiled with CONFIG_MODVERSIONS enabled.

Hosted on Skynet, powered by Linux and designed by me

Last Modified 27/Mar/2001 14:32:53