Skip to content

Commit

Permalink
LoongArch: Add support for kernel relocation
Browse files Browse the repository at this point in the history
This config allows to compile kernel as PIE and to relocate it at any
virtual address at runtime: this paves the way to KASLR.

Runtime relocation is possible since relocation metadata are embedded
into the kernel.

Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Xi Ruoyao <xry111@xry111.site> # Use arch_initcall
Signed-off-by: Jinyang He <hejinyang@loongson.cn> # Provide la_abs relocation code
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
  • Loading branch information
Youling Tang authored and Huacai Chen committed Feb 25, 2023
1 parent 396233c commit d8da19f
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 2 deletions.
8 changes: 8 additions & 0 deletions arch/loongarch/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,14 @@ config PHYSICAL_START
specified in the "crashkernel=YM@XM" command line boot parameter
passed to the panic-ed kernel).

config RELOCATABLE
bool "Relocatable kernel"
help
This builds the kernel as a Position Independent Executable (PIE),
which retains all relocation metadata required, so as to relocate
the kernel binary at runtime to a different virtual address from
its link address.

config SECCOMP
bool "Enable seccomp to safely compute untrusted bytecode"
depends on PROC_FS
Expand Down
5 changes: 5 additions & 0 deletions arch/loongarch/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ KBUILD_AFLAGS_MODULE += -Wa,-mla-global-with-abs
KBUILD_CFLAGS_MODULE += -fplt -Wa,-mla-global-with-abs,-mla-local-with-abs
endif

ifeq ($(CONFIG_RELOCATABLE),y)
KBUILD_CFLAGS_KERNEL += -fPIE
LDFLAGS_vmlinux += -static -pie --no-dynamic-linker -z notext
endif

cflags-y += -ffreestanding
cflags-y += $(call cc-option, -mno-check-zero-division)

Expand Down
13 changes: 13 additions & 0 deletions arch/loongarch/include/asm/asmmacro.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,20 @@
.endm

.macro la_abs reg, sym
#ifndef CONFIG_RELOCATABLE
la.abs \reg, \sym
#else
766:
lu12i.w \reg, 0
ori \reg, \reg, 0
lu32i.d \reg, 0
lu52i.d \reg, \reg, 0
.pushsection ".la_abs", "aw", %progbits
768:
.dword 768b-766b
.dword \sym
.popsection
#endif
.endm

#endif /* _ASM_ASMMACRO_H */
16 changes: 16 additions & 0 deletions arch/loongarch/include/asm/setup.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,20 @@ extern void per_cpu_trap_init(int cpu);
extern void set_handler(unsigned long offset, void *addr, unsigned long len);
extern void set_merr_handler(unsigned long offset, void *addr, unsigned long len);

#ifdef CONFIG_RELOCATABLE

struct rela_la_abs {
long offset;
long symvalue;
};

extern long __la_abs_begin;
extern long __la_abs_end;
extern long __rela_dyn_begin;
extern long __rela_dyn_end;

extern void __init relocate_kernel(void);

#endif

#endif /* __SETUP_H */
2 changes: 2 additions & 0 deletions arch/loongarch/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ obj-$(CONFIG_NUMA) += numa.o

obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o

obj-$(CONFIG_RELOCATABLE) += relocate.o

obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o

Expand Down
4 changes: 4 additions & 0 deletions arch/loongarch/kernel/head.S
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ SYM_CODE_START(kernel_entry) # kernel entry point
PTR_ADD sp, sp, tp
set_saved_sp sp, t0, t1

#ifdef CONFIG_RELOCATABLE
bl relocate_kernel
#endif

bl start_kernel
ASM_BUG()

Expand Down
107 changes: 107 additions & 0 deletions arch/loongarch/kernel/relocate.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Support for Kernel relocation at boot time
*
* Copyright (C) 2023 Loongson Technology Corporation Limited
*/

#include <linux/elf.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/panic_notifier.h>
#include <asm/inst.h>
#include <asm/sections.h>
#include <asm/setup.h>

#define RELOCATED(x) ((void *)((long)x + reloc_offset))

static unsigned long reloc_offset;

