Fix implementation of R_ARM_PC24 and R_ARM_THM_PC22 relocs to conform to spec.
[deliverable/binutils-gdb.git] / bfd / elf32-arm.h
index d4e97d6b9bf26ef4278d6009deb09d8ab9559bef..0cf2f9ce4436588be868ec8436f8930e0891f6cc 100644 (file)
@@ -34,7 +34,9 @@ static int elf32_arm_get_symbol_type
 static struct bfd_link_hash_table *elf32_arm_link_hash_table_create
   PARAMS ((bfd *));
 static bfd_reloc_status_type elf32_arm_final_link_relocate
-  PARAMS ((reloc_howto_type *, bfd *, bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, bfd_vma, struct bfd_link_info *, asection *, const char *, unsigned char));
+  PARAMS ((reloc_howto_type *, bfd *, bfd *, asection *, bfd_byte *,
+          Elf_Internal_Rela *, bfd_vma, struct bfd_link_info *, asection *,
+          const char *, unsigned char, struct elf_link_hash_entry *));
 
 static insn32 insert_thumb_branch
   PARAMS ((insn32, int));
@@ -46,6 +48,8 @@ static void record_arm_to_thumb_glue
   PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
 static void record_thumb_to_arm_glue
   PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
+static void elf32_arm_post_process_headers
+  PARAMS ((bfd *, struct bfd_link_info *));
 
 /* The linker script knows the section names for placement.
    The entry_names are used to do simple name mangling on the stubs.
@@ -113,7 +117,7 @@ struct elf32_arm_pcrel_relocs_copied
   bfd_size_type count;
 };
 
-/* arm ELF linker hash entry.  */
+/* Arm ELF linker hash entry.  */
 
 struct elf32_arm_link_hash_entry
 {
@@ -154,9 +158,43 @@ struct elf32_arm_link_hash_table
 
     /* An arbitary input BFD chosen to hold the glue sections.  */
     bfd * bfd_of_glue_owner;
+
+    /* A boolean indicating whether knowledge of the ARM's pipeline
+       length should be applied by the linker.  */
+    int no_pipeline_knowledge;
   };
 
 
+/* Create an entry in an ARM ELF linker hash table.  */
+
+static struct bfd_hash_entry *
+elf32_arm_link_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry * entry;
+     struct bfd_hash_table * table;
+     const char * string;
+{
+  struct elf32_arm_link_hash_entry * ret =
+    (struct elf32_arm_link_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct elf32_arm_link_hash_entry *) NULL)
+    ret = ((struct elf32_arm_link_hash_entry *)
+          bfd_hash_allocate (table,
+                             sizeof (struct elf32_arm_link_hash_entry)));
+  if (ret == (struct elf32_arm_link_hash_entry *) NULL)
+    return (struct bfd_hash_entry *) ret;
+
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct elf32_arm_link_hash_entry *)
+        _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret,
+                                    table, string));
+  if (ret != (struct elf32_arm_link_hash_entry *) NULL)
+    ret->pcrel_relocs_copied = NULL;
+
+  return (struct bfd_hash_entry *) ret;
+}
+
 /* Create an ARM elf linker hash table */
 
 static struct bfd_link_hash_table *
@@ -171,7 +209,7 @@ elf32_arm_link_hash_table_create (abfd)
     return NULL;
 
   if (!_bfd_elf_link_hash_table_init (&ret->root, abfd,
-                                     _bfd_elf_link_hash_newfunc))
+                                     elf32_arm_link_hash_newfunc))
     {
       bfd_release (abfd, ret);
       return NULL;
@@ -180,6 +218,7 @@ elf32_arm_link_hash_table_create (abfd)
   ret->thumb_glue_size = 0;
   ret->arm_glue_size = 0;
   ret->bfd_of_glue_owner = NULL;
+  ret->no_pipeline_knowledge = 0;
 
   return &ret->root.root;
 }
@@ -531,9 +570,10 @@ bfd_elf32_arm_get_bfd_for_interworking (abfd, info)
 }
 
 boolean
-bfd_elf32_arm_process_before_allocation (abfd, link_info)
+bfd_elf32_arm_process_before_allocation (abfd, link_info, no_pipeline_knowledge)
      bfd *abfd;
      struct bfd_link_info *link_info;
