Re: [PATCH 2/4] kbuild: sort the list of symbols exported by the kernel (__ksymtab)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



[CC: Sam Ravnborg as originally intended]

Alan Jenkins wrote:
> modpost of vmlinux.o now extracts the ksymtab sections and outputs
> sorted versions of them as .tmp_exports.c.  These sorted sections
> are linked into vmlinux and the original unsorted sections are
> discarded.
>
> This will allow modules to be loaded faster, resolving symbols using
> binary search, without any increase in the memory needed for the
> symbol tables.
>
> This does not affect the building of modules, so hopefully it won't
> affect compile times too much.
>
> Signed-off-by: Alan Jenkins <alan-jenkins@xxxxxxxxxxxxxx>
> ---
>  Makefile                          |   20 ++++++++---
>  include/asm-generic/vmlinux.lds.h |   40 ++++++++++++++++------
>  include/linux/mod_export.h        |   19 ++++++++--
>  scripts/Makefile.modpost          |    2 +-
>  scripts/mod/modpost.c             |   66 ++++++++++++++++++++++++++++++++++++-
>  5 files changed, 124 insertions(+), 23 deletions(-)
>
> diff --git a/Makefile b/Makefile
> index 60de4ef..bb35b4d 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -674,6 +674,8 @@ libs-y		:= $(libs-y1) $(libs-y2)
>  #   +--< $(vmlinux-main)
>  #   |    +--< driver/built-in.o mm/built-in.o + more
>  #   |
> +#   +-< .tmp_exports.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
> @@ -737,7 +739,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:
> @@ -797,13 +798,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.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.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.o .tmp_kallsyms2.o FORCE
>  	$(call if_changed,vmlinux__)
>  
>  # Needs to visit scripts/ before $(KALLSYMS) can be used.
> @@ -835,7 +836,7 @@ define rule_vmlinux-modpost
>  endef
>  
>  # 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.o $(kallsyms.o) FORCE
>  ifdef CONFIG_HEADERS_CHECK
>  	$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
>  endif
> @@ -858,6 +859,12 @@ modpost-init := $(filter-out init/built-in.o, $(vmlinux-init))
>  vmlinux.o: $(modpost-init) $(vmlinux-main) FORCE
>  	$(call if_changed_rule,vmlinux-modpost)
>  
> +# The modpost of vmlinux.o above creates .tmp_exports.c, 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.c: vmlinux.o
> +
>  # The actual objects are generated when descending, 
>  # make sure no implicit rule kicks in
>  $(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds): $(vmlinux-dirs) ;
> @@ -1190,7 +1197,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*
>  
>  # 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 6ad76bf..16c3e01 100644
> --- a/include/asm-generic/vmlinux.lds.h
> +++ b/include/asm-generic/vmlinux.lds.h
> @@ -253,79 +253,97 @@
>  									\
>  	TRACEDATA							\
>  									\
> +	/* 								\
> +	 * Kernel symbol table: discard the original unsorted tables	\
> +	 * in favour of the sorted versions. 				\
> +	 */								\
> +	/DISCARD/ : {							\
> +		*(__ksymtab)						\
> +		*(__ksymtab_gpl)					\
> +		*(__ksymtab_unused)					\
> +		*(__ksymtab_unused_gpl)					\
> +		*(__ksymtab_gpl_future)					\
> +		*(__kcrctab)						\
> +		*(__kcrctab_gpl)					\
> +		*(__kcrctab_unused)					\
> +		*(__kcrctab_unused_gpl)					\
> +		*(__kcrctab_gpl_future)					\
> +		*(__ksymtab_strings)					\
> +	}								\
> +									\
>  	/* 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 */						\
> diff --git a/include/linux/mod_export.h b/include/linux/mod_export.h
> index 3c51b9c..737a829 100644
> --- a/include/linux/mod_export.h
> +++ b/include/linux/mod_export.h
> @@ -31,17 +31,20 @@ struct kernel_symbol
>  #endif
>  
>  /* For every exported symbol, place a struct in the __ksymtab section */
> -#define __EXPORT_SYMBOL(sym, sec)				\
> +#define __EXPORT_SYMBOL_NAME(sym, sec, stringsec)			\
>  	extern typeof(sym) sym;					\
>  	__CRC_SYMBOL(sym, sec)					\
>  	static const char __kstrtab_##sym[]			\
> -	__attribute__((section("__ksymtab_strings"), aligned(1))) \
> +	__attribute__((section(stringsec), aligned(1))) \
>  	= MODULE_SYMBOL_PREFIX #sym;                    	\
>  	static const struct kernel_symbol __ksymtab_##sym	\
>  	__used							\
>  	__attribute__((section("__ksymtab" sec), unused))	\
>  	= { (unsigned long)&sym, __kstrtab_##sym }
>  
> +#define __EXPORT_SYMBOL(sym, sec) \
> +	__EXPORT_SYMBOL_NAME(sym, sec, "__ksymtab_strings")
> +
>  #define EXPORT_SYMBOL(sym)					\
>  	__EXPORT_SYMBOL(sym, "")
>  
> @@ -51,14 +54,22 @@ struct kernel_symbol
>  #define EXPORT_SYMBOL_GPL_FUTURE(sym)				\
>  	__EXPORT_SYMBOL(sym, "_gpl_future")
>  
> +
>  #ifdef CONFIG_UNUSED_SYMBOLS
> -#define EXPORT_UNUSED_SYMBOL(sym) __EXPORT_SYMBOL(sym, "_unused")
> -#define EXPORT_UNUSED_SYMBOL_GPL(sym) __EXPORT_SYMBOL(sym, "_unused_gpl")
> +#define EXPORT_UNUSED_SYMBOL(sym) __EXPORT_SYMBOL(sym, "_unused", "")
> +#define EXPORT_UNUSED_SYMBOL_GPL(sym) __EXPORT_SYMBOL(sym, "_unused_gpl", "")
>  #else
>  #define EXPORT_UNUSED_SYMBOL(sym)
>  #define EXPORT_UNUSED_SYMBOL_GPL(sym)
>  #endif
>  
> +/*
> + * In vmlinux (but not modules), __ksymtab and friends are replaced by sorted
> + * versions generated by scripts/mod/modpost.c
> + */
> +#define __EXPORT_SYMBOL_SORTED(sym, sec) \
> +	__EXPORT_SYMBOL_NAME(sym, sec "_sorted", "__ksymtab_strings_sorted")
> +
>  #endif /* __GENKSYMS__ */
>  
>  #else /* !CONFIG_MODULES */
> diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
> index f4053dc..8bee8cd 100644
> --- a/scripts/Makefile.modpost
> +++ b/scripts/Makefile.modpost
> @@ -98,7 +98,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.c $@
>  
>  vmlinux.o: FORCE
>  	@rm -fr $(kernelmarkersfile)
> diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
> index 4522948..6302ce7 100644
> --- a/scripts/mod/modpost.c
> +++ b/scripts/mod/modpost.c
> @@ -154,6 +154,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)
> @@ -191,6 +192,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;
> @@ -1976,6 +1978,61 @@ 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 as */
> +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, "#include <linux/mod_export.h>\n");
> +	buf_printf(&buf, "\n");
> +
> +	for (i = 0; i < n; i++) {
> +		sym = symbols[i];
> +
> +		/*
> +		 * We need to declare the symbol with extern linkage.
> +		 * Don't worry about the type. The linker won't care, and the
> +		 * compiler won't complain unless we include a conflicting
> +		 * declaration.
> +		 */
> +		buf_printf(&buf, "extern void *%s;\n", sym->name);
> +
> +		buf_printf(&buf, "__EXPORT_SYMBOL_SORTED(%s, \"%s\");\n",
> +				sym->name,
> +				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;
> @@ -2077,6 +2134,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;
> @@ -2084,7 +2142,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;
> @@ -2122,6 +2180,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;
> @@ -2182,6 +2243,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);
>  
>   

--
To unsubscribe from this list: send the line "unsubscribe linux-kbuild" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux&nblp;USB Development]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite Secrets]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux