From patchwork Sat Nov 7 21:03:56 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Jenkins X-Patchwork-Id: 58377 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id nA7L6FtW019420 for ; Sat, 7 Nov 2009 21:06:16 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751746AbZKGVEY (ORCPT ); Sat, 7 Nov 2009 16:04:24 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1753000AbZKGVEY (ORCPT ); Sat, 7 Nov 2009 16:04:24 -0500 Received: from mxout-08.mxes.net ([216.86.168.183]:39924 "EHLO mxout-08.mxes.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751746AbZKGVES (ORCPT ); Sat, 7 Nov 2009 16:04:18 -0500 Received: from localhost.localdomain (unknown [86.53.68.233]) by smtp.mxes.net (Postfix) with ESMTPA id AC273509DD; Sat, 7 Nov 2009 16:04:21 -0500 (EST) From: Alan Jenkins To: rusty@rustcorp.com.au Cc: linux-kernel@vger.kernel.org, linux-kbuild@vger.kernel.org, Alan Jenkins , Sam Ravnborg Subject: [PATCH 05/10] kbuild: sort the list of symbols exported by the kernel (__ksymtab) Date: Sat, 7 Nov 2009 21:03:56 +0000 Message-Id: <1257627841-15817-5-git-send-email-alan-jenkins@tuffmail.co.uk> X-Mailer: git-send-email 1.6.3.3 In-Reply-To: <4AF5DF9F.5020208@tuffmail.co.uk> References: <4AF5DF9F.5020208@tuffmail.co.uk> Sender: linux-kbuild-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kbuild@vger.kernel.org diff --git a/Makefile b/Makefile index 00444a8..a2deddf 100644 --- a/Makefile +++ b/Makefile @@ -721,6 +721,8 @@ libs-y := $(libs-y1) $(libs-y2) # +--< $(vmlinux-main) # | +--< driver/built-in.o mm/built-in.o + more # | +# +-< .tmp_exports-asm.o (see comments regarding modpost of vmlinux.o) +# | # +-< kallsyms.o (see description in CONFIG_KALLSYMS section) # # vmlinux version (uname -v) cannot be updated during normal @@ -784,7 +786,6 @@ define rule_vmlinux__ $(verify_kallsyms) endef - ifdef CONFIG_KALLSYMS # Generate section listing all symbols and add it into vmlinux $(kallsyms.o) # It's a three stage process: @@ -844,13 +845,13 @@ quiet_cmd_kallsyms = KSYM $@ $(call cmd,kallsyms) # .tmp_vmlinux1 must be complete except kallsyms, so update vmlinux version -.tmp_vmlinux1: $(vmlinux-lds) $(vmlinux-all) FORCE +.tmp_vmlinux1: $(vmlinux-lds) $(vmlinux-all) .tmp_exports-asm.o FORCE $(call if_changed_rule,ksym_ld) -.tmp_vmlinux2: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms1.o FORCE +.tmp_vmlinux2: $(vmlinux-lds) $(vmlinux-all) .tmp_exports-asm.o .tmp_kallsyms1.o FORCE $(call if_changed,vmlinux__) -.tmp_vmlinux3: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms2.o FORCE +.tmp_vmlinux3: $(vmlinux-lds) $(vmlinux-all) .tmp_exports-asm.o .tmp_kallsyms2.o FORCE $(call if_changed,vmlinux__) # Needs to visit scripts/ before $(KALLSYMS) can be used. @@ -881,8 +882,18 @@ define rule_vmlinux-modpost $(Q)echo 'cmd_$@ := $(cmd_vmlinux-modpost)' > $(dot-target).cmd endef +# The modpost of vmlinux.o above creates .tmp_exports-asm.S, a list of exported +# symbols sorted by name. This list is linked into vmlinux to replace the +# original unsorted exports. It allows symbols to be resolved efficiently +# when loading modules. +.tmp_exports-asm.S: vmlinux.o + +ifneq ($(CONFIG_SYMBOL_PREFIX),) +export AFLAGS_.tmp_exports-asm.o += -DSYMBOL_PREFIX=$(patsubst "%",%,$(CONFIG_SYMBOL_PREFIX)) +endif + # vmlinux image - including updated kernel symbols -vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE +vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o .tmp_exports-asm.o $(kallsyms.o) FORCE ifdef CONFIG_HEADERS_CHECK $(Q)$(MAKE) -f $(srctree)/Makefile headers_check endif @@ -1232,7 +1243,8 @@ endif # CONFIG_MODULES # Directories & files removed with 'make clean' CLEAN_DIRS += $(MODVERDIR) CLEAN_FILES += vmlinux System.map \ - .tmp_kallsyms* .tmp_version .tmp_vmlinux* .tmp_System.map + .tmp_kallsyms* .tmp_version .tmp_vmlinux* .tmp_System.map \ + .tmp_exports-asm* # Directories & files removed with 'make mrproper' MRPROPER_DIRS += include/config include2 usr/include include/generated diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 67e6520..ea14ede 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -258,76 +258,76 @@ /* Kernel symbol table: Normal symbols */ \ __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab) = .; \ - *(__ksymtab) \ + *(__ksymtab_sorted) \ VMLINUX_SYMBOL(__stop___ksymtab) = .; \ } \ \ /* Kernel symbol table: GPL-only symbols */ \ __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab_gpl) = .; \ - *(__ksymtab_gpl) \ + *(__ksymtab_gpl_sorted) \ VMLINUX_SYMBOL(__stop___ksymtab_gpl) = .; \ } \ \ /* Kernel symbol table: Normal unused symbols */ \ __ksymtab_unused : AT(ADDR(__ksymtab_unused) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab_unused) = .; \ - *(__ksymtab_unused) \ + *(__ksymtab_unused_sorted) \ VMLINUX_SYMBOL(__stop___ksymtab_unused) = .; \ } \ \ /* Kernel symbol table: GPL-only unused symbols */ \ __ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab_unused_gpl) = .; \ - *(__ksymtab_unused_gpl) \ + *(__ksymtab_unused_gpl_sorted) \ VMLINUX_SYMBOL(__stop___ksymtab_unused_gpl) = .; \ } \ \ /* Kernel symbol table: GPL-future-only symbols */ \ __ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab_gpl_future) = .; \ - *(__ksymtab_gpl_future) \ + *(__ksymtab_gpl_future_sorted) \ VMLINUX_SYMBOL(__stop___ksymtab_gpl_future) = .; \ } \ \ /* Kernel symbol table: Normal symbols */ \ __kcrctab : AT(ADDR(__kcrctab) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___kcrctab) = .; \ - *(__kcrctab) \ + *(__kcrctab_sorted) \ VMLINUX_SYMBOL(__stop___kcrctab) = .; \ } \ \ /* Kernel symbol table: GPL-only symbols */ \ __kcrctab_gpl : AT(ADDR(__kcrctab_gpl) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___kcrctab_gpl) = .; \ - *(__kcrctab_gpl) \ + *(__kcrctab_gpl_sorted) \ VMLINUX_SYMBOL(__stop___kcrctab_gpl) = .; \ } \ \ /* Kernel symbol table: Normal unused symbols */ \ __kcrctab_unused : AT(ADDR(__kcrctab_unused) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___kcrctab_unused) = .; \ - *(__kcrctab_unused) \ + *(__kcrctab_unused_sorted) \ VMLINUX_SYMBOL(__stop___kcrctab_unused) = .; \ } \ \ /* Kernel symbol table: GPL-only unused symbols */ \ __kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___kcrctab_unused_gpl) = .; \ - *(__kcrctab_unused_gpl) \ + *(__kcrctab_unused_gpl_sorted) \ VMLINUX_SYMBOL(__stop___kcrctab_unused_gpl) = .; \ } \ \ /* Kernel symbol table: GPL-future-only symbols */ \ __kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___kcrctab_gpl_future) = .; \ - *(__kcrctab_gpl_future) \ + *(__kcrctab_gpl_future_sorted) \ VMLINUX_SYMBOL(__stop___kcrctab_gpl_future) = .; \ } \ \ /* Kernel symbol table: strings */ \ __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \ - *(__ksymtab_strings) \ + *(__ksymtab_strings_sorted) \ } \ \ /* __*init sections */ \ @@ -643,6 +643,23 @@ EXIT_DATA \ EXIT_CALL \ *(.discard) \ + \ + /* \ + * Discard the original unsorted symbol tables. \ + * In vmlinux they are replaced by sorted versions \ + * generated by modpost -x. \ + */ \ + *(__ksymtab) \ + *(__ksymtab_gpl) \ + *(__ksymtab_unused) \ + *(__ksymtab_unused_gpl) \ + *(__ksymtab_gpl_future) \ + *(__kcrctab) \ + *(__kcrctab_gpl) \ + *(__kcrctab_unused) \ + *(__kcrctab_unused_gpl) \ + *(__kcrctab_gpl_future) \ + *(__ksymtab_strings) \ } /** diff --git a/include/linux/mod_export.h b/include/linux/mod_export.h index 9f38816..394795e 100644 --- a/include/linux/mod_export.h +++ b/include/linux/mod_export.h @@ -1,8 +1,20 @@ #ifndef LINUX_MOD_EXPORT_H #define LINUX_MOD_EXPORT_H +/* + * Define EXPORT_SYMBOL() and friends for kernel modules. + * + * Under __GENKSYMS__ these definitions are skipped, making it possible to + * scan for EXPORT_SYMBOL() in preprocessed C files. + * + * Under __MODPOST_EXPORTS__ we skip C definitions and define __EXPORT_SYMBOL() + * in arch-independent assembly code. This makes it possible to construct + * sorted symbol tables. + */ + +#ifndef __GENKSYMS__ +#ifndef __MODPOST_EXPORTS__ #include -#include /* Some toolchains use a `_' prefix for all user symbols. */ #ifdef CONFIG_SYMBOL_PREFIX @@ -11,13 +23,13 @@ #define MODULE_SYMBOL_PREFIX "" #endif +#ifdef CONFIG_MODULES + struct kernel_symbol { unsigned long value; const char *name; }; -#ifdef CONFIG_MODULES -#ifndef __GENKSYMS__ #ifdef CONFIG_MODVERSIONS /* Mark the CRC weak since genksyms apparently decides not to * generate a checksums for some symbols */ @@ -60,8 +72,6 @@ struct kernel_symbol { #define EXPORT_UNUSED_SYMBOL_GPL(sym) #endif -#endif /* __GENKSYMS__ */ - #else /* !CONFIG_MODULES */ #define EXPORT_SYMBOL(sym) @@ -72,4 +82,68 @@ struct kernel_symbol { #endif /* CONFIG_MODULES */ +#else /* __MODPOST_EXPORTS__ */ +/* + * Here is the arch-independent assembly version, used in .tmp_exports-asm.S. + * + * We use CPP macros since they are more familiar than assembly macros. + * Note that CPP macros eat newlines, so each pseudo-instruction must be + * terminated by a semicolon. + */ + +#include +#include + +#if BITS_PER_LONG == 64 +#define PTR .quad +#define ALGN .balign 8 +#else +#define PTR .long +#define ALGN .balign 4 +#endif + +/* build system gives us an unstringified version of CONFIG_SYMBOL_PREFIX */ +#ifndef SYMBOL_PREFIX +#define SYM(sym) sym +#else +#define PASTE2(x,y) x##y +#define PASTE(x,y) PASTE2(x,y) +#define SYM(sym) PASTE(SYMBOL_PREFIX, sym) +#endif + + +#ifdef CONFIG_MODVERSIONS +#define __CRC_SYMBOL(sym, crcsec) \ + .globl SYM(__crc_##sym); \ + .weak SYM(__crc_##sym); \ + .pushsection crcsec, "a"; \ + ALGN; \ + SYM(__kcrctab_##sym): \ + PTR SYM(__crc_##sym); \ + .popsection; +#else +#define __CRC_SYMBOL(sym, section) +#endif + +#define __EXPORT_SYMBOL(sym, sec, strsec, crcsec) \ + .globl SYM(sym); \ + \ + __CRC_SYMBOL(sym, crcsec) \ + \ + .pushsection strsec, "a"; \ + SYM(__kstrtab_##sym): \ + .asciz __stringify(SYM(sym)); \ + .popsection; \ + \ + .pushsection sec, "a"; \ + ALGN; \ + SYM(__ksymtab_##sym): \ + PTR SYM(sym); \ + PTR SYM(__kstrtab_##sym); \ + .popsection; + +#endif /* __MODPOST_EXPORTS__ */ + +#endif /* __GENKSYMS__ */ + #endif /* LINUX_MOD_EXPORT_H */ diff --git a/init/Kconfig b/init/Kconfig index c7bac39..bb4051c 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1100,6 +1100,11 @@ config BASE_SMALL default 0 if BASE_FULL default 1 if !BASE_FULL +config HAVE_SYMBOL_PREFIX + bool + help + Some arch toolchains use a `_' prefix for all user symbols. + menuconfig MODULES bool "Enable loadable module support" help diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 8f14c81..876a3c7 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -91,7 +91,7 @@ __modpost: $(modules:.ko=.o) FORCE $(call cmd,modpost) $(wildcard vmlinux) $(filter-out FORCE,$^) quiet_cmd_kernel-mod = MODPOST $@ - cmd_kernel-mod = $(modpost) $@ + cmd_kernel-mod = $(modpost) -x .tmp_exports-asm.S $@ vmlinux.o: FORCE $(call cmd,kernel-mod) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index fb0f9b7..b5a801d 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -163,6 +163,7 @@ struct symbol { }; static struct symbol *symbolhash[SYMBOL_HASH_SIZE]; +unsigned int symbolcount; /* This is based on the hash agorithm from gdbm, via tdb */ static inline unsigned int tdb_hash(const char *name) @@ -200,6 +201,7 @@ static struct symbol *new_symbol(const char *name, struct module *module, unsigned int hash; struct symbol *new; + symbolcount++; hash = tdb_hash(name) % SYMBOL_HASH_SIZE; new = symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]); new->module = module; @@ -1985,6 +1987,58 @@ static void write_dump(const char *fname) write_if_changed(&buf, fname); } +static const char *section_names[] = { + [export_plain] = "", + [export_unused] = "_unused", + [export_gpl] = "_gpl", + [export_unused_gpl] = "_unused_gpl", + [export_gpl_future] = "_gpl_future", +}; + +static int compare_symbol_names(const void *a, const void *b) +{ + struct symbol *const *syma = a; + struct symbol *const *symb = b; + + return strcmp((*syma)->name, (*symb)->name); +} + +/* sort exported symbols and output using arch-independent assembly macros */ +static void write_exports(const char *fname) +{ + struct buffer buf = { }; + struct symbol *sym, **symbols; + int i, n; + + symbols = NOFAIL(malloc(sizeof(struct symbol *) * symbolcount)); + n = 0; + + for (i = 0; i < SYMBOL_HASH_SIZE; i++) { + for (sym = symbolhash[i]; sym; sym = sym->next) + symbols[n++] = sym; + } + + qsort(symbols, n, sizeof(struct symbol *), compare_symbol_names); + + buf_printf(&buf, "#define __MODPOST_EXPORTS__\n"); + buf_printf(&buf, "#include \n"); + buf_printf(&buf, "\n"); + + for (i = 0; i < n; i++) { + sym = symbols[i]; + + buf_printf(&buf, "__EXPORT_SYMBOL(%s," + " __ksymtab%s_sorted," + " __ksymtab_strings_sorted," + " __kcrctab%s_sorted)\n", + sym->name, + section_names[sym->export], + section_names[sym->export]); + } + + write_if_changed(&buf, fname); +} + static void add_marker(struct module *mod, const char *name, const char *fmt) { char *line = NULL; @@ -2086,6 +2140,7 @@ int main(int argc, char **argv) struct buffer buf = { }; char *kernel_read = NULL, *module_read = NULL; char *dump_write = NULL; + char *exports_write = NULL; char *markers_read = NULL; char *markers_write = NULL; int opt; @@ -2093,7 +2148,7 @@ int main(int argc, char **argv) struct ext_sym_list *extsym_iter; struct ext_sym_list *extsym_start = NULL; - while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:")) != -1) { + while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:x:")) != -1) { switch (opt) { case 'i': kernel_read = optarg; @@ -2131,6 +2186,9 @@ int main(int argc, char **argv) case 'w': warn_unresolved = 1; break; + case 'x': + exports_write = optarg; + break; case 'M': markers_write = optarg; break; @@ -2191,6 +2249,9 @@ int main(int argc, char **argv) "'make CONFIG_DEBUG_SECTION_MISMATCH=y'\n", sec_mismatch_count); + if (exports_write) + write_exports(exports_write); + if (markers_read) read_markers(markers_read);