diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-04 12:38:10 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-04 12:38:10 -0400 |
| commit | aafcd5d75797ac423f6a03ce1f801933d4b114dc (patch) | |
| tree | 138af0b67e456b5fc8aa4ecff7f160f79d355e70 | |
| parent | 6832d9652f395f7d13003e3884942c40f52ac1fa (diff) | |
| parent | a02150610776f66b40257624822a879311592bb2 (diff) | |
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
| -rw-r--r-- | arch/x86/Kconfig | 8 | ||||
| -rw-r--r-- | arch/x86/Makefile | 8 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/head_32.S | 31 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/head_64.S | 1 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/misc.c | 77 | ||||
| -rw-r--r-- | arch/x86/include/asm/page_32_types.h | 2 | ||||
| -rw-r--r-- | arch/x86/include/asm/page_64_types.h | 5 | ||||
| -rw-r--r-- | arch/x86/include/asm/page_types.h | 5 |
8 files changed, 97 insertions, 40 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index ff47e230d355..b710efdec9a0 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 | ||
| 1718 | config PHYSICAL_ALIGN | 1718 | config 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 | ||
| 1741 | config HOTPLUG_CPU | 1745 | config 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 |
| 17 | export BITS | 17 | export BITS |
| 18 | 18 | ||
| 19 | ifdef CONFIG_X86_NEED_RELOCS | ||
| 20 | LDFLAGS_vmlinux := --emit-relocs | ||
| 21 | endif | ||
| 22 | |||
| 19 | ifeq ($(CONFIG_X86_32),y) | 23 | ifeq ($(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 | |||
| 213 | 1: subl $4, %edi | ||
| 214 | movl (%edi), %ecx | ||
| 215 | testl %ecx, %ecx | ||
| 216 | jz 2f | ||
| 217 | addl %ebx, -__PAGE_OFFSET(%ebx, %ecx) | ||
| 218 | jmp 1b | ||
| 219 | 2: | ||
| 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 | ||
| 275 | static 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"); | ||
