]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - bfd/elf64-bpf.c
Re: MSP430: Support relocations for subtract expressions in .uleb128 directives
[thirdparty/binutils-gdb.git] / bfd / elf64-bpf.c
index 67c45a1fa449ad3f546d20fe41a38d6b383f3109..d5a160f8f14dfcad4a2562fcf02e2ad786f40bde 100644 (file)
@@ -1,5 +1,5 @@
 /* Linux bpf specific support for 64-bit ELF
-   Copyright (C) 2019 Free Software Foundation, Inc.
+   Copyright (C) 2019-2020 Free Software Foundation, Inc.
    Contributed by Oracle Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
@@ -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 LDDW instruction.  */
+  /* 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,13 +441,12 @@ 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;
-            /* Make it 64-bit words.  */
-            relocation = relocation / 8;
+               64-bit words.  Note we need *signed* arithmetic
+               here.  */
+            relocation = ((bfd_signed_vma) relocation
+                         - (sec_addr (input_section) + rel->r_offset));
+            relocation = (bfd_signed_vma) relocation / 8;
             
             /* Get the addend from the instruction and apply it.  */
             addend = bfd_get (howto->bitsize, input_bfd,
@@ -460,12 +465,72 @@ 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 */
+
+           addend = bfd_get_32 (input_bfd, where + 4);
+           relocation += addend;
+            bfd_put_32 (input_bfd, relocation, where + 4);
+
+            r = bfd_reloc_ok;
+            break;
+          }
+        case R_BPF_INSN_64:
+          {
+            /*
+                LDDW instructions are 128 bits long, with a 64-bit immediate.
+                The lower 32 bits of the immediate are in the same position
+                as the imm32 field of other instructions.
+                The upper 32 bits of the immediate are stored at the end of
+                the instruction.
+             */
+
+
+            /* 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;
@@ -523,6 +588,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