Re: Making module versions (Was: OFFTOPIC: binary modules, bad

Roderich Schupp (rsch@ExperTeam.de)
Mon, 22 Dec 1997 01:29:16 +0100


Just for the record, the following is my understanding of how the current
module version scheme works. Feel free to correct any mistakes. If this
is already covered elsewhere in sufficient detail I'd appreciate a pointer.
This is rather longish so you might want to skip to the end for my
actual suggestion wrt module versioning.

(1) Every kernel object foo.o that exports a function bar must include a
line
EXPORT_SMYBOL(bar);
in foo.c. Also you must mark foo.o in the corresponding Makefile by
making it a member of LX_OBJS, OX_OBJS, MX_OBJS, or MIX_OBJS
(as is appropriate). Lets call this an exporting object.
(2) For every exporting object foo.o there's a foo.ver (placed into
directory include/linux/modules) containing lines like
#define __ver_bar 123
#define bar _set_ver(bar)
This is done by running genksyms on foo.c. "123" is a checksum supposed
to capture all "relevant" information that went into compiling foo.o.
"Relevant" means: if this information changes a call to bar might
not produce the expected result. The file include/linux/modversions.h
#includes all *.ver files.
(3) A module quux.c must #include include/linux/modversions.h.
When compiling quux.c (with module versioning on) _set_var(bar)
expands to
#define bar bar_R123
i.e. if quux.c makes a call to bar it really calls bar_R123 and hence
quux.o's symbol table contains an unresolved reference to bar_R123
(just do a 'nm -p quux.o'). On the other hand, when compiling
non-module files _set_var(bar) expands to bar.
(4) The exporting object foo.c is compiled with -DEXPORT_SYMTAB which
results in
EXPORT_SMYBOL(bar);
to expand into (cf. linux/modsetver.h, include/linux/module.h)
const char __kstrtab_bar[]
__attribute__((section(".kstrtab")))
"bar_R123";
const struct module_symbol
__ksymtab_bar __attribute__((section("__ksymtab")))
{ (unsigned long)&bar, __kstrtab_bar };
(As a side note: I can't figure out why the indirection __kstrtab_bar
is needed. I think it would suffice to have
const struct module_symbol __ksymtab_bar ...
{ (unsigned long)&bar, "bar_R123" };
(5) When the kernel image (vmlinux) is linked a standard ELF trick
gathers all __ksymtab_* entries and uses them to populate
the array __start___ksymtab. This array ends up as the `syms' field
of the special struct module called `kernel_module'.
(6) If insmod is asked to load quux.o it has to resolve all its unresolved
references against the running kernel. insmod asks the kernel (using
the system call query_module) for its "symbol table". This is an array
of entries
{ address_of_symbol, "symbol_name_Rversion" }
(You can see a human readable form of this "symbol table" by doing
a cat on /proc/ksyms.) Turns out to that "symbol table" just is the
array __start___ksymtab. insmod proceeds to look up the string "bar_R123",
fetches the actual address of in the kernel and and patches the reference
to `bar_R123' in quux.o accordingly.
(7) If the running kernel was compiled with a sufficiently
different version of function `bar', there will be no "bar_R123"
(but rather some "bar_R345"). The lookup will fail and insmod
will report a version mismatch.
(8) A very abstract way of saying the above is:
- There is a database of

exported symbol -> symbol version
- This database is also present in the running kernel.
- Every module object records the symbol version of each symbol
that it imports (as it was at the time the module was built).
- When loading a module we check the version recorded in the module
against the running kernel's database.
- If an exporting object changes we have to:
- update the records for its exported functions in the database
- update the records in all modules that import one of these functions
One of the problems with the current scheme is the implementation of these
abstract concepts which leads to excessive recompilation.
The database in the compilation environment is the union of all *.ver files
(represented by modversions.h) and the association symbol -> version
is implemented as
#define bar bar_R123
(i.e. the versioning record for `bar' in quux.o is simply the presence of
the external reference to `bar_R123'). This means the only way to
update the versioning information in a module is to recompile the module
(a fairly costly operation). Moreover, if any exporting object foo.o
changes, foo.ver will be rebuilt, and this also causes modversion.h
to be rebuilt. Since module objects like quux.o don't depend on individual
*.ver files, but rather on modversions.h as a whole this results
in recompilation of _every_ module object.
(In another posting Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>
has already explained why a simple move-if-changed for foo.ver (thus
avoiding rebuilding modversions.h) isn't really a solution to this
problem.)
OK, finally here's my SUGGESTION:

Scrap the whole
#define bar bar_R123
stuff (and let quux.o reference `bar' as `bar' even in the module case).
Instead record
{ "bar", 123 }
in a special ELF section of an augmented form of quux.o
(let's call this quux.mod). modversions.h is replaced by
modversions.o which is simply the (compiled) __ksymtab from above.
Building quux.mod is as simple as
ld -r -o quux.mod quux.o modversions.o -t some_loader_command_file
This means that every module contains the whole symbol table (not only
those symbols that it references). There is an obvious refinement
to only include what's needed (perhaps this could be achieved by ELF
magic?).
Coming to think of it, if one also does the init_module and cleanup_module
stuff correctly one might be able to use quux.mod also to link it into
the kernel unchanged (the module specific sections will simply be stripped
off when linking the kernel image).
Cheers, Roderich

--
  Do not meddle in the affairs of wizards, for they are subtle and quick
  to anger.  Do not meddle in the affairs of dragons for you are crunchy
  and taste good with ketchup.  Do not meddle in the affairs of cats, for
  they are subtle and will piss on your computer.
Roderich Schupp          mailto:rsch@ExperTeam.de
ExperTeam GmbH           http://www.experteam.de/
Munich, Germany               Linux: 2.1.72