1

kbuild: generate KSYMTAB entries by modpost

Commit 7b4537199a ("kbuild: link symbol CRCs at final link, removing
CONFIG_MODULE_REL_CRCS") made modpost output CRCs in the same way
whether the EXPORT_SYMBOL() is placed in *.c or *.S.

For further cleanups, this commit applies a similar approach to the
entire data structure of EXPORT_SYMBOL().

The EXPORT_SYMBOL() compilation is split into two stages.

When a source file is compiled, EXPORT_SYMBOL() will be converted into
a dummy symbol in the .export_symbol section.

For example,

    EXPORT_SYMBOL(foo);
    EXPORT_SYMBOL_NS_GPL(bar, BAR_NAMESPACE);

will be encoded into the following assembly code:

    .section ".export_symbol","a"
    __export_symbol_foo:
            .asciz ""                      /* license */
            .asciz ""                      /* name space */
            .balign 8
            .quad foo                      /* symbol reference */
    .previous

    .section ".export_symbol","a"
    __export_symbol_bar:
            .asciz "GPL"                   /* license */
            .asciz "BAR_NAMESPACE"         /* name space */
            .balign 8
            .quad bar                      /* symbol reference */
    .previous

They are mere markers to tell modpost the name, license, and namespace
of the symbols. They will be dropped from the final vmlinux and modules
because the *(.export_symbol) will go into /DISCARD/ in the linker script.

Then, modpost extracts all the information about EXPORT_SYMBOL() from the
.export_symbol section, and generates the final C code:

    KSYMTAB_FUNC(foo, "", "");
    KSYMTAB_FUNC(bar, "_gpl", "BAR_NAMESPACE");

KSYMTAB_FUNC() (or KSYMTAB_DATA() if it is data) is expanded to struct
kernel_symbol that will be linked to the vmlinux or a module.

With this change, EXPORT_SYMBOL() works in the same way for *.c and *.S
files, providing the following benefits.

[1] Deprecate EXPORT_DATA_SYMBOL()

In the old days, EXPORT_SYMBOL() was only available in C files. To export
a symbol in *.S, EXPORT_SYMBOL() was placed in a separate *.c file.
arch/arm/kernel/armksyms.c is one example written in the classic manner.

Commit 22823ab419 ("EXPORT_SYMBOL() for asm") removed this limitation.
Since then, EXPORT_SYMBOL() can be placed close to the symbol definition
in *.S files. It was a nice improvement.

However, as that commit mentioned, you need to use EXPORT_DATA_SYMBOL()
for data objects on some architectures.

In the new approach, modpost checks symbol's type (STT_FUNC or not),
and outputs KSYMTAB_FUNC() or KSYMTAB_DATA() accordingly.

There are only two users of EXPORT_DATA_SYMBOL:

  EXPORT_DATA_SYMBOL_GPL(empty_zero_page)    (arch/ia64/kernel/head.S)
  EXPORT_DATA_SYMBOL(ia64_ivt)               (arch/ia64/kernel/ivt.S)

They are transformed as follows and output into .vmlinux.export.c

  KSYMTAB_DATA(empty_zero_page, "_gpl", "");
  KSYMTAB_DATA(ia64_ivt, "", "");

The other EXPORT_SYMBOL users in ia64 assembly are output as
KSYMTAB_FUNC().

EXPORT_DATA_SYMBOL() is now deprecated.

[2] merge <linux/export.h> and <asm-generic/export.h>

There are two similar header implementations:

  include/linux/export.h        for .c files
  include/asm-generic/export.h  for .S files

Ideally, the functionality should be consistent between them, but they
tend to diverge.

Commit 8651ec01da ("module: add support for symbol namespaces.") did
not support the namespace for *.S files.

This commit shifts the essential implementation part to C, which supports
EXPORT_SYMBOL_NS() for *.S files.

<asm/export.h> and <asm-generic/export.h> will remain as a wrapper of
<linux/export.h> for a while.

They will be removed after #include <asm/export.h> directives are all
replaced with #include <linux/export.h>.

[3] Implement CONFIG_TRIM_UNUSED_KSYMS in one-pass algorithm (by a later commit)

When CONFIG_TRIM_UNUSED_KSYMS is enabled, Kbuild recursively traverses
the directory tree to determine which EXPORT_SYMBOL to trim. If an
EXPORT_SYMBOL turns out to be unused by anyone, Kbuild begins the
second traverse, where some source files are recompiled with their
EXPORT_SYMBOL() tuned into a no-op.

We can do this better now; modpost can selectively emit KSYMTAB entries
that are really used by modules.

Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
This commit is contained in:
Masahiro Yamada 2023-06-12 00:50:52 +09:00
parent 94d6cb6812
commit ddb5cdbafa
12 changed files with 190 additions and 184 deletions

View File

@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
generated-y += syscall_table.h generated-y += syscall_table.h
generic-y += agp.h generic-y += agp.h
generic-y += export.h
generic-y += kvm_para.h generic-y += kvm_para.h
generic-y += mcs_spinlock.h generic-y += mcs_spinlock.h
generic-y += vtime.h generic-y += vtime.h

View File

@ -1,3 +0,0 @@
/* EXPORT_DATA_SYMBOL != EXPORT_SYMBOL here */
#define KSYM_FUNC(name) @fptr(name)
#include <asm-generic/export.h>

View File

@ -3,86 +3,12 @@
#define __ASM_GENERIC_EXPORT_H #define __ASM_GENERIC_EXPORT_H
/* /*
* This comment block is used by fixdep. Please do not remove. * <asm/export.h> and <asm-generic/export.h> are deprecated.
* * Please include <linux/export.h> directly.
* When CONFIG_MODVERSIONS is changed from n to y, all source files having
* EXPORT_SYMBOL variants must be re-compiled because genksyms is run as a
* side effect of the *.o build rule.
*/ */
#include <linux/export.h>
#ifndef KSYM_FUNC #define EXPORT_DATA_SYMBOL(name) EXPORT_SYMBOL(name)
#define KSYM_FUNC(x) x #define EXPORT_DATA_SYMBOL_GPL(name) EXPORT_SYMBOL_GPL(name)
#endif
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
#define KSYM_ALIGN 4
#elif defined(CONFIG_64BIT)
#define KSYM_ALIGN 8
#else
#define KSYM_ALIGN 4
#endif
.macro __put, val, name
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
.long \val - ., \name - ., 0
#elif defined(CONFIG_64BIT)
.quad \val, \name, 0
#else
.long \val, \name, 0
#endif
.endm
/*
* note on .section use: we specify progbits since usage of the "M" (SHF_MERGE)
* section flag requires it. Use '%progbits' instead of '@progbits' since the
* former apparently works on all arches according to the binutils source.
*/
.macro ___EXPORT_SYMBOL name,val,sec
#if defined(CONFIG_MODULES) && !defined(__DISABLE_EXPORTS)
.section ___ksymtab\sec+\name,"a"
.balign KSYM_ALIGN
__ksymtab_\name:
__put \val, __kstrtab_\name
.previous
.section __ksymtab_strings,"aMS",%progbits,1
__kstrtab_\name:
.asciz "\name"
.previous
#endif
.endm
#if defined(CONFIG_TRIM_UNUSED_KSYMS)
#include <linux/kconfig.h>
#include <generated/autoksyms.h>
.macro __ksym_marker sym
.section ".discard.ksym","a"
__ksym_marker_\sym:
.previous
.endm
#define __EXPORT_SYMBOL(sym, val, sec) \
__ksym_marker sym; \
__cond_export_sym(sym, val, sec, __is_defined(__KSYM_##sym))
#define __cond_export_sym(sym, val, sec, conf) \
___cond_export_sym(sym, val, sec, conf)
#define ___cond_export_sym(sym, val, sec, enabled) \
__cond_export_sym_##enabled(sym, val, sec)
#define __cond_export_sym_1(sym, val, sec) ___EXPORT_SYMBOL sym, val, sec
#define __cond_export_sym_0(sym, val, sec) /* nothing */
#else
#define __EXPORT_SYMBOL(sym, val, sec) ___EXPORT_SYMBOL sym, val, sec
#endif
#define EXPORT_SYMBOL(name) \
__EXPORT_SYMBOL(name, KSYM_FUNC(name),)
#define EXPORT_SYMBOL_GPL(name) \
__EXPORT_SYMBOL(name, KSYM_FUNC(name), _gpl)
#define EXPORT_DATA_SYMBOL(name) \
__EXPORT_SYMBOL(name, name,)
#define EXPORT_DATA_SYMBOL_GPL(name) \
__EXPORT_SYMBOL(name, name,_gpl)
#endif #endif

View File

@ -1006,6 +1006,7 @@
PATCHABLE_DISCARDS \ PATCHABLE_DISCARDS \
*(.discard) \ *(.discard) \
*(.discard.*) \ *(.discard.*) \
*(.export_symbol) \
*(.modinfo) \ *(.modinfo) \
/* ld.bfd warns about .gnu.version* even when not emitted */ \ /* ld.bfd warns about .gnu.version* even when not emitted */ \
*(.gnu.version*) \ *(.gnu.version*) \

View File

@ -10,6 +10,55 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/types.h> #include <linux/types.h>
#if defined(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)
/*
* relative reference: this reduces the size by half on 64-bit architectures,
* and eliminates the need for absolute relocations that require runtime
* processing on relocatable kernels.
*/
#define __KSYM_REF(sym) ".long " #sym "- ."
#elif defined(CONFIG_64BIT)
#define __KSYM_REF(sym) ".quad " #sym
#else
#define __KSYM_REF(sym) ".long " #sym
#endif
/*
* For every exported symbol, do the following:
*
* - Put the name of the symbol and namespace (empty string "" for none) in
* __ksymtab_strings.
* - Place a struct kernel_symbol entry in the __ksymtab section.
*
* Note on .section use: we specify progbits since usage of the "M" (SHF_MERGE)
* section flag requires it. Use '%progbits' instead of '@progbits' since the
* former apparently works on all arches according to the binutils source.
*/
#define __KSYMTAB(name, sym, sec, ns) \
asm(" .section \"__ksymtab_strings\",\"aMS\",%progbits,1" "\n" \
"__kstrtab_" #name ":" "\n" \
" .asciz \"" #name "\"" "\n" \
"__kstrtabns_" #name ":" "\n" \
" .asciz \"" ns "\"" "\n" \
" .previous" "\n" \
" .section \"___ksymtab" sec "+" #name "\", \"a\"" "\n" \
" .balign 4" "\n" \
"__ksymtab_" #name ":" "\n" \
__KSYM_REF(sym) "\n" \
__KSYM_REF(__kstrtab_ ##name) "\n" \
__KSYM_REF(__kstrtabns_ ##name) "\n" \
" .previous" "\n" \
)
#ifdef CONFIG_IA64
#define KSYM_FUNC(name) @fptr(name)
#else
#define KSYM_FUNC(name) name
#endif
#define KSYMTAB_FUNC(name, sec, ns) __KSYMTAB(name, KSYM_FUNC(name), sec, ns)
#define KSYMTAB_DATA(name, sec, ns) __KSYMTAB(name, name, sec, ns)
#define SYMBOL_CRC(sym, crc, sec) \ #define SYMBOL_CRC(sym, crc, sec) \
asm(".section \"___kcrctab" sec "+" #sym "\",\"a\"" "\n" \ asm(".section \"___kcrctab" sec "+" #sym "\",\"a\"" "\n" \
"__crc_" #sym ":" "\n" \ "__crc_" #sym ":" "\n" \

View File

@ -2,6 +2,8 @@
#ifndef _LINUX_EXPORT_H #ifndef _LINUX_EXPORT_H
#define _LINUX_EXPORT_H #define _LINUX_EXPORT_H
#include <linux/compiler.h>
#include <linux/linkage.h>
#include <linux/stringify.h> #include <linux/stringify.h>
/* /*
@ -28,72 +30,41 @@ extern struct module __this_module;
#else #else
#define THIS_MODULE ((struct module *)0) #define THIS_MODULE ((struct module *)0)
#endif #endif
#endif /* __ASSEMBLY__ */
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS #ifdef CONFIG_64BIT
#include <linux/compiler.h> #define __EXPORT_SYMBOL_REF(sym) \
/* .balign 8 ASM_NL \
* Emit the ksymtab entry as a pair of relative references: this reduces .quad sym
* the size by half on 64-bit architectures, and eliminates the need for
* absolute relocations that require runtime processing on relocatable
* kernels.
*/
#define __KSYMTAB_ENTRY(sym, sec) \
__ADDRESSABLE(sym) \
asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \
" .balign 4 \n" \
"__ksymtab_" #sym ": \n" \
" .long " #sym "- . \n" \
" .long __kstrtab_" #sym "- . \n" \
" .long __kstrtabns_" #sym "- . \n" \
" .previous \n")
struct kernel_symbol {
int value_offset;
int name_offset;
int namespace_offset;
};
#else #else
#define __KSYMTAB_ENTRY(sym, sec) \ #define __EXPORT_SYMBOL_REF(sym) \
static const struct kernel_symbol __ksymtab_##sym \ .balign 4 ASM_NL \
__attribute__((section("___ksymtab" sec "+" #sym), used)) \ .long sym
__aligned(sizeof(void *)) \
= { (unsigned long)&sym, __kstrtab_##sym, __kstrtabns_##sym }
struct kernel_symbol {
unsigned long value;
const char *name;
const char *namespace;
};
#endif #endif
#define ____EXPORT_SYMBOL(sym, license, ns) \
.section ".export_symbol","a" ASM_NL \
__export_symbol_##sym: ASM_NL \
.asciz license ASM_NL \
.asciz ns ASM_NL \
__EXPORT_SYMBOL_REF(sym) ASM_NL \
.previous
#ifdef __GENKSYMS__ #ifdef __GENKSYMS__
#define ___EXPORT_SYMBOL(sym, sec, ns) __GENKSYMS_EXPORT_SYMBOL(sym) #define ___EXPORT_SYMBOL(sym, sec, ns) __GENKSYMS_EXPORT_SYMBOL(sym)
#elif defined(__ASSEMBLY__)
#define ___EXPORT_SYMBOL(sym, license, ns) \
____EXPORT_SYMBOL(sym, license, ns)
#else #else
/* #define ___EXPORT_SYMBOL(sym, license, ns) \
* For every exported symbol, do the following:
*
* - Put the name of the symbol and namespace (empty string "" for none) in
* __ksymtab_strings.
* - Place a struct kernel_symbol entry in the __ksymtab section.
*
* note on .section use: we specify progbits since usage of the "M" (SHF_MERGE)
* section flag requires it. Use '%progbits' instead of '@progbits' since the
* former apparently works on all arches according to the binutils source.
*/
#define ___EXPORT_SYMBOL(sym, sec, ns) \
extern typeof(sym) sym; \ extern typeof(sym) sym; \
extern const char __kstrtab_##sym[]; \ __ADDRESSABLE(sym) \
extern const char __kstrtabns_##sym[]; \ asm(__stringify(____EXPORT_SYMBOL(sym, license, ns)))
asm(" .section \"__ksymtab_strings\",\"aMS\",%progbits,1 \n" \
"__kstrtab_" #sym ": \n" \
" .asciz \"" #sym "\" \n" \
"__kstrtabns_" #sym ": \n" \
" .asciz \"" ns "\" \n" \
" .previous \n"); \
__KSYMTAB_ENTRY(sym, sec)
#endif #endif
@ -117,9 +88,21 @@ struct kernel_symbol {
* from the $(NM) output (see scripts/gen_ksymdeps.sh). These symbols are * from the $(NM) output (see scripts/gen_ksymdeps.sh). These symbols are
* discarded in the final link stage. * discarded in the final link stage.
*/ */
#ifdef __ASSEMBLY__
#define __ksym_marker(sym) \
.section ".discard.ksym","a" ; \
__ksym_marker_##sym: ; \
.previous
#else
#define __ksym_marker(sym) \ #define __ksym_marker(sym) \
static int __ksym_marker_##sym[0] __section(".discard.ksym") __used static int __ksym_marker_##sym[0] __section(".discard.ksym") __used
#endif
#define __EXPORT_SYMBOL(sym, sec, ns) \ #define __EXPORT_SYMBOL(sym, sec, ns) \
__ksym_marker(sym); \ __ksym_marker(sym); \
__cond_export_sym(sym, sec, ns, __is_defined(__KSYM_##sym)) __cond_export_sym(sym, sec, ns, __is_defined(__KSYM_##sym))
@ -148,10 +131,8 @@ struct kernel_symbol {
#endif #endif
#define EXPORT_SYMBOL(sym) _EXPORT_SYMBOL(sym, "") #define EXPORT_SYMBOL(sym) _EXPORT_SYMBOL(sym, "")
#define EXPORT_SYMBOL_GPL(sym) _EXPORT_SYMBOL(sym, "_gpl") #define EXPORT_SYMBOL_GPL(sym) _EXPORT_SYMBOL(sym, "GPL")
#define EXPORT_SYMBOL_NS(sym, ns) __EXPORT_SYMBOL(sym, "", __stringify(ns)) #define EXPORT_SYMBOL_NS(sym, ns) __EXPORT_SYMBOL(sym, "", __stringify(ns))
#define EXPORT_SYMBOL_NS_GPL(sym, ns) __EXPORT_SYMBOL(sym, "_gpl", __stringify(ns)) #define EXPORT_SYMBOL_NS_GPL(sym, ns) __EXPORT_SYMBOL(sym, "GPL", __stringify(ns))
#endif /* !__ASSEMBLY__ */
#endif /* _LINUX_EXPORT_H */ #endif /* _LINUX_EXPORT_H */

View File

@ -389,9 +389,9 @@ const struct dev_pm_ops name = { \
#endif #endif
#define EXPORT_DEV_PM_OPS(name) _EXPORT_DEV_PM_OPS(name, "", "") #define EXPORT_DEV_PM_OPS(name) _EXPORT_DEV_PM_OPS(name, "", "")
#define EXPORT_GPL_DEV_PM_OPS(name) _EXPORT_DEV_PM_OPS(name, "_gpl", "") #define EXPORT_GPL_DEV_PM_OPS(name) _EXPORT_DEV_PM_OPS(name, "GPL", "")
#define EXPORT_NS_DEV_PM_OPS(name, ns) _EXPORT_DEV_PM_OPS(name, "", #ns) #define EXPORT_NS_DEV_PM_OPS(name, ns) _EXPORT_DEV_PM_OPS(name, "", #ns)
#define EXPORT_NS_GPL_DEV_PM_OPS(name, ns) _EXPORT_DEV_PM_OPS(name, "_gpl", #ns) #define EXPORT_NS_GPL_DEV_PM_OPS(name, ns) _EXPORT_DEV_PM_OPS(name, "GPL", #ns)
/* /*
* Use this if you want to use the same suspend and resume callbacks for suspend * Use this if you want to use the same suspend and resume callbacks for suspend

View File

@ -32,6 +32,18 @@
/* Maximum number of characters written by module_flags() */ /* Maximum number of characters written by module_flags() */
#define MODULE_FLAGS_BUF_SIZE (TAINT_FLAGS_COUNT + 4) #define MODULE_FLAGS_BUF_SIZE (TAINT_FLAGS_COUNT + 4)
struct kernel_symbol {
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
int value_offset;
int name_offset;
int namespace_offset;
#else
unsigned long value;
const char *name;
const char *namespace;
#endif
};
extern struct mutex module_mutex; extern struct mutex module_mutex;
extern struct list_head modules; extern struct list_head modules;

View File

@ -163,7 +163,7 @@ quiet_cmd_cc_o_c = CC $(quiet_modtag) $@
ifdef CONFIG_MODVERSIONS ifdef CONFIG_MODVERSIONS
# When module versioning is enabled the following steps are executed: # When module versioning is enabled the following steps are executed:
# o compile a <file>.o from <file>.c # o compile a <file>.o from <file>.c
# o if <file>.o doesn't contain a __ksymtab version, i.e. does # o if <file>.o doesn't contain a __export_symbol_*, i.e. does
# not export symbols, it's done. # not export symbols, it's done.
# o otherwise, we calculate symbol versions using the good old # o otherwise, we calculate symbol versions using the good old
# genksyms on the preprocessed source and dump them into the .cmd file. # genksyms on the preprocessed source and dump them into the .cmd file.
@ -171,7 +171,7 @@ ifdef CONFIG_MODVERSIONS
# be compiled and linked to the kernel and/or modules. # be compiled and linked to the kernel and/or modules.
gen_symversions = \ gen_symversions = \
if $(NM) $@ 2>/dev/null | grep -q __ksymtab; then \ if $(NM) $@ 2>/dev/null | grep -q ' __export_symbol_'; then \
$(call cmd_gensymtypes_$(1),$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \ $(call cmd_gensymtypes_$(1),$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \
>> $(dot-target).cmd; \ >> $(dot-target).cmd; \
fi fi
@ -342,9 +342,7 @@ $(obj)/%.ll: $(src)/%.rs FORCE
cmd_gensymtypes_S = \ cmd_gensymtypes_S = \
{ echo "\#include <linux/kernel.h>" ; \ { echo "\#include <linux/kernel.h>" ; \
echo "\#include <asm/asm-prototypes.h>" ; \ echo "\#include <asm/asm-prototypes.h>" ; \
$(CPP) $(a_flags) $< | \ $(NM) $@ | sed -n 's/.* __export_symbol_\(.*\)/EXPORT_SYMBOL(\1);/p' ; } | \
grep "\<___EXPORT_SYMBOL\>" | \
sed 's/.*___EXPORT_SYMBOL[[:space:]]*\([a-zA-Z0-9_]*\)[[:space:]]*,.*/EXPORT_SYMBOL(\1);/' ; } | \
$(CPP) -D__GENKSYMS__ $(c_flags) -xc - | $(genksyms) $(CPP) -D__GENKSYMS__ $(c_flags) -xc - | $(genksyms)
quiet_cmd_cc_symtypes_S = SYM $(quiet_modtag) $@ quiet_cmd_cc_symtypes_S = SYM $(quiet_modtag) $@

View File

@ -46,9 +46,9 @@ BEGIN {
{ symbol_types[$3]=$2 } { symbol_types[$3]=$2 }
# append the exported symbol to the array # append the exported symbol to the array
($3 ~ /^__ksymtab_/) { ($3 ~ /^__export_symbol_.*/) {
export_symbols[i] = $3 export_symbols[i] = $3
sub(/^__ksymtab_/, "", export_symbols[i]) sub(/^__export_symbol_/, "", export_symbols[i])
i++ i++
} }

View File

@ -217,6 +217,7 @@ struct symbol {
unsigned int crc; unsigned int crc;
bool crc_valid; bool crc_valid;
bool weak; bool weak;
bool is_func;
bool is_gpl_only; /* exported by EXPORT_SYMBOL_GPL */ bool is_gpl_only; /* exported by EXPORT_SYMBOL_GPL */
char name[]; char name[];
}; };
@ -533,6 +534,8 @@ static int parse_elf(struct elf_info *info, const char *filename)
fatal("%s has NOBITS .modinfo\n", filename); fatal("%s has NOBITS .modinfo\n", filename);
info->modinfo = (void *)hdr + sechdrs[i].sh_offset; info->modinfo = (void *)hdr + sechdrs[i].sh_offset;
info->modinfo_len = sechdrs[i].sh_size; info->modinfo_len = sechdrs[i].sh_size;
} else if (!strcmp(secname, ".export_symbol")) {
info->export_symbol_secndx = i;
} }
if (sechdrs[i].sh_type == SHT_SYMTAB) { if (sechdrs[i].sh_type == SHT_SYMTAB) {
@ -655,18 +658,6 @@ static void handle_symbol(struct module *mod, struct elf_info *info,
ELF_ST_BIND(sym->st_info) == STB_WEAK); ELF_ST_BIND(sym->st_info) == STB_WEAK);
break; break;
default: default:
/* All exported symbols */
if (strstarts(symname, "__ksymtab_")) {
const char *name, *secname;
name = symname + strlen("__ksymtab_");
secname = sec_name(info, get_secindex(info, sym));
if (strstarts(secname, "___ksymtab_gpl+"))
sym_add_exported(name, mod, true);
else if (strstarts(secname, "___ksymtab+"))
sym_add_exported(name, mod, false);
}
if (strcmp(symname, "init_module") == 0) if (strcmp(symname, "init_module") == 0)
mod->has_init = true; mod->has_init = true;
if (strcmp(symname, "cleanup_module") == 0) if (strcmp(symname, "cleanup_module") == 0)
@ -848,7 +839,6 @@ enum mismatch {
XXXEXIT_TO_SOME_EXIT, XXXEXIT_TO_SOME_EXIT,
ANY_INIT_TO_ANY_EXIT, ANY_INIT_TO_ANY_EXIT,
ANY_EXIT_TO_ANY_INIT, ANY_EXIT_TO_ANY_INIT,
EXPORT_TO_INIT_EXIT,
EXTABLE_TO_NON_TEXT, EXTABLE_TO_NON_TEXT,
}; };
@ -920,12 +910,6 @@ static const struct sectioncheck sectioncheck[] = {
.bad_tosec = { INIT_SECTIONS, NULL }, .bad_tosec = { INIT_SECTIONS, NULL },
.mismatch = ANY_INIT_TO_ANY_EXIT, .mismatch = ANY_INIT_TO_ANY_EXIT,
}, },
/* Do not export init/exit functions or data */
{
.fromsec = { "___ksymtab*", NULL },
.bad_tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL },
.mismatch = EXPORT_TO_INIT_EXIT,
},
{ {
.fromsec = { "__ex_table", NULL }, .fromsec = { "__ex_table", NULL },
/* If you're adding any new black-listed sections in here, consider /* If you're adding any new black-listed sections in here, consider
@ -1180,10 +1164,6 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf,
warn("%s: section mismatch in reference: %s (section: %s) -> %s (section: %s)\n", warn("%s: section mismatch in reference: %s (section: %s) -> %s (section: %s)\n",
modname, fromsym, fromsec, tosym, tosec); modname, fromsym, fromsec, tosym, tosec);
break; break;
case EXPORT_TO_INIT_EXIT:
warn("%s: EXPORT_SYMBOL used for init/exit symbol: %s (section: %s)\n",
modname, tosym, tosec);
break;
case EXTABLE_TO_NON_TEXT: case EXTABLE_TO_NON_TEXT:
warn("%s(%s+0x%lx): Section mismatch in reference to the %s:%s\n", warn("%s(%s+0x%lx): Section mismatch in reference to the %s:%s\n",
modname, fromsec, (long)faddr, tosec, tosym); modname, fromsec, (long)faddr, tosec, tosym);
@ -1211,14 +1191,75 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf,
} }
} }
static void check_export_symbol(struct module *mod, struct elf_info *elf,
Elf_Addr faddr, const char *secname,
Elf_Sym *sym)
{
static const char *prefix = "__export_symbol_";
const char *label_name, *name, *data;
Elf_Sym *label;
struct symbol *s;
bool is_gpl;
label = find_fromsym(elf, faddr, elf->export_symbol_secndx);
label_name = sym_name(elf, label);
if (!strstarts(label_name, prefix)) {
error("%s: .export_symbol section contains strange symbol '%s'\n",
mod->name, label_name);
return;
}
name = sym_name(elf, sym);
if (strcmp(label_name + strlen(prefix), name)) {
error("%s: .export_symbol section references '%s', but it does not seem to be an export symbol\n",
mod->name, name);
return;
}
data = sym_get_data(elf, label); /* license */
if (!strcmp(data, "GPL")) {
is_gpl = true;
} else if (!strcmp(data, "")) {
is_gpl = false;
} else {
error("%s: unknown license '%s' was specified for '%s'\n",
mod->name, data, name);
return;
}
data += strlen(data) + 1; /* namespace */
s = sym_add_exported(name, mod, is_gpl);
sym_update_namespace(name, data);
/*
* We need to be aware whether we are exporting a function or
* a data on some architectures.
*/
s->is_func = (ELF_ST_TYPE(sym->st_info) == STT_FUNC);
if (match(secname, PATTERNS(INIT_SECTIONS)))
warn("%s: %s: EXPORT_SYMBOL used for init symbol. Remove __init or EXPORT_SYMBOL.\n",
mod->name, name);
else if (match(secname, PATTERNS(EXIT_SECTIONS)))
warn("%s: %s: EXPORT_SYMBOL used for exit symbol. Remove __exit or EXPORT_SYMBOL.\n",
mod->name, name);
}
static void check_section_mismatch(struct module *mod, struct elf_info *elf, static void check_section_mismatch(struct module *mod, struct elf_info *elf,
Elf_Sym *sym, Elf_Sym *sym,
unsigned int fsecndx, const char *fromsec, unsigned int fsecndx, const char *fromsec,
Elf_Addr faddr, Elf_Addr taddr) Elf_Addr faddr, Elf_Addr taddr)
{ {
const char *tosec = sec_name(elf, get_secindex(elf, sym)); const char *tosec = sec_name(elf, get_secindex(elf, sym));
const struct sectioncheck *mismatch = section_mismatch(fromsec, tosec); const struct sectioncheck *mismatch;
if (elf->export_symbol_secndx == fsecndx) {
check_export_symbol(mod, elf, faddr, tosec, sym);
return;
}
mismatch = section_mismatch(fromsec, tosec);
if (!mismatch) if (!mismatch)
return; return;
@ -1698,15 +1739,6 @@ static void read_symbols(const char *modname)
handle_moddevtable(mod, &info, sym, symname); handle_moddevtable(mod, &info, sym, symname);
} }
for (sym = info.symtab_start; sym < info.symtab_stop; sym++) {
symname = remove_dot(info.strtab + sym->st_name);
/* Apply symbol namespaces from __kstrtabns_<symbol> entries. */
if (strstarts(symname, "__kstrtabns_"))
sym_update_namespace(symname + strlen("__kstrtabns_"),
sym_get_data(&info, sym));
}
check_sec_ref(mod, &info); check_sec_ref(mod, &info);
if (!mod->is_vmlinux) { if (!mod->is_vmlinux) {
@ -1890,6 +1922,14 @@ static void add_exported_symbols(struct buffer *buf, struct module *mod)
{ {
struct symbol *sym; struct symbol *sym;
/* generate struct for exported symbols */
buf_printf(buf, "\n");
list_for_each_entry(sym, &mod->exported_symbols, list)
buf_printf(buf, "KSYMTAB_%s(%s, \"%s\", \"%s\");\n",
sym->is_func ? "FUNC" : "DATA", sym->name,
sym->is_gpl_only ? "_gpl" : "",
sym->namespace ?: "");
if (!modversions) if (!modversions)
return; return;

View File

@ -137,6 +137,7 @@ struct elf_info {
Elf_Shdr *sechdrs; Elf_Shdr *sechdrs;
Elf_Sym *symtab_start; Elf_Sym *symtab_start;
Elf_Sym *symtab_stop; Elf_Sym *symtab_stop;
unsigned int export_symbol_secndx; /* .export_symbol section */
char *strtab; char *strtab;
char *modinfo; char *modinfo;
unsigned int modinfo_len; unsigned int modinfo_len;