aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2013-07-08 12:15:17 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2013-08-08 00:00:04 -0400
commita02150610776f66b40257624822a879311592bb2 (patch)
treea14370b350bfead0b692d2b6a4b74f426c17024d /arch/x86
parentc095ba7224d8edc71dcef0d655911399a8bd4a3f (diff)
x86, relocs: Move ELF relocation handling to C
Moves the relocation handling into C, after decompression. This requires that the decompressed size is passed to the decompression routine as well so that relocations can be found. Only kernels that need relocation support will use the code (currently just x86_32), but this is laying the ground work for 64-bit using it in support of KASLR. Based on work by Neill Clift and Michael Davidson. Signed-off-by: Kees Cook <keescook@chromium.org> Link: http://lkml.kernel.org/r/20130708161517.GA4832@www.outflux.net Acked-by: Zhang Yanfei <zhangyanfei@cn.fujitsu.com> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/Kconfig8
-rw-r--r--arch/x86/Makefile8
-rw-r--r--arch/x86/boot/compressed/head_32.S31
-rw-r--r--arch/x86/boot/compressed/head_64.S1
-rw-r--r--arch/x86/boot/compressed/misc.c77
-rw-r--r--arch/x86/include/asm/page_32_types.h2
-rw-r--r--arch/x86/include/asm/page_64_types.h5
-rw-r--r--arch/x86/include/asm/page_types.h5
8 files changed, 97 insertions, 40 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index b32ebf92b0ce..8002668d60a1 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1716,9 +1716,10 @@ config X86_NEED_RELOCS
1716 depends on X86_32 && RELOCATABLE 1716 depends on X86_32 && RELOCATABLE
1717 1717
1718config PHYSICAL_ALIGN 1718config PHYSICAL_ALIGN
1719 hex "Alignment value to which kernel should be aligned" if X86_32 1719 hex "Alignment value to which kernel should be aligned"
1720 default "0x1000000" 1720 default "0x1000000"
1721 range 0x2000 0x1000000 1721 range 0x2000 0x1000000 if X86_32
1722 range 0x200000 0x1000000 if X86_64
1722 ---help--- 1723 ---help---
1723 This value puts the alignment restrictions on physical address 1724 This value puts the alignment restrictions on physical address
1724 where kernel is loaded and run from. Kernel is compiled for an 1725 where kernel is loaded and run from. Kernel is compiled for an
@@ -1736,6 +1737,9 @@ config PHYSICAL_ALIGN
1736 end result is that kernel runs from a physical address meeting 1737 end result is that kernel runs from a physical address meeting
1737 above alignment restrictions. 1738 above alignment restrictions.
1738 1739
1740 On 32-bit this value must be a multiple of 0x2000. On 64-bit
1741 this value must be a multiple of 0x200000.
1742
1739 Don't change this unless you know what you are doing. 1743 Don't change this unless you know what you are doing.
1740 1744
1741config HOTPLUG_CPU 1745config HOTPLUG_CPU
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 07639c656fcd..41250fb33985 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -16,6 +16,10 @@ endif
16# e.g.: obj-y += foo_$(BITS).o 16# e.g.: obj-y += foo_$(BITS).o
17export BITS 17export BITS
18 18
19ifdef CONFIG_X86_NEED_RELOCS
20 LDFLAGS_vmlinux := --emit-relocs
21endif
22
19ifeq ($(CONFIG_X86_32),y) 23ifeq ($(CONFIG_X86_32),y)
20 BITS := 32 24 BITS := 32
21 UTS_MACHINE := i386 25 UTS_MACHINE := i386
@@ -25,10 +29,6 @@ ifeq ($(CONFIG_X86_32),y)
25 KBUILD_AFLAGS += $(biarch) 29 KBUILD_AFLAGS += $(biarch)
26 KBUILD_CFLAGS += $(biarch) 30 KBUILD_CFLAGS += $(biarch)
27 31
28 ifdef CONFIG_RELOCATABLE
29 LDFLAGS_vmlinux := --emit-relocs
30 endif
31
32 KBUILD_CFLAGS += -msoft-float -mregparm=3 -freg-struct-return 32 KBUILD_CFLAGS += -msoft-float -mregparm=3 -freg-struct-return
33 33
34 # Never want PIC in a 32-bit kernel, prevent breakage with GCC built 34 # Never want PIC in a 32-bit kernel, prevent breakage with GCC built
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 1e3184f6072f..5d6f6891b188 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -181,8 +181,9 @@ relocated:
181/* 181/*
182 * Do the decompression, and jump to the new kernel.. 182 * Do the decompression, and jump to the new kernel..
183 */ 183 */
184 leal z_extract_offset_negative(%ebx), %ebp
185 /* push arguments for decompress_kernel: */ 184 /* push arguments for decompress_kernel: */
185 pushl $z_output_len /* decompressed length */
186 leal z_extract_offset_negative(%ebx), %ebp
186 pushl %ebp /* output address */ 187 pushl %ebp /* output address */
187 pushl $z_input_len /* input_len */ 188 pushl $z_input_len /* input_len */
188 leal input_data(%ebx), %eax 189 leal input_data(%ebx), %eax
@@ -191,33 +192,7 @@ relocated:
191 pushl %eax /* heap area */ 192 pushl %eax /* heap area */
192 pushl %esi /* real mode pointer */ 193 pushl %esi /* real mode pointer */
193 call decompress_kernel 194 call decompress_kernel
194 addl $20, %esp 195 addl $24, %esp
195
196#if CONFIG_RELOCATABLE
197/*
198 * Find the address of the relocations.
199 */
200 leal z_output_len(%ebp), %edi
201
202/*
203 * Calculate the delta between where vmlinux was compiled to run
204 * and where it was actually loaded.
205 */
206 movl %ebp, %ebx
207 subl $LOAD_PHYSICAL_ADDR, %ebx
208 jz 2f /* Nothing to be done if loaded at compiled addr. */
209/*
210 * Process relocations.
211 */
212
2131: subl $4, %edi
214 movl (%edi), %ecx
215 testl %ecx, %ecx
216 jz 2f
217 addl %ebx, -__PAGE_OFFSET(%ebx, %ecx)
218 jmp 1b
2192:
220#endif
221 196
222/* 197/*
223 * Jump to the decompressed kernel. 198 * Jump to the decompressed kernel.
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 06e71c2c16bf..c337422b575d 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -338,6 +338,7 @@ relocated:
338 leaq input_data(%rip), %rdx /* input_data */ 338 leaq input_data(%rip), %rdx /* input_data */
339 movl $z_input_len, %ecx /* input_len */ 339 movl $z_input_len, %ecx /* input_len */
340 movq %rbp, %r8 /* output target address */ 340 movq %rbp, %r8 /* output target address */
341 movq $z_output_len, %r9 /* decompressed length */
341 call decompress_kernel 342 call decompress_kernel
342 popq %rsi 343 popq %rsi
343 344
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index 0319c88290a5..434f077d2c4d 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -271,6 +271,79 @@ static void error(char *x)
271 asm("hlt"); 271 asm("hlt");
272} 272}
273 273
274#if CONFIG_X86_NEED_RELOCS
275static void handle_relocations(void *output, unsigned long output_len)
276{
277 int *reloc;
278 unsigned long delta, map, ptr;
279 unsigned long min_addr = (unsigned long)output;
280 unsigned long max_addr = min_addr + output_len;
281
282 /*
283 * Calculate the delta between where vmlinux was linked to load
284 * and where it was actually loaded.
285 */
286 delta = min_addr - LOAD_PHYSICAL_ADDR;
287 if (!delta) {
288 debug_putstr("No relocation needed... ");
289 return;
290 }
291 debug_putstr("Performing relocations... ");
292
293 /*
294 * The kernel contains a table of relocation addresses. Those
295 * addresses have the final load address of the kernel in virtual
296 * memory. We are currently working in the self map. So we need to
297 * create an adjustment for kernel memory addresses to the self map.
298 * This will involve subtracting out the base address of the kernel.
299 */
300 map = delta - __START_KERNEL_map;
301
302 /*
303 * Process relocations: 32 bit relocations first then 64 bit after.
304 * Two sets of binary relocations are added to the end of the kernel
305 * before compression. Each relocation table entry is the kernel
306 * address of the location which needs to be updated stored as a
307 * 32-bit value which is sign extended to 64 bits.
308 *
309 * Format is:
310 *
311 * kernel bits...
312 * 0 - zero terminator for 64 bit relocations
313 * 64 bit relocation repeated
314 * 0 - zero terminator for 32 bit relocations
315 * 32 bit relocation repeated
316 *
317 * So we work backwards from the end of the decompressed image.
318 */
319 for (reloc = output + output_len - sizeof(*reloc); *reloc; reloc--) {
320 int extended = *reloc;
321 extended += map;
322
323 ptr = (unsigned long)extended;
324 if (ptr < min_addr || ptr > max_addr)
325 error("32-bit relocation outside of kernel!\n");
326
327 *(uint32_t *)ptr += delta;
328 }
329#ifdef CONFIG_X86_64
330 for (reloc--; *reloc; reloc--) {
331 long extended = *reloc;
332 extended += map;
333
334 ptr = (unsigned long)extended;
335 if (ptr < min_addr || ptr > max_addr)
336 error("64-bit relocation outside of kernel!\n");
337
338 *(uint64_t *)ptr += delta;
339 }
340#endif
341}
342#else
343static inline void handle_relocations(void *output, unsigned long output_len)
344{ }
345#endif
346
274static void parse_elf(void *output) 347static void parse_elf(void *output)
275{ 348{
276#ifdef CONFIG_X86_64 349#ifdef CONFIG_X86_64
@@ -325,7 +398,8 @@ static void parse_elf(void *output)
325asmlinkage void decompress_kernel(void *rmode, memptr heap, 398asmlinkage void decompress_kernel(void *rmode, memptr heap,
326 unsigned char *input_data, 399 unsigned char *input_data,
327 unsigned long input_len, 400 unsigned long input_len,
328 unsigned char *output) 401 unsigned char *output,
402 unsigned long output_len)
329{ 403{
330 real_mode = rmode; 404 real_mode = rmode;
331 405
@@ -365,6 +439,7 @@ asmlinkage void decompress_kernel(void *rmode, memptr heap,
365 debug_putstr("\nDecompressing Linux... "); 439 debug_putstr("\nDecompressing Linux... ");
366 decompress(input_data, input_len, NULL, NULL, output, NULL, error); 440 decompress(input_data, input_len, NULL, NULL, output, NULL, error);
367 parse_elf(output); 441 parse_elf(output);
442 handle_relocations(output, output_len);
368 debug_putstr("done.\nBooting the kernel.\n"); 443 debug_putstr("done.\nBooting the kernel.\n");
369 return; 444 return;
370} 445}
diff --git a/arch/x86/include/asm/page_32_types.h b/arch/x86/include/asm/page_32_types.h
index ef17af013475..f48b17df4224 100644
--- a/arch/x86/include/asm/page_32_types.h
+++ b/arch/x86/include/asm/page_32_types.h
@@ -15,6 +15,8 @@
15 */ 15 */
16#define __PAGE_OFFSET _AC(CONFIG_PAGE_OFFSET, UL) 16#define __PAGE_OFFSET _AC(CONFIG_PAGE_OFFSET, UL)
17 17
18#define __START_KERNEL_map __PAGE_OFFSET
19
18#define THREAD_SIZE_ORDER 1 20#define THREAD_SIZE_ORDER 1
19#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER) 21#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)
20 22
diff --git a/arch/x86/include/asm/page_64_types.h b/arch/x86/include/asm/page_64_types.h
index 6c896fbe21db..43dcd804ebd5 100644
--- a/arch/x86/include/asm/page_64_types.h
+++ b/arch/x86/include/asm/page_64_types.h
@@ -32,11 +32,6 @@
32 */ 32 */
33#define __PAGE_OFFSET _AC(0xffff880000000000, UL) 33#define __PAGE_OFFSET _AC(0xffff880000000000, UL)
34 34
35#define __PHYSICAL_START ((CONFIG_PHYSICAL_START + \
36 (CONFIG_PHYSICAL_ALIGN - 1)) & \
37 ~(CONFIG_PHYSICAL_ALIGN - 1))
38
39#define __START_KERNEL (__START_KERNEL_map + __PHYSICAL_START)
40#define __START_KERNEL_map _AC(0xffffffff80000000, UL) 35#define __START_KERNEL_map _AC(0xffffffff80000000, UL)
41 36
42/* See Documentation/x86/x86_64/mm.txt for a description of the memory map. */ 37/* See Documentation/x86/x86_64/mm.txt for a description of the memory map. */
diff --git a/arch/x86/include/asm/page_types.h b/arch/x86/include/asm/page_types.h
index 54c97879195e..f97fbe3abb67 100644
--- a/arch/x86/include/asm/page_types.h
+++ b/arch/x86/include/asm/page_types.h
@@ -33,6 +33,11 @@
33 (((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0 ) | \ 33 (((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0 ) | \
34 VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) 34 VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
35 35
36#define __PHYSICAL_START ALIGN(CONFIG_PHYSICAL_START, \
37 CONFIG_PHYSICAL_ALIGN)
38
39#define __START_KERNEL (__START_KERNEL_map + __PHYSICAL_START)
40
36#ifdef CONFIG_X86_64 41#ifdef CONFIG_X86_64
37#include <asm/page_64_types.h> 42#include <asm/page_64_types.h>
38#else 43#else