diff options
author | H.J. Lu <hjl.tools@gmail.com> | 2016-03-16 23:04:35 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-03-29 06:51:12 -0400 |
commit | 6d92bc9d483aa1751755a66fee8fb39dffb088c0 (patch) | |
tree | e40d68f07481a2def59434884e3d0136cd028b65 | |
parent | 591b1d8d86074ac3a3163d89bcfe7b232cf83902 (diff) |
x86/build: Build compressed x86 kernels as PIE
The 32-bit x86 assembler in binutils 2.26 will generate R_386_GOT32X
relocation to get the symbol address in PIC. When the compressed x86
kernel isn't built as PIC, the linker optimizes R_386_GOT32X relocations
to their fixed symbol addresses. However, when the compressed x86
kernel is loaded at a different address, it leads to the following
load failure:
Failed to allocate space for phdrs
during the decompression stage.
If the compressed x86 kernel is relocatable at run-time, it should be
compiled with -fPIE, instead of -fPIC, if possible and should be built as
Position Independent Executable (PIE) so that linker won't optimize
R_386_GOT32X relocation to its fixed symbol address.
Older linkers generate R_386_32 relocations against locally defined
symbols, _bss, _ebss, _got and _egot, in PIE. It isn't wrong, just less
optimal than R_386_RELATIVE. But the x86 kernel fails to properly handle
R_386_32 relocations when relocating the kernel. To generate
R_386_RELATIVE relocations, we mark _bss, _ebss, _got and _egot as
hidden in both 32-bit and 64-bit x86 kernels.
To build a 64-bit compressed x86 kernel as PIE, we need to disable the
relocation overflow check to avoid relocation overflow errors. We do
this with a new linker command-line option, -z noreloc-overflow, which
got added recently:
commit 4c10bbaa0912742322f10d9d5bb630ba4e15dfa7
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Tue Mar 15 11:07:06 2016 -0700
Add -z noreloc-overflow option to x86-64 ld
Add -z noreloc-overflow command-line option to the x86-64 ELF linker to
disable relocation overflow check. This can be used to avoid relocation
overflow check if there will be no dynamic relocation overflow at
run-time.
The 64-bit compressed x86 kernel is built as PIE only if the linker supports
-z noreloc-overflow. So far 64-bit relocatable compressed x86 kernel
boots fine even when it is built as a normal executable.
Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-kernel@vger.kernel.org
[ Edited the changelog and comments. ]
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | arch/x86/boot/compressed/Makefile | 14 | ||||
-rw-r--r-- | arch/x86/boot/compressed/head_32.S | 28 | ||||
-rw-r--r-- | arch/x86/boot/compressed/head_64.S | 8 |
3 files changed, 49 insertions, 1 deletions
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index 6915ff2bd996..8774cb23064f 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile | |||
@@ -26,7 +26,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \ | |||
26 | vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4 | 26 | vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4 |
27 | 27 | ||
28 | KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2 | 28 | KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2 |
29 | KBUILD_CFLAGS += -fno-strict-aliasing -fPIC | 29 | KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC) |
30 | KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING | 30 | KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING |
31 | cflags-$(CONFIG_X86_32) := -march=i386 | 31 | cflags-$(CONFIG_X86_32) := -march=i386 |
32 | cflags-$(CONFIG_X86_64) := -mcmodel=small | 32 | cflags-$(CONFIG_X86_64) := -mcmodel=small |
@@ -40,6 +40,18 @@ GCOV_PROFILE := n | |||
40 | UBSAN_SANITIZE :=n | 40 | UBSAN_SANITIZE :=n |
41 | 41 | ||
42 | LDFLAGS := -m elf_$(UTS_MACHINE) | 42 | LDFLAGS := -m elf_$(UTS_MACHINE) |
43 | ifeq ($(CONFIG_RELOCATABLE),y) | ||
44 | # If kernel is relocatable, build compressed kernel as PIE. | ||
45 | ifeq ($(CONFIG_X86_32),y) | ||
46 | LDFLAGS += $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker) | ||
47 | else | ||
48 | # To build 64-bit compressed kernel as PIE, we disable relocation | ||
49 | # overflow check to avoid relocation overflow error with a new linker | ||
50 | # command-line option, -z noreloc-overflow. | ||
51 | LDFLAGS += $(shell $(LD) --help 2>&1 | grep -q "\-z noreloc-overflow" \ | ||
52 | && echo "-z noreloc-overflow -pie --no-dynamic-linker") | ||
53 | endif | ||
54 | endif | ||
43 | LDFLAGS_vmlinux := -T | 55 | LDFLAGS_vmlinux := -T |
44 | 56 | ||
45 | hostprogs-y := mkpiggy | 57 | hostprogs-y := mkpiggy |
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S index 8ef964ddc18e..0256064da8da 100644 --- a/arch/x86/boot/compressed/head_32.S +++ b/arch/x86/boot/compressed/head_32.S | |||
@@ -31,6 +31,34 @@ | |||
31 | #include <asm/asm-offsets.h> | 31 | #include <asm/asm-offsets.h> |
32 | #include <asm/bootparam.h> | 32 | #include <asm/bootparam.h> |
33 | 33 | ||
34 | /* | ||
35 | * The 32-bit x86 assembler in binutils 2.26 will generate R_386_GOT32X | ||
36 | * relocation to get the symbol address in PIC. When the compressed x86 | ||
37 | * kernel isn't built as PIC, the linker optimizes R_386_GOT32X | ||
38 | * relocations to their fixed symbol addresses. However, when the | ||
39 | * compressed x86 kernel is loaded at a different address, it leads | ||
40 | * to the following load failure: | ||
41 | * | ||
42 | * Failed to allocate space for phdrs | ||
43 | * | ||
44 | * during the decompression stage. | ||
45 | * | ||
46 | * If the compressed x86 kernel is relocatable at run-time, it should be | ||
47 | * compiled with -fPIE, instead of -fPIC, if possible and should be built as | ||
48 | * Position Independent Executable (PIE) so that linker won't optimize | ||
49 | * R_386_GOT32X relocation to its fixed symbol address. Older | ||
50 | * linkers generate R_386_32 relocations against locally defined symbols, | ||
51 | * _bss, _ebss, _got and _egot, in PIE. It isn't wrong, just less | ||
52 | * optimal than R_386_RELATIVE. But the x86 kernel fails to properly handle | ||
53 | * R_386_32 relocations when relocating the kernel. To generate | ||
54 | * R_386_RELATIVE relocations, we mark _bss, _ebss, _got and _egot as | ||
55 | * hidden: | ||
56 | */ | ||
57 | .hidden _bss | ||
58 | .hidden _ebss | ||
59 | .hidden _got | ||
60 | .hidden _egot | ||
61 | |||
34 | __HEAD | 62 | __HEAD |
35 | ENTRY(startup_32) | 63 | ENTRY(startup_32) |
36 | #ifdef CONFIG_EFI_STUB | 64 | #ifdef CONFIG_EFI_STUB |
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index b0c0d16ef58d..86558a199139 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S | |||
@@ -33,6 +33,14 @@ | |||
33 | #include <asm/asm-offsets.h> | 33 | #include <asm/asm-offsets.h> |
34 | #include <asm/bootparam.h> | 34 | #include <asm/bootparam.h> |
35 | 35 | ||
36 | /* | ||
37 | * Locally defined symbols should be marked hidden: | ||
38 | */ | ||
39 | .hidden _bss | ||
40 | .hidden _ebss | ||
41 | .hidden _got | ||
42 | .hidden _egot | ||
43 | |||
36 | __HEAD | 44 | __HEAD |
37 | .code32 | 45 | .code32 |
38 | ENTRY(startup_32) | 46 | ENTRY(startup_32) |