summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorKai Germaschewski <kai@tp1.ruhr-uni-bochum.de>2002-06-01 15:02:57 -0500
committerKai Germaschewski <kai@tp1.ruhr-uni-bochum.de>2002-06-01 15:02:57 -0500
commitc6fd296c975ab26aaff9c5fee6888a1a6a9f784f (patch)
treef7a8864f428a95fb4db10b54da4e5999c084473b /include/linux
parent9168efd228a9fe3ae8475b2f7a773250de9d5aff (diff)
kbuild: Clarify the CONFIG_MODVERSIONS logic
Observe that defined(MODVERSIONS) == defined(CONFIG_MODVERSIONS) && defined(MODULE) and from there I step by step simplified the logic in include/linux/module.h - staying logically equivalent, but it is much more understandable now, IMO. Still added a huge comment trying to help other people understand what kind of magic happens here.
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/module.h125
1 files changed, 108 insertions, 17 deletions
diff --git a/include/linux/module.h b/include/linux/module.h
index 7dae60a448fa..9a47e8926bfd 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -11,17 +11,6 @@
#include <linux/spinlock.h>
#include <linux/list.h>
-#ifdef __GENKSYMS__
-# define _set_ver(sym) sym
-# undef MODVERSIONS
-# define MODVERSIONS
-#else /* ! __GENKSYMS__ */
-# if !defined(MODVERSIONS) && defined(EXPORT_SYMTAB)
-# define _set_ver(sym) sym
-# include <linux/modversions.h>
-# endif
-#endif /* __GENKSYMS__ */
-
#include <asm/atomic.h>
/* Don't need to bring in all of uaccess.h just for this decl. */
@@ -297,7 +286,7 @@ extern struct module __this_module;
#include <linux/version.h>
static const char __module_kernel_version[] __attribute__((section(".modinfo"))) =
"kernel_version=" UTS_RELEASE;
-#ifdef MODVERSIONS
+#ifdef CONFIG_MODVERSIONS
static const char __module_using_checksums[] __attribute__((section(".modinfo"))) =
"using_checksums=1";
#endif
@@ -345,6 +334,101 @@ extern struct module *module_list;
insmod -[xX] flags. Otherwise, only the variables listed are exported.
This obviates the need for the old register_symtab() function. */
+/* So how does the CONFIG_MODVERSIONS magic work?
+ *
+ * A module can only be loaded if it's undefined symbols can be resolved
+ * using symbols the kernel exports for that purpose. The idea behind
+ * CONFIG_MODVERSIONS is to mangle those symbols depending on their
+ * definition (see man genksyms) - a change in the definition will thus
+ * caused the mangled name to change, and the module will refuse to
+ * load due to unresolved symbols.
+ *
+ * Let's start with taking a look how things work when we don't use
+ * CONFIG_MODVERSIONS. In this case, the only thing which is worth
+ * mentioning is the EXPORT_SYMBOL() macro. Using EXPORT_SYMBOL(foo)
+ * will expand into __EXPORT_SYMBOL(foo, "foo"), which then uses
+ * some ELF section magic to generate a list of pairs
+ * (address, symbol_name), which is used to resolve undefined
+ * symbols into addresses when loading a module.
+ *
+ * That's easy. Let's get back to CONFIG_MODVERSIONS=y.
+ *
+ * The first step is to generate the checksums. This is done at
+ * "make dep" time, code which exports symbols (using EXPORT_SYMTAB)
+ * is preprocessed with the additional macro __GENKSYMS__ set and fed
+ * into genksyms.
+ * At this stage, for each file that exports symbols an corresponding
+ * file in include/linux/module is generated, which for each exported
+ * symbol contains
+ *
+ * #define __ver_schedule_task 2d6c3d04
+ * #define schedule_task _set_ver(schedule_task)
+ *
+ * In addition, include/linux/modversions.h is generated, which
+ * looks like
+ *
+ * #include <linux/modsetver.h>
+ * #include <linux/modules/kernel__context.ver>
+ * <<<lists all of the files just described>>>
+ *
+ * Let's see what happens for different cases during compilation.
+ *
+ * o compile a file into the kernel which does not export symbols:
+ *
+ * Since the file is known to not export symbols (it's not listed
+ * in the export-objs variable in the corresponding Makefile), the
+ * kernel build system does compile it with no extra flags set.
+ * The macro EXPORT_SYMTAB is unset, and you can see below that
+ * files which still try to use EXPORT_SYMBOL() will be trapped.
+ * Other than that, just regular compilation.
+ *
+ * o compile a file into the kernel which does export symbols:
+ *
+ * In this case, the file will compiled with the macro
+ * EXPORT_SYMTAB defined.
+ * As MODULE is not set, we hit this case from below:
+ *
+ * #define _set_ver(sym) sym
+ * #include <linux/modversions.h>
+ *
+ * #define EXPORT_SYMBOL(var) \
+ * __EXPORT_SYMBOL(var, __MODULE_STRING(__VERSIONED_SYMBOL(var)))
+ *
+ * The first two lines will in essence include
+ *
+ * #define __ver_schedule_task 2d6c3d04
+ * #define schedule_task schedule_task
+ *
+ * for each symbol. The second line really doesn't do much, but the
+ * first one gives us the checksums we generated before.
+ *
+ * So EXPORT_SYMBOL(schedule_task) will expand into
+ * __EXPORT_SYMBOL(schedule_task, "schedule_task_R2d6c3d04"),
+ * hence exporting the symbol for schedule_task under the name of
+ * schedule_task_R2d6c3d04.
+ *
+ * o compile a file into a module
+ *
+ * In this case, the kernel build system will add
+ * "-include include/linux/modversions.h" to the command line. So
+ * modversions.h is prepended to the actual source, turning into
+ *
+ * #define __ver_schedule_task 2d6c3d04
+ * #define schedule_task schedule_task_R2d6c3d04
+ *
+ * Though the source code says "schedule_task", the compiler will
+ * see the mangled symbol everywhere. So the module will end up with
+ * an undefined symbol "schedule_task_R2d6c3d04" - which is exactly
+ * the symbols which occurs in the kernel's list of symbols, with
+ * a value of &schedule_task - it all comes together nicely.
+ *
+ * One question remains: What happens if a module itself exports
+ * a symbol - the answer is simple: It's actually handled as the
+ * CONFIG_MODVERSIONS=n case described first, only that the compiler
+ * sees the mangled symbol everywhere. So &foo_R12345678 is exported
+ * with the name "foo_R12345678". Think about it. It all makes sense.
+ */
+
#if defined(__GENKSYMS__)
/* We want the EXPORT_SYMBOL tag left intact for recognition. */
@@ -379,13 +463,20 @@ const struct module_symbol __ksymtab_##sym \
__attribute__((section("__ksymtab"))) = \
{ (unsigned long)&sym, __kstrtab_##sym }
-#if defined(MODVERSIONS) || !defined(CONFIG_MODVERSIONS)
-#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var))
-#define EXPORT_SYMBOL_GPL(var) __EXPORT_SYMBOL_GPL(var, __MODULE_STRING(var))
-#else
+#if defined(CONFIG_MODVERSIONS) && !defined(MODULE)
+
+#define _set_ver(sym) sym
+#include <linux/modversions.h>
+
#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(__VERSIONED_SYMBOL(var)))
#define EXPORT_SYMBOL_GPL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(__VERSIONED_SYMBOL(var)))
-#endif
+
+#else /* !defined (CONFIG_MODVERSIONS) || defined(MODULE) */
+
+#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var))
+#define EXPORT_SYMBOL_GPL(var) __EXPORT_SYMBOL_GPL(var, __MODULE_STRING(var))
+
+#endif /* defined(CONFIG_MODVERSIONS) && !defined(MODULE) */
#define EXPORT_SYMBOL_NOVERS(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var))