[tip:x86/urgent] x86/boot/compressed/64: Set up GOT for paging_prepare() and cleanup_trampoline()
- Date: Wed, 16 May 2018 03:35:52 -0700
- From: "tip-bot for Kirill A. Shutemov" <tipbot@xxxxxxxxx>
- Subject: [tip:x86/urgent] x86/boot/compressed/64: Set up GOT for paging_prepare() and cleanup_trampoline()
Author: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx>
AuthorDate: Wed, 16 May 2018 11:01:28 +0300
Committer: Ingo Molnar <mingo@xxxxxxxxxx>
CommitDate: Wed, 16 May 2018 12:15:13 +0200
x86/boot/compressed/64: Set up GOT for paging_prepare() and cleanup_trampoline()
Eric and Hugh have reported instant reboot due to my recent changes in
The root cause is that I didn't realize that we need to adjust GOT to be
able to run C code that early.
The problem is only visible with an older toolchain. Binutils >= 2.24 is
able to eliminate GOT references by replacing them with RIP-relative
We need to adjust GOT two times:
- before calling paging_prepare() using the initial load address
- before calling C code from the relocated kernel
Reported-by: Eric Dumazet <eric.dumazet@xxxxxxxxx>
Reported-by: Hugh Dickins <hughd@xxxxxxxxxx>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx>
Reviewed-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Cc: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Fixes: 194a9749c73d ("x86/boot/compressed/64: Handle 5-level paging boot if kernel is above 4G")
Signed-off-by: Ingo Molnar <mingo@xxxxxxxxxx>
arch/x86/boot/compressed/head_64.S | 68 ++++++++++++++++++++++++++++++--------
1 file changed, 55 insertions(+), 13 deletions(-)
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index fca012baba19..d17af6a4bfc9 100644
@@ -305,6 +305,25 @@ ENTRY(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 adjust_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.
@@ -381,6 +400,21 @@ trampoline_return:
+ * 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 adjust_got
* Copy the compressed kernel to the end of our buffer
* where decompression in place becomes safe.
@@ -481,19 +515,6 @@ relocated:
shrq $3, %rcx
- * Adjust our own GOT
- leaq _got(%rip), %rdx
- leaq _egot(%rip), %rcx
- cmpq %rcx, %rdx
- jae 2f
- addq %rbx, (%rdx)
- addq $8, %rdx
- jmp 1b
* Do the extraction, and jump to the new kernel..
@@ -512,6 +533,27 @@ relocated:
+ * 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.
+ /* Walk through the GOT adding the address to the entries */
+ leaq _got(%rip), %rdx
+ leaq _egot(%rip), %rcx
+ cmpq %rcx, %rdx
+ jae 2f
+ subq %rax, (%rdx) /* Undo previous adjustment */
+ addq %rdi, (%rdx) /* Apply the new adjustment */
+ addq $8, %rdx
+ jmp 1b
* This is the 32-bit trampoline that will be copied over to low memory.