+     int no_pipeline_knowledge;
 {
   Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Rela *free_relocs = NULL;
@@ -559,6 +599,8 @@ bfd_elf32_arm_process_before_allocation (abfd, link_info)
   BFD_ASSERT (globals != NULL);
   BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
 
+  globals->no_pipeline_knowledge = no_pipeline_knowledge;
+  
   /* Rummage around all the relocs and map the glue vectors.  */
   sec = abfd->sections;
 
@@ -591,7 +633,7 @@ bfd_elf32_arm_process_before_allocation (abfd, link_info)
          r_index = ELF32_R_SYM (irel->r_info);
 
          /* These are the only relocation types we care about */
-         if (r_type != R_ARM_PC24
+         if (   r_type != R_ARM_PC24
              && r_type != R_ARM_THM_PC22)
            continue;
 
@@ -943,7 +985,7 @@ elf32_arm_to_thumb_stub (info, name, input_bfd, output_bfd, input_section,
 static bfd_reloc_status_type
 elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
                               input_section, contents, rel, value,
-                              info, sym_sec, sym_name, sym_flags)
+                              info, sym_sec, sym_name, sym_flags, h)
      reloc_howto_type *     howto;
      bfd *                  input_bfd;
      bfd *                  output_bfd;
@@ -955,6 +997,7 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
      asection *             sym_sec;
      const char *           sym_name;
      unsigned char          sym_flags;
+     struct elf_link_hash_entry * h;
 {
   unsigned long                 r_type = howto->type;
   unsigned long                 r_symndx;
@@ -966,8 +1009,11 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
   asection *                    sgot = NULL;
   asection *                    splt = NULL;
   asection *                    sreloc = NULL;
-  struct elf_link_hash_entry *  h = NULL;
   bfd_vma                       addend;
+  bfd_signed_vma                signed_addend;
+  struct elf32_arm_link_hash_table * globals;
+  
+  globals = elf32_arm_hash_table (info);
     
   dynobj = elf_hash_table (info)->dynobj;
   if (dynobj)
@@ -981,9 +1027,18 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
   r_symndx = ELF32_R_SYM (rel->r_info);
 
 #ifdef USE_REL
-  addend = (bfd_get_32 (input_bfd, hit_data) & howto->src_mask);
+  addend = bfd_get_32 (input_bfd, hit_data) & howto->src_mask;
+
+  if (addend & ((howto->src_mask + 1) >> 1))
+    {
+      signed_addend = -1;
+      signed_addend &= ~ howto->src_mask;
+      signed_addend |= addend;
+    }
+  else
+    signed_addend = addend;
 #else
-  addend = rel->r_addend;
+  addend = signed_addend = rel->r_addend;
 #endif
   
   switch (r_type)
@@ -1112,15 +1167,66 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
                                       input_section, hit_data, sym_sec, rel->r_offset, addend, value);
              return bfd_reloc_ok;
            }
+
+         if (   strcmp (bfd_get_target (input_bfd), "elf32-littlearm-oabi") == 0
+             || strcmp (bfd_get_target (input_bfd), "elf32-bigarm-oabi") == 0)
+           {
+             /* The old way of doing things.  Trearing the addend as a
+                byte sized field and adding in the pipeline offset.  */
+             
+             value -= (input_section->output_section->vma
+                       + input_section->output_offset);
+             value -= rel->r_offset;
+             value += addend;
+             
+             if (! globals->no_pipeline_knowledge)
+               value -= 8;
+           }
+         else
+           {
+             /* The ARM ELF ABI says that this reloc is computed as: S - P + A
+                where:
+                 S is the address of the symbol in the relocation.
+                 P is address of the instruction being relocated.
+                 A is the addend (extracted from the instruction) in bytes.
+                
+                S is held in 'value'.
+                P is the base address of the section containing the instruction
+                  plus the offset of the reloc into that section, ie:
+                    (input_section->output_section->vma +
+                     input_section->output_offset +
+                     rel->r_offset).
+                A is the addend, converted into bytes, ie:
+                    (signed_addend * 4)
+
+                Note: None of these operations have knowledge of the pipeline
+                size of the processor, thus it is up to the assembler to encode
+                this information into the addend.  */
+
+             value -= (input_section->output_section->vma
+                       + input_section->output_offset);
+             value -= rel->r_offset;
+             value += (signed_addend << howto->size);
+             
+             /* Previous versions of this code also used to add in the pipeline
+                offset here.  This is wrong because the linker is not supposed
+                to know about such things, and one day it might change.  In order
+                to support old binaries that need the old behaviour however, so
+                we attempt to detect which ABI was used to create the reloc.  */
+             if (! globals->no_pipeline_knowledge)
+               { 
+                 Elf_Internal_Ehdr * i_ehdrp; /* Elf file header, internal form */
+                 
+                 i_ehdrp = elf_elfheader (input_bfd);
+                 
+                 if (i_ehdrp->e_ident[EI_OSABI] == 0)
+                   value -= 8;
+               }
+           }
          
