Skip to content

Commit aafcd5d

Browse files
committed
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
2 parents 6832d96 + a021506 commit aafcd5d

File tree

8 files changed

+97
-40
lines changed

8 files changed

+97
-40
lines changed

arch/x86/Kconfig

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,9 +1716,10 @@ config X86_NEED_RELOCS
17161716
depends on X86_32 && RELOCATABLE
17171717

17181718
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"
17201720
default "0x1000000"
1721-
range 0x2000 0x1000000
1721+
range 0x2000 0x1000000 if X86_32
1722+
range 0x200000 0x1000000 if X86_64
17221723
---help---
17231724
This value puts the alignment restrictions on physical address
17241725
where kernel is loaded and run from. Kernel is compiled for an
@@ -1736,6 +1737,9 @@ config PHYSICAL_ALIGN
17361737
end result is that kernel runs from a physical address meeting
17371738
above alignment restrictions.
17381739

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+
17391743
Don't change this unless you know what you are doing.
17401744

17411745
config HOTPLUG_CPU

arch/x86/Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ endif
1616
# e.g.: obj-y += foo_$(BITS).o
1717
export BITS
1818

19+
ifdef CONFIG_X86_NEED_RELOCS
20+
LDFLAGS_vmlinux := --emit-relocs
21+
endif
22+
1923
ifeq ($(CONFIG_X86_32),y)
2024
BITS := 32
2125
UTS_MACHINE := i386
@@ -25,10 +29,6 @@ ifeq ($(CONFIG_X86_32),y)
2529
KBUILD_AFLAGS += $(biarch)
2630
KBUILD_CFLAGS += $(biarch)
2731

28-
ifdef CONFIG_RELOCATABLE
29-
LDFLAGS_vmlinux := --emit-relocs
30-
endif
31-
3232
KBUILD_CFLAGS += -msoft-float -mregparm=3 -freg-struct-return
3333

3434
# Never want PIC in a 32-bit kernel, prevent breakage with GCC built

arch/x86/boot/compressed/head_32.S

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,9 @@ relocated:
181181
/*
182182
* Do the decompression, and jump to the new kernel..
183183
*/
184-
leal z_extract_offset_negative(%ebx), %ebp
185184
/* push arguments for decompress_kernel: */
185+
pushl $z_output_len /* decompressed length */
186+
leal z_extract_offset_negative(%ebx), %ebp
186187
pushl %ebp /* output address */
187188
pushl $z_input_len /* input_len */
188189
leal input_data(%ebx), %eax
@@ -191,33 +192,7 @@ relocated:
191192
pushl %eax /* heap area */
192193
pushl %esi /* real mode pointer */
193194
call decompress_kernel
194-
addl $20, %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
195+
addl $24, %esp
221196

222197
/*
223198
* Jump to the decompressed kernel.

arch/x86/boot/compressed/head_64.S

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ relocated:
338338
leaq input_data(%rip), %rdx /* input_data */
339339
movl $z_input_len, %ecx /* input_len */
340340
movq %rbp, %r8 /* output target address */
341+
movq $z_output_len, %r9 /* decompressed length */
341342
call decompress_kernel
342343
popq %rsi
343344

arch/x86/boot/compressed/misc.c

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,79 @@ static void error(char *x)
271271
asm("hlt");
272272
}
273273

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+
274347
static void parse_elf(void *output)
275348
{
276349
#ifdef CONFIG_X86_64
@@ -325,7 +398,8 @@ static void parse_elf(void *output)
325398
asmlinkage void decompress_kernel(void *rmode, memptr heap,
326399
unsigned char *input_data,
327400
unsigned long input_len,
328-
unsigned char *output)
401+
unsigned char *output,
402+
unsigned long output_len)
329403
{
330404
real_mode = rmode;
331405

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

arch/x86/include/asm/page_32_types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
#define __PAGE_OFFSET _AC(CONFIG_PAGE_OFFSET, UL)
1717

18+
#define __START_KERNEL_map __PAGE_OFFSET
19+
1820
#define THREAD_SIZE_ORDER 1
1921
#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)
2022

arch/x86/include/asm/page_64_types.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,6 @@
3232
*/
3333
#define __PAGE_OFFSET _AC(0xffff880000000000, UL)
3434

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)
4035
#define __START_KERNEL_map _AC(0xffffffff80000000, UL)
4136

4237
/* See Documentation/x86/x86_64/mm.txt for a description of the memory map. */

arch/x86/include/asm/page_types.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
(((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0 ) | \
3434
VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
3535

36+
#define __PHYSICAL_START ALIGN(CONFIG_PHYSICAL_START, \
37+
CONFIG_PHYSICAL_ALIGN)
38+
39+
#define __START_KERNEL (__START_KERNEL_map + __PHYSICAL_START)
40+
3641
#ifdef CONFIG_X86_64
3742
#include <asm/page_64_types.h>
3843
#else

0 commit comments

Comments
 (0)