bfd/
[binutils-gdb.git] / bfd / elf32-arm.c
index 9245bd92aeeeae7228e2b68ae819ae1dcb672092..ae52ab2a774af48b181c1fa8d9912c4bca089b70 100644 (file)
@@ -1324,6 +1324,9 @@ struct elf32_arm_link_hash_table
     /* Nonzero to fix BX instructions for ARMv4 targets.  */
     int fix_v4bx;
 
+    /* Nonzero if the ARM/Thumb BLX instructions are available for use.  */
+    int use_blx;
+
     /* The number of bytes in the initial entry in the PLT.  */
     bfd_size_type plt_header_size;
 
@@ -1559,6 +1562,8 @@ elf32_arm_link_hash_table_create (bfd *abfd)
   ret->plt_header_size = 20;
   ret->plt_entry_size = 12;
 #endif
+  ret->fix_v4bx = 0;
+  ret->use_blx = 0;
   ret->symbian_p = 0;
   ret->use_rel = 1;
   ret->sym_sec.abfd = NULL;
@@ -1636,20 +1641,35 @@ find_arm_glue (struct bfd_link_info *link_info,
   return myh;
 }
 
-/* ARM->Thumb glue:
+/* ARM->Thumb glue (static images):
 
    .arm
    __func_from_arm:
    ldr r12, __func_addr
    bx  r12
    __func_addr:
-   .word func    @ behave as if you saw a ARM_32 reloc.  */
+   .word func    @ behave as if you saw a ARM_32 reloc.  
+
+   (relocatable images)
+   .arm
+   __func_from_arm:
+   ldr r12, __func_offset
+   add r12, r12, pc
+   bx  r12
+   __func_offset:
+   .word func - .
+   */
 
-#define ARM2THUMB_GLUE_SIZE 12
+#define ARM2THUMB_STATIC_GLUE_SIZE 12
 static const insn32 a2t1_ldr_insn = 0xe59fc000;
 static const insn32 a2t2_bx_r12_insn = 0xe12fff1c;
 static const insn32 a2t3_func_addr_insn = 0x00000001;
 
+#define ARM2THUMB_PIC_GLUE_SIZE 16
+static const insn32 a2t1p_ldr_insn = 0xe59fc004;
+static const insn32 a2t2p_add_pc_insn = 0xe08cc00f;
+static const insn32 a2t3p_bx_r12_insn = 0xe12fff1c;
+
 /* Thumb->ARM:                          Thumb->(non-interworking aware) ARM
 
    .thumb                               .thumb
@@ -1769,7 +1789,10 @@ record_arm_to_thumb_glue (struct bfd_link_info * link_info,
 
   free (tmp_name);
 
-  globals->arm_glue_size += ARM2THUMB_GLUE_SIZE;
+  if ((link_info->shared || globals->root.is_relocatable_executable))
+    globals->arm_glue_size += ARM2THUMB_PIC_GLUE_SIZE;
+  else
+    globals->arm_glue_size += ARM2THUMB_STATIC_GLUE_SIZE;
 
   return;
 }
@@ -2050,6 +2073,7 @@ bfd_elf32_arm_process_before_allocation (bfd *abfd,
          switch (r_type)
            {
            case R_ARM_PC24:
+           case R_ARM_PLT32:
 #ifndef OLD_ARM_ABI
            case R_ARM_CALL:
            case R_ARM_JUMP24:
@@ -2070,7 +2094,7 @@ bfd_elf32_arm_process_before_allocation (bfd *abfd,
              break;
 
            default:
-             break;
+             abort ();
            }
        }
 
@@ -2107,7 +2131,8 @@ void
 bfd_elf32_arm_set_target_relocs (struct bfd_link_info *link_info,
                                 int target1_is_rel,
                                 char * target2_type,
-                                 int fix_v4bx)
+                                 int fix_v4bx,
+                                int use_blx)
 {
   struct elf32_arm_link_hash_table *globals;
 
@@ -2126,6 +2151,7 @@ bfd_elf32_arm_set_target_relocs (struct bfd_link_info *link_info,
                          target2_type);
     }
   globals->fix_v4bx = fix_v4bx;
+  globals->use_blx |= use_blx;
 }
 #endif
 
@@ -2342,15 +2368,39 @@ elf32_arm_to_thumb_stub (struct bfd_link_info * info,
       --my_offset;
       myh->root.u.def.value = my_offset;
 
-      bfd_put_32 (output_bfd, (bfd_vma) a2t1_ldr_insn,
-                 s->contents + my_offset);
+      if ((info->shared || globals->root.is_relocatable_executable))
+       {
+         /* For relocatable objects we can't use absolute addresses,
+            so construct the address from a relative offset.  */
+         /* TODO: If the offset is small it's probably worth
+            constructing the address with adds.  */
+         bfd_put_32 (output_bfd, (bfd_vma) a2t1p_ldr_insn,
+                     s->contents + my_offset);
+         bfd_put_32 (output_bfd, (bfd_vma) a2t2p_add_pc_insn,
+                     s->contents + my_offset + 4);
+         bfd_put_32 (output_bfd, (bfd_vma) a2t3p_bx_r12_insn,
+                     s->contents + my_offset + 8);
+         /* Adjust the offset by 4 for the position of the add,
+            and 8 for the pipeline offset.  */
+         ret_offset = (val - (s->output_offset
+                              + s->output_section->vma
+                              + my_offset + 12))
+                      | 1;
+         bfd_put_32 (output_bfd, ret_offset,
+                     s->contents + my_offset + 12);
+       }
+      else
+       {
+         bfd_put_32 (output_bfd, (bfd_vma) a2t1_ldr_insn,
+                     s->contents + my_offset);
 
-      bfd_put_32 (output_bfd, (bfd_vma) a2t2_bx_r12_insn,
-                 s->contents + my_offset + 4);
+         bfd_put_32 (output_bfd, (bfd_vma) a2t2_bx_r12_insn,
+                     s->contents + my_offset + 4);
 
-      /* It's a thumb address.  Add the low order bit.  */
-      bfd_put_32 (output_bfd, val | a2t3_func_addr_insn,
-                 s->contents + my_offset + 8);
+         /* It's a thumb address.  Add the low order bit.  */
+         bfd_put_32 (output_bfd, val | a2t3_func_addr_insn,
+                     s->contents + my_offset + 8);
+       }
     }
 
   BFD_ASSERT (my_offset <= globals->arm_glue_size);