-         value = value + addend;
-         value -= (input_section->output_section->vma
-                   + input_section->output_offset + 8);
-         value -= rel->r_offset;
-         value = value >> howto->rightshift;
-         
-         value &= 0xffffff;
-         value |= (bfd_get_32 (input_bfd, hit_data) & 0xff000000);
+         value >>= howto->rightshift;    
+         value &= howto->dst_mask;
+         value |= (bfd_get_32 (input_bfd, hit_data) & (~ howto->dst_mask));
          break;
          
        case R_ARM_ABS32:
@@ -1190,26 +1296,25 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
     case R_ARM_THM_PC22:
       /* Thumb BL (branch long instruction). */
       {
-       bfd_vma relocation;
-       boolean overflow = false;
-       bfd_vma upper_insn = bfd_get_16 (input_bfd, hit_data);
-       bfd_vma lower_insn = bfd_get_16 (input_bfd, hit_data + 2);
-       bfd_vma src_mask = 0x007FFFFE;
+       bfd_vma        relocation;
+       boolean        overflow = false;
+       bfd_vma        upper_insn = bfd_get_16 (input_bfd, hit_data);
+       bfd_vma        lower_insn = bfd_get_16 (input_bfd, hit_data + 2);
+       bfd_vma        src_mask = 0x007FFFFE;
        bfd_signed_vma reloc_signed_max = (1 << (howto->bitsize - 1)) - 1;
-       bfd_signed_vma reloc_signed_min = ~reloc_signed_max;
-       bfd_vma check;
+       bfd_signed_vma reloc_signed_min = ~ reloc_signed_max;
+       bfd_vma        check;
        bfd_signed_vma signed_check;
-       bfd_vma add;
-       bfd_signed_vma signed_add;
 
 #ifdef USE_REL
        /* Need to refetch the addend and squish the two 11 bit pieces
           together.  */
        {
-         bfd_vma upper = bfd_get_16 (input_bfd, hit_data) & 0x7ff;
-         bfd_vma lower = bfd_get_16 (input_bfd, hit_data + 2) & 0x7ff;
+         bfd_vma upper = upper_insn & 0x7ff;
+         bfd_vma lower = lower_insn & 0x7ff;
          upper = (upper ^ 0x400) - 0x400; /* sign extend */
          addend = (upper << 12) | (lower << 1);
+         signed_addend = addend;
        }
 #endif
 
@@ -1223,13 +1328,30 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
            else
              return bfd_reloc_dangerous;
          }
