[PATCH] Module rewrite 13/20: Module versions

Rusty Russell (rusty@rustcorp.com.au)
Wed, 25 Sep 2002 13:03:04 +1000


[ This does not delete the old CONFIG_MODVERSIONS code: I'll leave
that to Kai's pleasure, in return for fixing remaining makefile issues.
You'll note that the export-objs lines in Makefiles should no longer
be neccessary now. ]

Name: modversions support
Author: Rusty Russell
Status: Tested on 2.5.38
Depends: Module/param-oldstyle.patch.gz
Depends: Misc/2-5-38-fixes.patch.gz

D: This implementes CONFIG_MODVERSIONING (similar to the old
D: CONFIG_MODVERSIONS, but with a separate name for the moment so the
D: two can be clearly separated).
D:
D: The makefiles do not quite work correctly: you have to build using:
D: make kernel/modversions.o bzImage
D: make modules
D:
D: The idea is that modules which want to export symbols place their
D: full path name in the .needmodversion section. Just before the kernel
D: is linked, these names are extracted, and genksyms scans those files to
D: create a version table. This table is then linked into the kernel.
D:
D: For modules, after they have been built, another pass adds a
D: .versions section to each module containing the version of each
D: undefined symbol in it.
D:
D: These symbol versions are then checked on insmod. This means that
D: you can insert an unversioned module into a versioned kernel, and
D: vice versa (which taints your kernel).
D:
D: Also, the offensive export-objs defines in Makefiles are now unneccessary.

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .3225-linux-2.5.38/Makefile .3225-linux-2.5.38.updated/Makefile
--- .3225-linux-2.5.38/Makefile 2002-09-25 07:34:07.000000000 +1000
+++ .3225-linux-2.5.38.updated/Makefile 2002-09-25 07:34:34.000000000 +1000
@@ -296,6 +296,7 @@ cmd_link_vmlinux = $(LD) $(LDFLAGS) $(LD
$(LIBS) \
$(DRIVERS) \
$(NETWORKS) \
+ $(kernel_modversions) \
--end-group \
-o vmlinux

@@ -314,7 +315,7 @@ endef

LDFLAGS_vmlinux += -T arch/$(ARCH)/vmlinux.lds.s

-vmlinux: $(vmlinux-objs) arch/$(ARCH)/vmlinux.lds.s FORCE
+vmlinux: $(vmlinux-objs) $(kernel_modversions) arch/$(ARCH)/vmlinux.lds.s FORCE
$(call if_changed_rule,link_vmlinux)

# The actual objects are generated when descending,
@@ -424,8 +425,30 @@ depend dep: .hdepend
@$(MAKE) include/linux/modversions.h
@touch $@

-ifdef CONFIG_MODVERSIONS
+ifdef CONFIG_MODVERSIONING
+# Module versions for the kernel:
+kernel_modversions = kernel/modversions.o

+quiet_cmd_get_modversions = MODVER $@
+cmd_get_modversions = $(CONFIG_SHELL) scripts/extract_versions $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) $(vmlinux-objs) >$@
+
+include/linux/generated-modversions.h: $(vmlinux-objs) scripts/extract_versions
+ $(call cmd,get_modversions)
+
+kernel/modversions.o: kernel/modversions.c include/linux/generated-modversions.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+# FIXME: We don't version symbols exported by modules --RR
+module_modversions = add_modversions
+
+add_modversions: include/linux/generated-modversions.h $(patsubst %, _addmodver_%, $(SUBDIRS))
+
+$(patsubst %,_addmodver_%,$(SUBDIRS)): FORCE
+ @$(MAKE) -C $(patsubst _addmodver_%, %, $@) add_modversions
+
+endif
+
+ifdef CONFIG_MODVERSIONS
# Update modversions.h, but only if it would change.

include/linux/modversions.h: scripts/fixdep prepare FORCE
@@ -465,7 +488,7 @@ MODFLAGS += -include $(objtree)/include/
endif

.PHONY: modules
-modules: $(SUBDIRS)
+modules: $(SUBDIRS) $(module_modversions)

# Install modules

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .3225-linux-2.5.38/Rules.make .3225-linux-2.5.38.updated/Rules.make
--- .3225-linux-2.5.38/Rules.make 2002-09-25 07:34:07.000000000 +1000
+++ .3225-linux-2.5.38.updated/Rules.make 2002-09-25 07:34:34.000000000 +1000
@@ -44,6 +44,13 @@ endif
obj := .
src := .

