bpf: relocation fixes for eBPF ELF backend

The eBPF ELF backend was not properly recording relocation addends
during installation, nor reading and applying them when performing
the final relocation. This lead to various issues with incorrect
relocations.

These issues are fixed with a new howto special function to install
the relocations, and updates to bpf_elf_relocate_section to read and
use the addends as recorded in the input_bfd.

bfd/ChangeLog

2020-08-05  David Faust  <david.faust@oracle.com>

	* elf64-bpf.c (bpf_elf_generic_reloc): New function.
	(bpf_elf_howto_table): Use it here.
	(bpf_elf_relocate_section): Use addends recorded in input_bfd for
	instruction and data relocations.

ld/ChangeLog

2020-08-05  David Faust  <david.faust@oracle.com>

	* testsuite/ld-bpf/call-2.s: New file.
	* testsuite/ld-bpf/call-2.d: Likewise.
	* testsuite/ld-bpf/reloc-data-be.d: Likewise.
	* testsuite/ld-bpf/reloc-data-le.d: Likewise.
	* testsuite/ld-bpf/reloc-data.s: Likewise.
	* testsuite/ld-bpf/reloc-insn-external-be.d: Likewise.
	* testsuite/ld-bpf/reloc-insn-external-le.d: Likewise.
	* testsuite/ld-bpf/reloc-insn-external.s: Likewise.
	* testsuite/ld-bpf/reloc-insn32-be.d: Likewise.
	* testsuite/ld-bpf/reloc-insn32-le.d: Likewise.
	* testsuite/ld-bpf/reloc-insn32.s: Likewise.
	* testsuite/ld-bpf/reloc-insn64-be.d: Likewise.
	* testsuite/ld-bpf/reloc-insn64-le.d: Likewise.
	* testsuite/ld-bpf/reloc-insn64.s: Likewise.
This commit is contained in:
Jose E. Marchesi 2020-08-12 15:41:34 +02:00
parent 6ef598a264
commit 9478b1cc94
17 changed files with 371 additions and 48 deletions

View File

@ -3,6 +3,13 @@
* po/ru.po: Updated Russian translation.
* po/sr.po: Updated Serbian translation.
2020-08-05 David Faust <david.faust@oracle.com>
* elf64-bpf.c (bpf_elf_generic_reloc): New function.
(bpf_elf_howto_table): Use it here.
(bpf_elf_relocate_section): Use addends recorded in input_bfd for
instruction and data relocations.
2020-07-30 Szabolcs Nagy <szabolcs.nagy@arm.com>
PR ld/26312

View File

