lkml.org 
[lkml]   [2017]   [Feb]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 3.10 050/319] x86/build: Build compressed x86 kernels as PIE
    Date
    From: "H.J. Lu" <hjl.tools@gmail.com>

    commit 6d92bc9d483aa1751755a66fee8fb39dffb088c0 upstream.

    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>
    Signed-off-by: Willy Tarreau <w@1wt.eu>
    ---
    arch/x86/boot/compressed/Makefile | 14 +++++++++++++-
    arch/x86/boot/compressed/head_32.S | 28 ++++++++++++++++++++++++++++
    arch/x86/boot/compressed/head_64.S | 8 ++++++++
    3 files changed, 49 insertions(+), 1 deletion(-)

    diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
    index 7194d9f..349cf19 100644
    --- a/arch/x86/boot/compressed/Makefile
    +++ b/arch/x86/boot/compressed/Makefile
    @@ -7,7 +7,7 @@
    targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma vmlinux.bin.xz vmlinux.bin.lzo

    KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2
    -KBUILD_CFLAGS += -fno-strict-aliasing -fPIC
    +KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC)
    KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
    cflags-$(CONFIG_X86_32) := -march=i386
    cflags-$(CONFIG_X86_64) := -mcmodel=small
    @@ -20,6 +20,18 @@ KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
    GCOV_PROFILE := n

    LDFLAGS := -m elf_$(UTS_MACHINE)
    +ifeq ($(CONFIG_RELOCATABLE),y)
    +# If kernel is relocatable, build compressed kernel as PIE.
    +ifeq ($(CONFIG_X86_32),y)
    +LDFLAGS += $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
    +else
    +# To build 64-bit compressed kernel as PIE, we disable relocation
    +# overflow check to avoid relocation overflow error with a new linker
    +# command-line option, -z noreloc-overflow.
    +LDFLAGS += $(shell $(LD) --help 2>&1 | grep -q "\-z noreloc-overflow" \
    + && echo "-z noreloc-overflow -pie --no-dynamic-linker")
    +endif
    +endif
    LDFLAGS_vmlinux := -T

    hostprogs-y := mkpiggy
    diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
    index 3b28eff..104d7e4 100644
    --- a/arch/x86/boot/compressed/head_32.S
    +++ b/arch/x86/boot/compressed/head_32.S
    @@ -30,6 +30,34 @@
    #include <asm/boot.h>
    #include <asm/asm-offsets.h>

    +/*
    + * 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:
    + */
    + .hidden _bss
    + .hidden _ebss
    + .hidden _got
    + .hidden _egot
    +
    __HEAD
    ENTRY(startup_32)
    #ifdef CONFIG_EFI_STUB
    diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
    index 92059b8..6ac508a 100644
    --- a/arch/x86/boot/compressed/head_64.S
    +++ b/arch/x86/boot/compressed/head_64.S
    @@ -34,6 +34,14 @@
    #include <asm/processor-flags.h>
    #include <asm/asm-offsets.h>

    +/*
    + * Locally defined symbols should be marked hidden:
    + */
    + .hidden _bss
    + .hidden _ebss
    + .hidden _got
    + .hidden _egot
    +
    __HEAD
    .code32
    ENTRY(startup_32)
    --
    2.8.0.rc2.1.gbe9624a
    \
     
     \ /
      Last update: 2017-02-05 21:13    [W:2.779 / U:0.032 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site