2020-06-29 21:15:31

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v3 0/7] x86/boot: Remove runtime relocations from compressed kernel

The compressed kernel currently contains bogus runtime relocations in
the startup code in head_{32,64}.S, which are generated by the linker,
but must not actually be processed at runtime.

This generates warnings when linking with the BFD linker, and errors
with LLD, which defaults to erroring on runtime relocations in read-only
sections. It also requires the -z noreloc-overflow hack for the 64-bit
kernel, which prevents us from linking it as -pie on an older BFD linker
(<= 2.26) or on LLD, because the locations that are to be apparently
relocated are only 32-bits in size and so cannot really have
R_X86_64_RELATIVE relocations.

This series aims to get rid of these relocations. I've build- and
boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
gcc-4.8.5 with bfd-2.23, skipping clang on 32-bit because it currently
has other issues [0].

The first three patches by Ard remove indirection via the GOT from the
compressed kernel code.

The next patch is an independent fix for LLD, to avoid an orphan
section in arch/x86/boot/setup.elf.

The fifth patch gets rid of almost all the relocations. It uses
standard PIC addressing technique for 32-bit, i.e. loading a register
with the address of _GLOBAL_OFFSET_TABLE_ and then using GOTOFF
references to access variables. For 64-bit, there is 32-bit code that
cannot use RIP-relative addressing, and also cannot use the 32-bit
method, since GOTOFF references are 64-bit only. This is instead handled
using a macro to replace a reference like gdt with (gdt-startup_32)
instead. The assembler will generate a PC32 relocation entry, with
addend set to (.-startup_32), and these will be replaced with constants
at link time. This works as long as all the code using such references
lives in the same section as startup_32, i.e. in .head.text.

The sixth patch addresses a remaining issue with the BFD linker, which
generates runtime relocations for absolute symbols. We use z_input_len
and z_output_len, defined in the generated piggy.S file, as symbols
whose absolute "addresses" are actually the size of the compressed
payload and the size of the decompressed kernel image respectively. LLD
does not generate relocations for these two symbols, but the BFD linker
does, prior to the upcoming 2.35. To get around this, piggy.S is
extended to also define two u32 variables (in .rodata) with the lengths,
and the head code is modified to use those instead of the symbol
addresses.

An alternative way to handle z_input_len/z_output_len would be to just
include piggy.S in head_{32,64}.S instead of as a separate object file,
since the GNU assembler doesn't generate relocations for symbols set to
constants.

The last patch adds a check in the linker script to ensure that no
runtime relocations get reintroduced.

[0] https://lore.kernel.org/lkml/[email protected]/

Changes from v2:
- Incorporate Ard's patches for eliminating GOT references into this
series
- Rebase on v5.8-rc3

v2: https://lore.kernel.org/lkml/[email protected]/

Changes from v1:
- Add .text.* to setup.ld instead of just .text.startup
- Rename the la() macro introduced in the second patch for 64-bit to
rva(), and rework the explanatory comment.
- In the last patch, check both .rel.dyn and .rela.dyn, instead of just
one per arch.

Ard Biesheuvel (3):
x86/boot/compressed: Move .got.plt entries out of the .got section
x86/boot/compressed: Force hidden visibility for all symbol references
x86/boot/compressed: Get rid of GOT fixup code

Arvind Sankar (4):
x86/boot: Add .text.* to setup.ld
x86/boot: Remove run-time relocations from .head.text code
x86/boot: Remove runtime relocations from head_{32,64}.S
x86/boot: Check that there are no runtime relocations

arch/x86/boot/compressed/Makefile | 37 +-----
arch/x86/boot/compressed/head_32.S | 99 +++++----------
arch/x86/boot/compressed/head_64.S | 165 ++++++++++---------------
arch/x86/boot/compressed/hidden.h | 19 +++
arch/x86/boot/compressed/mkpiggy.c | 6 +
arch/x86/boot/compressed/vmlinux.lds.S | 24 +++-
arch/x86/boot/setup.ld | 2 +-
7 files changed, 151 insertions(+), 201 deletions(-)
create mode 100644 arch/x86/boot/compressed/hidden.h


base-commit: 9ebcfadb0610322ac537dd7aa5d9cbc2b2894c68
--
2.26.2


2020-06-29 21:16:08

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v3 3/7] x86/boot/compressed: Get rid of GOT fixup code

From: Ard Biesheuvel <[email protected]>

In a previous patch, we have eliminated GOT entries from the decompressor
binary and added an assertion that the .got section is empty. This means
that the GOT fixup routines that exist in both the 32-bit and 64-bit
startup routines have become dead code, and can be removed.

While at it, drop the KEEP() from the linker script, as it has no effect
on the contents of output sections that are created by the linker itself.

Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/head_32.S | 24 ++---------
arch/x86/boot/compressed/head_64.S | 57 --------------------------
arch/x86/boot/compressed/vmlinux.lds.S | 4 +-
3 files changed, 5 insertions(+), 80 deletions(-)

diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 03557f2174bf..39f0bb43218f 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -49,16 +49,13 @@
* 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, _egot and _end, 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, _egot and _end as
- * hidden:
+ * _bss, _ebss and _end, 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 and _end as hidden:
*/
.hidden _bss
.hidden _ebss
- .hidden _got
- .hidden _egot
.hidden _end

__HEAD
@@ -192,19 +189,6 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
shrl $2, %ecx
rep stosl

-/*
- * Adjust our own GOT
- */
- leal _got(%ebx), %edx
- leal _egot(%ebx), %ecx
-1:
- cmpl %ecx, %edx
- jae 2f
- addl %ebx, (%edx)
- addl $4, %edx
- jmp 1b
-2:
-
/*
* Do the extraction, and jump to the new kernel..
*/
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 97d37f0a34f5..bf1ab30acc5b 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -40,8 +40,6 @@
*/
.hidden _bss
.hidden _ebss
- .hidden _got
- .hidden _egot
.hidden _end

__HEAD
@@ -353,25 +351,6 @@ SYM_CODE_START(startup_64)
/* Set up the stack */
leaq boot_stack_end(%rbx), %rsp

- /*
- * paging_prepare() and cleanup_trampoline() below can have GOT
- * references. Adjust the table with address we are running at.
- *
- * Zero RAX for adjust_got: the GOT was not adjusted before;
- * there's no adjustment to undo.
- */
- xorq %rax, %rax
-
- /*
- * Calculate the address the binary is loaded at and use it as
- * a GOT adjustment.
- */
- call 1f
-1: popq %rdi
- subq $1b, %rdi
-
- call .Ladjust_got
-
/*
* At this point we are in long mode with 4-level paging enabled,
* but we might want to enable 5-level paging or vice versa.
@@ -464,21 +443,6 @@ trampoline_return:
pushq $0
popfq

- /*
- * Previously we've adjusted the GOT with address the binary was
- * loaded at. Now we need to re-adjust for relocation address.
- *
- * Calculate the address the binary is loaded at, so that we can
- * undo the previous GOT adjustment.
- */
- call 1f
-1: popq %rax
- subq $1b, %rax
-
- /* The new adjustment is the relocation address */
- movq %rbx, %rdi
- call .Ladjust_got
-
/*
* Copy the compressed kernel to the end of our buffer
* where decompression in place becomes safe.
@@ -556,27 +520,6 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
jmp *%rax
SYM_FUNC_END(.Lrelocated)

-/*
- * Adjust the global offset table
- *
- * RAX is the previous adjustment of the table to undo (use 0 if it's the
- * first time we touch GOT).
- * RDI is the new adjustment to apply.
- */
-.Ladjust_got:
- /* Walk through the GOT adding the address to the entries */
- leaq _got(%rip), %rdx
- leaq _egot(%rip), %rcx
-1:
- cmpq %rcx, %rdx
- jae 2f
- subq %rax, (%rdx) /* Undo previous adjustment */
- addq %rdi, (%rdx) /* Apply the new adjustment */
- addq $8, %rdx
- jmp 1b
-2:
- ret
-
.code32
/*
* This is the 32-bit trampoline that will be copied over to low memory.
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index 4bcc943842ab..a4a4a59a2628 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -43,9 +43,7 @@ SECTIONS
_erodata = . ;
}
.got : {
- _got = .;
- KEEP(*(.got))
- _egot = .;
+ *(.got)
}
.got.plt : {
*(.got.plt)
--
2.26.2

2020-06-29 21:17:48

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v3 4/7] x86/boot: Add .text.* to setup.ld

gcc puts the main function into .text.startup when compiled with -Os (or
-O2). This results in arch/x86/boot/main.c having a .text.startup
section which is currently not included explicitly in the linker script
setup.ld in the same directory.

The BFD linker places this orphan section immediately after .text, so
this still works. However, LLD git, since [1], is choosing to place it
immediately after the .bstext section instead (this is the first code
section). This plays havoc with the section layout that setup.elf
requires to create the setup header, for eg on 64-bit:

LD arch/x86/boot/setup.elf
ld.lld: error: section .text.startup file range overlaps with .header
>>> .text.startup range is [0x200040, 0x2001FE]
>>> .header range is [0x2001EF, 0x20026B]

ld.lld: error: section .header file range overlaps with .bsdata
>>> .header range is [0x2001EF, 0x20026B]
>>> .bsdata range is [0x2001FF, 0x200398]

ld.lld: error: section .bsdata file range overlaps with .entrytext
>>> .bsdata range is [0x2001FF, 0x200398]
>>> .entrytext range is [0x20026C, 0x2002D3]

ld.lld: error: section .text.startup virtual address range overlaps
with .header
>>> .text.startup range is [0x40, 0x1FE]
>>> .header range is [0x1EF, 0x26B]

ld.lld: error: section .header virtual address range overlaps with
.bsdata
>>> .header range is [0x1EF, 0x26B]
>>> .bsdata range is [0x1FF, 0x398]

ld.lld: error: section .bsdata virtual address range overlaps with
.entrytext
>>> .bsdata range is [0x1FF, 0x398]
>>> .entrytext range is [0x26C, 0x2D3]

ld.lld: error: section .text.startup load address range overlaps with
.header
>>> .text.startup range is [0x40, 0x1FE]
>>> .header range is [0x1EF, 0x26B]

ld.lld: error: section .header load address range overlaps with
.bsdata
>>> .header range is [0x1EF, 0x26B]
>>> .bsdata range is [0x1FF, 0x398]

ld.lld: error: section .bsdata load address range overlaps with
.entrytext
>>> .bsdata range is [0x1FF, 0x398]
>>> .entrytext range is [0x26C, 0x2D3]

Add .text.* to the .text output section to fix this, and also prevent
any future surprises if the compiler decides to create other such
sections.

[1] https://reviews.llvm.org/D75225

Signed-off-by: Arvind Sankar <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
---
arch/x86/boot/setup.ld | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/boot/setup.ld b/arch/x86/boot/setup.ld
index 24c95522f231..49546c247ae2 100644
--- a/arch/x86/boot/setup.ld
+++ b/arch/x86/boot/setup.ld
@@ -20,7 +20,7 @@ SECTIONS
.initdata : { *(.initdata) }
__end_init = .;

- .text : { *(.text) }
+ .text : { *(.text .text.*) }
.text32 : { *(.text32) }

. = ALIGN(16);
--
2.26.2

2020-07-14 02:39:20

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

The compressed kernel currently contains bogus runtime relocations in
the startup code in head_{32,64}.S, which are generated by the linker,
but must not actually be processed at runtime.

This generates warnings when linking with the BFD linker, and errors
with LLD, which defaults to erroring on runtime relocations in read-only
sections. It also requires the -z noreloc-overflow hack for the 64-bit
kernel, which prevents us from linking it as -pie on an older BFD linker
(<= 2.26) or on LLD, because the locations that are to be apparently
relocated are only 32-bits in size and so cannot really have
R_X86_64_RELATIVE relocations.

This series aims to get rid of these relocations. I've build- and
boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
has other issues [0].

The first three patches by Ard remove indirection via the GOT from the
compressed kernel code.

The next patch is an independent fix for LLD, to avoid an orphan
section in arch/x86/boot/setup.elf.

The fifth patch gets rid of almost all the relocations. It uses
standard PIC addressing technique for 32-bit, i.e. loading a register
with the address of _GLOBAL_OFFSET_TABLE_ and then using GOTOFF
references to access variables. For 64-bit, there is 32-bit code that
cannot use RIP-relative addressing, and also cannot use the 32-bit
method, since GOTOFF references are 64-bit only. This is instead handled
using a macro to replace a reference like gdt with (gdt-startup_32)
instead. The assembler will generate a PC32 relocation entry, with
addend set to (.-startup_32), and these will be replaced with constants
at link time. This works as long as all the code using such references
lives in the same section as startup_32, i.e. in .head.text.

The sixth patch addresses a remaining issue with the BFD linker, which
generates runtime relocations for absolute symbols. We use z_input_len
and z_output_len, defined in the generated piggy.S file, as symbols
whose absolute "addresses" are actually the size of the compressed
payload and the size of the decompressed kernel image respectively. LLD
does not generate relocations for these two symbols, but the BFD linker
does, prior to the upcoming 2.35. To get around this, piggy.S is
extended to also define two u32 variables (in .rodata) with the lengths,
and the head code is modified to use those instead of the symbol
addresses.

An alternative way to handle z_input_len/z_output_len would be to just
include piggy.S in head_{32,64}.S instead of as a separate object file,
since the GNU assembler doesn't generate relocations for symbols set to
constants.

The last patch adds a check in the linker script to ensure that no
runtime relocations get reintroduced.

[0] https://lore.kernel.org/lkml/[email protected]/

Changes from v3:
- Move hidden.h to include/linux so the EFI stub and the compressed
kernel can share the same file

Changes from v2:
- Incorporate Ard's patches for eliminating GOT references into this
series
- Rebase on v5.8-rc3

v2: https://lore.kernel.org/lkml/[email protected]/

Changes from v1:
- Add .text.* to setup.ld instead of just .text.startup
- Rename the la() macro introduced in the second patch for 64-bit to
rva(), and rework the explanatory comment.
- In the last patch, check both .rel.dyn and .rela.dyn, instead of just
one per arch.


Ard Biesheuvel (3):
x86/boot/compressed: Move .got.plt entries out of the .got section
x86/boot/compressed: Force hidden visibility for all symbol references
x86/boot/compressed: Get rid of GOT fixup code

Arvind Sankar (4):
x86/boot: Add .text.* to setup.ld
x86/boot: Remove run-time relocations from .head.text code
x86/boot: Remove runtime relocations from head_{32,64}.S
x86/boot: Check that there are no runtime relocations

arch/x86/boot/compressed/Makefile | 37 +-----
arch/x86/boot/compressed/head_32.S | 99 +++++----------
arch/x86/boot/compressed/head_64.S | 165 ++++++++++---------------
arch/x86/boot/compressed/mkpiggy.c | 6 +
arch/x86/boot/compressed/vmlinux.lds.S | 24 +++-
arch/x86/boot/setup.ld | 2 +-
drivers/firmware/efi/libstub/Makefile | 2 +-
drivers/firmware/efi/libstub/hidden.h | 6 -
include/linux/hidden.h | 19 +++
9 files changed, 152 insertions(+), 208 deletions(-)
delete mode 100644 drivers/firmware/efi/libstub/hidden.h
create mode 100644 include/linux/hidden.h


base-commit: 11ba468877bb23f28956a35e896356252d63c983
--
2.26.2

2020-07-14 02:39:25

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v4 2/7] x86/boot/compressed: Force hidden visibility for all symbol references

From: Ard Biesheuvel <[email protected]>

Eliminate all GOT entries in the decompressor binary, by forcing hidden
visibility for all symbol references, which informs the compiler that
such references will be resolved at link time without the need for
allocating GOT entries.

To ensure that no GOT entries will creep back in, add an assertion to
the decompressor linker script that will fire if the .got section has
a non-zero size.

[Arvind: move hidden.h to include/linux instead of making a copy]

Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/Makefile | 1 +
arch/x86/boot/compressed/vmlinux.lds.S | 1 +
drivers/firmware/efi/libstub/Makefile | 2 +-
drivers/firmware/efi/libstub/hidden.h | 6 ------
include/linux/hidden.h | 19 +++++++++++++++++++
5 files changed, 22 insertions(+), 7 deletions(-)
delete mode 100644 drivers/firmware/efi/libstub/hidden.h
create mode 100644 include/linux/hidden.h

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 7619742f91c9..c829d874dcac 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -42,6 +42,7 @@ KBUILD_CFLAGS += $(call cc-disable-warning, gnu)
KBUILD_CFLAGS += -Wno-pointer-sign
KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=)
KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
+KBUILD_CFLAGS += -include $(srctree)/include/linux/hidden.h

KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
GCOV_PROFILE := n
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index b17d218ccdf9..4bcc943842ab 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -81,6 +81,7 @@ SECTIONS
DISCARDS
}

+ASSERT(SIZEOF(.got) == 0, "Unexpected GOT entries detected!")
#ifdef CONFIG_X86_64
ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT entries detected!")
#else
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 4cce372edaf4..609157a40493 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -27,7 +27,7 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt

KBUILD_CFLAGS := $(cflags-y) -Os -DDISABLE_BRANCH_PROFILING \
- -include $(srctree)/drivers/firmware/efi/libstub/hidden.h \
+ -include $(srctree)/include/linux/hidden.h \
-D__NO_FORTIFY \
$(call cc-option,-ffreestanding) \
$(call cc-option,-fno-stack-protector) \
diff --git a/drivers/firmware/efi/libstub/hidden.h b/drivers/firmware/efi/libstub/hidden.h
deleted file mode 100644
index 3493b041f419..000000000000
--- a/drivers/firmware/efi/libstub/hidden.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * To prevent the compiler from emitting GOT-indirected (and thus absolute)
- * references to any global symbols, override their visibility as 'hidden'
- */
-#pragma GCC visibility push(hidden)
diff --git a/include/linux/hidden.h b/include/linux/hidden.h
new file mode 100644
index 000000000000..49a17b6b5962
--- /dev/null
+++ b/include/linux/hidden.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * When building position independent code with GCC using the -fPIC option,
+ * (or even the -fPIE one on older versions), it will assume that we are
+ * building a dynamic object (either a shared library or an executable) that
+ * may have symbol references that can only be resolved at load time. For a
+ * variety of reasons (ELF symbol preemption, the CoW footprint of the section
+ * that is modified by the loader), this results in all references to symbols
+ * with external linkage to go via entries in the Global Offset Table (GOT),
+ * which carries absolute addresses which need to be fixed up when the
+ * executable image is loaded at an offset which is different from its link
+ * time offset.
+ *
+ * Fortunately, there is a way to inform the compiler that such symbol
+ * references will be satisfied at link time rather than at load time, by
+ * giving them 'hidden' visibility.
+ */
+
+#pragma GCC visibility push(hidden)
--
2.26.2

2020-07-14 02:39:30

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v4 6/7] x86/boot: Remove runtime relocations from head_{32,64}.S

The BFD linker generates runtime relocations for z_input_len and
z_output_len, even though they are absolute symbols.

This is fixed for binutils-2.35 [1]. Work around this for earlier
versions by defining two variables input_len and output_len in addition
to the symbols, and use them via position-independent references.

This eliminates the last two runtime relocations in the head code and
allows us to drop the -z noreloc-overflow flag to the linker.

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=25754

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/compressed/Makefile | 8 --------
arch/x86/boot/compressed/head_32.S | 17 ++++++++---------
arch/x86/boot/compressed/head_64.S | 4 ++--
arch/x86/boot/compressed/mkpiggy.c | 6 ++++++
4 files changed, 16 insertions(+), 19 deletions(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index c829d874dcac..7cd9a2870f7c 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -51,15 +51,7 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
-ifeq ($(CONFIG_X86_32),y)
KBUILD_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.
-KBUILD_LDFLAGS += $(shell $(LD) --help 2>&1 | grep -q "\-z noreloc-overflow" \
- && echo "-z noreloc-overflow -pie --no-dynamic-linker")
-endif
LDFLAGS_vmlinux := -T

hostprogs := mkpiggy
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 8c1a4f5610f5..659fad53ca82 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -178,18 +178,17 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
/*
* Do the extraction, and jump to the new kernel..
*/
- /* push arguments for extract_kernel: */
- pushl $z_output_len /* decompressed length, end of relocs */
+ /* push arguments for extract_kernel: */

- pushl %ebp /* output address */
-
- pushl $z_input_len /* input_len */
+ pushl output_len@GOTOFF(%ebx) /* decompressed length, end of relocs */
+ pushl %ebp /* output address */
+ pushl input_len@GOTOFF(%ebx) /* input_len */
leal input_data@GOTOFF(%ebx), %eax
- pushl %eax /* input_data */
+ pushl %eax /* input_data */
leal boot_heap@GOTOFF(%ebx), %eax
- pushl %eax /* heap area */
- pushl %esi /* real mode pointer */
- call extract_kernel /* returns kernel location in %eax */
+ pushl %eax /* heap area */
+ pushl %esi /* real mode pointer */
+ call extract_kernel /* returns kernel location in %eax */
addl $24, %esp

/*
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 11429092c224..9e46729cf162 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -534,9 +534,9 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
movq %rsi, %rdi /* real mode address */
leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
leaq input_data(%rip), %rdx /* input_data */
- movl $z_input_len, %ecx /* input_len */
+ movl input_len(%rip), %ecx /* input_len */
movq %rbp, %r8 /* output target address */
- movl $z_output_len, %r9d /* decompressed length, end of relocs */
+ movl output_len(%rip), %r9d /* decompressed length, end of relocs */
call extract_kernel /* returns kernel location in %rax */
popq %rsi

diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c
index 7e01248765b2..52aa56cdbacc 100644
--- a/arch/x86/boot/compressed/mkpiggy.c
+++ b/arch/x86/boot/compressed/mkpiggy.c
@@ -60,6 +60,12 @@ int main(int argc, char *argv[])
printf(".incbin \"%s\"\n", argv[1]);
printf("input_data_end:\n");

+ printf(".section \".rodata\",\"a\",@progbits\n");
+ printf(".globl input_len\n");
+ printf("input_len:\n\t.long %lu\n", ilen);
+ printf(".globl output_len\n");
+ printf("output_len:\n\t.long %lu\n", (unsigned long)olen);
+
retval = 0;
bail:
if (f)
--
2.26.2

2020-07-14 02:40:38

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v4 7/7] x86/boot: Check that there are no runtime relocations

Add a linker script check that there are no runtime relocations, and
remove the old one that tries to check via looking for specially-named
sections in the object files.

Drop the tests for -fPIE compiler option and -pie linker option, as they
are available in all supported gcc and binutils versions (as well as
clang and lld).

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/compressed/Makefile | 28 +++-----------------------
arch/x86/boot/compressed/vmlinux.lds.S | 8 ++++++++
2 files changed, 11 insertions(+), 25 deletions(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 7cd9a2870f7c..cd286bb90423 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -29,7 +29,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4

KBUILD_CFLAGS := -m$(BITS) -O2
-KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC)
+KBUILD_CFLAGS += -fno-strict-aliasing -fPIE
KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
cflags-$(CONFIG_X86_32) := -march=i386
cflags-$(CONFIG_X86_64) := -mcmodel=small
@@ -51,7 +51,7 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
-KBUILD_LDFLAGS += $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
+KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
LDFLAGS_vmlinux := -T

hostprogs := mkpiggy
@@ -86,30 +86,8 @@ vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
vmlinux-objs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o

-# The compressed kernel is built with -fPIC/-fPIE so that a boot loader
-# can place it anywhere in memory and it will still run. However, since
-# it is executed as-is without any ELF relocation processing performed
-# (and has already had all relocation sections stripped from the binary),
-# none of the code can use data relocations (e.g. static assignments of
-# pointer values), since they will be meaningless at runtime. This check
-# will refuse to link the vmlinux if any of these relocations are found.
-quiet_cmd_check_data_rel = DATAREL $@
-define cmd_check_data_rel
- for obj in $(filter %.o,$^); do \
- $(READELF) -S $$obj | grep -qF .rel.local && { \
- echo "error: $$obj has data relocations!" >&2; \
- exit 1; \
- } || true; \
- done
-endef
-
-# We need to run two commands under "if_changed", so merge them into a
-# single invocation.
-quiet_cmd_check-and-link-vmlinux = LD $@
- cmd_check-and-link-vmlinux = $(cmd_check_data_rel); $(cmd_ld)
-
$(obj)/vmlinux: $(vmlinux-objs-y) FORCE
- $(call if_changed,check-and-link-vmlinux)
+ $(call if_changed,ld)

OBJCOPYFLAGS_vmlinux.bin := -R .comment -S
$(obj)/vmlinux.bin: vmlinux FORCE
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index a4a4a59a2628..a78510046eec 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -42,6 +42,12 @@ SECTIONS
*(.rodata.*)
_erodata = . ;
}
+ .rel.dyn : {
+ *(.rel.*)
+ }
+ .rela.dyn : {
+ *(.rela.*)
+ }
.got : {
*(.got)
}
@@ -85,3 +91,5 @@ ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT en
#else
ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0xc, "Unexpected GOT/PLT entries detected!")
#endif
+
+ASSERT(SIZEOF(.rel.dyn) == 0 && SIZEOF(.rela.dyn) == 0, "Unexpected runtime relocations detected!")
--
2.26.2

2020-07-14 02:40:49

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v4 1/7] x86/boot/compressed: Move .got.plt entries out of the .got section

From: Ard Biesheuvel <[email protected]>

The .got.plt section contains the part of the GOT which is used by PLT
entries, and which gets updated lazily by the dynamic loader when
function calls are dispatched through those PLT entries.

On fully linked binaries such as the kernel proper or the decompressor,
this never happens, and so in practice, the .got.plt section consists
only of the first 3 magic entries that are meant to point at the _DYNAMIC
section and at the fixup routine in the loader. However, since we don't
use a dynamic loader, those entries are never populated or used.

This means that treating those entries like ordinary GOT entries, and
updating their values based on the actual placement of the executable in
memory is completely pointless, and we can just ignore the .got.plt
section entirely, provided that it has no additional entries beyond
the first 3 ones.

So add an assertion in the linker script to ensure that this assumption
holds, and move the contents out of the [_got, _egot) memory range that
is modified by the GOT fixup routines.

While at it, drop the KEEP(), since it has no effect on the contents
of output sections that are created by the linker itself.

Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/vmlinux.lds.S | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index 8f1025d1f681..b17d218ccdf9 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -44,10 +44,13 @@ SECTIONS
}
.got : {
_got = .;
- KEEP(*(.got.plt))
KEEP(*(.got))
_egot = .;
}
+ .got.plt : {
+ *(.got.plt)
+ }
+
.data : {
_data = . ;
*(.data)
@@ -77,3 +80,9 @@ SECTIONS

DISCARDS
}
+
+#ifdef CONFIG_X86_64
+ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT entries detected!")
+#else
+ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0xc, "Unexpected GOT/PLT entries detected!")
+#endif
--
2.26.2

2020-07-14 02:41:10

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v4 3/7] x86/boot/compressed: Get rid of GOT fixup code

From: Ard Biesheuvel <[email protected]>

In a previous patch, we have eliminated GOT entries from the decompressor
binary and added an assertion that the .got section is empty. This means
that the GOT fixup routines that exist in both the 32-bit and 64-bit
startup routines have become dead code, and can be removed.

While at it, drop the KEEP() from the linker script, as it has no effect
on the contents of output sections that are created by the linker itself.

Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/head_32.S | 24 ++---------
arch/x86/boot/compressed/head_64.S | 57 --------------------------
arch/x86/boot/compressed/vmlinux.lds.S | 4 +-
3 files changed, 5 insertions(+), 80 deletions(-)

diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 03557f2174bf..39f0bb43218f 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -49,16 +49,13 @@
* 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, _egot and _end, 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, _egot and _end as
- * hidden:
+ * _bss, _ebss and _end, 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 and _end as hidden:
*/
.hidden _bss
.hidden _ebss
- .hidden _got
- .hidden _egot
.hidden _end

__HEAD
@@ -192,19 +189,6 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
shrl $2, %ecx
rep stosl

-/*
- * Adjust our own GOT
- */
- leal _got(%ebx), %edx
- leal _egot(%ebx), %ecx
-1:
- cmpl %ecx, %edx
- jae 2f
- addl %ebx, (%edx)
- addl $4, %edx
- jmp 1b
-2:
-
/*
* Do the extraction, and jump to the new kernel..
*/
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 97d37f0a34f5..bf1ab30acc5b 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -40,8 +40,6 @@
*/
.hidden _bss
.hidden _ebss
- .hidden _got
- .hidden _egot
.hidden _end

__HEAD
@@ -353,25 +351,6 @@ SYM_CODE_START(startup_64)
/* Set up the stack */
leaq boot_stack_end(%rbx), %rsp

- /*
- * paging_prepare() and cleanup_trampoline() below can have GOT
- * references. Adjust the table with address we are running at.
- *
- * Zero RAX for adjust_got: the GOT was not adjusted before;
- * there's no adjustment to undo.
- */
- xorq %rax, %rax
-
- /*
- * Calculate the address the binary is loaded at and use it as
- * a GOT adjustment.
- */
- call 1f
-1: popq %rdi
- subq $1b, %rdi
-
- call .Ladjust_got
-
/*
* At this point we are in long mode with 4-level paging enabled,
* but we might want to enable 5-level paging or vice versa.
@@ -464,21 +443,6 @@ trampoline_return:
pushq $0
popfq

- /*
- * Previously we've adjusted the GOT with address the binary was
- * loaded at. Now we need to re-adjust for relocation address.
- *
- * Calculate the address the binary is loaded at, so that we can
- * undo the previous GOT adjustment.
- */
- call 1f
-1: popq %rax
- subq $1b, %rax
-
- /* The new adjustment is the relocation address */
- movq %rbx, %rdi
- call .Ladjust_got
-
/*
* Copy the compressed kernel to the end of our buffer
* where decompression in place becomes safe.
@@ -556,27 +520,6 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
jmp *%rax
SYM_FUNC_END(.Lrelocated)

-/*
- * Adjust the global offset table
- *
- * RAX is the previous adjustment of the table to undo (use 0 if it's the
- * first time we touch GOT).
- * RDI is the new adjustment to apply.
- */
-.Ladjust_got:
- /* Walk through the GOT adding the address to the entries */
- leaq _got(%rip), %rdx
- leaq _egot(%rip), %rcx
-1:
- cmpq %rcx, %rdx
- jae 2f
- subq %rax, (%rdx) /* Undo previous adjustment */
- addq %rdi, (%rdx) /* Apply the new adjustment */
- addq $8, %rdx
- jmp 1b
-2:
- ret
-
.code32
/*
* This is the 32-bit trampoline that will be copied over to low memory.
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index 4bcc943842ab..a4a4a59a2628 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -43,9 +43,7 @@ SECTIONS
_erodata = . ;
}
.got : {
- _got = .;
- KEEP(*(.got))
- _egot = .;
+ *(.got)
}
.got.plt : {
*(.got.plt)
--
2.26.2

2020-07-14 02:41:12

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v4 4/7] x86/boot: Add .text.* to setup.ld

gcc puts the main function into .text.startup when compiled with -Os (or
-O2). This results in arch/x86/boot/main.c having a .text.startup
section which is currently not included explicitly in the linker script
setup.ld in the same directory.

The BFD linker places this orphan section immediately after .text, so
this still works. However, LLD git, since [1], is choosing to place it
immediately after the .bstext section instead (this is the first code
section). This plays havoc with the section layout that setup.elf
requires to create the setup header, for eg on 64-bit:

LD arch/x86/boot/setup.elf
ld.lld: error: section .text.startup file range overlaps with .header
>>> .text.startup range is [0x200040, 0x2001FE]
>>> .header range is [0x2001EF, 0x20026B]

ld.lld: error: section .header file range overlaps with .bsdata
>>> .header range is [0x2001EF, 0x20026B]
>>> .bsdata range is [0x2001FF, 0x200398]

ld.lld: error: section .bsdata file range overlaps with .entrytext
>>> .bsdata range is [0x2001FF, 0x200398]
>>> .entrytext range is [0x20026C, 0x2002D3]

ld.lld: error: section .text.startup virtual address range overlaps
with .header
>>> .text.startup range is [0x40, 0x1FE]
>>> .header range is [0x1EF, 0x26B]

ld.lld: error: section .header virtual address range overlaps with
.bsdata
>>> .header range is [0x1EF, 0x26B]
>>> .bsdata range is [0x1FF, 0x398]

ld.lld: error: section .bsdata virtual address range overlaps with
.entrytext
>>> .bsdata range is [0x1FF, 0x398]
>>> .entrytext range is [0x26C, 0x2D3]

ld.lld: error: section .text.startup load address range overlaps with
.header
>>> .text.startup range is [0x40, 0x1FE]
>>> .header range is [0x1EF, 0x26B]

ld.lld: error: section .header load address range overlaps with
.bsdata
>>> .header range is [0x1EF, 0x26B]
>>> .bsdata range is [0x1FF, 0x398]

ld.lld: error: section .bsdata load address range overlaps with
.entrytext
>>> .bsdata range is [0x1FF, 0x398]
>>> .entrytext range is [0x26C, 0x2D3]

Add .text.* to the .text output section to fix this, and also prevent
any future surprises if the compiler decides to create other such
sections.

[1] https://reviews.llvm.org/D75225

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/setup.ld | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/boot/setup.ld b/arch/x86/boot/setup.ld
index 24c95522f231..49546c247ae2 100644
--- a/arch/x86/boot/setup.ld
+++ b/arch/x86/boot/setup.ld
@@ -20,7 +20,7 @@ SECTIONS
.initdata : { *(.initdata) }
__end_init = .;

- .text : { *(.text) }
+ .text : { *(.text .text.*) }
.text32 : { *(.text32) }

. = ALIGN(16);
--
2.26.2

2020-07-14 02:41:36

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v4 5/7] x86/boot: Remove run-time relocations from .head.text code

The assembly code in head_{32,64}.S, while meant to be
position-independent, generates run-time relocations because it uses
instructions such as
leal gdt(%edx), %eax
which make the assembler and linker think that the code is using %edx as
an index into gdt, and hence gdt needs to be relocated to its run-time
address.

On 32-bit, with lld Dmitry Golovin reports that this results in a
link-time error with default options (i.e. unless -z notext is
explicitly passed):
LD arch/x86/boot/compressed/vmlinux
ld.lld: error: can't create dynamic relocation R_386_32 against local
symbol in readonly segment; recompile object files with -fPIC or pass
'-Wl,-z,notext' to allow text relocations in the output

With the BFD linker, this generates a warning during the build, if
--warn-shared-textrel is enabled, which at least Gentoo enables by
default:
LD arch/x86/boot/compressed/vmlinux
ld: arch/x86/boot/compressed/head_32.o: warning: relocation in read-only section `.head.text'
ld: warning: creating a DT_TEXTREL in object

On 64-bit, it is not possible to link the kernel as -pie with lld, and
it is only possible with a BFD linker that supports -z noreloc-overflow,
i.e. versions >2.26. This is because these instructions cannot really be
relocated: the displacement field is only 32-bits wide, and thus cannot
be relocated for a 64-bit load address. The -z noreloc-overflow option
simply overrides the linker error, and results in R_X86_64_RELATIVE
relocations that apply a 64-bit relocation to a 32-bit field anyway.
This happens to work because nothing will process these run-time
relocations.

Start fixing this by removing relocations from .head.text:
- On 32-bit, use a base register that holds the address of the GOT and
reference symbol addresses using @GOTOFF, i.e.
leal gdt@GOTOFF(%edx), %eax
- On 64-bit, most of the code can (and already does) use %rip-relative
addressing, however the .code32 bits can't, and the 64-bit code also
needs to reference symbol addresses as they will be after moving the
compressed kernel to the end of the decompression buffer.
For these cases, reference the symbols as an offset to startup_32 to
avoid creating relocations, i.e.
leal (gdt-startup_32)(%bp), %eax
This only works in .head.text as the subtraction cannot be represented
as a PC-relative relocation unless startup_32 is in the same section
as the code. Move efi32_pe_entry into .head.text so that it can use
the same method to avoid relocations.

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/compressed/head_32.S | 64 +++++++-----------
arch/x86/boot/compressed/head_64.S | 104 ++++++++++++++++++-----------
2 files changed, 90 insertions(+), 78 deletions(-)

diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 39f0bb43218f..8c1a4f5610f5 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -33,26 +33,10 @@
#include <asm/bootparam.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 and _end, 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 and _end as hidden:
+ * These symbols needed to be marked as .hidden to prevent the BFD linker from
+ * generating R_386_32 (rather than R_386_RELATIVE) relocations for them when
+ * the 32-bit compressed kernel is linked as PIE. This is no longer necessary,
+ * but it doesn't hurt to keep them .hidden.
*/
.hidden _bss
.hidden _ebss
@@ -74,10 +58,10 @@ SYM_FUNC_START(startup_32)
leal (BP_scratch+4)(%esi), %esp
call 1f
1: popl %edx
- subl $1b, %edx
+ addl $_GLOBAL_OFFSET_TABLE_+(.-1b), %edx

/* Load new GDT */
- leal gdt(%edx), %eax
+ leal gdt@GOTOFF(%edx), %eax
movl %eax, 2(%eax)
lgdt (%eax)

@@ -90,14 +74,16 @@ SYM_FUNC_START(startup_32)
movl %eax, %ss

/*
- * %edx contains the address we are loaded at by the boot loader and %ebx
- * contains the address where we should move the kernel image temporarily
- * for safe in-place decompression. %ebp contains the address that the kernel
- * will be decompressed to.
+ * %edx contains the address we are loaded at by the boot loader (plus the
+ * offset to the GOT). The below code calculates %ebx to be the address where
+ * we should move the kernel image temporarily for safe in-place decompression
+ * (again, plus the offset to the GOT).
+ *
+ * %ebp is calculated to be the address that the kernel will be decompressed to.
*/

#ifdef CONFIG_RELOCATABLE
- movl %edx, %ebx
+ leal startup_32@GOTOFF(%edx), %ebx

#ifdef CONFIG_EFI_STUB
/*
@@ -108,7 +94,7 @@ SYM_FUNC_START(startup_32)
* image_offset = startup_32 - image_base
* Otherwise image_offset will be zero and has no effect on the calculations.
*/
- subl image_offset(%edx), %ebx
+ subl image_offset@GOTOFF(%edx), %ebx
#endif

movl BP_kernel_alignment(%esi), %eax
@@ -125,10 +111,10 @@ SYM_FUNC_START(startup_32)
movl %ebx, %ebp // Save the output address for later
/* Target address to relocate to for decompression */
addl BP_init_size(%esi), %ebx
- subl $_end, %ebx
+ subl $_end@GOTOFF, %ebx

/* Set up the stack */
- leal boot_stack_end(%ebx), %esp
+ leal boot_stack_end@GOTOFF(%ebx), %esp

/* Zero EFLAGS */
pushl $0
@@ -139,8 +125,8 @@ SYM_FUNC_START(startup_32)
* where decompression in place becomes safe.
*/
pushl %esi
- leal (_bss-4)(%edx), %esi
- leal (_bss-4)(%ebx), %edi
+ leal (_bss@GOTOFF-4)(%edx), %esi
+ leal (_bss@GOTOFF-4)(%ebx), %edi
movl $(_bss - startup_32), %ecx
shrl $2, %ecx
std
@@ -153,14 +139,14 @@ SYM_FUNC_START(startup_32)
* during extract_kernel below. To avoid any issues, repoint the GDTR
* to the new copy of the GDT.
*/
- leal gdt(%ebx), %eax
+ leal gdt@GOTOFF(%ebx), %eax
movl %eax, 2(%eax)
lgdt (%eax)

