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 /arch/x86/boot/compressed | |
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
Diffstat (limited to 'arch/x86/boot/compressed')
-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 |
3 files changed, 80 insertions, 29 deletions
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"); | ||
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 | static void parse_elf(void *output) | 347 | static 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) | |||
325 | asmlinkage void decompress_kernel(void *rmode, memptr heap, | 398 | asmlinkage 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 | } |