@@ -2851,6 +2901,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        bfd_signed_vma reloc_signed_min = ~ reloc_signed_max;
        bfd_vma check;
        bfd_signed_vma signed_check;
+       bfd_boolean thumb_plt_call = FALSE;
 
        /* Need to refetch the addend and squish the two 11 bit pieces
           together.  */
@@ -2900,8 +2951,19 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
            value = (splt->output_section->vma
                     + splt->output_offset
                     + h->plt.offset);
-           /* Target the Thumb stub before the ARM PLT entry.  */
-           value -= 4;
+           if (globals->use_blx)
+             {
+               /* If the Thumb BLX instruction is available, convert the
+                  BL to a BLX instruction to call the ARM-mode PLT entry.  */
+               if ((lower_insn & (0x3 << 11)) == 0x3 << 11)
+                 {
+                   lower_insn = (lower_insn & ~(0x3 << 11)) | 0x1 << 11;
+                   thumb_plt_call = TRUE;
+                 }
+             }
+           else
+             /* Target the Thumb stub before the ARM PLT entry.  */
+             value -= PLT_THUMB_STUB_SIZE;
            *unresolved_reloc_p = FALSE;
          }
 
@@ -2925,8 +2987,9 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
          overflow = TRUE;
 
 #ifndef OLD_ARM_ABI
-       if (r_type == R_ARM_THM_XPC22
-           && ((lower_insn & 0x1800) == 0x0800))
+       if ((r_type == R_ARM_THM_XPC22
+            && ((lower_insn & 0x1800) == 0x0800))
+           || thumb_plt_call)
          /* For a BLX instruction, make sure that the relocation is rounded up
             to a word boundary.  This follows the semantics of the instruction
             which specifies that bit 1 of the target address will come from bit
@@ -3495,6 +3558,7 @@ arm_add_to_rel (bfd *              abfd,
          break;
 
        case R_ARM_PC24:
+       case R_ARM_PLT32:
 #ifndef OLD_ARM_ABI
        case R_ARM_CALL:
        case R_ARM_JUMP24:
@@ -5017,7 +5081,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
 
          /* If we will insert a Thumb trampoline before this PLT, leave room
             for it.  */
-         if (!htab->symbian_p && eh->plt_thumb_refcount > 0)
+         if (!htab->use_blx && eh->plt_thumb_refcount > 0)
            {
              h->plt.offset += PLT_THUMB_STUB_SIZE;
              s->size += PLT_THUMB_STUB_SIZE;
@@ -5590,7 +5654,7 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info,
 
          BFD_ASSERT ((got_displacement & 0xf0000000) == 0);
 
-         if (eh->plt_thumb_refcount > 0)
+         if (!htab->use_blx && eh->plt_thumb_refcount > 0)
            {
              bfd_put_16 (output_bfd, elf32_arm_plt_thumb_stub[0],
                          splt->contents + h->plt.offset - 4);
@@ -6463,6 +6527,8 @@ elf32_arm_symbian_link_hash_table_create (bfd *abfd)
       /* The PLT entries are each three instructions.  */
       htab->plt_entry_size = 4 * NUM_ELEM (elf32_arm_symbian_plt_entry);
       htab->symbian_p = 1;
+      /* Symbian uses armv5t or above, so use_blx is always true.  */
+      htab->use_blx = 1;
       htab->root.is_relocatable_executable = 1;
     }
   return ret;