/*
* Jump to the relocated address.
*/
- leal .Lrelocated(%ebx), %eax
+ leal .Lrelocated@GOTOFF(%ebx), %eax
jmp *%eax
SYM_FUNC_END(startup_32)

@@ -170,7 +156,7 @@ SYM_FUNC_START_ALIAS(efi_stub_entry)
add $0x4, %esp
movl 8(%esp), %esi /* save boot_params pointer */
call efi_main
- leal startup_32(%eax), %eax
+ /* efi_main returns the possibly relocated address of startup_32 */
jmp *%eax
SYM_FUNC_END(efi32_stub_entry)
SYM_FUNC_END_ALIAS(efi_stub_entry)
@@ -183,8 +169,8 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
* Clear BSS (stack is currently empty)
*/
xorl %eax, %eax
- leal _bss(%ebx), %edi
- leal _ebss(%ebx), %ecx
+ leal _bss@GOTOFF(%ebx), %edi
+ leal _ebss@GOTOFF(%ebx), %ecx
subl %edi, %ecx
shrl $2, %ecx
rep stosl
@@ -198,9 +184,9 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
pushl %ebp /* output address */

pushl $z_input_len /* input_len */
- leal input_data(%ebx), %eax
+ leal input_data@GOTOFF(%ebx), %eax
pushl %eax /* input_data */
- leal boot_heap(%ebx), %eax
+ leal boot_heap@GOTOFF(%ebx), %eax
pushl %eax /* heap area */
pushl %esi /* real mode pointer */
call extract_kernel /* returns kernel location in %eax */
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index bf1ab30acc5b..11429092c224 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -43,6 +43,32 @@
.hidden _end

__HEAD
+
+/*
+ * This macro gives the relative virtual address of X, i.e. the offset of X
+ * from startup_32. This is the same as the link-time virtual address of X,
+ * since startup_32 is at 0, but defining it this way tells the
+ * assembler/linker that we do not want the actual run-time address of X. This
+ * prevents the linker from trying to create unwanted run-time relocation
+ * entries for the reference when the compressed kernel is linked as PIE.
+ *
+ * A reference X(%reg) will result in the link-time VA of X being stored with
+ * the instruction, and a run-time R_X86_64_RELATIVE relocation entry that
+ * adds the 64-bit base address where the kernel is loaded.
+ *
+ * Replacing it with (X-startup_32)(%reg) results in the offset being stored,
+ * and no run-time relocation.
+ *
+ * The macro should be used as a displacement with a base register containing
+ * the run-time address of startup_32 [i.e. rva(X)(%reg)], or as an immediate
+ * [$ rva(X)].
+ *
+ * This macro can only be used from within the .head.text section, since the
+ * expression requires startup_32 to be in the same section as the code being
+ * assembled.
+ */
+#define rva(X) ((X) - startup_32)
+
.code32
SYM_FUNC_START(startup_32)
/*
@@ -65,10 +91,10 @@ SYM_FUNC_START(startup_32)
leal (BP_scratch+4)(%esi), %esp
call 1f
1: popl %ebp
- subl $1b, %ebp
+ subl $ rva(1b), %ebp

/* Load new GDT with the 64bit segments using 32bit descriptor */
- leal gdt(%ebp), %eax
+ leal rva(gdt)(%ebp), %eax
movl %eax, 2(%eax)
lgdt (%eax)

@@ -81,7 +107,7 @@ SYM_FUNC_START(startup_32)
movl %eax, %ss

/* setup a stack and make sure cpu supports long mode. */
- leal boot_stack_end(%ebp), %esp
+ leal rva(boot_stack_end)(%ebp), %esp

call verify_cpu
testl %eax, %eax
@@ -108,7 +134,7 @@ SYM_FUNC_START(startup_32)
* image_offset = startup_32 - image_base
* Otherwise image_offset will be zero and has no effect on the calculations.
*/
- subl image_offset(%ebp), %ebx
+ subl rva(image_offset)(%ebp), %ebx
#endif

movl BP_kernel_alignment(%esi), %eax
@@ -124,7 +150,7 @@ SYM_FUNC_START(startup_32)

/* Target address to relocate to for decompression */
addl BP_init_size(%esi), %ebx
- subl $_end, %ebx
+ subl $ rva(_end), %ebx

/*
* Prepare for entering 64 bit mode
@@ -152,19 +178,19 @@ SYM_FUNC_START(startup_32)
1:

/* Initialize Page tables to 0 */
- leal pgtable(%ebx), %edi
+ leal rva(pgtable)(%ebx), %edi
xorl %eax, %eax
movl $(BOOT_INIT_PGT_SIZE/4), %ecx
rep stosl

/* Build Level 4 */
- leal pgtable + 0(%ebx), %edi
+ leal rva(pgtable + 0)(%ebx), %edi
leal 0x1007 (%edi), %eax
movl %eax, 0(%edi)
addl %edx, 4(%edi)

/* Build Level 3 */
- leal pgtable + 0x1000(%ebx), %edi
+ leal rva(pgtable + 0x1000)(%ebx), %edi
leal 0x1007(%edi), %eax
movl $4, %ecx
1: movl %eax, 0x00(%edi)
@@ -175,7 +201,7 @@ SYM_FUNC_START(startup_32)
jnz 1b

/* Build Level 2 */
- leal pgtable + 0x2000(%ebx), %edi
+ leal rva(pgtable + 0x2000)(%ebx), %edi
movl $0x00000183, %eax
movl $2048, %ecx
1: movl %eax, 0(%edi)
@@ -186,7 +212,7 @@ SYM_FUNC_START(startup_32)
jnz 1b

/* Enable the boot page tables */
- leal pgtable(%ebx), %eax
+ leal rva(pgtable)(%ebx), %eax
movl %eax, %cr3

/* Enable Long mode in EFER (Extended Feature Enable Register) */
@@ -211,14 +237,14 @@ SYM_FUNC_START(startup_32)
* We place all of the values on our mini stack so lret can
* used to perform that far jump.
*/
- leal startup_64(%ebp), %eax
+ leal rva(startup_64)(%ebp), %eax
#ifdef CONFIG_EFI_MIXED
- movl efi32_boot_args(%ebp), %edi
+ movl rva(efi32_boot_args)(%ebp), %edi
cmp $0, %edi
jz 1f
- leal efi64_stub_entry(%ebp), %eax
- movl efi32_boot_args+4(%ebp), %esi
- movl efi32_boot_args+8(%ebp), %edx // saved bootparams pointer
+ leal rva(efi64_stub_entry)(%ebp), %eax
+ movl rva(efi32_boot_args+4)(%ebp), %esi
+ movl rva(efi32_boot_args+8)(%ebp), %edx // saved bootparams pointer
cmpl $0, %edx
jnz 1f
/*
@@ -229,7 +255,7 @@ SYM_FUNC_START(startup_32)
* the correct stack alignment for entry.
*/
subl $40, %esp
- leal efi_pe_entry(%ebp), %eax
+ leal rva(efi_pe_entry)(%ebp), %eax
movl %edi, %ecx // MS calling convention
movl %esi, %edx
1:
@@ -255,18 +281,18 @@ SYM_FUNC_START(efi32_stub_entry)

call 1f
1: pop %ebp
- subl $1b, %ebp
+ subl $ rva(1b), %ebp

- movl %esi, efi32_boot_args+8(%ebp)
+ movl %esi, rva(efi32_boot_args+8)(%ebp)
SYM_INNER_LABEL(efi32_pe_stub_entry, SYM_L_LOCAL)
- movl %ecx, efi32_boot_args(%ebp)
- movl %edx, efi32_boot_args+4(%ebp)
- movb $0, efi_is64(%ebp)
+ movl %ecx, rva(efi32_boot_args)(%ebp)
+ movl %edx, rva(efi32_boot_args+4)(%ebp)
+ movb $0, rva(efi_is64)(%ebp)

/* Save firmware GDTR and code/data selectors */
- sgdtl efi32_boot_gdt(%ebp)
- movw %cs, efi32_boot_cs(%ebp)
- movw %ds, efi32_boot_ds(%ebp)
+ sgdtl rva(efi32_boot_gdt)(%ebp)
+ movw %cs, rva(efi32_boot_cs)(%ebp)
+ movw %ds, rva(efi32_boot_ds)(%ebp)

/* Disable paging */
movl %cr0, %eax
@@ -345,11 +371,11 @@ SYM_CODE_START(startup_64)

/* Target address to relocate to for decompression */
movl BP_init_size(%rsi), %ebx
- subl $_end, %ebx
+ subl $ rva(_end), %ebx
addq %rbp, %rbx

/* Set up the stack */
- leaq boot_stack_end(%rbx), %rsp
+ leaq rva(boot_stack_end)(%rbx), %rsp

/*
* At this point we are in long mode with 4-level paging enabled,
@@ -423,7 +449,7 @@ SYM_CODE_START(startup_64)
lretq
trampoline_return:
/* Restore the stack, the 32-bit trampoline uses its own stack */
- leaq boot_stack_end(%rbx), %rsp
+ leaq rva(boot_stack_end)(%rbx), %rsp

/*
* cleanup_trampoline() would restore trampoline memory.
@@ -435,7 +461,7 @@ trampoline_return:
* this function call.
*/
pushq %rsi
- leaq top_pgtable(%rbx), %rdi
+ leaq rva(top_pgtable)(%rbx), %rdi
call cleanup_trampoline
popq %rsi

@@ -449,9 +475,9 @@ trampoline_return:
*/
pushq %rsi
leaq (_bss-8)(%rip), %rsi
- leaq (_bss-8)(%rbx), %rdi
- movq $_bss /* - $startup_32 */, %rcx
- shrq $3, %rcx
+ leaq rva(_bss-8)(%rbx), %rdi
+ movl $(_bss - startup_32), %ecx
+ shrl $3, %ecx
std
rep movsq
cld
@@ -462,15 +488,15 @@ trampoline_return:
* during extract_kernel below. To avoid any issues, repoint the GDTR
* to the new copy of the GDT.
*/
- leaq gdt64(%rbx), %rax
- leaq gdt(%rbx), %rdx
+ leaq rva(gdt64)(%rbx), %rax
+ leaq rva(gdt)(%rbx), %rdx
movq %rdx, 2(%rax)
lgdt (%rax)

/*
* Jump to the relocated address.
*/
- leaq .Lrelocated(%rbx), %rax
+ leaq rva(.Lrelocated)(%rbx), %rax
jmp *%rax
SYM_CODE_END(startup_64)

@@ -482,7 +508,7 @@ SYM_FUNC_START_ALIAS(efi_stub_entry)
movq %rdx, %rbx /* save boot_params pointer */
call efi_main
movq %rbx,%rsi
- leaq startup_64(%rax), %rax
+ leaq rva(startup_64)(%rax), %rax
jmp *%rax
SYM_FUNC_END(efi64_stub_entry)
SYM_FUNC_END_ALIAS(efi_stub_entry)
@@ -645,7 +671,7 @@ SYM_DATA(efi_is64, .byte 1)
#define BS32_handle_protocol 88 // offsetof(efi_boot_services_32_t, handle_protocol)
#define LI32_image_base 32 // offsetof(efi_loaded_image_32_t, image_base)

- .text
+ __HEAD
.code32
SYM_FUNC_START(efi32_pe_entry)
/*
@@ -667,12 +693,12 @@ SYM_FUNC_START(efi32_pe_entry)

call 1f
1: pop %ebx
- subl $1b, %ebx
+ subl $ rva(1b), %ebx

/* Get the loaded image protocol pointer from the image handle */
leal -4(%ebp), %eax
pushl %eax // &loaded_image
- leal loaded_image_proto(%ebx), %eax
+ leal rva(loaded_image_proto)(%ebx), %eax
pushl %eax // pass the GUID address
pushl 8(%ebp) // pass the image handle

@@ -707,7 +733,7 @@ SYM_FUNC_START(efi32_pe_entry)
* use it before we get to the 64-bit efi_pe_entry() in C code.
*/
subl %esi, %ebx
- movl %ebx, image_offset(%ebp) // save image_offset
+ movl %ebx, rva(image_offset)(%ebp) // save image_offset
jmp efi32_pe_stub_entry

2: popl %edi // restore callee-save registers
--
2.26.2

2020-07-14 13:17:41

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 4:38 AM Arvind Sankar <[email protected]> wrote:
>
> The compressed kernel currently contains bogus runtime relocations in
> the startup code in head_{32,64}.S, which are generated by the linker,
> but must not actually be processed at runtime.
>
> This generates warnings when linking with the BFD linker, and errors
> with LLD, which defaults to erroring on runtime relocations in read-only
> sections. It also requires the -z noreloc-overflow hack for the 64-bit
> kernel, which prevents us from linking it as -pie on an older BFD linker
> (<= 2.26) or on LLD, because the locations that are to be apparently
> relocated are only 32-bits in size and so cannot really have
> R_X86_64_RELATIVE relocations.
>
> This series aims to get rid of these relocations. I've build- and
> boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
> gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
> has other issues [0].
>
> The first three patches by Ard remove indirection via the GOT from the
> compressed kernel code.
>
> The next patch is an independent fix for LLD, to avoid an orphan
> section in arch/x86/boot/setup.elf.
>
> The fifth patch gets rid of almost all the relocations. It uses
> standard PIC addressing technique for 32-bit, i.e. loading a register
> with the address of _GLOBAL_OFFSET_TABLE_ and then using GOTOFF
> references to access variables. For 64-bit, there is 32-bit code that
> cannot use RIP-relative addressing, and also cannot use the 32-bit
> method, since GOTOFF references are 64-bit only. This is instead handled
> using a macro to replace a reference like gdt with (gdt-startup_32)
> instead. The assembler will generate a PC32 relocation entry, with
> addend set to (.-startup_32), and these will be replaced with constants
> at link time. This works as long as all the code using such references
> lives in the same section as startup_32, i.e. in .head.text.
>
> The sixth patch addresses a remaining issue with the BFD linker, which
> generates runtime relocations for absolute symbols. We use z_input_len
> and z_output_len, defined in the generated piggy.S file, as symbols
> whose absolute "addresses" are actually the size of the compressed
> payload and the size of the decompressed kernel image respectively. LLD
> does not generate relocations for these two symbols, but the BFD linker
> does, prior to the upcoming 2.35. To get around this, piggy.S is
> extended to also define two u32 variables (in .rodata) with the lengths,
> and the head code is modified to use those instead of the symbol
> addresses.
>
> An alternative way to handle z_input_len/z_output_len would be to just
> include piggy.S in head_{32,64}.S instead of as a separate object file,
> since the GNU assembler doesn't generate relocations for symbols set to
> constants.
>
> The last patch adds a check in the linker script to ensure that no
> runtime relocations get reintroduced.
>
> [0] https://lore.kernel.org/lkml/[email protected]/
>

How to test this series without building a full new kernel?

make $make_opts vmlinux

- Sedat -

> Changes from v3:
> - Move hidden.h to include/linux so the EFI stub and the compressed
> kernel can share the same file
>
> Changes from v2:
> - Incorporate Ard's patches for eliminating GOT references into this
> series
> - Rebase on v5.8-rc3
>
> v2: https://lore.kernel.org/lkml/[email protected]/
>
> Changes from v1:
> - Add .text.* to setup.ld instead of just .text.startup
> - Rename the la() macro introduced in the second patch for 64-bit to
> rva(), and rework the explanatory comment.
> - In the last patch, check both .rel.dyn and .rela.dyn, instead of just
> one per arch.
>
>
> Ard Biesheuvel (3):
> x86/boot/compressed: Move .got.plt entries out of the .got section
> x86/boot/compressed: Force hidden visibility for all symbol references
> x86/boot/compressed: Get rid of GOT fixup code
>
> Arvind Sankar (4):
> x86/boot: Add .text.* to setup.ld
> x86/boot: Remove run-time relocations from .head.text code
> x86/boot: Remove runtime relocations from head_{32,64}.S
> x86/boot: Check that there are no runtime relocations
>
> arch/x86/boot/compressed/Makefile | 37 +-----
> arch/x86/boot/compressed/head_32.S | 99 +++++----------
> arch/x86/boot/compressed/head_64.S | 165 ++++++++++---------------
> arch/x86/boot/compressed/mkpiggy.c | 6 +
> arch/x86/boot/compressed/vmlinux.lds.S | 24 +++-
> arch/x86/boot/setup.ld | 2 +-
> drivers/firmware/efi/libstub/Makefile | 2 +-
> drivers/firmware/efi/libstub/hidden.h | 6 -
> include/linux/hidden.h | 19 +++
> 9 files changed, 152 insertions(+), 208 deletions(-)
> delete mode 100644 drivers/firmware/efi/libstub/hidden.h
> create mode 100644 include/linux/hidden.h
>
>
> base-commit: 11ba468877bb23f28956a35e896356252d63c983
> --
> 2.26.2
>

2020-07-14 14:16:54

by Arvind Sankar

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 03:15:49PM +0200, Sedat Dilek wrote:
>
> How to test this series without building a full new kernel?
>
> make $make_opts vmlinux
>
> - Sedat -
>

Not sure I understood the question: you do have to build a full new
kernel with this patch series to test it. Since the changes are to the
compressed kernel, you would need to make bzImage, which is the end
product of the full kernel build.

Thanks.

2020-07-14 18:17:00

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 4:15 PM Arvind Sankar <[email protected]> wrote:
>
> On Tue, Jul 14, 2020 at 03:15:49PM +0200, Sedat Dilek wrote:
> >
> > How to test this series without building a full new kernel?
> >
> > make $make_opts vmlinux
> >
> > - Sedat -
> >
>
> Not sure I understood the question: you do have to build a full new
> kernel with this patch series to test it. Since the changes are to the
> compressed kernel, you would need to make bzImage, which is the end
> product of the full kernel build.
>
> Thanks.
>

Thanks for the informations.

make bzImage

...was the last I tried - gasped 15mins on this - after gasping 30mins on

make vmlinux

I did a full new build...

...and it fails with ld.lld-11 as linker:

ld.lld-11 -m elf_x86_64 -pie --no-dynamic-linker -r -o
arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o
-T arch/x86/boot/compressed/.tmp_misc.ver; mv -f
arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o;
rm -f arch/x86/boot/compressed/.tmp_misc.ver; fi
*** ld.lld-11: error: -r and -pie may not be used together ***
make[5]: *** [scripts/Makefile.build:281:
arch/x86/boot/compressed/misc.o] Error 1

It's annoying to fail on the last minutes of a build.
Sorry for being very honest.

- Sedat -

2020-07-14 18:32:09

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

> I did a full new build...
>
> ...and it fails with ld.lld-11 as linker:
>
> ld.lld-11 -m elf_x86_64 -pie --no-dynamic-linker -r -o
> arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o
> -T arch/x86/boot/compressed/.tmp_misc.ver; mv -f
> arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o;
> rm -f arch/x86/boot/compressed/.tmp_misc.ver; fi
> *** ld.lld-11: error: -r and -pie may not be used together ***
> make[5]: *** [scripts/Makefile.build:281:
> arch/x86/boot/compressed/misc.o] Error 1
>
> It's annoying to fail on the last minutes of a build.
> Sorry for being very honest.
>

I applied this diff...

$ git diff arch/x86/boot/compressed/Makefile
diff --git a/arch/x86/boot/compressed/Makefile
b/arch/x86/boot/compressed/Makefile
index 789d5d14d8b0..9ba52a656838 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -51,7 +51,10 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
+# LLD linker does not allow -r and -pie options to be used together.
+ifndef CONFIG_LD_IS_LLD
KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
+endif
LDFLAGS_vmlinux := -T

hostprogs := mkpiggy

