Commit 71810db2 authored by Ard Biesheuvel's avatar Ard Biesheuvel Committed by Linus Torvalds

modversions: treat symbol CRCs as 32 bit quantities

The modversion symbol CRCs are emitted as ELF symbols, which allows us
to easily populate the kcrctab sections by relying on the linker to
associate each kcrctab slot with the correct value.

This has a couple of downsides:

 - Given that the CRCs are treated as memory addresses, we waste 4 bytes
   for each CRC on 64 bit architectures,

 - On architectures that support runtime relocation, a R_<arch>_RELATIVE
   relocation entry is emitted for each CRC value, which identifies it
   as a quantity that requires fixing up based on the actual runtime
   load offset of the kernel. This results in corrupted CRCs unless we
   explicitly undo the fixup (and this is currently being handled in the
   core module code)

 - Such runtime relocation entries take up 24 bytes of __init space
   each, resulting in a x8 overhead in [uncompressed] kernel size for
   CRCs.

Switching to explicit 32 bit values on 64 bit architectures fixes most
of these issues, given that 32 bit values are not treated as quantities
that require fixing up based on the actual runtime load offset.  Note
that on some ELF64 architectures [such as PPC64], these 32-bit values
are still emitted as [absolute] runtime relocatable quantities, even if
the value resolves to a build time constant.  Since relative relocations
are always resolved at build time, this patch enables MODULE_REL_CRCS on
powerpc when CONFIG_RELOCATABLE=y, which turns the absolute CRC
references into relative references into .rodata where the actual CRC
value is stored.

So redefine all CRC fields and variables as u32, and redefine the
__CRC_SYMBOL() macro for 64 bit builds to emit the CRC reference using
inline assembler (which is necessary since 64-bit C code cannot use
32-bit types to hold memory addresses, even if they are ultimately
resolved using values that do not exceed 0xffffffff).  To avoid
potential problems with legacy 32-bit architectures using legacy
toolchains, the equivalent C definition of the kcrctab entry is retained
for 32-bit architectures.

