[v5,05/20] x86/decompressor: Use proper sequence to take the address of the GOT

Message ID 20230607072342.4054036-6-ardb@kernel.org
State New
Headers
Series efi/x86: Avoid bare metal decompressor during EFI boot |

Commit Message

Ard Biesheuvel June 7, 2023, 7:23 a.m. UTC
  The 32-bit decompressor does not actually use a global offset table
(GOT), but as is common for 32-bit position independent code, it uses
the magic symbol _GLOBAL_OFFSET_TABLE_ as an anchor from which to derive
the actual runtime addresses of other symbols, using special @GOTOFF
symbol references that are resolved at link time, and populated with the
distance between the address of the magic _GLOBAL_OFFSET_TABLE_ anchor
and the address of the symbol in question.

This means _GLOBAL_OFFSET_TABLE_ is the only symbol whose actual runtime
address needs to be determined explicitly, which is one of the first
things that happens in startup_32. However, it does so by taking the
absolute address via the immediate field of an ADD instruction (plus a
small offset), which seems to defeat the point.

Fortunately, the assembler knows that _GLOBAL_OFFSET_TABLE_ is magic,
and emits a special relative relocation instead, and so the resulting
code works as expected. However, this is not obvious for someone reading
the code, and the use of LEA with an explicit relative addend is more
idiomatic so use that instead.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 arch/x86/boot/compressed/head_32.S | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
  

Comments

Borislav Petkov June 21, 2023, 11:08 a.m. UTC | #1
On Wed, Jun 07, 2023 at 09:23:27AM +0200, Ard Biesheuvel wrote:
> The 32-bit decompressor does not actually use a global offset table
> (GOT), but as is common for 32-bit position independent code, it uses
> the magic symbol _GLOBAL_OFFSET_TABLE_ as an anchor from which to derive
> the actual runtime addresses of other symbols, using special @GOTOFF
> symbol references that are resolved at link time, and populated with the
> distance between the address of the magic _GLOBAL_OFFSET_TABLE_ anchor
> and the address of the symbol in question.
> 
> This means _GLOBAL_OFFSET_TABLE_ is the only symbol whose actual runtime
> address needs to be determined explicitly, which is one of the first
> things that happens in startup_32. However, it does so by taking the
> absolute address via the immediate field of an ADD instruction (plus a
> small offset), which seems to defeat the point.
> 
> Fortunately, the assembler knows that _GLOBAL_OFFSET_TABLE_ is magic,
> and emits a special relative relocation instead, and so the resulting

Which special relocation do you mean?

This guy:

Relocation section '.rel.head.text' at offset 0x3a0 contains 12 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000010  00000d0a R_386_GOTPC       00000000   _GLOBAL_OFFSET_TABLE_

?

In any case, this thing came from

a2c4fc4d4e2c ("x86/boot: Remove run-time relocations from .head.text code")

Thx.
  
Ard Biesheuvel June 23, 2023, 2 p.m. UTC | #2
On Wed, 21 Jun 2023 at 13:09, Borislav Petkov <bp@alien8.de> wrote:
>
> On Wed, Jun 07, 2023 at 09:23:27AM +0200, Ard Biesheuvel wrote:
> > The 32-bit decompressor does not actually use a global offset table
> > (GOT), but as is common for 32-bit position independent code, it uses
> > the magic symbol _GLOBAL_OFFSET_TABLE_ as an anchor from which to derive
> > the actual runtime addresses of other symbols, using special @GOTOFF
> > symbol references that are resolved at link time, and populated with the
> > distance between the address of the magic _GLOBAL_OFFSET_TABLE_ anchor
> > and the address of the symbol in question.
> >
> > This means _GLOBAL_OFFSET_TABLE_ is the only symbol whose actual runtime
> > address needs to be determined explicitly, which is one of the first
> > things that happens in startup_32. However, it does so by taking the
> > absolute address via the immediate field of an ADD instruction (plus a
> > small offset), which seems to defeat the point.
> >
> > Fortunately, the assembler knows that _GLOBAL_OFFSET_TABLE_ is magic,
> > and emits a special relative relocation instead, and so the resulting
>
> Which special relocation do you mean?
>
> This guy:
>
> Relocation section '.rel.head.text' at offset 0x3a0 contains 12 entries:
>  Offset     Info    Type            Sym.Value  Sym. Name
> 00000010  00000d0a R_386_GOTPC       00000000   _GLOBAL_OFFSET_TABLE_
>
> ?

Yep.

if you assemble this

movl $_GLOBAL_OFFSET_TABLE_, %eax
movl $_GLOBAL_OFFSET_TABLE, %eax

you'll end up with

   0: b8 01 00 00 00        mov    $0x1,%eax
1: R_386_GOTPC _GLOBAL_OFFSET_TABLE_
   5: b8 00 00 00 00        mov    $0x0,%eax
6: R_386_32 _GLOBAL_OFFSET_TABLE

So it is not possible to take the absolute address of
_GLOBAL_OFFSET_TABLE_ via an absolute relocation, you will always get
the relative offset instead.
  
Borislav Petkov July 7, 2023, 1:56 p.m. UTC | #3
On Fri, Jun 23, 2023 at 04:00:30PM +0200, Ard Biesheuvel wrote:
> if you assemble this
> 
> movl $_GLOBAL_OFFSET_TABLE_, %eax
> movl $_GLOBAL_OFFSET_TABLE, %eax
> 
> you'll end up with
> 
>    0: b8 01 00 00 00        mov    $0x1,%eax
> 1: R_386_GOTPC _GLOBAL_OFFSET_TABLE_
>    5: b8 00 00 00 00        mov    $0x0,%eax
> 6: R_386_32 _GLOBAL_OFFSET_TABLE
> 
> So it is not possible to take the absolute address of
> _GLOBAL_OFFSET_TABLE_ via an absolute relocation, you will always get
> the relative offset instead.

Right.

Thx.
  

Patch

diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 8876ffe30e9a4819..3530465b5b85ccf3 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -58,7 +58,7 @@  SYM_FUNC_START(startup_32)
 	leal	(BP_scratch+4)(%esi), %esp
 	call	1f
 1:	popl	%edx
-	addl	$_GLOBAL_OFFSET_TABLE_+(.-1b), %edx
+	leal	(_GLOBAL_OFFSET_TABLE_ - 1b)(%edx), %edx
 
 	/* Load new GDT */
 	leal	gdt@GOTOFF(%edx), %eax