...and was able to build, assemble, link arch/x86/boot/compressed/*.

- Sedat -

2020-07-14 18:34:32

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 8:30 PM Sedat Dilek <[email protected]> wrote:
>
> > I did a full new build...
> >
> > ...and it fails with ld.lld-11 as linker:
> >
> > ld.lld-11 -m elf_x86_64 -pie --no-dynamic-linker -r -o
> > arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o
> > -T arch/x86/boot/compressed/.tmp_misc.ver; mv -f
> > arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o;
> > rm -f arch/x86/boot/compressed/.tmp_misc.ver; fi
> > *** ld.lld-11: error: -r and -pie may not be used together ***
> > make[5]: *** [scripts/Makefile.build:281:
> > arch/x86/boot/compressed/misc.o] Error 1
> >
> > It's annoying to fail on the last minutes of a build.
> > Sorry for being very honest.
> >
>
> I applied this diff...
>
> $ git diff arch/x86/boot/compressed/Makefile
> diff --git a/arch/x86/boot/compressed/Makefile
> b/arch/x86/boot/compressed/Makefile
> index 789d5d14d8b0..9ba52a656838 100644
> --- a/arch/x86/boot/compressed/Makefile
> +++ b/arch/x86/boot/compressed/Makefile
> @@ -51,7 +51,10 @@ UBSAN_SANITIZE :=n
> KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> # Compressed kernel should be built as PIE since it may be loaded at any
> # address by the bootloader.
> +# LLD linker does not allow -r and -pie options to be used together.
> +ifndef CONFIG_LD_IS_LLD
> KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
> +endif
> LDFLAGS_vmlinux := -T
>
> hostprogs := mkpiggy
>
> ...and was able to build, assemble, link arch/x86/boot/compressed/*.
>

I checked my last succesfull build without your patchset:

$ grep no-dynamic-linker build-log_5.8.0-rc5-1-amd64-llvm11-ias.txt
[ EMPTY ]

- Sedat -

2020-07-14 19:24:44

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 8:33 PM Sedat Dilek <[email protected]> wrote:
>
> On Tue, Jul 14, 2020 at 8:30 PM Sedat Dilek <[email protected]> wrote:
> >
> > > I did a full new build...
> > >
> > > ...and it fails with ld.lld-11 as linker:
> > >
> > > ld.lld-11 -m elf_x86_64 -pie --no-dynamic-linker -r -o
> > > arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o
> > > -T arch/x86/boot/compressed/.tmp_misc.ver; mv -f
> > > arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o;
> > > rm -f arch/x86/boot/compressed/.tmp_misc.ver; fi
> > > *** ld.lld-11: error: -r and -pie may not be used together ***
> > > make[5]: *** [scripts/Makefile.build:281:
> > > arch/x86/boot/compressed/misc.o] Error 1
> > >
> > > It's annoying to fail on the last minutes of a build.
> > > Sorry for being very honest.
> > >
> >
> > I applied this diff...
> >
> > $ git diff arch/x86/boot/compressed/Makefile
> > diff --git a/arch/x86/boot/compressed/Makefile
> > b/arch/x86/boot/compressed/Makefile
> > index 789d5d14d8b0..9ba52a656838 100644
> > --- a/arch/x86/boot/compressed/Makefile
> > +++ b/arch/x86/boot/compressed/Makefile
> > @@ -51,7 +51,10 @@ UBSAN_SANITIZE :=n
> > KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> > # Compressed kernel should be built as PIE since it may be loaded at any
> > # address by the bootloader.
> > +# LLD linker does not allow -r and -pie options to be used together.
> > +ifndef CONFIG_LD_IS_LLD
> > KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
> > +endif
> > LDFLAGS_vmlinux := -T
> >
> > hostprogs := mkpiggy
> >
> > ...and was able to build, assemble, link arch/x86/boot/compressed/*.
> >
>
> I checked my last succesfull build without your patchset:
>
> $ grep no-dynamic-linker build-log_5.8.0-rc5-1-amd64-llvm11-ias.txt
> [ EMPTY ]
>

I was able to boot on bare metal.

Feel free to add my...

Tested-by: Sedat Dilek <[email protected]>

...when you restore "old" behaviour when CONFIG_LD_IS_LLD=y (apply or
fold-in my diff with comment)...

...and use one "runtime" in your subject-line:

$ git log --oneline
v5.8-rc5..for-5.8/x86-boot-compressed-remove-runtime-relocations-nivedita-v4
| egrep 'runtime|run-time'
9547f8f08689 x86/boot: Check that there are no runtime relocations
ede02a307b30 x86/boot: Remove runtime relocations from head_{32,64}.S
525a67ac7ea9 x86/boot: Remove run-time relocations from .head.text code

Thanks.

- Sedat -

2020-07-14 19:34:15

by Arvind Sankar

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 08:30:14PM +0200, Sedat Dilek wrote:
> > I did a full new build...
> >
> > ...and it fails with ld.lld-11 as linker:
> >
> > ld.lld-11 -m elf_x86_64 -pie --no-dynamic-linker -r -o
> > arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o
> > -T arch/x86/boot/compressed/.tmp_misc.ver; mv -f
> > arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o;
> > rm -f arch/x86/boot/compressed/.tmp_misc.ver; fi
> > *** ld.lld-11: error: -r and -pie may not be used together ***
> > make[5]: *** [scripts/Makefile.build:281:
> > arch/x86/boot/compressed/misc.o] Error 1
> >
> > It's annoying to fail on the last minutes of a build.
> > Sorry for being very honest.
> >
>
> I applied this diff...
>
> $ git diff arch/x86/boot/compressed/Makefile
> diff --git a/arch/x86/boot/compressed/Makefile
> b/arch/x86/boot/compressed/Makefile
> index 789d5d14d8b0..9ba52a656838 100644
> --- a/arch/x86/boot/compressed/Makefile
> +++ b/arch/x86/boot/compressed/Makefile
> @@ -51,7 +51,10 @@ UBSAN_SANITIZE :=n
> KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> # Compressed kernel should be built as PIE since it may be loaded at any
> # address by the bootloader.
> +# LLD linker does not allow -r and -pie options to be used together.
> +ifndef CONFIG_LD_IS_LLD
> KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
> +endif
> LDFLAGS_vmlinux := -T
>
> hostprogs := mkpiggy
>
> ...and was able to build, assemble, link arch/x86/boot/compressed/*.
>
> - Sedat -

Thanks for the test.

Can you share your .config? The error messages look like they're coming
from running modversions on misc.o, which is unexpected as it shouldn't
have any exported symbols, and it doesn't in my builds.

In any case, I think the right fix here would be to add -pie and
--no-dynamic-linker to LDFLAGS_vmlinux instead of KBUILD_LDFLAGS.

2020-07-14 19:57:18

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 9:29 PM Arvind Sankar <[email protected]> wrote:
>
> On Tue, Jul 14, 2020 at 08:30:14PM +0200, Sedat Dilek wrote:
> > > I did a full new build...
> > >
> > > ...and it fails with ld.lld-11 as linker:
> > >
> > > ld.lld-11 -m elf_x86_64 -pie --no-dynamic-linker -r -o
> > > arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o
> > > -T arch/x86/boot/compressed/.tmp_misc.ver; mv -f
> > > arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o;
> > > rm -f arch/x86/boot/compressed/.tmp_misc.ver; fi
> > > *** ld.lld-11: error: -r and -pie may not be used together ***
> > > make[5]: *** [scripts/Makefile.build:281:
> > > arch/x86/boot/compressed/misc.o] Error 1
> > >
> > > It's annoying to fail on the last minutes of a build.
> > > Sorry for being very honest.
> > >
> >
> > I applied this diff...
> >
> > $ git diff arch/x86/boot/compressed/Makefile
> > diff --git a/arch/x86/boot/compressed/Makefile
> > b/arch/x86/boot/compressed/Makefile
> > index 789d5d14d8b0..9ba52a656838 100644
> > --- a/arch/x86/boot/compressed/Makefile
> > +++ b/arch/x86/boot/compressed/Makefile
> > @@ -51,7 +51,10 @@ UBSAN_SANITIZE :=n
> > KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> > # Compressed kernel should be built as PIE since it may be loaded at any
> > # address by the bootloader.
> > +# LLD linker does not allow -r and -pie options to be used together.
> > +ifndef CONFIG_LD_IS_LLD
> > KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
> > +endif
> > LDFLAGS_vmlinux := -T
> >
> > hostprogs := mkpiggy
> >
> > ...and was able to build, assemble, link arch/x86/boot/compressed/*.
> >
> > - Sedat -
>
> Thanks for the test.
>
> Can you share your .config? The error messages look like they're coming
> from running modversions on misc.o, which is unexpected as it shouldn't
> have any exported symbols, and it doesn't in my builds.
>
> In any case, I think the right fix here would be to add -pie and
> --no-dynamic-linker to LDFLAGS_vmlinux instead of KBUILD_LDFLAGS.

Hmm, you might be right with moving to LDFLAGS_vmlinux.

Attached are my linux-config and dmesg-output.

- Sedat -


Attachments:
dmesg-T_5.8.0-rc5-2-amd64-llvm11-ias.txt (69.74 kB)
config-5.8.0-rc5-2-amd64-llvm11-ias (227.79 kB)
Download all attachments

2020-07-14 20:08:29

by Arvind Sankar

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 09:53:19PM +0200, Sedat Dilek wrote:
>
> Hmm, you might be right with moving to LDFLAGS_vmlinux.
>
> Attached are my linux-config and dmesg-output.
>
> - Sedat -

Which tree are you building against? I notice you have KERNEL_ZSTD
enabled, which hasn't been merged yet.

Are you using Nick's series [v7]?

Also from the naming -- are you using LLVM_IAS=1 with some patches to
make it work?

[v7] https://lore.kernel.org/lkml/[email protected]/

2020-07-14 20:11:09

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 10:07 PM Arvind Sankar <[email protected]> wrote:
>
> On Tue, Jul 14, 2020 at 09:53:19PM +0200, Sedat Dilek wrote:
> >
> > Hmm, you might be right with moving to LDFLAGS_vmlinux.
> >
> > Attached are my linux-config and dmesg-output.
> >
> > - Sedat -
>
> Which tree are you building against? I notice you have KERNEL_ZSTD
> enabled, which hasn't been merged yet.
>
> Are you using Nick's series [v7]?
>
> Also from the naming -- are you using LLVM_IAS=1 with some patches to
> make it work?
>
> [v7] https://lore.kernel.org/lkml/[email protected]/

Sorry for not telling you the full story.
Yes, I have some additional patches like Nick T. "zstd-v7" which
should IMHO not touch this area.

- Sedat -

2020-07-14 20:12:58

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 9:53 PM Sedat Dilek <[email protected]> wrote:
>
> On Tue, Jul 14, 2020 at 9:29 PM Arvind Sankar <[email protected]> wrote:
> >
> > On Tue, Jul 14, 2020 at 08:30:14PM +0200, Sedat Dilek wrote:
> > > > I did a full new build...
> > > >
> > > > ...and it fails with ld.lld-11 as linker:
> > > >
> > > > ld.lld-11 -m elf_x86_64 -pie --no-dynamic-linker -r -o
> > > > arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o
> > > > -T arch/x86/boot/compressed/.tmp_misc.ver; mv -f
> > > > arch/x86/boot/compressed/.tmp_misc.o arch/x86/boot/compressed/misc.o;
> > > > rm -f arch/x86/boot/compressed/.tmp_misc.ver; fi
> > > > *** ld.lld-11: error: -r and -pie may not be used together ***
> > > > make[5]: *** [scripts/Makefile.build:281:
> > > > arch/x86/boot/compressed/misc.o] Error 1
> > > >
> > > > It's annoying to fail on the last minutes of a build.
> > > > Sorry for being very honest.
> > > >
> > >
> > > I applied this diff...
> > >
> > > $ git diff arch/x86/boot/compressed/Makefile
> > > diff --git a/arch/x86/boot/compressed/Makefile
> > > b/arch/x86/boot/compressed/Makefile
> > > index 789d5d14d8b0..9ba52a656838 100644
> > > --- a/arch/x86/boot/compressed/Makefile
> > > +++ b/arch/x86/boot/compressed/Makefile
> > > @@ -51,7 +51,10 @@ UBSAN_SANITIZE :=n
> > > KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> > > # Compressed kernel should be built as PIE since it may be loaded at any
> > > # address by the bootloader.
> > > +# LLD linker does not allow -r and -pie options to be used together.
> > > +ifndef CONFIG_LD_IS_LLD
> > > KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
> > > +endif
> > > LDFLAGS_vmlinux := -T
> > >
> > > hostprogs := mkpiggy
> > >
> > > ...and was able to build, assemble, link arch/x86/boot/compressed/*.
> > >
> > > - Sedat -
> >
> > Thanks for the test.
> >
> > Can you share your .config? The error messages look like they're coming
> > from running modversions on misc.o, which is unexpected as it shouldn't
> > have any exported symbols, and it doesn't in my builds.
> >
> > In any case, I think the right fix here would be to add -pie and
> > --no-dynamic-linker to LDFLAGS_vmlinux instead of KBUILD_LDFLAGS.
>
> Hmm, you might be right with moving to LDFLAGS_vmlinux.
>

We will need the "ifndef CONFIG_LD_IS_LLD" as -r and -pie cannot be
used together.
Is that the or not the fact when moving to LDFLAGS_vmlinux?

I cannot test as I modified my local Git and re-invoking my
build-script is doing a whole new build-dance.

- Sedat -

2020-07-14 20:16:41

by Arvind Sankar

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 10:10:14PM +0200, Sedat Dilek wrote:
> On Tue, Jul 14, 2020 at 10:07 PM Arvind Sankar <[email protected]> wrote:
> >
> > On Tue, Jul 14, 2020 at 09:53:19PM +0200, Sedat Dilek wrote:
> > >
> > > Hmm, you might be right with moving to LDFLAGS_vmlinux.
> > >
> > > Attached are my linux-config and dmesg-output.
> > >
> > > - Sedat -
> >
> > Which tree are you building against? I notice you have KERNEL_ZSTD
> > enabled, which hasn't been merged yet.
> >
> > Are you using Nick's series [v7]?
> >
> > Also from the naming -- are you using LLVM_IAS=1 with some patches to
> > make it work?
> >
> > [v7] https://lore.kernel.org/lkml/[email protected]/
>
> Sorry for not telling you the full story.
> Yes, I have some additional patches like Nick T. "zstd-v7" which
> should IMHO not touch this area.
>
> - Sedat -

That series does touch boot/compressed, since the point is to add
support for zstd-compressed kernels. I'll need to test it out, the
__DISABLE_EXPORTS he's now using must not be working for some reason.

2020-07-14 20:19:14

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 10:14 PM Arvind Sankar <[email protected]> wrote:
>
> On Tue, Jul 14, 2020 at 10:10:14PM +0200, Sedat Dilek wrote:
> > On Tue, Jul 14, 2020 at 10:07 PM Arvind Sankar <[email protected]> wrote:
> > >
> > > On Tue, Jul 14, 2020 at 09:53:19PM +0200, Sedat Dilek wrote:
> > > >
> > > > Hmm, you might be right with moving to LDFLAGS_vmlinux.
> > > >
> > > > Attached are my linux-config and dmesg-output.
> > > >
> > > > - Sedat -
> > >
> > > Which tree are you building against? I notice you have KERNEL_ZSTD
> > > enabled, which hasn't been merged yet.
> > >
> > > Are you using Nick's series [v7]?
> > >
> > > Also from the naming -- are you using LLVM_IAS=1 with some patches to
> > > make it work?
> > >
> > > [v7] https://lore.kernel.org/lkml/[email protected]/
> >
> > Sorry for not telling you the full story.
> > Yes, I have some additional patches like Nick T. "zstd-v7" which
> > should IMHO not touch this area.
> >
> > - Sedat -
>
> That series does touch boot/compressed, since the point is to add
> support for zstd-compressed kernels. I'll need to test it out, the
> __DISABLE_EXPORTS he's now using must not be working for some reason.

I have seen this in the between zstd-v6 and zstd-v7 - diff attached.

- Sedat -


Attachments:
zstd-initramfs-terrelln-v6-VS-zstd-initramfs-terrelln-v7.diff (6.88 kB)

2020-07-14 20:30:12

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 10:21 PM Arvind Sankar <[email protected]> wrote:
>
> On Tue, Jul 14, 2020 at 10:08:04PM +0200, Sedat Dilek wrote:
> > > >
> > > > In any case, I think the right fix here would be to add -pie and
> > > > --no-dynamic-linker to LDFLAGS_vmlinux instead of KBUILD_LDFLAGS.
> > >
> > > Hmm, you might be right with moving to LDFLAGS_vmlinux.
> > >
> >
> > We will need the "ifndef CONFIG_LD_IS_LLD" as -r and -pie cannot be
> > used together.
> > Is that the or not the fact when moving to LDFLAGS_vmlinux?
>
> LDFLAGS_vmlinux will only be used to link boot/compressed/vmlinux,
> whereas KBUILD_LDFLAGS is used for all linker invocations, and in
> particular the little link to do the modversions stuff ends up using it.
>
> So once we move -pie --no-dynamic-linker to the more correct
> LDFLAGS_vmlinux and/or stop modversions running, we'll be fine. Being
> able to use -pie with lld is one of the goals of this series.
>

OK, I am doing a new full kernel build with:

$ git diff arch/x86/boot/compressed/Makefile
diff --git a/arch/x86/boot/compressed/Makefile
b/arch/x86/boot/compressed/Makefile
index 789d5d14d8b0..056a738e47c6 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -51,7 +51,7 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
-KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
+LDFLAGS_vmlinux += -pie $(call ld-option, --no-dynamic-linker)
LDFLAGS_vmlinux := -T

hostprogs := mkpiggy

- Sedat -

2020-07-14 20:30:56

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 10:24 PM Sedat Dilek <[email protected]> wrote:
>
> On Tue, Jul 14, 2020 at 10:21 PM Arvind Sankar <[email protected]> wrote:
> >
> > On Tue, Jul 14, 2020 at 10:08:04PM +0200, Sedat Dilek wrote:
> > > > >
> > > > > In any case, I think the right fix here would be to add -pie and
> > > > > --no-dynamic-linker to LDFLAGS_vmlinux instead of KBUILD_LDFLAGS.
> > > >
> > > > Hmm, you might be right with moving to LDFLAGS_vmlinux.
> > > >
> > >
> > > We will need the "ifndef CONFIG_LD_IS_LLD" as -r and -pie cannot be
> > > used together.
> > > Is that the or not the fact when moving to LDFLAGS_vmlinux?
> >
> > LDFLAGS_vmlinux will only be used to link boot/compressed/vmlinux,
> > whereas KBUILD_LDFLAGS is used for all linker invocations, and in
> > particular the little link to do the modversions stuff ends up using it.
> >
> > So once we move -pie --no-dynamic-linker to the more correct
> > LDFLAGS_vmlinux and/or stop modversions running, we'll be fine. Being
> > able to use -pie with lld is one of the goals of this series.
> >
>
> OK, I am doing a new full kernel build with:
>
> $ git diff arch/x86/boot/compressed/Makefile
> diff --git a/arch/x86/boot/compressed/Makefile
> b/arch/x86/boot/compressed/Makefile
> index 789d5d14d8b0..056a738e47c6 100644
> --- a/arch/x86/boot/compressed/Makefile
> +++ b/arch/x86/boot/compressed/Makefile
> @@ -51,7 +51,7 @@ UBSAN_SANITIZE :=n
> KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> # Compressed kernel should be built as PIE since it may be loaded at any
> # address by the bootloader.
> -KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
> +LDFLAGS_vmlinux += -pie $(call ld-option, --no-dynamic-linker)
> LDFLAGS_vmlinux := -T
>
> hostprogs := mkpiggy
>
> - Sedat -

Not my day - today.

$ git diff arch/x86/boot/compressed/Makefile
diff --git a/arch/x86/boot/compressed/Makefile
b/arch/x86/boot/compressed/Makefile
index 789d5d14d8b0..9784ed37a5d7 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -51,8 +51,8 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
-KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
LDFLAGS_vmlinux := -T
+LDFLAGS_vmlinux += -pie $(call ld-option, --no-dynamic-linker)

hostprogs := mkpiggy
HOST_EXTRACFLAGS += -I$(srctree)/tools/include

- Sedat -

2020-07-14 20:32:12

by Arvind Sankar

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 10:08:04PM +0200, Sedat Dilek wrote:
> > >
> > > In any case, I think the right fix here would be to add -pie and
> > > --no-dynamic-linker to LDFLAGS_vmlinux instead of KBUILD_LDFLAGS.
> >
> > Hmm, you might be right with moving to LDFLAGS_vmlinux.
> >
>
> We will need the "ifndef CONFIG_LD_IS_LLD" as -r and -pie cannot be
> used together.
> Is that the or not the fact when moving to LDFLAGS_vmlinux?

LDFLAGS_vmlinux will only be used to link boot/compressed/vmlinux,
whereas KBUILD_LDFLAGS is used for all linker invocations, and in
particular the little link to do the modversions stuff ends up using it.

So once we move -pie --no-dynamic-linker to the more correct
LDFLAGS_vmlinux and/or stop modversions running, we'll be fine. Being
able to use -pie with lld is one of the goals of this series.

>
> I cannot test as I modified my local Git and re-invoking my
> build-script is doing a whole new build-dance.
>
> - Sedat -

2020-07-14 20:36:14

by Arvind Sankar

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 10:24:07PM +0200, Sedat Dilek wrote:
> On Tue, Jul 14, 2020 at 10:21 PM Arvind Sankar <[email protected]> wrote:
> >
> > On Tue, Jul 14, 2020 at 10:08:04PM +0200, Sedat Dilek wrote:
> > > > >
> > > > > In any case, I think the right fix here would be to add -pie and
> > > > > --no-dynamic-linker to LDFLAGS_vmlinux instead of KBUILD_LDFLAGS.
> > > >
> > > > Hmm, you might be right with moving to LDFLAGS_vmlinux.
> > > >
> > >
> > > We will need the "ifndef CONFIG_LD_IS_LLD" as -r and -pie cannot be
> > > used together.
> > > Is that the or not the fact when moving to LDFLAGS_vmlinux?
> >
> > LDFLAGS_vmlinux will only be used to link boot/compressed/vmlinux,
> > whereas KBUILD_LDFLAGS is used for all linker invocations, and in
> > particular the little link to do the modversions stuff ends up using it.
> >
> > So once we move -pie --no-dynamic-linker to the more correct
> > LDFLAGS_vmlinux and/or stop modversions running, we'll be fine. Being
> > able to use -pie with lld is one of the goals of this series.
> >
>
> OK, I am doing a new full kernel build with:
>
> $ git diff arch/x86/boot/compressed/Makefile
> diff --git a/arch/x86/boot/compressed/Makefile
> b/arch/x86/boot/compressed/Makefile
> index 789d5d14d8b0..056a738e47c6 100644
> --- a/arch/x86/boot/compressed/Makefile
> +++ b/arch/x86/boot/compressed/Makefile
> @@ -51,7 +51,7 @@ UBSAN_SANITIZE :=n
> KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> # Compressed kernel should be built as PIE since it may be loaded at any
> # address by the bootloader.
> -KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
> +LDFLAGS_vmlinux += -pie $(call ld-option, --no-dynamic-linker)
> LDFLAGS_vmlinux := -T
>
> hostprogs := mkpiggy
>
> - Sedat -

That needs to be in just one line (or with the += on the -T line)
LDFLAGS_vmlinux := -pie $(call ld-option, --no-dynamic-linker) -T

Your second assignment will blow away the -pie.. flags.

2020-07-14 20:38:02

by Arvind Sankar

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 10:27:25PM +0200, Sedat Dilek wrote:
> On Tue, Jul 14, 2020 at 10:24 PM Sedat Dilek <[email protected]> wrote:
> >
> > On Tue, Jul 14, 2020 at 10:21 PM Arvind Sankar <[email protected]> wrote:
> > >
> > > On Tue, Jul 14, 2020 at 10:08:04PM +0200, Sedat Dilek wrote:
> > > > > >
> > > > > > In any case, I think the right fix here would be to add -pie and
> > > > > > --no-dynamic-linker to LDFLAGS_vmlinux instead of KBUILD_LDFLAGS.
> > > > >
> > > > > Hmm, you might be right with moving to LDFLAGS_vmlinux.
> > > > >
> > > >
> > > > We will need the "ifndef CONFIG_LD_IS_LLD" as -r and -pie cannot be
> > > > used together.
> > > > Is that the or not the fact when moving to LDFLAGS_vmlinux?
> > >
> > > LDFLAGS_vmlinux will only be used to link boot/compressed/vmlinux,
> > > whereas KBUILD_LDFLAGS is used for all linker invocations, and in
> > > particular the little link to do the modversions stuff ends up using it.
> > >
> > > So once we move -pie --no-dynamic-linker to the more correct
> > > LDFLAGS_vmlinux and/or stop modversions running, we'll be fine. Being
> > > able to use -pie with lld is one of the goals of this series.
> > >
> >
> > OK, I am doing a new full kernel build with:
> >
> > $ git diff arch/x86/boot/compressed/Makefile
> > diff --git a/arch/x86/boot/compressed/Makefile
> > b/arch/x86/boot/compressed/Makefile
> > index 789d5d14d8b0..056a738e47c6 100644
> > --- a/arch/x86/boot/compressed/Makefile
> > +++ b/arch/x86/boot/compressed/Makefile
> > @@ -51,7 +51,7 @@ UBSAN_SANITIZE :=n
> > KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> > # Compressed kernel should be built as PIE since it may be loaded at any
> > # address by the bootloader.
> > -KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
> > +LDFLAGS_vmlinux += -pie $(call ld-option, --no-dynamic-linker)
> > LDFLAGS_vmlinux := -T
> >
> > hostprogs := mkpiggy
> >
> > - Sedat -
>
> Not my day - today.
>
> $ git diff arch/x86/boot/compressed/Makefile
> diff --git a/arch/x86/boot/compressed/Makefile
> b/arch/x86/boot/compressed/Makefile
> index 789d5d14d8b0..9784ed37a5d7 100644
> --- a/arch/x86/boot/compressed/Makefile
> +++ b/arch/x86/boot/compressed/Makefile
> @@ -51,8 +51,8 @@ UBSAN_SANITIZE :=n
> KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> # Compressed kernel should be built as PIE since it may be loaded at any
> # address by the bootloader.
> -KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
> LDFLAGS_vmlinux := -T
> +LDFLAGS_vmlinux += -pie $(call ld-option, --no-dynamic-linker)
>
> hostprogs := mkpiggy
> HOST_EXTRACFLAGS += -I$(srctree)/tools/include
>
> - Sedat -

Nope -- -T needs to be last, since it's (trickily) put together with the
first prerequisite $(obj)/vmlinux.lds.

2020-07-14 20:46:41

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 10:35 PM Arvind Sankar <[email protected]> wrote:
>
> On Tue, Jul 14, 2020 at 10:27:25PM +0200, Sedat Dilek wrote:
> > On Tue, Jul 14, 2020 at 10:24 PM Sedat Dilek <[email protected]> wrote:
> > >
> > > On Tue, Jul 14, 2020 at 10:21 PM Arvind Sankar <[email protected]> wrote:
> > > >
> > > > On Tue, Jul 14, 2020 at 10:08:04PM +0200, Sedat Dilek wrote:
> > > > > > >
> > > > > > > In any case, I think the right fix here would be to add -pie and
> > > > > > > --no-dynamic-linker to LDFLAGS_vmlinux instead of KBUILD_LDFLAGS.
> > > > > >
> > > > > > Hmm, you might be right with moving to LDFLAGS_vmlinux.
> > > > > >
> > > > >
> > > > > We will need the "ifndef CONFIG_LD_IS_LLD" as -r and -pie cannot be
> > > > > used together.
> > > > > Is that the or not the fact when moving to LDFLAGS_vmlinux?
> > > >
> > > > LDFLAGS_vmlinux will only be used to link boot/compressed/vmlinux,
> > > > whereas KBUILD_LDFLAGS is used for all linker invocations, and in
> > > > particular the little link to do the modversions stuff ends up using it.
> > > >
> > > > So once we move -pie --no-dynamic-linker to the more correct
> > > > LDFLAGS_vmlinux and/or stop modversions running, we'll be fine. Being
> > > > able to use -pie with lld is one of the goals of this series.
> > > >
> > >
> > > OK, I am doing a new full kernel build with:
> > >
> > > $ git diff arch/x86/boot/compressed/Makefile
> > > diff --git a/arch/x86/boot/compressed/Makefile
> > > b/arch/x86/boot/compressed/Makefile
> > > index 789d5d14d8b0..056a738e47c6 100644
> > > --- a/arch/x86/boot/compressed/Makefile
> > > +++ b/arch/x86/boot/compressed/Makefile
> > > @@ -51,7 +51,7 @@ UBSAN_SANITIZE :=n
> > > KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> > > # Compressed kernel should be built as PIE since it may be loaded at any
> > > # address by the bootloader.
> > > -KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
> > > +LDFLAGS_vmlinux += -pie $(call ld-option, --no-dynamic-linker)
> > > LDFLAGS_vmlinux := -T
> > >
> > > hostprogs := mkpiggy
> > >
> > > - Sedat -
> >
> > Not my day - today.
> >
> > $ git diff arch/x86/boot/compressed/Makefile
> > diff --git a/arch/x86/boot/compressed/Makefile
> > b/arch/x86/boot/compressed/Makefile
> > index 789d5d14d8b0..9784ed37a5d7 100644
> > --- a/arch/x86/boot/compressed/Makefile
> > +++ b/arch/x86/boot/compressed/Makefile
> > @@ -51,8 +51,8 @@ UBSAN_SANITIZE :=n
> > KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> > # Compressed kernel should be built as PIE since it may be loaded at any
> > # address by the bootloader.
> > -KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
> > LDFLAGS_vmlinux := -T
> > +LDFLAGS_vmlinux += -pie $(call ld-option, --no-dynamic-linker)
> >
> > hostprogs := mkpiggy
> > HOST_EXTRACFLAGS += -I$(srctree)/tools/include
> >
> > - Sedat -
>
> Nope -- -T needs to be last, since it's (trickily) put together with the
> first prerequisite $(obj)/vmlinux.lds.

Good I read this.

Checked the previous build-log:

ld.lld-11 -m elf_x86_64 -T arch/x86/boot/compressed/vmlinux.lds
arch/x86/boot/compressed/kernel_info.o
arch/x86/boot/compressed/head_64.o arch/x86/boot/compressed/misc.o
arch/x86/boot/compressed/string.o arch/x86/boot/compressed/cmdline.o
arch/x86/boot/compressed/error.o arch/x86/boot/compressed/piggy.o
arch/x86/boot/compressed/cpuflags.o
arch/x86/boot/compressed/early_serial_console.o
arch/x86/boot/compressed/kaslr.o arch/x86/boot/compressed/kaslr_64.o
arch/x86/boot/compressed/mem_encrypt.o
arch/x86/boot/compressed/pgtable_64.o arch/x86/boot/compressed/acpi.o
drivers/firmware/efi/libstub/lib.a
arch/x86/boot/compressed/efi_thunk_64.o -o
arch/x86/boot/compressed/vmlinux

So there is no -r option in this line.

If we move to LDFLAGS_vmlinux we can drop the "call ld-option" as both
linker GNU/ld.bfd and LLVM/lld.ld support this?

Do we need to adjust the comments?
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader

That's the minimal change needed:

$ git diff arch/x86/boot/compressed/Makefile
diff --git a/arch/x86/boot/compressed/Makefile
b/arch/x86/boot/compressed/Makefile
index 789d5d14d8b0..d0aafcd8cf6c 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -51,8 +51,8 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
-KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
-LDFLAGS_vmlinux := -T
+LDFLAGS_vmlinux := -pie $(call ld-option, --no-dynamic-linker)
+LDFLAGS_vmlinux += -T

hostprogs := mkpiggy
HOST_EXTRACFLAGS += -I$(srctree)/tools/include

- Sedat -

2020-07-14 21:11:11

by Arvind Sankar

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] x86/boot: Remove runtime relocations from compressed kernel

On Tue, Jul 14, 2020 at 10:43:11PM +0200, Sedat Dilek wrote:
> If we move to LDFLAGS_vmlinux we can drop the "call ld-option" as both
> linker GNU/ld.bfd and LLVM/lld.ld support this?

No, because ld.bfd only started supporting it from v2.26, and the kernel
aims to be buildable with v2.23.

>
> Do we need to adjust the comments?
> # Compressed kernel should be built as PIE since it may be loaded at any
> # address by the bootloader
>

It looks fine, no?

2020-07-15 00:42:17

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

The compressed kernel currently contains bogus run-time relocations in
the startup code in head_{32,64}.S, which are generated by the linker,
but must not actually be processed at run-time.

This generates warnings when linking with the BFD linker, and errors
with LLD, which defaults to erroring on run-time relocations in read-only
sections. It also requires the -z noreloc-overflow hack for the 64-bit
kernel, which prevents us from linking it as -pie on an older BFD linker
(<= 2.26) or on LLD, because the locations that are to be apparently
relocated are only 32-bits in size and so cannot really have
R_X86_64_RELATIVE relocations.

This series aims to get rid of these relocations. I've build- and
boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
has other issues [0].

The first three patches by Ard remove indirection via the GOT from the
compressed kernel code.

The next patch is an independent fix for LLD, to avoid an orphan
section in arch/x86/boot/setup.elf.

The fifth patch gets rid of almost all the relocations. It uses
standard PIC addressing technique for 32-bit, i.e. loading a register
with the address of _GLOBAL_OFFSET_TABLE_ and then using GOTOFF
references to access variables. For 64-bit, there is 32-bit code that
cannot use RIP-relative addressing, and also cannot use the 32-bit
method, since GOTOFF references are 64-bit only. This is instead handled
using a macro to replace a reference like gdt with (gdt-startup_32)
instead. The assembler will generate a PC32 relocation entry, with
addend set to (.-startup_32), and these will be replaced with constants
at link time. This works as long as all the code using such references
lives in the same section as startup_32, i.e. in .head.text.

The sixth patch addresses a remaining issue with the BFD linker, which
generates run-time relocations for absolute symbols. We use z_input_len
and z_output_len, defined in the generated piggy.S file, as symbols
whose absolute "addresses" are actually the size of the compressed
payload and the size of the decompressed kernel image respectively. LLD
does not generate relocations for these two symbols, but the BFD linker
does, prior to the upcoming 2.35. To get around this, piggy.S is
extended to also define two u32 variables (in .rodata) with the lengths,
and the head code is modified to use those instead of the symbol
addresses.

An alternative way to handle z_input_len/z_output_len would be to just
include piggy.S in head_{32,64}.S instead of as a separate object file,
since the GNU assembler doesn't generate relocations for symbols set to
constants.

The last patch adds a check in the linker script to ensure that no
run-time relocations get reintroduced.

[0] https://lore.kernel.org/lkml/[email protected]/

Changes from v4:
- Move -pie --no-dynamic-linker from KBUILD_LDFLAGS to LDFLAGS_vmlinux
Sedat: I'm not clear on whether you tested with the final LDFLAGS,
could you confirm: i.e. if you tested with -pie passed to LLD?
- Replace runtime -> run-time to be consistent in wording

Changes from v3:
- Move hidden.h to include/linux so the EFI stub and the compressed
kernel can share the same file

Changes from v2:
- Incorporate Ard's patches for eliminating GOT references into this
series
- Rebase on v5.8-rc3

v2: https://lore.kernel.org/lkml/[email protected]/

Changes from v1:
- Add .text.* to setup.ld instead of just .text.startup
- Rename the la() macro introduced in the second patch for 64-bit to
rva(), and rework the explanatory comment.
- In the last patch, check both .rel.dyn and .rela.dyn, instead of just
one per arch.

Ard Biesheuvel (3):
x86/boot/compressed: Move .got.plt entries out of the .got section
x86/boot/compressed: Force hidden visibility for all symbol references
x86/boot/compressed: Get rid of GOT fixup code

Arvind Sankar (4):
x86/boot: Add .text.* to setup.ld
x86/boot: Remove run-time relocations from .head.text code
x86/boot: Remove run-time relocations from head_{32,64}.S
x86/boot: Check that there are no run-time relocations

arch/x86/boot/compressed/Makefile | 39 +-----
arch/x86/boot/compressed/head_32.S | 99 +++++----------
arch/x86/boot/compressed/head_64.S | 165 ++++++++++---------------
arch/x86/boot/compressed/mkpiggy.c | 6 +
arch/x86/boot/compressed/vmlinux.lds.S | 24 +++-
arch/x86/boot/setup.ld | 2 +-
drivers/firmware/efi/libstub/Makefile | 2 +-
drivers/firmware/efi/libstub/hidden.h | 6 -
include/linux/hidden.h | 19 +++
9 files changed, 153 insertions(+), 209 deletions(-)
delete mode 100644 drivers/firmware/efi/libstub/hidden.h
create mode 100644 include/linux/hidden.h


base-commit: e9919e11e219eaa5e8041b7b1a196839143e9125
--
2.26.2

2020-07-15 00:42:20

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v5 1/7] x86/boot/compressed: Move .got.plt entries out of the .got section

From: Ard Biesheuvel <[email protected]>

The .got.plt section contains the part of the GOT which is used by PLT
entries, and which gets updated lazily by the dynamic loader when
function calls are dispatched through those PLT entries.

On fully linked binaries such as the kernel proper or the decompressor,
this never happens, and so in practice, the .got.plt section consists
only of the first 3 magic entries that are meant to point at the _DYNAMIC
section and at the fixup routine in the loader. However, since we don't
use a dynamic loader, those entries are never populated or used.

This means that treating those entries like ordinary GOT entries, and
updating their values based on the actual placement of the executable in
memory is completely pointless, and we can just ignore the .got.plt
section entirely, provided that it has no additional entries beyond
the first 3 ones.

So add an assertion in the linker script to ensure that this assumption
holds, and move the contents out of the [_got, _egot) memory range that
is modified by the GOT fixup routines.

While at it, drop the KEEP(), since it has no effect on the contents
of output sections that are created by the linker itself.

Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/vmlinux.lds.S | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index 8f1025d1f681..b17d218ccdf9 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -44,10 +44,13 @@ SECTIONS
}
.got : {
_got = .;
- KEEP(*(.got.plt))
KEEP(*(.got))
_egot = .;
}
+ .got.plt : {
+ *(.got.plt)
+ }
+
.data : {
_data = . ;
*(.data)
@@ -77,3 +80,9 @@ SECTIONS

DISCARDS
}
+
+#ifdef CONFIG_X86_64
+ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT entries detected!")
+#else
+ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0xc, "Unexpected GOT/PLT entries detected!")
+#endif
--
2.26.2

2020-07-15 00:42:23

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v5 5/7] x86/boot: Remove run-time relocations from .head.text code

The assembly code in head_{32,64}.S, while meant to be
position-independent, generates run-time relocations because it uses
instructions such as
leal gdt(%edx), %eax
which make the assembler and linker think that the code is using %edx as
an index into gdt, and hence gdt needs to be relocated to its run-time
address.

On 32-bit, with lld Dmitry Golovin reports that this results in a
link-time error with default options (i.e. unless -z notext is
explicitly passed):
LD arch/x86/boot/compressed/vmlinux
ld.lld: error: can't create dynamic relocation R_386_32 against local
symbol in readonly segment; recompile object files with -fPIC or pass
'-Wl,-z,notext' to allow text relocations in the output

With the BFD linker, this generates a warning during the build, if
--warn-shared-textrel is enabled, which at least Gentoo enables by
default:
LD arch/x86/boot/compressed/vmlinux
ld: arch/x86/boot/compressed/head_32.o: warning: relocation in read-only section `.head.text'
ld: warning: creating a DT_TEXTREL in object

On 64-bit, it is not possible to link the kernel as -pie with lld, and
it is only possible with a BFD linker that supports -z noreloc-overflow,
i.e. versions >2.26. This is because these instructions cannot really be
relocated: the displacement field is only 32-bits wide, and thus cannot
be relocated for a 64-bit load address. The -z noreloc-overflow option
simply overrides the linker error, and results in R_X86_64_RELATIVE
relocations that apply a 64-bit relocation to a 32-bit field anyway.
This happens to work because nothing will process these run-time
relocations.

Start fixing this by removing relocations from .head.text:
- On 32-bit, use a base register that holds the address of the GOT and
reference symbol addresses using @GOTOFF, i.e.
leal gdt@GOTOFF(%edx), %eax
- On 64-bit, most of the code can (and already does) use %rip-relative
addressing, however the .code32 bits can't, and the 64-bit code also
needs to reference symbol addresses as they will be after moving the
compressed kernel to the end of the decompression buffer.
For these cases, reference the symbols as an offset to startup_32 to
avoid creating relocations, i.e.
leal (gdt-startup_32)(%bp), %eax
This only works in .head.text as the subtraction cannot be represented
as a PC-relative relocation unless startup_32 is in the same section
as the code. Move efi32_pe_entry into .head.text so that it can use
the same method to avoid relocations.

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/compressed/head_32.S | 64 +++++++-----------
arch/x86/boot/compressed/head_64.S | 104 ++++++++++++++++++-----------
2 files changed, 90 insertions(+), 78 deletions(-)

diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 39f0bb43218f..8c1a4f5610f5 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -33,26 +33,10 @@
#include <asm/bootparam.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 and _end, 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 and _end as hidden:
+ * These symbols needed to be marked as .hidden to prevent the BFD linker from
+ * generating R_386_32 (rather than R_386_RELATIVE) relocations for them when
+ * the 32-bit compressed kernel is linked as PIE. This is no longer necessary,
+ * but it doesn't hurt to keep them .hidden.
*/
.hidden _bss
.hidden _ebss
@@ -74,10 +58,10 @@ SYM_FUNC_START(startup_32)
leal (BP_scratch+4)(%esi), %esp
call 1f
1: popl %edx
- subl $1b, %edx
+ addl $_GLOBAL_OFFSET_TABLE_+(.-1b), %edx

/* Load new GDT */
- leal gdt(%edx), %eax
+ leal gdt@GOTOFF(%edx), %eax
movl %eax, 2(%eax)
lgdt (%eax)

@@ -90,14 +74,16 @@ SYM_FUNC_START(startup_32)
movl %eax, %ss

/*
- * %edx contains the address we are loaded at by the boot loader and %ebx
- * contains the address where we should move the kernel image temporarily
- * for safe in-place decompression. %ebp contains the address that the kernel
- * will be decompressed to.
+ * %edx contains the address we are loaded at by the boot loader (plus the
+ * offset to the GOT). The below code calculates %ebx to be the address where
+ * we should move the kernel image temporarily for safe in-place decompression
+ * (again, plus the offset to the GOT).
+ *
+ * %ebp is calculated to be the address that the kernel will be decompressed to.
*/

#ifdef CONFIG_RELOCATABLE
- movl %edx, %ebx
+ leal startup_32@GOTOFF(%edx), %ebx

#ifdef CONFIG_EFI_STUB
/*
@@ -108,7 +94,7 @@ SYM_FUNC_START(startup_32)
* image_offset = startup_32 - image_base
* Otherwise image_offset will be zero and has no effect on the calculations.
*/
- subl image_offset(%edx), %ebx
+ subl image_offset@GOTOFF(%edx), %ebx
#endif

movl BP_kernel_alignment(%esi), %eax
@@ -125,10 +111,10 @@ SYM_FUNC_START(startup_32)
movl %ebx, %ebp // Save the output address for later
/* Target address to relocate to for decompression */
addl BP_init_size(%esi), %ebx
- subl $_end, %ebx
+ subl $_end@GOTOFF, %ebx

/* Set up the stack */
- leal boot_stack_end(%ebx), %esp
+ leal boot_stack_end@GOTOFF(%ebx), %esp

/* Zero EFLAGS */
pushl $0
@@ -139,8 +125,8 @@ SYM_FUNC_START(startup_32)
* where decompression in place becomes safe.
*/
pushl %esi
- leal (_bss-4)(%edx), %esi
- leal (_bss-4)(%ebx), %edi
+ leal (_bss@GOTOFF-4)(%edx), %esi
+ leal (_bss@GOTOFF-4)(%ebx), %edi
movl $(_bss - startup_32), %ecx
shrl $2, %ecx
std
@@ -153,14 +139,14 @@ SYM_FUNC_START(startup_32)
* during extract_kernel below. To avoid any issues, repoint the GDTR
* to the new copy of the GDT.
*/
- leal gdt(%ebx), %eax
+ leal gdt@GOTOFF(%ebx), %eax
movl %eax, 2(%eax)
lgdt (%eax)

/*
* Jump to the relocated address.
*/
- leal .Lrelocated(%ebx), %eax
+ leal .Lrelocated@GOTOFF(%ebx), %eax
jmp *%eax
SYM_FUNC_END(startup_32)

@@ -170,7 +156,7 @@ SYM_FUNC_START_ALIAS(efi_stub_entry)
add $0x4, %esp
movl 8(%esp), %esi /* save boot_params pointer */
call efi_main
- leal startup_32(%eax), %eax
+ /* efi_main returns the possibly relocated address of startup_32 */
jmp *%eax
SYM_FUNC_END(efi32_stub_entry)
SYM_FUNC_END_ALIAS(efi_stub_entry)
@@ -183,8 +169,8 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
* Clear BSS (stack is currently empty)
*/
xorl %eax, %eax
- leal _bss(%ebx), %edi
- leal _ebss(%ebx), %ecx
+ leal _bss@GOTOFF(%ebx), %edi
+ leal _ebss@GOTOFF(%ebx), %ecx
subl %edi, %ecx
shrl $2, %ecx
rep stosl
@@ -198,9 +184,9 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
pushl %ebp /* output address */

pushl $z_input_len /* input_len */
- leal input_data(%ebx), %eax
+ leal input_data@GOTOFF(%ebx), %eax
pushl %eax /* input_data */
- leal boot_heap(%ebx), %eax
+ leal boot_heap@GOTOFF(%ebx), %eax
pushl %eax /* heap area */
pushl %esi /* real mode pointer */
call extract_kernel /* returns kernel location in %eax */
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index bf1ab30acc5b..11429092c224 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -43,6 +43,32 @@
.hidden _end

__HEAD
+
+/*
+ * This macro gives the relative virtual address of X, i.e. the offset of X
+ * from startup_32. This is the same as the link-time virtual address of X,
+ * since startup_32 is at 0, but defining it this way tells the
+ * assembler/linker that we do not want the actual run-time address of X. This
+ * prevents the linker from trying to create unwanted run-time relocation
+ * entries for the reference when the compressed kernel is linked as PIE.
+ *
+ * A reference X(%reg) will result in the link-time VA of X being stored with
+ * the instruction, and a run-time R_X86_64_RELATIVE relocation entry that
+ * adds the 64-bit base address where the kernel is loaded.
+ *
+ * Replacing it with (X-startup_32)(%reg) results in the offset being stored,
+ * and no run-time relocation.
+ *
+ * The macro should be used as a displacement with a base register containing
+ * the run-time address of startup_32 [i.e. rva(X)(%reg)], or as an immediate
+ * [$ rva(X)].
+ *
+ * This macro can only be used from within the .head.text section, since the
+ * expression requires startup_32 to be in the same section as the code being
+ * assembled.
+ */
+#define rva(X) ((X) - startup_32)
+
.code32
SYM_FUNC_START(startup_32)
/*
@@ -65,10 +91,10 @@ SYM_FUNC_START(startup_32)
leal (BP_scratch+4)(%esi), %esp
call 1f
1: popl %ebp
- subl $1b, %ebp
+ subl $ rva(1b), %ebp

/* Load new GDT with the 64bit segments using 32bit descriptor */
- leal gdt(%ebp), %eax
+ leal rva(gdt)(%ebp), %eax
movl %eax, 2(%eax)
lgdt (%eax)

@@ -81,7 +107,7 @@ SYM_FUNC_START(startup_32)
movl %eax, %ss

/* setup a stack and make sure cpu supports long mode. */
- leal boot_stack_end(%ebp), %esp
+ leal rva(boot_stack_end)(%ebp), %esp

call verify_cpu
testl %eax, %eax
@@ -108,7 +134,7 @@ SYM_FUNC_START(startup_32)
* image_offset = startup_32 - image_base
* Otherwise image_offset will be zero and has no effect on the calculations.
*/
- subl image_offset(%ebp), %ebx
+ subl rva(image_offset)(%ebp), %ebx
#endif

movl BP_kernel_alignment(%esi), %eax
@@ -124,7 +150,7 @@ SYM_FUNC_START(startup_32)

/* Target address to relocate to for decompression */
addl BP_init_size(%esi), %ebx
- subl $_end, %ebx
+ subl $ rva(_end), %ebx

/*
* Prepare for entering 64 bit mode
@@ -152,19 +178,19 @@ SYM_FUNC_START(startup_32)
1:

/* Initialize Page tables to 0 */
- leal pgtable(%ebx), %edi
+ leal rva(pgtable)(%ebx), %edi
xorl %eax, %eax
movl $(BOOT_INIT_PGT_SIZE/4), %ecx
rep stosl

/* Build Level 4 */
- leal pgtable + 0(%ebx), %edi
+ leal rva(pgtable + 0)(%ebx), %edi
leal 0x1007 (%edi), %eax
movl %eax, 0(%edi)
addl %edx, 4(%edi)

/* Build Level 3 */
- leal pgtable + 0x1000(%ebx), %edi
+ leal rva(pgtable + 0x1000)(%ebx), %edi
leal 0x1007(%edi), %eax
movl $4, %ecx
1: movl %eax, 0x00(%edi)
@@ -175,7 +201,7 @@ SYM_FUNC_START(startup_32)
jnz 1b

/* Build Level 2 */
- leal pgtable + 0x2000(%ebx), %edi
+ leal rva(pgtable + 0x2000)(%ebx), %edi
movl $0x00000183, %eax
movl $2048, %ecx
1: movl %eax, 0(%edi)
@@ -186,7 +212,7 @@ SYM_FUNC_START(startup_32)
jnz 1b

/* Enable the boot page tables */
- leal pgtable(%ebx), %eax
+ leal rva(pgtable)(%ebx), %eax
movl %eax, %cr3

/* Enable Long mode in EFER (Extended Feature Enable Register) */
@@ -211,14 +237,14 @@ SYM_FUNC_START(startup_32)
* We place all of the values on our mini stack so lret can
* used to perform that far jump.
*/
- leal startup_64(%ebp), %eax
+ leal rva(startup_64)(%ebp), %eax
#ifdef CONFIG_EFI_MIXED
- movl efi32_boot_args(%ebp), %edi
+ movl rva(efi32_boot_args)(%ebp), %edi
cmp $0, %edi
jz 1f
- leal efi64_stub_entry(%ebp), %eax
- movl efi32_boot_args+4(%ebp), %esi
- movl efi32_boot_args+8(%ebp), %edx // saved bootparams pointer
+ leal rva(efi64_stub_entry)(%ebp), %eax
+ movl rva(efi32_boot_args+4)(%ebp), %esi
+ movl rva(efi32_boot_args+8)(%ebp), %edx // saved bootparams pointer
cmpl $0, %edx
jnz 1f
/*
@@ -229,7 +255,7 @@ SYM_FUNC_START(startup_32)
* the correct stack alignment for entry.
*/
subl $40, %esp
- leal efi_pe_entry(%ebp), %eax
+ leal rva(efi_pe_entry)(%ebp), %eax
movl %edi, %ecx // MS calling convention
movl %esi, %edx
1:
@@ -255,18 +281,18 @@ SYM_FUNC_START(efi32_stub_entry)

call 1f
1: pop %ebp
- subl $1b, %ebp
+ subl $ rva(1b), %ebp

- movl %esi, efi32_boot_args+8(%ebp)
+ movl %esi, rva(efi32_boot_args+8)(%ebp)
SYM_INNER_LABEL(efi32_pe_stub_entry, SYM_L_LOCAL)
- movl %ecx, efi32_boot_args(%ebp)
- movl %edx, efi32_boot_args+4(%ebp)
- movb $0, efi_is64(%ebp)
+ movl %ecx, rva(efi32_boot_args)(%ebp)
+ movl %edx, rva(efi32_boot_args+4)(%ebp)
+ movb $0, rva(efi_is64)(%ebp)

/* Save firmware GDTR and code/data selectors */
- sgdtl efi32_boot_gdt(%ebp)
- movw %cs, efi32_boot_cs(%ebp)
- movw %ds, efi32_boot_ds(%ebp)
+ sgdtl rva(efi32_boot_gdt)(%ebp)
+ movw %cs, rva(efi32_boot_cs)(%ebp)
+ movw %ds, rva(efi32_boot_ds)(%ebp)

/* Disable paging */
movl %cr0, %eax
@@ -345,11 +371,11 @@ SYM_CODE_START(startup_64)

/* Target address to relocate to for decompression */
movl BP_init_size(%rsi), %ebx
- subl $_end, %ebx
+ subl $ rva(_end), %ebx
addq %rbp, %rbx

/* Set up the stack */
- leaq boot_stack_end(%rbx), %rsp
+ leaq rva(boot_stack_end)(%rbx), %rsp

/*
* At this point we are in long mode with 4-level paging enabled,
@@ -423,7 +449,7 @@ SYM_CODE_START(startup_64)
lretq
trampoline_return:
/* Restore the stack, the 32-bit trampoline uses its own stack */
- leaq boot_stack_end(%rbx), %rsp
+ leaq rva(boot_stack_end)(%rbx), %rsp

/*
* cleanup_trampoline() would restore trampoline memory.
@@ -435,7 +461,7 @@ trampoline_return:
* this function call.
*/
pushq %rsi
- leaq top_pgtable(%rbx), %rdi
+ leaq rva(top_pgtable)(%rbx), %rdi
call cleanup_trampoline
popq %rsi

@@ -449,9 +475,9 @@ trampoline_return:
*/
pushq %rsi
leaq (_bss-8)(%rip), %rsi
- leaq (_bss-8)(%rbx), %rdi
- movq $_bss /* - $startup_32 */, %rcx
- shrq $3, %rcx
+ leaq rva(_bss-8)(%rbx), %rdi
+ movl $(_bss - startup_32), %ecx
+ shrl $3, %ecx
std
rep movsq
cld
@@ -462,15 +488,15 @@ trampoline_return:
* during extract_kernel below. To avoid any issues, repoint the GDTR
* to the new copy of the GDT.
*/
- leaq gdt64(%rbx), %rax
- leaq gdt(%rbx), %rdx
+ leaq rva(gdt64)(%rbx), %rax
+ leaq rva(gdt)(%rbx), %rdx
movq %rdx, 2(%rax)
lgdt (%rax)

/*
* Jump to the relocated address.
*/
- leaq .Lrelocated(%rbx), %rax
+ leaq rva(.Lrelocated)(%rbx), %rax
jmp *%rax
SYM_CODE_END(startup_64)

@@ -482,7 +508,7 @@ SYM_FUNC_START_ALIAS(efi_stub_entry)
movq %rdx, %rbx /* save boot_params pointer */
call efi_main
movq %rbx,%rsi
- leaq startup_64(%rax), %rax
+ leaq rva(startup_64)(%rax), %rax
jmp *%rax
SYM_FUNC_END(efi64_stub_entry)
SYM_FUNC_END_ALIAS(efi_stub_entry)
@@ -645,7 +671,7 @@ SYM_DATA(efi_is64, .byte 1)
#define BS32_handle_protocol 88 // offsetof(efi_boot_services_32_t, handle_protocol)
#define LI32_image_base 32 // offsetof(efi_loaded_image_32_t, image_base)

- .text
+ __HEAD
.code32
SYM_FUNC_START(efi32_pe_entry)
/*
@@ -667,12 +693,12 @@ SYM_FUNC_START(efi32_pe_entry)

call 1f
1: pop %ebx
- subl $1b, %ebx
+ subl $ rva(1b), %ebx

/* Get the loaded image protocol pointer from the image handle */
leal -4(%ebp), %eax
pushl %eax // &loaded_image
- leal loaded_image_proto(%ebx), %eax
+ leal rva(loaded_image_proto)(%ebx), %eax
pushl %eax // pass the GUID address
pushl 8(%ebp) // pass the image handle

@@ -707,7 +733,7 @@ SYM_FUNC_START(efi32_pe_entry)
* use it before we get to the 64-bit efi_pe_entry() in C code.
*/
subl %esi, %ebx
- movl %ebx, image_offset(%ebp) // save image_offset
+ movl %ebx, rva(image_offset)(%ebp) // save image_offset
jmp efi32_pe_stub_entry

2: popl %edi // restore callee-save registers
--
2.26.2

2020-07-15 00:43:00

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v5 6/7] x86/boot: Remove run-time relocations from head_{32,64}.S

The BFD linker generates run-time relocations for z_input_len and
z_output_len, even though they are absolute symbols.

This is fixed for binutils-2.35 [1]. Work around this for earlier
versions by defining two variables input_len and output_len in addition
to the symbols, and use them via position-independent references.

This eliminates the last two run-time relocations in the head code and
allows us to drop the -z noreloc-overflow flag to the linker.

Move the -pie and --no-dynamic-linker LDFLAGS to LDFLAGS_vmlinux instead
of KBUILD_LDFLAGS. There shouldn't be anything else getting linked, but
this is the more logical location for these flags, and modversions might
call the linker if an EXPORT_SYMBOL is left over accidentally in one of
the decompressors.

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=25754

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/compressed/Makefile | 12 ++----------
arch/x86/boot/compressed/head_32.S | 17 ++++++++---------
arch/x86/boot/compressed/head_64.S | 4 ++--
arch/x86/boot/compressed/mkpiggy.c | 6 ++++++
4 files changed, 18 insertions(+), 21 deletions(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index c829d874dcac..ae2c0dc98a6a 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -51,16 +51,8 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
-ifeq ($(CONFIG_X86_32),y)
-KBUILD_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.
-KBUILD_LDFLAGS += $(shell $(LD) --help 2>&1 | grep -q "\-z noreloc-overflow" \
- && echo "-z noreloc-overflow -pie --no-dynamic-linker")
-endif
-LDFLAGS_vmlinux := -T
+LDFLAGS_vmlinux := $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
+LDFLAGS_vmlinux += -T

hostprogs := mkpiggy
HOST_EXTRACFLAGS += -I$(srctree)/tools/include
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 8c1a4f5610f5..659fad53ca82 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -178,18 +178,17 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
/*
* Do the extraction, and jump to the new kernel..
*/
- /* push arguments for extract_kernel: */
- pushl $z_output_len /* decompressed length, end of relocs */
+ /* push arguments for extract_kernel: */

- pushl %ebp /* output address */
-
- pushl $z_input_len /* input_len */
+ pushl output_len@GOTOFF(%ebx) /* decompressed length, end of relocs */
+ pushl %ebp /* output address */
+ pushl input_len@GOTOFF(%ebx) /* input_len */
leal input_data@GOTOFF(%ebx), %eax
- pushl %eax /* input_data */
+ pushl %eax /* input_data */
leal boot_heap@GOTOFF(%ebx), %eax
- pushl %eax /* heap area */
- pushl %esi /* real mode pointer */
- call extract_kernel /* returns kernel location in %eax */
+ pushl %eax /* heap area */
+ pushl %esi /* real mode pointer */
+ call extract_kernel /* returns kernel location in %eax */
addl $24, %esp

/*
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 11429092c224..9e46729cf162 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -534,9 +534,9 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
movq %rsi, %rdi /* real mode address */
leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
leaq input_data(%rip), %rdx /* input_data */
- movl $z_input_len, %ecx /* input_len */
+ movl input_len(%rip), %ecx /* input_len */
movq %rbp, %r8 /* output target address */
- movl $z_output_len, %r9d /* decompressed length, end of relocs */
+ movl output_len(%rip), %r9d /* decompressed length, end of relocs */
call extract_kernel /* returns kernel location in %rax */
popq %rsi

diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c
index 7e01248765b2..52aa56cdbacc 100644
--- a/arch/x86/boot/compressed/mkpiggy.c
+++ b/arch/x86/boot/compressed/mkpiggy.c
@@ -60,6 +60,12 @@ int main(int argc, char *argv[])
printf(".incbin \"%s\"\n", argv[1]);
printf("input_data_end:\n");

+ printf(".section \".rodata\",\"a\",@progbits\n");
+ printf(".globl input_len\n");
+ printf("input_len:\n\t.long %lu\n", ilen);
+ printf(".globl output_len\n");
+ printf("output_len:\n\t.long %lu\n", (unsigned long)olen);
+
retval = 0;
bail:
if (f)
--
2.26.2

2020-07-15 00:45:22

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v5 4/7] x86/boot: Add .text.* to setup.ld