Note that this mostly reverts commit d4703aef ("module: handle ppc64
relocating kcrctabs when CONFIG_RELOCATABLE=y")
Acked-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Signed-off-by: default avatarArd Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 56067812
...@@ -484,6 +484,7 @@ config RELOCATABLE ...@@ -484,6 +484,7 @@ config RELOCATABLE
bool "Build a relocatable kernel" bool "Build a relocatable kernel"
depends on (PPC64 && !COMPILE_TEST) || (FLATMEM && (44x || FSL_BOOKE)) depends on (PPC64 && !COMPILE_TEST) || (FLATMEM && (44x || FSL_BOOKE))
select NONSTATIC_KERNEL select NONSTATIC_KERNEL
select MODULE_REL_CRCS if MODVERSIONS
help help
This builds a kernel image that is capable of running at the This builds a kernel image that is capable of running at the
location the kernel is loaded at. For ppc32, there is no any location the kernel is loaded at. For ppc32, there is no any
......
...@@ -90,9 +90,5 @@ static inline int module_finalize_ftrace(struct module *mod, const Elf_Shdr *sec ...@@ -90,9 +90,5 @@ static inline int module_finalize_ftrace(struct module *mod, const Elf_Shdr *sec
} }
#endif #endif
#if defined(CONFIG_MODVERSIONS) && defined(CONFIG_PPC64)
#define ARCH_RELOCATES_KCRCTAB
#define reloc_start PHYSICAL_START
#endif
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_MODULE_H */ #endif /* _ASM_POWERPC_MODULE_H */
...@@ -286,14 +286,6 @@ static void dedotify_versions(struct modversion_info *vers, ...@@ -286,14 +286,6 @@ static void dedotify_versions(struct modversion_info *vers,
for (end = (void *)vers + size; vers < end; vers++) for (end = (void *)vers + size; vers < end; vers++)
if (vers->name[0] == '.') { if (vers->name[0] == '.') {
memmove(vers->name, vers->name+1, strlen(vers->name)); memmove(vers->name, vers->name+1, strlen(vers->name));
#ifdef ARCH_RELOCATES_KCRCTAB
/* The TOC symbol has no CRC computed. To avoid CRC
* check failing, we must force it to the expected
* value (see CRC check in module.c).
*/
if (!strcmp(vers->name, "TOC."))
vers->crc = -(unsigned long)reloc_start;
#endif
} }
} }
......
...@@ -9,18 +9,15 @@ ...@@ -9,18 +9,15 @@
#ifndef KSYM_ALIGN #ifndef KSYM_ALIGN
#define KSYM_ALIGN 8 #define KSYM_ALIGN 8
#endif #endif
#ifndef KCRC_ALIGN
#define KCRC_ALIGN 8
#endif
#else #else
#define __put .long #define __put .long
#ifndef KSYM_ALIGN #ifndef KSYM_ALIGN
#define KSYM_ALIGN 4 #define KSYM_ALIGN 4
#endif #endif
#endif
#ifndef KCRC_ALIGN #ifndef KCRC_ALIGN
#define KCRC_ALIGN 4 #define KCRC_ALIGN 4
#endif #endif
#endif
#ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX #ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX
#define KSYM(name) _##name #define KSYM(name) _##name
...@@ -52,7 +49,11 @@ KSYM(__kstrtab_\name): ...@@ -52,7 +49,11 @@ KSYM(__kstrtab_\name):
.section ___kcrctab\sec+\name,"a" .section ___kcrctab\sec+\name,"a"
.balign KCRC_ALIGN .balign KCRC_ALIGN
KSYM(__kcrctab_\name): KSYM(__kcrctab_\name):
__put KSYM(__crc_\name) #if defined(CONFIG_MODULE_REL_CRCS)
.long KSYM(__crc_\name) - .
#else
.long KSYM(__crc_\name)
#endif
.weak KSYM(__crc_\name) .weak KSYM(__crc_\name)
.previous .previous
#endif #endif
......
...@@ -43,6 +43,13 @@ extern struct module __this_module; ...@@ -43,6 +43,13 @@ extern struct module __this_module;
#ifdef CONFIG_MODVERSIONS #ifdef CONFIG_MODVERSIONS
/* Mark the CRC weak since genksyms apparently decides not to /* Mark the CRC weak since genksyms apparently decides not to
* generate a checksums for some symbols */ * generate a checksums for some symbols */
#if defined(CONFIG_MODULE_REL_CRCS)
#define __CRC_SYMBOL(sym, sec) \
asm(" .section \"___kcrctab" sec "+" #sym "\", \"a\" \n" \
" .weak " VMLINUX_SYMBOL_STR(__crc_##sym) " \n" \
" .long " VMLINUX_SYMBOL_STR(__crc_##sym) " - . \n" \
" .previous \n");
#elif !defined(CONFIG_64BIT)
#define __CRC_SYMBOL(sym, sec) \ #define __CRC_SYMBOL(sym, sec) \
extern __visible void *__crc_##sym __attribute__((weak)); \ extern __visible void *__crc_##sym __attribute__((weak)); \
static const unsigned long __kcrctab_##sym \ static const unsigned long __kcrctab_##sym \
...@@ -50,6 +57,13 @@ extern struct module __this_module; ...@@ -50,6 +57,13 @@ extern struct module __this_module;
__attribute__((section("___kcrctab" sec "+" #sym), used)) \ __attribute__((section("___kcrctab" sec "+" #sym), used)) \
= (unsigned long) &__crc_##sym; = (unsigned long) &__crc_##sym;
#else #else
#define __CRC_SYMBOL(sym, sec) \
asm(" .section \"___kcrctab" sec "+" #sym "\", \"a\" \n" \
" .weak " VMLINUX_SYMBOL_STR(__crc_##sym) " \n" \
" .long " VMLINUX_SYMBOL_STR(__crc_##sym) " \n" \
" .previous \n");
#endif
#else
#define __CRC_SYMBOL(sym, sec) #define __CRC_SYMBOL(sym, sec)
#endif #endif
......
...@@ -346,7 +346,7 @@ struct module { ...@@ -346,7 +346,7 @@ struct module {
/* Exported symbols */ /* Exported symbols */
const struct kernel_symbol *syms; const struct kernel_symbol *syms;
const unsigned long *crcs; const s32 *crcs;
unsigned int num_syms; unsigned int num_syms;
/* Kernel parameters. */ /* Kernel parameters. */
...@@ -359,18 +359,18 @@ struct module { ...@@ -359,18 +359,18 @@ struct module {
/* GPL-only exported symbols. */ /* GPL-only exported symbols. */
unsigned int num_gpl_syms; unsigned int num_gpl_syms;
const struct kernel_symbol *gpl_syms; const struct kernel_symbol *gpl_syms;
const unsigned long *gpl_crcs; const s32 *gpl_crcs;
#ifdef CONFIG_UNUSED_SYMBOLS #ifdef CONFIG_UNUSED_SYMBOLS
/* unused exported symbols. */ /* unused exported symbols. */
const struct kernel_symbol *unused_syms; const struct kernel_symbol *unused_syms;
const unsigned long *unused_crcs; const s32 *unused_crcs;
unsigned int num_unused_syms; unsigned int num_unused_syms;
/* GPL-only, unused exported symbols. */ /* GPL-only, unused exported symbols. */
unsigned int num_unused_gpl_syms; unsigned int num_unused_gpl_syms;
const struct kernel_symbol *unused_gpl_syms; const struct kernel_symbol *unused_gpl_syms;
const unsigned long *unused_gpl_crcs; const s32 *unused_gpl_crcs;
#endif #endif
#ifdef CONFIG_MODULE_SIG #ifdef CONFIG_MODULE_SIG
...@@ -382,7 +382,7 @@ struct module { ...@@ -382,7 +382,7 @@ struct module {
/* symbols that will be GPL-only in the near future. */ /* symbols that will be GPL-only in the near future. */
const struct kernel_symbol *gpl_future_syms; const struct kernel_symbol *gpl_future_syms;
const unsigned long *gpl_future_crcs; const s32 *gpl_future_crcs;
unsigned int num_gpl_future_syms; unsigned int num_gpl_future_syms;
/* Exception table */ /* Exception table */
...@@ -523,7 +523,7 @@ struct module *find_module(const char *name); ...@@ -523,7 +523,7 @@ struct module *find_module(const char *name);
struct symsearch { struct symsearch {
const struct kernel_symbol *start, *stop; const struct kernel_symbol *start, *stop;
const unsigned long *crcs; const s32 *crcs;
enum { enum {
NOT_GPL_ONLY, NOT_GPL_ONLY,
GPL_ONLY, GPL_ONLY,
...@@ -539,7 +539,7 @@ struct symsearch { ...@@ -539,7 +539,7 @@ struct symsearch {
*/ */
const struct kernel_symbol *find_symbol(const char *name, const struct kernel_symbol *find_symbol(const char *name,
struct module **owner, struct module **owner,
const unsigned long **crc, const s32 **crc,
bool gplok, bool gplok,
bool warn); bool warn);
......
...@@ -389,16 +389,16 @@ extern const struct kernel_symbol __start___ksymtab_gpl[]; ...@@ -389,16 +389,16 @@ extern const struct kernel_symbol __start___ksymtab_gpl[];
extern const struct kernel_symbol __stop___ksymtab_gpl[]; extern const struct kernel_symbol __stop___ksymtab_gpl[];
extern const struct kernel_symbol __start___ksymtab_gpl_future[]; extern const struct kernel_symbol __start___ksymtab_gpl_future[];
extern const struct kernel_symbol __stop___ksymtab_gpl_future[]; extern const struct kernel_symbol __stop___ksymtab_gpl_future[];
extern const unsigned long __start___kcrctab[]; extern const s32 __start___kcrctab[];
extern const unsigned long __start___kcrctab_gpl[]; extern const s32 __start___kcrctab_gpl[];
extern const unsigned long __start___kcrctab_gpl_future[]; extern const s32 __start___kcrctab_gpl_future[];
#ifdef CONFIG_UNUSED_SYMBOLS #ifdef CONFIG_UNUSED_SYMBOLS
extern const struct kernel_symbol __start___ksymtab_unused[]; extern const struct kernel_symbol __start___ksymtab_unused[];
extern const struct kernel_symbol __stop___ksymtab_unused[]; extern const struct kernel_symbol __stop___ksymtab_unused[];
extern const struct kernel_symbol __start___ksymtab_unused_gpl[]; extern const struct kernel_symbol __start___ksymtab_unused_gpl[];
extern const struct kernel_symbol __stop___ksymtab_unused_gpl[]; extern const struct kernel_symbol __stop___ksymtab_unused_gpl[];
extern const unsigned long __start___kcrctab_unused[]; extern const s32 __start___kcrctab_unused[];
extern const unsigned long __start___kcrctab_unused_gpl[]; extern const s32 __start___kcrctab_unused_gpl[];
#endif #endif
#ifndef CONFIG_MODVERSIONS #ifndef CONFIG_MODVERSIONS
...@@ -497,7 +497,7 @@ struct find_symbol_arg { ...@@ -497,7 +497,7 @@ struct find_symbol_arg {
/* Output */ /* Output */
struct module *owner; struct module *owner;
const unsigned long *crc; const s32 *crc;
const struct kernel_symbol *sym; const struct kernel_symbol *sym;
}; };
...@@ -563,7 +563,7 @@ static bool find_symbol_in_section(const struct symsearch *syms, ...@@ -563,7 +563,7 @@ static bool find_symbol_in_section(const struct symsearch *syms,
* (optional) module which owns it. Needs preempt disabled or module_mutex. */ * (optional) module which owns it. Needs preempt disabled or module_mutex. */
const struct kernel_symbol *find_symbol(const char *name, const struct kernel_symbol *find_symbol(const char *name,
struct module **owner, struct module **owner,
const unsigned long **crc, const s32 **crc,
bool gplok, bool gplok,
bool warn) bool warn)
{ {
...@@ -1249,23 +1249,17 @@ static int try_to_force_load(struct module *mod, const char *reason) ...@@ -1249,23 +1249,17 @@ static int try_to_force_load(struct module *mod, const char *reason)
} }
#ifdef CONFIG_MODVERSIONS #ifdef CONFIG_MODVERSIONS
/* If the arch applies (non-zero) relocations to kernel kcrctab, unapply it. */
static unsigned long maybe_relocated(unsigned long crc, static u32 resolve_rel_crc(const s32 *crc)
const struct module *crc_owner)
{ {
#ifdef ARCH_RELOCATES_KCRCTAB return *(u32 *)((void *)crc + *crc);
if (crc_owner == NULL)
return crc - (unsigned long)reloc_start;
#endif
return crc;
} }
static int check_version(Elf_Shdr *sechdrs, static int check_version(Elf_Shdr *sechdrs,
unsigned int versindex, unsigned int versindex,
const char *symname, const char *symname,
struct module *mod, struct module *mod,
const unsigned long *crc, const s32 *crc)
const struct module *crc_owner)
{ {
unsigned int i, num_versions; unsigned int i, num_versions;
struct modversion_info *versions; struct modversion_info *versions;
...@@ -1283,13 +1277,19 @@ static int check_version(Elf_Shdr *sechdrs, ...@@ -1283,13 +1277,19 @@ static int check_version(Elf_Shdr *sechdrs,
/ sizeof(struct modversion_info); / sizeof(struct modversion_info);
for (i = 0; i < num_versions; i++) { for (i = 0; i < num_versions; i++) {
u32 crcval;
if (strcmp(versions[i].name, symname) != 0) if (strcmp(versions[i].name, symname) != 0)
continue; continue;
if (versions[i].crc == maybe_relocated(*crc, crc_owner)) if (IS_ENABLED(CONFIG_MODULE_REL_CRCS))
crcval = resolve_rel_crc(crc);
else
crcval = *crc;
if (versions[i].crc == crcval)
return 1; return 1;
pr_debug("Found checksum %lX vs module %lX\n", pr_debug("Found checksum %X vs module %lX\n",
maybe_relocated(*crc, crc_owner), versions[i].crc); crcval, versions[i].crc);
goto bad_version; goto bad_version;
} }
...@@ -1307,7 +1307,7 @@ static inline int check_modstruct_version(Elf_Shdr *sechdrs, ...@@ -1307,7 +1307,7 @@ static inline int check_modstruct_version(Elf_Shdr *sechdrs,
unsigned int versindex, unsigned int versindex,
struct module *mod) struct module *mod)
{ {
const unsigned long *crc; const s32 *crc;
/* /*
* Since this should be found in kernel (which can't be removed), no * Since this should be found in kernel (which can't be removed), no
...@@ -1321,8 +1321,7 @@ static inline int check_modstruct_version(Elf_Shdr *sechdrs, ...@@ -1321,8 +1321,7 @@ static inline int check_modstruct_version(Elf_Shdr *sechdrs,
} }
preempt_enable(); preempt_enable();
return check_version(sechdrs, versindex, return check_version(sechdrs, versindex,
VMLINUX_SYMBOL_STR(module_layout), mod, crc, VMLINUX_SYMBOL_STR(module_layout), mod, crc);
NULL);
} }
/* First part is kernel version, which we ignore if module has crcs. */ /* First part is kernel version, which we ignore if module has crcs. */
...@@ -1340,8 +1339,7 @@ static inline int check_version(Elf_Shdr *sechdrs, ...@@ -1340,8 +1339,7 @@ static inline int check_version(Elf_Shdr *sechdrs,
unsigned int versindex, unsigned int versindex,
const char *symname, const char *symname,
struct module *mod, struct module *mod,
const unsigned long *crc, const s32 *crc)
const struct module *crc_owner)
{ {
return 1; return 1;
} }
...@@ -1368,7 +1366,7 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod, ...@@ -1368,7 +1366,7 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod,
{ {
struct module *owner; struct module *owner;
const struct kernel_symbol *sym; const struct kernel_symbol *sym;
const unsigned long *crc; const s32 *crc;
int err; int err;
/* /*
...@@ -1383,8 +1381,7 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod, ...@@ -1383,8 +1381,7 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod,
if (!sym) if (!sym)
goto unlock; goto unlock;
if (!check_version(info->sechdrs, info->index.vers, name, mod, crc, if (!check_version(info->sechdrs, info->index.vers, name, mod, crc)) {
owner)) {
sym = ERR_PTR(-EINVAL); sym = ERR_PTR(-EINVAL);
goto getname; goto getname;
} }
......
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