+# We insert an extra symbol for modversions on SMP
+ifdef CONFIG_SMP
+modversions_extra = { "__SMP__", 0x01 },
+else
+modversions_extra = { "__SMP__", 0x00 },
+endif
+
# For use in the quiet output

echo_target = $(RELDIR)/$@
@@ -130,7 +137,7 @@ basename_flags = -DKBUILD_BASENAME=$(sub
modname_flags = -DKBUILD_MODNAME=$(subst $(comma),_,$(subst -,_,$(modname)))
c_flags = -Wp,-MD,$(depfile) $(CFLAGS) $(NOSTDINC_FLAGS) \
$(modkern_cflags) $(EXTRA_CFLAGS) $(CFLAGS_$(*F).o) \
- $(basename_flags) $(modname_flags) $(export_flags)
+ $(basename_flags) $(modname_flags) $(export_flags) -D__DIRNAME__=\"$(CURDIR)\"

# Finds the multi-part object the current object will be linked into
modname-multi = $(subst $(space),_,$(strip $(foreach m,$(multi-used),\
@@ -271,6 +278,12 @@ first_rule: $(if $(KBUILD_BUILTIN),$(O_T
sub_dirs
@echo -n

+# Module versioning
+add_modversions: sub_dirs FORCE
+ @RELDIR=$(RELDIR) $(CONFIG_SHELL) -e $(TOPDIR)/scripts/add_versions '$(modversions_extra)' $(obj-m)
+
+
+
# Compile C sources (.c)
# ---------------------------------------------------------------------------

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .3225-linux-2.5.38/include/linux/module.h .3225-linux-2.5.38.updated/include/linux/module.h
--- .3225-linux-2.5.38/include/linux/module.h 2002-09-25 07:34:11.000000000 +1000
+++ .3225-linux-2.5.38.updated/include/linux/module.h 2002-09-25 07:34:51.000000000 +1000
@@ -34,6 +34,11 @@ struct kernel_symbol
char name[MODULE_NAME_LEN];
};

+struct modversion_info {
+ char symbol[MODULE_NAME_LEN];
+ unsigned long checksum;
+};
+
#ifdef MODULE
/* This is magically filled in by the linker, but THIS_MODULE must be
a constant so it works in initializers. */
@@ -47,13 +52,28 @@ extern struct module __this_module;
/* Get/put a kernel symbol (calls should be symmetric) */
#define symbol_get(x) ((typeof(&x))(__get_symbol(#x)))

+#ifdef CONFIG_MODVERSIONING
+#define NEED_MODVERSION(sym) \
+ const char __needver_##sym[256] \
+ __attribute__((section(".needmodversion"))) \
+ = { __DIRNAME__ "/" __FILE__ };
+
+extern struct modversion_info modversions[];
+#else /* !CONFIG_MODVERSIONING */
+#define NEED_MODVERSION(sym)
+#endif
+
/* For every exported symbol, place a struct in the __ksymtab section */
-#define EXPORT_SYMBOL(sym) \
+#define EXPORT_SYMBOL_NOVERS(sym) \
const struct kernel_symbol __ksymtab_##sym \
__attribute__((section("__ksymtab"))) \
= { (unsigned long)&sym, #sym }

-#define EXPORT_SYMBOL_NOVERS(sym) EXPORT_SYMBOL(sym)
+#define EXPORT_SYMBOL(sym) \
+ NEED_MODVERSION(sym) \
+ EXPORT_SYMBOL_NOVERS(sym)
+
+/* FIXME: Enforce this. */
#define EXPORT_SYMBOL_GPL(sym) EXPORT_SYMBOL(sym)

struct kernel_symbol_group
@@ -285,4 +305,12 @@ extern int module_dummy_usage;
(((m)->module_init \
&& __mod_between((p),(n),(m)->module_init,(m)->init_size)) \
|| __mod_between((p),(n),(m)->module_core,(m)->core_size))
+
+#ifdef __GENKSYMS__
+/* We want the EXPORT_SYMBOL tag left intact for recognition. */
+#undef EXPORT_SYMBOL
+#undef EXPORT_SYMBOL_NOVERS
+#undef EXPORT_SYMBOL_GPL
+#endif /* __GENKSYMS__ */
+
#endif /* _LINUX_MODULE_H */
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .3225-linux-2.5.38/init/Config.in .3225-linux-2.5.38.updated/init/Config.in
--- .3225-linux-2.5.38/init/Config.in 2002-09-25 07:34:11.000000000 +1000
+++ .3225-linux-2.5.38.updated/init/Config.in 2002-09-25 07:34:34.000000000 +1000
@@ -15,6 +15,7 @@ mainmenu_option next_comment
comment 'Loadable module support'
bool 'Enable loadable module support' CONFIG_MODULES
dep_bool ' Kernel module loader' CONFIG_KMOD $CONFIG_MODULES
+dep_bool ' Kernel module versioning' CONFIG_MODVERSIONING $CONFIG_MODULES
dep_bool ' Module unloading' CONFIG_MODULE_UNLOAD $CONFIG_MODULES
dep_bool ' Obsolete module parameters' CONFIG_OBSOLETE_MODPARM $CONFIG_MODULES
endmenu
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .3225-linux-2.5.38/kernel/module.c .3225-linux-2.5.38.updated/kernel/module.c
--- .3225-linux-2.5.38/kernel/module.c 2002-09-25 07:34:11.000000000 +1000
+++ .3225-linux-2.5.38.updated/kernel/module.c 2002-09-25 07:34:34.000000000 +1000
@@ -459,6 +459,48 @@ static int obsolete_params(const char *n
}
#endif /* CONFIG_OBSOLETE_MODPARM */

+#ifdef CONFIG_MODVERSIONING
+static inline int check_version(const char *name,
+ const char *symname,
+ const struct modversion_info *versions,
+ unsigned int num)
+{
+ unsigned int i, k;
+
+ /* First search kernel (unversioned symbols not listed). */
+ for (k = 0; !streq(modversions[k].symbol, symname); k++)
+ if (!modversions[k].symbol[0])
+ return 0;
+
+ /* Now see if module has matching symbol. */
+ for (i = 0; i < num; i++) {
+ if (streq(versions[i].symbol, symname)) {
+ if (versions[i].checksum == modversions[k].checksum)
+ return 0;
+ printk("%s: disagrees about version of symbol %s\n",
+ name, symname);
+ DEBUGP("Kernel checksum %lX vs module %lX\n",
+ modversions[k].checksum, versions[i].checksum);
+ return -ESRCH;
+ }
+ }
+
+ /* Not in module's version table. OK, but that taints the kernel. */
+ printk("%s: no version for %s found: kernel tainted.\n",
+ name, symname);
+ tainted |= TAINT_FORCED_MODULE;
+ return 0;
+}
+#else
+static inline int check_version(const char *name,
+ const char *symname,
+ const struct modversion_info *versions,
+ unsigned int num)
+{
+ return 0;
+}
+#endif /* CONFIG_MODVERSIONING */
+
/* Find an symbol for this module (ie. resolve internals first).
It we find one, record usage. Must be holding module_mutex. */
unsigned long find_symbol_internal(Elf_Shdr *sechdrs,
@@ -644,21 +686,21 @@ static unsigned long read_commons(void *
}

/* Change all symbols so that sh_value encodes the pointer directly. */
-static void simplify_symbols(Elf_Shdr *sechdrs,
+static int simplify_symbols(Elf_Shdr *sechdrs,
unsigned int symindex,
unsigned int strindex,
+ unsigned int versindex,
void *common,
struct module *mod)
{
+ int ret;
unsigned int i;
- Elf_Sym *sym;
+ Elf_Sym *sym = (void *)sechdrs[symindex].sh_offset;

/* First simplify defined symbols, so if they become the
"answer" to undefined symbols, copying their st_value us
correct. */
- for (sym = (void *)sechdrs[symindex].sh_offset, i = 0;
- i < sechdrs[symindex].sh_size / sizeof(Elf_Sym);
- i++) {
+ for (i = 0; i < sechdrs[symindex].sh_size / sizeof(Elf_Sym); i++) {
switch (sym[i].st_shndx) {
case SHN_COMMON:
/* Value encodes alignment. */
@@ -687,27 +729,38 @@ static void simplify_symbols(Elf_Shdr *s
}

/* Now try to resolve undefined symbols */
- for (sym = (void *)sechdrs[symindex].sh_offset, i = 0;
- i < sechdrs[symindex].sh_size / sizeof(Elf_Sym);
- i++) {
- if (sym[i].st_shndx == SHN_UNDEF) {
- /* Look for symbol */
- struct kernel_symbol_group *ksg = NULL;
- const char *strtab
- = (char *)sechdrs[strindex].sh_offset;
+ for (i = 0; i < sechdrs[symindex].sh_size / sizeof(Elf_Sym); i++) {
+ struct kernel_symbol_group *ksg = NULL;
+ const char *strtab = (char *)sechdrs[strindex].sh_offset;

- sym[i].st_value
- = find_symbol_internal(sechdrs,
- symindex,
- strtab,
- strtab + sym[i].st_name,
- mod,
- &ksg);
- /* We fake up "__this_module" */
- if (strcmp(strtab+sym[i].st_name, "__this_module")==0)
- sym[i].st_value = (unsigned long)mod;
+ if (sym[i].st_shndx != SHN_UNDEF)
+ continue;
+
+ /* Look for symbol */
+ sym[i].st_value
+ = find_symbol_internal(sechdrs,
+ symindex,
+ strtab,
+ strtab + sym[i].st_name,
+ mod,
+ &ksg);
+
+ if (sym[i].st_value && ksg == &kernel_symbols) {
+ ret = check_version(mod->name,
+ strtab + sym[i].st_name,
+ (void *)sechdrs[versindex]
+ .sh_offset,
+ sechdrs[versindex].sh_size
+ / sizeof(struct modversion_info));
+ if (ret != 0)
+ return ret;
}
+
+ /* We fake up "__this_module" */
+ if (streq(strtab+sym[i].st_name, "__this_module"))
+ sym[i].st_value = (unsigned long)mod;
}
+ return 0;
}

/* Get the total allocation size of the init and non-init sections */
@@ -748,7 +801,7 @@ static struct module *load_module(void *
Elf_Shdr *sechdrs;
char *secstrings;
unsigned int i, symindex, exportindex, strindex, setupindex, exindex,
- modnameindex, obsparmindex;
+ modnameindex, obsparmindex, versindex;
long arglen;
unsigned long common_length;
struct sizes sizes, used;
@@ -786,7 +839,7 @@ static struct module *load_module(void *

/* May not export symbols, or have setup params, so these may
not exist */
- exportindex = setupindex = obsparmindex = 0;
+ exportindex = setupindex = obsparmindex = versindex = 0;

/* And these should exist, but gcc whinges if we don't init them */
symindex = strindex = exindex = modnameindex = 0;
@@ -827,6 +880,11 @@ static struct module *load_module(void *
/* Obsolete MODULE_PARM() table */
DEBUGP("Obsolete param found in section %u\n", i);
obsparmindex = i;
+ } else if (strcmp(secstrings+sechdrs[i].sh_name, ".versions")
+ == 0) {
+ /* Module versioning table. */
+ DEBUGP("Symbol versions found in section %u\n", i);
+ versindex = i;
}
#ifndef CONFIG_MODULE_UNLOAD
/* Don't load .exit sections */
@@ -913,7 +971,18 @@ static struct module *load_module(void *
BUG();

/* Fix up syms, so that st_value is a pointer to location. */
- simplify_symbols(sechdrs, symindex, strindex, mod->module_core, mod);
+ err = simplify_symbols(sechdrs, symindex, strindex, versindex,
+ mod->module_core, mod);
+ if (err < 0)
+ goto cleanup;
+
+ /* Always do SMP check for modversions. */
+ err = check_version(mod->name, "__SMP__",
+ (void *)sechdrs[versindex].sh_offset,
+ sechdrs[versindex].sh_size
+ / sizeof(struct modversion_info));
+ if (err < 0)
+ goto cleanup;

/* Set up EXPORTed symbols */
if (exportindex) {
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .3225-linux-2.5.38/kernel/modversions.c .3225-linux-2.5.38.updated/kernel/modversions.c
--- .3225-linux-2.5.38/kernel/modversions.c 1970-01-01 10:00:00.000000000 +1000
+++ .3225-linux-2.5.38.updated/kernel/modversions.c 2002-09-25 07:34:34.000000000 +1000
@@ -0,0 +1,13 @@
+/* Made very late: not referred to be normal makefiles.
+ (C) Copyright 2002 Rusty Russell, IBM Corporation.
+*/
+#include <linux/module.h>
+struct modversion_info modversions[] __attribute__((section(".versions"))) = {
+#include <linux/generated-modversions.h>
+#ifdef CONFIG_SMP
+ { "__SMP__", 1 },
+#else
+ { "__SMP__", 0 },
+#endif
+ { "", 0 }
+};
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .3225-linux-2.5.38/scripts/add_versions .3225-linux-2.5.38.updated/scripts/add_versions
--- .3225-linux-2.5.38/scripts/add_versions 1970-01-01 10:00:00.000000000 +1000
+++ .3225-linux-2.5.38.updated/scripts/add_versions 2002-09-25 07:34:34.000000000 +1000
@@ -0,0 +1,32 @@
+#! /bin/sh
+# For a given list of modules, add a .versions section containing the
+# versions it expects.
+
+set -e
+
+if [ $# -lt 1 ]; then
+ echo Usage: add_versions extraversion objfiles... >&2
+ exit 1
+fi
+
+EXTRAVERSION=$1
+shift 1
+
+for file; do
+ trap "rm -f .$file.versions.*" 0
+ $NM -u $file | sed 's/\(.*\)/"\1",/' | sort | join -1 1 -2 2 -o 2.1,1.1,2.3,2.4 - $TOPDIR/include/linux/generated-modversions.h > .$file.versions
+ if [ -s .$file.versions ]; then
+ echo "Adding versions to $RELDIR/$file"
+ echo '#include <linux/module.h>' > .$file.versions.c
+ echo 'struct modversion_info __modversions[] __attribute__((section(".versions"))) = {' >> .$file.versions.c
+ cat .$file.versions >> .$file.versions.c
+ echo "$EXTRAVERSION" >> .$file.versions.c
+ echo '};' >> .$file.versions.c
+ # Remove old .versions section, won't be required once Makefiles work.
+ $OBJCOPY --remove-section=.versions $file .$file.versions.none.o
+ # Compile and add .versions section.
+ $CC $CFLAGS -c .$file.versions.c
+ $LD $LDFLAGS -r -o $file .$file.versions.none.o .$file.versions.o
+ fi
+ rm -f .$file.versions.*
+done
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .3225-linux-2.5.38/scripts/extract_versions .3225-linux-2.5.38.updated/scripts/extract_versions
--- .3225-linux-2.5.38/scripts/extract_versions 1970-01-01 10:00:00.000000000 +1000
+++ .3225-linux-2.5.38.updated/scripts/extract_versions 2002-09-25 07:34:34.000000000 +1000
@@ -0,0 +1,23 @@
+#! /bin/sh
+# Given a list of object files, output the C structure for the version table.
+
+set -e
+
+if [ $# -lt 2 ]; then
+ echo Usage: extract_versions kernelversion objfiles... >&2
+ exit 1
+fi
+
+VERSION=$1
+shift 1
+
+# Join 16-lines into one big filename, then run each filename through genksyms.
+# Genksyms produces:
+# #define __ver_printk smp_1b7d4074
+# #define printk _set_ver(printk)
+#
+# And since it's outside the kernel source tree, we use sed on it.
+for file in `"$OBJDUMP" --section=.needmodversion --full-contents "$@" | sed -n '/^ *[0-9a-fA-Z][0-9a-fA-Z][0-9a-fA-Z][0-9a-fA-Z] /{N;N;N;N;N;N;N;N;N;N;N;N;N;N;N;s/[0-9A-Za-z ]* //g;s/\n//g;s/\.*$//gp;}' | sort -u`; do
+ echo "GENKSYMS $file" >&2
+ $CPP $CFLAGS -D__GENKSYMS__ $file | genksyms -k $VERSION | sed -n '/#define __ver_/{s///;N;s/\n.*//;s/\([a-zA-Z0-9_]*\)[^a-zA-Z0-9_]*\(.*\)/{ "\1", 0x\2 },/p;}'
+done | sort

--
  Anyone who quotes me in their sig is an idiot. -- Rusty Russell.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/