lkml.org 
[lkml]   [2017]   [Sep]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
SubjectRe: module: use relative references for __ksymtab entries
From
Date

> On 9 Sep 2017, at 16:33, Thomas Garnier <thgarnie@google.com> wrote:
>
>> On Sat, Sep 9, 2017 at 6:08 AM, Jessica Yu <jeyu@kernel.org> wrote:
>> +++ Ard Biesheuvel [19/08/17 19:10 +0100]:
>>
>>> 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 dependend on whether
>>> CONFIG_HAVE_ARCH_PREL32_RELOCATIONS is defined.
>
> Good idea, I assume we may still get relocations given the compiler is
> pretty bad at optimizing (_ptr - .) but I might be wrong.

The point is that the resulting place relative static relocations are fixed up at link time (and discarded) rather than being converted into dynamic R_xxx_RELATIVE relocations that require fixing up at runtime.

The compiler rarely emits place relative relocations, in my experience, but this is probably highly arch and CFLAGS dependent. I am not sure what you mean by 'optimizing' (<sym> - . ): if such expressions are emitted into asm, it is up to the assembler to fully resolve the expression or emit a place relative relocation, depending in whether <sym> has external linkage.

> Anyway, the
> size decrease is great and we can ignore these relocations if need be.

Yes, if you are using --emit-relocs and mangling static relocations manually (which I suppose is the case for x86), you can add any resulting place relative relocations to the ignore list, although I would assume they are already on it.

Thanks.

