diff --git a/elf/arch/x86_64.rs b/elf/arch/x86_64.rs index 60e2f45e6..719cc146b 100644 --- a/elf/arch/x86_64.rs +++ b/elf/arch/x86_64.rs @@ -54,7 +54,7 @@ impl Target for X86_64 { const FAMILY: Family = Family::X86_64; const PAGE_SIZE: u64 = 4096; const E_MACHINE: u32 = EM_X86_64; - const PLTGOT_SIZE: u64 = 8; + const PLTGOT_SIZE: u64 = 16; const SFRAME_ABI: Option = Some(SFRAME_ABI_AMD64_ENDIAN_LITTLE); const TRAP: &'static [u8] = &[0xcc]; // int3 @@ -136,14 +136,27 @@ impl Target for X86_64 { } } + // A .plt.got entry is used as a function's address if it belongs to an + // ifunc in a position-dependent executable, so such an entry starts with + // endbr64. fn write_pltgot_entry(ctx: &Context, buf: &mut [u8], sym: &Symbol) { - const INSN: [u8; 8] = [ - 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT - 0xcc, 0xcc, // (padding) - ]; - buf[..8].copy_from_slice(&INSN); - let disp = sym.got_pltgot_addr(ctx).wrapping_sub(sym.plt_addr(ctx)).wrapping_sub(6); - write_ul32(&mut buf[2..], disp as u32); + let disp = sym.got_pltgot_addr(ctx).wrapping_sub(sym.plt_addr(ctx)); + if sym.is_pde_ifunc(ctx) { + const INSN: [u8; 16] = [ + 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 + 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding) + ]; + buf[..16].copy_from_slice(&INSN); + write_ul32(&mut buf[6..], disp.wrapping_sub(10) as u32); + } else { + const INSN: [u8; 16] = [ + 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding) + ]; + buf[..16].copy_from_slice(&INSN); + write_ul32(&mut buf[2..], disp.wrapping_sub(6) as u32); + } } fn apply_eh_reloc( diff --git a/tests/elf/arch-x86_64-ifunc-endbr.sh b/tests/elf/arch-x86_64-ifunc-endbr.sh new file mode 100755 index 000000000..60796f662 --- /dev/null +++ b/tests/elf/arch-x86_64-ifunc-endbr.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +. $(dirname $0)/common.inc + +supports_ifunc || skip + +# In a position-dependent executable, the address of an ifunc is that of +# its .plt.got entry, so the entry has to start with endbr64 for IBT. +cat < + +static int real_foo(void) { return 3; } +static void *resolve_foo(void) { return real_foo; } +int foo(void) __attribute__((ifunc("resolve_foo"))); + +int (*fp)(void) = foo; + +int main() { + printf("%d %d\n", foo(), fp()); +} +EOF + +$CC -B. -no-pie -o $t/exe $t/a.o +$QEMU $t/exe | grep '^3 3$' +$OBJDUMP -d -j .plt.got $t/exe | grep -A1 ':' | grep -w endbr64