From 3ee9565c95f49b6f9e80641f6fda51adffc3af1e Mon Sep 17 00:00:00 2001 From: David Faust Date: Thu, 6 Aug 2020 15:14:54 +0200 Subject: 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 * 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 * 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. --- bfd/elf64-bpf.c | 206 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 158 insertions(+), 48 deletions(-) (limited to 'bfd/elf64-bpf.c') diff --git a/bfd/elf64-bpf.c b/bfd/elf64-bpf.c index 641caa1..c6a726d 100644 --- a/bfd/elf64-bpf.c +++ b/bfd/elf64-bpf.c @@ -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 -- cgit v1.1