gcc puts the main function into .text.startup when compiled with -Os (or
-O2). This results in arch/x86/boot/main.c having a .text.startup
section which is currently not included explicitly in the linker script
setup.ld in the same directory.

The BFD linker places this orphan section immediately after .text, so
this still works. However, LLD git, since [1], is choosing to place it
immediately after the .bstext section instead (this is the first code
section). This plays havoc with the section layout that setup.elf
requires to create the setup header, for eg on 64-bit:

LD arch/x86/boot/setup.elf
ld.lld: error: section .text.startup file range overlaps with .header
>>> .text.startup range is [0x200040, 0x2001FE]
>>> .header range is [0x2001EF, 0x20026B]

ld.lld: error: section .header file range overlaps with .bsdata
>>> .header range is [0x2001EF, 0x20026B]
>>> .bsdata range is [0x2001FF, 0x200398]

ld.lld: error: section .bsdata file range overlaps with .entrytext
>>> .bsdata range is [0x2001FF, 0x200398]
>>> .entrytext range is [0x20026C, 0x2002D3]

ld.lld: error: section .text.startup virtual address range overlaps
with .header
>>> .text.startup range is [0x40, 0x1FE]
>>> .header range is [0x1EF, 0x26B]

ld.lld: error: section .header virtual address range overlaps with
.bsdata
>>> .header range is [0x1EF, 0x26B]
>>> .bsdata range is [0x1FF, 0x398]

ld.lld: error: section .bsdata virtual address range overlaps with
.entrytext
>>> .bsdata range is [0x1FF, 0x398]
>>> .entrytext range is [0x26C, 0x2D3]

ld.lld: error: section .text.startup load address range overlaps with
.header
>>> .text.startup range is [0x40, 0x1FE]
>>> .header range is [0x1EF, 0x26B]

ld.lld: error: section .header load address range overlaps with
.bsdata
>>> .header range is [0x1EF, 0x26B]
>>> .bsdata range is [0x1FF, 0x398]

ld.lld: error: section .bsdata load address range overlaps with
.entrytext
>>> .bsdata range is [0x1FF, 0x398]
>>> .entrytext range is [0x26C, 0x2D3]

Add .text.* to the .text output section to fix this, and also prevent
any future surprises if the compiler decides to create other such
sections.

[1] https://reviews.llvm.org/D75225

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/setup.ld | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/boot/setup.ld b/arch/x86/boot/setup.ld
index 24c95522f231..49546c247ae2 100644
--- a/arch/x86/boot/setup.ld
+++ b/arch/x86/boot/setup.ld
@@ -20,7 +20,7 @@ SECTIONS
.initdata : { *(.initdata) }
__end_init = .;

- .text : { *(.text) }
+ .text : { *(.text .text.*) }
.text32 : { *(.text32) }

. = ALIGN(16);
--
2.26.2

2020-07-15 00:45:27

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v5 7/7] x86/boot: Check that there are no run-time relocations

Add a linker script check that there are no run-time relocations, and
remove the old one that tries to check via looking for specially-named
sections in the object files.

Drop the tests for -fPIE compiler option and -pie linker option, as they
are available in all supported gcc and binutils versions (as well as
clang and lld).

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/compressed/Makefile | 28 +++-----------------------
arch/x86/boot/compressed/vmlinux.lds.S | 8 ++++++++
2 files changed, 11 insertions(+), 25 deletions(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index ae2c0dc98a6a..a9e082b8c720 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -29,7 +29,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4

KBUILD_CFLAGS := -m$(BITS) -O2
-KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC)
+KBUILD_CFLAGS += -fno-strict-aliasing -fPIE
KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
cflags-$(CONFIG_X86_32) := -march=i386
cflags-$(CONFIG_X86_64) := -mcmodel=small
@@ -51,7 +51,7 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
-LDFLAGS_vmlinux := $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
+LDFLAGS_vmlinux := -pie $(call ld-option, --no-dynamic-linker)
LDFLAGS_vmlinux += -T

hostprogs := mkpiggy
@@ -86,30 +86,8 @@ vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
vmlinux-objs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o

-# The compressed kernel is built with -fPIC/-fPIE so that a boot loader
-# can place it anywhere in memory and it will still run. However, since
-# it is executed as-is without any ELF relocation processing performed
-# (and has already had all relocation sections stripped from the binary),
-# none of the code can use data relocations (e.g. static assignments of
-# pointer values), since they will be meaningless at runtime. This check
-# will refuse to link the vmlinux if any of these relocations are found.
-quiet_cmd_check_data_rel = DATAREL $@
-define cmd_check_data_rel
- for obj in $(filter %.o,$^); do \
- $(READELF) -S $$obj | grep -qF .rel.local && { \
- echo "error: $$obj has data relocations!" >&2; \
- exit 1; \
- } || true; \
- done
-endef
-
-# We need to run two commands under "if_changed", so merge them into a
-# single invocation.
-quiet_cmd_check-and-link-vmlinux = LD $@
- cmd_check-and-link-vmlinux = $(cmd_check_data_rel); $(cmd_ld)
-
$(obj)/vmlinux: $(vmlinux-objs-y) FORCE
- $(call if_changed,check-and-link-vmlinux)
+ $(call if_changed,ld)

OBJCOPYFLAGS_vmlinux.bin := -R .comment -S
$(obj)/vmlinux.bin: vmlinux FORCE
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index a4a4a59a2628..29df99b6cc64 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -42,6 +42,12 @@ SECTIONS
*(.rodata.*)
_erodata = . ;
}
+ .rel.dyn : {
+ *(.rel.*)
+ }
+ .rela.dyn : {
+ *(.rela.*)
+ }
.got : {
*(.got)
}
@@ -85,3 +91,5 @@ ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT en
#else
ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0xc, "Unexpected GOT/PLT entries detected!")
#endif
+
+ASSERT(SIZEOF(.rel.dyn) == 0 && SIZEOF(.rela.dyn) == 0, "Unexpected run-time relocations detected!")
--
2.26.2

2020-07-15 00:45:32

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v5 3/7] x86/boot/compressed: Get rid of GOT fixup code

From: Ard Biesheuvel <[email protected]>

In a previous patch, we have eliminated GOT entries from the decompressor
binary and added an assertion that the .got section is empty. This means
that the GOT fixup routines that exist in both the 32-bit and 64-bit
startup routines have become dead code, and can be removed.

While at it, drop the KEEP() from the linker script, as it has no effect
on the contents of output sections that are created by the linker itself.

Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/head_32.S | 24 ++---------
arch/x86/boot/compressed/head_64.S | 57 --------------------------
arch/x86/boot/compressed/vmlinux.lds.S | 4 +-
3 files changed, 5 insertions(+), 80 deletions(-)

diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 03557f2174bf..39f0bb43218f 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -49,16 +49,13 @@
* 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, _egot and _end, 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, _egot and _end as
- * hidden:
+ * _bss, _ebss and _end, 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 and _end as hidden:
*/
.hidden _bss
.hidden _ebss
- .hidden _got
- .hidden _egot
.hidden _end

__HEAD
@@ -192,19 +189,6 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
shrl $2, %ecx
rep stosl

-/*
- * Adjust our own GOT
- */
- leal _got(%ebx), %edx
- leal _egot(%ebx), %ecx
-1:
- cmpl %ecx, %edx
- jae 2f
- addl %ebx, (%edx)
- addl $4, %edx
- jmp 1b
-2:
-
/*
* Do the extraction, and jump to the new kernel..
*/
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 97d37f0a34f5..bf1ab30acc5b 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -40,8 +40,6 @@
*/
.hidden _bss
.hidden _ebss
- .hidden _got
- .hidden _egot
.hidden _end

__HEAD
@@ -353,25 +351,6 @@ SYM_CODE_START(startup_64)
/* Set up the stack */
leaq boot_stack_end(%rbx), %rsp

- /*
- * paging_prepare() and cleanup_trampoline() below can have GOT
- * references. Adjust the table with address we are running at.
- *
- * Zero RAX for adjust_got: the GOT was not adjusted before;
- * there's no adjustment to undo.
- */
- xorq %rax, %rax
-
- /*
- * Calculate the address the binary is loaded at and use it as
- * a GOT adjustment.
- */
- call 1f
-1: popq %rdi
- subq $1b, %rdi
-
- call .Ladjust_got
-
/*
* At this point we are in long mode with 4-level paging enabled,
* but we might want to enable 5-level paging or vice versa.
@@ -464,21 +443,6 @@ trampoline_return:
pushq $0
popfq

- /*
- * Previously we've adjusted the GOT with address the binary was
- * loaded at. Now we need to re-adjust for relocation address.
- *
- * Calculate the address the binary is loaded at, so that we can
- * undo the previous GOT adjustment.
- */
- call 1f
-1: popq %rax
- subq $1b, %rax
-
- /* The new adjustment is the relocation address */
- movq %rbx, %rdi
- call .Ladjust_got
-
/*
* Copy the compressed kernel to the end of our buffer
* where decompression in place becomes safe.
@@ -556,27 +520,6 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
jmp *%rax
SYM_FUNC_END(.Lrelocated)

-/*
- * Adjust the global offset table
- *
- * RAX is the previous adjustment of the table to undo (use 0 if it's the
- * first time we touch GOT).
- * RDI is the new adjustment to apply.
- */
-.Ladjust_got:
- /* Walk through the GOT adding the address to the entries */
- leaq _got(%rip), %rdx
- leaq _egot(%rip), %rcx
-1:
- cmpq %rcx, %rdx
- jae 2f
- subq %rax, (%rdx) /* Undo previous adjustment */
- addq %rdi, (%rdx) /* Apply the new adjustment */
- addq $8, %rdx
- jmp 1b
-2:
- ret
-
.code32
/*
* This is the 32-bit trampoline that will be copied over to low memory.
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index 4bcc943842ab..a4a4a59a2628 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -43,9 +43,7 @@ SECTIONS
_erodata = . ;
}
.got : {
- _got = .;
- KEEP(*(.got))
- _egot = .;
+ *(.got)
}
.got.plt : {
*(.got.plt)
--
2.26.2

2020-07-15 00:46:14

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v5 2/7] x86/boot/compressed: Force hidden visibility for all symbol references

From: Ard Biesheuvel <[email protected]>

Eliminate all GOT entries in the decompressor binary, by forcing hidden
visibility for all symbol references, which informs the compiler that
such references will be resolved at link time without the need for
allocating GOT entries.

To ensure that no GOT entries will creep back in, add an assertion to
the decompressor linker script that will fire if the .got section has
a non-zero size.

[Arvind: move hidden.h to include/linux instead of making a copy]

Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/Makefile | 1 +
arch/x86/boot/compressed/vmlinux.lds.S | 1 +
drivers/firmware/efi/libstub/Makefile | 2 +-
drivers/firmware/efi/libstub/hidden.h | 6 ------
include/linux/hidden.h | 19 +++++++++++++++++++
5 files changed, 22 insertions(+), 7 deletions(-)
delete mode 100644 drivers/firmware/efi/libstub/hidden.h
create mode 100644 include/linux/hidden.h

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 7619742f91c9..c829d874dcac 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -42,6 +42,7 @@ KBUILD_CFLAGS += $(call cc-disable-warning, gnu)
KBUILD_CFLAGS += -Wno-pointer-sign
KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=)
KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
+KBUILD_CFLAGS += -include $(srctree)/include/linux/hidden.h

KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
GCOV_PROFILE := n
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index b17d218ccdf9..4bcc943842ab 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -81,6 +81,7 @@ SECTIONS
DISCARDS
}

+ASSERT(SIZEOF(.got) == 0, "Unexpected GOT entries detected!")
#ifdef CONFIG_X86_64
ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT entries detected!")
#else
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 4cce372edaf4..609157a40493 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -27,7 +27,7 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt

KBUILD_CFLAGS := $(cflags-y) -Os -DDISABLE_BRANCH_PROFILING \
- -include $(srctree)/drivers/firmware/efi/libstub/hidden.h \
+ -include $(srctree)/include/linux/hidden.h \
-D__NO_FORTIFY \
$(call cc-option,-ffreestanding) \
$(call cc-option,-fno-stack-protector) \
diff --git a/drivers/firmware/efi/libstub/hidden.h b/drivers/firmware/efi/libstub/hidden.h
deleted file mode 100644
index 3493b041f419..000000000000
--- a/drivers/firmware/efi/libstub/hidden.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * To prevent the compiler from emitting GOT-indirected (and thus absolute)
- * references to any global symbols, override their visibility as 'hidden'
- */
-#pragma GCC visibility push(hidden)
diff --git a/include/linux/hidden.h b/include/linux/hidden.h
new file mode 100644
index 000000000000..49a17b6b5962
--- /dev/null
+++ b/include/linux/hidden.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * When building position independent code with GCC using the -fPIC option,
+ * (or even the -fPIE one on older versions), it will assume that we are
+ * building a dynamic object (either a shared library or an executable) that
+ * may have symbol references that can only be resolved at load time. For a
+ * variety of reasons (ELF symbol preemption, the CoW footprint of the section
+ * that is modified by the loader), this results in all references to symbols
+ * with external linkage to go via entries in the Global Offset Table (GOT),
+ * which carries absolute addresses which need to be fixed up when the
+ * executable image is loaded at an offset which is different from its link
+ * time offset.
+ *
+ * Fortunately, there is a way to inform the compiler that such symbol
+ * references will be satisfied at link time rather than at load time, by
+ * giving them 'hidden' visibility.
+ */
+
+#pragma GCC visibility push(hidden)
--
2.26.2

2020-07-15 02:19:53

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Wed, Jul 15, 2020 at 2:41 AM Arvind Sankar <[email protected]> wrote:
>
> The compressed kernel currently contains bogus run-time relocations in
> the startup code in head_{32,64}.S, which are generated by the linker,
> but must not actually be processed at run-time.
>
> This generates warnings when linking with the BFD linker, and errors
> with LLD, which defaults to erroring on run-time relocations in read-only
> sections. It also requires the -z noreloc-overflow hack for the 64-bit
> kernel, which prevents us from linking it as -pie on an older BFD linker
> (<= 2.26) or on LLD, because the locations that are to be apparently
> relocated are only 32-bits in size and so cannot really have
> R_X86_64_RELATIVE relocations.
>
> This series aims to get rid of these relocations. I've build- and
> boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
> gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
> has other issues [0].
>
> The first three patches by Ard remove indirection via the GOT from the
> compressed kernel code.
>
> The next patch is an independent fix for LLD, to avoid an orphan
> section in arch/x86/boot/setup.elf.
>
> The fifth patch gets rid of almost all the relocations. It uses
> standard PIC addressing technique for 32-bit, i.e. loading a register
> with the address of _GLOBAL_OFFSET_TABLE_ and then using GOTOFF
> references to access variables. For 64-bit, there is 32-bit code that
> cannot use RIP-relative addressing, and also cannot use the 32-bit
> method, since GOTOFF references are 64-bit only. This is instead handled
> using a macro to replace a reference like gdt with (gdt-startup_32)
> instead. The assembler will generate a PC32 relocation entry, with
> addend set to (.-startup_32), and these will be replaced with constants
> at link time. This works as long as all the code using such references
> lives in the same section as startup_32, i.e. in .head.text.
>
> The sixth patch addresses a remaining issue with the BFD linker, which
> generates run-time relocations for absolute symbols. We use z_input_len
> and z_output_len, defined in the generated piggy.S file, as symbols
> whose absolute "addresses" are actually the size of the compressed
> payload and the size of the decompressed kernel image respectively. LLD
> does not generate relocations for these two symbols, but the BFD linker
> does, prior to the upcoming 2.35. To get around this, piggy.S is
> extended to also define two u32 variables (in .rodata) with the lengths,
> and the head code is modified to use those instead of the symbol
> addresses.
>
> An alternative way to handle z_input_len/z_output_len would be to just
> include piggy.S in head_{32,64}.S instead of as a separate object file,
> since the GNU assembler doesn't generate relocations for symbols set to
> constants.
>
> The last patch adds a check in the linker script to ensure that no
> run-time relocations get reintroduced.
>
> [0] https://lore.kernel.org/lkml/[email protected]/
>
> Changes from v4:
> - Move -pie --no-dynamic-linker from KBUILD_LDFLAGS to LDFLAGS_vmlinux
> Sedat: I'm not clear on whether you tested with the final LDFLAGS,
> could you confirm: i.e. if you tested with -pie passed to LLD?
> - Replace runtime -> run-time to be consistent in wording
>

Hi Arvind,

thanks for v5.

With my diff applied against your patchset *v4*:

diff --git a/arch/x86/boot/compressed/Makefile
b/arch/x86/boot/compressed/Makefile
index 789d5d14d8b0..d0aafcd8cf6c 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -51,8 +51,8 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
-KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
-LDFLAGS_vmlinux := -T
+LDFLAGS_vmlinux := -pie $(call ld-option, --no-dynamic-linker)
+LDFLAGS_vmlinux += -T

hostprogs := mkpiggy
HOST_EXTRACFLAGS += -I$(srctree)/tools/include

I was able to build/assemble with LLVM/Clang v11.0.0-git+ffee8040534
and boot on bare metal.

Note:
I have applied some additional patches to be compliant with LLVM_IAS=1
(Clang's Integrated Assembler) and LLVM=1 means LLVM utilities.
( As pointed out zstd-v7. )

- Sedat -

P.S.: Check my build-log

$ grep 'arch/x86/boot/compressed/vmlinux'
build-log_5.8.0-rc5-3-amd64-llvm11-ias.txt
make -f ./scripts/Makefile.build obj=arch/x86/boot/compressed
arch/x86/boot/compressed/vmlinux
clang-11 -E -Wp,-MMD,arch/x86/boot/compressed/.vmlinux.lds.d
-nostdinc -isystem /usr/lib/llvm-11/lib/clang/11.0.0/include
-I./arch/x86/include -I./arch/x86/include/generated -I./include
-I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi
-I./include/uapi -I./include/generated/uapi -include
./include/linux/kconfig.h -D__KERNEL__ -Qunused-arguments -P -Ux86
-D__ASSEMBLY__ -DLINKER_SCRIPT -o arch/x86/boot/compressed/vmlinux.lds
arch/x86/boot/compressed/vmlinux.lds.S
llvm-objcopy-11 -R .comment -S vmlinux arch/x86/boot/compressed/vmlinux.bin
arch/x86/tools/relocs vmlinux >
arch/x86/boot/compressed/vmlinux.relocs;arch/x86/tools/relocs
--abs-relocs vmlinux
{ cat arch/x86/boot/compressed/vmlinux.bin
arch/x86/boot/compressed/vmlinux.relocs | zstd -22 --ultra; printf
\114\015\315\001; } > arch/x86/boot/compressed/vmlinux.bin.zst
arch/x86/boot/compressed/mkpiggy
arch/x86/boot/compressed/vmlinux.bin.zst >
arch/x86/boot/compressed/piggy.S
ld.lld-11 -m elf_x86_64 -pie --no-dynamic-linker -T
arch/x86/boot/compressed/vmlinux.lds
arch/x86/boot/compressed/kernel_info.o
arch/x86/boot/compressed/head_64.o arch/x86/boot/compressed/misc.o
arch/x86/boot/compressed/string.o arch/x86/boot/compressed/cmdline.o
arch/x86/boot/compressed/error.o arch/x86/boot/compressed/piggy.o
arch/x86/boot/compressed/cpuflags.o
arch/x86/boot/compressed/early_serial_console.o
arch/x86/boot/compressed/kaslr.o arch/x86/boot/compressed/kaslr_64.o
arch/x86/boot/compressed/mem_encrypt.o
arch/x86/boot/compressed/pgtable_64.o arch/x86/boot/compressed/acpi.o
drivers/firmware/efi/libstub/lib.a
arch/x86/boot/compressed/efi_thunk_64.o -o
arch/x86/boot/compressed/vmlinux
llvm-nm-11 arch/x86/boot/compressed/vmlinux | sed -n -e
's/^\([0-9a-fA-F]*\) [a-zA-Z]
\(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|efi32_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|z_.*\)$/#define
ZO_ 0x/p' > arch/x86/boot/zoffset.h
llvm-objcopy-11 -O binary -R .note -R .comment -S
arch/x86/boot/compressed/vmlinux arch/x86/boot/vmlinux.bin

- EOT -

> Changes from v3:
> - Move hidden.h to include/linux so the EFI stub and the compressed
> kernel can share the same file
>
> Changes from v2:
> - Incorporate Ard's patches for eliminating GOT references into this
> series
> - Rebase on v5.8-rc3
>
> v2: https://lore.kernel.org/lkml/[email protected]/
>
> Changes from v1:
> - Add .text.* to setup.ld instead of just .text.startup
> - Rename the la() macro introduced in the second patch for 64-bit to
> rva(), and rework the explanatory comment.
> - In the last patch, check both .rel.dyn and .rela.dyn, instead of just
> one per arch.
>
> Ard Biesheuvel (3):
> x86/boot/compressed: Move .got.plt entries out of the .got section
> x86/boot/compressed: Force hidden visibility for all symbol references
> x86/boot/compressed: Get rid of GOT fixup code
>
> Arvind Sankar (4):
> x86/boot: Add .text.* to setup.ld
> x86/boot: Remove run-time relocations from .head.text code
> x86/boot: Remove run-time relocations from head_{32,64}.S
> x86/boot: Check that there are no run-time relocations
>
> arch/x86/boot/compressed/Makefile | 39 +-----
> arch/x86/boot/compressed/head_32.S | 99 +++++----------
> arch/x86/boot/compressed/head_64.S | 165 ++++++++++---------------
> arch/x86/boot/compressed/mkpiggy.c | 6 +
> arch/x86/boot/compressed/vmlinux.lds.S | 24 +++-
> arch/x86/boot/setup.ld | 2 +-
> drivers/firmware/efi/libstub/Makefile | 2 +-
> drivers/firmware/efi/libstub/hidden.h | 6 -
> include/linux/hidden.h | 19 +++
> 9 files changed, 153 insertions(+), 209 deletions(-)
> delete mode 100644 drivers/firmware/efi/libstub/hidden.h
> create mode 100644 include/linux/hidden.h
>
>
> base-commit: e9919e11e219eaa5e8041b7b1a196839143e9125
> --
> 2.26.2
>

2020-07-15 07:13:24

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Wed, Jul 15, 2020 at 3:46 AM Sedat Dilek <[email protected]> wrote:
>
> On Wed, Jul 15, 2020 at 2:41 AM Arvind Sankar <[email protected]> wrote:
> >
> > The compressed kernel currently contains bogus run-time relocations in
> > the startup code in head_{32,64}.S, which are generated by the linker,
> > but must not actually be processed at run-time.
> >
> > This generates warnings when linking with the BFD linker, and errors
> > with LLD, which defaults to erroring on run-time relocations in read-only
> > sections. It also requires the -z noreloc-overflow hack for the 64-bit
> > kernel, which prevents us from linking it as -pie on an older BFD linker
> > (<= 2.26) or on LLD, because the locations that are to be apparently
> > relocated are only 32-bits in size and so cannot really have
> > R_X86_64_RELATIVE relocations.
> >
> > This series aims to get rid of these relocations. I've build- and
> > boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
> > gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
> > has other issues [0].
> >
> > The first three patches by Ard remove indirection via the GOT from the
> > compressed kernel code.
> >
> > The next patch is an independent fix for LLD, to avoid an orphan
> > section in arch/x86/boot/setup.elf.
> >
> > The fifth patch gets rid of almost all the relocations. It uses
> > standard PIC addressing technique for 32-bit, i.e. loading a register
> > with the address of _GLOBAL_OFFSET_TABLE_ and then using GOTOFF
> > references to access variables. For 64-bit, there is 32-bit code that
> > cannot use RIP-relative addressing, and also cannot use the 32-bit
> > method, since GOTOFF references are 64-bit only. This is instead handled
> > using a macro to replace a reference like gdt with (gdt-startup_32)
> > instead. The assembler will generate a PC32 relocation entry, with
> > addend set to (.-startup_32), and these will be replaced with constants
> > at link time. This works as long as all the code using such references
> > lives in the same section as startup_32, i.e. in .head.text.
> >
> > The sixth patch addresses a remaining issue with the BFD linker, which
> > generates run-time relocations for absolute symbols. We use z_input_len
> > and z_output_len, defined in the generated piggy.S file, as symbols
> > whose absolute "addresses" are actually the size of the compressed
> > payload and the size of the decompressed kernel image respectively. LLD
> > does not generate relocations for these two symbols, but the BFD linker
> > does, prior to the upcoming 2.35. To get around this, piggy.S is
> > extended to also define two u32 variables (in .rodata) with the lengths,
> > and the head code is modified to use those instead of the symbol
> > addresses.
> >
> > An alternative way to handle z_input_len/z_output_len would be to just
> > include piggy.S in head_{32,64}.S instead of as a separate object file,
> > since the GNU assembler doesn't generate relocations for symbols set to
> > constants.
> >
> > The last patch adds a check in the linker script to ensure that no
> > run-time relocations get reintroduced.
> >
> > [0] https://lore.kernel.org/lkml/[email protected]/
> >
> > Changes from v4:
> > - Move -pie --no-dynamic-linker from KBUILD_LDFLAGS to LDFLAGS_vmlinux
> > Sedat: I'm not clear on whether you tested with the final LDFLAGS,
> > could you confirm: i.e. if you tested with -pie passed to LLD?
> > - Replace runtime -> run-time to be consistent in wording
> >
>
> Hi Arvind,
>
> thanks for v5.
>
> With my diff applied against your patchset *v4*:
>
> diff --git a/arch/x86/boot/compressed/Makefile
> b/arch/x86/boot/compressed/Makefile
> index 789d5d14d8b0..d0aafcd8cf6c 100644
> --- a/arch/x86/boot/compressed/Makefile
> +++ b/arch/x86/boot/compressed/Makefile
> @@ -51,8 +51,8 @@ UBSAN_SANITIZE :=n
> KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> # Compressed kernel should be built as PIE since it may be loaded at any
> # address by the bootloader.
> -KBUILD_LDFLAGS += -pie $(call ld-option, --no-dynamic-linker)
> -LDFLAGS_vmlinux := -T
> +LDFLAGS_vmlinux := -pie $(call ld-option, --no-dynamic-linker)
> +LDFLAGS_vmlinux += -T
>
> hostprogs := mkpiggy
> HOST_EXTRACFLAGS += -I$(srctree)/tools/include
>
> I was able to build/assemble with LLVM/Clang v11.0.0-git+ffee8040534
> and boot on bare metal.
>
> Note:
> I have applied some additional patches to be compliant with LLVM_IAS=1
> (Clang's Integrated Assembler) and LLVM=1 means LLVM utilities.
> ( As pointed out zstd-v7. )
>
> - Sedat -
>
> P.S.: Check my build-log
>
> $ grep 'arch/x86/boot/compressed/vmlinux'
> build-log_5.8.0-rc5-3-amd64-llvm11-ias.txt
> make -f ./scripts/Makefile.build obj=arch/x86/boot/compressed
> arch/x86/boot/compressed/vmlinux
> clang-11 -E -Wp,-MMD,arch/x86/boot/compressed/.vmlinux.lds.d
> -nostdinc -isystem /usr/lib/llvm-11/lib/clang/11.0.0/include
> -I./arch/x86/include -I./arch/x86/include/generated -I./include
> -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi
> -I./include/uapi -I./include/generated/uapi -include
> ./include/linux/kconfig.h -D__KERNEL__ -Qunused-arguments -P -Ux86
> -D__ASSEMBLY__ -DLINKER_SCRIPT -o arch/x86/boot/compressed/vmlinux.lds
> arch/x86/boot/compressed/vmlinux.lds.S
> llvm-objcopy-11 -R .comment -S vmlinux arch/x86/boot/compressed/vmlinux.bin
> arch/x86/tools/relocs vmlinux >
> arch/x86/boot/compressed/vmlinux.relocs;arch/x86/tools/relocs
> --abs-relocs vmlinux
> { cat arch/x86/boot/compressed/vmlinux.bin
> arch/x86/boot/compressed/vmlinux.relocs | zstd -22 --ultra; printf
> \114\015\315\001; } > arch/x86/boot/compressed/vmlinux.bin.zst
> arch/x86/boot/compressed/mkpiggy
> arch/x86/boot/compressed/vmlinux.bin.zst >
> arch/x86/boot/compressed/piggy.S
> ld.lld-11 -m elf_x86_64 -pie --no-dynamic-linker -T
> arch/x86/boot/compressed/vmlinux.lds
> arch/x86/boot/compressed/kernel_info.o
> arch/x86/boot/compressed/head_64.o arch/x86/boot/compressed/misc.o
> arch/x86/boot/compressed/string.o arch/x86/boot/compressed/cmdline.o
> arch/x86/boot/compressed/error.o arch/x86/boot/compressed/piggy.o
> arch/x86/boot/compressed/cpuflags.o
> arch/x86/boot/compressed/early_serial_console.o
> arch/x86/boot/compressed/kaslr.o arch/x86/boot/compressed/kaslr_64.o
> arch/x86/boot/compressed/mem_encrypt.o
> arch/x86/boot/compressed/pgtable_64.o arch/x86/boot/compressed/acpi.o
> drivers/firmware/efi/libstub/lib.a
> arch/x86/boot/compressed/efi_thunk_64.o -o
> arch/x86/boot/compressed/vmlinux
> llvm-nm-11 arch/x86/boot/compressed/vmlinux | sed -n -e
> 's/^\([0-9a-fA-F]*\) [a-zA-Z]
> \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|efi32_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|z_.*\)$/#define
> ZO_ 0x/p' > arch/x86/boot/zoffset.h
> llvm-objcopy-11 -O binary -R .note -R .comment -S
> arch/x86/boot/compressed/vmlinux arch/x86/boot/vmlinux.bin
>
> - EOT -
>

Re-tested v5 of your patchset, feel free to add appropriate credits.

- Sedat -

> > Changes from v3:
> > - Move hidden.h to include/linux so the EFI stub and the compressed
> > kernel can share the same file
> >
> > Changes from v2:
> > - Incorporate Ard's patches for eliminating GOT references into this
> > series
> > - Rebase on v5.8-rc3
> >
> > v2: https://lore.kernel.org/lkml/[email protected]/
> >
> > Changes from v1:
> > - Add .text.* to setup.ld instead of just .text.startup
> > - Rename the la() macro introduced in the second patch for 64-bit to
> > rva(), and rework the explanatory comment.
> > - In the last patch, check both .rel.dyn and .rela.dyn, instead of just
> > one per arch.
> >
> > Ard Biesheuvel (3):
> > x86/boot/compressed: Move .got.plt entries out of the .got section
> > x86/boot/compressed: Force hidden visibility for all symbol references
> > x86/boot/compressed: Get rid of GOT fixup code
> >
> > Arvind Sankar (4):
> > x86/boot: Add .text.* to setup.ld
> > x86/boot: Remove run-time relocations from .head.text code
> > x86/boot: Remove run-time relocations from head_{32,64}.S
> > x86/boot: Check that there are no run-time relocations
> >
> > arch/x86/boot/compressed/Makefile | 39 +-----
> > arch/x86/boot/compressed/head_32.S | 99 +++++----------
> > arch/x86/boot/compressed/head_64.S | 165 ++++++++++---------------
> > arch/x86/boot/compressed/mkpiggy.c | 6 +
> > arch/x86/boot/compressed/vmlinux.lds.S | 24 +++-
> > arch/x86/boot/setup.ld | 2 +-
> > drivers/firmware/efi/libstub/Makefile | 2 +-
> > drivers/firmware/efi/libstub/hidden.h | 6 -
> > include/linux/hidden.h | 19 +++
> > 9 files changed, 153 insertions(+), 209 deletions(-)
> > delete mode 100644 drivers/firmware/efi/libstub/hidden.h
> > create mode 100644 include/linux/hidden.h
> >
> >
> > base-commit: e9919e11e219eaa5e8041b7b1a196839143e9125
> > --
> > 2.26.2
> >

2020-07-15 09:18:45

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 1/7] x86/boot/compressed: Move .got.plt entries out of the .got section

On Wed, Jul 15, 2020 at 2:41 AM Arvind Sankar <[email protected]> wrote:
>
> From: Ard Biesheuvel <[email protected]>
>
> The .got.plt section contains the part of the GOT which is used by PLT
> entries, and which gets updated lazily by the dynamic loader when
> function calls are dispatched through those PLT entries.
>
> On fully linked binaries such as the kernel proper or the decompressor,
> this never happens, and so in practice, the .got.plt section consists
> only of the first 3 magic entries that are meant to point at the _DYNAMIC
> section and at the fixup routine in the loader. However, since we don't
> use a dynamic loader, those entries are never populated or used.
>
> This means that treating those entries like ordinary GOT entries, and
> updating their values based on the actual placement of the executable in
> memory is completely pointless, and we can just ignore the .got.plt
> section entirely, provided that it has no additional entries beyond
> the first 3 ones.
>
> So add an assertion in the linker script to ensure that this assumption
> holds, and move the contents out of the [_got, _egot) memory range that
> is modified by the GOT fixup routines.
>
> While at it, drop the KEEP(), since it has no effect on the contents
> of output sections that are created by the linker itself.
>
> Reviewed-by: Kees Cook <[email protected]>
> Signed-off-by: Ard Biesheuvel <[email protected]>
> Acked-by: Arvind Sankar <[email protected]>
> Signed-off-by: Arvind Sankar <[email protected]>
> From: Ard Biesheuvel <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]

Tested-by: Sedat Dilek <[email protected]>

- Sedat -

> ---
> arch/x86/boot/compressed/vmlinux.lds.S | 11 ++++++++++-
> 1 file changed, 10 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
> index 8f1025d1f681..b17d218ccdf9 100644
> --- a/arch/x86/boot/compressed/vmlinux.lds.S
> +++ b/arch/x86/boot/compressed/vmlinux.lds.S
> @@ -44,10 +44,13 @@ SECTIONS
> }
> .got : {
> _got = .;
> - KEEP(*(.got.plt))
> KEEP(*(.got))
> _egot = .;
> }
> + .got.plt : {
> + *(.got.plt)
> + }
> +
> .data : {
> _data = . ;
> *(.data)
> @@ -77,3 +80,9 @@ SECTIONS
>
> DISCARDS
> }
> +
> +#ifdef CONFIG_X86_64
> +ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT entries detected!")
> +#else
> +ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0xc, "Unexpected GOT/PLT entries detected!")
> +#endif
> --
> 2.26.2
>

2020-07-15 09:20:21

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 2/7] x86/boot/compressed: Force hidden visibility for all symbol references

On Wed, Jul 15, 2020 at 2:41 AM Arvind Sankar <[email protected]> wrote:
>
> From: Ard Biesheuvel <[email protected]>
>
> Eliminate all GOT entries in the decompressor binary, by forcing hidden
> visibility for all symbol references, which informs the compiler that
> such references will be resolved at link time without the need for
> allocating GOT entries.
>
> To ensure that no GOT entries will creep back in, add an assertion to
> the decompressor linker script that will fire if the .got section has
> a non-zero size.
>
> [Arvind: move hidden.h to include/linux instead of making a copy]
>

Tested-by: Sedat Dilek <[email protected]>
Reported-by: Sedat Dilek <[email protected]>

Reported hidden.h breakage and asked for a follow-up.

- Sedat -

[1] https://marc.info/?l=linux-kernel&m=159056070321982&w=2

> Reviewed-by: Kees Cook <[email protected]>
> Signed-off-by: Ard Biesheuvel <[email protected]>
> Acked-by: Arvind Sankar <[email protected]>
> Signed-off-by: Arvind Sankar <[email protected]>
> From: Ard Biesheuvel <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]
> ---
> arch/x86/boot/compressed/Makefile | 1 +
> arch/x86/boot/compressed/vmlinux.lds.S | 1 +
> drivers/firmware/efi/libstub/Makefile | 2 +-
> drivers/firmware/efi/libstub/hidden.h | 6 ------
> include/linux/hidden.h | 19 +++++++++++++++++++
> 5 files changed, 22 insertions(+), 7 deletions(-)
> delete mode 100644 drivers/firmware/efi/libstub/hidden.h
> create mode 100644 include/linux/hidden.h
>
> diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
> index 7619742f91c9..c829d874dcac 100644
> --- a/arch/x86/boot/compressed/Makefile
> +++ b/arch/x86/boot/compressed/Makefile
> @@ -42,6 +42,7 @@ KBUILD_CFLAGS += $(call cc-disable-warning, gnu)
> KBUILD_CFLAGS += -Wno-pointer-sign
> KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=)
> KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
> +KBUILD_CFLAGS += -include $(srctree)/include/linux/hidden.h
>
> KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
> GCOV_PROFILE := n
> diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
> index b17d218ccdf9..4bcc943842ab 100644
> --- a/arch/x86/boot/compressed/vmlinux.lds.S
> +++ b/arch/x86/boot/compressed/vmlinux.lds.S
> @@ -81,6 +81,7 @@ SECTIONS
> DISCARDS
> }
>
> +ASSERT(SIZEOF(.got) == 0, "Unexpected GOT entries detected!")
> #ifdef CONFIG_X86_64
> ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT entries detected!")
> #else
> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
> index 4cce372edaf4..609157a40493 100644
> --- a/drivers/firmware/efi/libstub/Makefile
> +++ b/drivers/firmware/efi/libstub/Makefile
> @@ -27,7 +27,7 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
> cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt
>
> KBUILD_CFLAGS := $(cflags-y) -Os -DDISABLE_BRANCH_PROFILING \
> - -include $(srctree)/drivers/firmware/efi/libstub/hidden.h \
> + -include $(srctree)/include/linux/hidden.h \
> -D__NO_FORTIFY \
> $(call cc-option,-ffreestanding) \
> $(call cc-option,-fno-stack-protector) \
> diff --git a/drivers/firmware/efi/libstub/hidden.h b/drivers/firmware/efi/libstub/hidden.h
> deleted file mode 100644
> index 3493b041f419..000000000000
> --- a/drivers/firmware/efi/libstub/hidden.h
> +++ /dev/null
> @@ -1,6 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0 */
> -/*
> - * To prevent the compiler from emitting GOT-indirected (and thus absolute)
> - * references to any global symbols, override their visibility as 'hidden'
> - */
> -#pragma GCC visibility push(hidden)
> diff --git a/include/linux/hidden.h b/include/linux/hidden.h
> new file mode 100644
> index 000000000000..49a17b6b5962
> --- /dev/null
> +++ b/include/linux/hidden.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * When building position independent code with GCC using the -fPIC option,
> + * (or even the -fPIE one on older versions), it will assume that we are
> + * building a dynamic object (either a shared library or an executable) that
> + * may have symbol references that can only be resolved at load time. For a
> + * variety of reasons (ELF symbol preemption, the CoW footprint of the section
> + * that is modified by the loader), this results in all references to symbols
> + * with external linkage to go via entries in the Global Offset Table (GOT),
> + * which carries absolute addresses which need to be fixed up when the
> + * executable image is loaded at an offset which is different from its link
> + * time offset.
> + *
> + * Fortunately, there is a way to inform the compiler that such symbol
> + * references will be satisfied at link time rather than at load time, by
> + * giving them 'hidden' visibility.
> + */
> +
> +#pragma GCC visibility push(hidden)
> --
> 2.26.2
>

