include/elf/
[binutils-gdb.git] / bfd / elf32-arm.c
index 6f66a7150dff32e7e481dfca56dd60494e841385..580d5900d4ca48f9fc66689685dc67770634315d 100644 (file)
@@ -696,8 +696,8 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_ARM_MOVW_ABS_NC",   /* name */
         FALSE,                 /* partial_inplace */
-        0x0000ffff,            /* src_mask */
-        0x0000ffff,            /* dst_mask */
+        0x000f0fff,            /* src_mask */
+        0x000f0fff,            /* dst_mask */
         FALSE),                /* pcrel_offset */
 
   HOWTO (R_ARM_MOVT_ABS,       /* type */
@@ -710,8 +710,8 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_ARM_MOVT_ABS",      /* name */
         FALSE,                 /* partial_inplace */
-        0x0000ffff,            /* src_mask */
-        0x0000ffff,            /* dst_mask */
+        0x000f0fff,            /* src_mask */
+        0x000f0fff,            /* dst_mask */
         FALSE),                /* pcrel_offset */
 
   HOWTO (R_ARM_MOVW_PREL_NC,   /* type */
@@ -724,8 +724,8 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_ARM_MOVW_PREL_NC",  /* name */
         FALSE,                 /* partial_inplace */
-        0x0000ffff,            /* src_mask */
-        0x0000ffff,            /* dst_mask */
+        0x000f0fff,            /* src_mask */
+        0x000f0fff,            /* dst_mask */
         TRUE),                 /* pcrel_offset */
 
   HOWTO (R_ARM_MOVT_PREL,      /* type */
@@ -738,8 +738,8 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_ARM_MOVT_PREL",     /* name */
         FALSE,                 /* partial_inplace */
-        0x0000ffff,            /* src_mask */
-        0x0000ffff,            /* dst_mask */
+        0x000f0fff,            /* src_mask */
+        0x000f0fff,            /* dst_mask */
         TRUE),                 /* pcrel_offset */
 
   HOWTO (R_ARM_THM_MOVW_ABS_NC,        /* type */
@@ -4070,58 +4070,29 @@ bfd_elf32_arm_set_target_relocs (struct bfd *output_bfd,
   elf_arm_tdata (output_bfd)->no_enum_size_warning = no_enum_warn;
 }
 
-/* The thumb form of a long branch is a bit finicky, because the offset
-   encoding is split over two fields, each in it's own instruction. They
-   can occur in any order. So given a thumb form of long branch, and an
-   offset, insert the offset into the thumb branch and return finished
-   instruction.
+/* Replace the target offset of a Thumb bl or b.w instruction.  */
 
-   It takes two thumb instructions to encode the target address. Each has
-   11 bits to invest. The upper 11 bits are stored in one (identified by
-   H-0.. see below), the lower 11 bits are stored in the other (identified
-   by H-1).
-
-   Combine together and shifted left by 1 (it's a half word address) and
-   there you have it.
-
-   Op: 1111 = F,
-   H-0, upper address-0 = 000
-   Op: 1111 = F,
-   H-1, lower address-0 = 800
-
-   They can be ordered either way, but the arm tools I've seen always put
-   the lower one first. It probably doesn't matter. krk@cygnus.com
-
-   XXX:  Actually the order does matter.  The second instruction (H-1)
-   moves the computed address into the PC, so it must be the second one
-   in the sequence.  The problem, however is that whilst little endian code
-   stores the instructions in HI then LOW order, big endian code does the
-   reverse.  nickc@cygnus.com.  */
-
-#define LOW_HI_ORDER      0xF800F000
-#define HI_LOW_ORDER      0xF000F800
-
-static insn32
-insert_thumb_branch (insn32 br_insn, int rel_off)
+static void
+insert_thumb_branch (bfd *abfd, long int offset, bfd_byte *insn)
 {
-  unsigned int low_bits;
-  unsigned int high_bits;
-
-  BFD_ASSERT ((rel_off & 1) != 1);
-
-  rel_off >>= 1;                               /* Half word aligned address.  */
-  low_bits = rel_off & 0x000007FF;             /* The bottom 11 bits.  */
-  high_bits = (rel_off >> 11) & 0x000007FF;    /* The top 11 bits.  */
-
-  if ((br_insn & LOW_HI_ORDER) == LOW_HI_ORDER)
-    br_insn = LOW_HI_ORDER | (low_bits << 16) | high_bits;
-  else if ((br_insn & HI_LOW_ORDER) == HI_LOW_ORDER)
-    br_insn = HI_LOW_ORDER | (high_bits << 16) | low_bits;
-  else
-    /* FIXME: abort is probably not the right call. krk@cygnus.com  */
-    abort ();  /* Error - not a valid branch instruction form.  */
-
-  return br_insn;
+  bfd_vma upper;
+  bfd_vma lower;
+  int reloc_sign;
+
+  BFD_ASSERT ((offset & 1) == 0);
+
+  upper = bfd_get_16 (abfd, insn);
+  lower = bfd_get_16 (abfd, insn + 2);
+  reloc_sign = (offset < 0) ? 1 : 0;
+  upper = (upper & ~(bfd_vma) 0x7ff)
+         | ((offset >> 12) & 0x3ff)
+         | (reloc_sign << 10);
+  lower = (lower & ~(bfd_vma) 0x2fff) 
+         | (((!((offset >> 23) & 1)) ^ reloc_sign) << 13)
+         | (((!((offset >> 22) & 1)) ^ reloc_sign) << 11)
+         | ((offset >> 1) & 0x7ff);
+  bfd_put_16 (abfd, upper, insn);
+  bfd_put_16 (abfd, lower, insn + 2);
 }
 
 
@@ -4170,7 +4141,6 @@ elf32_thumb_to_arm_stub (struct bfd_link_info * info,
 {
   asection * s = 0;
   bfd_vma my_offset;
-  unsigned long int tmp;
   long int ret_offset;
   struct elf_link_hash_entry * myh;
   struct elf32_arm_link_hash_table * globals;
@@ -4251,12 +4221,7 @@ elf32_thumb_to_arm_stub (struct bfd_link_info * info,
     /* Biassing for PC-relative addressing.  */
     - 8;
 
-  tmp = bfd_get_32 (input_bfd, hit_data
-                   - input_section->vma);
-
-  bfd_put_32 (output_bfd,
-             (bfd_vma) insert_thumb_branch (tmp, ret_offset),
-             hit_data - input_section->vma);
+  insert_thumb_branch (input_bfd, ret_offset, hit_data - input_section->vma);
 
   return TRUE;
 }
@@ -5916,7 +5881,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        if (globals->use_rel)
          {
            addend = ((insn >> 4) & 0xf000) | (insn & 0xfff);
-           signed_addend = (addend ^ 0x10000) - 0x10000;
+           signed_addend = (addend ^ 0x8000) - 0x8000;
          }
 
        value += signed_addend;
@@ -5966,7 +5931,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                   | ((insn >> 15) & 0x0800)
                   | ((insn >> 4)  & 0x0700)
                   | (insn         & 0x00ff);
-           signed_addend = (addend ^ 0x10000) - 0x10000;
+           signed_addend = (addend ^ 0x8000) - 0x8000;
          }
 
        value += signed_addend;
@@ -6548,34 +6513,85 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
                  asection *msec;
                  bfd_vma addend, value;
 
-                 if (howto->rightshift)
+                 switch (r_type)
                    {
-                     (*_bfd_error_handler)
-                       (_("%B(%A+0x%lx): %s relocation against SEC_MERGE section"),
-                        input_bfd, input_section,
-                        (long) rel->r_offset, howto->name);
-                     return FALSE;
-                   }
+                   case R_ARM_MOVW_ABS_NC:
+                   case R_ARM_MOVT_ABS:
+                     value = bfd_get_32 (input_bfd, contents + rel->r_offset);
+                     addend = ((value & 0xf0000) >> 4) | (value & 0xfff);
+                     addend = (addend ^ 0x8000) - 0x8000;
+                     break;
 
-                 value = bfd_get_32 (input_bfd, contents + rel->r_offset);
+                   case R_ARM_THM_MOVW_ABS_NC:
+                   case R_ARM_THM_MOVT_ABS:
+                     value = bfd_get_16 (input_bfd, contents + rel->r_offset)
+                             << 16;
+                     value |= bfd_get_16 (input_bfd,
+                                          contents + rel->r_offset + 2);
+                     addend = ((value & 0xf7000) >> 4) | (value & 0xff)
+                              | ((value & 0x04000000) >> 15);
+                     addend = (addend ^ 0x8000) - 0x8000;
+                     break;
 
-                 /* Get the (signed) value from the instruction.  */
-                 addend = value & howto->src_mask;
-                 if (addend & ((howto->src_mask + 1) >> 1))
-                   {
-                     bfd_signed_vma mask;
+                   default:
+                     if (howto->rightshift
+                         || (howto->src_mask & (howto->src_mask + 1)))
+                       {
+                         (*_bfd_error_handler)
+                           (_("%B(%A+0x%lx): %s relocation against SEC_MERGE section"),
+                            input_bfd, input_section,
+                            (long) rel->r_offset, howto->name);
+                         return FALSE;
+                       }
+
+                     value = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+                     /* Get the (signed) value from the instruction.  */
+                     addend = value & howto->src_mask;
+                     if (addend & ((howto->src_mask + 1) >> 1))
+                       {
+                         bfd_signed_vma mask;
 
-                     mask = -1;
-                     mask &= ~ howto->src_mask;
-                     addend |= mask;
+                         mask = -1;
+                         mask &= ~ howto->src_mask;
+                         addend |= mask;
+                       }
+                     break;
                    }
+
                  msec = sec;
                  addend =
                    _bfd_elf_rel_local_sym (output_bfd, sym, &msec, addend)
                    - relocation;
                  addend += msec->output_section->vma + msec->output_offset;
-                 value = (value & ~ howto->dst_mask) | (addend & howto->dst_mask);
-                 bfd_put_32 (input_bfd, value, contents + rel->r_offset);
+
+                 /* Cases here must match those in the preceeding
+                    switch statement.  */
+                 switch (r_type)
+                   {
+                   case R_ARM_MOVW_ABS_NC:
+                   case R_ARM_MOVT_ABS:
+                     value = (value & 0xfff0f000) | ((addend & 0xf000) << 4)
+                             | (addend & 0xfff);
+                     bfd_put_32 (input_bfd, value, contents + rel->r_offset);
+                     break;
+
+                   case R_ARM_THM_MOVW_ABS_NC:
+                   case R_ARM_THM_MOVT_ABS:
+                     value = (value & 0xfbf08f00) | ((addend & 0xf700) << 4)
+                             | (addend & 0xff) | ((addend & 0x0800) << 15);
+                     bfd_put_16 (input_bfd, value >> 16,
+                                 contents + rel->r_offset);
+                     bfd_put_16 (input_bfd, value,
+                                 contents + rel->r_offset + 2);
+                     break;
+
+                   default:
+                     value = (value & ~ howto->dst_mask)
+                             | (addend & howto->dst_mask);
+                     bfd_put_32 (input_bfd, value, contents + rel->r_offset);
+                     break;
+                   }
                }
            }
          else
@@ -6886,6 +6902,8 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
   /* Some tags have 0 = don't care, 1 = strong requirement,
      2 = weak requirement.  */
   static const int order_312[3] = {3, 1, 2};
+  /* For use with Tag_VFP_arch.  */
+  static const int order_01243[5] = {0, 1, 2, 4, 3};
   int i;
 
   if (!elf_known_obj_attributes_proc (obfd)[0].i)
@@ -6940,7 +6958,6 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
        case Tag_CPU_arch:
        case Tag_ARM_ISA_use:
        case Tag_THUMB_ISA_use:
-       case Tag_VFP_arch:
        case Tag_WMMX_arch:
        case Tag_NEON_arch:
          /* ??? Do NEON and WMMX conflict?  */
@@ -6968,6 +6985,11 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
          if (in_attr[i].i)
            out_attr[i].i = in_attr[i].i;
          break;
+       case Tag_VFP_arch:
+         if (in_attr[i].i > 4 || out_attr[i].i > 4
+             || order_01243[in_attr[i].i] > order_01243[out_attr[i].i])
+           out_attr[i].i = in_attr[i].i;
+         break;
        case Tag_PCS_config:
          if (out_attr[i].i == 0)
            out_attr[i].i = in_attr[i].i;
@@ -7663,6 +7685,7 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
   asection *sreloc;
   bfd_vma *local_got_offsets;
   struct elf32_arm_link_hash_table *htab;
+  bfd_boolean needs_plt;
 
   if (info->relocatable)
     return TRUE;
@@ -7804,10 +7827,6 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
              break;
            /* Fall through */
 
-         case R_ARM_ABS32:
-         case R_ARM_ABS32_NOI:
-         case R_ARM_REL32:
-         case R_ARM_REL32_NOI:
          case R_ARM_PC24:
          case R_ARM_PLT32:
          case R_ARM_CALL:
@@ -7816,6 +7835,13 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
          case R_ARM_THM_CALL:
          case R_ARM_THM_JUMP24:
          case R_ARM_THM_JUMP19:
+           needs_plt = 1;
+           goto normal_reloc;
+
+         case R_ARM_ABS32:
+         case R_ARM_ABS32_NOI:
+         case R_ARM_REL32:
+         case R_ARM_REL32_NOI:
          case R_ARM_MOVW_ABS_NC:
          case R_ARM_MOVT_ABS:
          case R_ARM_MOVW_PREL_NC:
@@ -7824,6 +7850,9 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
          case R_ARM_THM_MOVT_ABS:
          case R_ARM_THM_MOVW_PREL_NC:
          case R_ARM_THM_MOVT_PREL:
+           needs_plt = 0;
+         normal_reloc:
+
            /* Should the interworking branches be listed here?  */
            if (h != NULL)
              {
@@ -7840,11 +7869,7 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
                   refers to is in a different object.  We can't tell for
                   sure yet, because something later might force the
                   symbol local.  */
-               if (r_type != R_ARM_ABS32
-                    && r_type != R_ARM_REL32
-                    && r_type != R_ARM_ABS32_NOI
-                    && r_type != R_ARM_REL32_NOI
-                    && r_type != R_ARM_ABS12)
+               if (needs_plt)
                  h->needs_plt = 1;
 
                /* If we create a PLT entry, this relocation will reference
@@ -8018,7 +8043,9 @@ elf32_arm_gc_mark_extra_sections(struct bfd_link_info *info,
              Elf_Internal_Shdr *hdr;
 
              hdr = &elf_section_data (o)->this_hdr;
-             if (hdr->sh_type == SHT_ARM_EXIDX && hdr->sh_link
+             if (hdr->sh_type == SHT_ARM_EXIDX
+                 && hdr->sh_link
+                 && hdr->sh_link < elf_numsections (sub)
                  && !o->gc_mark
                  && elf_shdrp[hdr->sh_link]->bfd_section->gc_mark)
                {