Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

+97 -40
+6 -2
arch/x86/Kconfig
··· 1716 1716 depends on X86_32 && RELOCATABLE 1717 1717 1718 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 1720 default "0x1000000" 1721 - range 0x2000 0x1000000 1721 + range 0x2000 0x1000000 if X86_32 1722 + range 0x200000 0x1000000 if X86_64 1722 1723 ---help--- 1723 1724 This value puts the alignment restrictions on physical address 1724 1725 where kernel is loaded and run from. Kernel is compiled for an ··· 1736 1735 compiled already meets above alignment restrictions. Hence the 1737 1736 end result is that kernel runs from a physical address meeting 1738 1737 above alignment restrictions. 1738 + 1739 + On 32-bit this value must be a multiple of 0x2000. On 64-bit 1740 + this value must be a multiple of 0x200000. 1739 1741 1740 1742 Don't change this unless you know what you are doing. 1741 1743
+4 -4
arch/x86/Makefile
··· 16 16 # e.g.: obj-y += foo_$(BITS).o 17 17 export BITS 18 18 19 + ifdef CONFIG_X86_NEED_RELOCS 20 + LDFLAGS_vmlinux := --emit-relocs 21 + endif 22 + 19 23 ifeq ($(CONFIG_X86_32),y) 20 24 BITS := 32 21 25 UTS_MACHINE := i386 ··· 28 24 biarch := $(call cc-option,-m32) 29 25 KBUILD_AFLAGS += $(biarch) 30 26 KBUILD_CFLAGS += $(biarch) 31 - 32 - ifdef CONFIG_RELOCATABLE 33 - LDFLAGS_vmlinux := --emit-relocs 34 - endif 35 27 36 28 KBUILD_CFLAGS += -msoft-float -mregparm=3 -freg-struct-return 37 29
+3 -28
arch/x86/boot/compressed/head_32.S
··· 181 181 /* 182 182 * Do the decompression, and jump to the new kernel.. 183 183 */ 184 - leal z_extract_offset_negative(%ebx), %ebp 185 184 /* push arguments for decompress_kernel: */ 185 + pushl $z_output_len /* decompressed length */ 186 + leal z_extract_offset_negative(%ebx), %ebp 186 187 pushl %ebp /* output address */ 187 188 pushl $z_input_len /* input_len */ 188 189 leal input_data(%ebx), %eax ··· 192 191 pushl %eax /* heap area */ 193 192 pushl %esi /* real mode pointer */ 194 193 call decompress_kernel 195 - addl $20, %esp 196 - 197 - #if CONFIG_RELOCATABLE 198 - /* 199 - * Find the address of the relocations. 200 - */ 201 - leal z_output_len(%ebp), %edi 202 - 203 - /* 204 - * Calculate the delta between where vmlinux was compiled to run 205 - * and where it was actually loaded. 206 - */ 207 - movl %ebp, %ebx 208 - subl $LOAD_PHYSICAL_ADDR, %ebx 209 - jz 2f /* Nothing to be done if loaded at compiled addr. */ 210 - /* 211 - * Process relocations. 212 - */ 213 - 214 - 1: subl $4, %edi 215 - movl (%edi), %ecx 216 - testl %ecx, %ecx 217 - jz 2f 218 - addl %ebx, -__PAGE_OFFSET(%ebx, %ecx) 219 - jmp 1b 220 - 2: 221 - #endif 194 + addl $24, %esp 222 195 223 196 /* 224 197 * Jump to the decompressed kernel.
+1
arch/x86/boot/compressed/head_64.S
··· 338 338 leaq input_data(%rip), %rdx /* input_data */ 339 339 movl $z_input_len, %ecx /* input_len */ 340 340 movq %rbp, %r8 /* output target address */ 341 + movq $z_output_len, %r9 /* decompressed length */ 341 342 call decompress_kernel 342 343 popq %rsi 343 344
+76 -1
arch/x86/boot/compressed/misc.c
··· 271 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"); 337 + 338 + *(uint64_t *)ptr += delta; 339 + } 340 + #endif 341 + } 342 + #else 343 + static inline void handle_relocations(void *output, unsigned long output_len) 344 + { } 345 + #endif 346 + 274 347 static void parse_elf(void *output) 275 348 { 276 349 #ifdef CONFIG_X86_64 ··· 398 325 asmlinkage void decompress_kernel(void *rmode, memptr heap, 399 326 unsigned char *input_data, 400 327 unsigned long input_len, 401 - unsigned char *output) 328 + unsigned char *output, 329 + unsigned long output_len) 402 330 { 403 331 real_mode = rmode; 404 332 ··· 439 365 debug_putstr("\nDecompressing Linux... "); 440 366 decompress(input_data, input_len, NULL, NULL, output, NULL, error); 441 367 parse_elf(output); 368 + handle_relocations(output, output_len); 442 369 debug_putstr("done.\nBooting the kernel.\n"); 443 370 return; 444 371 }
+2
arch/x86/include/asm/page_32_types.h
··· 15 15 */ 16 16 #define __PAGE_OFFSET _AC(CONFIG_PAGE_OFFSET, UL) 17 17 18 + #define __START_KERNEL_map __PAGE_OFFSET 19 + 18 20 #define THREAD_SIZE_ORDER 1 19 21 #define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER) 20 22
-5
arch/x86/include/asm/page_64_types.h
··· 32 32 */ 33 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 35 #define __START_KERNEL_map _AC(0xffffffff80000000, UL) 41 36 42 37 /* See Documentation/x86/x86_64/mm.txt for a description of the memory map. */
+5
arch/x86/include/asm/page_types.h
··· 33 33 (((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0 ) | \ 34 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 41 #ifdef CONFIG_X86_64 37 42 #include <asm/page_64_types.h> 38 43 #else