Re: [RFC PATCH 04/28] x86/boot: Permit GOTPCREL relocations for x86_64 builds

From: Ard Biesheuvel
Date: Tue Oct 01 2024 - 02:56:45 EST


On Tue, 1 Oct 2024 at 07:33, Josh Poimboeuf <jpoimboe@xxxxxxxxxx> wrote:
>
> On Wed, Sep 25, 2024 at 05:01:04PM +0200, Ard Biesheuvel wrote:
> > + if (r_type == R_X86_64_GOTPCREL) {
> > + Elf_Shdr *s = &secs[sec->shdr.sh_info].shdr;
> > + unsigned file_off = offset - s->sh_addr + s->sh_offset;
> > +
> > + /*
> > + * GOTPCREL relocations refer to instructions that load
> > + * a 64-bit address via a 32-bit relative reference to
> > + * the GOT. In this case, it is the GOT entry that
> > + * needs to be fixed up, not the immediate offset in
> > + * the opcode. Note that the linker will have applied an
> > + * addend of -4 to compensate for the delta between the
> > + * relocation offset and the value of RIP when the
> > + * instruction executes, and this needs to be backed out
> > + * again. (Addends other than -4 are permitted in
> > + * principle, but make no sense in practice so they are
> > + * not supported.)
> > + */
> > + if (rel->r_addend != -4) {
> > + die("invalid addend (%ld) for %s relocation: %s\n",
> > + rel->r_addend, rel_type(r_type), symname);
> > + break;
> > + }
>
> For x86 PC-relative addressing, the addend is <reloc offset> -
> <subsequent insn offset>. So a PC-relative addend can be something
> other than -4 when the relocation applies to the middle of an
> instruction, e.g.:
>
> 5b381: 66 81 3d 00 00 00 00 01 06 cmpw $0x601,0x0(%rip) # 5b38a <generic_validate_add_page+0x4a> 5b384: R_X86_64_PC32 boot_cpu_data-0x6
>
> 5f283: 81 3d 00 00 00 00 ff ff ff 00 cmpl $0xffffff,0x0(%rip) # 5f28d <x86_acpi_suspend_lowlevel+0x9d> 5f285: R_X86_64_PC32 smpboot_control-0x8
>
> 72f67: c6 05 00 00 00 00 01 movb $0x1,0x0(%rip) # 72f6e <sched_itmt_update_handler+0x6e> 72f69: R_X86_64_PC32 x86_topology_update-0x5
>
> Presumably that could also happen with R_X86_64_GOTPCREL?
>

In theory, yes.

But for the class of GOTPCREL relaxable instructions listed in the
psABI, the addend is always -4, and these are the only ones we might
expect from the compiler when using -fpic with 'hidden' visibility
and/or -mdirect-extern-access. Note that the memory operand
foo@GOTPCREL(%rip) produces the *address* of foo, and so it is always
the source operand, appearing at the end of the encoding.

Alternatively, we might simply subtract the addend from 'offset'
before applying the displacement from the opcode.

Note that this code gets removed again in the last patch, after
switching to PIE linking.