2020-07-15 09:20:51

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 3/7] x86/boot/compressed: Get rid of GOT fixup code

On Wed, Jul 15, 2020 at 2:41 AM Arvind Sankar <[email protected]> wrote:
>
> From: Ard Biesheuvel <[email protected]>
>
> In a previous patch, we have eliminated GOT entries from the decompressor
> binary and added an assertion that the .got section is empty. This means
> that the GOT fixup routines that exist in both the 32-bit and 64-bit
> startup routines have become dead code, and can be removed.
>
> While at it, drop the KEEP() from the linker script, as it has no effect
> on the contents of output sections that are created by the linker itself.
>
> Reviewed-by: Kees Cook <[email protected]>
> Signed-off-by: Ard Biesheuvel <[email protected]>
> Acked-by: Arvind Sankar <[email protected]>
> Signed-off-by: Arvind Sankar <[email protected]>
> From: Ard Biesheuvel <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]

Tested-by: Sedat Dilek <[email protected]>

- Sedat -

> ---
> arch/x86/boot/compressed/head_32.S | 24 ++---------
> arch/x86/boot/compressed/head_64.S | 57 --------------------------
> arch/x86/boot/compressed/vmlinux.lds.S | 4 +-
> 3 files changed, 5 insertions(+), 80 deletions(-)
>
> diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
> index 03557f2174bf..39f0bb43218f 100644
> --- a/arch/x86/boot/compressed/head_32.S
> +++ b/arch/x86/boot/compressed/head_32.S
> @@ -49,16 +49,13 @@
> * 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, _egot and _end, 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, _egot and _end as
> - * hidden:
> + * _bss, _ebss and _end, 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 and _end as hidden:
> */
> .hidden _bss
> .hidden _ebss
> - .hidden _got
> - .hidden _egot
> .hidden _end
>
> __HEAD
> @@ -192,19 +189,6 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
> shrl $2, %ecx
> rep stosl
>
> -/*
> - * Adjust our own GOT
> - */
> - leal _got(%ebx), %edx
> - leal _egot(%ebx), %ecx
> -1:
> - cmpl %ecx, %edx
> - jae 2f
> - addl %ebx, (%edx)
> - addl $4, %edx
> - jmp 1b
> -2:
> -
> /*
> * Do the extraction, and jump to the new kernel..
> */
> diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
> index 97d37f0a34f5..bf1ab30acc5b 100644
> --- a/arch/x86/boot/compressed/head_64.S
> +++ b/arch/x86/boot/compressed/head_64.S
> @@ -40,8 +40,6 @@
> */
> .hidden _bss
> .hidden _ebss
> - .hidden _got
> - .hidden _egot
> .hidden _end
>
> __HEAD
> @@ -353,25 +351,6 @@ SYM_CODE_START(startup_64)
> /* Set up the stack */
> leaq boot_stack_end(%rbx), %rsp
>
> - /*
> - * paging_prepare() and cleanup_trampoline() below can have GOT
> - * references. Adjust the table with address we are running at.
> - *
> - * Zero RAX for adjust_got: the GOT was not adjusted before;
> - * there's no adjustment to undo.
> - */
> - xorq %rax, %rax
> -
> - /*
> - * Calculate the address the binary is loaded at and use it as
> - * a GOT adjustment.
> - */
> - call 1f
> -1: popq %rdi
> - subq $1b, %rdi
> -
> - call .Ladjust_got
> -
> /*
> * At this point we are in long mode with 4-level paging enabled,
> * but we might want to enable 5-level paging or vice versa.
> @@ -464,21 +443,6 @@ trampoline_return:
> pushq $0
> popfq
>
> - /*
> - * Previously we've adjusted the GOT with address the binary was
> - * loaded at. Now we need to re-adjust for relocation address.
> - *
> - * Calculate the address the binary is loaded at, so that we can
> - * undo the previous GOT adjustment.
> - */
> - call 1f
> -1: popq %rax
> - subq $1b, %rax
> -
> - /* The new adjustment is the relocation address */
> - movq %rbx, %rdi
> - call .Ladjust_got
> -
> /*
> * Copy the compressed kernel to the end of our buffer
> * where decompression in place becomes safe.
> @@ -556,27 +520,6 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
> jmp *%rax
> SYM_FUNC_END(.Lrelocated)
>
> -/*
> - * Adjust the global offset table
> - *
> - * RAX is the previous adjustment of the table to undo (use 0 if it's the
> - * first time we touch GOT).
> - * RDI is the new adjustment to apply.
> - */
> -.Ladjust_got:
> - /* Walk through the GOT adding the address to the entries */
> - leaq _got(%rip), %rdx
> - leaq _egot(%rip), %rcx
> -1:
> - cmpq %rcx, %rdx
> - jae 2f
> - subq %rax, (%rdx) /* Undo previous adjustment */
> - addq %rdi, (%rdx) /* Apply the new adjustment */
> - addq $8, %rdx
> - jmp 1b
> -2:
> - ret
> -
> .code32
> /*
> * This is the 32-bit trampoline that will be copied over to low memory.
> diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
> index 4bcc943842ab..a4a4a59a2628 100644
> --- a/arch/x86/boot/compressed/vmlinux.lds.S
> +++ b/arch/x86/boot/compressed/vmlinux.lds.S
> @@ -43,9 +43,7 @@ SECTIONS
> _erodata = . ;
> }
> .got : {
> - _got = .;
> - KEEP(*(.got))
> - _egot = .;
> + *(.got)
> }
> .got.plt : {
> *(.got.plt)
> --
> 2.26.2
>

2020-07-15 09:23:01

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 4/7] x86/boot: Add .text.* to setup.ld

On Wed, Jul 15, 2020 at 2:41 AM Arvind Sankar <[email protected]> wrote:
>
> gcc puts the main function into .text.startup when compiled with -Os (or
> -O2). This results in arch/x86/boot/main.c having a .text.startup
> section which is currently not included explicitly in the linker script
> setup.ld in the same directory.
>
> The BFD linker places this orphan section immediately after .text, so
> this still works. However, LLD git, since [1], is choosing to place it
> immediately after the .bstext section instead (this is the first code
> section). This plays havoc with the section layout that setup.elf
> requires to create the setup header, for eg on 64-bit:
>
> LD arch/x86/boot/setup.elf
> ld.lld: error: section .text.startup file range overlaps with .header
> >>> .text.startup range is [0x200040, 0x2001FE]
> >>> .header range is [0x2001EF, 0x20026B]
>
> ld.lld: error: section .header file range overlaps with .bsdata
> >>> .header range is [0x2001EF, 0x20026B]
> >>> .bsdata range is [0x2001FF, 0x200398]
>
> ld.lld: error: section .bsdata file range overlaps with .entrytext
> >>> .bsdata range is [0x2001FF, 0x200398]
> >>> .entrytext range is [0x20026C, 0x2002D3]
>
> ld.lld: error: section .text.startup virtual address range overlaps
> with .header
> >>> .text.startup range is [0x40, 0x1FE]
> >>> .header range is [0x1EF, 0x26B]
>
> ld.lld: error: section .header virtual address range overlaps with
> .bsdata
> >>> .header range is [0x1EF, 0x26B]
> >>> .bsdata range is [0x1FF, 0x398]
>
> ld.lld: error: section .bsdata virtual address range overlaps with
> .entrytext
> >>> .bsdata range is [0x1FF, 0x398]
> >>> .entrytext range is [0x26C, 0x2D3]
>
> ld.lld: error: section .text.startup load address range overlaps with
> .header
> >>> .text.startup range is [0x40, 0x1FE]
> >>> .header range is [0x1EF, 0x26B]
>
> ld.lld: error: section .header load address range overlaps with
> .bsdata
> >>> .header range is [0x1EF, 0x26B]
> >>> .bsdata range is [0x1FF, 0x398]
>
> ld.lld: error: section .bsdata load address range overlaps with
> .entrytext
> >>> .bsdata range is [0x1FF, 0x398]
> >>> .entrytext range is [0x26C, 0x2D3]
>
> Add .text.* to the .text output section to fix this, and also prevent
> any future surprises if the compiler decides to create other such
> sections.
>
> [1] https://reviews.llvm.org/D75225
>
> Reviewed-by: Kees Cook <[email protected]>
> Reviewed-by: Ard Biesheuvel <[email protected]>
> Reviewed-by: Fangrui Song <[email protected]>
> Signed-off-by: Arvind Sankar <[email protected]>

Tested-by: Sedat Dilek <[email protected]>

- Sedat -

> ---
> arch/x86/boot/setup.ld | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/x86/boot/setup.ld b/arch/x86/boot/setup.ld
> index 24c95522f231..49546c247ae2 100644
> --- a/arch/x86/boot/setup.ld
> +++ b/arch/x86/boot/setup.ld
> @@ -20,7 +20,7 @@ SECTIONS
> .initdata : { *(.initdata) }
> __end_init = .;
>
> - .text : { *(.text) }
> + .text : { *(.text .text.*) }
> .text32 : { *(.text32) }
>
> . = ALIGN(16);
> --
> 2.26.2
>
> --
> You received this message because you are subscribed to the Google Groups "Clang Built Linux" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
> To view this discussion on the web visit https://groups.google.com/d/msgid/clang-built-linux/20200715004133.1430068-5-nivedita%40alum.mit.edu.

2020-07-15 09:23:57

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 5/7] x86/boot: Remove run-time relocations from .head.text code

On Wed, Jul 15, 2020 at 2:41 AM Arvind Sankar <[email protected]> wrote:
>
> The assembly code in head_{32,64}.S, while meant to be
> position-independent, generates run-time relocations because it uses
> instructions such as
> leal gdt(%edx), %eax
> which make the assembler and linker think that the code is using %edx as
> an index into gdt, and hence gdt needs to be relocated to its run-time
> address.
>
> On 32-bit, with lld Dmitry Golovin reports that this results in a
> link-time error with default options (i.e. unless -z notext is
> explicitly passed):
> LD arch/x86/boot/compressed/vmlinux
> ld.lld: error: can't create dynamic relocation R_386_32 against local
> symbol in readonly segment; recompile object files with -fPIC or pass
> '-Wl,-z,notext' to allow text relocations in the output
>
> With the BFD linker, this generates a warning during the build, if
> --warn-shared-textrel is enabled, which at least Gentoo enables by
> default:
> LD arch/x86/boot/compressed/vmlinux
> ld: arch/x86/boot/compressed/head_32.o: warning: relocation in read-only section `.head.text'
> ld: warning: creating a DT_TEXTREL in object
>
> On 64-bit, it is not possible to link the kernel as -pie with lld, and
> it is only possible with a BFD linker that supports -z noreloc-overflow,
> i.e. versions >2.26. This is because these instructions cannot really be
> relocated: the displacement field is only 32-bits wide, and thus cannot
> be relocated for a 64-bit load address. The -z noreloc-overflow option
> simply overrides the linker error, and results in R_X86_64_RELATIVE
> relocations that apply a 64-bit relocation to a 32-bit field anyway.
> This happens to work because nothing will process these run-time
> relocations.
>
> Start fixing this by removing relocations from .head.text:
> - On 32-bit, use a base register that holds the address of the GOT and
> reference symbol addresses using @GOTOFF, i.e.
> leal gdt@GOTOFF(%edx), %eax
> - On 64-bit, most of the code can (and already does) use %rip-relative
> addressing, however the .code32 bits can't, and the 64-bit code also
> needs to reference symbol addresses as they will be after moving the
> compressed kernel to the end of the decompression buffer.
> For these cases, reference the symbols as an offset to startup_32 to
> avoid creating relocations, i.e.
> leal (gdt-startup_32)(%bp), %eax
> This only works in .head.text as the subtraction cannot be represented
> as a PC-relative relocation unless startup_32 is in the same section
> as the code. Move efi32_pe_entry into .head.text so that it can use
> the same method to avoid relocations.
>
> Reviewed-by: Kees Cook <[email protected]>
> Reviewed-by: Ard Biesheuvel <[email protected]>
> Reviewed-by: Fangrui Song <[email protected]>
> Signed-off-by: Arvind Sankar <[email protected]>

Tested-by: Sedat Dilek <[email protected]>

- Sedat -

> ---
> arch/x86/boot/compressed/head_32.S | 64 +++++++-----------
> arch/x86/boot/compressed/head_64.S | 104 ++++++++++++++++++-----------
> 2 files changed, 90 insertions(+), 78 deletions(-)
>
> diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
> index 39f0bb43218f..8c1a4f5610f5 100644
> --- a/arch/x86/boot/compressed/head_32.S
> +++ b/arch/x86/boot/compressed/head_32.S
> @@ -33,26 +33,10 @@
> #include <asm/bootparam.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 and _end, 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 and _end as hidden:
> + * These symbols needed to be marked as .hidden to prevent the BFD linker from
> + * generating R_386_32 (rather than R_386_RELATIVE) relocations for them when
> + * the 32-bit compressed kernel is linked as PIE. This is no longer necessary,
> + * but it doesn't hurt to keep them .hidden.
> */
> .hidden _bss
> .hidden _ebss
> @@ -74,10 +58,10 @@ SYM_FUNC_START(startup_32)
> leal (BP_scratch+4)(%esi), %esp
> call 1f
> 1: popl %edx
> - subl $1b, %edx
> + addl $_GLOBAL_OFFSET_TABLE_+(.-1b), %edx
>
> /* Load new GDT */
> - leal gdt(%edx), %eax
> + leal gdt@GOTOFF(%edx), %eax
> movl %eax, 2(%eax)
> lgdt (%eax)
>
> @@ -90,14 +74,16 @@ SYM_FUNC_START(startup_32)
> movl %eax, %ss
>
> /*
> - * %edx contains the address we are loaded at by the boot loader and %ebx
> - * contains the address where we should move the kernel image temporarily
> - * for safe in-place decompression. %ebp contains the address that the kernel
> - * will be decompressed to.
> + * %edx contains the address we are loaded at by the boot loader (plus the
> + * offset to the GOT). The below code calculates %ebx to be the address where
> + * we should move the kernel image temporarily for safe in-place decompression
> + * (again, plus the offset to the GOT).
> + *
> + * %ebp is calculated to be the address that the kernel will be decompressed to.
> */
>
> #ifdef CONFIG_RELOCATABLE
> - movl %edx, %ebx
> + leal startup_32@GOTOFF(%edx), %ebx
>
> #ifdef CONFIG_EFI_STUB
> /*
> @@ -108,7 +94,7 @@ SYM_FUNC_START(startup_32)
> * image_offset = startup_32 - image_base
> * Otherwise image_offset will be zero and has no effect on the calculations.
> */
> - subl image_offset(%edx), %ebx
> + subl image_offset@GOTOFF(%edx), %ebx
> #endif
>
> movl BP_kernel_alignment(%esi), %eax
> @@ -125,10 +111,10 @@ SYM_FUNC_START(startup_32)
> movl %ebx, %ebp // Save the output address for later
> /* Target address to relocate to for decompression */
> addl BP_init_size(%esi), %ebx
> - subl $_end, %ebx
> + subl $_end@GOTOFF, %ebx
>
> /* Set up the stack */
> - leal boot_stack_end(%ebx), %esp
> + leal boot_stack_end@GOTOFF(%ebx), %esp
>
> /* Zero EFLAGS */
> pushl $0
> @@ -139,8 +125,8 @@ SYM_FUNC_START(startup_32)
> * where decompression in place becomes safe.
> */
> pushl %esi
> - leal (_bss-4)(%edx), %esi
> - leal (_bss-4)(%ebx), %edi
> + leal (_bss@GOTOFF-4)(%edx), %esi
> + leal (_bss@GOTOFF-4)(%ebx), %edi
> movl $(_bss - startup_32), %ecx
> shrl $2, %ecx
> std
> @@ -153,14 +139,14 @@ SYM_FUNC_START(startup_32)
> * during extract_kernel below. To avoid any issues, repoint the GDTR
> * to the new copy of the GDT.
> */
> - leal gdt(%ebx), %eax
> + leal gdt@GOTOFF(%ebx), %eax
> movl %eax, 2(%eax)
> lgdt (%eax)
>
> /*
> * Jump to the relocated address.
> */
> - leal .Lrelocated(%ebx), %eax
> + leal .Lrelocated@GOTOFF(%ebx), %eax
> jmp *%eax
> SYM_FUNC_END(startup_32)
>
> @@ -170,7 +156,7 @@ SYM_FUNC_START_ALIAS(efi_stub_entry)
> add $0x4, %esp
> movl 8(%esp), %esi /* save boot_params pointer */
> call efi_main
> - leal startup_32(%eax), %eax
> + /* efi_main returns the possibly relocated address of startup_32 */
> jmp *%eax
> SYM_FUNC_END(efi32_stub_entry)
> SYM_FUNC_END_ALIAS(efi_stub_entry)
> @@ -183,8 +169,8 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
> * Clear BSS (stack is currently empty)
> */
> xorl %eax, %eax
> - leal _bss(%ebx), %edi
> - leal _ebss(%ebx), %ecx
> + leal _bss@GOTOFF(%ebx), %edi
> + leal _ebss@GOTOFF(%ebx), %ecx
> subl %edi, %ecx
> shrl $2, %ecx
> rep stosl
> @@ -198,9 +184,9 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
> pushl %ebp /* output address */
>
> pushl $z_input_len /* input_len */
> - leal input_data(%ebx), %eax
> + leal input_data@GOTOFF(%ebx), %eax
> pushl %eax /* input_data */
> - leal boot_heap(%ebx), %eax
> + leal boot_heap@GOTOFF(%ebx), %eax
> pushl %eax /* heap area */
> pushl %esi /* real mode pointer */
> call extract_kernel /* returns kernel location in %eax */
> diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
> index bf1ab30acc5b..11429092c224 100644
> --- a/arch/x86/boot/compressed/head_64.S
> +++ b/arch/x86/boot/compressed/head_64.S
> @@ -43,6 +43,32 @@
> .hidden _end
>
> __HEAD
> +
> +/*
> + * This macro gives the relative virtual address of X, i.e. the offset of X
> + * from startup_32. This is the same as the link-time virtual address of X,
> + * since startup_32 is at 0, but defining it this way tells the
> + * assembler/linker that we do not want the actual run-time address of X. This
> + * prevents the linker from trying to create unwanted run-time relocation
> + * entries for the reference when the compressed kernel is linked as PIE.
> + *
> + * A reference X(%reg) will result in the link-time VA of X being stored with
> + * the instruction, and a run-time R_X86_64_RELATIVE relocation entry that
> + * adds the 64-bit base address where the kernel is loaded.
> + *
> + * Replacing it with (X-startup_32)(%reg) results in the offset being stored,
> + * and no run-time relocation.
> + *
> + * The macro should be used as a displacement with a base register containing
> + * the run-time address of startup_32 [i.e. rva(X)(%reg)], or as an immediate
> + * [$ rva(X)].
> + *
> + * This macro can only be used from within the .head.text section, since the
> + * expression requires startup_32 to be in the same section as the code being
> + * assembled.
> + */
> +#define rva(X) ((X) - startup_32)
> +
> .code32
> SYM_FUNC_START(startup_32)
> /*
> @@ -65,10 +91,10 @@ SYM_FUNC_START(startup_32)
> leal (BP_scratch+4)(%esi), %esp
> call 1f
> 1: popl %ebp
> - subl $1b, %ebp
> + subl $ rva(1b), %ebp
>
> /* Load new GDT with the 64bit segments using 32bit descriptor */
> - leal gdt(%ebp), %eax
> + leal rva(gdt)(%ebp), %eax
> movl %eax, 2(%eax)
> lgdt (%eax)
>
> @@ -81,7 +107,7 @@ SYM_FUNC_START(startup_32)
> movl %eax, %ss
>
> /* setup a stack and make sure cpu supports long mode. */
> - leal boot_stack_end(%ebp), %esp
> + leal rva(boot_stack_end)(%ebp), %esp
>
> call verify_cpu
> testl %eax, %eax
> @@ -108,7 +134,7 @@ SYM_FUNC_START(startup_32)
> * image_offset = startup_32 - image_base
> * Otherwise image_offset will be zero and has no effect on the calculations.
> */
> - subl image_offset(%ebp), %ebx
> + subl rva(image_offset)(%ebp), %ebx
> #endif
>
> movl BP_kernel_alignment(%esi), %eax
> @@ -124,7 +150,7 @@ SYM_FUNC_START(startup_32)
>
> /* Target address to relocate to for decompression */
> addl BP_init_size(%esi), %ebx
> - subl $_end, %ebx
> + subl $ rva(_end), %ebx
>
> /*
> * Prepare for entering 64 bit mode
> @@ -152,19 +178,19 @@ SYM_FUNC_START(startup_32)
> 1:
>
> /* Initialize Page tables to 0 */
> - leal pgtable(%ebx), %edi
> + leal rva(pgtable)(%ebx), %edi
> xorl %eax, %eax
> movl $(BOOT_INIT_PGT_SIZE/4), %ecx
> rep stosl
>
> /* Build Level 4 */
> - leal pgtable + 0(%ebx), %edi
> + leal rva(pgtable + 0)(%ebx), %edi
> leal 0x1007 (%edi), %eax
> movl %eax, 0(%edi)
> addl %edx, 4(%edi)
>
> /* Build Level 3 */
> - leal pgtable + 0x1000(%ebx), %edi
> + leal rva(pgtable + 0x1000)(%ebx), %edi
> leal 0x1007(%edi), %eax
> movl $4, %ecx
> 1: movl %eax, 0x00(%edi)
> @@ -175,7 +201,7 @@ SYM_FUNC_START(startup_32)
> jnz 1b
>
> /* Build Level 2 */
> - leal pgtable + 0x2000(%ebx), %edi
> + leal rva(pgtable + 0x2000)(%ebx), %edi
> movl $0x00000183, %eax
> movl $2048, %ecx
> 1: movl %eax, 0(%edi)
> @@ -186,7 +212,7 @@ SYM_FUNC_START(startup_32)
> jnz 1b
>
> /* Enable the boot page tables */
> - leal pgtable(%ebx), %eax
> + leal rva(pgtable)(%ebx), %eax
> movl %eax, %cr3
>
> /* Enable Long mode in EFER (Extended Feature Enable Register) */
> @@ -211,14 +237,14 @@ SYM_FUNC_START(startup_32)
> * We place all of the values on our mini stack so lret can
> * used to perform that far jump.
> */
> - leal startup_64(%ebp), %eax
> + leal rva(startup_64)(%ebp), %eax
> #ifdef CONFIG_EFI_MIXED
> - movl efi32_boot_args(%ebp), %edi
> + movl rva(efi32_boot_args)(%ebp), %edi
> cmp $0, %edi
> jz 1f
> - leal efi64_stub_entry(%ebp), %eax
> - movl efi32_boot_args+4(%ebp), %esi
> - movl efi32_boot_args+8(%ebp), %edx // saved bootparams pointer
> + leal rva(efi64_stub_entry)(%ebp), %eax
> + movl rva(efi32_boot_args+4)(%ebp), %esi
> + movl rva(efi32_boot_args+8)(%ebp), %edx // saved bootparams pointer
> cmpl $0, %edx
> jnz 1f
> /*
> @@ -229,7 +255,7 @@ SYM_FUNC_START(startup_32)
> * the correct stack alignment for entry.
> */
> subl $40, %esp
> - leal efi_pe_entry(%ebp), %eax
> + leal rva(efi_pe_entry)(%ebp), %eax
> movl %edi, %ecx // MS calling convention
> movl %esi, %edx
> 1:
> @@ -255,18 +281,18 @@ SYM_FUNC_START(efi32_stub_entry)
>
> call 1f
> 1: pop %ebp
> - subl $1b, %ebp
> + subl $ rva(1b), %ebp
>
> - movl %esi, efi32_boot_args+8(%ebp)
> + movl %esi, rva(efi32_boot_args+8)(%ebp)
> SYM_INNER_LABEL(efi32_pe_stub_entry, SYM_L_LOCAL)
> - movl %ecx, efi32_boot_args(%ebp)
> - movl %edx, efi32_boot_args+4(%ebp)
> - movb $0, efi_is64(%ebp)
> + movl %ecx, rva(efi32_boot_args)(%ebp)
> + movl %edx, rva(efi32_boot_args+4)(%ebp)
> + movb $0, rva(efi_is64)(%ebp)
>
> /* Save firmware GDTR and code/data selectors */
> - sgdtl efi32_boot_gdt(%ebp)
> - movw %cs, efi32_boot_cs(%ebp)
> - movw %ds, efi32_boot_ds(%ebp)
> + sgdtl rva(efi32_boot_gdt)(%ebp)
> + movw %cs, rva(efi32_boot_cs)(%ebp)
> + movw %ds, rva(efi32_boot_ds)(%ebp)
>
> /* Disable paging */
> movl %cr0, %eax
> @@ -345,11 +371,11 @@ SYM_CODE_START(startup_64)
>
> /* Target address to relocate to for decompression */
> movl BP_init_size(%rsi), %ebx
> - subl $_end, %ebx
> + subl $ rva(_end), %ebx
> addq %rbp, %rbx
>
> /* Set up the stack */
> - leaq boot_stack_end(%rbx), %rsp
> + leaq rva(boot_stack_end)(%rbx), %rsp
>
> /*
> * At this point we are in long mode with 4-level paging enabled,
> @@ -423,7 +449,7 @@ SYM_CODE_START(startup_64)
> lretq
> trampoline_return:
> /* Restore the stack, the 32-bit trampoline uses its own stack */
> - leaq boot_stack_end(%rbx), %rsp
> + leaq rva(boot_stack_end)(%rbx), %rsp
>
> /*
> * cleanup_trampoline() would restore trampoline memory.
> @@ -435,7 +461,7 @@ trampoline_return:
> * this function call.
> */
> pushq %rsi
> - leaq top_pgtable(%rbx), %rdi
> + leaq rva(top_pgtable)(%rbx), %rdi
> call cleanup_trampoline
> popq %rsi
>
> @@ -449,9 +475,9 @@ trampoline_return:
> */
> pushq %rsi
> leaq (_bss-8)(%rip), %rsi
> - leaq (_bss-8)(%rbx), %rdi
> - movq $_bss /* - $startup_32 */, %rcx
> - shrq $3, %rcx
> + leaq rva(_bss-8)(%rbx), %rdi
> + movl $(_bss - startup_32), %ecx
> + shrl $3, %ecx
> std
> rep movsq
> cld
> @@ -462,15 +488,15 @@ trampoline_return:
> * during extract_kernel below. To avoid any issues, repoint the GDTR
> * to the new copy of the GDT.
> */
> - leaq gdt64(%rbx), %rax
> - leaq gdt(%rbx), %rdx
> + leaq rva(gdt64)(%rbx), %rax
> + leaq rva(gdt)(%rbx), %rdx
> movq %rdx, 2(%rax)
> lgdt (%rax)
>
> /*
> * Jump to the relocated address.
> */
> - leaq .Lrelocated(%rbx), %rax
> + leaq rva(.Lrelocated)(%rbx), %rax
> jmp *%rax
> SYM_CODE_END(startup_64)
>
> @@ -482,7 +508,7 @@ SYM_FUNC_START_ALIAS(efi_stub_entry)
> movq %rdx, %rbx /* save boot_params pointer */
> call efi_main
> movq %rbx,%rsi
> - leaq startup_64(%rax), %rax
> + leaq rva(startup_64)(%rax), %rax
> jmp *%rax
> SYM_FUNC_END(efi64_stub_entry)
> SYM_FUNC_END_ALIAS(efi_stub_entry)
> @@ -645,7 +671,7 @@ SYM_DATA(efi_is64, .byte 1)
> #define BS32_handle_protocol 88 // offsetof(efi_boot_services_32_t, handle_protocol)
> #define LI32_image_base 32 // offsetof(efi_loaded_image_32_t, image_base)
>
> - .text
> + __HEAD
> .code32
> SYM_FUNC_START(efi32_pe_entry)
> /*
> @@ -667,12 +693,12 @@ SYM_FUNC_START(efi32_pe_entry)
>
> call 1f
> 1: pop %ebx
> - subl $1b, %ebx
> + subl $ rva(1b), %ebx
>
> /* Get the loaded image protocol pointer from the image handle */
> leal -4(%ebp), %eax
> pushl %eax // &loaded_image
> - leal loaded_image_proto(%ebx), %eax
> + leal rva(loaded_image_proto)(%ebx), %eax
> pushl %eax // pass the GUID address
> pushl 8(%ebp) // pass the image handle
>
> @@ -707,7 +733,7 @@ SYM_FUNC_START(efi32_pe_entry)
> * use it before we get to the 64-bit efi_pe_entry() in C code.
> */
> subl %esi, %ebx
> - movl %ebx, image_offset(%ebp) // save image_offset
> + movl %ebx, rva(image_offset)(%ebp) // save image_offset
> jmp efi32_pe_stub_entry
>
> 2: popl %edi // restore callee-save registers
> --
> 2.26.2
>

2020-07-15 09:24:14

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 6/7] x86/boot: Remove run-time relocations from head_{32,64}.S

On Wed, Jul 15, 2020 at 2:41 AM Arvind Sankar <[email protected]> wrote:
>
> The BFD linker generates run-time relocations for z_input_len and
> z_output_len, even though they are absolute symbols.
>
> This is fixed for binutils-2.35 [1]. Work around this for earlier
> versions by defining two variables input_len and output_len in addition
> to the symbols, and use them via position-independent references.
>
> This eliminates the last two run-time relocations in the head code and
> allows us to drop the -z noreloc-overflow flag to the linker.
>
> Move the -pie and --no-dynamic-linker LDFLAGS to LDFLAGS_vmlinux instead
> of KBUILD_LDFLAGS. There shouldn't be anything else getting linked, but
> this is the more logical location for these flags, and modversions might
> call the linker if an EXPORT_SYMBOL is left over accidentally in one of
> the decompressors.
>

Tested-by: Sedat Dilek <[email protected]>
Reported-by: Sedat Dilek <[email protected]>

Reported breakage with LLD in previous patchset version.

- Sedat -

> [1] https://sourceware.org/bugzilla/show_bug.cgi?id=25754
>
> Reviewed-by: Kees Cook <[email protected]>
> Reviewed-by: Ard Biesheuvel <[email protected]>
> Reviewed-by: Fangrui Song <[email protected]>
> Signed-off-by: Arvind Sankar <[email protected]>
> ---
> arch/x86/boot/compressed/Makefile | 12 ++----------
> arch/x86/boot/compressed/head_32.S | 17 ++++++++---------
> arch/x86/boot/compressed/head_64.S | 4 ++--
> arch/x86/boot/compressed/mkpiggy.c | 6 ++++++
> 4 files changed, 18 insertions(+), 21 deletions(-)
>
> diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
> index c829d874dcac..ae2c0dc98a6a 100644
> --- a/arch/x86/boot/compressed/Makefile
> +++ b/arch/x86/boot/compressed/Makefile
> @@ -51,16 +51,8 @@ UBSAN_SANITIZE :=n
> KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> # Compressed kernel should be built as PIE since it may be loaded at any
> # address by the bootloader.
> -ifeq ($(CONFIG_X86_32),y)
> -KBUILD_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.
> -KBUILD_LDFLAGS += $(shell $(LD) --help 2>&1 | grep -q "\-z noreloc-overflow" \
> - && echo "-z noreloc-overflow -pie --no-dynamic-linker")
> -endif
> -LDFLAGS_vmlinux := -T
> +LDFLAGS_vmlinux := $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
> +LDFLAGS_vmlinux += -T
>
> hostprogs := mkpiggy
> HOST_EXTRACFLAGS += -I$(srctree)/tools/include
> diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
> index 8c1a4f5610f5..659fad53ca82 100644
> --- a/arch/x86/boot/compressed/head_32.S
> +++ b/arch/x86/boot/compressed/head_32.S
> @@ -178,18 +178,17 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
> /*
> * Do the extraction, and jump to the new kernel..
> */
> - /* push arguments for extract_kernel: */
> - pushl $z_output_len /* decompressed length, end of relocs */
> + /* push arguments for extract_kernel: */
>
> - pushl %ebp /* output address */
> -
> - pushl $z_input_len /* input_len */
> + pushl output_len@GOTOFF(%ebx) /* decompressed length, end of relocs */
> + pushl %ebp /* output address */
> + pushl input_len@GOTOFF(%ebx) /* input_len */
> leal input_data@GOTOFF(%ebx), %eax
> - pushl %eax /* input_data */
> + pushl %eax /* input_data */
> leal boot_heap@GOTOFF(%ebx), %eax
> - pushl %eax /* heap area */
> - pushl %esi /* real mode pointer */
> - call extract_kernel /* returns kernel location in %eax */
> + pushl %eax /* heap area */
> + pushl %esi /* real mode pointer */
> + call extract_kernel /* returns kernel location in %eax */
> addl $24, %esp
>
> /*
> diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
> index 11429092c224..9e46729cf162 100644
> --- a/arch/x86/boot/compressed/head_64.S
> +++ b/arch/x86/boot/compressed/head_64.S
> @@ -534,9 +534,9 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
> movq %rsi, %rdi /* real mode address */
> leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
> leaq input_data(%rip), %rdx /* input_data */
> - movl $z_input_len, %ecx /* input_len */
> + movl input_len(%rip), %ecx /* input_len */
> movq %rbp, %r8 /* output target address */
> - movl $z_output_len, %r9d /* decompressed length, end of relocs */
> + movl output_len(%rip), %r9d /* decompressed length, end of relocs */
> call extract_kernel /* returns kernel location in %rax */
> popq %rsi
>
> diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c
> index 7e01248765b2..52aa56cdbacc 100644
> --- a/arch/x86/boot/compressed/mkpiggy.c
> +++ b/arch/x86/boot/compressed/mkpiggy.c
> @@ -60,6 +60,12 @@ int main(int argc, char *argv[])
> printf(".incbin \"%s\"\n", argv[1]);
> printf("input_data_end:\n");
>
> + printf(".section \".rodata\",\"a\",@progbits\n");
> + printf(".globl input_len\n");
> + printf("input_len:\n\t.long %lu\n", ilen);
> + printf(".globl output_len\n");
> + printf("output_len:\n\t.long %lu\n", (unsigned long)olen);
> +
> retval = 0;
> bail:
> if (f)
> --
> 2.26.2
>

2020-07-15 09:25:12

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 7/7] x86/boot: Check that there are no run-time relocations

On Wed, Jul 15, 2020 at 2:41 AM Arvind Sankar <[email protected]> wrote:
>
> Add a linker script check that there are no run-time relocations, and
> remove the old one that tries to check via looking for specially-named
> sections in the object files.
>
> Drop the tests for -fPIE compiler option and -pie linker option, as they
> are available in all supported gcc and binutils versions (as well as
> clang and lld).
>

Tested-by: Sedat Dilek <[email protected]>
Reported-by: Sedat Dilek <[email protected]>

Here I reported the breakage with LLD and tested the move to
LDFLAGS_vmlinux with a previous version of the patch.

- Sedat -

> Reviewed-by: Kees Cook <[email protected]>
> Reviewed-by: Ard Biesheuvel <[email protected]>
> Reviewed-by: Fangrui Song <[email protected]>
> Signed-off-by: Arvind Sankar <[email protected]>
> ---
> arch/x86/boot/compressed/Makefile | 28 +++-----------------------
> arch/x86/boot/compressed/vmlinux.lds.S | 8 ++++++++
> 2 files changed, 11 insertions(+), 25 deletions(-)
>
> diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
> index ae2c0dc98a6a..a9e082b8c720 100644
> --- a/arch/x86/boot/compressed/Makefile
> +++ b/arch/x86/boot/compressed/Makefile
> @@ -29,7 +29,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
> vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4
>
> KBUILD_CFLAGS := -m$(BITS) -O2
> -KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC)
> +KBUILD_CFLAGS += -fno-strict-aliasing -fPIE
> KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
> cflags-$(CONFIG_X86_32) := -march=i386
> cflags-$(CONFIG_X86_64) := -mcmodel=small
> @@ -51,7 +51,7 @@ UBSAN_SANITIZE :=n
> KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> # Compressed kernel should be built as PIE since it may be loaded at any
> # address by the bootloader.
> -LDFLAGS_vmlinux := $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
> +LDFLAGS_vmlinux := -pie $(call ld-option, --no-dynamic-linker)
> LDFLAGS_vmlinux += -T
>
> hostprogs := mkpiggy
> @@ -86,30 +86,8 @@ vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
> vmlinux-objs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
> vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o
>
> -# The compressed kernel is built with -fPIC/-fPIE so that a boot loader
> -# can place it anywhere in memory and it will still run. However, since
> -# it is executed as-is without any ELF relocation processing performed
> -# (and has already had all relocation sections stripped from the binary),
> -# none of the code can use data relocations (e.g. static assignments of
> -# pointer values), since they will be meaningless at runtime. This check
> -# will refuse to link the vmlinux if any of these relocations are found.
> -quiet_cmd_check_data_rel = DATAREL $@
> -define cmd_check_data_rel
> - for obj in $(filter %.o,$^); do \
> - $(READELF) -S $$obj | grep -qF .rel.local && { \
> - echo "error: $$obj has data relocations!" >&2; \
> - exit 1; \
> - } || true; \
> - done
> -endef
> -
> -# We need to run two commands under "if_changed", so merge them into a
> -# single invocation.
> -quiet_cmd_check-and-link-vmlinux = LD $@
> - cmd_check-and-link-vmlinux = $(cmd_check_data_rel); $(cmd_ld)
> -
> $(obj)/vmlinux: $(vmlinux-objs-y) FORCE
> - $(call if_changed,check-and-link-vmlinux)
> + $(call if_changed,ld)
>
> OBJCOPYFLAGS_vmlinux.bin := -R .comment -S
> $(obj)/vmlinux.bin: vmlinux FORCE
> diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
> index a4a4a59a2628..29df99b6cc64 100644
> --- a/arch/x86/boot/compressed/vmlinux.lds.S
> +++ b/arch/x86/boot/compressed/vmlinux.lds.S
> @@ -42,6 +42,12 @@ SECTIONS
> *(.rodata.*)
> _erodata = . ;
> }
> + .rel.dyn : {
> + *(.rel.*)
> + }
> + .rela.dyn : {
> + *(.rela.*)
> + }
> .got : {
> *(.got)
> }
> @@ -85,3 +91,5 @@ ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT en
> #else
> ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0xc, "Unexpected GOT/PLT entries detected!")
> #endif
> +
> +ASSERT(SIZEOF(.rel.dyn) == 0 && SIZEOF(.rela.dyn) == 0, "Unexpected run-time relocations detected!")
> --
> 2.26.2
>

2020-07-15 09:25:59

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: [PATCH v5 6/7] x86/boot: Remove run-time relocations from head_{32,64}.S

