Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aafcd5d7 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'x86-kaslr-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 relocation changes from Ingo Molnar:
 "This tree contains a single change, ELF relocation handling in C - one
  of the kernel randomization patches that makes sense even without
  randomization present upstream"

* 'x86-kaslr-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86, relocs: Move ELF relocation handling to C
parents 6832d965 a0215061
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -1716,9 +1716,10 @@ config X86_NEED_RELOCS
	depends on X86_32 && RELOCATABLE

config PHYSICAL_ALIGN
	hex "Alignment value to which kernel should be aligned" if X86_32
	hex "Alignment value to which kernel should be aligned"
	default "0x1000000"
	range 0x2000 0x1000000
	range 0x2000 0x1000000 if X86_32
	range 0x200000 0x1000000 if X86_64
	---help---
	  This value puts the alignment restrictions on physical address
	  where kernel is loaded and run from. Kernel is compiled for an
@@ -1736,6 +1737,9 @@ config PHYSICAL_ALIGN
	  end result is that kernel runs from a physical address meeting
	  above alignment restrictions.

	  On 32-bit this value must be a multiple of 0x2000. On 64-bit
	  this value must be a multiple of 0x200000.

	  Don't change this unless you know what you are doing.

config HOTPLUG_CPU
+4 −4
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@ endif
# e.g.: obj-y += foo_$(BITS).o
export BITS

ifdef CONFIG_X86_NEED_RELOCS
        LDFLAGS_vmlinux := --emit-relocs
endif

ifeq ($(CONFIG_X86_32),y)
        BITS := 32
        UTS_MACHINE := i386
@@ -25,10 +29,6 @@ ifeq ($(CONFIG_X86_32),y)
        KBUILD_AFLAGS += $(biarch)
        KBUILD_CFLAGS += $(biarch)

        ifdef CONFIG_RELOCATABLE
                LDFLAGS_vmlinux := --emit-relocs
        endif

        KBUILD_CFLAGS += -msoft-float -mregparm=3 -freg-struct-return

        # Never want PIC in a 32-bit kernel, prevent breakage with GCC built
+3 −28
Original line number Diff line number Diff line
@@ -181,8 +181,9 @@ relocated:
/*
 * Do the decompression, and jump to the new kernel..
 */
	leal	z_extract_offset_negative(%ebx), %ebp
				/* push arguments for decompress_kernel: */
	pushl	$z_output_len	/* decompressed length */
	leal	z_extract_offset_negative(%ebx), %ebp
	pushl	%ebp		/* output address */
	pushl	$z_input_len	/* input_len */
	leal	input_data(%ebx), %eax
@@ -191,33 +192,7 @@ relocated:
	pushl	%eax		/* heap area */
	pushl	%esi		/* real mode pointer */
	call	decompress_kernel
	addl	$20, %esp

#if CONFIG_RELOCATABLE
/*
 * Find the address of the relocations.
 */
	leal	z_output_len(%ebp), %edi

/*
 * Calculate the delta between where vmlinux was compiled to run
 * and where it was actually loaded.
 */
	movl	%ebp, %ebx
	subl	$LOAD_PHYSICAL_ADDR, %ebx
	jz	2f	/* Nothing to be done if loaded at compiled addr. */
/*
 * Process relocations.
 */

1:	subl	$4, %edi
	movl	(%edi), %ecx
	testl	%ecx, %ecx
	jz	2f
	addl	%ebx, -__PAGE_OFFSET(%ebx, %ecx)
	jmp	1b
2:
#endif
	addl	$24, %esp

/*
 * Jump to the decompressed kernel.
+1 −0
Original line number Diff line number Diff line
@@ -338,6 +338,7 @@ relocated:
	leaq	input_data(%rip), %rdx  /* input_data */
	movl	$z_input_len, %ecx	/* input_len */
	movq	%rbp, %r8		/* output target address */
	movq	$z_output_len, %r9	/* decompressed length */
	call	decompress_kernel
	popq	%rsi

+76 −1
Original line number Diff line number Diff line
@@ -271,6 +271,79 @@ static void error(char *x)
		asm("hlt");
}

#if CONFIG_X86_NEED_RELOCS
static void handle_relocations(void *output, unsigned long output_len)
{
	int *reloc;
	unsigned long delta, map, ptr;
	unsigned long min_addr = (unsigned long)output;
	unsigned long max_addr = min_addr + output_len;

	/*
	 * Calculate the delta between where vmlinux was linked to load
	 * and where it was actually loaded.
	 */
	delta = min_addr - LOAD_PHYSICAL_ADDR;
	if (!delta) {
		debug_putstr("No relocation needed... ");
		return;
	}
	debug_putstr("Performing relocations... ");

	/*
	 * The kernel contains a table of relocation addresses. Those
	 * addresses have the final load address of the kernel in virtual
	 * memory. We are currently working in the self map. So we need to
	 * create an adjustment for kernel memory addresses to the self map.
	 * This will involve subtracting out the base address of the kernel.
	 */
	map = delta - __START_KERNEL_map;

	/*
	 * Process relocations: 32 bit relocations first then 64 bit after.
	 * Two sets of binary relocations are added to the end of the kernel
	 * before compression. Each relocation table entry is the kernel
	 * address of the location which needs to be updated stored as a
	 * 32-bit value which is sign extended to 64 bits.
	 *
	 * Format is:
	 *
	 * kernel bits...
	 * 0 - zero terminator for 64 bit relocations
	 * 64 bit relocation repeated
	 * 0 - zero terminator for 32 bit relocations
	 * 32 bit relocation repeated
	 *
	 * So we work backwards from the end of the decompressed image.
	 */
	for (reloc = output + output_len - sizeof(*reloc); *reloc; reloc--) {
		int extended = *reloc;
		extended += map;

		ptr = (unsigned long)extended;
		if (ptr < min_addr || ptr > max_addr)
			error("32-bit relocation outside of kernel!\n");

		*(uint32_t *)ptr += delta;
	}
#ifdef CONFIG_X86_64
	for (reloc--; *reloc; reloc--) {
		long extended = *reloc;
		extended += map;

		ptr = (unsigned long)extended;
		if (ptr < min_addr || ptr > max_addr)
			error("64-bit relocation outside of kernel!\n");

		*(uint64_t *)ptr += delta;
	}
#endif
}
#else
static inline void handle_relocations(void *output, unsigned long output_len)
{ }
#endif

static void parse_elf(void *output)
{
#ifdef CONFIG_X86_64
@@ -325,7 +398,8 @@ static void parse_elf(void *output)
asmlinkage void decompress_kernel(void *rmode, memptr heap,
				  unsigned char *input_data,
				  unsigned long input_len,
				  unsigned char *output)
				  unsigned char *output,
				  unsigned long output_len)
{
	real_mode = rmode;

@@ -365,6 +439,7 @@ asmlinkage void decompress_kernel(void *rmode, memptr heap,
	debug_putstr("\nDecompressing Linux... ");
	decompress(input_data, input_len, NULL, NULL, output, NULL, error);
	parse_elf(output);
	handle_relocations(output, output_len);
	debug_putstr("done.\nBooting the kernel.\n");
	return;
}
Loading