-
-       /* +4: pc is offset by 4 */
-       relocation = value + addend + 4;
+       
+       relocation = value + signed_addend;
+       
        relocation -= (input_section->output_section->vma
-                      + input_section->output_offset);
-       relocation -= rel->r_offset;
-
+                      + input_section->output_offset
+                      + rel->r_offset);
+      
+       if (! globals->no_pipeline_knowledge)
+         {
+           Elf_Internal_Ehdr * i_ehdrp; /* Elf file header, internal form */
+               
+           i_ehdrp = elf_elfheader (input_bfd);
+           
+           /* Previous versions of this code also used to add in the pipline
+              offset here.  This is wrong because the linker is not supposed
+              to know about such things, and one day it might change.  In order
+              to support old binaries that need the old behaviour however, so
+              we attempt to detect which ABI was used to create the reloc.  */
+           if (   strcmp (bfd_get_target (input_bfd), "elf32-littlearm-oabi") == 0
+               || strcmp (bfd_get_target (input_bfd), "elf32-bigarm-oabi") == 0
+               || i_ehdrp->e_ident[EI_OSABI] == 0)
+             relocation += 4;
+         }
+       
        check = relocation >> howto->rightshift;
 
        /* If this is a signed value, the rightshift just dropped
@@ -1239,17 +1361,8 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
        else
          signed_check = check | ~((bfd_vma) -1 >> howto->rightshift);
 
-       add = ((upper_insn & 0x7ff) << 12) | ((lower_insn & 0x7ff) << 1);
-       /* sign extend */
-       signed_add = (add ^ 0x400000) - 0x400000;
-
-       /* Add the value from the object file.  */
-       signed_check += signed_add;
-       relocation += signed_add;
-
        /* Assumes two's complement.  */
-       if (signed_check > reloc_signed_max
-           || signed_check < reloc_signed_min)
+       if (signed_check > reloc_signed_max || signed_check < reloc_signed_min)
          overflow = true;
 
        /* Put RELOCATION back into the insn.  */