static inline void __init relocate_relative(void)
{
Elf64_Rela *rela, *rela_end;
rela = (Elf64_Rela *)&__rela_dyn_begin;
rela_end = (Elf64_Rela *)&__rela_dyn_end;

for ( ; rela < rela_end; rela++) {
Elf64_Addr addr = rela->r_offset;
Elf64_Addr relocated_addr = rela->r_addend;

if (rela->r_info != R_LARCH_RELATIVE)
continue;

if (relocated_addr >= VMLINUX_LOAD_ADDRESS)
relocated_addr = (Elf64_Addr)RELOCATED(relocated_addr);

*(Elf64_Addr *)RELOCATED(addr) = relocated_addr;
}
}

static inline void __init relocate_absolute(void)
{
void *begin, *end;
struct rela_la_abs *p;

begin = &__la_abs_begin;
end = &__la_abs_end;

for (p = begin; (void *)p < end; p++) {
long v = p->symvalue;
uint32_t lu12iw, ori, lu32id, lu52id;
union loongarch_instruction *insn = (void *)p - p->offset;

lu12iw = (v >> 12) & 0xfffff;
ori = v & 0xfff;
lu32id = (v >> 32) & 0xfffff;
lu52id = v >> 52;

insn[0].reg1i20_format.immediate = lu12iw;
insn[1].reg2i12_format.immediate = ori;
insn[2].reg1i20_format.immediate = lu32id;
insn[3].reg2i12_format.immediate = lu52id;
}
}

void __init relocate_kernel(void)
{
reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;

if (reloc_offset)
relocate_relative();

relocate_absolute();
}

/*
* Show relocation information on panic.
*/
static void show_kernel_relocation(const char *level)
{
if (reloc_offset > 0) {
printk(level);
pr_cont("Kernel relocated by 0x%lx\n", reloc_offset);
pr_cont(" .text @ 0x%px\n", _text);
pr_cont(" .data @ 0x%px\n", _sdata);
pr_cont(" .bss @ 0x%px\n", __bss_start);
}
}

static int kernel_location_notifier_fn(struct notifier_block *self,
unsigned long v, void *p)
{
show_kernel_relocation(KERN_EMERG);
return NOTIFY_DONE;
}

static struct notifier_block kernel_location_notifier = {
.notifier_call = kernel_location_notifier_fn
};

static int __init register_kernel_offset_dumper(void)
{
atomic_notifier_chain_register(&panic_notifier_list,
&kernel_location_notifier);
return 0;
}

arch_initcall(register_kernel_offset_dumper);
20 changes: 18 additions & 2 deletions arch/loongarch/kernel/vmlinux.lds.S
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,21 @@ SECTIONS
__alt_instructions_end = .;
}

#ifdef CONFIG_RELOCATABLE
. = ALIGN(8);
.la_abs : AT(ADDR(.la_abs) - LOAD_OFFSET) {
__la_abs_begin = .;
*(.la_abs)
__la_abs_end = .;
}
#endif

.got : ALIGN(16) { *(.got) }
.plt : ALIGN(16) { *(.plt) }
.got.plt : ALIGN(16) { *(.got.plt) }

.data.rel : { *(.data.rel*) }

. = ALIGN(PECOFF_SEGMENT_ALIGN);
__init_begin = .;
__inittext_begin = .;
Expand All @@ -93,8 +104,6 @@ SECTIONS
PERCPU_SECTION(1 << CONFIG_L1_CACHE_SHIFT)
#endif

.rela.dyn : ALIGN(8) { *(.rela.dyn) *(.rela*) }

.init.bss : {
*(.init.bss)
}
Expand All @@ -107,6 +116,12 @@ SECTIONS
RO_DATA(4096)
RW_DATA(1 << CONFIG_L1_CACHE_SHIFT, PAGE_SIZE, THREAD_SIZE)

.rela.dyn : ALIGN(8) {
__rela_dyn_begin = .;
*(.rela.dyn) *(.rela*)
__rela_dyn_end = .;
}

.sdata : {
*(.sdata)
}
Expand All @@ -133,6 +148,7 @@ SECTIONS

DISCARDS
/DISCARD/ : {
*(.dynamic .dynsym .dynstr .hash .gnu.hash)
*(.gnu.attributes)
*(.options)
*(.eh_frame)
Expand Down

0 comments on commit d8da19f

Please sign in to comment.