diff options
Diffstat (limited to 'bfd/elf64-bpf.c')
-rw-r--r-- | bfd/elf64-bpf.c | 206 |
1 files changed, 158 insertions, 48 deletions
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 |