@@ -1483,25 +1596,25 @@ elf32_arm_relocate_section (output_bfd, info, input_bfd, input_section,
   relend = relocs + input_section->reloc_count;
   for (; rel < relend; rel++)
     {
-      int r_type;
-      reloc_howto_type * howto;
-      unsigned long r_symndx;
-      Elf_Internal_Sym * sym;
-      asection * sec;
+      int                          r_type;
+      reloc_howto_type *           howto;
+      unsigned long                r_symndx;
+      Elf_Internal_Sym *           sym;
+      asection *                   sec;
       struct elf_link_hash_entry * h;
-      bfd_vma relocation;
-      bfd_reloc_status_type r;
-
+      bfd_vma                      relocation;
+      bfd_reloc_status_type        r;
+      arelent                      bfd_reloc;
+      
       r_symndx = ELF32_R_SYM (rel->r_info);
-      r_type = ELF32_R_TYPE (rel->r_info);
+      r_type   = ELF32_R_TYPE (rel->r_info);
 
-      if (r_type == R_ARM_GNU_VTENTRY
-          || r_type == R_ARM_GNU_VTINHERIT )
+      if (   r_type == R_ARM_GNU_VTENTRY
+          || r_type == R_ARM_GNU_VTINHERIT)
         continue;
 
-      /* ScottB: range check r_type here. */
-      
-      howto = elf32_arm_howto_table + r_type;
+      elf32_arm_info_to_howto (input_bfd, & bfd_reloc, rel);
+      howto = bfd_reloc.howto;
 
       if (info->relocateable)
        {
@@ -1519,7 +1632,7 @@ elf32_arm_relocate_section (output_bfd, info, input_bfd, input_section,
                  {
                    bfd_vma val;
                    val = bfd_get_32 (input_bfd, contents + rel->r_offset);
-                   val += (sec->output_offset + sym->st_value) >> howto->rightshift;
+                   val += (sec->output_offset + sym->st_value);
                    bfd_put_32 (input_bfd, val, contents + rel->r_offset);
                  }
 #else
@@ -1553,11 +1666,9 @@ elf32_arm_relocate_section (output_bfd, info, input_bfd, input_section,
          if (h->root.type == bfd_link_hash_defined
              || h->root.type == bfd_link_hash_defweak)
            {
-             sec = h->root.u.def.section;
+             int relocation_needed = 1;
              
-              relocation = (h->root.u.def.value
-                          + sec->output_section->vma
-                          + sec->output_offset);
+             sec = h->root.u.def.section;
              
              /* In these cases, we don't need the relocation value.
                 We check specially because in some obscure cases 
@@ -1569,15 +1680,15 @@ elf32_arm_relocate_section (output_bfd, info, input_bfd, input_section,
                  if (info->shared
                      && (
                          (!info->symbolic && h->dynindx != -1)
-                         || (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR == 0)
+                         || (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
                          )
                      && ((input_section->flags & SEC_ALLOC) != 0)
                      )
-                   relocation = 0;
+                   relocation_needed = 0;
                  break;
                  
                case R_ARM_GOTPC:
-                 relocation = 0;
+                 relocation_needed = 0;
                  break;
                  
                case R_ARM_GOT32:
@@ -1587,12 +1698,12 @@ elf32_arm_relocate_section (output_bfd, info, input_bfd, input_section,
                          || (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
                          )
                      )
-                   relocation = 0;
+                   relocation_needed = 0;
                  break;
                  
                case R_ARM_PLT32:
                  if (h->plt.offset != (bfd_vma)-1)
-                   relocation = 0;
+                   relocation_needed = 0;
                  break;
                  
                default:
@@ -1602,9 +1713,16 @@ elf32_arm_relocate_section (output_bfd, info, input_bfd, input_section,
                        (_("%s: warning: unresolvable relocation against symbol `%s' from %s section"),
                         bfd_get_filename (input_bfd), h->root.root.string,
                         bfd_get_section_name (input_bfd, input_section));
-                     relocation = 0;
+                     relocation_needed = 0;
                    }
                }
+
+             if (relocation_needed)
+               relocation = h->root.u.def.value
+                 + sec->output_section->vma
+                 + sec->output_offset;
+             else
+               relocation = 0;
            }
          else if (h->root.type == bfd_link_hash_undefweak)
            relocation = 0;
@@ -1632,7 +1750,7 @@ elf32_arm_relocate_section (output_bfd, info, input_bfd, input_section,
                                         input_section, contents, rel,
                                         relocation, info, sec, name,
                                         (h ? ELF_ST_TYPE (h->type) :
-                                         ELF_ST_TYPE (sym->st_info)));
+                                         ELF_ST_TYPE (sym->st_info)), h);
 
       if (r != bfd_reloc_ok)
        {
@@ -1957,6 +2075,8 @@ elf32_arm_gc_mark_hook (abfd, info, rel, h, sym)
   return NULL;
 }
 
+/* Update the got entry reference counts for the section being removed.  */
+
 static boolean
 elf32_arm_gc_sweep_hook (abfd, info, sec, relocs)
      bfd *abfd;
@@ -1964,14 +2084,12 @@ elf32_arm_gc_sweep_hook (abfd, info, sec, relocs)
      asection *sec;
      const Elf_Internal_Rela *relocs;
 {
-  /* we don't use got and plt entries for armelf */
+  /* We don't support garbage collection of GOT and PLT relocs yet.  */
   return true;
 }
 
-/* Look through the relocs for a section during the first phase.
-   Since we don't do .gots or .plts, we just need to consider the
-   virtual table relocs for gc.  */
+/* Look through the relocs for a section during the first phase.  */
+
 static boolean
 elf32_arm_check_relocs (abfd, info, sec, relocs)
      bfd *                      abfd;
@@ -2978,6 +3096,20 @@ elf32_arm_finish_dynamic_sections (output_bfd, info)
   return true;
 }
 
+static void
+elf32_arm_post_process_headers (abfd, link_info)
+     bfd * abfd;
+     struct bfd_link_info * link_info;
+{
+  Elf_Internal_Ehdr * i_ehdrp; /* Elf file header, internal form */
+
+  i_ehdrp = elf_elfheader (abfd);
+
+  i_ehdrp->e_ident[EI_OSABI]      = ARM_ELF_OS_ABI_VERSION;
+  i_ehdrp->e_ident[EI_ABIVERSION] = ARM_ELF_ABI_VERSION;
+}
+
+
 #define ELF_ARCH                       bfd_arch_arm
 #define ELF_MACHINE_CODE               EM_ARM
 #define ELF_MAXPAGE_SIZE               0x8000
@@ -3001,6 +3133,7 @@ elf32_arm_finish_dynamic_sections (output_bfd, info)
 #define elf_backend_finish_dynamic_symbol      elf32_arm_finish_dynamic_symbol
 #define elf_backend_finish_dynamic_sections    elf32_arm_finish_dynamic_sections
 #define elf_backend_size_dynamic_sections      elf32_arm_size_dynamic_sections
+#define elf_backend_post_process_headers       elf32_arm_post_process_headers
 
 #define elf_backend_can_gc_sections 1
 #define elf_backend_plt_readonly    1
This page took 0.029242 seconds and 4 git commands to generate.