On Wed, 15 Jul 2020 at 11:58, Sedat Dilek <[email protected]> wrote:
>
> On Wed, Jul 15, 2020 at 2:41 AM Arvind Sankar <[email protected]> wrote:
> >
> > The BFD linker generates run-time relocations for z_input_len and
> > z_output_len, even though they are absolute symbols.
> >
> > This is fixed for binutils-2.35 [1]. Work around this for earlier
> > versions by defining two variables input_len and output_len in addition
> > to the symbols, and use them via position-independent references.
> >
> > This eliminates the last two run-time relocations in the head code and
> > allows us to drop the -z noreloc-overflow flag to the linker.
> >
> > Move the -pie and --no-dynamic-linker LDFLAGS to LDFLAGS_vmlinux instead
> > of KBUILD_LDFLAGS. There shouldn't be anything else getting linked, but
> > this is the more logical location for these flags, and modversions might
> > call the linker if an EXPORT_SYMBOL is left over accidentally in one of
> > the decompressors.
> >
>
> Tested-by: Sedat Dilek <[email protected]>
> Reported-by: Sedat Dilek <[email protected]>
>
> Reported breakage with LLD in previous patchset version.
>

Please drop the bogus reported-bys. The patch does *not* address an
issue you reported, so recording this in the commit log would be
incorrect.

Your review and testing is appreciated, and resulted in substantial
improvements. So feel free to give your reviewed-by in addition to
your tested-by. But reported-by is inappropriate here.


>
> > [1] https://sourceware.org/bugzilla/show_bug.cgi?id=25754
> >
> > Reviewed-by: Kees Cook <[email protected]>
> > Reviewed-by: Ard Biesheuvel <[email protected]>
> > Reviewed-by: Fangrui Song <[email protected]>
> > Signed-off-by: Arvind Sankar <[email protected]>
> > ---
> > arch/x86/boot/compressed/Makefile | 12 ++----------
> > arch/x86/boot/compressed/head_32.S | 17 ++++++++---------
> > arch/x86/boot/compressed/head_64.S | 4 ++--
> > arch/x86/boot/compressed/mkpiggy.c | 6 ++++++
> > 4 files changed, 18 insertions(+), 21 deletions(-)
> >
> > diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
> > index c829d874dcac..ae2c0dc98a6a 100644
> > --- a/arch/x86/boot/compressed/Makefile
> > +++ b/arch/x86/boot/compressed/Makefile
> > @@ -51,16 +51,8 @@ UBSAN_SANITIZE :=n
> > KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> > # Compressed kernel should be built as PIE since it may be loaded at any
> > # address by the bootloader.
> > -ifeq ($(CONFIG_X86_32),y)
> > -KBUILD_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.
> > -KBUILD_LDFLAGS += $(shell $(LD) --help 2>&1 | grep -q "\-z noreloc-overflow" \
> > - && echo "-z noreloc-overflow -pie --no-dynamic-linker")
> > -endif
> > -LDFLAGS_vmlinux := -T
> > +LDFLAGS_vmlinux := $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
> > +LDFLAGS_vmlinux += -T
> >
> > hostprogs := mkpiggy
> > HOST_EXTRACFLAGS += -I$(srctree)/tools/include
> > diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
> > index 8c1a4f5610f5..659fad53ca82 100644
> > --- a/arch/x86/boot/compressed/head_32.S
> > +++ b/arch/x86/boot/compressed/head_32.S
> > @@ -178,18 +178,17 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
> > /*
> > * Do the extraction, and jump to the new kernel..
> > */
> > - /* push arguments for extract_kernel: */
> > - pushl $z_output_len /* decompressed length, end of relocs */
> > + /* push arguments for extract_kernel: */
> >
> > - pushl %ebp /* output address */
> > -
> > - pushl $z_input_len /* input_len */
> > + pushl output_len@GOTOFF(%ebx) /* decompressed length, end of relocs */
> > + pushl %ebp /* output address */
> > + pushl input_len@GOTOFF(%ebx) /* input_len */
> > leal input_data@GOTOFF(%ebx), %eax
> > - pushl %eax /* input_data */
> > + pushl %eax /* input_data */
> > leal boot_heap@GOTOFF(%ebx), %eax
> > - pushl %eax /* heap area */
> > - pushl %esi /* real mode pointer */
> > - call extract_kernel /* returns kernel location in %eax */
> > + pushl %eax /* heap area */
> > + pushl %esi /* real mode pointer */
> > + call extract_kernel /* returns kernel location in %eax */
> > addl $24, %esp
> >
> > /*
> > diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
> > index 11429092c224..9e46729cf162 100644
> > --- a/arch/x86/boot/compressed/head_64.S
> > +++ b/arch/x86/boot/compressed/head_64.S
> > @@ -534,9 +534,9 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
> > movq %rsi, %rdi /* real mode address */
> > leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
> > leaq input_data(%rip), %rdx /* input_data */
> > - movl $z_input_len, %ecx /* input_len */
> > + movl input_len(%rip), %ecx /* input_len */
> > movq %rbp, %r8 /* output target address */
> > - movl $z_output_len, %r9d /* decompressed length, end of relocs */
> > + movl output_len(%rip), %r9d /* decompressed length, end of relocs */
> > call extract_kernel /* returns kernel location in %rax */
> > popq %rsi
> >
> > diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c
> > index 7e01248765b2..52aa56cdbacc 100644
> > --- a/arch/x86/boot/compressed/mkpiggy.c
> > +++ b/arch/x86/boot/compressed/mkpiggy.c
> > @@ -60,6 +60,12 @@ int main(int argc, char *argv[])
> > printf(".incbin \"%s\"\n", argv[1]);
> > printf("input_data_end:\n");
> >
> > + printf(".section \".rodata\",\"a\",@progbits\n");
> > + printf(".globl input_len\n");
> > + printf("input_len:\n\t.long %lu\n", ilen);
> > + printf(".globl output_len\n");
> > + printf("output_len:\n\t.long %lu\n", (unsigned long)olen);
> > +
> > retval = 0;
> > bail:
> > if (f)
> > --
> > 2.26.2
> >

2020-07-15 09:32:38

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 6/7] x86/boot: Remove run-time relocations from head_{32,64}.S

On Wed, Jul 15, 2020 at 11:03 AM Ard Biesheuvel <[email protected]> wrote:
>
> On Wed, 15 Jul 2020 at 11:58, Sedat Dilek <[email protected]> wrote:
> >
> > On Wed, Jul 15, 2020 at 2:41 AM Arvind Sankar <[email protected]> wrote:
> > >
> > > The BFD linker generates run-time relocations for z_input_len and
> > > z_output_len, even though they are absolute symbols.
> > >
> > > This is fixed for binutils-2.35 [1]. Work around this for earlier
> > > versions by defining two variables input_len and output_len in addition
> > > to the symbols, and use them via position-independent references.
> > >
> > > This eliminates the last two run-time relocations in the head code and
> > > allows us to drop the -z noreloc-overflow flag to the linker.
> > >
> > > Move the -pie and --no-dynamic-linker LDFLAGS to LDFLAGS_vmlinux instead
> > > of KBUILD_LDFLAGS. There shouldn't be anything else getting linked, but
> > > this is the more logical location for these flags, and modversions might
> > > call the linker if an EXPORT_SYMBOL is left over accidentally in one of
> > > the decompressors.
> > >
> >
> > Tested-by: Sedat Dilek <[email protected]>
> > Reported-by: Sedat Dilek <[email protected]>
> >
> > Reported breakage with LLD in previous patchset version.
> >
>
> Please drop the bogus reported-bys. The patch does *not* address an
> issue you reported, so recording this in the commit log would be
> incorrect.
>
> Your review and testing is appreciated, and resulted in substantial
> improvements. So feel free to give your reviewed-by in addition to
> your tested-by. But reported-by is inappropriate here.
>

Correct, in a hurry I mixed it up as said in the next patch 7/7.
Please drop this Reported-by.

- Sedat -

>
> >
> > > [1] https://sourceware.org/bugzilla/show_bug.cgi?id=25754
> > >
> > > Reviewed-by: Kees Cook <[email protected]>
> > > Reviewed-by: Ard Biesheuvel <[email protected]>
> > > Reviewed-by: Fangrui Song <[email protected]>
> > > Signed-off-by: Arvind Sankar <[email protected]>
> > > ---
> > > arch/x86/boot/compressed/Makefile | 12 ++----------
> > > arch/x86/boot/compressed/head_32.S | 17 ++++++++---------
> > > arch/x86/boot/compressed/head_64.S | 4 ++--
> > > arch/x86/boot/compressed/mkpiggy.c | 6 ++++++
> > > 4 files changed, 18 insertions(+), 21 deletions(-)
> > >
> > > diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
> > > index c829d874dcac..ae2c0dc98a6a 100644
> > > --- a/arch/x86/boot/compressed/Makefile
> > > +++ b/arch/x86/boot/compressed/Makefile
> > > @@ -51,16 +51,8 @@ UBSAN_SANITIZE :=n
> > > KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> > > # Compressed kernel should be built as PIE since it may be loaded at any
> > > # address by the bootloader.
> > > -ifeq ($(CONFIG_X86_32),y)
> > > -KBUILD_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.
> > > -KBUILD_LDFLAGS += $(shell $(LD) --help 2>&1 | grep -q "\-z noreloc-overflow" \
> > > - && echo "-z noreloc-overflow -pie --no-dynamic-linker")
> > > -endif
> > > -LDFLAGS_vmlinux := -T
> > > +LDFLAGS_vmlinux := $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
> > > +LDFLAGS_vmlinux += -T
> > >
> > > hostprogs := mkpiggy
> > > HOST_EXTRACFLAGS += -I$(srctree)/tools/include
> > > diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
> > > index 8c1a4f5610f5..659fad53ca82 100644
> > > --- a/arch/x86/boot/compressed/head_32.S
> > > +++ b/arch/x86/boot/compressed/head_32.S
> > > @@ -178,18 +178,17 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
> > > /*
> > > * Do the extraction, and jump to the new kernel..
> > > */
> > > - /* push arguments for extract_kernel: */
> > > - pushl $z_output_len /* decompressed length, end of relocs */
> > > + /* push arguments for extract_kernel: */
> > >
> > > - pushl %ebp /* output address */
> > > -
> > > - pushl $z_input_len /* input_len */
> > > + pushl output_len@GOTOFF(%ebx) /* decompressed length, end of relocs */
> > > + pushl %ebp /* output address */
> > > + pushl input_len@GOTOFF(%ebx) /* input_len */
> > > leal input_data@GOTOFF(%ebx), %eax
> > > - pushl %eax /* input_data */
> > > + pushl %eax /* input_data */
> > > leal boot_heap@GOTOFF(%ebx), %eax
> > > - pushl %eax /* heap area */
> > > - pushl %esi /* real mode pointer */
> > > - call extract_kernel /* returns kernel location in %eax */
> > > + pushl %eax /* heap area */
> > > + pushl %esi /* real mode pointer */
> > > + call extract_kernel /* returns kernel location in %eax */
> > > addl $24, %esp
> > >
> > > /*
> > > diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
> > > index 11429092c224..9e46729cf162 100644
> > > --- a/arch/x86/boot/compressed/head_64.S
> > > +++ b/arch/x86/boot/compressed/head_64.S
> > > @@ -534,9 +534,9 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
> > > movq %rsi, %rdi /* real mode address */
> > > leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
> > > leaq input_data(%rip), %rdx /* input_data */
> > > - movl $z_input_len, %ecx /* input_len */
> > > + movl input_len(%rip), %ecx /* input_len */
> > > movq %rbp, %r8 /* output target address */
> > > - movl $z_output_len, %r9d /* decompressed length, end of relocs */
> > > + movl output_len(%rip), %r9d /* decompressed length, end of relocs */
> > > call extract_kernel /* returns kernel location in %rax */
> > > popq %rsi
> > >
> > > diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c
> > > index 7e01248765b2..52aa56cdbacc 100644
> > > --- a/arch/x86/boot/compressed/mkpiggy.c
> > > +++ b/arch/x86/boot/compressed/mkpiggy.c
> > > @@ -60,6 +60,12 @@ int main(int argc, char *argv[])
> > > printf(".incbin \"%s\"\n", argv[1]);
> > > printf("input_data_end:\n");
> > >
> > > + printf(".section \".rodata\",\"a\",@progbits\n");
> > > + printf(".globl input_len\n");
> > > + printf("input_len:\n\t.long %lu\n", ilen);
> > > + printf(".globl output_len\n");
> > > + printf("output_len:\n\t.long %lu\n", (unsigned long)olen);
> > > +
> > > retval = 0;
> > > bail:
> > > if (f)
> > > --
> > > 2.26.2
> > >

2020-07-15 09:32:52

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 7/7] x86/boot: Check that there are no run-time relocations

On Wed, Jul 15, 2020 at 11:00 AM Sedat Dilek <[email protected]> wrote:
>
> On Wed, Jul 15, 2020 at 2:41 AM Arvind Sankar <[email protected]> wrote:
> >
> > Add a linker script check that there are no run-time relocations, and
> > remove the old one that tries to check via looking for specially-named
> > sections in the object files.
> >
> > Drop the tests for -fPIE compiler option and -pie linker option, as they
> > are available in all supported gcc and binutils versions (as well as
> > clang and lld).
> >
>
> Tested-by: Sedat Dilek <[email protected]>
> Reported-by: Sedat Dilek <[email protected]>
>
> Here I reported the breakage with LLD and tested the move to
> LDFLAGS_vmlinux with a previous version of the patch.
>

Feel free to also add my:

Reviewed-by: Sedat Dilek <[email protected]>

- Sedat -

>
> > Reviewed-by: Kees Cook <[email protected]>
> > Reviewed-by: Ard Biesheuvel <[email protected]>
> > Reviewed-by: Fangrui Song <[email protected]>
> > Signed-off-by: Arvind Sankar <[email protected]>
> > ---
> > arch/x86/boot/compressed/Makefile | 28 +++-----------------------
> > arch/x86/boot/compressed/vmlinux.lds.S | 8 ++++++++
> > 2 files changed, 11 insertions(+), 25 deletions(-)
> >
> > diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
> > index ae2c0dc98a6a..a9e082b8c720 100644
> > --- a/arch/x86/boot/compressed/Makefile
> > +++ b/arch/x86/boot/compressed/Makefile
> > @@ -29,7 +29,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
> > vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4
> >
> > KBUILD_CFLAGS := -m$(BITS) -O2
> > -KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC)
> > +KBUILD_CFLAGS += -fno-strict-aliasing -fPIE
> > KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
> > cflags-$(CONFIG_X86_32) := -march=i386
> > cflags-$(CONFIG_X86_64) := -mcmodel=small
> > @@ -51,7 +51,7 @@ UBSAN_SANITIZE :=n
> > KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
> > # Compressed kernel should be built as PIE since it may be loaded at any
> > # address by the bootloader.
> > -LDFLAGS_vmlinux := $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
> > +LDFLAGS_vmlinux := -pie $(call ld-option, --no-dynamic-linker)
> > LDFLAGS_vmlinux += -T
> >
> > hostprogs := mkpiggy
> > @@ -86,30 +86,8 @@ vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
> > vmlinux-objs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
> > vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o
> >
> > -# The compressed kernel is built with -fPIC/-fPIE so that a boot loader
> > -# can place it anywhere in memory and it will still run. However, since
> > -# it is executed as-is without any ELF relocation processing performed
> > -# (and has already had all relocation sections stripped from the binary),
> > -# none of the code can use data relocations (e.g. static assignments of
> > -# pointer values), since they will be meaningless at runtime. This check
> > -# will refuse to link the vmlinux if any of these relocations are found.
> > -quiet_cmd_check_data_rel = DATAREL $@
> > -define cmd_check_data_rel
> > - for obj in $(filter %.o,$^); do \
> > - $(READELF) -S $$obj | grep -qF .rel.local && { \
> > - echo "error: $$obj has data relocations!" >&2; \
> > - exit 1; \
> > - } || true; \
> > - done
> > -endef
> > -
> > -# We need to run two commands under "if_changed", so merge them into a
> > -# single invocation.
> > -quiet_cmd_check-and-link-vmlinux = LD $@
> > - cmd_check-and-link-vmlinux = $(cmd_check_data_rel); $(cmd_ld)
> > -
> > $(obj)/vmlinux: $(vmlinux-objs-y) FORCE
> > - $(call if_changed,check-and-link-vmlinux)
> > + $(call if_changed,ld)
> >
> > OBJCOPYFLAGS_vmlinux.bin := -R .comment -S
> > $(obj)/vmlinux.bin: vmlinux FORCE
> > diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
> > index a4a4a59a2628..29df99b6cc64 100644
> > --- a/arch/x86/boot/compressed/vmlinux.lds.S
> > +++ b/arch/x86/boot/compressed/vmlinux.lds.S
> > @@ -42,6 +42,12 @@ SECTIONS
> > *(.rodata.*)
> > _erodata = . ;
> > }
> > + .rel.dyn : {
> > + *(.rel.*)
> > + }
> > + .rela.dyn : {
> > + *(.rela.*)
> > + }
> > .got : {
> > *(.got)
> > }
> > @@ -85,3 +91,5 @@ ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT en
> > #else
> > ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0xc, "Unexpected GOT/PLT entries detected!")
> > #endif
> > +
> > +ASSERT(SIZEOF(.rel.dyn) == 0 && SIZEOF(.rela.dyn) == 0, "Unexpected run-time relocations detected!")
> > --
> > 2.26.2
> >

2020-07-17 13:47:45

by Arvind Sankar

[permalink] [raw]
Subject: Re: [PATCH v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Tue, Jul 14, 2020 at 08:41:26PM -0400, Arvind Sankar wrote:
> The compressed kernel currently contains bogus run-time relocations in
> the startup code in head_{32,64}.S, which are generated by the linker,
> but must not actually be processed at run-time.
>
> This generates warnings when linking with the BFD linker, and errors
> with LLD, which defaults to erroring on run-time relocations in read-only
> sections. It also requires the -z noreloc-overflow hack for the 64-bit
> kernel, which prevents us from linking it as -pie on an older BFD linker
> (<= 2.26) or on LLD, because the locations that are to be apparently
> relocated are only 32-bits in size and so cannot really have
> R_X86_64_RELATIVE relocations.
>
> This series aims to get rid of these relocations. I've build- and
> boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
> gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
> has other issues [0].
>

Hi Thomas, Ingo, Borislav, would you be able to take a look over this
series in time for 5.9?

Thanks.

2020-07-17 18:19:46

by Nick Desaulniers

[permalink] [raw]
Subject: Re: [PATCH v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Fri, Jul 17, 2020 at 6:46 AM Arvind Sankar <[email protected]> wrote:
>
> On Tue, Jul 14, 2020 at 08:41:26PM -0400, Arvind Sankar wrote:
> > The compressed kernel currently contains bogus run-time relocations in
> > the startup code in head_{32,64}.S, which are generated by the linker,
> > but must not actually be processed at run-time.
> >
> > This generates warnings when linking with the BFD linker, and errors
> > with LLD, which defaults to erroring on run-time relocations in read-only
> > sections. It also requires the -z noreloc-overflow hack for the 64-bit
> > kernel, which prevents us from linking it as -pie on an older BFD linker
> > (<= 2.26) or on LLD, because the locations that are to be apparently
> > relocated are only 32-bits in size and so cannot really have
> > R_X86_64_RELATIVE relocations.
> >
> > This series aims to get rid of these relocations. I've build- and
> > boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
> > gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
> > has other issues [0].
> >
>
> Hi Thomas, Ingo, Borislav, would you be able to take a look over this
> series in time for 5.9?

Hi Arvind, thanks for the series; I'm behind on testing. When I try
to apply this series on top of linux-next, I get a collision in
drivers/firmware/efi/libstub/Makefile:27 when applying "0002
x86/boot/compressed: Force hidden visibility for all symbol
references". Would you mind refreshing the series to avoid that
collision?
--
Thanks,
~Nick Desaulniers

2020-07-17 18:22:38

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Fri, Jul 17, 2020 at 8:17 PM Nick Desaulniers
<[email protected]> wrote:
>
> On Fri, Jul 17, 2020 at 6:46 AM Arvind Sankar <[email protected]> wrote:
> >
> > On Tue, Jul 14, 2020 at 08:41:26PM -0400, Arvind Sankar wrote:
> > > The compressed kernel currently contains bogus run-time relocations in
> > > the startup code in head_{32,64}.S, which are generated by the linker,
> > > but must not actually be processed at run-time.
> > >
> > > This generates warnings when linking with the BFD linker, and errors
> > > with LLD, which defaults to erroring on run-time relocations in read-only
> > > sections. It also requires the -z noreloc-overflow hack for the 64-bit
> > > kernel, which prevents us from linking it as -pie on an older BFD linker
> > > (<= 2.26) or on LLD, because the locations that are to be apparently
> > > relocated are only 32-bits in size and so cannot really have
> > > R_X86_64_RELATIVE relocations.
> > >
> > > This series aims to get rid of these relocations. I've build- and
> > > boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
> > > gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
> > > has other issues [0].
> > >
> >
> > Hi Thomas, Ingo, Borislav, would you be able to take a look over this
> > series in time for 5.9?
>
> Hi Arvind, thanks for the series; I'm behind on testing. When I try
> to apply this series on top of linux-next, I get a collision in
> drivers/firmware/efi/libstub/Makefile:27 when applying "0002
> x86/boot/compressed: Force hidden visibility for all symbol
> references". Would you mind refreshing the series to avoid that
> collision?
>

I guess taking a Linux-next release as a new base is not a good idea.
With the next Linux-next release... new troubles.
Please, keep the base on recent Linux v5.8-rcX.

- Sedat -

2020-07-17 20:18:42

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH-next v5 1/7] x86/boot/compressed: Move .got.plt entries out of the .got section

From: Ard Biesheuvel <[email protected]>

The .got.plt section contains the part of the GOT which is used by PLT
entries, and which gets updated lazily by the dynamic loader when
function calls are dispatched through those PLT entries.

On fully linked binaries such as the kernel proper or the decompressor,
this never happens, and so in practice, the .got.plt section consists
only of the first 3 magic entries that are meant to point at the _DYNAMIC
section and at the fixup routine in the loader. However, since we don't
use a dynamic loader, those entries are never populated or used.

This means that treating those entries like ordinary GOT entries, and
updating their values based on the actual placement of the executable in
memory is completely pointless, and we can just ignore the .got.plt
section entirely, provided that it has no additional entries beyond
the first 3 ones.

So add an assertion in the linker script to ensure that this assumption
holds, and move the contents out of the [_got, _egot) memory range that
is modified by the GOT fixup routines.

While at it, drop the KEEP(), since it has no effect on the contents
of output sections that are created by the linker itself.

Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/vmlinux.lds.S | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index 8f1025d1f681..b17d218ccdf9 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -44,10 +44,13 @@ SECTIONS
}
.got : {
_got = .;
- KEEP(*(.got.plt))
KEEP(*(.got))
_egot = .;
}
+ .got.plt : {
+ *(.got.plt)
+ }
+
.data : {
_data = . ;
*(.data)
@@ -77,3 +80,9 @@ SECTIONS

DISCARDS
}
+
+#ifdef CONFIG_X86_64
+ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT entries detected!")
+#else
+ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0xc, "Unexpected GOT/PLT entries detected!")
+#endif
--
2.26.2

2020-07-17 20:18:45

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH-next v5 2/7] x86/boot/compressed: Force hidden visibility for all symbol references

From: Ard Biesheuvel <[email protected]>

Eliminate all GOT entries in the decompressor binary, by forcing hidden
visibility for all symbol references, which informs the compiler that
such references will be resolved at link time without the need for
allocating GOT entries.

To ensure that no GOT entries will creep back in, add an assertion to
the decompressor linker script that will fire if the .got section has
a non-zero size.

[Arvind: move hidden.h to include/linux instead of making a copy]

Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/Makefile | 1 +
arch/x86/boot/compressed/vmlinux.lds.S | 1 +
drivers/firmware/efi/libstub/Makefile | 2 +-
drivers/firmware/efi/libstub/hidden.h | 6 ------
include/linux/hidden.h | 19 +++++++++++++++++++
5 files changed, 22 insertions(+), 7 deletions(-)
delete mode 100644 drivers/firmware/efi/libstub/hidden.h
create mode 100644 include/linux/hidden.h

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index b7beabecef8a..b6d7caaaef9e 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -42,6 +42,7 @@ KBUILD_CFLAGS += $(call cc-disable-warning, gnu)
KBUILD_CFLAGS += -Wno-pointer-sign
KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=)
KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
+KBUILD_CFLAGS += -include $(srctree)/include/linux/hidden.h

KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
GCOV_PROFILE := n
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index b17d218ccdf9..4bcc943842ab 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -81,6 +81,7 @@ SECTIONS
DISCARDS
}

+ASSERT(SIZEOF(.got) == 0, "Unexpected GOT entries detected!")
#ifdef CONFIG_X86_64
ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT entries detected!")
#else
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 2a156f7fec3b..8b350e5a65bc 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -27,7 +27,7 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt

KBUILD_CFLAGS := $(cflags-y) -Os -DDISABLE_BRANCH_PROFILING \
- -include $(srctree)/drivers/firmware/efi/libstub/hidden.h \
+ -include $(srctree)/include/linux/hidden.h \
-D__NO_FORTIFY \
-ffreestanding \
-fno-stack-protector \
diff --git a/drivers/firmware/efi/libstub/hidden.h b/drivers/firmware/efi/libstub/hidden.h
deleted file mode 100644
index 3493b041f419..000000000000
--- a/drivers/firmware/efi/libstub/hidden.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * To prevent the compiler from emitting GOT-indirected (and thus absolute)
- * references to any global symbols, override their visibility as 'hidden'
- */
-#pragma GCC visibility push(hidden)
diff --git a/include/linux/hidden.h b/include/linux/hidden.h
new file mode 100644
index 000000000000..49a17b6b5962
--- /dev/null
+++ b/include/linux/hidden.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * When building position independent code with GCC using the -fPIC option,
+ * (or even the -fPIE one on older versions), it will assume that we are
+ * building a dynamic object (either a shared library or an executable) that
+ * may have symbol references that can only be resolved at load time. For a
+ * variety of reasons (ELF symbol preemption, the CoW footprint of the section
+ * that is modified by the loader), this results in all references to symbols
+ * with external linkage to go via entries in the Global Offset Table (GOT),
+ * which carries absolute addresses which need to be fixed up when the
+ * executable image is loaded at an offset which is different from its link
+ * time offset.
+ *
+ * Fortunately, there is a way to inform the compiler that such symbol
+ * references will be satisfied at link time rather than at load time, by
+ * giving them 'hidden' visibility.
+ */
+
+#pragma GCC visibility push(hidden)
--
2.26.2

2020-07-17 20:18:53

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH-next v5 3/7] x86/boot/compressed: Get rid of GOT fixup code

From: Ard Biesheuvel <[email protected]>

In a previous patch, we have eliminated GOT entries from the decompressor
binary and added an assertion that the .got section is empty. This means
that the GOT fixup routines that exist in both the 32-bit and 64-bit
startup routines have become dead code, and can be removed.

While at it, drop the KEEP() from the linker script, as it has no effect
on the contents of output sections that are created by the linker itself.

Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/head_32.S | 24 ++---------
arch/x86/boot/compressed/head_64.S | 57 --------------------------
arch/x86/boot/compressed/vmlinux.lds.S | 4 +-
3 files changed, 5 insertions(+), 80 deletions(-)

diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 03557f2174bf..39f0bb43218f 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -49,16 +49,13 @@
* 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, _egot and _end, 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, _egot and _end as
- * hidden:
+ * _bss, _ebss and _end, 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 and _end as hidden:
*/
.hidden _bss
.hidden _ebss
- .hidden _got
- .hidden _egot
.hidden _end

__HEAD
@@ -192,19 +189,6 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
shrl $2, %ecx
rep stosl

-/*
- * Adjust our own GOT
- */
- leal _got(%ebx), %edx
- leal _egot(%ebx), %ecx
-1:
- cmpl %ecx, %edx
- jae 2f
- addl %ebx, (%edx)
- addl $4, %edx
- jmp 1b
-2:
-
/*
* Do the extraction, and jump to the new kernel..
*/
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 97d37f0a34f5..bf1ab30acc5b 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -40,8 +40,6 @@
*/
.hidden _bss
.hidden _ebss
- .hidden _got
- .hidden _egot
.hidden _end

__HEAD
@@ -353,25 +351,6 @@ SYM_CODE_START(startup_64)
/* Set up the stack */
leaq boot_stack_end(%rbx), %rsp

- /*
- * paging_prepare() and cleanup_trampoline() below can have GOT
- * references. Adjust the table with address we are running at.
- *
- * Zero RAX for adjust_got: the GOT was not adjusted before;
- * there's no adjustment to undo.
- */
- xorq %rax, %rax
-
- /*
- * Calculate the address the binary is loaded at and use it as
- * a GOT adjustment.
- */
- call 1f
-1: popq %rdi
- subq $1b, %rdi
-
- call .Ladjust_got
-
/*
* At this point we are in long mode with 4-level paging enabled,
* but we might want to enable 5-level paging or vice versa.
@@ -464,21 +443,6 @@ trampoline_return:
pushq $0
popfq

- /*
- * Previously we've adjusted the GOT with address the binary was
- * loaded at. Now we need to re-adjust for relocation address.
- *
- * Calculate the address the binary is loaded at, so that we can
- * undo the previous GOT adjustment.
- */
- call 1f
-1: popq %rax
- subq $1b, %rax
-
- /* The new adjustment is the relocation address */
- movq %rbx, %rdi
- call .Ladjust_got
-
/*
* Copy the compressed kernel to the end of our buffer
* where decompression in place becomes safe.
@@ -556,27 +520,6 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
jmp *%rax
SYM_FUNC_END(.Lrelocated)

-/*
- * Adjust the global offset table
- *
- * RAX is the previous adjustment of the table to undo (use 0 if it's the
- * first time we touch GOT).
- * RDI is the new adjustment to apply.
- */
-.Ladjust_got:
- /* Walk through the GOT adding the address to the entries */
- leaq _got(%rip), %rdx
- leaq _egot(%rip), %rcx
-1:
- cmpq %rcx, %rdx
- jae 2f
- subq %rax, (%rdx) /* Undo previous adjustment */
- addq %rdi, (%rdx) /* Apply the new adjustment */
- addq $8, %rdx
- jmp 1b
-2:
- ret
-
.code32
/*
* This is the 32-bit trampoline that will be copied over to low memory.
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index 4bcc943842ab..a4a4a59a2628 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -43,9 +43,7 @@ SECTIONS
_erodata = . ;
}
.got : {
- _got = .;
- KEEP(*(.got))
- _egot = .;
+ *(.got)
}
.got.plt : {
*(.got.plt)
--
2.26.2

2020-07-17 20:19:08

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH-next v5 4/7] x86/boot: Add .text.* to setup.ld

gcc puts the main function into .text.startup when compiled with -Os (or
-O2). This results in arch/x86/boot/main.c having a .text.startup
section which is currently not included explicitly in the linker script
setup.ld in the same directory.

The BFD linker places this orphan section immediately after .text, so
this still works. However, LLD git, since [1], is choosing to place it
immediately after the .bstext section instead (this is the first code
section). This plays havoc with the section layout that setup.elf
requires to create the setup header, for eg on 64-bit:

LD arch/x86/boot/setup.elf
ld.lld: error: section .text.startup file range overlaps with .header
>>> .text.startup range is [0x200040, 0x2001FE]
>>> .header range is [0x2001EF, 0x20026B]

ld.lld: error: section .header file range overlaps with .bsdata
>>> .header range is [0x2001EF, 0x20026B]
>>> .bsdata range is [0x2001FF, 0x200398]

ld.lld: error: section .bsdata file range overlaps with .entrytext
>>> .bsdata range is [0x2001FF, 0x200398]
>>> .entrytext range is [0x20026C, 0x2002D3]

ld.lld: error: section .text.startup virtual address range overlaps
with .header
>>> .text.startup range is [0x40, 0x1FE]
>>> .header range is [0x1EF, 0x26B]

ld.lld: error: section .header virtual address range overlaps with
.bsdata
>>> .header range is [0x1EF, 0x26B]
>>> .bsdata range is [0x1FF, 0x398]

ld.lld: error: section .bsdata virtual address range overlaps with
.entrytext
>>> .bsdata range is [0x1FF, 0x398]
>>> .entrytext range is [0x26C, 0x2D3]

ld.lld: error: section .text.startup load address range overlaps with
.header
>>> .text.startup range is [0x40, 0x1FE]
>>> .header range is [0x1EF, 0x26B]

ld.lld: error: section .header load address range overlaps with
.bsdata
>>> .header range is [0x1EF, 0x26B]
>>> .bsdata range is [0x1FF, 0x398]

ld.lld: error: section .bsdata load address range overlaps with
.entrytext
>>> .bsdata range is [0x1FF, 0x398]
>>> .entrytext range is [0x26C, 0x2D3]

Add .text.* to the .text output section to fix this, and also prevent
any future surprises if the compiler decides to create other such
sections.

[1] https://reviews.llvm.org/D75225

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/setup.ld | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/boot/setup.ld b/arch/x86/boot/setup.ld
index 24c95522f231..49546c247ae2 100644
--- a/arch/x86/boot/setup.ld
+++ b/arch/x86/boot/setup.ld
@@ -20,7 +20,7 @@ SECTIONS
.initdata : { *(.initdata) }
__end_init = .;

- .text : { *(.text) }
+ .text : { *(.text .text.*) }
.text32 : { *(.text32) }

. = ALIGN(16);
--
2.26.2

2020-07-17 20:19:54

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH-next v5 6/7] x86/boot: Remove run-time relocations from head_{32,64}.S

The BFD linker generates run-time relocations for z_input_len and
z_output_len, even though they are absolute symbols.

This is fixed for binutils-2.35 [1]. Work around this for earlier
versions by defining two variables input_len and output_len in addition
to the symbols, and use them via position-independent references.

This eliminates the last two run-time relocations in the head code and
allows us to drop the -z noreloc-overflow flag to the linker.

Move the -pie and --no-dynamic-linker LDFLAGS to LDFLAGS_vmlinux instead
of KBUILD_LDFLAGS. There shouldn't be anything else getting linked, but
this is the more logical location for these flags, and modversions might
call the linker if an EXPORT_SYMBOL is left over accidentally in one of
the decompressors.

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=25754

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/compressed/Makefile | 12 ++----------
arch/x86/boot/compressed/head_32.S | 17 ++++++++---------
arch/x86/boot/compressed/head_64.S | 4 ++--
arch/x86/boot/compressed/mkpiggy.c | 6 ++++++
4 files changed, 18 insertions(+), 21 deletions(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index b6d7caaaef9e..4a3953a596e0 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -51,16 +51,8 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
-ifeq ($(CONFIG_X86_32),y)
-KBUILD_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.
-KBUILD_LDFLAGS += $(shell $(LD) --help 2>&1 | grep -q "\-z noreloc-overflow" \
- && echo "-z noreloc-overflow -pie --no-dynamic-linker")
-endif
-LDFLAGS_vmlinux := -T
+LDFLAGS_vmlinux := $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
+LDFLAGS_vmlinux += -T

hostprogs := mkpiggy
HOST_EXTRACFLAGS += -I$(srctree)/tools/include
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 8c1a4f5610f5..659fad53ca82 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -178,18 +178,17 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
/*
* Do the extraction, and jump to the new kernel..
*/
- /* push arguments for extract_kernel: */
- pushl $z_output_len /* decompressed length, end of relocs */
+ /* push arguments for extract_kernel: */

- pushl %ebp /* output address */
-
- pushl $z_input_len /* input_len */
+ pushl output_len@GOTOFF(%ebx) /* decompressed length, end of relocs */
+ pushl %ebp /* output address */
+ pushl input_len@GOTOFF(%ebx) /* input_len */
leal input_data@GOTOFF(%ebx), %eax
- pushl %eax /* input_data */
+ pushl %eax /* input_data */
leal boot_heap@GOTOFF(%ebx), %eax
- pushl %eax /* heap area */
- pushl %esi /* real mode pointer */
- call extract_kernel /* returns kernel location in %eax */
+ pushl %eax /* heap area */
+ pushl %esi /* real mode pointer */
+ call extract_kernel /* returns kernel location in %eax */
addl $24, %esp

/*
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 11429092c224..9e46729cf162 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -534,9 +534,9 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
movq %rsi, %rdi /* real mode address */
leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
leaq input_data(%rip), %rdx /* input_data */
- movl $z_input_len, %ecx /* input_len */
+ movl input_len(%rip), %ecx /* input_len */
movq %rbp, %r8 /* output target address */
- movl $z_output_len, %r9d /* decompressed length, end of relocs */
+ movl output_len(%rip), %r9d /* decompressed length, end of relocs */
call extract_kernel /* returns kernel location in %rax */
popq %rsi

diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c
index 7e01248765b2..52aa56cdbacc 100644
--- a/arch/x86/boot/compressed/mkpiggy.c
+++ b/arch/x86/boot/compressed/mkpiggy.c
@@ -60,6 +60,12 @@ int main(int argc, char *argv[])
printf(".incbin \"%s\"\n", argv[1]);
printf("input_data_end:\n");

+ printf(".section \".rodata\",\"a\",@progbits\n");
+ printf(".globl input_len\n");
+ printf("input_len:\n\t.long %lu\n", ilen);
+ printf(".globl output_len\n");
+ printf("output_len:\n\t.long %lu\n", (unsigned long)olen);
+
retval = 0;
bail:
if (f)
--
2.26.2

2020-07-17 20:20:41

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH-next v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

Same as v5 previously posted, but rebased onto next-20200717.

v5: https://lore.kernel.org/lkml/[email protected]/

Ard Biesheuvel (3):
x86/boot/compressed: Move .got.plt entries out of the .got section
x86/boot/compressed: Force hidden visibility for all symbol references
x86/boot/compressed: Get rid of GOT fixup code

Arvind Sankar (4):
x86/boot: Add .text.* to setup.ld
x86/boot: Remove run-time relocations from .head.text code
x86/boot: Remove run-time relocations from head_{32,64}.S
x86/boot: Check that there are no run-time relocations

arch/x86/boot/compressed/Makefile | 39 +-----
arch/x86/boot/compressed/head_32.S | 99 +++++----------
arch/x86/boot/compressed/head_64.S | 165 ++++++++++---------------
arch/x86/boot/compressed/mkpiggy.c | 6 +
arch/x86/boot/compressed/vmlinux.lds.S | 24 +++-
arch/x86/boot/setup.ld | 2 +-
drivers/firmware/efi/libstub/Makefile | 2 +-
drivers/firmware/efi/libstub/hidden.h | 6 -
include/linux/hidden.h | 19 +++
9 files changed, 153 insertions(+), 209 deletions(-)
delete mode 100644 drivers/firmware/efi/libstub/hidden.h
create mode 100644 include/linux/hidden.h


base-commit: aab7ee9f8ff0110bfcd594b33dc33748dc1baf46
--
2.26.2

2020-07-17 20:20:57

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH-next v5 7/7] x86/boot: Check that there are no run-time relocations

Add a linker script check that there are no run-time relocations, and
remove the old one that tries to check via looking for specially-named
sections in the object files.

Drop the tests for -fPIE compiler option and -pie linker option, as they
are available in all supported gcc and binutils versions (as well as
clang and lld).

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/compressed/Makefile | 28 +++-----------------------
arch/x86/boot/compressed/vmlinux.lds.S | 8 ++++++++
2 files changed, 11 insertions(+), 25 deletions(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 4a3953a596e0..271cc933c59c 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -29,7 +29,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4

KBUILD_CFLAGS := -m$(BITS) -O2
-KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC)
+KBUILD_CFLAGS += -fno-strict-aliasing -fPIE
KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
cflags-$(CONFIG_X86_32) := -march=i386
cflags-$(CONFIG_X86_64) := -mcmodel=small
@@ -51,7 +51,7 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
-LDFLAGS_vmlinux := $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
+LDFLAGS_vmlinux := -pie $(call ld-option, --no-dynamic-linker)
LDFLAGS_vmlinux += -T

