aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf64-bpf.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf64-bpf.c')
-rw-r--r--bfd/elf64-bpf.c206
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