@ -31,6 +31,9 @@
#define BASEADDR(SEC) ((SEC)->output_section->vma + (SEC)->output_offset)
static bfd_reloc_status_type bpf_elf_generic_reloc
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
/* Relocation tables. */
static reloc_howto_type bpf_elf_howto_table [] =
{
@ -42,7 +45,7 @@ static reloc_howto_type bpf_elf_howto_table [] =
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_NONE", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
@ -55,27 +58,27 @@ static reloc_howto_type bpf_elf_howto_table [] =
4, /* size (0 = byte, 1 = short, 2 = long) */
64, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
32, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_INSN_64", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
TRUE, /* partial_inplace */
MINUS_ONE, /* src_mask */
MINUS_ONE, /* dst_mask */
TRUE), /* pcrel_offset */
/* 32-immediate in many instructions. Note: handled manually. */
/* 32-immediate in many instructions. */
HOWTO (R_BPF_INSN_32, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
32, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
32, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_INSN_32", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
TRUE, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
TRUE), /* pcrel_offset */
@ -85,12 +88,12 @@ static reloc_howto_type bpf_elf_howto_table [] =
1, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
16, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_INSN_16", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
TRUE, /* partial_inplace */
0x0000ffff, /* src_mask */
0x0000ffff, /* dst_mask */
TRUE), /* pcrel_offset */
@ -100,11 +103,11 @@ static reloc_howto_type bpf_elf_howto_table [] =
1, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
TRUE, /* pc_relative */
32, /* bitpos */
16, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_INSN_DISP16", /* name */
FALSE, /* partial_inplace */
TRUE, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
TRUE), /* pcrel_offset */
@ -116,10 +119,10 @@ static reloc_howto_type bpf_elf_howto_table [] =
TRUE, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_8_PCREL", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
TRUE, /* partial_inplace */
0xff, /* src_mask */
0xff, /* dst_mask */
TRUE), /* pcrel_offset */
@ -130,10 +133,10 @@ static reloc_howto_type bpf_elf_howto_table [] =
TRUE, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_16_PCREL", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
0xffff, /* src_mask */
0xffff, /* dst_mask */
TRUE), /* pcrel_offset */
@ -144,10 +147,10 @@ static reloc_howto_type bpf_elf_howto_table [] =
TRUE, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_32_PCREL", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
TRUE), /* pcrel_offset */
@ -158,10 +161,10 @@ static reloc_howto_type bpf_elf_howto_table [] =
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_unsigned, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_DATA_8", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
TRUE, /* partial_inplace */
0xff, /* src_mask */
0xff, /* dst_mask */
FALSE), /* pcrel_offset */
@ -172,10 +175,10 @@ static reloc_howto_type bpf_elf_howto_table [] =
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_unsigned, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_DATA_16", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
0xffff, /* src_mask */
0xffff, /* dst_mask */
FALSE), /* pcrel_offset */
@ -185,11 +188,11 @@ static reloc_howto_type bpf_elf_howto_table [] =
2, /* size (0 = byte, 1 = short, 2 = long) */
32, /* bitsize */
TRUE, /* pc_relative */
0, /* bitpos */
32, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_INSN_DISP32", /* name */
FALSE, /* partial_inplace */
TRUE, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
TRUE), /* pcrel_offset */
@ -202,10 +205,10 @@ static reloc_howto_type bpf_elf_howto_table [] =
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_DATA_32", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
TRUE), /* pcrel_offset */
@ -217,7 +220,7 @@ static reloc_howto_type bpf_elf_howto_table [] =
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_DATA_64", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
@ -231,10 +234,10 @@ static reloc_howto_type bpf_elf_howto_table [] =
TRUE, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
bpf_elf_generic_reloc, /* special_function */
"R_BPF_64_PCREL", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
MINUS_ONE, /* src_mask */
MINUS_ONE, /* dst_mask */
TRUE), /* pcrel_offset */
};
@ -391,6 +394,8 @@ bpf_elf_relocate_section (bfd *output_bfd ATTRIBUTE_UNUSED,
bfd_reloc_status_type r;
const char * name = NULL;
int r_type ATTRIBUTE_UNUSED;
bfd_signed_vma addend;
bfd_byte * where;
r_type = ELF64_R_TYPE (rel->r_info);
r_symndx = ELF64_R_SYM (rel->r_info);
@ -398,6 +403,7 @@ bpf_elf_relocate_section (bfd *output_bfd ATTRIBUTE_UNUSED,
h = NULL;
sym = NULL;
sec = NULL;
where = contents + rel->r_offset;
if (r_symndx < symtab_hdr->sh_info)
{
@ -435,8 +441,6 @@ bpf_elf_relocate_section (bfd *output_bfd ATTRIBUTE_UNUSED,
case R_BPF_INSN_DISP16:
case R_BPF_INSN_DISP32:
{
bfd_signed_vma addend;
/* Make the relocation PC-relative, and change its unit to
64-bit words. */
relocation -= sec_addr (input_section) + rel->r_offset;
@ -460,11 +464,35 @@ bpf_elf_relocate_section (bfd *output_bfd ATTRIBUTE_UNUSED,
r = bfd_reloc_ok;
break;
}
case R_BPF_DATA_8:
case R_BPF_DATA_16:
case R_BPF_DATA_32:
case R_BPF_DATA_64:
{
addend = bfd_get (howto->bitsize, input_bfd, where);
relocation += addend;
bfd_put (howto->bitsize, input_bfd, relocation, where);
r = bfd_reloc_ok;
break;
}
case R_BPF_INSN_16:
{
addend = bfd_get_16 (input_bfd, where + 2);
relocation += addend;
bfd_put_16 (input_bfd, relocation, where + 2);
r = bfd_reloc_ok;
break;
}
case R_BPF_INSN_32:
{
/* Write relocated value */
bfd_put (howto->bitsize, input_bfd, relocation,
contents + rel->r_offset + 4);
addend = bfd_get_32 (input_bfd, where + 4);
relocation += addend;
bfd_put_32 (input_bfd, relocation, where + 4);
r = bfd_reloc_ok;
break;
@ -478,19 +506,30 @@ bpf_elf_relocate_section (bfd *output_bfd ATTRIBUTE_UNUSED,
The upper 32 bits of the immediate are stored at the end of
the instruction.
*/
bfd_put (32, input_bfd, (relocation & 0xFFFFFFFF),
contents + rel->r_offset + 4);
bfd_put (32, input_bfd, (relocation >> 32),
contents + rel->r_offset + 12);
/* Get the addend. The upper and lower 32 bits are split.
'where' is the beginning of the 16-byte instruction. */
addend = bfd_get_32 (input_bfd, where + 4);
addend |= (bfd_get_32 (input_bfd, where + 12) << 32);
relocation += addend;
bfd_put_32 (input_bfd, (relocation & 0xFFFFFFFF), where + 4);
bfd_put_32 (input_bfd, (relocation >> 32), where + 12);
r = bfd_reloc_ok;
break;
}
default:
r = _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset, relocation,
rel->r_addend);
r = bfd_reloc_notsupported;
}
if (r == bfd_reloc_ok)
r = bfd_check_overflow (howto->complain_on_overflow,
howto->bitsize,
howto->rightshift,
64, relocation);
if (r != bfd_reloc_ok)
{
const char * msg = NULL;
@ -548,6 +587,77 @@ elf64_bpf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
return TRUE;
}
/* A generic howto special function for installing BPF relocations.
This function will be called by the assembler (via bfd_install_relocation).
At link time, bpf_elf_relocate_section will resolve the final relocations.
BPF instructions are always big endian, and this approach avoids problems in
bfd_install_relocation. */
static bfd_reloc_status_type
bpf_elf_generic_reloc (bfd * abfd, arelent *reloc_entry, asymbol *symbol,
void *data, asection *input_section,
bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
bfd_signed_vma relocation;
bfd_reloc_status_type status;
bfd_byte *where;
/* Sanity check that the address is in range. */
if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
return bfd_reloc_outofrange;
/* Get the symbol value. */
if (bfd_is_com_section (symbol->section))
relocation = 0;
else
relocation = symbol->value;
if (symbol->flags & BSF_SECTION_SYM)
/* Relocation against a section symbol: add in the section base address. */
relocation += BASEADDR (symbol->section);
relocation += reloc_entry->addend;
where = (bfd_byte *) data + reloc_entry->address;
status = bfd_check_overflow (reloc_entry->howto->complain_on_overflow,
reloc_entry->howto->bitsize,
reloc_entry->howto->rightshift, 64, relocation);
if (status != bfd_reloc_ok)
return status;
/* Now finally install the relocation. */
if (reloc_entry->howto->type == R_BPF_INSN_64)
{
/* lddw is a 128-bit (!) instruction that allows loading a 64-bit
immediate into a register. the immediate is split in half, with the
lower 32 bits in the same position as the imm32 field of other
instructions, and the upper 32 bits placed at the very end of the
instruction. that is, there are 32 unused bits between them. */
bfd_put_32 (output_bfd, (relocation & 0xFFFFFFFF), where + 4);
bfd_put_32 (output_bfd, (relocation >> 32), where + 12);
}
else
{
/* For other kinds of relocations, the relocated value simply goes
BITPOS bits from the start of the entry. This is always a multiple
of 8, i.e. whole bytes. */
bfd_put (reloc_entry->howto->bitsize, output_bfd, relocation,
where + reloc_entry->howto->bitpos / 8);
}
reloc_entry->addend = relocation;
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
/* The macros below configure the architecture. */
#define TARGET_LITTLE_SYM bpf_elf64_le_vec

View File

@ -1,3 +1,20 @@
2020-08-05 David Faust <david.faust@oracle.com>
* testsuite/ld-bpf/call-2.s: New file.
* testsuite/ld-bpf/call-2.d: Likewise.
* testsuite/ld-bpf/reloc-data-be.d: Likewise.
* testsuite/ld-bpf/reloc-data-le.d: Likewise.
* testsuite/ld-bpf/reloc-data.s: Likewise.
* testsuite/ld-bpf/reloc-insn-external-be.d: Likewise.
* testsuite/ld-bpf/reloc-insn-external-le.d: Likewise.
* testsuite/ld-bpf/reloc-insn-external.s: Likewise.
* testsuite/ld-bpf/reloc-insn32-be.d: Likewise.
* testsuite/ld-bpf/reloc-insn32-le.d: Likewise.
* testsuite/ld-bpf/reloc-insn32.s: Likewise.
* testsuite/ld-bpf/reloc-insn64-be.d: Likewise.
* testsuite/ld-bpf/reloc-insn64-le.d: Likewise.
* testsuite/ld-bpf/reloc-insn64.s: Likewise.
2020-08-03 Alan Modra <amodra@gmail.com>
* ldlex.l (yy_create_string_buffer): Use xmalloc rather than malloc.

View File

@ -0,0 +1,20 @@
#as: --EL
#source: call-2.s
#source: bar.s
#objdump: -dr
#ld: -EL
#name: CALL with disp32 reloc and addend
.*: +file format .*bpf.*
Disassembly of section .text:
[0-9a-f]+ <.*>:
*[0-9a-f]+: b7 01 00 00 01 00 00 00 mov %r1,1
*[0-9a-f]+: 85 10 00 00 02 00 00 00 call 2
*[0-9a-f]+: 95 00 00 00 00 00 00 00 exit
[0-9a-f]+ <bar>:
*[0-9a-f]+: bf 21 00 00 00 00 00 00 mov %r1,%r2
*[0-9a-f]+: bf 13 00 00 00 00 00 00 mov %r3,%r1
*[0-9a-f]+: 95 00 00 00 00 00 00 00 exit

View File

@ -0,0 +1,3 @@
mov %r1, 1
call bar + 0x1
exit

View File

@ -0,0 +1,11 @@
#as: --EB
#source: reloc-data.s
#objdump: -s
#ld: -Tdata=0x20 -EB
#name: data relocs BE
.*: file format .*-bpfbe
Contents of section \.data:
0020 666f6f00 62617200 00000000 00000020 .*
0030 00000028 003034 .*

View File

@ -0,0 +1,11 @@
#as: --EL
#source: reloc-data.s
#objdump: -s
#ld: -Tdata=0x20 -EL
#name: data relocs LE
.*: file format .*-bpfle
Contents of section \.data:
0020 666f6f00 62617200 20000000 00000000 .*
0030 28000000 300034 .*

View File

@ -0,0 +1,16 @@
.data
foo:
.string "foo"
.global bar
bar:
.string "bar"
d64:
.quad foo
d32:
.word d64
d16:
.half d32
d8:
.byte d16

View File

@ -0,0 +1,16 @@
#as: --EB
#source: reloc-data.s
#source: reloc-insn-external.s
#objdump: -dr
#ld: -Tdata=0x20 -EB
#name: reloc insn external BE
.*: +file format .*bpfbe
Disassembly of section .text:
[0-9a-f]+ <.*>:
*[0-9a-f]+: 18 10 00 00 00 00 00 28[ ]*lddw %r1,0x28
*[0-9a-f]+: 00 00 00 00 00 00 00 00[ ]*
*[0-9a-f]+: b7 20 00 00 00 00 00 26[ ]*mov %r2,0x26
*[0-9a-f]+: 61 34 00 24 00 00 00 00[ ]*ldxw %r3,\[%r4\+0x24\]

View File

@ -0,0 +1,16 @@
#as: --EL
#source: reloc-data.s
#source: reloc-insn-external.s
#objdump: -dr
#ld: -Tdata=0x20 -EL
#name: reloc insn external LE
.*: +file format .*bpfle
Disassembly of section .text:
[0-9a-f]+ <.*>:
*[0-9a-f]+: 18 01 00 00 28 00 00 00[ ]*lddw %r1,0x28
*[0-9a-f]+: 00 00 00 00 00 00 00 00[ ]*
*[0-9a-f]+: b7 02 00 00 26 00 00 00[ ]*mov %r2,0x26
*[0-9a-f]+: 61 43 24 00 00 00 00 00[ ]*ldxw %r3,\[%r4\+0x24\]

View File

@ -0,0 +1,5 @@
lddw %r1, bar + 4
mov %r2, bar + 2
ldxw %r3, [%r4 + bar]

View File

@ -0,0 +1,19 @@
#as: --EB
#source: reloc-insn32.s
#objdump: -d
#ld: -Tdata=0xdabeef -EB
#name: reloc INSN32 BE
.*: +file format .*bpfbe
Disassembly of section .text:
[0-9a-f]+ <main>:
*[0-9a-f]+: b7 10 00 00 00 da be f3[ ]*mov %r1,0xdabef3
*[0-9a-f]+: 16 10 00 02 00 da be f3[ ]*jeq32 %r1,0xdabef3,2
*[0-9a-f]+: 38 00 00 00 00 da be ff[ ]*ldabsdw 0xdabeff
*[0-9a-f]+: 95 00 00 00 00 00 00 00[ ]*exit
[0-9a-f]+ <baz>:
*[0-9a-f]+: 07 10 00 00 00 da be ef[ ]*add %r1,0xdabeef
*[0-9a-f]+: 62 20 00 08 00 da be f7[ ]*stw \[%r2\+8\],0xdabef7

View File

@ -0,0 +1,19 @@
#as: --EL
#source: reloc-insn32.s
#objdump: -d
#ld: -Tdata=0xdabeef -EL
#name: reloc INSN32 LE
.*: +file format .*bpfle
Disassembly of section .text:
[0-9a-f]+ <main>:
*[0-9a-f]+: b7 01 00 00 f3 be da 00[ ]*mov %r1,0xdabef3
*[0-9a-f]+: 16 01 02 00 f3 be da 00[ ]*jeq32 %r1,0xdabef3,2
*[0-9a-f]+: 38 00 00 00 ff be da 00[ ]*ldabsdw 0xdabeff
*[0-9a-f]+: 95 00 00 00 00 00 00 00[ ]*exit
[0-9a-f]+ <baz>:
*[0-9a-f]+: 07 01 00 00 ef be da 00[ ]*add %r1,0xdabeef
*[0-9a-f]+: 62 02 08 00 f7 be da 00[ ]*stw \[%r2\+8\],0xdabef7

View File

@ -0,0 +1,20 @@
.data
foo:
.string "foo"
bar:
.string "bar"
d64:
.dword bar
d32:
.word d64
.text
main:
mov %r1, bar
jeq32 %r1, bar, baz
ldabsdw d32
exit
baz:
add %r1, foo
stw [%r2 + 8], d64

View File

@ -0,0 +1,12 @@
#as: --EB
#source: reloc-insn64.s
#objdump: -s
#ld: -Tdata=0xdeadbeef1234 -EB
#name: reloc INSN64 big-endian
.*: file format .*-bpfbe
#...
Contents of section \.text:
[ ][0-9a-f]+ 18100000 beef1238 00000000 0000dead .*
#pass

View File

@ -0,0 +1,12 @@
#as: --EL
#source: reloc-insn64.s
#objdump: -s
#ld: -Tdata=0xdeadbeef1234 -EL
#name: reloc INSN64 little-endian
.*: file format .*-bpfle
#...
Contents of section \.text:
[ ][0-9a-f]+ 18010000 3812efbe 00000000 adde0000 .*
#pass

View File

@ -0,0 +1,9 @@
.data
x:
.string "foo"
y:
.string "bar"
.text
main:
lddw %r1, y