hostprogs := mkpiggy
@@ -86,30 +86,8 @@ vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
vmlinux-objs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o

-# The compressed kernel is built with -fPIC/-fPIE so that a boot loader
-# can place it anywhere in memory and it will still run. However, since
-# it is executed as-is without any ELF relocation processing performed
-# (and has already had all relocation sections stripped from the binary),
-# none of the code can use data relocations (e.g. static assignments of
-# pointer values), since they will be meaningless at runtime. This check
-# will refuse to link the vmlinux if any of these relocations are found.
-quiet_cmd_check_data_rel = DATAREL $@
-define cmd_check_data_rel
- for obj in $(filter %.o,$^); do \
- $(READELF) -S $$obj | grep -qF .rel.local && { \
- echo "error: $$obj has data relocations!" >&2; \
- exit 1; \
- } || true; \
- done
-endef
-
-# We need to run two commands under "if_changed", so merge them into a
-# single invocation.
-quiet_cmd_check-and-link-vmlinux = LD $@
- cmd_check-and-link-vmlinux = $(cmd_check_data_rel); $(cmd_ld)
-
$(obj)/vmlinux: $(vmlinux-objs-y) FORCE
- $(call if_changed,check-and-link-vmlinux)
+ $(call if_changed,ld)

OBJCOPYFLAGS_vmlinux.bin := -R .comment -S
$(obj)/vmlinux.bin: vmlinux FORCE
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index a4a4a59a2628..29df99b6cc64 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -42,6 +42,12 @@ SECTIONS
*(.rodata.*)
_erodata = . ;
}
+ .rel.dyn : {
+ *(.rel.*)
+ }
+ .rela.dyn : {
+ *(.rela.*)
+ }
.got : {
*(.got)
}
@@ -85,3 +91,5 @@ ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT en
#else
ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0xc, "Unexpected GOT/PLT entries detected!")
#endif
+
+ASSERT(SIZEOF(.rel.dyn) == 0 && SIZEOF(.rela.dyn) == 0, "Unexpected run-time relocations detected!")
--
2.26.2

2020-07-17 20:22:31

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH-next v5 5/7] x86/boot: Remove run-time relocations from .head.text code

The assembly code in head_{32,64}.S, while meant to be
position-independent, generates run-time relocations because it uses
instructions such as
leal gdt(%edx), %eax
which make the assembler and linker think that the code is using %edx as
an index into gdt, and hence gdt needs to be relocated to its run-time
address.

On 32-bit, with lld Dmitry Golovin reports that this results in a
link-time error with default options (i.e. unless -z notext is
explicitly passed):
LD arch/x86/boot/compressed/vmlinux
ld.lld: error: can't create dynamic relocation R_386_32 against local
symbol in readonly segment; recompile object files with -fPIC or pass
'-Wl,-z,notext' to allow text relocations in the output

With the BFD linker, this generates a warning during the build, if
--warn-shared-textrel is enabled, which at least Gentoo enables by
default:
LD arch/x86/boot/compressed/vmlinux
ld: arch/x86/boot/compressed/head_32.o: warning: relocation in read-only section `.head.text'
ld: warning: creating a DT_TEXTREL in object

On 64-bit, it is not possible to link the kernel as -pie with lld, and
it is only possible with a BFD linker that supports -z noreloc-overflow,
i.e. versions >2.26. This is because these instructions cannot really be
relocated: the displacement field is only 32-bits wide, and thus cannot
be relocated for a 64-bit load address. The -z noreloc-overflow option
simply overrides the linker error, and results in R_X86_64_RELATIVE
relocations that apply a 64-bit relocation to a 32-bit field anyway.
This happens to work because nothing will process these run-time
relocations.

Start fixing this by removing relocations from .head.text:
- On 32-bit, use a base register that holds the address of the GOT and
reference symbol addresses using @GOTOFF, i.e.
leal gdt@GOTOFF(%edx), %eax
- On 64-bit, most of the code can (and already does) use %rip-relative
addressing, however the .code32 bits can't, and the 64-bit code also
needs to reference symbol addresses as they will be after moving the
compressed kernel to the end of the decompression buffer.
For these cases, reference the symbols as an offset to startup_32 to
avoid creating relocations, i.e.
leal (gdt-startup_32)(%bp), %eax
This only works in .head.text as the subtraction cannot be represented
as a PC-relative relocation unless startup_32 is in the same section
as the code. Move efi32_pe_entry into .head.text so that it can use
the same method to avoid relocations.

Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/compressed/head_32.S | 64 +++++++-----------
arch/x86/boot/compressed/head_64.S | 104 ++++++++++++++++++-----------
2 files changed, 90 insertions(+), 78 deletions(-)

diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 39f0bb43218f..8c1a4f5610f5 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -33,26 +33,10 @@
#include <asm/bootparam.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 and _end, 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 and _end as hidden:
+ * These symbols needed to be marked as .hidden to prevent the BFD linker from
+ * generating R_386_32 (rather than R_386_RELATIVE) relocations for them when
+ * the 32-bit compressed kernel is linked as PIE. This is no longer necessary,
+ * but it doesn't hurt to keep them .hidden.
*/
.hidden _bss
.hidden _ebss
@@ -74,10 +58,10 @@ SYM_FUNC_START(startup_32)
leal (BP_scratch+4)(%esi), %esp
call 1f
1: popl %edx
- subl $1b, %edx
+ addl $_GLOBAL_OFFSET_TABLE_+(.-1b), %edx

/* Load new GDT */
- leal gdt(%edx), %eax
+ leal gdt@GOTOFF(%edx), %eax
movl %eax, 2(%eax)
lgdt (%eax)

@@ -90,14 +74,16 @@ SYM_FUNC_START(startup_32)
movl %eax, %ss

/*
- * %edx contains the address we are loaded at by the boot loader and %ebx
- * contains the address where we should move the kernel image temporarily
- * for safe in-place decompression. %ebp contains the address that the kernel
- * will be decompressed to.
+ * %edx contains the address we are loaded at by the boot loader (plus the
+ * offset to the GOT). The below code calculates %ebx to be the address where
+ * we should move the kernel image temporarily for safe in-place decompression
+ * (again, plus the offset to the GOT).
+ *
+ * %ebp is calculated to be the address that the kernel will be decompressed to.
*/

#ifdef CONFIG_RELOCATABLE
- movl %edx, %ebx
+ leal startup_32@GOTOFF(%edx), %ebx

#ifdef CONFIG_EFI_STUB
/*
@@ -108,7 +94,7 @@ SYM_FUNC_START(startup_32)
* image_offset = startup_32 - image_base
* Otherwise image_offset will be zero and has no effect on the calculations.
*/
- subl image_offset(%edx), %ebx
+ subl image_offset@GOTOFF(%edx), %ebx
#endif

movl BP_kernel_alignment(%esi), %eax
@@ -125,10 +111,10 @@ SYM_FUNC_START(startup_32)
movl %ebx, %ebp // Save the output address for later
/* Target address to relocate to for decompression */
addl BP_init_size(%esi), %ebx
- subl $_end, %ebx
+ subl $_end@GOTOFF, %ebx

/* Set up the stack */
- leal boot_stack_end(%ebx), %esp
+ leal boot_stack_end@GOTOFF(%ebx), %esp

/* Zero EFLAGS */
pushl $0
@@ -139,8 +125,8 @@ SYM_FUNC_START(startup_32)
* where decompression in place becomes safe.
*/
pushl %esi
- leal (_bss-4)(%edx), %esi
- leal (_bss-4)(%ebx), %edi
+ leal (_bss@GOTOFF-4)(%edx), %esi
+ leal (_bss@GOTOFF-4)(%ebx), %edi
movl $(_bss - startup_32), %ecx
shrl $2, %ecx
std
@@ -153,14 +139,14 @@ SYM_FUNC_START(startup_32)
* during extract_kernel below. To avoid any issues, repoint the GDTR
* to the new copy of the GDT.
*/
- leal gdt(%ebx), %eax
+ leal gdt@GOTOFF(%ebx), %eax
movl %eax, 2(%eax)
lgdt (%eax)

/*
* Jump to the relocated address.
*/
- leal .Lrelocated(%ebx), %eax
+ leal .Lrelocated@GOTOFF(%ebx), %eax
jmp *%eax
SYM_FUNC_END(startup_32)

@@ -170,7 +156,7 @@ SYM_FUNC_START_ALIAS(efi_stub_entry)
add $0x4, %esp
movl 8(%esp), %esi /* save boot_params pointer */
call efi_main
- leal startup_32(%eax), %eax
+ /* efi_main returns the possibly relocated address of startup_32 */
jmp *%eax
SYM_FUNC_END(efi32_stub_entry)
SYM_FUNC_END_ALIAS(efi_stub_entry)
@@ -183,8 +169,8 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
* Clear BSS (stack is currently empty)
*/
xorl %eax, %eax
- leal _bss(%ebx), %edi
- leal _ebss(%ebx), %ecx
+ leal _bss@GOTOFF(%ebx), %edi
+ leal _ebss@GOTOFF(%ebx), %ecx
subl %edi, %ecx
shrl $2, %ecx
rep stosl
@@ -198,9 +184,9 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
pushl %ebp /* output address */

pushl $z_input_len /* input_len */
- leal input_data(%ebx), %eax
+ leal input_data@GOTOFF(%ebx), %eax
pushl %eax /* input_data */
- leal boot_heap(%ebx), %eax
+ leal boot_heap@GOTOFF(%ebx), %eax
pushl %eax /* heap area */
pushl %esi /* real mode pointer */
call extract_kernel /* returns kernel location in %eax */
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index bf1ab30acc5b..11429092c224 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -43,6 +43,32 @@
.hidden _end

__HEAD
+
+/*
+ * This macro gives the relative virtual address of X, i.e. the offset of X
+ * from startup_32. This is the same as the link-time virtual address of X,
+ * since startup_32 is at 0, but defining it this way tells the
+ * assembler/linker that we do not want the actual run-time address of X. This
+ * prevents the linker from trying to create unwanted run-time relocation
+ * entries for the reference when the compressed kernel is linked as PIE.
+ *
+ * A reference X(%reg) will result in the link-time VA of X being stored with
+ * the instruction, and a run-time R_X86_64_RELATIVE relocation entry that
+ * adds the 64-bit base address where the kernel is loaded.
+ *
+ * Replacing it with (X-startup_32)(%reg) results in the offset being stored,
+ * and no run-time relocation.
+ *
+ * The macro should be used as a displacement with a base register containing
+ * the run-time address of startup_32 [i.e. rva(X)(%reg)], or as an immediate
+ * [$ rva(X)].
+ *
+ * This macro can only be used from within the .head.text section, since the
+ * expression requires startup_32 to be in the same section as the code being
+ * assembled.
+ */
+#define rva(X) ((X) - startup_32)
+
.code32
SYM_FUNC_START(startup_32)
/*
@@ -65,10 +91,10 @@ SYM_FUNC_START(startup_32)
leal (BP_scratch+4)(%esi), %esp
call 1f
1: popl %ebp
- subl $1b, %ebp
+ subl $ rva(1b), %ebp

/* Load new GDT with the 64bit segments using 32bit descriptor */
- leal gdt(%ebp), %eax
+ leal rva(gdt)(%ebp), %eax
movl %eax, 2(%eax)
lgdt (%eax)

@@ -81,7 +107,7 @@ SYM_FUNC_START(startup_32)
movl %eax, %ss

/* setup a stack and make sure cpu supports long mode. */
- leal boot_stack_end(%ebp), %esp
+ leal rva(boot_stack_end)(%ebp), %esp

call verify_cpu
testl %eax, %eax
@@ -108,7 +134,7 @@ SYM_FUNC_START(startup_32)
* image_offset = startup_32 - image_base
* Otherwise image_offset will be zero and has no effect on the calculations.
*/
- subl image_offset(%ebp), %ebx
+ subl rva(image_offset)(%ebp), %ebx
#endif

movl BP_kernel_alignment(%esi), %eax
@@ -124,7 +150,7 @@ SYM_FUNC_START(startup_32)

/* Target address to relocate to for decompression */
addl BP_init_size(%esi), %ebx
- subl $_end, %ebx
+ subl $ rva(_end), %ebx

/*
* Prepare for entering 64 bit mode
@@ -152,19 +178,19 @@ SYM_FUNC_START(startup_32)
1:

/* Initialize Page tables to 0 */
- leal pgtable(%ebx), %edi
+ leal rva(pgtable)(%ebx), %edi
xorl %eax, %eax
movl $(BOOT_INIT_PGT_SIZE/4), %ecx
rep stosl

/* Build Level 4 */
- leal pgtable + 0(%ebx), %edi
+ leal rva(pgtable + 0)(%ebx), %edi
leal 0x1007 (%edi), %eax
movl %eax, 0(%edi)
addl %edx, 4(%edi)

/* Build Level 3 */
- leal pgtable + 0x1000(%ebx), %edi
+ leal rva(pgtable + 0x1000)(%ebx), %edi
leal 0x1007(%edi), %eax
movl $4, %ecx
1: movl %eax, 0x00(%edi)
@@ -175,7 +201,7 @@ SYM_FUNC_START(startup_32)
jnz 1b

/* Build Level 2 */
- leal pgtable + 0x2000(%ebx), %edi
+ leal rva(pgtable + 0x2000)(%ebx), %edi
movl $0x00000183, %eax
movl $2048, %ecx
1: movl %eax, 0(%edi)
@@ -186,7 +212,7 @@ SYM_FUNC_START(startup_32)
jnz 1b

/* Enable the boot page tables */
- leal pgtable(%ebx), %eax
+ leal rva(pgtable)(%ebx), %eax
movl %eax, %cr3

/* Enable Long mode in EFER (Extended Feature Enable Register) */
@@ -211,14 +237,14 @@ SYM_FUNC_START(startup_32)
* We place all of the values on our mini stack so lret can
* used to perform that far jump.
*/
- leal startup_64(%ebp), %eax
+ leal rva(startup_64)(%ebp), %eax
#ifdef CONFIG_EFI_MIXED
- movl efi32_boot_args(%ebp), %edi
+ movl rva(efi32_boot_args)(%ebp), %edi
cmp $0, %edi
jz 1f
- leal efi64_stub_entry(%ebp), %eax
- movl efi32_boot_args+4(%ebp), %esi
- movl efi32_boot_args+8(%ebp), %edx // saved bootparams pointer
+ leal rva(efi64_stub_entry)(%ebp), %eax
+ movl rva(efi32_boot_args+4)(%ebp), %esi
+ movl rva(efi32_boot_args+8)(%ebp), %edx // saved bootparams pointer
cmpl $0, %edx
jnz 1f
/*
@@ -229,7 +255,7 @@ SYM_FUNC_START(startup_32)
* the correct stack alignment for entry.
*/
subl $40, %esp
- leal efi_pe_entry(%ebp), %eax
+ leal rva(efi_pe_entry)(%ebp), %eax
movl %edi, %ecx // MS calling convention
movl %esi, %edx
1:
@@ -255,18 +281,18 @@ SYM_FUNC_START(efi32_stub_entry)

call 1f
1: pop %ebp
- subl $1b, %ebp
+ subl $ rva(1b), %ebp

- movl %esi, efi32_boot_args+8(%ebp)
+ movl %esi, rva(efi32_boot_args+8)(%ebp)
SYM_INNER_LABEL(efi32_pe_stub_entry, SYM_L_LOCAL)
- movl %ecx, efi32_boot_args(%ebp)
- movl %edx, efi32_boot_args+4(%ebp)
- movb $0, efi_is64(%ebp)
+ movl %ecx, rva(efi32_boot_args)(%ebp)
+ movl %edx, rva(efi32_boot_args+4)(%ebp)
+ movb $0, rva(efi_is64)(%ebp)

/* Save firmware GDTR and code/data selectors */
- sgdtl efi32_boot_gdt(%ebp)
- movw %cs, efi32_boot_cs(%ebp)
- movw %ds, efi32_boot_ds(%ebp)
+ sgdtl rva(efi32_boot_gdt)(%ebp)
+ movw %cs, rva(efi32_boot_cs)(%ebp)
+ movw %ds, rva(efi32_boot_ds)(%ebp)

/* Disable paging */
movl %cr0, %eax
@@ -345,11 +371,11 @@ SYM_CODE_START(startup_64)

/* Target address to relocate to for decompression */
movl BP_init_size(%rsi), %ebx
- subl $_end, %ebx
+ subl $ rva(_end), %ebx
addq %rbp, %rbx

/* Set up the stack */
- leaq boot_stack_end(%rbx), %rsp
+ leaq rva(boot_stack_end)(%rbx), %rsp

/*
* At this point we are in long mode with 4-level paging enabled,
@@ -423,7 +449,7 @@ SYM_CODE_START(startup_64)
lretq
trampoline_return:
/* Restore the stack, the 32-bit trampoline uses its own stack */
- leaq boot_stack_end(%rbx), %rsp
+ leaq rva(boot_stack_end)(%rbx), %rsp

/*
* cleanup_trampoline() would restore trampoline memory.
@@ -435,7 +461,7 @@ trampoline_return:
* this function call.
*/
pushq %rsi
- leaq top_pgtable(%rbx), %rdi
+ leaq rva(top_pgtable)(%rbx), %rdi
call cleanup_trampoline
popq %rsi

@@ -449,9 +475,9 @@ trampoline_return:
*/
pushq %rsi
leaq (_bss-8)(%rip), %rsi
- leaq (_bss-8)(%rbx), %rdi
- movq $_bss /* - $startup_32 */, %rcx
- shrq $3, %rcx
+ leaq rva(_bss-8)(%rbx), %rdi
+ movl $(_bss - startup_32), %ecx
+ shrl $3, %ecx
std
rep movsq
cld
@@ -462,15 +488,15 @@ trampoline_return:
* during extract_kernel below. To avoid any issues, repoint the GDTR
* to the new copy of the GDT.
*/
- leaq gdt64(%rbx), %rax
- leaq gdt(%rbx), %rdx
+ leaq rva(gdt64)(%rbx), %rax
+ leaq rva(gdt)(%rbx), %rdx
movq %rdx, 2(%rax)
lgdt (%rax)

/*
* Jump to the relocated address.
*/
- leaq .Lrelocated(%rbx), %rax
+ leaq rva(.Lrelocated)(%rbx), %rax
jmp *%rax
SYM_CODE_END(startup_64)

@@ -482,7 +508,7 @@ SYM_FUNC_START_ALIAS(efi_stub_entry)
movq %rdx, %rbx /* save boot_params pointer */
call efi_main
movq %rbx,%rsi
- leaq startup_64(%rax), %rax
+ leaq rva(startup_64)(%rax), %rax
jmp *%rax
SYM_FUNC_END(efi64_stub_entry)
SYM_FUNC_END_ALIAS(efi_stub_entry)
@@ -645,7 +671,7 @@ SYM_DATA(efi_is64, .byte 1)
#define BS32_handle_protocol 88 // offsetof(efi_boot_services_32_t, handle_protocol)
#define LI32_image_base 32 // offsetof(efi_loaded_image_32_t, image_base)

- .text
+ __HEAD
.code32
SYM_FUNC_START(efi32_pe_entry)
/*
@@ -667,12 +693,12 @@ SYM_FUNC_START(efi32_pe_entry)

call 1f
1: pop %ebx
- subl $1b, %ebx
+ subl $ rva(1b), %ebx

/* Get the loaded image protocol pointer from the image handle */
leal -4(%ebp), %eax
pushl %eax // &loaded_image
- leal loaded_image_proto(%ebx), %eax
+ leal rva(loaded_image_proto)(%ebx), %eax
pushl %eax // pass the GUID address
pushl 8(%ebp) // pass the image handle

@@ -707,7 +733,7 @@ SYM_FUNC_START(efi32_pe_entry)
* use it before we get to the 64-bit efi_pe_entry() in C code.
*/
subl %esi, %ebx
- movl %ebx, image_offset(%ebp) // save image_offset
+ movl %ebx, rva(image_offset)(%ebp) // save image_offset
jmp efi32_pe_stub_entry

2: popl %edi // restore callee-save registers
--
2.26.2

2020-07-17 23:48:24

by Nick Desaulniers

[permalink] [raw]
Subject: Re: [PATCH-next v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Fri, Jul 17, 2020 at 1:18 PM Arvind Sankar <[email protected]> wrote:
>
> Same as v5 previously posted, but rebased onto next-20200717.
>
> v5: https://lore.kernel.org/lkml/[email protected]/
>
> Ard Biesheuvel (3):
> x86/boot/compressed: Move .got.plt entries out of the .got section
> x86/boot/compressed: Force hidden visibility for all symbol references
> x86/boot/compressed: Get rid of GOT fixup code
>
> Arvind Sankar (4):
> x86/boot: Add .text.* to setup.ld
> x86/boot: Remove run-time relocations from .head.text code
> x86/boot: Remove run-time relocations from head_{32,64}.S
> x86/boot: Check that there are no run-time relocations

Thank you Arvind for the series. I did quick build+boot tests of x86
defconfigs with `make LLVM=1` which look good.

Further, I was able to build+boot i386 defconfigs with `make LLVM=1`
with your series, Mr. Rothwell's proposed fixed for today's linux-next
breakage: https://lore.kernel.org/linux-next/[email protected]/,
and Mr. Gerst's series for i386+clang support. So that looks like
with this series, we can now link i386 defconfig with LLD! Nice!

Tested-by: Nick Desaulniers <[email protected]>

>
> arch/x86/boot/compressed/Makefile | 39 +-----
> arch/x86/boot/compressed/head_32.S | 99 +++++----------
> arch/x86/boot/compressed/head_64.S | 165 ++++++++++---------------
> arch/x86/boot/compressed/mkpiggy.c | 6 +
> arch/x86/boot/compressed/vmlinux.lds.S | 24 +++-
> arch/x86/boot/setup.ld | 2 +-
> drivers/firmware/efi/libstub/Makefile | 2 +-
> drivers/firmware/efi/libstub/hidden.h | 6 -
> include/linux/hidden.h | 19 +++
> 9 files changed, 153 insertions(+), 209 deletions(-)
> delete mode 100644 drivers/firmware/efi/libstub/hidden.h
> create mode 100644 include/linux/hidden.h
>
>
> base-commit: aab7ee9f8ff0110bfcd594b33dc33748dc1baf46
> --
> 2.26.2
>


--
Thanks,
~Nick Desaulniers

2020-07-18 05:47:44

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: [PATCH v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Fri, 17 Jul 2020 at 21:17, Nick Desaulniers <[email protected]> wrote:
>
> On Fri, Jul 17, 2020 at 6:46 AM Arvind Sankar <[email protected]> wrote:
> >
> > On Tue, Jul 14, 2020 at 08:41:26PM -0400, Arvind Sankar wrote:
> > > The compressed kernel currently contains bogus run-time relocations in
> > > the startup code in head_{32,64}.S, which are generated by the linker,
> > > but must not actually be processed at run-time.
> > >
> > > This generates warnings when linking with the BFD linker, and errors
> > > with LLD, which defaults to erroring on run-time relocations in read-only
> > > sections. It also requires the -z noreloc-overflow hack for the 64-bit
> > > kernel, which prevents us from linking it as -pie on an older BFD linker
> > > (<= 2.26) or on LLD, because the locations that are to be apparently
> > > relocated are only 32-bits in size and so cannot really have
> > > R_X86_64_RELATIVE relocations.
> > >
> > > This series aims to get rid of these relocations. I've build- and
> > > boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
> > > gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
> > > has other issues [0].
> > >
> >
> > Hi Thomas, Ingo, Borislav, would you be able to take a look over this
> > series in time for 5.9?
>
> Hi Arvind, thanks for the series; I'm behind on testing. When I try
> to apply this series on top of linux-next, I get a collision in
> drivers/firmware/efi/libstub/Makefile:27 when applying "0002
> x86/boot/compressed: Force hidden visibility for all symbol
> references". Would you mind refreshing the series to avoid that
> collision?

That is not the right way to deal with conflicts against -next.

This series targets the -tip tree, and applies fine against it. If you
want to apply it on some other tree and test it, that is fine, and
highly appreciated, but 'refreshing' the series against -next means it
no longer applies to -tip, and may be based on unidentified conflict
resolutions performed by Stephen that the maintainers will have to
deal with.

Boris, Ingo, Thomas,

Mind taking v5 of this series? (With Nick's Tested-by) I think these
patches have been simmering long enough. Do note there is a conflict
against the kbuild tree, but the resolution should be straightforward.

2020-07-18 07:02:30

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Sat, Jul 18, 2020 at 7:45 AM Ard Biesheuvel <[email protected]> wrote:
>
> On Fri, 17 Jul 2020 at 21:17, Nick Desaulniers <[email protected]> wrote:
> >
> > On Fri, Jul 17, 2020 at 6:46 AM Arvind Sankar <[email protected]> wrote:
> > >
> > > On Tue, Jul 14, 2020 at 08:41:26PM -0400, Arvind Sankar wrote:
> > > > The compressed kernel currently contains bogus run-time relocations in
> > > > the startup code in head_{32,64}.S, which are generated by the linker,
> > > > but must not actually be processed at run-time.
> > > >
> > > > This generates warnings when linking with the BFD linker, and errors
> > > > with LLD, which defaults to erroring on run-time relocations in read-only
> > > > sections. It also requires the -z noreloc-overflow hack for the 64-bit
> > > > kernel, which prevents us from linking it as -pie on an older BFD linker
> > > > (<= 2.26) or on LLD, because the locations that are to be apparently
> > > > relocated are only 32-bits in size and so cannot really have
> > > > R_X86_64_RELATIVE relocations.
> > > >
> > > > This series aims to get rid of these relocations. I've build- and
> > > > boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
> > > > gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
> > > > has other issues [0].
> > > >
> > >
> > > Hi Thomas, Ingo, Borislav, would you be able to take a look over this
> > > series in time for 5.9?
> >
> > Hi Arvind, thanks for the series; I'm behind on testing. When I try
> > to apply this series on top of linux-next, I get a collision in
> > drivers/firmware/efi/libstub/Makefile:27 when applying "0002
> > x86/boot/compressed: Force hidden visibility for all symbol
> > references". Would you mind refreshing the series to avoid that
> > collision?
>
> That is not the right way to deal with conflicts against -next.
>
> This series targets the -tip tree, and applies fine against it. If you
> want to apply it on some other tree and test it, that is fine, and
> highly appreciated, but 'refreshing' the series against -next means it
> no longer applies to -tip, and may be based on unidentified conflict
> resolutions performed by Stephen that the maintainers will have to
> deal with.
>
> Boris, Ingo, Thomas,
>
> Mind taking v5 of this series? (With Nick's Tested-by) I think these
> patches have been simmering long enough. Do note there is a conflict
> against the kbuild tree, but the resolution should be straightforward.

Agreed with that approach.

v5 misses also my credits - Tested-by for the whole series is sufficient.

- Sedat -

2020-07-24 23:26:06

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Sat, Jul 18, 2020 at 08:44:50AM +0300, Ard Biesheuvel wrote:
> On Fri, 17 Jul 2020 at 21:17, Nick Desaulniers <[email protected]> wrote:
> >
> > On Fri, Jul 17, 2020 at 6:46 AM Arvind Sankar <[email protected]> wrote:
> > >
> > > On Tue, Jul 14, 2020 at 08:41:26PM -0400, Arvind Sankar wrote:
> > > > The compressed kernel currently contains bogus run-time relocations in
> > > > the startup code in head_{32,64}.S, which are generated by the linker,
> > > > but must not actually be processed at run-time.
> > > >
> > > > This generates warnings when linking with the BFD linker, and errors
> > > > with LLD, which defaults to erroring on run-time relocations in read-only
> > > > sections. It also requires the -z noreloc-overflow hack for the 64-bit
> > > > kernel, which prevents us from linking it as -pie on an older BFD linker
> > > > (<= 2.26) or on LLD, because the locations that are to be apparently
> > > > relocated are only 32-bits in size and so cannot really have
> > > > R_X86_64_RELATIVE relocations.
> > > >
> > > > This series aims to get rid of these relocations. I've build- and
> > > > boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
> > > > gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
> > > > has other issues [0].
> > > >
> > >
> > > Hi Thomas, Ingo, Borislav, would you be able to take a look over this
> > > series in time for 5.9?
> >
> > Hi Arvind, thanks for the series; I'm behind on testing. When I try
> > to apply this series on top of linux-next, I get a collision in
> > drivers/firmware/efi/libstub/Makefile:27 when applying "0002
> > x86/boot/compressed: Force hidden visibility for all symbol
> > references". Would you mind refreshing the series to avoid that
> > collision?
>
> That is not the right way to deal with conflicts against -next.
>
> This series targets the -tip tree, and applies fine against it. If you
> want to apply it on some other tree and test it, that is fine, and
> highly appreciated, but 'refreshing' the series against -next means it
> no longer applies to -tip, and may be based on unidentified conflict
> resolutions performed by Stephen that the maintainers will have to
> deal with.
>
> Boris, Ingo, Thomas,
>
> Mind taking v5 of this series? (With Nick's Tested-by) I think these
> patches have been simmering long enough. Do note there is a conflict
> against the kbuild tree, but the resolution should be straightforward.

I would love that; I need to rebase my orphan series on this too...

--
Kees Cook

2020-07-29 22:07:52

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH-next v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Fri, Jul 17, 2020 at 04:17:54PM -0400, Arvind Sankar wrote:
> Same as v5 previously posted, but rebased onto next-20200717.
>
> v5: https://lore.kernel.org/lkml/[email protected]/

BTW, some bits of feedback on process:

- please don't re-use version numbers, this is confusing :)
- please fix whatever is happening with the "In-Reply-To:" headers, the
threading is extremely hard to understand (each patch isn't a reply to
the cover letter, and everything is a reply to a single earlier email)

--
Kees Cook

2020-07-29 22:26:26

by Arvind Sankar

[permalink] [raw]
Subject: Re: [PATCH-next v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Wed, Jul 29, 2020 at 03:04:43PM -0700, Kees Cook wrote:
> On Fri, Jul 17, 2020 at 04:17:54PM -0400, Arvind Sankar wrote:
> > Same as v5 previously posted, but rebased onto next-20200717.
> >
> > v5: https://lore.kernel.org/lkml/[email protected]/
>
> BTW, some bits of feedback on process:
>
> - please don't re-use version numbers, this is confusing :)

This was a special case as there were no actual changes in this version.

> - please fix whatever is happening with the "In-Reply-To:" headers, the
> threading is extremely hard to understand (each patch isn't a reply to
> the cover letter, and everything is a reply to a single earlier email)
>
> --
> Kees Cook

I think this is because I'm passing --in-reply-to to format-patch.
Seems like I should specify that in send-email instead, hopefully that
will fix the threading.

Thanks.

2020-07-30 02:39:27

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH-next v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Wed, Jul 29, 2020 at 06:23:41PM -0400, Arvind Sankar wrote:
> On Wed, Jul 29, 2020 at 03:04:43PM -0700, Kees Cook wrote:
> > On Fri, Jul 17, 2020 at 04:17:54PM -0400, Arvind Sankar wrote:
> > > Same as v5 previously posted, but rebased onto next-20200717.
> > >
> > > v5: https://lore.kernel.org/lkml/[email protected]/
> >
> > BTW, some bits of feedback on process:
> >
> > - please don't re-use version numbers, this is confusing :)
>
> This was a special case as there were no actual changes in this version.

It ended up missing some review tags, etc.

> > - please fix whatever is happening with the "In-Reply-To:" headers, the
> > threading is extremely hard to understand (each patch isn't a reply to
> > the cover letter, and everything is a reply to a single earlier email)
>
> I think this is because I'm passing --in-reply-to to format-patch.
> Seems like I should specify that in send-email instead, hopefully that
> will fix the threading.

Ah yes, I think would do it -- it overrides the normal threading that
send-email does. Usually --in-reply-to is for a single email.

Thanks for checking!

--
Kees Cook

2020-07-31 14:59:41

by Arvind Sankar

[permalink] [raw]
Subject: Re: [PATCH v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Fri, Jul 24, 2020 at 04:25:20PM -0700, Kees Cook wrote:
> On Sat, Jul 18, 2020 at 08:44:50AM +0300, Ard Biesheuvel wrote:
> > On Fri, 17 Jul 2020 at 21:17, Nick Desaulniers <[email protected]> wrote:
> > >
> > > On Fri, Jul 17, 2020 at 6:46 AM Arvind Sankar <[email protected]> wrote:
> > > >
> > > > On Tue, Jul 14, 2020 at 08:41:26PM -0400, Arvind Sankar wrote:
> > > > > The compressed kernel currently contains bogus run-time relocations in
> > > > > the startup code in head_{32,64}.S, which are generated by the linker,
> > > > > but must not actually be processed at run-time.
> > > > >
> > > > > This generates warnings when linking with the BFD linker, and errors
> > > > > with LLD, which defaults to erroring on run-time relocations in read-only
> > > > > sections. It also requires the -z noreloc-overflow hack for the 64-bit
> > > > > kernel, which prevents us from linking it as -pie on an older BFD linker
> > > > > (<= 2.26) or on LLD, because the locations that are to be apparently
> > > > > relocated are only 32-bits in size and so cannot really have
> > > > > R_X86_64_RELATIVE relocations.
> > > > >
> > > > > This series aims to get rid of these relocations. I've build- and
> > > > > boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
> > > > > gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
> > > > > has other issues [0].
> > > > >
> > > >
> > > > Hi Thomas, Ingo, Borislav, would you be able to take a look over this
> > > > series in time for 5.9?
> > >
> > > Hi Arvind, thanks for the series; I'm behind on testing. When I try
> > > to apply this series on top of linux-next, I get a collision in
> > > drivers/firmware/efi/libstub/Makefile:27 when applying "0002
> > > x86/boot/compressed: Force hidden visibility for all symbol
> > > references". Would you mind refreshing the series to avoid that
> > > collision?
> >
> > That is not the right way to deal with conflicts against -next.
> >
> > This series targets the -tip tree, and applies fine against it. If you
> > want to apply it on some other tree and test it, that is fine, and
> > highly appreciated, but 'refreshing' the series against -next means it
> > no longer applies to -tip, and may be based on unidentified conflict
> > resolutions performed by Stephen that the maintainers will have to
> > deal with.
> >
> > Boris, Ingo, Thomas,
> >
> > Mind taking v5 of this series? (With Nick's Tested-by) I think these
> > patches have been simmering long enough. Do note there is a conflict
> > against the kbuild tree, but the resolution should be straightforward.
>
> I would love that; I need to rebase my orphan series on this too...
>
> --
> Kees Cook

Ping?

2020-07-31 15:00:51

by Sedat Dilek

[permalink] [raw]
Subject: Re: [PATCH v5 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Fri, Jul 31, 2020 at 4:53 PM Arvind Sankar <[email protected]> wrote:
>
> On Fri, Jul 24, 2020 at 04:25:20PM -0700, Kees Cook wrote:
> > On Sat, Jul 18, 2020 at 08:44:50AM +0300, Ard Biesheuvel wrote:
> > > On Fri, 17 Jul 2020 at 21:17, Nick Desaulniers <[email protected]> wrote:
> > > >
> > > > On Fri, Jul 17, 2020 at 6:46 AM Arvind Sankar <[email protected]> wrote:
> > > > >
> > > > > On Tue, Jul 14, 2020 at 08:41:26PM -0400, Arvind Sankar wrote:
> > > > > > The compressed kernel currently contains bogus run-time relocations in
> > > > > > the startup code in head_{32,64}.S, which are generated by the linker,
> > > > > > but must not actually be processed at run-time.
> > > > > >
> > > > > > This generates warnings when linking with the BFD linker, and errors
> > > > > > with LLD, which defaults to erroring on run-time relocations in read-only
> > > > > > sections. It also requires the -z noreloc-overflow hack for the 64-bit
> > > > > > kernel, which prevents us from linking it as -pie on an older BFD linker
> > > > > > (<= 2.26) or on LLD, because the locations that are to be apparently
> > > > > > relocated are only 32-bits in size and so cannot really have
> > > > > > R_X86_64_RELATIVE relocations.
> > > > > >
> > > > > > This series aims to get rid of these relocations. I've build- and
> > > > > > boot-tested with combinations of clang/gcc-10 with lld/bfd-2.34, and
> > > > > > gcc-4.9.0 with bfd-2.24, skipping clang on 32-bit because it currently
> > > > > > has other issues [0].
> > > > > >
> > > > >
> > > > > Hi Thomas, Ingo, Borislav, would you be able to take a look over this
> > > > > series in time for 5.9?
> > > >
> > > > Hi Arvind, thanks for the series; I'm behind on testing. When I try
> > > > to apply this series on top of linux-next, I get a collision in
> > > > drivers/firmware/efi/libstub/Makefile:27 when applying "0002
> > > > x86/boot/compressed: Force hidden visibility for all symbol
> > > > references". Would you mind refreshing the series to avoid that
> > > > collision?
> > >
> > > That is not the right way to deal with conflicts against -next.
> > >
> > > This series targets the -tip tree, and applies fine against it. If you
> > > want to apply it on some other tree and test it, that is fine, and
> > > highly appreciated, but 'refreshing' the series against -next means it
> > > no longer applies to -tip, and may be based on unidentified conflict
> > > resolutions performed by Stephen that the maintainers will have to
> > > deal with.
> > >
> > > Boris, Ingo, Thomas,
> > >
> > > Mind taking v5 of this series? (With Nick's Tested-by) I think these
> > > patches have been simmering long enough. Do note there is a conflict
> > > against the kbuild tree, but the resolution should be straightforward.
> >
> > I would love that; I need to rebase my orphan series on this too...
> >
> > --
> > Kees Cook
>
> Ping?

I just tested v5 against Linux v5.8-rc7 and the conflict you mention is with:

commit da05b143a308bd6a7a444401f9732678ae63fc70
x86/boot: Don't add the EFI stub to targets

If I forgot...
Tested-by: Sedat Dilek <[email protected]>

- Sedat -

2020-07-31 20:28:26

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v6 0/7] x86/boot: Remove run-time relocations from compressed kernel

Changes from v5:
- Rebase onto v5.8-rc7 to resolve Makefile conflict

v5: https://lore.kernel.org/lkml/[email protected]/

Changes from v4:
- Move -pie --no-dynamic-linker from KBUILD_LDFLAGS to LDFLAGS_vmlinux
Sedat: I'm not clear on whether you tested with the final LDFLAGS,
could you confirm: i.e. if you tested with -pie passed to LLD?
- Replace runtime -> run-time to be consistent in wording

Changes from v3:
- Move hidden.h to include/linux so the EFI stub and the compressed
kernel can share the same file

Changes from v2:
- Incorporate Ard's patches for eliminating GOT references into this
series
- Rebase on v5.8-rc3

Changes from v1:
- Add .text.* to setup.ld instead of just .text.startup
- Rename the la() macro introduced in the second patch for 64-bit to
rva(), and rework the explanatory comment.
- In the last patch, check both .rel.dyn and .rela.dyn, instead of just
one per arch.

Ard Biesheuvel (3):
x86/boot/compressed: Move .got.plt entries out of the .got section
x86/boot/compressed: Force hidden visibility for all symbol references
x86/boot/compressed: Get rid of GOT fixup code

Arvind Sankar (4):
x86/boot: Add .text.* to setup.ld
x86/boot: Remove run-time relocations from .head.text code
x86/boot: Remove run-time relocations from head_{32,64}.S
x86/boot: Check that there are no run-time relocations

arch/x86/boot/compressed/Makefile | 39 +-----
arch/x86/boot/compressed/head_32.S | 99 +++++----------
arch/x86/boot/compressed/head_64.S | 165 ++++++++++---------------
arch/x86/boot/compressed/mkpiggy.c | 6 +
arch/x86/boot/compressed/vmlinux.lds.S | 24 +++-
arch/x86/boot/setup.ld | 2 +-
drivers/firmware/efi/libstub/Makefile | 2 +-
drivers/firmware/efi/libstub/hidden.h | 6 -
include/linux/hidden.h | 19 +++
9 files changed, 153 insertions(+), 209 deletions(-)
delete mode 100644 drivers/firmware/efi/libstub/hidden.h
create mode 100644 include/linux/hidden.h


base-commit: 92ed301919932f777713b9172e525674157e983d
--
2.26.2

2020-07-31 20:29:11

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v6 7/7] x86/boot: Check that there are no run-time relocations

Add a linker script check that there are no run-time relocations, and
remove the old one that tries to check via looking for specially-named
sections in the object files.

Drop the tests for -fPIE compiler option and -pie linker option, as they
are available in all supported gcc and binutils versions (as well as
clang and lld).

Tested-by: Nick Desaulniers <[email protected]>
Tested-by: Sedat Dilek <[email protected]>
Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/compressed/Makefile | 28 +++-----------------------
arch/x86/boot/compressed/vmlinux.lds.S | 8 ++++++++
2 files changed, 11 insertions(+), 25 deletions(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 7db0102a573d..96d53e300ab6 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -29,7 +29,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4

KBUILD_CFLAGS := -m$(BITS) -O2
-KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC)
+KBUILD_CFLAGS += -fno-strict-aliasing -fPIE
KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
cflags-$(CONFIG_X86_32) := -march=i386
cflags-$(CONFIG_X86_64) := -mcmodel=small
@@ -51,7 +51,7 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
-LDFLAGS_vmlinux := $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
+LDFLAGS_vmlinux := -pie $(call ld-option, --no-dynamic-linker)
LDFLAGS_vmlinux += -T

hostprogs := mkpiggy
@@ -86,30 +86,8 @@ vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o
efi-obj-$(CONFIG_EFI_STUB) = $(objtree)/drivers/firmware/efi/libstub/lib.a

-# The compressed kernel is built with -fPIC/-fPIE so that a boot loader
-# can place it anywhere in memory and it will still run. However, since
-# it is executed as-is without any ELF relocation processing performed
-# (and has already had all relocation sections stripped from the binary),
-# none of the code can use data relocations (e.g. static assignments of
-# pointer values), since they will be meaningless at runtime. This check
-# will refuse to link the vmlinux if any of these relocations are found.
-quiet_cmd_check_data_rel = DATAREL $@
-define cmd_check_data_rel
- for obj in $(filter %.o,$^); do \
- $(READELF) -S $$obj | grep -qF .rel.local && { \
- echo "error: $$obj has data relocations!" >&2; \
- exit 1; \
- } || true; \
- done
-endef
-
-# We need to run two commands under "if_changed", so merge them into a
-# single invocation.
-quiet_cmd_check-and-link-vmlinux = LD $@
- cmd_check-and-link-vmlinux = $(cmd_check_data_rel); $(cmd_ld)
-
$(obj)/vmlinux: $(vmlinux-objs-y) $(efi-obj-y) FORCE
- $(call if_changed,check-and-link-vmlinux)
+ $(call if_changed,ld)

OBJCOPYFLAGS_vmlinux.bin := -R .comment -S
$(obj)/vmlinux.bin: vmlinux FORCE
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index a4a4a59a2628..29df99b6cc64 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -42,6 +42,12 @@ SECTIONS
*(.rodata.*)
_erodata = . ;
}
+ .rel.dyn : {
+ *(.rel.*)
+ }
+ .rela.dyn : {
+ *(.rela.*)
+ }
.got : {
*(.got)
}
@@ -85,3 +91,5 @@ ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT en
#else
ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0xc, "Unexpected GOT/PLT entries detected!")
#endif
+
+ASSERT(SIZEOF(.rel.dyn) == 0 && SIZEOF(.rela.dyn) == 0, "Unexpected run-time relocations detected!")
--
2.26.2

2020-07-31 20:29:22

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v6 3/7] x86/boot/compressed: Get rid of GOT fixup code

From: Ard Biesheuvel <[email protected]>

In a previous patch, we have eliminated GOT entries from the decompressor
binary and added an assertion that the .got section is empty. This means
that the GOT fixup routines that exist in both the 32-bit and 64-bit
startup routines have become dead code, and can be removed.

While at it, drop the KEEP() from the linker script, as it has no effect
on the contents of output sections that are created by the linker itself.

Tested-by: Nick Desaulniers <[email protected]>
Tested-by: Sedat Dilek <[email protected]>
Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/head_32.S | 24 ++---------
arch/x86/boot/compressed/head_64.S | 57 --------------------------
arch/x86/boot/compressed/vmlinux.lds.S | 4 +-
3 files changed, 5 insertions(+), 80 deletions(-)

diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 03557f2174bf..39f0bb43218f 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -49,16 +49,13 @@
* 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, _egot and _end, 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, _egot and _end as
- * hidden:
+ * _bss, _ebss and _end, 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 and _end as hidden:
*/
.hidden _bss
.hidden _ebss
- .hidden _got
- .hidden _egot
.hidden _end

__HEAD
@@ -192,19 +189,6 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
shrl $2, %ecx
rep stosl

-/*
- * Adjust our own GOT
- */
- leal _got(%ebx), %edx
- leal _egot(%ebx), %ecx
-1:
- cmpl %ecx, %edx
- jae 2f
- addl %ebx, (%edx)
- addl $4, %edx
- jmp 1b
-2:
-
/*
* Do the extraction, and jump to the new kernel..
*/
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 97d37f0a34f5..bf1ab30acc5b 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -40,8 +40,6 @@
*/
.hidden _bss
.hidden _ebss
- .hidden _got
- .hidden _egot
.hidden _end

__HEAD
@@ -353,25 +351,6 @@ SYM_CODE_START(startup_64)
/* Set up the stack */
leaq boot_stack_end(%rbx), %rsp

- /*
- * paging_prepare() and cleanup_trampoline() below can have GOT
- * references. Adjust the table with address we are running at.
- *
- * Zero RAX for adjust_got: the GOT was not adjusted before;
- * there's no adjustment to undo.
- */
- xorq %rax, %rax
-
- /*
- * Calculate the address the binary is loaded at and use it as
- * a GOT adjustment.
- */
- call 1f
-1: popq %rdi
- subq $1b, %rdi
-
- call .Ladjust_got
-
/*
* At this point we are in long mode with 4-level paging enabled,
* but we might want to enable 5-level paging or vice versa.
@@ -464,21 +443,6 @@ trampoline_return:
pushq $0
popfq

- /*
- * Previously we've adjusted the GOT with address the binary was
- * loaded at. Now we need to re-adjust for relocation address.
- *
- * Calculate the address the binary is loaded at, so that we can
- * undo the previous GOT adjustment.
- */
- call 1f
-1: popq %rax
- subq $1b, %rax
-
- /* The new adjustment is the relocation address */
- movq %rbx, %rdi
- call .Ladjust_got
-
/*
* Copy the compressed kernel to the end of our buffer
* where decompression in place becomes safe.
@@ -556,27 +520,6 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
jmp *%rax
SYM_FUNC_END(.Lrelocated)

-/*
- * Adjust the global offset table
- *
- * RAX is the previous adjustment of the table to undo (use 0 if it's the
- * first time we touch GOT).
- * RDI is the new adjustment to apply.
- */
-.Ladjust_got:
- /* Walk through the GOT adding the address to the entries */
- leaq _got(%rip), %rdx
- leaq _egot(%rip), %rcx
-1:
- cmpq %rcx, %rdx
- jae 2f
- subq %rax, (%rdx) /* Undo previous adjustment */
- addq %rdi, (%rdx) /* Apply the new adjustment */
- addq $8, %rdx
- jmp 1b
-2:
- ret
-
.code32
/*
* This is the 32-bit trampoline that will be copied over to low memory.
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index 4bcc943842ab..a4a4a59a2628 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -43,9 +43,7 @@ SECTIONS
_erodata = . ;
}
.got : {
- _got = .;
- KEEP(*(.got))
- _egot = .;
+ *(.got)
}
.got.plt : {
*(.got.plt)
--
2.26.2

2020-07-31 20:30:30

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v6 6/7] x86/boot: Remove run-time relocations from head_{32,64}.S

The BFD linker generates run-time relocations for z_input_len and
z_output_len, even though they are absolute symbols.

This is fixed for binutils-2.35 [1]. Work around this for earlier
versions by defining two variables input_len and output_len in addition
to the symbols, and use them via position-independent references.

This eliminates the last two run-time relocations in the head code and
allows us to drop the -z noreloc-overflow flag to the linker.

Move the -pie and --no-dynamic-linker LDFLAGS to LDFLAGS_vmlinux instead
of KBUILD_LDFLAGS. There shouldn't be anything else getting linked, but
this is the more logical location for these flags, and modversions might
call the linker if an EXPORT_SYMBOL is left over accidentally in one of
the decompressors.

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=25754

Tested-by: Nick Desaulniers <[email protected]>
Tested-by: Sedat Dilek <[email protected]>
Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/compressed/Makefile | 12 ++----------
arch/x86/boot/compressed/head_32.S | 17 ++++++++---------
arch/x86/boot/compressed/head_64.S | 4 ++--
arch/x86/boot/compressed/mkpiggy.c | 6 ++++++
4 files changed, 18 insertions(+), 21 deletions(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 489fea16bcfb..7db0102a573d 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -51,16 +51,8 @@ UBSAN_SANITIZE :=n
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE)
# Compressed kernel should be built as PIE since it may be loaded at any
# address by the bootloader.
-ifeq ($(CONFIG_X86_32),y)
-KBUILD_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.
-KBUILD_LDFLAGS += $(shell $(LD) --help 2>&1 | grep -q "\-z noreloc-overflow" \
- && echo "-z noreloc-overflow -pie --no-dynamic-linker")
-endif
-LDFLAGS_vmlinux := -T
+LDFLAGS_vmlinux := $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
+LDFLAGS_vmlinux += -T

hostprogs := mkpiggy
HOST_EXTRACFLAGS += -I$(srctree)/tools/include
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 8c1a4f5610f5..659fad53ca82 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -178,18 +178,17 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
/*
* Do the extraction, and jump to the new kernel..
*/
- /* push arguments for extract_kernel: */
- pushl $z_output_len /* decompressed length, end of relocs */
+ /* push arguments for extract_kernel: */

- pushl %ebp /* output address */
-
- pushl $z_input_len /* input_len */
+ pushl output_len@GOTOFF(%ebx) /* decompressed length, end of relocs */
+ pushl %ebp /* output address */
+ pushl input_len@GOTOFF(%ebx) /* input_len */
leal input_data@GOTOFF(%ebx), %eax
- pushl %eax /* input_data */
+ pushl %eax /* input_data */
leal boot_heap@GOTOFF(%ebx), %eax
- pushl %eax /* heap area */
- pushl %esi /* real mode pointer */
- call extract_kernel /* returns kernel location in %eax */
+ pushl %eax /* heap area */
+ pushl %esi /* real mode pointer */
+ call extract_kernel /* returns kernel location in %eax */
addl $24, %esp

/*
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 11429092c224..9e46729cf162 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -534,9 +534,9 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
movq %rsi, %rdi /* real mode address */
leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
leaq input_data(%rip), %rdx /* input_data */
- movl $z_input_len, %ecx /* input_len */
+ movl input_len(%rip), %ecx /* input_len */
movq %rbp, %r8 /* output target address */
- movl $z_output_len, %r9d /* decompressed length, end of relocs */
+ movl output_len(%rip), %r9d /* decompressed length, end of relocs */
call extract_kernel /* returns kernel location in %rax */
popq %rsi

diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c
index 7e01248765b2..52aa56cdbacc 100644
--- a/arch/x86/boot/compressed/mkpiggy.c
+++ b/arch/x86/boot/compressed/mkpiggy.c
@@ -60,6 +60,12 @@ int main(int argc, char *argv[])
printf(".incbin \"%s\"\n", argv[1]);
printf("input_data_end:\n");

+ printf(".section \".rodata\",\"a\",@progbits\n");
+ printf(".globl input_len\n");
+ printf("input_len:\n\t.long %lu\n", ilen);
+ printf(".globl output_len\n");
+ printf("output_len:\n\t.long %lu\n", (unsigned long)olen);
+
retval = 0;
bail:
if (f)
--
2.26.2

2020-07-31 20:30:43

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v6 5/7] x86/boot: Remove run-time relocations from .head.text code

The assembly code in head_{32,64}.S, while meant to be
position-independent, generates run-time relocations because it uses
instructions such as
leal gdt(%edx), %eax
which make the assembler and linker think that the code is using %edx as
an index into gdt, and hence gdt needs to be relocated to its run-time
address.

On 32-bit, with lld Dmitry Golovin reports that this results in a
link-time error with default options (i.e. unless -z notext is
explicitly passed):
LD arch/x86/boot/compressed/vmlinux
ld.lld: error: can't create dynamic relocation R_386_32 against local
symbol in readonly segment; recompile object files with -fPIC or pass
'-Wl,-z,notext' to allow text relocations in the output

With the BFD linker, this generates a warning during the build, if
--warn-shared-textrel is enabled, which at least Gentoo enables by
default:
LD arch/x86/boot/compressed/vmlinux
ld: arch/x86/boot/compressed/head_32.o: warning: relocation in read-only section `.head.text'
ld: warning: creating a DT_TEXTREL in object

