Commit 7290d580 authored by Ard Biesheuvel's avatar Ard Biesheuvel Committed by Linus Torvalds

module: use relative references for __ksymtab entries

An ordinary arm64 defconfig build has ~64 KB worth of __ksymtab entries,
each consisting of two 64-bit fields containing absolute references, to
the symbol itself and to a char array containing its name, respectively.

When we build the same configuration with KASLR enabled, we end up with an
additional ~192 KB of relocations in the .init section, i.e., one 24 byte
entry for each absolute reference, which all need to be processed at boot
time.

Given how the struct kernel_symbol that describes each entry is completely
local to module.c (except for the references emitted by EXPORT_SYMBOL()
itself), we can easily modify it to contain two 32-bit relative references
instead.  This reduces the size of the __ksymtab section by 50% for all
64-bit architectures, and gets rid of the runtime relocations entirely for
architectures implementing KASLR, either via standard PIE linking (arm64)
or using custom host tools (x86).

Note that the binary search involving __ksymtab contents relies on each
section being sorted by symbol name.  This is implemented based on the
input section names, not the names in the ksymtab entries, so this patch
does not interfere with that.

Given that the use of place-relative relocations requires support both in
the toolchain and in the module loader, we cannot enable this feature for
all architectures.  So make it dependent on whether
CONFIG_HAVE_ARCH_PREL32_RELOCATIONS is defined.