>>>
>>> Cc: Jessica Yu <jeyu@kernel.org>
>>> Cc: Arnd Bergmann <arnd@arndb.de>
>>> Cc: Andrew Morton <akpm@linux-foundation.org>
>>> Cc: Ingo Molnar <mingo@kernel.org>
>>> Cc: Kees Cook <keescook@chromium.org>
>>> Cc: Thomas Garnier <thgarnie@google.com>
>>> Cc: Nicolas Pitre <nico@linaro.org>
>>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>>
>>
>> I ran this through some light testing and the relative ksymtab
>> references seem to work just fine:
>>
>> Acked-by: Jessica Yu <jeyu@kernel.org> (for module changes)
>>
>>
>>> arch/x86/include/asm/Kbuild | 1 +
>>> arch/x86/include/asm/export.h | 4 --
>>> include/asm-generic/export.h | 12 +++++-
>>> include/linux/compiler.h | 11 +++++
>>> include/linux/export.h | 45 +++++++++++++++-----
>>> kernel/module.c | 33 +++++++++++---
>>> 6 files changed, 83 insertions(+), 23 deletions(-)
>>>
>>> diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild
>>> index 5d6a53fd7521..3e8a88dcaa1d 100644
>>> --- a/arch/x86/include/asm/Kbuild
>>> +++ b/arch/x86/include/asm/Kbuild
>>> @@ -9,5 +9,6 @@ generated-y += xen-hypercalls.h
>>> generic-y += clkdev.h
>>> generic-y += dma-contiguous.h
>>> generic-y += early_ioremap.h
>>> +generic-y += export.h
>>> generic-y += mcs_spinlock.h
>>> generic-y += mm-arch-hooks.h
>>> diff --git a/arch/x86/include/asm/export.h b/arch/x86/include/asm/export.h
>>> deleted file mode 100644
>>> index 138de56b13eb..000000000000
>>> --- a/arch/x86/include/asm/export.h
>>> +++ /dev/null
>>> @@ -1,4 +0,0 @@
>>> -#ifdef CONFIG_64BIT
>>> -#define KSYM_ALIGN 16
>>> -#endif
>>> -#include <asm-generic/export.h>
>>> diff --git a/include/asm-generic/export.h b/include/asm-generic/export.h
>>> index 719db1968d81..97ce606459ae 100644
>>> --- a/include/asm-generic/export.h
>>> +++ b/include/asm-generic/export.h
>>> @@ -5,12 +5,10 @@
>>> #define KSYM_FUNC(x) x
>>> #endif
>>> #ifdef CONFIG_64BIT
>>> -#define __put .quad
>>> #ifndef KSYM_ALIGN
>>> #define KSYM_ALIGN 8
>>> #endif
>>> #else
>>> -#define __put .long
>>> #ifndef KSYM_ALIGN
>>> #define KSYM_ALIGN 4
>>> #endif
>>> @@ -25,6 +23,16 @@
>>> #define KSYM(name) name
>>> #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,
>>> * since we immediately emit into those sections anyway.
>>> diff --git a/include/linux/compiler.h b/include/linux/compiler.h
>>> index eca8ad75e28b..363fb3e734ec 100644
>>> --- a/include/linux/compiler.h
>>> +++ b/include/linux/compiler.h
>>> @@ -590,4 +590,15 @@ static __always_inline void
>>> __write_once_size(volatile void *p, void *res, int s
>>> (_________p1); \
>>> })
>>>
>>> +/*
>>> + * 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
>>> + * visibile to the compiler.
>>> + */
>>> +#define __ADDRESSABLE(sym) \
>>> + static void *__attribute__((section(".discard.text"), used)) \
>>> + __PASTE(__discard_##sym, __LINE__)(void) \
>>> + { return (void *)&sym; } \
>>> +
>>> #endif /* __LINUX_COMPILER_H */
>>> diff --git a/include/linux/export.h b/include/linux/export.h
>>> index 1a1dfdb2a5c6..156b974181a4 100644
>>> --- a/include/linux/export.h
>>> +++ b/include/linux/export.h
>>> @@ -24,12 +24,6 @@
>>> #define VMLINUX_SYMBOL_STR(x) __VMLINUX_SYMBOL_STR(x)
>>>
>>> #ifndef __ASSEMBLY__
>>> -struct kernel_symbol
>>> -{
>>> - unsigned long value;
>>> - const char *name;
>>> -};
>>> -
>>> #ifdef MODULE
>>> extern struct module __this_module;
>>> #define THIS_MODULE (&__this_module)
>>> @@ -60,17 +54,46 @@ extern struct module __this_module;
>>> #define __CRC_SYMBOL(sym, sec)
>>> #endif
>>>
>>> +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
>>> +/*
>>> + * 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" \
>>> + VMLINUX_SYMBOL_STR(__ksymtab_##sym) ": \n" \
>>> + " .long " VMLINUX_SYMBOL_STR(sym) "- . \n" \
>>> + " .long " VMLINUX_SYMBOL_STR(__kstrtab_##sym) "- .\n" \
>>> + " .previous \n")
>>> +
>>> +struct kernel_symbol {
>>> + signed int value_offset;
>>> + signed 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 */
>>> #define ___EXPORT_SYMBOL(sym, sec) \
>>> extern typeof(sym) sym; \
>>> __CRC_SYMBOL(sym, sec) \
>>> static const char __kstrtab_##sym[] \
>>> - __attribute__((section("__ksymtab_strings"), aligned(1))) \
>>> + __attribute__((section("__ksymtab_strings"), used, aligned(1))) \
>>> = VMLINUX_SYMBOL_STR(sym); \
>>> - static const struct kernel_symbol __ksymtab_##sym \
>>> - __used \
>>> - __attribute__((section("___ksymtab" sec "+" #sym), used)) \
>>> - = { (unsigned long)&sym, __kstrtab_##sym }
>>> + __KSYMTAB_ENTRY(sym, sec)
>>>
>>> #if defined(__KSYM_DEPS__)
>>>
>>> diff --git a/kernel/module.c b/kernel/module.c
>>> index 40f983cbea81..a45423dcc32d 100644
>>> --- a/kernel/module.c
>>> +++ b/kernel/module.c
>>> @@ -539,12 +539,31 @@ static bool check_symbol(const struct symsearch
>>> *syms,
>>> return true;
>>> }
>>>
>>> +static unsigned long kernel_symbol_value(const struct kernel_symbol *sym)
>>> +{
>>> +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
>>> + return (unsigned long)&sym->value_offset + 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 (const char *)((unsigned long)&sym->name_offset +
>>> + sym->name_offset);
>>> +#else
>>> + return sym->name;
>>> +#endif
>>> +}
>>> +
>>> static int cmp_name(const void *va, const void *vb)
>>> {
>>> const char *a;
>>> const struct kernel_symbol *b;
>>> 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,
>>> @@ -2190,7 +2209,7 @@ void *__symbol_get(const char *symbol)
>>> sym = NULL;
>>> preempt_enable();
>>>
>>> - return sym ? (void *)sym->value : NULL;
>>> + return sym ? (void *)kernel_symbol_value(sym) : NULL;
>>> }
>>> EXPORT_SYMBOL_GPL(__symbol_get);
>>>
>>> @@ -2220,10 +2239,12 @@ static int verify_export_symbols(struct module
>>> *mod)
>>>
>>> for (i = 0; i < ARRAY_SIZE(arr); i++) {
>>> 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"
>>> " (owned by %s)\n",
>>> - mod->name, s->name,
>>> module_name(owner));
>>> + mod->name, kernel_symbol_name(s),
>>> + module_name(owner));
>>> return -ENOEXEC;
>>> }
>>> }
>>> @@ -2272,7 +2293,7 @@ static int simplify_symbols(struct module *mod,
>>> const struct load_info *info)
>>> ksym = resolve_symbol_wait(mod, info, name);
>>> /* Ok if resolved. */
>>> if (ksym && !IS_ERR(ksym)) {
>>> - sym[i].st_value = ksym->value;
>>> + sym[i].st_value =
>>> kernel_symbol_value(ksym);
>>> break;
>>> }
>>>
>>> @@ -2532,7 +2553,7 @@ static int is_exported(const char *name, unsigned
>>> long value,
>>> ks = lookup_symbol(name, __start___ksymtab,
>>> __stop___ksymtab);
>>> else
>>> 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 */
>>> --
>>> 2.11.0
>>>
>>
>
>
>
> --
> Thomas

\
 
 \ /
  Last update: 2017-09-09 18:48    [W:0.103 / U:0.148 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site