On 64-bit, it is not possible to link the kernel as -pie with lld, and
it is only possible with a BFD linker that supports -z noreloc-overflow,
i.e. versions >2.26. This is because these instructions cannot really be
relocated: the displacement field is only 32-bits wide, and thus cannot
be relocated for a 64-bit load address. The -z noreloc-overflow option
simply overrides the linker error, and results in R_X86_64_RELATIVE
relocations that apply a 64-bit relocation to a 32-bit field anyway.
This happens to work because nothing will process these run-time
relocations.

Start fixing this by removing relocations from .head.text:
- On 32-bit, use a base register that holds the address of the GOT and
reference symbol addresses using @GOTOFF, i.e.
leal gdt@GOTOFF(%edx), %eax
- On 64-bit, most of the code can (and already does) use %rip-relative
addressing, however the .code32 bits can't, and the 64-bit code also
needs to reference symbol addresses as they will be after moving the
compressed kernel to the end of the decompression buffer.
For these cases, reference the symbols as an offset to startup_32 to
avoid creating relocations, i.e.
leal (gdt-startup_32)(%bp), %eax
This only works in .head.text as the subtraction cannot be represented
as a PC-relative relocation unless startup_32 is in the same section
as the code. Move efi32_pe_entry into .head.text so that it can use
the same method to avoid relocations.

Tested-by: Nick Desaulniers <[email protected]>
Tested-by: Sedat Dilek <[email protected]>
Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/compressed/head_32.S | 64 +++++++-----------
arch/x86/boot/compressed/head_64.S | 104 ++++++++++++++++++-----------
2 files changed, 90 insertions(+), 78 deletions(-)

diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 39f0bb43218f..8c1a4f5610f5 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -33,26 +33,10 @@
#include <asm/bootparam.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 and _end, 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 and _end as hidden:
+ * These symbols needed to be marked as .hidden to prevent the BFD linker from
+ * generating R_386_32 (rather than R_386_RELATIVE) relocations for them when
+ * the 32-bit compressed kernel is linked as PIE. This is no longer necessary,
+ * but it doesn't hurt to keep them .hidden.
*/
.hidden _bss
.hidden _ebss
@@ -74,10 +58,10 @@ SYM_FUNC_START(startup_32)
leal (BP_scratch+4)(%esi), %esp
call 1f
1: popl %edx
- subl $1b, %edx
+ addl $_GLOBAL_OFFSET_TABLE_+(.-1b), %edx

/* Load new GDT */
- leal gdt(%edx), %eax
+ leal gdt@GOTOFF(%edx), %eax
movl %eax, 2(%eax)
lgdt (%eax)

@@ -90,14 +74,16 @@ SYM_FUNC_START(startup_32)
movl %eax, %ss

/*
- * %edx contains the address we are loaded at by the boot loader and %ebx
- * contains the address where we should move the kernel image temporarily
- * for safe in-place decompression. %ebp contains the address that the kernel
- * will be decompressed to.
+ * %edx contains the address we are loaded at by the boot loader (plus the
+ * offset to the GOT). The below code calculates %ebx to be the address where
+ * we should move the kernel image temporarily for safe in-place decompression
+ * (again, plus the offset to the GOT).
+ *
+ * %ebp is calculated to be the address that the kernel will be decompressed to.
*/

#ifdef CONFIG_RELOCATABLE
- movl %edx, %ebx
+ leal startup_32@GOTOFF(%edx), %ebx

#ifdef CONFIG_EFI_STUB
/*
@@ -108,7 +94,7 @@ SYM_FUNC_START(startup_32)
* image_offset = startup_32 - image_base
* Otherwise image_offset will be zero and has no effect on the calculations.
*/
- subl image_offset(%edx), %ebx
+ subl image_offset@GOTOFF(%edx), %ebx
#endif

movl BP_kernel_alignment(%esi), %eax
@@ -125,10 +111,10 @@ SYM_FUNC_START(startup_32)
movl %ebx, %ebp // Save the output address for later
/* Target address to relocate to for decompression */
addl BP_init_size(%esi), %ebx
- subl $_end, %ebx
+ subl $_end@GOTOFF, %ebx

/* Set up the stack */
- leal boot_stack_end(%ebx), %esp
+ leal boot_stack_end@GOTOFF(%ebx), %esp

/* Zero EFLAGS */
pushl $0
@@ -139,8 +125,8 @@ SYM_FUNC_START(startup_32)
* where decompression in place becomes safe.
*/
pushl %esi
- leal (_bss-4)(%edx), %esi
- leal (_bss-4)(%ebx), %edi
+ leal (_bss@GOTOFF-4)(%edx), %esi
+ leal (_bss@GOTOFF-4)(%ebx), %edi
movl $(_bss - startup_32), %ecx
shrl $2, %ecx
std
@@ -153,14 +139,14 @@ SYM_FUNC_START(startup_32)
* during extract_kernel below. To avoid any issues, repoint the GDTR
* to the new copy of the GDT.
*/
- leal gdt(%ebx), %eax
+ leal gdt@GOTOFF(%ebx), %eax
movl %eax, 2(%eax)
lgdt (%eax)

/*
* Jump to the relocated address.
*/
- leal .Lrelocated(%ebx), %eax
+ leal .Lrelocated@GOTOFF(%ebx), %eax
jmp *%eax
SYM_FUNC_END(startup_32)

@@ -170,7 +156,7 @@ SYM_FUNC_START_ALIAS(efi_stub_entry)
add $0x4, %esp
movl 8(%esp), %esi /* save boot_params pointer */
call efi_main
- leal startup_32(%eax), %eax
+ /* efi_main returns the possibly relocated address of startup_32 */
jmp *%eax
SYM_FUNC_END(efi32_stub_entry)
SYM_FUNC_END_ALIAS(efi_stub_entry)
@@ -183,8 +169,8 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
* Clear BSS (stack is currently empty)
*/
xorl %eax, %eax
- leal _bss(%ebx), %edi
- leal _ebss(%ebx), %ecx
+ leal _bss@GOTOFF(%ebx), %edi
+ leal _ebss@GOTOFF(%ebx), %ecx
subl %edi, %ecx
shrl $2, %ecx
rep stosl
@@ -198,9 +184,9 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
pushl %ebp /* output address */

pushl $z_input_len /* input_len */
- leal input_data(%ebx), %eax
+ leal input_data@GOTOFF(%ebx), %eax
pushl %eax /* input_data */
- leal boot_heap(%ebx), %eax
+ leal boot_heap@GOTOFF(%ebx), %eax
pushl %eax /* heap area */
pushl %esi /* real mode pointer */
call extract_kernel /* returns kernel location in %eax */
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index bf1ab30acc5b..11429092c224 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -43,6 +43,32 @@
.hidden _end

__HEAD
+
+/*
+ * This macro gives the relative virtual address of X, i.e. the offset of X
+ * from startup_32. This is the same as the link-time virtual address of X,
+ * since startup_32 is at 0, but defining it this way tells the
+ * assembler/linker that we do not want the actual run-time address of X. This
+ * prevents the linker from trying to create unwanted run-time relocation
+ * entries for the reference when the compressed kernel is linked as PIE.
+ *
+ * A reference X(%reg) will result in the link-time VA of X being stored with
+ * the instruction, and a run-time R_X86_64_RELATIVE relocation entry that
+ * adds the 64-bit base address where the kernel is loaded.
+ *
+ * Replacing it with (X-startup_32)(%reg) results in the offset being stored,
+ * and no run-time relocation.
+ *
+ * The macro should be used as a displacement with a base register containing
+ * the run-time address of startup_32 [i.e. rva(X)(%reg)], or as an immediate
+ * [$ rva(X)].
+ *
+ * This macro can only be used from within the .head.text section, since the
+ * expression requires startup_32 to be in the same section as the code being
+ * assembled.
+ */
+#define rva(X) ((X) - startup_32)
+
.code32
SYM_FUNC_START(startup_32)
/*
@@ -65,10 +91,10 @@ SYM_FUNC_START(startup_32)
leal (BP_scratch+4)(%esi), %esp
call 1f
1: popl %ebp
- subl $1b, %ebp
+ subl $ rva(1b), %ebp

/* Load new GDT with the 64bit segments using 32bit descriptor */
- leal gdt(%ebp), %eax
+ leal rva(gdt)(%ebp), %eax
movl %eax, 2(%eax)
lgdt (%eax)

@@ -81,7 +107,7 @@ SYM_FUNC_START(startup_32)
movl %eax, %ss

/* setup a stack and make sure cpu supports long mode. */
- leal boot_stack_end(%ebp), %esp
+ leal rva(boot_stack_end)(%ebp), %esp

call verify_cpu
testl %eax, %eax
@@ -108,7 +134,7 @@ SYM_FUNC_START(startup_32)
* image_offset = startup_32 - image_base
* Otherwise image_offset will be zero and has no effect on the calculations.
*/
- subl image_offset(%ebp), %ebx
+ subl rva(image_offset)(%ebp), %ebx
#endif

movl BP_kernel_alignment(%esi), %eax
@@ -124,7 +150,7 @@ SYM_FUNC_START(startup_32)

/* Target address to relocate to for decompression */
addl BP_init_size(%esi), %ebx
- subl $_end, %ebx
+ subl $ rva(_end), %ebx

/*
* Prepare for entering 64 bit mode
@@ -152,19 +178,19 @@ SYM_FUNC_START(startup_32)
1:

/* Initialize Page tables to 0 */
- leal pgtable(%ebx), %edi
+ leal rva(pgtable)(%ebx), %edi
xorl %eax, %eax
movl $(BOOT_INIT_PGT_SIZE/4), %ecx
rep stosl

/* Build Level 4 */
- leal pgtable + 0(%ebx), %edi
+ leal rva(pgtable + 0)(%ebx), %edi
leal 0x1007 (%edi), %eax
movl %eax, 0(%edi)
addl %edx, 4(%edi)

/* Build Level 3 */
- leal pgtable + 0x1000(%ebx), %edi
+ leal rva(pgtable + 0x1000)(%ebx), %edi
leal 0x1007(%edi), %eax
movl $4, %ecx
1: movl %eax, 0x00(%edi)
@@ -175,7 +201,7 @@ SYM_FUNC_START(startup_32)
jnz 1b

/* Build Level 2 */
- leal pgtable + 0x2000(%ebx), %edi
+ leal rva(pgtable + 0x2000)(%ebx), %edi
movl $0x00000183, %eax
movl $2048, %ecx
1: movl %eax, 0(%edi)
@@ -186,7 +212,7 @@ SYM_FUNC_START(startup_32)
jnz 1b

/* Enable the boot page tables */
- leal pgtable(%ebx), %eax
+ leal rva(pgtable)(%ebx), %eax
movl %eax, %cr3

/* Enable Long mode in EFER (Extended Feature Enable Register) */
@@ -211,14 +237,14 @@ SYM_FUNC_START(startup_32)
* We place all of the values on our mini stack so lret can
* used to perform that far jump.
*/
- leal startup_64(%ebp), %eax
+ leal rva(startup_64)(%ebp), %eax
#ifdef CONFIG_EFI_MIXED
- movl efi32_boot_args(%ebp), %edi
+ movl rva(efi32_boot_args)(%ebp), %edi
cmp $0, %edi
jz 1f
- leal efi64_stub_entry(%ebp), %eax
- movl efi32_boot_args+4(%ebp), %esi
- movl efi32_boot_args+8(%ebp), %edx // saved bootparams pointer
+ leal rva(efi64_stub_entry)(%ebp), %eax
+ movl rva(efi32_boot_args+4)(%ebp), %esi
+ movl rva(efi32_boot_args+8)(%ebp), %edx // saved bootparams pointer
cmpl $0, %edx
jnz 1f
/*
@@ -229,7 +255,7 @@ SYM_FUNC_START(startup_32)
* the correct stack alignment for entry.
*/
subl $40, %esp
- leal efi_pe_entry(%ebp), %eax
+ leal rva(efi_pe_entry)(%ebp), %eax
movl %edi, %ecx // MS calling convention
movl %esi, %edx
1:
@@ -255,18 +281,18 @@ SYM_FUNC_START(efi32_stub_entry)

call 1f
1: pop %ebp
- subl $1b, %ebp
+ subl $ rva(1b), %ebp

- movl %esi, efi32_boot_args+8(%ebp)
+ movl %esi, rva(efi32_boot_args+8)(%ebp)
SYM_INNER_LABEL(efi32_pe_stub_entry, SYM_L_LOCAL)
- movl %ecx, efi32_boot_args(%ebp)
- movl %edx, efi32_boot_args+4(%ebp)
- movb $0, efi_is64(%ebp)
+ movl %ecx, rva(efi32_boot_args)(%ebp)
+ movl %edx, rva(efi32_boot_args+4)(%ebp)
+ movb $0, rva(efi_is64)(%ebp)

/* Save firmware GDTR and code/data selectors */
- sgdtl efi32_boot_gdt(%ebp)
- movw %cs, efi32_boot_cs(%ebp)
- movw %ds, efi32_boot_ds(%ebp)
+ sgdtl rva(efi32_boot_gdt)(%ebp)
+ movw %cs, rva(efi32_boot_cs)(%ebp)
+ movw %ds, rva(efi32_boot_ds)(%ebp)

/* Disable paging */
movl %cr0, %eax
@@ -345,11 +371,11 @@ SYM_CODE_START(startup_64)

/* Target address to relocate to for decompression */
movl BP_init_size(%rsi), %ebx
- subl $_end, %ebx
+ subl $ rva(_end), %ebx
addq %rbp, %rbx

/* Set up the stack */
- leaq boot_stack_end(%rbx), %rsp
+ leaq rva(boot_stack_end)(%rbx), %rsp

/*
* At this point we are in long mode with 4-level paging enabled,
@@ -423,7 +449,7 @@ SYM_CODE_START(startup_64)
lretq
trampoline_return:
/* Restore the stack, the 32-bit trampoline uses its own stack */
- leaq boot_stack_end(%rbx), %rsp
+ leaq rva(boot_stack_end)(%rbx), %rsp

/*
* cleanup_trampoline() would restore trampoline memory.
@@ -435,7 +461,7 @@ trampoline_return:
* this function call.
*/
pushq %rsi
- leaq top_pgtable(%rbx), %rdi
+ leaq rva(top_pgtable)(%rbx), %rdi
call cleanup_trampoline
popq %rsi

@@ -449,9 +475,9 @@ trampoline_return:
*/
pushq %rsi
leaq (_bss-8)(%rip), %rsi
- leaq (_bss-8)(%rbx), %rdi
- movq $_bss /* - $startup_32 */, %rcx
- shrq $3, %rcx
+ leaq rva(_bss-8)(%rbx), %rdi
+ movl $(_bss - startup_32), %ecx
+ shrl $3, %ecx
std
rep movsq
cld
@@ -462,15 +488,15 @@ trampoline_return:
* during extract_kernel below. To avoid any issues, repoint the GDTR
* to the new copy of the GDT.
*/
- leaq gdt64(%rbx), %rax
- leaq gdt(%rbx), %rdx
+ leaq rva(gdt64)(%rbx), %rax
+ leaq rva(gdt)(%rbx), %rdx
movq %rdx, 2(%rax)
lgdt (%rax)

/*
* Jump to the relocated address.
*/
- leaq .Lrelocated(%rbx), %rax
+ leaq rva(.Lrelocated)(%rbx), %rax
jmp *%rax
SYM_CODE_END(startup_64)

@@ -482,7 +508,7 @@ SYM_FUNC_START_ALIAS(efi_stub_entry)
movq %rdx, %rbx /* save boot_params pointer */
call efi_main
movq %rbx,%rsi
- leaq startup_64(%rax), %rax
+ leaq rva(startup_64)(%rax), %rax
jmp *%rax
SYM_FUNC_END(efi64_stub_entry)
SYM_FUNC_END_ALIAS(efi_stub_entry)
@@ -645,7 +671,7 @@ SYM_DATA(efi_is64, .byte 1)
#define BS32_handle_protocol 88 // offsetof(efi_boot_services_32_t, handle_protocol)
#define LI32_image_base 32 // offsetof(efi_loaded_image_32_t, image_base)

- .text
+ __HEAD
.code32
SYM_FUNC_START(efi32_pe_entry)
/*
@@ -667,12 +693,12 @@ SYM_FUNC_START(efi32_pe_entry)

call 1f
1: pop %ebx
- subl $1b, %ebx
+ subl $ rva(1b), %ebx

/* Get the loaded image protocol pointer from the image handle */
leal -4(%ebp), %eax
pushl %eax // &loaded_image
- leal loaded_image_proto(%ebx), %eax
+ leal rva(loaded_image_proto)(%ebx), %eax
pushl %eax // pass the GUID address
pushl 8(%ebp) // pass the image handle

@@ -707,7 +733,7 @@ SYM_FUNC_START(efi32_pe_entry)
* use it before we get to the 64-bit efi_pe_entry() in C code.
*/
subl %esi, %ebx
- movl %ebx, image_offset(%ebp) // save image_offset
+ movl %ebx, rva(image_offset)(%ebp) // save image_offset
jmp efi32_pe_stub_entry

2: popl %edi // restore callee-save registers
--
2.26.2

2020-07-31 20:31:00

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v6 4/7] x86/boot: Add .text.* to setup.ld

gcc puts the main function into .text.startup when compiled with -Os (or
-O2). This results in arch/x86/boot/main.c having a .text.startup
section which is currently not included explicitly in the linker script
setup.ld in the same directory.

The BFD linker places this orphan section immediately after .text, so
this still works. However, LLD git, since [1], is choosing to place it
immediately after the .bstext section instead (this is the first code
section). This plays havoc with the section layout that setup.elf
requires to create the setup header, for eg on 64-bit:

LD arch/x86/boot/setup.elf
ld.lld: error: section .text.startup file range overlaps with .header
>>> .text.startup range is [0x200040, 0x2001FE]
>>> .header range is [0x2001EF, 0x20026B]

ld.lld: error: section .header file range overlaps with .bsdata
>>> .header range is [0x2001EF, 0x20026B]
>>> .bsdata range is [0x2001FF, 0x200398]

ld.lld: error: section .bsdata file range overlaps with .entrytext
>>> .bsdata range is [0x2001FF, 0x200398]
>>> .entrytext range is [0x20026C, 0x2002D3]

ld.lld: error: section .text.startup virtual address range overlaps
with .header
>>> .text.startup range is [0x40, 0x1FE]
>>> .header range is [0x1EF, 0x26B]

ld.lld: error: section .header virtual address range overlaps with
.bsdata
>>> .header range is [0x1EF, 0x26B]
>>> .bsdata range is [0x1FF, 0x398]

ld.lld: error: section .bsdata virtual address range overlaps with
.entrytext
>>> .bsdata range is [0x1FF, 0x398]
>>> .entrytext range is [0x26C, 0x2D3]

ld.lld: error: section .text.startup load address range overlaps with
.header
>>> .text.startup range is [0x40, 0x1FE]
>>> .header range is [0x1EF, 0x26B]

ld.lld: error: section .header load address range overlaps with
.bsdata
>>> .header range is [0x1EF, 0x26B]
>>> .bsdata range is [0x1FF, 0x398]

ld.lld: error: section .bsdata load address range overlaps with
.entrytext
>>> .bsdata range is [0x1FF, 0x398]
>>> .entrytext range is [0x26C, 0x2D3]

Add .text.* to the .text output section to fix this, and also prevent
any future surprises if the compiler decides to create other such
sections.

[1] https://reviews.llvm.org/D75225

Tested-by: Nick Desaulniers <[email protected]>
Tested-by: Sedat Dilek <[email protected]>
Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Ard Biesheuvel <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
---
arch/x86/boot/setup.ld | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/boot/setup.ld b/arch/x86/boot/setup.ld
index 24c95522f231..49546c247ae2 100644
--- a/arch/x86/boot/setup.ld
+++ b/arch/x86/boot/setup.ld
@@ -20,7 +20,7 @@ SECTIONS
.initdata : { *(.initdata) }
__end_init = .;

- .text : { *(.text) }
+ .text : { *(.text .text.*) }
.text32 : { *(.text32) }

. = ALIGN(16);
--
2.26.2

2020-07-31 20:31:27

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v6 2/7] x86/boot/compressed: Force hidden visibility for all symbol references

From: Ard Biesheuvel <[email protected]>

Eliminate all GOT entries in the decompressor binary, by forcing hidden
visibility for all symbol references, which informs the compiler that
such references will be resolved at link time without the need for
allocating GOT entries.

To ensure that no GOT entries will creep back in, add an assertion to
the decompressor linker script that will fire if the .got section has
a non-zero size.

[Arvind: move hidden.h to include/linux instead of making a copy]

Tested-by: Nick Desaulniers <[email protected]>
Tested-by: Sedat Dilek <[email protected]>
Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/Makefile | 1 +
arch/x86/boot/compressed/vmlinux.lds.S | 1 +
drivers/firmware/efi/libstub/Makefile | 2 +-
drivers/firmware/efi/libstub/hidden.h | 6 ------
include/linux/hidden.h | 19 +++++++++++++++++++
5 files changed, 22 insertions(+), 7 deletions(-)
delete mode 100644 drivers/firmware/efi/libstub/hidden.h
create mode 100644 include/linux/hidden.h

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 5a828fde7a42..489fea16bcfb 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -42,6 +42,7 @@ KBUILD_CFLAGS += $(call cc-disable-warning, gnu)
KBUILD_CFLAGS += -Wno-pointer-sign
KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=)
KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
+KBUILD_CFLAGS += -include $(srctree)/include/linux/hidden.h

KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
GCOV_PROFILE := n
diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index b17d218ccdf9..4bcc943842ab 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -81,6 +81,7 @@ SECTIONS
DISCARDS
}

+ASSERT(SIZEOF(.got) == 0, "Unexpected GOT entries detected!")
#ifdef CONFIG_X86_64
ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT entries detected!")
#else
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 75daaf20374e..b4f8c80cc591 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -26,7 +26,7 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt

KBUILD_CFLAGS := $(cflags-y) -Os -DDISABLE_BRANCH_PROFILING \
- -include $(srctree)/drivers/firmware/efi/libstub/hidden.h \
+ -include $(srctree)/include/linux/hidden.h \
-D__NO_FORTIFY \
$(call cc-option,-ffreestanding) \
$(call cc-option,-fno-stack-protector) \
diff --git a/drivers/firmware/efi/libstub/hidden.h b/drivers/firmware/efi/libstub/hidden.h
deleted file mode 100644
index 3493b041f419..000000000000
--- a/drivers/firmware/efi/libstub/hidden.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * To prevent the compiler from emitting GOT-indirected (and thus absolute)
- * references to any global symbols, override their visibility as 'hidden'
- */
-#pragma GCC visibility push(hidden)
diff --git a/include/linux/hidden.h b/include/linux/hidden.h
new file mode 100644
index 000000000000..49a17b6b5962
--- /dev/null
+++ b/include/linux/hidden.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * When building position independent code with GCC using the -fPIC option,
+ * (or even the -fPIE one on older versions), it will assume that we are
+ * building a dynamic object (either a shared library or an executable) that
+ * may have symbol references that can only be resolved at load time. For a
+ * variety of reasons (ELF symbol preemption, the CoW footprint of the section
+ * that is modified by the loader), this results in all references to symbols
+ * with external linkage to go via entries in the Global Offset Table (GOT),
+ * which carries absolute addresses which need to be fixed up when the
+ * executable image is loaded at an offset which is different from its link
+ * time offset.
+ *
+ * Fortunately, there is a way to inform the compiler that such symbol
+ * references will be satisfied at link time rather than at load time, by
+ * giving them 'hidden' visibility.
+ */
+
+#pragma GCC visibility push(hidden)
--
2.26.2

2020-07-31 20:31:29

by Arvind Sankar

[permalink] [raw]
Subject: [PATCH v6 1/7] x86/boot/compressed: Move .got.plt entries out of the .got section

From: Ard Biesheuvel <[email protected]>

The .got.plt section contains the part of the GOT which is used by PLT
entries, and which gets updated lazily by the dynamic loader when
function calls are dispatched through those PLT entries.

On fully linked binaries such as the kernel proper or the decompressor,
this never happens, and so in practice, the .got.plt section consists
only of the first 3 magic entries that are meant to point at the _DYNAMIC
section and at the fixup routine in the loader. However, since we don't
use a dynamic loader, those entries are never populated or used.

This means that treating those entries like ordinary GOT entries, and
updating their values based on the actual placement of the executable in
memory is completely pointless, and we can just ignore the .got.plt
section entirely, provided that it has no additional entries beyond
the first 3 ones.

So add an assertion in the linker script to ensure that this assumption
holds, and move the contents out of the [_got, _egot) memory range that
is modified by the GOT fixup routines.

While at it, drop the KEEP(), since it has no effect on the contents
of output sections that are created by the linker itself.

Tested-by: Nick Desaulniers <[email protected]>
Tested-by: Sedat Dilek <[email protected]>
Reviewed-by: Kees Cook <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Acked-by: Arvind Sankar <[email protected]>
Signed-off-by: Arvind Sankar <[email protected]>
From: Ard Biesheuvel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/boot/compressed/vmlinux.lds.S | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S
index 8f1025d1f681..b17d218ccdf9 100644
--- a/arch/x86/boot/compressed/vmlinux.lds.S
+++ b/arch/x86/boot/compressed/vmlinux.lds.S
@@ -44,10 +44,13 @@ SECTIONS
}
.got : {
_got = .;
- KEEP(*(.got.plt))
KEEP(*(.got))
_egot = .;
}
+ .got.plt : {
+ *(.got.plt)
+ }
+
.data : {
_data = . ;
*(.data)
@@ -77,3 +80,9 @@ SECTIONS

DISCARDS
}
+
+#ifdef CONFIG_X86_64
+ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT/PLT entries detected!")
+#else
+ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0xc, "Unexpected GOT/PLT entries detected!")
+#endif
--
2.26.2

2020-07-31 23:15:43

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v6 0/7] x86/boot: Remove run-time relocations from compressed kernel

On Fri, Jul 31, 2020 at 04:27:31PM -0400, Arvind Sankar wrote:
> Changes from v5:
> - Rebase onto v5.8-rc7 to resolve Makefile conflict

Heh, I just merged your series into mine for orphans (just sent it too)
and ironically, I did this too (rebase to rc7) but for ARM fixes on
Clang vs binutils AS.

Regardless, if it goes in separately, please consider this series:

Reviewed-by: Kees Cook <[email protected]>
Tested-by: Kees Cook <[email protected]>

--
Kees Cook