Link: http://lkml.kernel.org/r/20180704083651.24360-4-ard.biesheuvel@linaro.orgSigned-off-by: default avatarArd Biesheuvel <ard.biesheuvel@linaro.org>
Acked-by: default avatarJessica Yu <jeyu@kernel.org>
Acked-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Reviewed-by: default avatarWill Deacon <will.deacon@arm.com>
Acked-by: default avatarIngo Molnar <mingo@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: James Morris <james.morris@microsoft.com>
Cc: James Morris <jmorris@namei.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Nicolas Pitre <nico@linaro.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Petr Mladek <pmladek@suse.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Thomas Garnier <thgarnie@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f922c4ab
...@@ -8,5 +8,6 @@ generated-y += xen-hypercalls.h ...@@ -8,5 +8,6 @@ generated-y += xen-hypercalls.h
generic-y += dma-contiguous.h generic-y += dma-contiguous.h
generic-y += early_ioremap.h generic-y += early_ioremap.h
generic-y += export.h
generic-y += mcs_spinlock.h generic-y += mcs_spinlock.h
generic-y += mm-arch-hooks.h generic-y += mm-arch-hooks.h
/* SPDX-License-Identifier: GPL-2.0 */
#ifdef CONFIG_64BIT
#define KSYM_ALIGN 16
#endif
#include <asm-generic/export.h>
...@@ -5,12 +5,10 @@ ...@@ -5,12 +5,10 @@
#define KSYM_FUNC(x) x #define KSYM_FUNC(x) x
#endif #endif
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
#define __put .quad
#ifndef KSYM_ALIGN #ifndef KSYM_ALIGN
#define KSYM_ALIGN 8 #define KSYM_ALIGN 8
#endif #endif
#else #else
#define __put .long
#ifndef KSYM_ALIGN #ifndef KSYM_ALIGN
#define KSYM_ALIGN 4 #define KSYM_ALIGN 4
#endif #endif
...@@ -19,6 +17,16 @@ ...@@ -19,6 +17,16 @@
#define KCRC_ALIGN 4 #define KCRC_ALIGN 4
#endif #endif
.macro __put, val, name
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
.long \val - ., \name - .
#elif defined(CONFIG_64BIT)
.quad \val, \name
#else
.long \val, \name
#endif
.endm
/* /*
* note on .section use: @progbits vs %progbits nastiness doesn't matter, * note on .section use: @progbits vs %progbits nastiness doesn't matter,
* since we immediately emit into those sections anyway. * since we immediately emit into those sections anyway.
......
...@@ -280,6 +280,25 @@ unsigned long read_word_at_a_time(const void *addr) ...@@ -280,6 +280,25 @@ unsigned long read_word_at_a_time(const void *addr)
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
/*
* Force the compiler to emit 'sym' as a symbol, so that we can reference
* it from inline assembler. Necessary in case 'sym' could be inlined
* otherwise, or eliminated entirely due to lack of references that are
* visible to the compiler.
*/
#define __ADDRESSABLE(sym) \
static void * __attribute__((section(".discard.addressable"), used)) \
__PASTE(__addressable_##sym, __LINE__) = (void *)&sym;
/**
* offset_to_ptr - convert a relative memory offset to an absolute pointer
* @off: the address of the 32-bit offset value
*/
static inline void *offset_to_ptr(const int *off)
{
return (void *)((unsigned long)off + *off);
}
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#ifndef __optimize #ifndef __optimize
......
...@@ -18,12 +18,6 @@ ...@@ -18,12 +18,6 @@
#define VMLINUX_SYMBOL_STR(x) __VMLINUX_SYMBOL_STR(x) #define VMLINUX_SYMBOL_STR(x) __VMLINUX_SYMBOL_STR(x)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
struct kernel_symbol
{
unsigned long value;
const char *name;
};
#ifdef MODULE #ifdef MODULE
extern struct module __this_module; extern struct module __this_module;
#define THIS_MODULE (&__this_module) #define THIS_MODULE (&__this_module)
...@@ -54,17 +48,47 @@ extern struct module __this_module; ...@@ -54,17 +48,47 @@ extern struct module __this_module;
#define __CRC_SYMBOL(sym, sec) #define __CRC_SYMBOL(sym, sec)
#endif #endif
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
#include <linux/compiler.h>
/*
* Emit the ksymtab entry as a pair of relative references: 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 __KSYMTAB_ENTRY(sym, sec) \
__ADDRESSABLE(sym) \
asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \
" .balign 8 \n" \
"__ksymtab_" #sym ": \n" \
" .long " #sym "- . \n" \
" .long __kstrtab_" #sym "- . \n" \
" .previous \n")
struct kernel_symbol {
int value_offset;
int name_offset;
};
#else
#define __KSYMTAB_ENTRY(sym, sec) \
static const struct kernel_symbol __ksymtab_##sym \
__attribute__((section("___ksymtab" sec "+" #sym), used)) \
= { (unsigned long)&sym, __kstrtab_##sym }
struct kernel_symbol {
unsigned long value;
const char *name;
};
#endif
/* For every exported symbol, place a struct in the __ksymtab section */ /* For every exported symbol, place a struct in the __ksymtab section */
#define ___EXPORT_SYMBOL(sym, sec) \ #define ___EXPORT_SYMBOL(sym, sec) \
extern typeof(sym) sym; \ extern typeof(sym) sym; \
__CRC_SYMBOL(sym, sec) \ __CRC_SYMBOL(sym, sec) \
static const char __kstrtab_##sym[] \ static const char __kstrtab_##sym[] \
__attribute__((section("__ksymtab_strings"), aligned(1))) \ __attribute__((section("__ksymtab_strings"), used, aligned(1))) \
= #sym; \ = #sym; \
static const struct kernel_symbol __ksymtab_##sym \ __KSYMTAB_ENTRY(sym, sec)
__used \
__attribute__((section("___ksymtab" sec "+" #sym), used)) \
= { (unsigned long)&sym, __kstrtab_##sym }
#if defined(__DISABLE_EXPORTS) #if defined(__DISABLE_EXPORTS)
......
...@@ -529,12 +529,30 @@ static bool check_symbol(const struct symsearch *syms, ...@@ -529,12 +529,30 @@ static bool check_symbol(const struct symsearch *syms,
return true; return true;
} }
static unsigned long kernel_symbol_value(const struct kernel_symbol *sym)
{
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
return (unsigned long)offset_to_ptr(&sym->value_offset);
#else
return sym->value;
#endif
}
static const char *kernel_symbol_name(const struct kernel_symbol *sym)
{
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
return offset_to_ptr(&sym->name_offset);
#else
return sym->name;
#endif
}
static int cmp_name(const void *va, const void *vb) static int cmp_name(const void *va, const void *vb)
{ {
const char *a; const char *a;
const struct kernel_symbol *b; const struct kernel_symbol *b;
a = va; b = vb; a = va; b = vb;
return strcmp(a, b->name); return strcmp(a, kernel_symbol_name(b));
} }
static bool find_symbol_in_section(const struct symsearch *syms, static bool find_symbol_in_section(const struct symsearch *syms,
...@@ -2170,7 +2188,7 @@ void *__symbol_get(const char *symbol) ...@@ -2170,7 +2188,7 @@ void *__symbol_get(const char *symbol)
sym = NULL; sym = NULL;
preempt_enable(); preempt_enable();
return sym ? (void *)sym->value : NULL; return sym ? (void *)kernel_symbol_value(sym) : NULL;
} }
EXPORT_SYMBOL_GPL(__symbol_get); EXPORT_SYMBOL_GPL(__symbol_get);
...@@ -2200,10 +2218,12 @@ static int verify_export_symbols(struct module *mod) ...@@ -2200,10 +2218,12 @@ static int verify_export_symbols(struct module *mod)
for (i = 0; i < ARRAY_SIZE(arr); i++) { for (i = 0; i < ARRAY_SIZE(arr); i++) {
for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) { for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) {
if (find_symbol(s->name, &owner, NULL, true, false)) { if (find_symbol(kernel_symbol_name(s), &owner, NULL,
true, false)) {
pr_err("%s: exports duplicate symbol %s" pr_err("%s: exports duplicate symbol %s"
" (owned by %s)\n", " (owned by %s)\n",
mod->name, s->name, module_name(owner)); mod->name, kernel_symbol_name(s),
module_name(owner));
return -ENOEXEC; return -ENOEXEC;
} }
} }
...@@ -2252,7 +2272,7 @@ static int simplify_symbols(struct module *mod, const struct load_info *info) ...@@ -2252,7 +2272,7 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
ksym = resolve_symbol_wait(mod, info, name); ksym = resolve_symbol_wait(mod, info, name);
/* Ok if resolved. */ /* Ok if resolved. */
if (ksym && !IS_ERR(ksym)) { if (ksym && !IS_ERR(ksym)) {
sym[i].st_value = ksym->value; sym[i].st_value = kernel_symbol_value(ksym);
break; break;
} }
...@@ -2516,7 +2536,7 @@ static int is_exported(const char *name, unsigned long value, ...@@ -2516,7 +2536,7 @@ static int is_exported(const char *name, unsigned long value,
ks = lookup_symbol(name, __start___ksymtab, __stop___ksymtab); ks = lookup_symbol(name, __start___ksymtab, __stop___ksymtab);
else else
ks = lookup_symbol(name, mod->syms, mod->syms + mod->num_syms); ks = lookup_symbol(name, mod->syms, mod->syms + mod->num_syms);
return ks != NULL && ks->value == value; return ks != NULL && kernel_symbol_value(ks) == value;
} }
/* As per nm */ /* As per nm */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment