aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2016-03-16 23:04:35 -0400
committerIngo Molnar <mingo@kernel.org>2016-03-29 06:51:12 -0400
commit6d92bc9d483aa1751755a66fee8fb39dffb088c0 (patch)
treee40d68f07481a2def59434884e3d0136cd028b65
parent591b1d8d86074ac3a3163d89bcfe7b232cf83902 (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/Makefile14
-rw-r--r--arch/x86/boot/compressed/head_32.S28
-rw-r--r--arch/x86/boot/compressed/head_64.S8
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
28KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2 28KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2
29KBUILD_CFLAGS += -fno-strict-aliasing -fPIC 29KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC)
30KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING 30KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
31cflags-$(CONFIG_X86_32) := -march=i386 31cflags-$(CONFIG_X86_32) := -march=i386
32cflags-$(CONFIG_X86_64) := -mcmodel=small 32cflags-$(CONFIG_X86_64) := -mcmodel=small
@@ -40,6 +40,18 @@ GCOV_PROFILE := n
40UBSAN_SANITIZE :=n 40UBSAN_SANITIZE :=n
41 41
42LDFLAGS := -m elf_$(UTS_MACHINE) 42LDFLAGS := -m elf_$(UTS_MACHINE)
43ifeq ($(CONFIG_RELOCATABLE),y)
44# If kernel is relocatable, build compressed kernel as PIE.
45ifeq ($(CONFIG_X86_32),y)
46LDFLAGS += $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
47else
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.
51LDFLAGS += $(shell $(LD) --help 2>&1 | grep -q "\-z noreloc-overflow" \
52 && echo "-z noreloc-overflow -pie --no-dynamic-linker")
53endif
54endif
43LDFLAGS_vmlinux := -T 55LDFLAGS_vmlinux := -T
44 56
45hostprogs-y := mkpiggy 57hostprogs-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
35ENTRY(startup_32) 63ENTRY(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
38ENTRY(startup_32) 46ENTRY(startup_32)