OSDN Git Service

bfd/
authornickc <nickc>
Thu, 15 May 2008 17:00:11 +0000 (17:00 +0000)
committernickc <nickc>
Thu, 15 May 2008 17:00:11 +0000 (17:00 +0000)
        * elf32-arm.c (THM2_MAX_FWD_BRANCH_OFFSET): Define.
        (THM2_MAX_BWD_BRANCH_OFFSET): Define.
        (ARM_MAX_FWD_BRANCH_OFFSET): Define.
        (ARM_MAX_BWD_BRANCH_OFFSET): Define.
        (THM_MAX_FWD_BRANCH_OFFSET): Define.
        (THM_MAX_BWD_BRANCH_OFFSET): Define.
        (arm_long_branch_stub): Define.
        (arm_pic_long_branch_stub): Define.
        (arm_thumb_v4t_long_branch_stub): Define.
        (arm_thumb_thumb_long_branch_stub): Define.
        (arm_thumb_arm_v4t_long_branch_stub): Define.
        (STUB_SUFFIX): Define.
        (elf32_arm_stub_type): Define.
        (elf32_arm_stub_hash_entry): Define.
        (elf32_arm_link_hash_entry): Add stub_cache field.
        (arm_stub_hash_lookup): Define.
        (elf32_arm_link_hash_table): Add stub_hash_table, stub_bfd,
        add_stub_section, layout_sections_again, stub_group, bfd_count,
        top_index, input_list fields.
        (elf32_arm_link_hash_newfunc): Init new field.
        (stub_hash_newfunc): New function.
        (elf32_arm_link_hash_table_create): Init stub_hash_table.
        (elf32_arm_hash_table_free): New function.
        (arm_type_of_stub): New function.
        (elf32_arm_stub_name): New function.
        (elf32_arm_get_stub_entry): New function.
        (elf32_arm_stub_add_mapping_symbol): New function.
        (elf32_arm_add_stub): New function.
        (arm_build_one_stub): New function.
        (arm_size_one_stub): New function.
        (elf32_arm_setup_section_lists): New function.
        (elf32_arm_next_input_section): New function.
        (group_sections): New function.
        (elf32_arm_size_stubs): New function.
        (elf32_arm_build_stubs): New function.
        (bfd_elf32_arm_add_glue_sections_to_bfd): Skip stub sections.
        (bfd_elf32_arm_process_before_allocation): No longer handle
        R_ARM_CALL and R_ARM_THM_CALL.
        (using_thumb_only): New function.
        (elf32_arm_final_link_relocate): Redirect calls to stub if range
        exceeds encoding capabilities.
        (bfd_elf32_bfd_link_hash_table_free): Define.
        * bfd-in.h (R_ARM_max): Fix value to 130.
        (elf32_arm_setup_section_lists): Protype.
        (elf32_arm_next_input_section): Protype.
        (elf32_arm_size_stubs): Protype.
        (elf32_arm_build_stubs): Protype.

        ld/
        * emultempl/armelf.em (build_section_lists): New function.
        (stub_file): Define.
        (need_laying_out): Define.
        (group_size): Define.
        (hook_stub_info): Define.
        (hook_in_stub): New function.
        (elf32_arm_add_stub_section): New function.
        (gldarm_layout_sections_again): New function.
        (gld${EMULATION_NAME}_finish): Replace arm_elf_finish(). Generate
        stubs for long calls if needed.
        (arm_elf_create_output_section_statements): create stub_file bfd.
        (arm_for_each_input_file_wrapper): New function.
        (arm_lang_for_each_input_file): New function.
        (lang_for_each_input_file): Define.
        (PARSE_AND_LIST_PROLOGUE): Add option token OPTION_STUBGROUP_SIZE.
        (PARSE_AND_LIST_LONGOPTS): Add option stub-group-size.
        (PARSE_AND_LIST_OPTIONS): Add option stub-group-size.
        (PARSE_AND_LIST_ARGS_CASES): Add OPTION_STUBGROUP_SIZE case.
        (LDEMUL_FINISH): Update to gld${EMULATION_NAME}_finish.
        * ld/lang.c (print_input_statement): Skip if bfd has
        BFD_LINKER_CREATED.

        ld/testsuite
        * ld-arm/arm-elf.exp (armelftests): Add farcall-arm-arm,
        farcall-arm-arm-pic-veneer, farcall-arm-arm-be8 farcall-arm-thumb,
        farcall-arm-thumb-blx, farcall-arm-thumb-pic-veneer,
        farcall-arm-thumb-blx-pic-veneer, farcall-thumb-thumb,
        farcall-thumb-thumb-pic-veneer, farcall-thumb-thumb-blx,
        farcall-thumb-thumb-m, farcall-thumb-thumb-m-pic-veneer,
        farcall-thumb-thumb-blx-pic-veneer, farcall-thumb-arm,
        farcall-thumb-arm-pic-veneer, farcall-thumb-arm-blx,
        farcall-thumb-arm-blx-pic-veneer.
        Change thumb2-bl-as-thumb1-bad, thumb2-bl-bad.
        * ld-arm/thumb2-bl-as-thumb1-bad.d: Reflects farcall stub
        generation.
        * ld-arm/thumb2-bl-bad.d: Likewise.
        * ld-arm/thumb2-bl-as-thumb1-bad.s: Update comments.
        * ld-arm/thumb2-bl-bad.s: Likewise.

35 files changed:
bfd/ChangeLog
bfd/bfd-in.h
bfd/bfd-in2.h
bfd/elf32-arm.c
bfd/elf32-frv.c
ld/ChangeLog
ld/emultempl/armelf.em
ld/ldlang.c
ld/testsuite/ChangeLog
ld/testsuite/ld-arm/arm-elf.exp
ld/testsuite/ld-arm/farcall-arm-arm-be8.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-arm-arm-pic-veneer.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-arm-arm.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-arm-arm.s [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-arm-thumb-blx-pic-veneer.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-arm-thumb-blx.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-arm-thumb-pic-veneer.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-arm-thumb.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-arm-thumb.s [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-thumb-arm-blx-pic-veneer.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-thumb-arm-blx.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-thumb-arm-pic-veneer.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-thumb-arm.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-thumb-arm.s [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-thumb-thumb-blx-pic-veneer.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-thumb-thumb-blx.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-thumb-thumb-m-pic-veneer.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-thumb-thumb-m.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-thumb-thumb-pic-veneer.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-thumb-thumb.d [new file with mode: 0644]
ld/testsuite/ld-arm/farcall-thumb-thumb.s [new file with mode: 0644]
ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.d
ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.s
ld/testsuite/ld-arm/thumb2-bl-bad.d
ld/testsuite/ld-arm/thumb2-bl-bad.s

index 7f8f329..8246fa7 100644 (file)
@@ -1,3 +1,55 @@
+2008-05-15  Christophe Lyon  <christophe.lyon@st.com>
+
+       Add long call support for ARM.
+       * elf32-arm.c (THM2_MAX_FWD_BRANCH_OFFSET): Define.
+       (THM2_MAX_BWD_BRANCH_OFFSET): Define.
+       (ARM_MAX_FWD_BRANCH_OFFSET): Define.
+       (ARM_MAX_BWD_BRANCH_OFFSET): Define.
+       (THM_MAX_FWD_BRANCH_OFFSET): Define.
+       (THM_MAX_BWD_BRANCH_OFFSET): Define.
+       (arm_long_branch_stub): Define.
+       (arm_pic_long_branch_stub): Define.
+       (arm_thumb_v4t_long_branch_stub): Define.
+       (arm_thumb_thumb_long_branch_stub): Define.
+       (arm_thumb_arm_v4t_long_branch_stub): Define.
+       (STUB_SUFFIX): Define.
+       (elf32_arm_stub_type): Define.
+       (elf32_arm_stub_hash_entry): Define.
+       (elf32_arm_link_hash_entry): Add stub_cache field.
+       (arm_stub_hash_lookup): Define.
+       (elf32_arm_link_hash_table): Add stub_hash_table, stub_bfd,
+       add_stub_section, layout_sections_again, stub_group, bfd_count,
+       top_index, input_list fields.
+       (elf32_arm_link_hash_newfunc): Init new field.
+       (stub_hash_newfunc): New function.
+       (elf32_arm_link_hash_table_create): Init stub_hash_table.
+       (elf32_arm_hash_table_free): New function.
+       (arm_type_of_stub): New function.
+       (elf32_arm_stub_name): New function.
+       (elf32_arm_get_stub_entry): New function.
+       (elf32_arm_stub_add_mapping_symbol): New function.
+       (elf32_arm_add_stub): New function.
+       (arm_build_one_stub): New function.
+       (arm_size_one_stub): New function.
+       (elf32_arm_setup_section_lists): New function.
+       (elf32_arm_next_input_section): New function.
+       (group_sections): New function.
+       (elf32_arm_size_stubs): New function.
+       (elf32_arm_build_stubs): New function.
+       (bfd_elf32_arm_add_glue_sections_to_bfd): Skip stub sections.
+       (bfd_elf32_arm_process_before_allocation): No longer handle
+       R_ARM_CALL and R_ARM_THM_CALL.
+       (using_thumb_only): New function.
+       (elf32_arm_final_link_relocate): Redirect calls to stub if range
+       exceeds encoding capabilities.
+       (bfd_elf32_bfd_link_hash_table_free): Define.
+       * bfd-in.h (R_ARM_max): Fix value to 130.
+       (elf32_arm_setup_section_lists): Protype.
+       (elf32_arm_next_input_section): Protype.
+       (elf32_arm_size_stubs): Protype.
+       (elf32_arm_build_stubs): Protype.
+       * bfd-in2.h: Regenerate.
+
 2008-05-14  Ulrich Weigand  <uweigand@de.ibm.com>
 
        * elf32-ppc.c (ppc_elf_get_synthetic_symtab): Fix memset calls.
index 319c0df..01c1d80 100644 (file)
@@ -883,6 +883,17 @@ extern bfd_boolean bfd_arm_update_notes
 extern unsigned int bfd_arm_get_mach_from_notes
   (bfd *, const char *);
 
+/* ARM stub generation support.  Called from the linker.  */
+extern int elf32_arm_setup_section_lists
+  (bfd *, struct bfd_link_info *);
+extern void elf32_arm_next_input_section
+  (struct bfd_link_info *, struct bfd_section *);
+extern bfd_boolean elf32_arm_size_stubs
+  (bfd *, bfd *, struct bfd_link_info *, bfd_signed_vma,
+   struct bfd_section * (*) (const char *, struct bfd_section *), void (*) (void));
+extern bfd_boolean elf32_arm_build_stubs
+  (struct bfd_link_info *);
+  
 /* TI COFF load page support.  */
 extern void bfd_ticoff_set_section_load_page
   (struct bfd_section *, int);
index a3af3ce..949d601 100644 (file)
@@ -890,6 +890,17 @@ extern bfd_boolean bfd_arm_update_notes
 extern unsigned int bfd_arm_get_mach_from_notes
   (bfd *, const char *);
 
+/* ARM stub generation support.  Called from the linker.  */
+extern int elf32_arm_setup_section_lists
+  (bfd *, struct bfd_link_info *);
+extern void elf32_arm_next_input_section
+  (struct bfd_link_info *, struct bfd_section *);
+extern bfd_boolean elf32_arm_size_stubs
+  (bfd *, bfd *, struct bfd_link_info *, bfd_signed_vma,
+   struct bfd_section * (*) (const char *, struct bfd_section *), void (*) (void));
+extern bfd_boolean elf32_arm_build_stubs
+  (struct bfd_link_info *);
+  
 /* TI COFF load page support.  */
 extern void bfd_ticoff_set_section_load_page
   (struct bfd_section *, int);
index b8abd58..c81d7ea 100644 (file)
 #include "elf-vxworks.h"
 #include "elf/arm.h"
 
-#ifndef NUM_ELEM
-#define NUM_ELEM(a)  (sizeof (a) / (sizeof (a)[0]))
-#endif
-
 /* Return the relocation section associated with NAME.  HTAB is the
    bfd's elf32_arm_link_hash_entry.  */
 #define RELOC_SECTION(HTAB, NAME) \
@@ -1668,11 +1664,11 @@ static reloc_howto_type elf32_arm_howto_table_2[4] =
 static reloc_howto_type *
 elf32_arm_howto_from_type (unsigned int r_type)
 {
-  if (r_type < NUM_ELEM (elf32_arm_howto_table_1))
+  if (r_type < ARRAY_SIZE (elf32_arm_howto_table_1))
     return &elf32_arm_howto_table_1[r_type];
 
   if (r_type >= R_ARM_RREL32
-      && r_type < R_ARM_RREL32 + NUM_ELEM (elf32_arm_howto_table_2))
+      && r_type < R_ARM_RREL32 + ARRAY_SIZE (elf32_arm_howto_table_2))
     return &elf32_arm_howto_table_2[r_type - R_ARM_RREL32];
 
   return NULL;
@@ -1782,7 +1778,7 @@ elf32_arm_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
                             bfd_reloc_code_real_type code)
 {
   unsigned int i;
-  for (i = 0; i < NUM_ELEM (elf32_arm_reloc_map); i ++)
+  for (i = 0; i < ARRAY_SIZE (elf32_arm_reloc_map); i ++)
     if (elf32_arm_reloc_map[i].bfd_reloc_val == code)
       return elf32_arm_howto_from_type (elf32_arm_reloc_map[i].elf_reloc_val);
 
@@ -1795,18 +1791,12 @@ elf32_arm_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
 {
   unsigned int i;
 
-  for (i = 0;
-       i < (sizeof (elf32_arm_howto_table_1)
-           / sizeof (elf32_arm_howto_table_1[0]));
-       i++)
+  for (i = 0; i < ARRAY_SIZE (elf32_arm_howto_table_1); i++)
     if (elf32_arm_howto_table_1[i].name != NULL
        && strcasecmp (elf32_arm_howto_table_1[i].name, r_name) == 0)
       return &elf32_arm_howto_table_1[i];
 
-  for (i = 0;
-       i < (sizeof (elf32_arm_howto_table_2)
-           / sizeof (elf32_arm_howto_table_2[0]));
-       i++)
+  for (i = 0; i < ARRAY_SIZE (elf32_arm_howto_table_2); i++)
     if (elf32_arm_howto_table_2[i].name != NULL
        && strcasecmp (elf32_arm_howto_table_2[i].name, r_name) == 0)
       return &elf32_arm_howto_table_2[i];
@@ -1814,7 +1804,8 @@ elf32_arm_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
   return NULL;
 }
 
-/* Support for core dump NOTE sections */
+/* Support for core dump NOTE sections.  */
+
 static bfd_boolean
 elf32_arm_nabi_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
 {
@@ -1863,7 +1854,6 @@ elf32_arm_nabi_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
   /* Note that for some reason, a spurious space is tacked
      onto the end of the args in some (at least one anyway)
      implementations, so strip it off if it exists.  */
-
   {
     char *command = elf_tdata (abfd)->core_command;
     int n = strlen (command);
@@ -2004,12 +1994,104 @@ static const bfd_vma elf32_arm_plt_thumb_stub [] =
 
 /* The entries in a PLT when using a DLL-based target with multiple
    address spaces.  */
-static const bfd_vma elf32_arm_symbian_plt_entry [] = 
+static const bfd_vma elf32_arm_symbian_plt_entry [] =
   {
     0xe51ff004,         /* ldr   pc, [pc, #-4] */
     0x00000000,         /* dcd   R_ARM_GLOB_DAT(X) */
   };
 
+#define ARM_MAX_FWD_BRANCH_OFFSET  ((((1 << 23) - 1) << 2) + 8)
+#define ARM_MAX_BWD_BRANCH_OFFSET  ((-((1 << 23) << 2)) + 8)
+#define THM_MAX_FWD_BRANCH_OFFSET  ((1 << 22) -2 + 4)
+#define THM_MAX_BWD_BRANCH_OFFSET  (-(1 << 22) + 4)
+#define THM2_MAX_FWD_BRANCH_OFFSET (((1 << 24) - 2) + 4)
+#define THM2_MAX_BWD_BRANCH_OFFSET (-(1 << 24) + 4)
+
+static const bfd_vma arm_long_branch_stub[] =
+  {
+    0xe51ff004,         /* ldr   pc, [pc, #-4] */
+    0x00000000,         /* dcd   R_ARM_ABS32(X) */
+  };
+
+static const bfd_vma arm_thumb_v4t_long_branch_stub[] =
+  {
+    0xe59fc000,         /* ldr   ip, [pc, #0] */
+    0xe12fff1c,         /* bx    ip */
+    0x00000000,         /* dcd   R_ARM_ABS32(X) */
+  };
+
+static const bfd_vma arm_thumb_thumb_long_branch_stub[] =
+  {
+    0x4e02b540,         /* push {r6, lr} */
+                        /* ldr  r6, [pc, #8] */
+    0xe7fe46fe,         /* mov  lr, pc */
+                        /* b.n  r6 */
+    0xbf00bd40,         /* pop  {r6, pc} */
+                        /* nop */
+    0x00000000,         /* dcd  R_ARM_ABS32(X) */
+  };
+
+static const bfd_vma arm_thumb_arm_v4t_long_branch_stub[] =
+  {
+    0x4e03b540,         /* push {r6, lr} */
+                        /* ldr  r6, [pc, #12] */
+    0x473046fe,         /* mov  lr, pc */
+                        /* bx   r6 */
+    0xe8bd4040,         /* pop  {r6, pc} */
+    0xe12fff1e,         /* bx   lr */
+    0x00000000,         /* dcd  R_ARM_ABS32(X) */
+  };
+
+static const bfd_vma arm_pic_long_branch_stub[] =
+  {
+    0xe59fc000,         /* ldr   r12, [pc] */
+    0xe08ff00c,         /* add   pc, pc, ip */
+    0x00000000,         /* dcd   R_ARM_ABS32(X) */
+  };
+
+/* Section name for stubs is the associated section name plus this
+   string.  */
+#define STUB_SUFFIX ".stub"
+
+enum elf32_arm_stub_type
+{
+  arm_stub_none,
+  arm_stub_long_branch,
+  arm_thumb_v4t_stub_long_branch,
+  arm_thumb_thumb_stub_long_branch,
+  arm_thumb_arm_v4t_stub_long_branch,
+  arm_stub_pic_long_branch,
+};
+
+struct elf32_arm_stub_hash_entry
+{
+  /* Base hash table entry structure.  */
+  struct bfd_hash_entry root;
+
+  /* The stub section.  */
+  asection *stub_sec;
+
+  /* Offset within stub_sec of the beginning of this stub.  */
+  bfd_vma stub_offset;
+
+  /* Given the symbol's value and its section we can determine its final
+     value when building the stubs (so the stub knows where to jump).  */
+  bfd_vma target_value;
+  asection *target_section;
+
+  enum elf32_arm_stub_type stub_type;
+
+  /* The symbol table entry, if any, that this was derived from.  */
+  struct elf32_arm_link_hash_entry *h;
+
+  /* Destination symbol type (STT_ARM_TFUNC, ...) */
+  unsigned char st_type;
+
+  /* Where this stub is being called from, or, in the case of combined
+     stub sections, the first input section in the group.  */
+  asection *id_sec;
+};
+
 /* Used to build a map of a section.  This is required for mixed-endian
    code/data.  */
 
@@ -2150,6 +2232,10 @@ struct elf32_arm_link_hash_entry
     /* The symbol marking the real symbol location for exported thumb
        symbols with Arm stubs.  */
     struct elf_link_hash_entry *export_glue;
+
+  /* A pointer to the most recently used stub hash entry against this
+     symbol. */
+  struct elf32_arm_stub_hash_entry *stub_cache;
   };
 
 /* Traverse an arm ELF linker hash table.  */
@@ -2163,99 +2249,130 @@ struct elf32_arm_link_hash_entry
 #define elf32_arm_hash_table(info) \
   ((struct elf32_arm_link_hash_table *) ((info)->hash))
 
+#define arm_stub_hash_lookup(table, string, create, copy) \
+  ((struct elf32_arm_stub_hash_entry *) \
+   bfd_hash_lookup ((table), (string), (create), (copy)))
+
 /* ARM ELF linker hash table.  */
 struct elf32_arm_link_hash_table
-  {
-    /* The main hash table.  */
-    struct elf_link_hash_table root;
+{
+  /* The main hash table.  */
+  struct elf_link_hash_table root;
 
-    /* The size in bytes of the section containing the Thumb-to-ARM glue.  */
-    bfd_size_type thumb_glue_size;
+  /* The size in bytes of the section containing the Thumb-to-ARM glue.  */
+  bfd_size_type thumb_glue_size;
 
-    /* The size in bytes of the section containing the ARM-to-Thumb glue.  */
-    bfd_size_type arm_glue_size;
+  /* The size in bytes of the section containing the ARM-to-Thumb glue.  */
+  bfd_size_type arm_glue_size;
 
-    /* The size in bytes of section containing the ARMv4 BX veneers.  */
-    bfd_size_type bx_glue_size;
+  /* The size in bytes of section containing the ARMv4 BX veneers.  */
+  bfd_size_type bx_glue_size;
 
-    /* Offsets of ARMv4 BX veneers.  Bit1 set if present, and Bit0 set when
-       veneer has been populated.  */
-    bfd_vma bx_glue_offset[15];
+  /* Offsets of ARMv4 BX veneers.  Bit1 set if present, and Bit0 set when
+     veneer has been populated.  */
+  bfd_vma bx_glue_offset[15];
 
-    /* The size in bytes of the section containing glue for VFP11 erratum
-       veneers.  */
-    bfd_size_type vfp11_erratum_glue_size;
+  /* The size in bytes of the section containing glue for VFP11 erratum
+     veneers.  */
+  bfd_size_type vfp11_erratum_glue_size;
 
-    /* An arbitrary input BFD chosen to hold the glue sections.  */
-    bfd * bfd_of_glue_owner;
+  /* An arbitrary input BFD chosen to hold the glue sections.  */
+  bfd * bfd_of_glue_owner;
 
-    /* Nonzero to output a BE8 image.  */
-    int byteswap_code;
+  /* Nonzero to output a BE8 image.  */
+  int byteswap_code;
 
-    /* Zero if R_ARM_TARGET1 means R_ARM_ABS32.
-       Nonzero if R_ARM_TARGET1 means R_ARM_REL32.  */
-    int target1_is_rel;
+  /* Zero if R_ARM_TARGET1 means R_ARM_ABS32.
+     Nonzero if R_ARM_TARGET1 means R_ARM_REL32.  */
+  int target1_is_rel;
 
-    /* The relocation to use for R_ARM_TARGET2 relocations.  */
-    int target2_reloc;
+  /* The relocation to use for R_ARM_TARGET2 relocations.  */
+  int target2_reloc;
 
-    /* 0 = Ignore R_ARM_V4BX.
-       1 = Convert BX to MOV PC.
-       2 = Generate v4 interworing stubs.  */
-    int fix_v4bx;
+  /* 0 = Ignore R_ARM_V4BX.
+     1 = Convert BX to MOV PC.
+     2 = Generate v4 interworing stubs.  */
+  int fix_v4bx;
 
-    /* Nonzero if the ARM/Thumb BLX instructions are available for use.  */
-    int use_blx;
+  /* Nonzero if the ARM/Thumb BLX instructions are available for use.  */
+  int use_blx;
 
-    /* What sort of code sequences we should look for which may trigger the
-       VFP11 denorm erratum.  */
-    bfd_arm_vfp11_fix vfp11_fix;
+  /* What sort of code sequences we should look for which may trigger the
+     VFP11 denorm erratum.  */
+  bfd_arm_vfp11_fix vfp11_fix;
 
-    /* Global counter for the number of fixes we have emitted.  */
-    int num_vfp11_fixes;
+  /* Global counter for the number of fixes we have emitted.  */
+  int num_vfp11_fixes;
 
-    /* Nonzero to force PIC branch veneers.  */
-    int pic_veneer;
+  /* Nonzero to force PIC branch veneers.  */
+  int pic_veneer;
 
-    /* The number of bytes in the initial entry in the PLT.  */
-    bfd_size_type plt_header_size;
+  /* The number of bytes in the initial entry in the PLT.  */
+  bfd_size_type plt_header_size;
 
-    /* The number of bytes in the subsequent PLT etries.  */
-    bfd_size_type plt_entry_size;
+  /* The number of bytes in the subsequent PLT etries.  */
+  bfd_size_type plt_entry_size;
 
-    /* True if the target system is VxWorks.  */
-    int vxworks_p;
+  /* True if the target system is VxWorks.  */
+  int vxworks_p;
 
-    /* True if the target system is Symbian OS.  */
-    int symbian_p;
+  /* True if the target system is Symbian OS.  */
+  int symbian_p;
 
-    /* True if the target uses REL relocations.  */
-    int use_rel;
+  /* True if the target uses REL relocations.  */
+  int use_rel;
 
-    /* Short-cuts to get to dynamic linker sections.  */
-    asection *sgot;
-    asection *sgotplt;
-    asection *srelgot;
-    asection *splt;
-    asection *srelplt;
-    asection *sdynbss;
-    asection *srelbss;
+  /* Short-cuts to get to dynamic linker sections.  */
+  asection *sgot;
+  asection *sgotplt;
+  asection *srelgot;
+  asection *splt;
+  asection *srelplt;
+  asection *sdynbss;
+  asection *srelbss;
 
-    /* The (unloaded but important) VxWorks .rela.plt.unloaded section.  */
-    asection *srelplt2;
+  /* The (unloaded but important) VxWorks .rela.plt.unloaded section.  */
+  asection *srelplt2;
 
-    /* Data for R_ARM_TLS_LDM32 relocations.  */
-    union {
-      bfd_signed_vma refcount;
-      bfd_vma offset;
-    } tls_ldm_got;
-    
-    /* Small local sym to section mapping cache.  */
-    struct sym_sec_cache sym_sec;
+  /* Data for R_ARM_TLS_LDM32 relocations.  */
+  union
+  {
+    bfd_signed_vma refcount;
+    bfd_vma offset;
+  } tls_ldm_got;
 
-    /* For convenience in allocate_dynrelocs.  */
-    bfd * obfd;
-  };
+  /* Small local sym to section mapping cache.  */
+  struct sym_sec_cache sym_sec;
+
+  /* For convenience in allocate_dynrelocs.  */
+  bfd * obfd;
+
+  /* The stub hash table.  */
+  struct bfd_hash_table stub_hash_table;
+
+  /* Linker stub bfd.  */
+  bfd *stub_bfd;
+
+  /* Linker call-backs.  */
+  asection * (*add_stub_section) (const char *, asection *);
+  void (*layout_sections_again) (void);
+
+  /* Array to keep track of which stub sections have been created, and
+     information on stub grouping.  */
+  struct map_stub
+  {
+    /* This is the section to which stubs in the group will be
+       attached.  */
+    asection *link_sec;
+    /* The stub section.  */
+    asection *stub_sec;
+  } *stub_group;
+
+  /* Assorted information used by elf32_arm_size_stubs.  */
+  unsigned int bfd_count;
+  int top_index;
+  asection **input_list;
+};
 
 /* Create an entry in an ARM ELF linker hash table.  */
 
@@ -2269,7 +2386,7 @@ elf32_arm_link_hash_newfunc (struct bfd_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)
+  if (ret == NULL)
     ret = bfd_hash_allocate (table, sizeof (struct elf32_arm_link_hash_entry));
   if (ret == NULL)
     return (struct bfd_hash_entry *) ret;
@@ -2286,11 +2403,50 @@ elf32_arm_link_hash_newfunc (struct bfd_hash_entry * entry,
       ret->plt_maybe_thumb_refcount = 0;
       ret->plt_got_offset = -1;
       ret->export_glue = NULL;
+
+      ret->stub_cache = NULL;
     }
 
   return (struct bfd_hash_entry *) ret;
 }
 
+/* Initialize an entry in the stub hash table.  */
+
+static struct bfd_hash_entry *
+stub_hash_newfunc (struct bfd_hash_entry *entry,
+                  struct bfd_hash_table *table,
+                  const char *string)
+{
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (entry == NULL)
+    {
+      entry = bfd_hash_allocate (table,
+                                sizeof (struct elf32_arm_stub_hash_entry));
+      if (entry == NULL)
+       return entry;
+    }
+
+  /* Call the allocation method of the superclass.  */
+  entry = bfd_hash_newfunc (entry, table, string);
+  if (entry != NULL)
+    {
+      struct elf32_arm_stub_hash_entry *eh;
+
+      /* Initialize the local fields.  */
+      eh = (struct elf32_arm_stub_hash_entry *) entry;
+      eh->stub_sec = NULL;
+      eh->stub_offset = 0;
+      eh->target_value = 0;
+      eh->target_section = NULL;
+      eh->stub_type = arm_stub_none;
+      eh->h = NULL;
+      eh->id_sec = NULL;
+    }
+
+  return entry;
+}
+
 /* Return true if NAME is the name of the relocation section associated
    with S.  */
 
@@ -2382,7 +2538,7 @@ elf32_arm_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
        }
     }
 
-  if (!htab->splt 
+  if (!htab->splt
       || !htab->srelplt
       || !htab->sdynbss
       || (!info->shared && !htab->srelbss))
@@ -2391,123 +2547,1168 @@ elf32_arm_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
   return TRUE;
 }
 
-/* Copy the extra info we tack onto an elf_link_hash_entry.  */
+/* Copy the extra info we tack onto an elf_link_hash_entry.  */
+
+static void
+elf32_arm_copy_indirect_symbol (struct bfd_link_info *info,
+                               struct elf_link_hash_entry *dir,
+                               struct elf_link_hash_entry *ind)
+{
+  struct elf32_arm_link_hash_entry *edir, *eind;
+
+  edir = (struct elf32_arm_link_hash_entry *) dir;
+  eind = (struct elf32_arm_link_hash_entry *) ind;
+
+  if (eind->relocs_copied != NULL)
+    {
+      if (edir->relocs_copied != NULL)
+       {
+         struct elf32_arm_relocs_copied **pp;
+         struct elf32_arm_relocs_copied *p;
+
+         /* Add reloc counts against the indirect sym to the direct sym
+            list.  Merge any entries against the same section.  */
+         for (pp = &eind->relocs_copied; (p = *pp) != NULL; )
+           {
+             struct elf32_arm_relocs_copied *q;
+
+             for (q = edir->relocs_copied; q != NULL; q = q->next)
+               if (q->section == p->section)
+                 {
+                   q->pc_count += p->pc_count;
+                   q->count += p->count;
+                   *pp = p->next;
+                   break;
+                 }
+             if (q == NULL)
+               pp = &p->next;
+           }
+         *pp = edir->relocs_copied;
+       }
+
+      edir->relocs_copied = eind->relocs_copied;
+      eind->relocs_copied = NULL;
+    }
+
+  if (ind->root.type == bfd_link_hash_indirect)
+    {
+      /* Copy over PLT info.  */
+      edir->plt_thumb_refcount += eind->plt_thumb_refcount;
+      eind->plt_thumb_refcount = 0;
+      edir->plt_maybe_thumb_refcount += eind->plt_maybe_thumb_refcount;
+      eind->plt_maybe_thumb_refcount = 0;
+
+      if (dir->got.refcount <= 0)
+       {
+         edir->tls_type = eind->tls_type;
+         eind->tls_type = GOT_UNKNOWN;
+       }
+    }
+
+  _bfd_elf_link_hash_copy_indirect (info, dir, ind);
+}
+
+/* Create an ARM elf linker hash table.  */
+
+static struct bfd_link_hash_table *
+elf32_arm_link_hash_table_create (bfd *abfd)
+{
+  struct elf32_arm_link_hash_table *ret;
+  bfd_size_type amt = sizeof (struct elf32_arm_link_hash_table);
+
+  ret = bfd_malloc (amt);
+  if (ret == NULL)
+    return NULL;
+
+  if (!_bfd_elf_link_hash_table_init (& ret->root, abfd,
+                                     elf32_arm_link_hash_newfunc,
+                                     sizeof (struct elf32_arm_link_hash_entry)))
+    {
+      free (ret);
+      return NULL;
+    }
+
+  ret->sgot = NULL;
+  ret->sgotplt = NULL;
+  ret->srelgot = NULL;
+  ret->splt = NULL;
+  ret->srelplt = NULL;
+  ret->sdynbss = NULL;
+  ret->srelbss = NULL;
+  ret->srelplt2 = NULL;
+  ret->thumb_glue_size = 0;
+  ret->arm_glue_size = 0;
+  ret->bx_glue_size = 0;
+  memset (ret->bx_glue_offset, 0, sizeof (ret->bx_glue_offset));
+  ret->vfp11_fix = BFD_ARM_VFP11_FIX_NONE;
+  ret->vfp11_erratum_glue_size = 0;
+  ret->num_vfp11_fixes = 0;
+  ret->bfd_of_glue_owner = NULL;
+  ret->byteswap_code = 0;
+  ret->target1_is_rel = 0;
+  ret->target2_reloc = R_ARM_NONE;
+#ifdef FOUR_WORD_PLT
+  ret->plt_header_size = 16;
+  ret->plt_entry_size = 16;
+#else
+  ret->plt_header_size = 20;
+  ret->plt_entry_size = 12;
+#endif
+  ret->fix_v4bx = 0;
+  ret->use_blx = 0;
+  ret->vxworks_p = 0;
+  ret->symbian_p = 0;
+  ret->use_rel = 1;
+  ret->sym_sec.abfd = NULL;
+  ret->obfd = abfd;
+  ret->tls_ldm_got.refcount = 0;
+
+  if (!bfd_hash_table_init (&ret->stub_hash_table, stub_hash_newfunc,
+                           sizeof (struct elf32_arm_stub_hash_entry)))
+    {
+      free (ret);
+      return NULL;
+    }
+
+  return &ret->root.root;
+}
+
+/* Free the derived linker hash table.  */
+
+static void
+elf32_arm_hash_table_free (struct bfd_link_hash_table *hash)
+{
+  struct elf32_arm_link_hash_table *ret
+    = (struct elf32_arm_link_hash_table *) hash;
+
+  bfd_hash_table_free (&ret->stub_hash_table);
+  _bfd_generic_link_hash_table_free (hash);
+}
+
+/* Determine if we're dealing with a Thumb only architecture.  */
+
+static bfd_boolean
+using_thumb_only (struct elf32_arm_link_hash_table *globals)
+{
+  int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+                                      Tag_CPU_arch);
+  int profile;
+
+  if (arch != TAG_CPU_ARCH_V7)
+    return FALSE;
+
+  profile = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+                                     Tag_CPU_arch_profile);
+
+  return profile == 'M';
+}
+
+/* Determine if we're dealing with a Thumb-2 object.  */
+
+static bfd_boolean
+using_thumb2 (struct elf32_arm_link_hash_table *globals)
+{
+  int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+                                      Tag_CPU_arch);
+  return arch == TAG_CPU_ARCH_V6T2 || arch >= TAG_CPU_ARCH_V7;
+}
+
+/* Determine the type of stub needed, if any, for a call.  */
+
+static enum elf32_arm_stub_type
+arm_type_of_stub (struct bfd_link_info *info,
+                 asection *input_sec,
+                 const Elf_Internal_Rela *rel,
+                 unsigned char st_type,
+                 struct elf32_arm_link_hash_entry *hash,
+                 bfd_vma destination)
+{
+  bfd_vma location;
+  bfd_signed_vma branch_offset;
+  unsigned int r_type;
+  struct elf32_arm_link_hash_table * globals;
+  int thumb2;
+  int thumb_only;
+  enum elf32_arm_stub_type stub_type = arm_stub_none;
+
+  globals = elf32_arm_hash_table (info);
+
+  thumb_only = using_thumb_only (globals);
+
+  thumb2 = using_thumb2 (globals);
+
+  /* Determine where the call point is.  */
+  location = (input_sec->output_offset
+             + input_sec->output_section->vma
+             + rel->r_offset);
+
+  branch_offset = (bfd_signed_vma)(destination - location);
+
+  r_type = ELF32_R_TYPE (rel->r_info);
+
+  /* If the call will go through a PLT entry then we do not need
+     glue.  */
+  if (globals->splt != NULL && hash->root.plt.offset != (bfd_vma) -1)
+    return stub_type;
+
+  if (r_type == R_ARM_THM_CALL)
+    {
+      if ((!thumb2
+           && (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
+               || (branch_offset < THM_MAX_BWD_BRANCH_OFFSET)))
+         || (thumb2
+             && (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
+                 || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
+         || ((st_type != STT_ARM_TFUNC) && !globals->use_blx))
+       {
+         if (st_type == STT_ARM_TFUNC)
+           {
+             /* Thumb to thumb.  */
+             if (!thumb_only)
+               {
+                 stub_type = (info->shared | globals->pic_veneer)
+                   ? ((globals->use_blx)
+                      ? arm_stub_pic_long_branch
+                      : arm_stub_none)
+                   : (globals->use_blx)
+                   ? arm_stub_long_branch
+                   : arm_stub_none;
+               }
+             else
+               {
+                 stub_type = (info->shared | globals->pic_veneer)
+                   ? arm_stub_none
+                   : (globals->use_blx)
+                   ? arm_thumb_thumb_stub_long_branch
+                   : arm_stub_none;
+               }
+           }
+         else
+           {
+             /* Thumb to arm.  */
+             stub_type = (info->shared | globals->pic_veneer)
+               ? ((globals->use_blx)
+                  ? arm_stub_pic_long_branch
+                  : arm_stub_none)
+               : (globals->use_blx)
+               ? arm_stub_long_branch
+               : arm_thumb_arm_v4t_stub_long_branch;
+           }
+       }
+    }
+  else if (r_type == R_ARM_CALL)
+    {
+      if (st_type == STT_ARM_TFUNC)
+       {
+         /* Arm to thumb.  */
+         /* We have an extra 2-bytes reach because of the mode change
+            (bit 24 (H) of BLX encoding).  */
+         if (branch_offset > (ARM_MAX_FWD_BRANCH_OFFSET + 2)
+             || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)
+             || !globals->use_blx)
+           {
+             stub_type = (info->shared | globals->pic_veneer)
+               ? arm_stub_pic_long_branch
+               : (globals->use_blx)
+               ? arm_stub_long_branch
+               : arm_thumb_v4t_stub_long_branch;
+           }
+       }
+      else
+       {
+         /* Arm to arm.  */
+         if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
+             || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET))
+           {
+             stub_type = (info->shared | globals->pic_veneer)
+               ? arm_stub_pic_long_branch
+               : arm_stub_long_branch;
+           }
+       }
+    }
+
+  return stub_type;
+}
+
+/* Build a name for an entry in the stub hash table.  */
+
+static char *
+elf32_arm_stub_name (const asection *input_section,
+                    const asection *sym_sec,
+                    const struct elf32_arm_link_hash_entry *hash,
+                    const Elf_Internal_Rela *rel)
+{
+  char *stub_name;
+  bfd_size_type len;
+
+  if (hash)
+    {
+      len = 8 + 1 + strlen (hash->root.root.root.string) + 1 + 8 + 1;
+      stub_name = bfd_malloc (len);
+      if (stub_name != NULL)
+       sprintf (stub_name, "%08x_%s+%x",
+                input_section->id & 0xffffffff,
+                hash->root.root.root.string,
+                (int) rel->r_addend & 0xffffffff);
+    }
+  else
+    {
+      len = 8 + 1 + 8 + 1 + 8 + 1 + 8 + 1;
+      stub_name = bfd_malloc (len);
+      if (stub_name != NULL)
+       sprintf (stub_name, "%08x_%x:%x+%x",
+                input_section->id & 0xffffffff,
+                sym_sec->id & 0xffffffff,
+                (int) ELF32_R_SYM (rel->r_info) & 0xffffffff,
+                (int) rel->r_addend & 0xffffffff);
+    }
+
+  return stub_name;
+}
+
+/* Look up an entry in the stub hash.  Stub entries are cached because
+   creating the stub name takes a bit of time.  */
+
+static struct elf32_arm_stub_hash_entry *
+elf32_arm_get_stub_entry (const asection *input_section,
+                         const asection *sym_sec,
+                         struct elf_link_hash_entry *hash,
+                         const Elf_Internal_Rela *rel,
+                         struct elf32_arm_link_hash_table *htab)
+{
+  struct elf32_arm_stub_hash_entry *stub_entry;
+  struct elf32_arm_link_hash_entry *h = (struct elf32_arm_link_hash_entry *) hash;
+  const asection *id_sec;
+
+  if ((input_section->flags & SEC_CODE) == 0)
+    return NULL;
+
+  /* If this input section is part of a group of sections sharing one
+     stub section, then use the id of the first section in the group.
+     Stub names need to include a section id, as there may well be
+     more than one stub used to reach say, printf, and we need to
+     distinguish between them.  */
+  id_sec = htab->stub_group[input_section->id].link_sec;
+
+  if (h != NULL && h->stub_cache != NULL
+      && h->stub_cache->h == h
+      && h->stub_cache->id_sec == id_sec)
+    {
+      stub_entry = h->stub_cache;
+    }
+  else
+    {
+      char *stub_name;
+
+      stub_name = elf32_arm_stub_name (id_sec, sym_sec, h, rel);
+      if (stub_name == NULL)
+       return NULL;
+
+      stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table,
+                                       stub_name, FALSE, FALSE);
+      if (h != NULL)
+       h->stub_cache = stub_entry;
+
+      free (stub_name);
+    }
+
+  return stub_entry;
+}
+
+static void elf32_arm_stub_add_mapping_symbol (struct bfd_link_info * link_info,
+                                              asection *stub_sec,
+                                              char* name,
+                                              bfd_vma val)
+{
+  struct bfd_link_hash_entry * bh = NULL;
+  struct elf_link_hash_entry * myh;
+
+  _bfd_generic_link_add_one_symbol (link_info,
+                                   stub_sec->owner, name,
+                                   BSF_LOCAL, stub_sec, stub_sec->size + val,
+                                   NULL, TRUE, FALSE, &bh);
+
+  myh = (struct elf_link_hash_entry *) bh;
+  myh->type = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE);
+  myh->forced_local = 1;
+}
+
+/* Add a new stub entry to the stub hash.  Not all fields of the new
+   stub entry are initialised.  */
+
+static struct elf32_arm_stub_hash_entry *
+elf32_arm_add_stub (const char *stub_name,
+                   asection *section,
+                   struct elf32_arm_link_hash_table *htab,
+                   struct bfd_link_info * link_info,
+                   enum elf32_arm_stub_type stub_type)
+{
+  asection *link_sec;
+  asection *stub_sec;
+  struct elf32_arm_stub_hash_entry *stub_entry;
+
+  link_sec = htab->stub_group[section->id].link_sec;
+  stub_sec = htab->stub_group[section->id].stub_sec;
+  if (stub_sec == NULL)
+    {
+      stub_sec = htab->stub_group[link_sec->id].stub_sec;
+      if (stub_sec == NULL)
+       {
+         size_t namelen;
+         bfd_size_type len;
+         char *s_name;
+
+         namelen = strlen (link_sec->name);
+         len = namelen + sizeof (STUB_SUFFIX);
+         s_name = bfd_alloc (htab->stub_bfd, len);
+         if (s_name == NULL)
+           return NULL;
+
+         memcpy (s_name, link_sec->name, namelen);
+         memcpy (s_name + namelen, STUB_SUFFIX, sizeof (STUB_SUFFIX));
+         stub_sec = (*htab->add_stub_section) (s_name, link_sec);
+         if (stub_sec == NULL)
+           return NULL;
+         htab->stub_group[link_sec->id].stub_sec = stub_sec;
+       }
+      htab->stub_group[section->id].stub_sec = stub_sec;
+    }
+
+  /* Enter this entry into the linker stub hash table.  */
+  stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table, stub_name,
+                                    TRUE, FALSE);
+  if (stub_entry == NULL)
+    {
+      (*_bfd_error_handler) (_("%s: cannot create stub entry %s"),
+                            section->owner,
+                            stub_name);
+      return NULL;
+    }
+
+  stub_entry->stub_sec = stub_sec;
+  stub_entry->stub_offset = 0;
+  stub_entry->id_sec = link_sec;
+
+  switch (stub_type)
+    {
+    case arm_stub_long_branch:
+      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$a", 0);
+      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 4);
+      break;
+    case arm_thumb_v4t_stub_long_branch:
+      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$a", 0);
+      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 8);
+      break;
+    case arm_thumb_thumb_stub_long_branch:
+      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$t", 0);
+      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 12);
+      break;
+    case arm_thumb_arm_v4t_stub_long_branch:
+      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$t", 0);
+      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$a", 8);
+      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 16);
+      break;
+    case arm_stub_pic_long_branch:
+      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$a", 0);
+      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 8);
+      break;
+    default:
+      BFD_FAIL ();
+  }
+
+  return stub_entry;
+}
+
+/* Store an Arm insn into an output section not processed by
+   elf32_arm_write_section.  */
+
+static void
+put_arm_insn (struct elf32_arm_link_hash_table *htab,
+            bfd * output_bfd, bfd_vma val, void * ptr)
+{
+  if (htab->byteswap_code != bfd_little_endian (output_bfd))
+    bfd_putl32 (val, ptr);
+  else
+    bfd_putb32 (val, ptr);
+}
+
+/* Store a 16-bit Thumb insn into an output section not processed by
+   elf32_arm_write_section.  */
+
+static void
+put_thumb_insn (struct elf32_arm_link_hash_table *htab,
+              bfd * output_bfd, bfd_vma val, void * ptr)
+{
+  if (htab->byteswap_code != bfd_little_endian (output_bfd))
+    bfd_putl16 (val, ptr);
+  else
+    bfd_putb16 (val, ptr);
+}
+
+static bfd_boolean
+arm_build_one_stub (struct bfd_hash_entry *gen_entry,
+                   void * in_arg)
+{
+  struct elf32_arm_stub_hash_entry *stub_entry;
+  struct bfd_link_info *info;
+  struct elf32_arm_link_hash_table *htab;
+  asection *stub_sec;
+  bfd *stub_bfd;
+  bfd_vma stub_addr;
+  bfd_byte *loc;
+  bfd_vma sym_value;
+  int template_size;
+  int size;
+  const bfd_vma *template;
+  int i;
+  struct elf32_arm_link_hash_table * globals;
+
+  /* Massage our args to the form they really have.  */
+  stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
+  info = (struct bfd_link_info *) in_arg;
+
+  globals = elf32_arm_hash_table (info);
+
+  htab = elf32_arm_hash_table (info);
+  stub_sec = stub_entry->stub_sec;
+
+  /* Make a note of the offset within the stubs for this entry.  */
+  stub_entry->stub_offset = stub_sec->size;
+  loc = stub_sec->contents + stub_entry->stub_offset;
+
+  stub_bfd = stub_sec->owner;
+
+  /* This is the address of the start of the stub.  */
+  stub_addr = stub_sec->output_section->vma + stub_sec->output_offset
+    + stub_entry->stub_offset;
+
+  /* This is the address of the stub destination.  */
+  sym_value = (stub_entry->target_value
+              + stub_entry->target_section->output_offset
+              + stub_entry->target_section->output_section->vma);
+
+  switch (stub_entry->stub_type)
+    {
+    case arm_stub_long_branch:
+      template = arm_long_branch_stub;
+      template_size = (sizeof (arm_long_branch_stub) / sizeof (bfd_vma)) * 4;
+      break;
+    case arm_thumb_v4t_stub_long_branch:
+      template =  arm_thumb_v4t_long_branch_stub;
+      template_size = (sizeof (arm_thumb_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
+      break;
+    case arm_thumb_thumb_stub_long_branch:
+      template =  arm_thumb_thumb_long_branch_stub;
+      template_size = (sizeof (arm_thumb_thumb_long_branch_stub) / sizeof (bfd_vma)) * 4;
+      break;
+    case arm_thumb_arm_v4t_stub_long_branch:
+      template =  arm_thumb_arm_v4t_long_branch_stub;
+      template_size = (sizeof (arm_thumb_arm_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
+      break;
+    case arm_stub_pic_long_branch:
+      template = arm_pic_long_branch_stub;
+      template_size = (sizeof (arm_pic_long_branch_stub) / sizeof (bfd_vma)) * 4;
+      break;
+    default:
+      BFD_FAIL ();
+      return FALSE;
+    }
+
+  size = 0;
+  for (i = 0; i < (template_size / 4); i++)
+    {
+      /* A 0 pattern is a placeholder, every other pattern is an
+        instruction.  */
+      if (template[i] != 0)
+       put_arm_insn (globals, stub_bfd, template[i], loc + size);
+      else
+       bfd_put_32 (stub_bfd, template[i], loc + size);
+
+      size += 4;
+    }
+  stub_sec->size += size;
+
+  /* Destination is Thumb. Force bit 0 to 1 to reflect this.  */
+  if (stub_entry->st_type == STT_ARM_TFUNC)
+    sym_value |= 1;
+
+  switch (stub_entry->stub_type)
+    {
+    case arm_stub_long_branch:
+      _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
+                               stub_bfd, stub_sec, stub_sec->contents + 4,
+                               stub_entry->stub_offset, sym_value, 0);
+      break;
+    case arm_thumb_v4t_stub_long_branch:
+      _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
+                               stub_bfd, stub_sec, stub_sec->contents + 8,
+                               stub_entry->stub_offset, sym_value, 0);
+      break;
+    case arm_thumb_thumb_stub_long_branch:
+      _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
+                               stub_bfd, stub_sec, stub_sec->contents + 12,
+                               stub_entry->stub_offset, sym_value, 0);
+      break;
+    case arm_thumb_arm_v4t_stub_long_branch:
+      _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
+                               stub_bfd, stub_sec, stub_sec->contents + 20,
+                               stub_entry->stub_offset, sym_value, 0);
+      break;
+    case arm_stub_pic_long_branch:
+      /* We want the value relative to the address 8 bytes from the
+        start of the stub.  */
+      sym_value -= stub_addr + 8;
+
+      _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
+                               stub_bfd, stub_sec, stub_sec->contents + 8,
+                               stub_entry->stub_offset, sym_value, 0);
+      break;
+    default:
+      break;
+    }
+
+  return TRUE;
+}
+
+/* As above, but don't actually build the stub.  Just bump offset so
+   we know stub section sizes.  */
+
+static bfd_boolean
+arm_size_one_stub (struct bfd_hash_entry *gen_entry,
+                  void * in_arg)
+{
+  struct elf32_arm_stub_hash_entry *stub_entry;
+  struct elf32_arm_link_hash_table *htab;
+  const bfd_vma *template;
+  int template_size;
+  int size;
+  int i;
+
+  /* Massage our args to the form they really have.  */
+  stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
+  htab = (struct elf32_arm_link_hash_table *) in_arg;
+
+  switch (stub_entry->stub_type)
+    {
+    case arm_stub_long_branch:
+      template =  arm_long_branch_stub;
+      template_size = (sizeof (arm_long_branch_stub) / sizeof (bfd_vma)) * 4;
+      break;
+    case arm_thumb_v4t_stub_long_branch:
+      template =  arm_thumb_v4t_long_branch_stub;
+      template_size = (sizeof (arm_thumb_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
+      break;
+    case arm_thumb_thumb_stub_long_branch:
+      template =  arm_thumb_thumb_long_branch_stub;
+      template_size = (sizeof (arm_thumb_thumb_long_branch_stub) / sizeof (bfd_vma)) * 4;
+      break;
+    case arm_thumb_arm_v4t_stub_long_branch:
+      template =  arm_thumb_arm_v4t_long_branch_stub;
+      template_size = (sizeof (arm_thumb_arm_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
+      break;
+    case arm_stub_pic_long_branch:
+      template = arm_pic_long_branch_stub;
+      template_size = (sizeof (arm_pic_long_branch_stub) / sizeof (bfd_vma)) * 4;
+      break;
+    default:
+      BFD_FAIL ();
+      return FALSE;
+      break;
+    }
+
+  size = 0;
+  for (i = 0; i < (template_size / 4); i++)
+      size += 4;
+  size = (size + 7) & ~7;
+  stub_entry->stub_sec->size += size;
+  return TRUE;
+}
+
+/* External entry points for sizing and building linker stubs.  */
+
+/* Set up various things so that we can make a list of input sections
+   for each output section included in the link.  Returns -1 on error,
+   0 when no stubs will be needed, and 1 on success.  */
+
+int
+elf32_arm_setup_section_lists (bfd *output_bfd,
+                              struct bfd_link_info *info)
+{
+  bfd *input_bfd;
+  unsigned int bfd_count;
+  int top_id, top_index;
+  asection *section;
+  asection **input_list, **list;
+  bfd_size_type amt;
+  struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
+
+  if (! is_elf_hash_table (htab))
+    return 0;
+
+  /* Count the number of input BFDs and find the top input section id.  */
+  for (input_bfd = info->input_bfds, bfd_count = 0, top_id = 0;
+       input_bfd != NULL;
+       input_bfd = input_bfd->link_next)
+    {
+      bfd_count += 1;
+      for (section = input_bfd->sections;
+          section != NULL;
+          section = section->next)
+       {
+         if (top_id < section->id)
+           top_id = section->id;
+       }
+    }
+  htab->bfd_count = bfd_count;
+
+  amt = sizeof (struct map_stub) * (top_id + 1);
+  htab->stub_group = bfd_zmalloc (amt);
+  if (htab->stub_group == NULL)
+    return -1;
+
+  /* We can't use output_bfd->section_count here to find the top output
+     section index as some sections may have been removed, and
+     _bfd_strip_section_from_output doesn't renumber the indices.  */
+  for (section = output_bfd->sections, top_index = 0;
+       section != NULL;
+       section = section->next)
+    {
+      if (top_index < section->index)
+       top_index = section->index;
+    }
+
+  htab->top_index = top_index;
+  amt = sizeof (asection *) * (top_index + 1);
+  input_list = bfd_malloc (amt);
+  htab->input_list = input_list;
+  if (input_list == NULL)
+    return -1;
+
+  /* For sections we aren't interested in, mark their entries with a
+     value we can check later.  */
+  list = input_list + top_index;
+  do
+    *list = bfd_abs_section_ptr;
+  while (list-- != input_list);
+
+  for (section = output_bfd->sections;
+       section != NULL;
+       section = section->next)
+    {
+      if ((section->flags & SEC_CODE) != 0)
+       input_list[section->index] = NULL;
+    }
+
+  return 1;
+}
+
+/* The linker repeatedly calls this function for each input section,
+   in the order that input sections are linked into output sections.
+   Build lists of input sections to determine groupings between which
+   we may insert linker stubs.  */
+
+void
+elf32_arm_next_input_section (struct bfd_link_info *info,
+                             asection *isec)
+{
+  struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
+
+  if (isec->output_section->index <= htab->top_index)
+    {
+      asection **list = htab->input_list + isec->output_section->index;
+
+      if (*list != bfd_abs_section_ptr)
+       {
+         /* Steal the link_sec pointer for our list.  */
+#define PREV_SEC(sec) (htab->stub_group[(sec)->id].link_sec)
+         /* This happens to make the list in reverse order,
+            which is what we want.  */
+         PREV_SEC (isec) = *list;
+         *list = isec;
+       }
+    }
+}
+
+/* See whether we can group stub sections together.  Grouping stub
+   sections may result in fewer stubs.  More importantly, we need to
+   put all .init* and .fini* stubs at the beginning of the .init or
+   .fini output sections respectively, because glibc splits the
+   _init and _fini functions into multiple parts.  Putting a stub in
+   the middle of a function is not a good idea.  */
+
+static void
+group_sections (struct elf32_arm_link_hash_table *htab,
+               bfd_size_type stub_group_size,
+               bfd_boolean stubs_always_before_branch)
+{
+  asection **list = htab->input_list + htab->top_index;
+
+  do
+    {
+      asection *tail = *list;
+
+      if (tail == bfd_abs_section_ptr)
+       continue;
+
+      while (tail != NULL)
+       {
+         asection *curr;
+         asection *prev;
+         bfd_size_type total;
+
+         curr = tail;
+         total = tail->size;
+         while ((prev = PREV_SEC (curr)) != NULL
+                && ((total += curr->output_offset - prev->output_offset)
+                    < stub_group_size))
+           curr = prev;
+
+         /* OK, the size from the start of CURR to the end is less
+            than stub_group_size and thus can be handled by one stub
+            section.  (or the tail section is itself larger than
+            stub_group_size, in which case we may be toast.)
+            We should really be keeping track of the total size of
+            stubs added here, as stubs contribute to the final output
+            section size. */
+         do
+           {
+             prev = PREV_SEC (tail);
+             /* Set up this stub group.  */
+             htab->stub_group[tail->id].link_sec = curr;
+           }
+         while (tail != curr && (tail = prev) != NULL);
+
+         /* But wait, there's more!  Input sections up to stub_group_size
+            bytes before the stub section can be handled by it too.  */
+         if (!stubs_always_before_branch)
+           {
+             total = 0;
+             while (prev != NULL
+                    && ((total += tail->output_offset - prev->output_offset)
+                        < stub_group_size))
+               {
+                 tail = prev;
+                 prev = PREV_SEC (tail);
+                 htab->stub_group[tail->id].link_sec = curr;
+               }
+           }
+         tail = prev;
+       }
+    }
+  while (list-- != htab->input_list);
+
+  free (htab->input_list);
+#undef PREV_SEC
+}
+
+/* Determine and set the size of the stub section for a final link.
+
+   The basic idea here is to examine all the relocations looking for
+   PC-relative calls to a target that is unreachable with a "bl"
+   instruction.  */
+
+bfd_boolean
+elf32_arm_size_stubs (bfd *output_bfd,
+                     bfd *stub_bfd,
+                     struct bfd_link_info *info,
+                     bfd_signed_vma group_size,
+                     asection * (*add_stub_section) (const char *, asection *),
+                     void (*layout_sections_again) (void))
+{
+  bfd_size_type stub_group_size;
+  bfd_boolean stubs_always_before_branch;
+  bfd_boolean stub_changed = 0;
+  struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
+
+  /* Propagate mach to stub bfd, because it may not have been
+     finalized when we created stub_bfd.  */
+  bfd_set_arch_mach (stub_bfd, bfd_get_arch (output_bfd),
+                    bfd_get_mach (output_bfd));
+
+  /* Stash our params away.  */
+  htab->stub_bfd = stub_bfd;
+  htab->add_stub_section = add_stub_section;
+  htab->layout_sections_again = layout_sections_again;
+  stubs_always_before_branch = group_size < 0;
+  if (group_size < 0)
+    stub_group_size = -group_size;
+  else
+    stub_group_size = group_size;
+
+  if (stub_group_size == 1)
+    {
+      /* Default values.  */
+      /* Thumb branch range is +-4MB has to be used as the default
+        maximum size (a given section can contain both ARM and Thumb
+        code, so the worst case has to be taken into account).
+
+        This value is 24K less than that, which allows for 2025
+        12-byte stubs.  If we exceed that, then we will fail to link.
+        The user will have to relink with an explicit group size
+        option.  */
+      stub_group_size = 4170000;
+    }
+
+  group_sections (htab, stub_group_size, stubs_always_before_branch);
+
+  while (1)
+    {
+      bfd *input_bfd;
+      unsigned int bfd_indx;
+      asection *stub_sec;
+
+      for (input_bfd = info->input_bfds, bfd_indx = 0;
+          input_bfd != NULL;
+          input_bfd = input_bfd->link_next, bfd_indx++)
+       {
+         Elf_Internal_Shdr *symtab_hdr;
+         asection *section;
+         Elf_Internal_Sym *local_syms = NULL;
+
+         /* We'll need the symbol table in a second.  */
+         symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+         if (symtab_hdr->sh_info == 0)
+           continue;
+
+         /* Walk over each section attached to the input bfd.  */
+         for (section = input_bfd->sections;
+              section != NULL;
+              section = section->next)
+           {
+             Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
+
+             /* If there aren't any relocs, then there's nothing more
+                to do.  */
+             if ((section->flags & SEC_RELOC) == 0
+                 || section->reloc_count == 0
+                 || (section->flags & SEC_CODE) == 0)
+               continue;
+
+             /* If this section is a link-once section that will be
+                discarded, then don't create any stubs.  */
+             if (section->output_section == NULL
+                 || section->output_section->owner != output_bfd)
+               continue;
+
+             /* Get the relocs.  */
+             internal_relocs
+               = _bfd_elf_link_read_relocs (input_bfd, section, NULL,
+                                            NULL, info->keep_memory);
+             if (internal_relocs == NULL)
+               goto error_ret_free_local;
+
+             /* Now examine each relocation.  */
+             irela = internal_relocs;
+             irelaend = irela + section->reloc_count;
+             for (; irela < irelaend; irela++)
+               {
+                 unsigned int r_type, r_indx;
+                 enum elf32_arm_stub_type stub_type;
+                 struct elf32_arm_stub_hash_entry *stub_entry;
+                 asection *sym_sec;
+                 bfd_vma sym_value;
+                 bfd_vma destination;
+                 struct elf32_arm_link_hash_entry *hash;
+                 char *stub_name;
+                 const asection *id_sec;
+                 unsigned char st_type;
+
+                 r_type = ELF32_R_TYPE (irela->r_info);
+                 r_indx = ELF32_R_SYM (irela->r_info);
+
+                 if (r_type >= (unsigned int) R_ARM_max)
+                   {
+                     bfd_set_error (bfd_error_bad_value);
+                   error_ret_free_internal:
+                     if (elf_section_data (section)->relocs == NULL)
+                       free (internal_relocs);
+                     goto error_ret_free_local;
+                   }
+
+                 /* Only look for stubs on call instructions.  */
+                 if ((r_type != (unsigned int) R_ARM_CALL)
+                     && (r_type != (unsigned int) R_ARM_THM_CALL))
+                   continue;
+
+                 /* Now determine the call target, its name, value,
+                    section.  */
+                 sym_sec = NULL;
+                 sym_value = 0;
+                 destination = 0;
+                 hash = NULL;
+                 if (r_indx < symtab_hdr->sh_info)
+                   {
+                     /* It's a local symbol.  */
+                     Elf_Internal_Sym *sym;
+                     Elf_Internal_Shdr *hdr;
+
+                     if (local_syms == NULL)
+                       {
+                         local_syms
+                           = (Elf_Internal_Sym *) symtab_hdr->contents;
+                         if (local_syms == NULL)
+                           local_syms
+                             = bfd_elf_get_elf_syms (input_bfd, symtab_hdr,
+                                                     symtab_hdr->sh_info, 0,
+                                                     NULL, NULL, NULL);
+                         if (local_syms == NULL)
+                           goto error_ret_free_internal;
+                       }
+
+                     sym = local_syms + r_indx;
+                     hdr = elf_elfsections (input_bfd)[sym->st_shndx];
+                     sym_sec = hdr->bfd_section;
+                     if (ELF_ST_TYPE (sym->st_info) != STT_SECTION)
+                       sym_value = sym->st_value;
+                     destination = (sym_value + irela->r_addend
+                                    + sym_sec->output_offset
+                                    + sym_sec->output_section->vma);
+                     st_type = ELF_ST_TYPE (sym->st_info);
+                   }
+                 else
+                   {
+                     /* It's an external symbol.  */
+                     int e_indx;
+
+                     e_indx = r_indx - symtab_hdr->sh_info;
+                     hash = ((struct elf32_arm_link_hash_entry *)
+                             elf_sym_hashes (input_bfd)[e_indx]);
+
+                     while (hash->root.root.type == bfd_link_hash_indirect
+                            || hash->root.root.type == bfd_link_hash_warning)
+                       hash = ((struct elf32_arm_link_hash_entry *)
+                               hash->root.root.u.i.link);
+
+                     if (hash->root.root.type == bfd_link_hash_defined
+                         || hash->root.root.type == bfd_link_hash_defweak)
+                       {
+                         sym_sec = hash->root.root.u.def.section;
+                         sym_value = hash->root.root.u.def.value;
+                         if (sym_sec->output_section != NULL)
+                           destination = (sym_value + irela->r_addend
+                                          + sym_sec->output_offset
+                                          + sym_sec->output_section->vma);
+                       }
+                     else if (hash->root.root.type == bfd_link_hash_undefweak
+                              || hash->root.root.type == bfd_link_hash_undefined)
+                       /* For a shared library, these will need a PLT stub,
+                          which is treated separately.
+                          For absolute code, they cannot be handled.  */
+                       continue;
+                     else
+                       {
+                         bfd_set_error (bfd_error_bad_value);
+                         goto error_ret_free_internal;
+                       }
+                     st_type = ELF_ST_TYPE (hash->root.type);
+                   }
+
+                 /* Determine what (if any) linker stub is needed.  */
+                 stub_type = arm_type_of_stub (info, section, irela, st_type,
+                                               hash, destination);
+                 if (stub_type == arm_stub_none)
+                   continue;
 
-static void
-elf32_arm_copy_indirect_symbol (struct bfd_link_info *info,
-                               struct elf_link_hash_entry *dir,
-                               struct elf_link_hash_entry *ind)
-{
-  struct elf32_arm_link_hash_entry *edir, *eind;
+                 /* Support for grouping stub sections.  */
+                 id_sec = htab->stub_group[section->id].link_sec;
 
-  edir = (struct elf32_arm_link_hash_entry *) dir;
-  eind = (struct elf32_arm_link_hash_entry *) ind;
+                 /* Get the name of this stub.  */
+                 stub_name = elf32_arm_stub_name (id_sec, sym_sec, hash, irela);
+                 if (!stub_name)
+                   goto error_ret_free_internal;
 
-  if (eind->relocs_copied != NULL)
-    {
-      if (edir->relocs_copied != NULL)
-       {
-         struct elf32_arm_relocs_copied **pp;
-         struct elf32_arm_relocs_copied *p;
+                 stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table,
+                                                   stub_name,
+                                                   FALSE, FALSE);
+                 if (stub_entry != NULL)
+                   {
+                     /* The proper stub has already been created.  */
+                     free (stub_name);
+                     continue;
+                   }
 
-         /* Add reloc counts against the indirect sym to the direct sym
-            list.  Merge any entries against the same section.  */
-         for (pp = &eind->relocs_copied; (p = *pp) != NULL; )
-           {
-             struct elf32_arm_relocs_copied *q;
+                 stub_entry = elf32_arm_add_stub (stub_name, section, htab, info, stub_type);
+                 if (stub_entry == NULL)
+                   {
+                     free (stub_name);
+                     goto error_ret_free_internal;
+                   }
 
-             for (q = edir->relocs_copied; q != NULL; q = q->next)
-               if (q->section == p->section)
-                 {
-                   q->pc_count += p->pc_count;
-                   q->count += p->count;
-                   *pp = p->next;
-                   break;
-                 }
-             if (q == NULL)
-               pp = &p->next;
+                 stub_entry->target_value = sym_value;
+                 stub_entry->target_section = sym_sec;
+                 stub_entry->stub_type = stub_type;
+                 stub_entry->h = hash;
+                 stub_entry->st_type = st_type;
+                 stub_changed = TRUE;
+               }
+
+             /* We're done with the internal relocs, free them.  */
+             if (elf_section_data (section)->relocs == NULL)
+               free (internal_relocs);
            }
-         *pp = edir->relocs_copied;
        }
 
-      edir->relocs_copied = eind->relocs_copied;
-      eind->relocs_copied = NULL;
-    }
+      if (!stub_changed)
+       break;
 
-  if (ind->root.type == bfd_link_hash_indirect)
-    {
-      /* Copy over PLT info.  */
-      edir->plt_thumb_refcount += eind->plt_thumb_refcount;
-      eind->plt_thumb_refcount = 0;
-      edir->plt_maybe_thumb_refcount += eind->plt_maybe_thumb_refcount;
-      eind->plt_maybe_thumb_refcount = 0;
+      /* OK, we've added some stubs.  Find out the new size of the
+        stub sections.  */
+      for (stub_sec = htab->stub_bfd->sections;
+          stub_sec != NULL;
+          stub_sec = stub_sec->next)
+       stub_sec->size = 0;
 
-      if (dir->got.refcount <= 0)
-       {
-         edir->tls_type = eind->tls_type;
-         eind->tls_type = GOT_UNKNOWN;
-       }
+      bfd_hash_traverse (&htab->stub_hash_table, arm_size_one_stub, htab);
+
+      /* Ask the linker to do its stuff.  */
+      (*htab->layout_sections_again) ();
+      stub_changed = FALSE;
     }
 
-  _bfd_elf_link_hash_copy_indirect (info, dir, ind);
+  return TRUE;
+
+ error_ret_free_local:
+  return FALSE;
 }
 
-/* Create an ARM elf linker hash table.  */
+/* Build all the stubs associated with the current output file.  The
+   stubs are kept in a hash table attached to the main linker hash
+   table.  We also set up the .plt entries for statically linked PIC
+   functions here.  This function is called via arm_elf_finish in the
+   linker.  */
 
-static struct bfd_link_hash_table *
-elf32_arm_link_hash_table_create (bfd *abfd)
+bfd_boolean
+elf32_arm_build_stubs (struct bfd_link_info *info)
 {
-  struct elf32_arm_link_hash_table *ret;
-  bfd_size_type amt = sizeof (struct elf32_arm_link_hash_table);
+  asection *stub_sec;
+  struct bfd_hash_table *table;
+  struct elf32_arm_link_hash_table *htab;
 
-  ret = bfd_malloc (amt);
-  if (ret == NULL)
-    return NULL;
+  htab = elf32_arm_hash_table (info);
 
-  if (!_bfd_elf_link_hash_table_init (& ret->root, abfd,
-                                     elf32_arm_link_hash_newfunc,
-                                     sizeof (struct elf32_arm_link_hash_entry)))
+  for (stub_sec = htab->stub_bfd->sections;
+       stub_sec != NULL;
+       stub_sec = stub_sec->next)
     {
-      free (ret);
-      return NULL;
+      bfd_size_type size;
+
+      /* Ignore non-stub sections */
+      if (!strstr (stub_sec->name, STUB_SUFFIX))
+       continue;
+
+      /* Allocate memory to hold the linker stubs.  */
+      size = stub_sec->size;
+      stub_sec->contents = bfd_zalloc (htab->stub_bfd, size);
+      if (stub_sec->contents == NULL && size != 0)
+       return FALSE;
+      stub_sec->size = 0;
     }
 
-  ret->sgot = NULL;
-  ret->sgotplt = NULL;
-  ret->srelgot = NULL;
-  ret->splt = NULL;
-  ret->srelplt = NULL;
-  ret->sdynbss = NULL;
-  ret->srelbss = NULL;
-  ret->srelplt2 = NULL;
-  ret->thumb_glue_size = 0;
-  ret->arm_glue_size = 0;
-  ret->bx_glue_size = 0;
-  memset (ret->bx_glue_offset, 0, sizeof(ret->bx_glue_offset));
-  ret->vfp11_fix = BFD_ARM_VFP11_FIX_NONE;
-  ret->vfp11_erratum_glue_size = 0;
-  ret->num_vfp11_fixes = 0;
-  ret->bfd_of_glue_owner = NULL;
-  ret->byteswap_code = 0;
-  ret->target1_is_rel = 0;
-  ret->target2_reloc = R_ARM_NONE;
-#ifdef FOUR_WORD_PLT
-  ret->plt_header_size = 16;
-  ret->plt_entry_size = 16;
-#else
-  ret->plt_header_size = 20;
-  ret->plt_entry_size = 12;
-#endif
-  ret->fix_v4bx = 0;
-  ret->use_blx = 0;
-  ret->vxworks_p = 0;
-  ret->symbian_p = 0;
-  ret->use_rel = 1;
-  ret->sym_sec.abfd = NULL;
-  ret->obfd = abfd;
-  ret->tls_ldm_got.refcount = 0;
+  /* Build the stubs as directed by the stub hash table.  */
+  table = &htab->stub_hash_table;
+  bfd_hash_traverse (table, arm_build_one_stub, info);
 
-  return &ret->root.root;
+  return TRUE;
 }
 
 /* Locate the Thumb encoded calling stub for NAME.  */
@@ -2585,14 +3786,14 @@ find_arm_glue (struct bfd_link_info *link_info,
    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.
 
    (v5t static images)
    .arm
    __func_from_arm:
    ldr pc, __func_addr
    __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
@@ -2687,19 +3888,19 @@ bfd_elf32_arm_allocate_interworking_sections (struct bfd_link_info * info)
       BFD_ASSERT (s->size == globals->thumb_glue_size);
       s->contents = foo;
     }
-  
+
   if (globals->vfp11_erratum_glue_size != 0)
     {
       BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
-      
+
       s = bfd_get_section_by_name
         (globals->bfd_of_glue_owner, VFP11_ERRATUM_VENEER_SECTION_NAME);
-      
+
       BFD_ASSERT (s != NULL);
-      
+
       foo = bfd_alloc (globals->bfd_of_glue_owner,
                       globals->vfp11_erratum_glue_size);
-      
+
       BFD_ASSERT (s->size == globals->vfp11_erratum_glue_size);
       s->contents = foo;
     }
@@ -2723,7 +3924,8 @@ bfd_elf32_arm_allocate_interworking_sections (struct bfd_link_info * info)
 }
 
 /* Allocate space and symbols for calling a Thumb function from Arm mode.
-   returns the symbol identifying teh stub.  */
+   returns the symbol identifying the stub.  */
+
 static struct elf_link_hash_entry *
 record_arm_to_thumb_glue (struct bfd_link_info * link_info,
                          struct elf_link_hash_entry * h)
@@ -2902,16 +4104,16 @@ record_arm_bx_glue (struct bfd_link_info * link_info, int reg)
 
   /* Add symbol for veneer.  */
   tmp_name = bfd_malloc ((bfd_size_type) strlen (ARM_BX_GLUE_ENTRY_NAME) + 1);
-  
+
   BFD_ASSERT (tmp_name);
-  
+
   sprintf (tmp_name, ARM_BX_GLUE_ENTRY_NAME, reg);
-  
+
   myh = elf_link_hash_lookup
     (&(globals)->root, tmp_name, FALSE, FALSE, FALSE);
-  
+
   BFD_ASSERT (myh == NULL);
-  
+
   bh = NULL;
   val = globals->bx_glue_size;
   _bfd_generic_link_add_one_symbol (link_info, globals->bfd_of_glue_owner,
@@ -2935,16 +4137,16 @@ elf32_arm_section_map_add (asection *sec, char type, bfd_vma vma)
 {
   struct _arm_elf_section_data *sec_data = elf32_arm_section_data (sec);
   unsigned int newidx;
-  
+
   if (sec_data->map == NULL)
     {
       sec_data->map = bfd_malloc (sizeof (elf32_arm_section_map));
       sec_data->mapcount = 0;
       sec_data->mapsize = 1;
     }
-  
+
   newidx = sec_data->mapcount++;
-  
+
   if (sec_data->mapcount > sec_data->mapsize)
     {
       sec_data->mapsize *= 2;
@@ -2979,32 +4181,32 @@ record_vfp11_erratum_veneer (struct bfd_link_info *link_info,
   struct _arm_elf_section_data *sec_data;
   int errcount;
   elf32_vfp11_erratum_list *newerr;
-  
+
   hash_table = elf32_arm_hash_table (link_info);
-  
+
   BFD_ASSERT (hash_table != NULL);
   BFD_ASSERT (hash_table->bfd_of_glue_owner != NULL);
-  
+
   s = bfd_get_section_by_name
     (hash_table->bfd_of_glue_owner, VFP11_ERRATUM_VENEER_SECTION_NAME);
-  
+
   sec_data = elf32_arm_section_data (s);
-  
+
   BFD_ASSERT (s != NULL);
-  
+
   tmp_name = bfd_malloc ((bfd_size_type) strlen
                         (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10);
-  
+
   BFD_ASSERT (tmp_name);
-  
+
   sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME,
           hash_table->num_vfp11_fixes);
-  
+
   myh = elf_link_hash_lookup
     (&(hash_table)->root, tmp_name, FALSE, FALSE, FALSE);
-  
+
   BFD_ASSERT (myh == NULL);
-  
+
   bh = NULL;
   val = hash_table->vfp11_erratum_glue_size;
   _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner,
@@ -3018,7 +4220,7 @@ record_vfp11_erratum_veneer (struct bfd_link_info *link_info,
   /* Link veneer back to calling location.  */
   errcount = ++(sec_data->erratumcount);
   newerr = bfd_zmalloc (sizeof (elf32_vfp11_erratum_list));
-  
+
   newerr->type = VFP11_ERRATUM_ARM_VENEER;
   newerr->vma = -1;
   newerr->u.v.branch = branch;
@@ -3034,7 +4236,7 @@ record_vfp11_erratum_veneer (struct bfd_link_info *link_info,
 
   myh = elf_link_hash_lookup
     (&(hash_table)->root, tmp_name, FALSE, FALSE, FALSE);
-  
+
   if (myh != NULL)
     abort ();
 
@@ -3042,13 +4244,13 @@ record_vfp11_erratum_veneer (struct bfd_link_info *link_info,
   val = offset + 4;
   _bfd_generic_link_add_one_symbol (link_info, branch_bfd, tmp_name, BSF_LOCAL,
                                    branch_sec, val, NULL, TRUE, FALSE, &bh);
-  
+
   myh = (struct elf_link_hash_entry *) bh;
   myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
   myh->forced_local = 1;
 
   free (tmp_name);
-  
+
   /* Generate a mapping symbol for the veneer section, and explicitly add an
      entry for that symbol to the code/data map for the section.  */
   if (hash_table->vfp11_erratum_glue_size == 0)
@@ -3064,18 +4266,18 @@ record_vfp11_erratum_veneer (struct bfd_link_info *link_info,
       myh = (struct elf_link_hash_entry *) bh;
       myh->type = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE);
       myh->forced_local = 1;
-      
+
       /* The elf32_arm_init_maps function only cares about symbols from input
          BFDs.  We must make a note of this generated mapping symbol
          ourselves so that code byteswapping works properly in
          elf32_arm_write_section.  */
       elf32_arm_section_map_add (s, 'a', 0);
     }
-  
+
   s->size += VFP11_ERRATUM_VENEER_SIZE;
   hash_table->vfp11_erratum_glue_size += VFP11_ERRATUM_VENEER_SIZE;
   hash_table->num_vfp11_fixes++;
-  
+
   /* The offset of the veneer.  */
   return val;
 }
@@ -3095,6 +4297,10 @@ bfd_elf32_arm_add_glue_sections_to_bfd (bfd *abfd,
   if (info->relocatable)
     return TRUE;
 
+  /* linker stubs don't need glue */
+  if (!strcmp (abfd->filename, "linker stubs"))
+    return TRUE;
+
   sec = bfd_get_section_by_name (abfd, ARM2THUMB_GLUE_SECTION_NAME);
 
   if (sec == NULL)
@@ -3205,7 +4411,8 @@ bfd_elf32_arm_get_bfd_for_interworking (bfd *abfd, struct bfd_link_info *info)
   return TRUE;
 }
 
-static void check_use_blx(struct elf32_arm_link_hash_table *globals)
+static void
+check_use_blx (struct elf32_arm_link_hash_table *globals)
 {
   if (bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
                                Tag_CPU_arch) > 2)
@@ -3268,8 +4475,7 @@ bfd_elf32_arm_process_before_allocation (bfd *abfd,
 
       /* Load the relocs.  */
       internal_relocs
-       = _bfd_elf_link_read_relocs (abfd, sec, (void *) NULL,
-                                    (Elf_Internal_Rela *) NULL, FALSE);
+       = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL, FALSE);
 
       if (internal_relocs == NULL)
        goto error_return;
@@ -3288,9 +4494,7 @@ bfd_elf32_arm_process_before_allocation (bfd *abfd,
          /* These are the only relocation types we care about.  */
          if (   r_type != R_ARM_PC24
              && r_type != R_ARM_PLT32
-             && r_type != R_ARM_CALL
              && r_type != R_ARM_JUMP24
-             && r_type != R_ARM_THM_CALL
              && r_type != R_ARM_THM_JUMP24
              && (r_type != R_ARM_V4BX || globals->fix_v4bx < 2))
            continue;
@@ -3344,17 +4548,15 @@ bfd_elf32_arm_process_before_allocation (bfd *abfd,
            {
            case R_ARM_PC24:
            case R_ARM_PLT32:
-           case R_ARM_CALL:
            case R_ARM_JUMP24:
              /* This one is a call from arm code.  We need to look up
                 the target of the call.  If it is a thumb target, we
                 insert glue.  */
-             if (ELF_ST_TYPE(h->type) == STT_ARM_TFUNC
+             if (ELF_ST_TYPE (h->type) == STT_ARM_TFUNC
                  && !(r_type == R_ARM_CALL && globals->use_blx))
                record_arm_to_thumb_glue (link_info, h);
              break;
 
-           case R_ARM_THM_CALL:
            case R_ARM_THM_JUMP24:
              /* This one is a call from thumb code.  We look
                 up the target of the call.  If it is not a thumb
@@ -3426,13 +4628,13 @@ bfd_elf32_arm_init_maps (bfd *abfd)
       Elf_Internal_Sym *isym = &isymbuf[i];
       asection *sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
       const char *name;
-      
+
       if (sec != NULL
           && ELF_ST_BIND (isym->st_info) == STB_LOCAL)
         {
           name = bfd_elf_string_from_elf_section (abfd,
             hdr->sh_link, isym->st_name);
-          
+
           if (bfd_is_arm_special_symbol_name (name,
                                              BFD_ARM_SPECIAL_SYM_TYPE_MAP))
             elf32_arm_section_map_add (sec, name[1], isym->st_value);
@@ -3446,7 +4648,7 @@ bfd_elf32_arm_set_vfp11_fix (bfd *obfd, struct bfd_link_info *link_info)
 {
   struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info);
   obj_attribute *out_attr = elf_known_obj_attributes_proc (obfd);
-  
+
   /* We assume that ARMv7+ does not need the VFP11 denorm erratum fix.  */
   if (out_attr[Tag_CPU_arch].i >= TAG_CPU_ARCH_V7)
     {
@@ -3456,7 +4658,7 @@ bfd_elf32_arm_set_vfp11_fix (bfd *obfd, struct bfd_link_info *link_info)
         case BFD_ARM_VFP11_FIX_NONE:
           globals->vfp11_fix = BFD_ARM_VFP11_FIX_NONE;
           break;
-        
+
         default:
           /* Give a warning, but do as the user requests anyway.  */
           (*_bfd_error_handler) (_("%B: warning: selected VFP11 erratum "
@@ -3471,7 +4673,8 @@ bfd_elf32_arm_set_vfp11_fix (bfd *obfd, struct bfd_link_info *link_info)
 }
 
 
-enum bfd_arm_vfp11_pipe {
+enum bfd_arm_vfp11_pipe
+{
   VFP11_FMAC,
   VFP11_LS,
   VFP11_DS,
@@ -3486,10 +4689,10 @@ enum bfd_arm_vfp11_pipe {
 
      0...31: single-precision registers s0...s31
      32...63: double-precision registers d0...d31.
-  
+
    Although X should be zero for VFP11 (encoding d0...d15 only), we might
    encounter VFP3 instructions, so we allow the full range for DP registers.  */
-   
+
 static unsigned int
 bfd_arm_vfp11_regno (unsigned int insn, bfd_boolean is_double, unsigned int rx,
                      unsigned int x)
@@ -3518,23 +4721,23 @@ static bfd_boolean
 bfd_arm_vfp11_antidependency (unsigned int wmask, int *regs, int numregs)
 {
   int i;
-  
+
   for (i = 0; i < numregs; i++)
     {
       unsigned int reg = regs[i];
 
       if (reg < 32 && (wmask & (1 << reg)) != 0)
         return TRUE;
-      
+
       reg -= 32;
 
       if (reg >= 16)
         continue;
-      
+
       if ((wmask & (3 << (reg * 2))) != 0)
         return TRUE;
     }
-  
+
   return FALSE;
 }
 
@@ -3655,7 +4858,7 @@ bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
   else if ((insn & 0x0fe00ed0) == 0x0c400a10)
     {
       unsigned int fm = bfd_arm_vfp11_regno (insn, is_double, 0, 5);
-      
+
       if ((insn & 0x100000) == 0)
        {
           if (is_double)
@@ -3673,12 +4876,12 @@ bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
     {
       int fd = bfd_arm_vfp11_regno (insn, is_double, 12, 22);
       unsigned int puw = ((insn >> 21) & 0x1) | (((insn >> 23) & 3) << 1);
-      
+
       switch (puw)
         {
         case 0: /* Two-reg transfer.  We should catch these above.  */
           abort ();
-        
+
         case 2: /* fldm[sdx].  */
         case 3:
         case 5:
@@ -3692,12 +4895,12 @@ bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
               bfd_arm_vfp11_write_mask (destmask, i);
           }
           break;
-        
+
         case 4: /* fld[sd].  */
         case 6:
           bfd_arm_vfp11_write_mask (destmask, fd);
           break;
-        
+
         default:
           return VFP11_BAD;
         }
@@ -3748,10 +4951,10 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
   int regs[3], numregs = 0;
   struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info);
   int use_vector = (globals->vfp11_fix == BFD_ARM_VFP11_FIX_VECTOR);
-  
+
   /* We use a simple FSM to match troublesome VFP11 instruction sequences.
      The states transition as follows:
-     
+
        0 -> 1 (vector) or 0 -> 2 (scalar)
            A VFP FMAC-pipeline instruction has been seen. Fill
            regs[0]..regs[numregs-1] with its input operands. Remember this
@@ -3760,21 +4963,20 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
        1 -> 2
            Any instruction, except for a VFP instruction which overwrites
            regs[*].
-       
+
        1 -> 3 [ -> 0 ]  or
        2 -> 3 [ -> 0 ]
            A VFP instruction has been seen which overwrites any of regs[*].
            We must make a veneer!  Reset state to 0 before examining next
            instruction.
-       
+
        2 -> 0
            If we fail to match anything in state 2, reset to state 0 and reset
            the instruction pointer to the instruction after 'first_fmac'.
 
      If the VFP11 vector mode is in use, there must be at least two unrelated
      instructions between anti-dependent VFP11 instructions to properly avoid
-     triggering the erratum, hence the use of the extra state 1.
-  */
+     triggering the erratum, hence the use of the extra state 1.  */
 
   /* If we are only performing a partial link do not bother
      to construct any glue.  */
@@ -3784,7 +4986,7 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
   /* Skip if this bfd does not correspond to an ELF image.  */
   if (! is_arm_elf (abfd))
     return TRUE;
-  
+
   /* We should have chosen a fix type by the time we get here.  */
   BFD_ASSERT (globals->vfp11_fix != BFD_ARM_VFP11_FIX_DEFAULT);
 
@@ -3805,10 +5007,10 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
         continue;
 
       sec_data = elf32_arm_section_data (sec);
-      
+
       if (sec_data->mapcount == 0)
         continue;
-      
+
       if (elf_section_data (sec)->this_hdr.contents != NULL)
        contents = elf_section_data (sec)->this_hdr.contents;
       else if (! bfd_malloc_and_get_section (abfd, sec, &contents))
@@ -3823,7 +5025,7 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
           unsigned int span_end = (span == sec_data->mapcount - 1)
                                  ? sec->size : sec_data->map[span + 1].vma;
           char span_type = sec_data->map[span].type;
-          
+
           /* FIXME: Only ARM mode is supported at present.  We may need to
              support Thumb-2 mode also at some point.  */
           if (span_type != 'a')
@@ -3912,7 +5114,7 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
                     case 'a':
                       newerr->type = VFP11_ERRATUM_BRANCH_TO_ARM_VENEER;
                       break;
-                    
+
                     default:
                       abort ();
                     }
@@ -3931,7 +5133,7 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
               i = next_i;
             }
         }
-      
+
       if (contents != NULL
           && elf_section_data (sec)->this_hdr.contents != contents)
         free (contents);
@@ -3944,7 +5146,7 @@ error_return:
   if (contents != NULL
       && elf_section_data (sec)->this_hdr.contents != contents)
     free (contents);
-  
+
   return FALSE;
 }
 
@@ -3958,7 +5160,7 @@ bfd_elf32_arm_vfp11_fix_veneer_locations (bfd *abfd,
   asection *sec;
   struct elf32_arm_link_hash_table *globals;
   char *tmp_name;
-  
+
   if (link_info->relocatable)
     return;
 
@@ -3967,7 +5169,7 @@ bfd_elf32_arm_vfp11_fix_veneer_locations (bfd *abfd,
     return;
 
   globals = elf32_arm_hash_table (link_info);
-  
+
   tmp_name = bfd_malloc ((bfd_size_type) strlen
                           (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10);
 
@@ -3975,7 +5177,7 @@ bfd_elf32_arm_vfp11_fix_veneer_locations (bfd *abfd,
     {
       struct _arm_elf_section_data *sec_data = elf32_arm_section_data (sec);
       elf32_vfp11_erratum_list *errnode = sec_data->erratumlist;
-      
+
       for (; errnode != NULL; errnode = errnode->next)
         {
           struct elf_link_hash_entry *myh;
@@ -4022,13 +5224,13 @@ bfd_elf32_arm_vfp11_fix_veneer_locations (bfd *abfd,
 
               errnode->u.v.branch->vma = vma;
               break;
-            
+
             default:
               abort ();
             }
         }
     }
-  
+
   free (tmp_name);
 }
 
@@ -4087,7 +5289,7 @@ insert_thumb_branch (bfd *abfd, long int offset, bfd_byte *insn)
   upper = (upper & ~(bfd_vma) 0x7ff)
          | ((offset >> 12) & 0x3ff)
          | (reloc_sign << 10);
-  lower = (lower & ~(bfd_vma) 0x2fff) 
+  lower = (lower & ~(bfd_vma) 0x2fff)
          | (((!((offset >> 23) & 1)) ^ reloc_sign) << 13)
          | (((!((offset >> 22) & 1)) ^ reloc_sign) << 11)
          | ((offset >> 1) & 0x7ff);
@@ -4095,35 +5297,6 @@ insert_thumb_branch (bfd *abfd, long int offset, bfd_byte *insn)
   bfd_put_16 (abfd, lower, insn + 2);
 }
 
-
-/* Store an Arm insn into an output section not processed by
-   elf32_arm_write_section.  */
-
-static void
-put_arm_insn (struct elf32_arm_link_hash_table *htab,
-            bfd * output_bfd, bfd_vma val, void * ptr)
-{
-    if (htab->byteswap_code != bfd_little_endian (output_bfd))
-      bfd_putl32 (val, ptr);
-    else
-      bfd_putb32 (val, ptr);
-}
-
-
-/* Store a 16-bit Thumb insn into an output section not processed by
-   elf32_arm_write_section.  */
-
-static void
-put_thumb_insn (struct elf32_arm_link_hash_table *htab,
-              bfd * output_bfd, bfd_vma val, void * ptr)
-{
-    if (htab->byteswap_code != bfd_little_endian (output_bfd))
-      bfd_putl16 (val, ptr);
-    else
-      bfd_putb16 (val, ptr);
-}
-
-
 /* Thumb code calling an ARM function.  */
 
 static int
@@ -4391,7 +5564,7 @@ elf32_arm_to_thumb_export_stub (struct elf_link_hash_entry *h, void * inf)
   bfd_vma val;
   char *error_message;
 
-  eh = elf32_arm_hash_entry(h);
+  eh = elf32_arm_hash_entry (h);
   /* Allocate stubs for exported Thumb functions on v4t.  */
   if (eh->export_glue == NULL)
     return TRUE;
@@ -4460,7 +5633,7 @@ elf32_arm_bx_glue (struct bfd_link_info * info, int reg)
 
 /* Generate Arm stubs for exported Thumb symbols.  */
 static void
-elf32_arm_begin_write_processing (bfd *abfd ATTRIBUTE_UNUSED, 
+elf32_arm_begin_write_processing (bfd *abfd ATTRIBUTE_UNUSED,
                                  struct bfd_link_info *link_info)
 {
   struct elf32_arm_link_hash_table * globals;
@@ -4597,8 +5770,9 @@ calculate_group_reloc_mask (bfd_vma value, int n, bfd_vma *final_residual)
 
 /* Given an ARM instruction, determine whether it is an ADD or a SUB.
    Returns 1 if it is an ADD, -1 if it is a SUB, and 0 otherwise.  */
+
 static int
-identify_add_or_sub(bfd_vma insn)
+identify_add_or_sub (bfd_vma insn)
 {
   int opcode = insn & 0x1e00000;
 
@@ -4611,15 +5785,6 @@ identify_add_or_sub(bfd_vma insn)
   return 0;
 }
 
-/* Determine if we're dealing with a Thumb-2 object.  */
-
-static int using_thumb2 (struct elf32_arm_link_hash_table *globals)
-{
-  int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
-                                      Tag_CPU_arch);
-  return arch == TAG_CPU_ARCH_V6T2 || arch >= TAG_CPU_ARCH_V7;
-}
-
 /* Perform a relocation as part of a final link.  */
 
 static bfd_reloc_status_type
@@ -4888,6 +6053,16 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        case R_ARM_JUMP24:
        case R_ARM_PC24:          /* Arm B/BL instruction */
        case R_ARM_PLT32:
+         {
+         bfd_vma from;
+         bfd_signed_vma branch_offset;
+         struct elf32_arm_stub_hash_entry *stub_entry = NULL;
+
+         from = (input_section->output_section->vma
+                 + input_section->output_offset
+                 + rel->r_offset);
+         branch_offset = (bfd_signed_vma)(value - from);
+
          if (r_type == R_ARM_XPC25)
            {
              /* Check for Arm calling Arm function.  */
@@ -4899,7 +6074,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                   input_bfd,
                   h ? h->root.root.string : "(local)");
            }
-         else if (r_type != R_ARM_CALL || !globals->use_blx)
+         else if (r_type != R_ARM_CALL)
            {
              /* Check for Arm calling Thumb function.  */
              if (sym_flags == STT_ARM_TFUNC)
@@ -4915,6 +6090,27 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                }
            }
 
+         /* Check if a stub has to be inserted because the
+            destination is too far or we are changing mode */
+         if (r_type == R_ARM_CALL)
+           {
+             if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
+                 || branch_offset < ARM_MAX_BWD_BRANCH_OFFSET
+                 || sym_flags == STT_ARM_TFUNC)
+               {
+                 /* The target is out of reach, so redirect the
+                    branch to the local stub for this function.  */
+
+                 stub_entry = elf32_arm_get_stub_entry (input_section,
+                                                        sym_sec, h,
+                                                        rel, globals);
+                 if (stub_entry != NULL)
+                   value = (stub_entry->stub_offset
+                            + stub_entry->stub_sec->output_offset
+                            + stub_entry->stub_sec->output_section->vma);
+               }
+           }
+
          /* 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.
@@ -4976,7 +6172,9 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
              if (r_type == R_ARM_CALL)
                {
                  /* Select the correct instruction (BL or BLX).  */
-                 if (sym_flags == STT_ARM_TFUNC)
+                 /* Only if we are not handling a BL to a stub. In this
+                    case, mode switching is performed by the stub. */
+                 if (sym_flags == STT_ARM_TFUNC && !stub_entry)
                    value |= (1 << 28);
                  else
                    {
@@ -4985,6 +6183,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                    }
                }
            }
+         }
          break;
 
        case R_ARM_ABS32:
@@ -5248,6 +6447,43 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
            *unresolved_reloc_p = FALSE;
          }
 
+       if (r_type == R_ARM_THM_CALL)
+         {
+           /* Check if a stub has to be inserted because the destination
+              is too far. */
+           bfd_vma from;
+           bfd_signed_vma branch_offset;
+           struct elf32_arm_stub_hash_entry *stub_entry = NULL;
+
+           from = (input_section->output_section->vma
+                   + input_section->output_offset
+                   + rel->r_offset);
+           branch_offset = (bfd_signed_vma)(value - from);
+
+           if ((!thumb2
+                && (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
+                    || (branch_offset < THM_MAX_BWD_BRANCH_OFFSET)))
+               ||
+               (thumb2
+                && (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
+                    || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET))))
+             {
+               /* The target is out of reach or we are changing modes, so
+                  redirect the branch to the local stub for this
+                  function.  */
+               stub_entry = elf32_arm_get_stub_entry (input_section,
+                                                      sym_sec, h,
+                                                      rel, globals);
+               if (stub_entry != NULL)
+                 value = (stub_entry->stub_offset
+                          + stub_entry->stub_sec->output_offset
+                          + stub_entry->stub_sec->output_section->vma);
+
+               /* This call becomes a call to Arm for sure. Force BLX.  */
+               lower_insn = (lower_insn & ~0x1000) | 0x0800;
+             }
+         }
+
        relocation = value + signed_addend;
 
        relocation -= (input_section->output_section->vma
@@ -5290,7 +6526,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        upper_insn = (upper_insn & ~(bfd_vma) 0x7ff)
                      | ((relocation >> 12) & 0x3ff)
                      | (reloc_sign << 10);
-       lower_insn = (lower_insn & ~(bfd_vma) 0x2fff) 
+       lower_insn = (lower_insn & ~(bfd_vma) 0x2fff)
                      | (((!((relocation >> 23) & 1)) ^ reloc_sign) << 13)
                      | (((!((relocation >> 22) & 1)) ^ reloc_sign) << 11)
                      | ((relocation >> 1) & 0x7ff);
@@ -5655,7 +6891,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
            globals->tls_ldm_got.offset |= 1;
          }
 
-       value = globals->sgot->output_section->vma + globals->sgot->output_offset + off 
+       value = globals->sgot->output_section->vma + globals->sgot->output_offset + off
          - (input_section->output_section->vma + input_section->output_offset + rel->r_offset);
 
        return _bfd_final_link_relocate (howto, input_bfd, input_section,
@@ -5813,7 +7049,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
 
        if ((tls_type & GOT_TLS_GD) && r_type != R_ARM_TLS_GD32)
          off += 8;
-       value = globals->sgot->output_section->vma + globals->sgot->output_offset + off 
+       value = globals->sgot->output_section->vma + globals->sgot->output_offset + off
          - (input_section->output_section->vma + input_section->output_offset + rel->r_offset);
 
        return _bfd_final_link_relocate (howto, input_bfd, input_section,
@@ -5828,11 +7064,11 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
            (_("%B(%A+0x%lx): R_ARM_TLS_LE32 relocation not permitted in shared object"),
             input_bfd, input_section,
             (long) rel->r_offset, howto->name);
-         return FALSE;   
+         return FALSE;
        }
       else
        value = tpoff (info, value);
-      
+
       return _bfd_final_link_relocate (howto, input_bfd, input_section,
                                       contents, rel->r_offset, value,
                                       rel->r_addend);
@@ -5924,7 +7160,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
     case R_ARM_THM_MOVT_BREL:
       {
        bfd_vma insn;
-       
+
        insn = bfd_get_16 (input_bfd, hit_data) << 16;
        insn |= bfd_get_16 (input_bfd, hit_data + 2);
 
@@ -6010,7 +7246,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
             break;
 
           default:
-            abort();
+            abort ();
           }
 
         /* If REL, extract the addend from the insn.  If RELA, it will
@@ -6043,7 +7279,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                   (_("%B(%A+0x%lx): Only ADD or SUB instructions are allowed for ALU group relocations"),
                   input_bfd, input_section,
                   (long) rel->r_offset, howto->name);
-                return bfd_reloc_overflow;       
+                return bfd_reloc_overflow;
              }
 
             signed_addend *= negative;
@@ -6138,7 +7374,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
             break;
 
           default:
-            abort();
+            abort ();
           }
 
         /* If REL, extract the addend from the insn.  If RELA, it will
@@ -6221,7 +7457,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
             break;
 
           default:
-            abort();
+            abort ();
           }
 
         /* If REL, extract the addend from the insn.  If RELA, it will
@@ -6304,7 +7540,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
             break;
 
           default:
-            abort();
+            abort ();
           }
 
         /* If REL, extract the addend from the insn.  If RELA, it will
@@ -6445,6 +7681,7 @@ arm_add_to_rel (bfd *              abfd,
    || (R_TYPE) == R_ARM_TLS_IE32)
 
 /* Relocate an ARM ELF section.  */
+
 static bfd_boolean
 elf32_arm_relocate_section (bfd *                  output_bfd,
                            struct bfd_link_info * info,
@@ -6881,6 +8118,7 @@ enum
 
 /* Determine whether an object attribute tag takes an integer, a
    string or both.  */
+
 static int
 elf32_arm_obj_attrs_arg_type (int tag)
 {
@@ -6896,6 +8134,7 @@ elf32_arm_obj_attrs_arg_type (int tag)
 
 /* Merge EABI object attributes from IBFD into OBFD.  Raise an error if there
    are conflicting attributes.  */
+
 static bfd_boolean
 elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
 {
@@ -7562,7 +8801,7 @@ elf32_arm_gc_sweep_hook (bfd *                     abfd,
   sym_hashes = elf_sym_hashes (abfd);
   local_got_refcounts = elf_local_got_refcounts (abfd);
 
-  check_use_blx(globals);
+  check_use_blx (globals);
 
   relend = relocs + sec->reloc_count;
   for (rel = relocs; rel < relend; rel++)
@@ -7775,9 +9014,9 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  if (local_got_refcounts == NULL)
                    {
                      bfd_size_type size;
-                     
+
                      size = symtab_hdr->sh_info;
-                     size *= (sizeof (bfd_signed_vma) + sizeof(char));
+                     size *= (sizeof (bfd_signed_vma) + sizeof (char));
                      local_got_refcounts = bfd_zalloc (abfd, size);
                      if (local_got_refcounts == NULL)
                        return FALSE;
@@ -8020,8 +9259,8 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
    required if the corresponding code section is marked.  */
 
 static bfd_boolean
-elf32_arm_gc_mark_extra_sections(struct bfd_link_info *info,
-                                elf_gc_mark_hook_fn gc_mark_hook)
+elf32_arm_gc_mark_extra_sections (struct bfd_link_info *info,
+                                 elf_gc_mark_hook_fn gc_mark_hook)
 {
   bfd *sub;
   Elf_Internal_Shdr **elf_shdrp;
@@ -8131,7 +9370,7 @@ arm_elf_find_function (bfd *         abfd ATTRIBUTE_UNUSED,
     *functionname_ptr = bfd_asymbol_name (func);
 
   return TRUE;
-}  
+}
 
 
 /* Find the nearest line to a particular section and offset, for error
@@ -8531,7 +9770,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
       bh = NULL;
       /* Create a new symbol to regist the real location of the function.  */
       s = h->root.u.def.section;
-      sprintf(name, "__real_%s", h->root.root.string);
+      sprintf (name, "__real_%s", h->root.root.string);
       _bfd_generic_link_add_one_symbol (info, s->owner,
                                        name, BSF_GLOBAL, s,
                                        h->root.u.def.value,
@@ -8835,7 +10074,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
 
       /* Initialise mapping tables for code/data.  */
       bfd_elf32_arm_init_maps (ibfd);
-      
+
       if (!bfd_elf32_arm_process_before_allocation (ibfd, info)
          || !bfd_elf32_arm_vfp11_erratum_scan (ibfd, info))
         /* xgettext:c-format */
@@ -8904,7 +10143,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
        continue;
 
       /* Allocate memory for the section contents.  */
-      s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size);
+      s->contents = bfd_zalloc (dynobj, s->size);
       if (s->contents == NULL)
        return FALSE;
     }
@@ -8977,8 +10216,10 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
    dynamic sections here.  */
 
 static bfd_boolean
-elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info,
-                                struct elf_link_hash_entry * h, Elf_Internal_Sym * sym)
+elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
+                                struct bfd_link_info * info,
+                                struct elf_link_hash_entry * h,
+                                Elf_Internal_Sym * sym)
 {
   bfd * dynobj;
   struct elf32_arm_link_hash_table *htab;
@@ -9008,13 +10249,13 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info,
       /* Fill in the entry in the procedure linkage table.  */
       if (htab->symbian_p)
        {
-         put_arm_insn (htab, output_bfd, 
+         put_arm_insn (htab, output_bfd,
                      elf32_arm_symbian_plt_entry[0],
                      splt->contents + h->plt.offset);
-         bfd_put_32 (output_bfd, 
+         bfd_put_32 (output_bfd,
                      elf32_arm_symbian_plt_entry[1],
                      splt->contents + h->plt.offset + 4);
-         
+
          /* Fill in the entry in the .rel.plt section.  */
          rel.r_offset = (splt->output_section->vma
                          + splt->output_offset
@@ -9025,7 +10266,7 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info,
             corresponds to this symbol.  This is the index of this symbol
             in all the symbols for which we are making plt entries.  The
             first entry in the procedure linkage table is reserved.  */
-         plt_index = ((h->plt.offset - htab->plt_header_size) 
+         plt_index = ((h->plt.offset - htab->plt_header_size)
                       / htab->plt_entry_size);
        }
       else
@@ -9034,7 +10275,7 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info,
          bfd_vma got_displacement;
          asection * sgot;
          bfd_byte * ptr;
-         
+
          sgot = bfd_get_section_by_name (dynobj, ".got.plt");
          BFD_ASSERT (sgot != NULL);
 
@@ -9161,7 +10402,7 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info,
                      (splt->output_section->vma
                       + splt->output_offset),
                      sgot->contents + got_offset);
-         
+
          /* Fill in the entry in the .rel(a).plt section.  */
          rel.r_addend = 0;
          rel.r_offset = got_address;
@@ -9215,7 +10456,7 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info,
       if (info->shared
          && SYMBOL_REFERENCES_LOCAL (info, h))
        {
-         BFD_ASSERT((h->got.offset & 1) != 0);
+         BFD_ASSERT ((h->got.offset & 1) != 0);
          rel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
          if (!htab->use_rel)
            {
@@ -9225,7 +10466,7 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info,
        }
       else
        {
-         BFD_ASSERT((h->got.offset & 1) == 0);
+         BFD_ASSERT ((h->got.offset & 1) == 0);
          bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + offset);
          rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_GLOB_DAT);
        }
@@ -9363,7 +10604,7 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
              dyn.d_un.d_val = s->size;
              bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
              break;
-             
+
            case DT_RELSZ:
            case DT_RELASZ:
              if (!htab->symbian_p)
@@ -9404,11 +10645,11 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
                  dyn.d_un.d_val = 0;
                  for (i = 1; i < elf_numsections (output_bfd); i++)
                    {
-                     Elf_Internal_Shdr *hdr 
+                     Elf_Internal_Shdr *hdr
                        = elf_elfsections (output_bfd)[i];
                      if (hdr->sh_type == type)
                        {
-                         if (dyn.d_tag == DT_RELSZ 
+                         if (dyn.d_tag == DT_RELSZ
                              || dyn.d_tag == DT_RELASZ)
                            dyn.d_un.d_val += hdr->sh_size;
                          else if ((ufile_ptr) hdr->sh_offset
@@ -9436,7 +10677,7 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
 
                  eh = elf_link_hash_lookup (elf_hash_table (info), name,
                                             FALSE, FALSE, TRUE);
-                 if (eh != (struct elf_link_hash_entry *) NULL
+                 if (eh != NULL
                      && ELF_ST_TYPE (eh->type) == STT_ARM_TFUNC)
                    {
                      dyn.d_un.d_val |= 1;
@@ -9881,7 +11122,7 @@ elf32_arm_output_plt_map (struct elf_link_hash_entry *h, void *inf)
       if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 12))
        return FALSE;
 #else
-      /* A three-word PLT with no Thumb thunk contains only Arm code, 
+      /* A three-word PLT with no Thumb thunk contains only Arm code,
         so only need to output a mapping symbol for the first PLT entry and
         entries with thumb thunks.  */
       if (thumb_refs > 0 || addr == 20)
@@ -9900,11 +11141,12 @@ elf32_arm_output_plt_map (struct elf_link_hash_entry *h, void *inf)
 
 static bfd_boolean
 elf32_arm_output_arch_local_syms (bfd *output_bfd,
-    struct bfd_link_info *info,
-    void *finfo, bfd_boolean (*func) (void *, const char *,
-                                   Elf_Internal_Sym *,
-                                   asection *,
-                                   struct elf_link_hash_entry *))
+                                 struct bfd_link_info *info,
+                                 void *finfo,
+                                 bfd_boolean (*func) (void *, const char *,
+                                                      Elf_Internal_Sym *,
+                                                      asection *,
+                                                      struct elf_link_hash_entry *))
 {
   output_arch_syminfo osi;
   struct elf32_arm_link_hash_table *htab;
@@ -9912,12 +11154,12 @@ elf32_arm_output_arch_local_syms (bfd *output_bfd,
   bfd_size_type size;
 
   htab = elf32_arm_hash_table (info);
-  check_use_blx(htab);
+  check_use_blx (htab);
 
   osi.finfo = finfo;
   osi.info = info;
   osi.func = func;
-  
+
   /* ARM->Thumb glue.  */
   if (htab->arm_glue_size > 0)
     {
@@ -10309,7 +11551,7 @@ elf32_arm_swap_symbol_out (bfd *abfd,
           */
           newsym.st_value |= 1;
         }
-      
+
       src = &newsym;
     }
   bfd_elf32_swap_symbol_out (abfd, src, cdst, shndx);
@@ -10318,7 +11560,7 @@ elf32_arm_swap_symbol_out (bfd *abfd,
 /* Add the PT_ARM_EXIDX program header.  */
 
 static bfd_boolean
-elf32_arm_modify_segment_map (bfd *abfd, 
+elf32_arm_modify_segment_map (bfd *abfd,
                              struct bfd_link_info *info ATTRIBUTE_UNUSED)
 {
   struct elf_segment_map *m;
@@ -10366,6 +11608,7 @@ elf32_arm_additional_program_headers (bfd *abfd,
 }
 
 /* We have two function types: STT_FUNC and STT_ARM_TFUNC.  */
+
 static bfd_boolean
 elf32_arm_is_function_type (unsigned int type)
 {
@@ -10373,7 +11616,8 @@ elf32_arm_is_function_type (unsigned int type)
 }
 
 /* We use this to override swap_symbol_in and swap_symbol_out.  */
-const struct elf_size_info elf32_arm_size_info = {
+const struct elf_size_info elf32_arm_size_info =
+{
   sizeof (Elf32_External_Ehdr),
   sizeof (Elf32_External_Phdr),
   sizeof (Elf32_External_Shdr),
@@ -10419,6 +11663,7 @@ const struct elf_size_info elf32_arm_size_info = {
 #define bfd_elf32_bfd_set_private_flags                elf32_arm_set_private_flags
 #define bfd_elf32_bfd_print_private_bfd_data   elf32_arm_print_private_bfd_data
 #define bfd_elf32_bfd_link_hash_table_create    elf32_arm_link_hash_table_create
+#define bfd_elf32_bfd_link_hash_table_free      elf32_arm_hash_table_free
 #define bfd_elf32_bfd_reloc_type_lookup                elf32_arm_reloc_type_lookup
 #define bfd_elf32_bfd_reloc_name_lookup        elf32_arm_reloc_name_lookup
 #define bfd_elf32_find_nearest_line            elf32_arm_find_nearest_line
@@ -10452,49 +11697,47 @@ const struct elf_size_info elf32_arm_size_info = {
 #define elf_backend_symbol_processing          elf32_arm_symbol_processing
 #define elf_backend_size_info                  elf32_arm_size_info
 #define elf_backend_modify_segment_map         elf32_arm_modify_segment_map
-#define elf_backend_additional_program_headers \
-  elf32_arm_additional_program_headers
-#define elf_backend_output_arch_local_syms \
-  elf32_arm_output_arch_local_syms
-#define elf_backend_begin_write_processing \
-    elf32_arm_begin_write_processing
-#define elf_backend_is_function_type           elf32_arm_is_function_type 
-
-#define elf_backend_can_refcount    1
-#define elf_backend_can_gc_sections 1
-#define elf_backend_plt_readonly    1
-#define elf_backend_want_got_plt    1
-#define elf_backend_want_plt_sym    0
-#define elf_backend_may_use_rel_p   1
-#define elf_backend_may_use_rela_p  0
+#define elf_backend_additional_program_headers  elf32_arm_additional_program_headers
+#define elf_backend_output_arch_local_syms      elf32_arm_output_arch_local_syms
+#define elf_backend_begin_write_processing      elf32_arm_begin_write_processing
+#define elf_backend_is_function_type           elf32_arm_is_function_type
+
+#define elf_backend_can_refcount       1
+#define elf_backend_can_gc_sections    1
+#define elf_backend_plt_readonly       1
+#define elf_backend_want_got_plt       1
+#define elf_backend_want_plt_sym       0
+#define elf_backend_may_use_rel_p      1
+#define elf_backend_may_use_rela_p     0
 #define elf_backend_default_use_rela_p 0
 
 #define elf_backend_got_header_size    12
 
-#undef elf_backend_obj_attrs_vendor
-#define elf_backend_obj_attrs_vendor   "aeabi"
-#undef elf_backend_obj_attrs_section
-#define elf_backend_obj_attrs_section  ".ARM.attributes"
-#undef elf_backend_obj_attrs_arg_type
-#define elf_backend_obj_attrs_arg_type elf32_arm_obj_attrs_arg_type
-#undef elf_backend_obj_attrs_section_type
+#undef  elf_backend_obj_attrs_vendor
+#define elf_backend_obj_attrs_vendor           "aeabi"
+#undef  elf_backend_obj_attrs_section
+#define elf_backend_obj_attrs_section          ".ARM.attributes"
+#undef  elf_backend_obj_attrs_arg_type
+#define elf_backend_obj_attrs_arg_type         elf32_arm_obj_attrs_arg_type
+#undef  elf_backend_obj_attrs_section_type
 #define elf_backend_obj_attrs_section_type     SHT_ARM_ATTRIBUTES
 
 #include "elf32-target.h"
 
-/* VxWorks Targets */
+/* VxWorks Targets */
 
-#undef TARGET_LITTLE_SYM
+#undef  TARGET_LITTLE_SYM
 #define TARGET_LITTLE_SYM               bfd_elf32_littlearm_vxworks_vec
-#undef TARGET_LITTLE_NAME
+#undef  TARGET_LITTLE_NAME
 #define TARGET_LITTLE_NAME              "elf32-littlearm-vxworks"
-#undef TARGET_BIG_SYM
+#undef  TARGET_BIG_SYM
 #define TARGET_BIG_SYM                  bfd_elf32_bigarm_vxworks_vec
-#undef TARGET_BIG_NAME
+#undef  TARGET_BIG_NAME
 #define TARGET_BIG_NAME                 "elf32-bigarm-vxworks"
 
 /* Like elf32_arm_link_hash_table_create -- but overrides
    appropriately for VxWorks.  */
+
 static struct bfd_link_hash_table *
 elf32_arm_vxworks_link_hash_table_create (bfd *abfd)
 {
@@ -10509,7 +11752,7 @@ elf32_arm_vxworks_link_hash_table_create (bfd *abfd)
       htab->vxworks_p = 1;
     }
   return ret;
-}     
+}
 
 static void
 elf32_arm_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker)
@@ -10518,49 +11761,46 @@ elf32_arm_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker)
   elf_vxworks_final_write_processing (abfd, linker);
 }
 
-#undef elf32_bed
+#undef  elf32_bed
 #define elf32_bed elf32_arm_vxworks_bed
 
-#undef bfd_elf32_bfd_link_hash_table_create
-#define bfd_elf32_bfd_link_hash_table_create \
-  elf32_arm_vxworks_link_hash_table_create
-#undef elf_backend_add_symbol_hook
-#define elf_backend_add_symbol_hook \
-  elf_vxworks_add_symbol_hook
-#undef elf_backend_final_write_processing
-#define elf_backend_final_write_processing \
-  elf32_arm_vxworks_final_write_processing
-#undef elf_backend_emit_relocs
-#define elf_backend_emit_relocs \
-  elf_vxworks_emit_relocs
+#undef  bfd_elf32_bfd_link_hash_table_create
+#define bfd_elf32_bfd_link_hash_table_create   elf32_arm_vxworks_link_hash_table_create
+#undef  elf_backend_add_symbol_hook
+#define elf_backend_add_symbol_hook            elf_vxworks_add_symbol_hook
+#undef  elf_backend_final_write_processing
+#define elf_backend_final_write_processing     elf32_arm_vxworks_final_write_processing
+#undef  elf_backend_emit_relocs
+#define elf_backend_emit_relocs                        elf_vxworks_emit_relocs
 
-#undef elf_backend_may_use_rel_p
+#undef  elf_backend_may_use_rel_p
 #define elf_backend_may_use_rel_p      0
-#undef elf_backend_may_use_rela_p
+#undef  elf_backend_may_use_rela_p
 #define elf_backend_may_use_rela_p     1
-#undef elf_backend_default_use_rela_p
+#undef  elf_backend_default_use_rela_p
 #define elf_backend_default_use_rela_p 1
-#undef elf_backend_want_plt_sym
+#undef  elf_backend_want_plt_sym
 #define elf_backend_want_plt_sym       1
-#undef ELF_MAXPAGESIZE
+#undef  ELF_MAXPAGESIZE
 #define ELF_MAXPAGESIZE                        0x1000
 
 #include "elf32-target.h"
 
 
-/* Symbian OS Targets */
+/* Symbian OS Targets */
 
-#undef TARGET_LITTLE_SYM
+#undef  TARGET_LITTLE_SYM
 #define TARGET_LITTLE_SYM               bfd_elf32_littlearm_symbian_vec
-#undef TARGET_LITTLE_NAME
+#undef  TARGET_LITTLE_NAME
 #define TARGET_LITTLE_NAME              "elf32-littlearm-symbian"
-#undef TARGET_BIG_SYM
+#undef  TARGET_BIG_SYM
 #define TARGET_BIG_SYM                  bfd_elf32_bigarm_symbian_vec
-#undef TARGET_BIG_NAME
+#undef  TARGET_BIG_NAME
 #define TARGET_BIG_NAME                 "elf32-bigarm-symbian"
 
 /* Like elf32_arm_link_hash_table_create -- but overrides
    appropriately for Symbian OS.  */
+
 static struct bfd_link_hash_table *
 elf32_arm_symbian_link_hash_table_create (bfd *abfd)
 {
@@ -10581,7 +11821,7 @@ elf32_arm_symbian_link_hash_table_create (bfd *abfd)
       htab->root.is_relocatable_executable = 1;
     }
   return ret;
-}     
+}
 
 static const struct bfd_elf_special_section
 elf32_arm_symbian_special_sections[] =
@@ -10605,7 +11845,7 @@ elf32_arm_symbian_special_sections[] =
 };
 
 static void
-elf32_arm_symbian_begin_write_processing (bfd *abfd, 
+elf32_arm_symbian_begin_write_processing (bfd *abfd,
                                          struct bfd_link_info *link_info)
 {
   /* BPABI objects are never loaded directly by an OS kernel; they are
@@ -10617,11 +11857,11 @@ elf32_arm_symbian_begin_write_processing (bfd *abfd,
      recognize that the program headers should not be mapped into any
      loadable segment.  */
   abfd->flags &= ~D_PAGED;
-  elf32_arm_begin_write_processing(abfd, link_info);
+  elf32_arm_begin_write_processing (abfd, link_info);
 }
 
 static bfd_boolean
-elf32_arm_symbian_modify_segment_map (bfd *abfd, 
+elf32_arm_symbian_modify_segment_map (bfd *abfd,
                                      struct bfd_link_info *info)
 {
   struct elf_segment_map *m;
@@ -10666,49 +11906,45 @@ elf32_arm_symbian_plt_sym_val (bfd_vma i, const asection *plt,
 
 /* The dynamic sections are not allocated on SymbianOS; the postlinker
    will process them and then discard them.  */
-#undef ELF_DYNAMIC_SEC_FLAGS
+#undef  ELF_DYNAMIC_SEC_FLAGS
 #define ELF_DYNAMIC_SEC_FLAGS \
   (SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED)
 
-#undef bfd_elf32_bfd_link_hash_table_create
-#define bfd_elf32_bfd_link_hash_table_create \
-  elf32_arm_symbian_link_hash_table_create
 #undef elf_backend_add_symbol_hook
-
-#undef elf_backend_special_sections
-#define elf_backend_special_sections elf32_arm_symbian_special_sections
-
-#undef elf_backend_begin_write_processing
-#define elf_backend_begin_write_processing \
-    elf32_arm_symbian_begin_write_processing
-#undef elf_backend_final_write_processing
-#define elf_backend_final_write_processing \
-  elf32_arm_final_write_processing
 #undef elf_backend_emit_relocs
 
-#undef elf_backend_modify_segment_map
+#undef  bfd_elf32_bfd_link_hash_table_create
+#define bfd_elf32_bfd_link_hash_table_create   elf32_arm_symbian_link_hash_table_create
+#undef  elf_backend_special_sections
+#define elf_backend_special_sections           elf32_arm_symbian_special_sections
+#undef  elf_backend_begin_write_processing
+#define elf_backend_begin_write_processing     elf32_arm_symbian_begin_write_processing
+#undef  elf_backend_final_write_processing
+#define elf_backend_final_write_processing     elf32_arm_final_write_processing
+
+#undef  elf_backend_modify_segment_map
 #define elf_backend_modify_segment_map elf32_arm_symbian_modify_segment_map
 
 /* There is no .got section for BPABI objects, and hence no header.  */
-#undef elf_backend_got_header_size
+#undef  elf_backend_got_header_size
 #define elf_backend_got_header_size 0
 
 /* Similarly, there is no .got.plt section.  */
-#undef elf_backend_want_got_plt
+#undef  elf_backend_want_got_plt
 #define elf_backend_want_got_plt 0
 
-#undef elf_backend_plt_sym_val
+#undef  elf_backend_plt_sym_val
 #define elf_backend_plt_sym_val                elf32_arm_symbian_plt_sym_val
 
-#undef elf_backend_may_use_rel_p
+#undef  elf_backend_may_use_rel_p
 #define elf_backend_may_use_rel_p      1
-#undef elf_backend_may_use_rela_p
+#undef  elf_backend_may_use_rela_p
 #define elf_backend_may_use_rela_p     0
-#undef elf_backend_default_use_rela_p
+#undef  elf_backend_default_use_rela_p
 #define elf_backend_default_use_rela_p 0
-#undef elf_backend_want_plt_sym
+#undef  elf_backend_want_plt_sym
 #define elf_backend_want_plt_sym       0
-#undef ELF_MAXPAGESIZE
+#undef  ELF_MAXPAGESIZE
 #define ELF_MAXPAGESIZE                        0x8000
 
 #include "elf32-target.h"
index 6ce1b9f..aad8253 100644 (file)
@@ -1397,7 +1397,8 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
        dynindx = entry->d.h->dynindx;
       else
        {
-         if (sec->output_section
+         if (sec
+             && sec->output_section
              && ! bfd_is_abs_section (sec->output_section)
              && ! bfd_is_und_section (sec->output_section))
            dynindx = elf_section_data (sec->output_section)->dynindx;
@@ -1679,8 +1680,9 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
             of the section.  For a non-local function, it's
             disregarded.  */
          lowword = ad;
-         if (entry->symndx == -1 && entry->d.h->dynindx != -1
-             && entry->d.h->dynindx == idx)
+         if (sec == NULL
+             || (entry->symndx == -1 && entry->d.h->dynindx != -1
+                 && entry->d.h->dynindx == idx))
            highword = 0;
          else
            highword = _frvfdpic_osec_to_segment
index 72fd32e..f43f5de 100644 (file)
@@ -1,3 +1,27 @@
+2008-05-15  Christophe Lyon  <christophe.lyon@st.com>
+       
+       * emultempl/armelf.em (build_section_lists): New function.
+       (stub_file): Define.
+       (need_laying_out): Define.
+       (group_size): Define.
+       (hook_stub_info): Define.
+       (hook_in_stub): New function.
+       (elf32_arm_add_stub_section): New function.
+       (gldarm_layout_sections_again): New function.
+       (gld${EMULATION_NAME}_finish): Replace arm_elf_finish(). Generate
+       stubs for long calls if needed.
+       (arm_elf_create_output_section_statements): create stub_file bfd.
+       (arm_for_each_input_file_wrapper): New function.
+       (arm_lang_for_each_input_file): New function.
+       (lang_for_each_input_file): Define.
+       (PARSE_AND_LIST_PROLOGUE): Add option token OPTION_STUBGROUP_SIZE.
+       (PARSE_AND_LIST_LONGOPTS): Add option stub-group-size.
+       (PARSE_AND_LIST_OPTIONS): Add option stub-group-size.
+       (PARSE_AND_LIST_ARGS_CASES): Add OPTION_STUBGROUP_SIZE case.
+       (LDEMUL_FINISH): Update to gld${EMULATION_NAME}_finish.
+       * lang.c (print_input_statement): Skip if bfd has
+       BFD_LINKER_CREATED.
+
 2008-05-14  Alan Modra  <amodra@bigpond.net.au>
 
        * Makefile.in: Regenerate.
index 29e9902..57842f4 100644 (file)
@@ -27,6 +27,7 @@
 test -z "$TARGET2_TYPE" && TARGET2_TYPE="rel"
 fragment <<EOF
 
+#include "ldctor.h"
 #include "elf/arm.h"
 
 static char *thumb_entry_symbol = NULL;
@@ -173,13 +174,221 @@ arm_elf_after_allocation (void)
   }
 }
 
+/* Fake input file for stubs.  */
+static lang_input_statement_type *stub_file;
+
+/* Whether we need to call gldarm_layout_sections_again.  */
+static int need_laying_out = 0;
+
+/* Maximum size of a group of input sections that can be handled by
+   one stub section.  A value of +/-1 indicates the bfd back-end
+   should use a suitable default size.  */
+static bfd_signed_vma group_size = 1;
+
+struct hook_stub_info
+{
+  lang_statement_list_type add;
+  asection *input_section;
+};
+
+/* Traverse the linker tree to find the spot where the stub goes.  */
+
+static bfd_boolean
+hook_in_stub (struct hook_stub_info *info, lang_statement_union_type **lp)
+{
+  lang_statement_union_type *l;
+  bfd_boolean ret;
+
+  for (; (l = *lp) != NULL; lp = &l->header.next)
+    {
+      switch (l->header.type)
+       {
+       case lang_constructors_statement_enum:
+         ret = hook_in_stub (info, &constructor_list.head);
+         if (ret)
+           return ret;
+         break;
+
+       case lang_output_section_statement_enum:
+         ret = hook_in_stub (info,
+                             &l->output_section_statement.children.head);
+         if (ret)
+           return ret;
+         break;
+
+       case lang_wild_statement_enum:
+         ret = hook_in_stub (info, &l->wild_statement.children.head);
+         if (ret)
+           return ret;
+         break;
+
+       case lang_group_statement_enum:
+         ret = hook_in_stub (info, &l->group_statement.children.head);
+         if (ret)
+           return ret;
+         break;
+
+       case lang_input_section_enum:
+         if (l->input_section.section == info->input_section)
+           {
+             /* We've found our section.  Insert the stub immediately
+                before its associated input section.  */
+             *lp = info->add.head;
+             *(info->add.tail) = l;
+             return TRUE;
+           }
+         break;
+
+       case lang_data_statement_enum:
+       case lang_reloc_statement_enum:
+       case lang_object_symbols_statement_enum:
+       case lang_output_statement_enum:
+       case lang_target_statement_enum:
+       case lang_input_statement_enum:
+       case lang_assignment_statement_enum:
+       case lang_padding_statement_enum:
+       case lang_address_statement_enum:
+       case lang_fill_statement_enum:
+         break;
+
+       default:
+         FAIL ();
+         break;
+       }
+    }
+  return FALSE;
+}
+
+
+/* Call-back for elf32_arm_size_stubs.  */
+
+/* Create a new stub section, and arrange for it to be linked
+   immediately before INPUT_SECTION.  */
+
+static asection *
+elf32_arm_add_stub_section (const char *stub_sec_name,
+                           asection *input_section)
+{
+  asection *stub_sec;
+  flagword flags;
+  asection *output_section;
+  const char *secname;
+  lang_output_section_statement_type *os;
+  struct hook_stub_info info;
+
+  stub_sec = bfd_make_section_anyway (stub_file->the_bfd, stub_sec_name);
+  if (stub_sec == NULL)
+    goto err_ret;
+
+  flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE
+          | SEC_HAS_CONTENTS | SEC_RELOC | SEC_IN_MEMORY | SEC_KEEP);
+  if (!bfd_set_section_flags (stub_file->the_bfd, stub_sec, flags))
+    goto err_ret;
+
+  bfd_set_section_alignment (stub_file->the_bfd, stub_sec, 3);
+
+  output_section = input_section->output_section;
+  secname = bfd_get_section_name (output_section->owner, output_section);
+  os = lang_output_section_find (secname);
+
+  info.input_section = input_section;
+  lang_list_init (&info.add);
+  lang_add_section (&info.add, stub_sec, os);
+
+  if (info.add.head == NULL)
+    goto err_ret;
+
+  if (hook_in_stub (&info, &os->children.head))
+    return stub_sec;
+
+ err_ret:
+  einfo ("%X%P: can not make stub section: %E\n");
+  return NULL;
+}
+
+/* Another call-back for elf_arm_size_stubs.  */
+
 static void
-arm_elf_finish (void)
+gldarm_layout_sections_again (void)
+{
+  /* If we have changed sizes of the stub sections, then we need
+     to recalculate all the section offsets.  This may mean we need to
+     add even more stubs.  */
+  gld${EMULATION_NAME}_map_segments (TRUE);
+  need_laying_out = -1;
+}
+
+static void
+build_section_lists (lang_statement_union_type *statement)
+{
+  if (statement->header.type == lang_input_section_enum)
+    {
+      asection *i = statement->input_section.section;
+
+      if (!((lang_input_statement_type *) i->owner->usrdata)->just_syms_flag
+         && (i->flags & SEC_EXCLUDE) == 0
+         && i->output_section != NULL
+         && i->output_section->owner == link_info.output_bfd)
+       elf32_arm_next_input_section (& link_info, i);
+    }
+}
+
+static void
+gld${EMULATION_NAME}_finish (void)
 {
   struct bfd_link_hash_entry * h;
 
-  /* Call the elf32.em routine.  */
-  gld${EMULATION_NAME}_finish ();
+  /* bfd_elf32_discard_info just plays with debugging sections,
+     ie. doesn't affect any code, so we can delay resizing the
+     sections.  It's likely we'll resize everything in the process of
+     adding stubs.  */
+  if (bfd_elf_discard_info (link_info.output_bfd, & link_info))
+    need_laying_out = 1;
+
+  /* If generating a relocatable output file, then we don't
+     have to examine the relocs.  */
+  if (stub_file != NULL && !link_info.relocatable)
+    {
+      int  ret = elf32_arm_setup_section_lists (link_info.output_bfd, & link_info);
+
+      if (ret != 0)
+       {
+         if (ret < 0)
+           {
+             einfo ("%X%P: can not size stub section: %E\n");
+             return;
+           }
+
+         lang_for_each_statement (build_section_lists);
+
+         /* Call into the BFD backend to do the real work.  */
+         if (! elf32_arm_size_stubs (link_info.output_bfd,
+                                     stub_file->the_bfd,
+                                     & link_info,
+                                     group_size,
+                                     & elf32_arm_add_stub_section,
+                                     & gldarm_layout_sections_again))
+           {
+             einfo ("%X%P: can not size stub section: %E\n");
+             return;
+           }
+       }
+    }
+
+  if (need_laying_out != -1)
+    gld${EMULATION_NAME}_map_segments (need_laying_out);
+
+  if (! link_info.relocatable)
+    {
+      /* Now build the linker stubs.  */
+      if (stub_file->the_bfd->sections != NULL)
+       {
+         if (! elf32_arm_build_stubs (& link_info))
+           einfo ("%X%P: can not build stubs: %E\n");
+       }
+    }
+
+  finish_default ();
 
   if (thumb_entry_symbol)
     {
@@ -246,8 +455,44 @@ arm_elf_create_output_section_statements (void)
                                   target2_type, fix_v4bx, use_blx,
                                   vfp11_denorm_fix, no_enum_size_warning,
                                   pic_veneer);
+
+  stub_file = lang_add_input_file ("linker stubs",
+                                  lang_input_file_is_fake_enum,
+                                  NULL);
+  stub_file->the_bfd = bfd_create ("linker stubs", link_info.output_bfd);
+  if (stub_file->the_bfd == NULL
+      || ! bfd_set_arch_mach (stub_file->the_bfd,
+                             bfd_get_arch (link_info.output_bfd),
+                             bfd_get_mach (link_info.output_bfd)))
+    {
+      einfo ("%X%P: can not create BFD %E\n");
+      return;
+    }
+  stub_file->the_bfd->flags |= BFD_LINKER_CREATED;
+  ldlang_add_file (stub_file);
+}
+
+/* Avoid processing the fake stub_file in vercheck, stat_needed and
+   check_needed routines.  */
+
+static void (*real_func) (lang_input_statement_type *);
+
+static void arm_for_each_input_file_wrapper (lang_input_statement_type *l)
+{
+  if (l != stub_file)
+    (*real_func) (l);
 }
 
+static void
+arm_lang_for_each_input_file (void (*func) (lang_input_statement_type *))
+{
+  real_func = func;
+  lang_for_each_input_file (&arm_for_each_input_file_wrapper);
+}
+
+#define lang_for_each_input_file arm_lang_for_each_input_file
+
 EOF
 
 # Define some shell vars to insert bits of code into the standard elf
@@ -265,6 +510,7 @@ PARSE_AND_LIST_PROLOGUE='
 #define OPTION_NO_ENUM_SIZE_WARNING    309
 #define OPTION_PIC_VENEER              310
 #define OPTION_FIX_V4BX_INTERWORKING   311
+#define OPTION_STUBGROUP_SIZE           312
 '
 
 PARSE_AND_LIST_SHORTOPTS=p
@@ -282,6 +528,7 @@ PARSE_AND_LIST_LONGOPTS='
   { "vfp11-denorm-fix", required_argument, NULL, OPTION_VFP11_DENORM_FIX},
   { "no-enum-size-warning", no_argument, NULL, OPTION_NO_ENUM_SIZE_WARNING},
   { "pic-veneer", no_argument, NULL, OPTION_PIC_VENEER},
+  { "stub-group-size", required_argument, NULL, OPTION_STUBGROUP_SIZE },
 '
 
 PARSE_AND_LIST_OPTIONS='
@@ -297,6 +544,15 @@ PARSE_AND_LIST_OPTIONS='
   fprintf (file, _("  --no-enum-size-warning      Don'\''t warn about objects with incompatible"
                   "                                enum sizes\n"));
   fprintf (file, _("  --pic-veneer                Always generate PIC interworking veneers\n"));
+  fprintf (file, _("\
+   --stub-group-size=N   Maximum size of a group of input sections that can be\n\
+                           handled by one stub section.  A negative value\n\
+                           locates all stubs before their branches (with a\n\
+                           group size of -N), while a positive value allows\n\
+                           two groups of input sections, one before, and one\n\
+                           after each stub section.  Values of +/-1 indicate\n\
+                           the linker should choose suitable defaults.\n"
+                  ));
 '
 
 PARSE_AND_LIST_ARGS_CASES='
@@ -354,6 +610,16 @@ PARSE_AND_LIST_ARGS_CASES='
     case OPTION_PIC_VENEER:
       pic_veneer = 1;
       break;
+
+    case OPTION_STUBGROUP_SIZE:
+      {
+       const char *end;
+
+        group_size = bfd_scan_vma (optarg, &end, 0);
+        if (*end)
+         einfo (_("%P%F: invalid number `%s'\''\n"), optarg);
+      }
+      break;
 '
 
 # We have our own after_open and before_allocation functions, but they call
@@ -367,4 +633,4 @@ LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS=arm_elf_create_output_section_statements
 LDEMUL_BEFORE_PARSE=gld"${EMULATION_NAME}"_before_parse
 
 # Call the extra arm-elf function
-LDEMUL_FINISH=arm_elf_finish
+LDEMUL_FINISH=gld${EMULATION_NAME}_finish
index 3c24b66..d3018b2 100644 (file)
@@ -3729,10 +3729,11 @@ print_assignment (lang_assignment_statement_type *assignment,
 static void
 print_input_statement (lang_input_statement_type *statm)
 {
-  if (statm->filename != NULL)
-    {
-      fprintf (config.map_file, "LOAD %s\n", statm->filename);
-    }
+  if ((statm->filename != NULL)
+       && ((statm->the_bfd == NULL)
+          ||
+          ((statm->the_bfd->flags & BFD_LINKER_CREATED) == 0)))
+    fprintf (config.map_file, "LOAD %s\n", statm->filename);
 }
 
 /* Print all symbols defined in a particular section.  This is called
@@ -4185,8 +4186,8 @@ insert_pad (lang_statement_union_type **ptr,
       /* Use the existing pad statement.  */
     }
   else if ((pad = *ptr) != NULL
-      && pad->header.type == lang_padding_statement_enum
-      && pad->padding_statement.output_section == output_section)
+          && pad->header.type == lang_padding_statement_enum
+          && pad->padding_statement.output_section == output_section)
     {
       /* Use the existing pad statement.  */
     }
index 5383fbd..ee4105b 100644 (file)
@@ -1,3 +1,21 @@
+2008-05-15  Christophe Lyon  <christophe.lyon@st.com>
+       
+       * ld-arm/arm-elf.exp (armelftests): Add farcall-arm-arm,
+       farcall-arm-arm-pic-veneer, farcall-arm-arm-be8 farcall-arm-thumb,
+       farcall-arm-thumb-blx, farcall-arm-thumb-pic-veneer,
+       farcall-arm-thumb-blx-pic-veneer, farcall-thumb-thumb,
+       farcall-thumb-thumb-pic-veneer, farcall-thumb-thumb-blx,
+       farcall-thumb-thumb-m, farcall-thumb-thumb-m-pic-veneer,
+       farcall-thumb-thumb-blx-pic-veneer, farcall-thumb-arm,
+       farcall-thumb-arm-pic-veneer, farcall-thumb-arm-blx,
+       farcall-thumb-arm-blx-pic-veneer.
+       Change thumb2-bl-as-thumb1-bad, thumb2-bl-bad.
+       * ld-arm/thumb2-bl-as-thumb1-bad.d: Reflects farcall stub
+       generation.
+       * ld-arm/thumb2-bl-bad.d: Likewise.
+       * ld-arm/thumb2-bl-as-thumb1-bad.s: Update comments.
+       * ld-arm/thumb2-bl-bad.s: Likewise.
+
 2008-05-08  Alan Modra  <amodra@bigpond.net.au>
 
        * ld-spu/ear.s: Align various sections.
index 7447b7c..0dd64bc 100644 (file)
@@ -1,5 +1,5 @@
 # Expect script for various ARM ELF tests.
-#   Copyright 2002, 2003, 2004, 2007 Free Software Foundation, Inc.
+#   Copyright 2002, 2003, 2004, 2007, 2008 Free Software Foundation, Inc.
 #
 # This file is part of the GNU Binutils.
 #
@@ -177,15 +177,65 @@ set armelftests {
      {"callweak" "-static -T arm.ld" "" {callweak.s}
       {{objdump -dr callweak.d}}
       "callweak"}
-    {"ARMv4 interworking" "-static -T arm.ld --fix-v4bx-interworking" "--fix-v4bx -meabi=4" {armv4-bx.s}
-     {{objdump -d armv4-bx.d}}
-     "armv4-bx"}
-    {"MOVW/MOVT and merged sections" "-T arm.ld" "" {movw-merge.s}
-     {{objdump -dw movw-merge.d}}
-     "movw-merge"}
-    {"MOVW/MOVT against shared libraries" "tmpdir/arm-lib.so" "" {arm-app-movw.s}
-     {{objdump -Rw arm-app.r}}
-     "arm-app-movw"}
+    
+     {"ARMv4 interworking" "-static -T arm.ld --fix-v4bx-interworking" "--fix-v4bx -meabi=4" {armv4-bx.s}
+      {{objdump -d armv4-bx.d}}
+      "armv4-bx"}
+     {"MOVW/MOVT and merged sections" "-T arm.ld" "" {movw-merge.s}
+      {{objdump -dw movw-merge.d}}
+      "movw-merge"}
+     {"MOVW/MOVT against shared libraries" "tmpdir/arm-lib.so" "" {arm-app-movw.s}
+      {{objdump -Rw arm-app.r}}
+      "arm-app-movw"}
+     {"Thumb-2-as-Thumb-1 BL" "-Ttext 0x1000 --section-start .foo=0x40100c" "" {thumb2-bl-as-thumb1-bad.s}
+      {{objdump -d thumb2-bl-as-thumb1-bad.d}}
+      "thumb2-bl-as-thumb1-bad"}
+     {"Thumb-2 BL" "-Ttext 0x1000 --section-start .foo=0x100100c" "" {thumb2-bl-bad.s}
+      {{objdump -d thumb2-bl-bad.d}}
+      "thumb2-bl-bad"}
+
+     {"ARM-ARM farcall" "-Ttext 0x1000 --section-start .foo=0x2001020" "" {farcall-arm-arm.s}
+      {{objdump -d farcall-arm-arm.d}}
+      "farcall-arm-arm"}
+     {"ARM-ARM farcall (PIC veneer)" "-Ttext 0x1000 --section-start .foo=0x2001020 --pic-veneer" "" {farcall-arm-arm.s}
+      {{objdump -d farcall-arm-arm-pic-veneer.d}}
+      "farcall-arm-arm-pic-veneer"}
+     {"ARM-ARM farcall (BE8)" "-Ttext 0x1000 --section-start .foo=0x2001020 -EB --be8" "-EB" {farcall-arm-arm.s}
+      {{objdump -d farcall-arm-arm-be8.d}}
+      "farcall-arm-arm-be8"}
+
+     {"ARM-Thumb farcall" "-Ttext 0x1000 --section-start .foo=0x2001014" "" {farcall-arm-thumb.s}
+      {{objdump -d farcall-arm-thumb.d}}
+      "farcall-arm-thumb"}
+     {"ARM-Thumb farcall with BLX" "-Ttext 0x1000 --section-start .foo=0x2001014" "-march=armv5t" {farcall-arm-thumb.s}
+      {{objdump -d farcall-arm-thumb-blx.d}}
+      "farcall-arm-thumb-blx"}
+     {"ARM-Thumb farcall (PIC veneer)" "-Ttext 0x1000 --section-start .foo=0x2001014 --pic-veneer" "" {farcall-arm-thumb.s}
+      {{objdump -d farcall-arm-thumb-pic-veneer.d}}
+      "farcall-arm-thumb-pic-veneer"}
+     {"ARM-Thumb farcall with BLX (PIC veneer)" "-Ttext 0x1000 --section-start .foo=0x2001014 --pic-veneer" "-march=armv5t" {farcall-arm-thumb.s}
+      {{objdump -d farcall-arm-thumb-blx-pic-veneer.d}}
+      "farcall-arm-thumb-blx-pic-veneer"}
+
+     {"Thumb-Thumb farcall with BLX" "-Ttext 0x1000 --section-start .foo=0x2001014" "-march=armv5t" {farcall-thumb-thumb.s}
+      {{objdump -d farcall-thumb-thumb-blx.d}}
+      "farcall-thumb-thumb-blx"}
+     {"Thumb-Thumb farcall M profile" "-Ttext 0x1000 --section-start .foo=0x2001014" "-march=armv7m" {farcall-thumb-thumb.s}
+      {{objdump -d farcall-thumb-thumb-m.d}}
+      "farcall-thumb-thumb-m"}
+     {"Thumb-Thumb farcall with BLX (PIC veneer)" "-Ttext 0x1000 --section-start .foo=0x2001014 --pic-veneer" "-march=armv5t" {farcall-thumb-thumb.s}
+      {{objdump -d farcall-thumb-thumb-blx-pic-veneer.d}}
+      "farcall-thumb-thumb-blx-pic-veneer"}
+
+     {"Thumb-ARM farcall" "-Ttext 0x1000 --section-start .foo=0x2001014" "" {farcall-thumb-arm.s}
+      {{objdump -d farcall-thumb-arm.d}}
+      "farcall-thumb-arm"}
+     {"Thumb-ARM farcall with BLX" "-Ttext 0x1000 --section-start .foo=0x2001014" "-march=armv5t" {farcall-thumb-arm.s}
+      {{objdump -d farcall-thumb-arm-blx.d}}
+      "farcall-thumb-arm-blx"}
+     {"Thumb-ARM farcall with BLX (PIC veneer)" "-Ttext 0x1000 --section-start .foo=0x2001014 --pic-veneer" "-march=armv5t" {farcall-thumb-arm.s}
+      {{objdump -d farcall-thumb-arm-blx-pic-veneer.d}}
+      "farcall-thumb-arm-blx-pic-veneer"}
 }
 
 run_ld_link_tests $armelftests
@@ -193,9 +243,11 @@ run_dump_test "group-relocs-alu-bad"
 run_dump_test "group-relocs-ldr-bad"
 run_dump_test "group-relocs-ldrs-bad"
 run_dump_test "group-relocs-ldc-bad"
-run_dump_test "thumb2-bl-as-thumb1-bad"
-run_dump_test "thumb2-bl-bad"
 run_dump_test "emit-relocs1"
+run_dump_test "farcall-thumb-thumb"
+run_dump_test "farcall-thumb-thumb-pic-veneer"
+run_dump_test "farcall-thumb-thumb-m-pic-veneer"
+run_dump_test "farcall-thumb-arm-pic-veneer"
 
 # Exclude non-ARM-EABI targets.
 
diff --git a/ld/testsuite/ld-arm/farcall-arm-arm-be8.d b/ld/testsuite/ld-arm/farcall-arm-arm-be8.d
new file mode 100644 (file)
index 0000000..b409517
--- /dev/null
@@ -0,0 +1,14 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+    1000:      04f01fe5        .*
+    1004:      02001020        .word   0x02001020
+
+00001008 <_start>:
+    1008:      fcffffeb        .*
+Disassembly of section .foo:
+
+02001020 <bar>:
+ 2001020:      1eff2fe1        .*
diff --git a/ld/testsuite/ld-arm/farcall-arm-arm-pic-veneer.d b/ld/testsuite/ld-arm/farcall-arm-arm-pic-veneer.d
new file mode 100644 (file)
index 0000000..1d0af22
--- /dev/null
@@ -0,0 +1,16 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+    1000:      e59fc000        ldr     ip, \[pc, #0\]  ; 1008 <_start-0x8>
+    1004:      e08ff00c        add     pc, pc, ip
+    1008:      02000018        .word   0x02000018
+    100c:      00000000        .word   0x00000000
+
+00001010 <_start>:
+    1010:      ebfffffa        bl      1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001020 <bar>:
+ 2001020:      e12fff1e        bx      lr
diff --git a/ld/testsuite/ld-arm/farcall-arm-arm.d b/ld/testsuite/ld-arm/farcall-arm-arm.d
new file mode 100644 (file)
index 0000000..3ccb2c4
--- /dev/null
@@ -0,0 +1,14 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+    1000:      e51ff004        ldr     pc, \[pc, #-4\] ; 1004 <_start-0x4>
+    1004:      02001020        .word   0x02001020
+
+00001008 <_start>:
+    1008:      ebfffffc        bl      1000 <_start-0x8>
+Disassembly of section .foo:
+
+02001020 <bar>:
+ 2001020:      e12fff1e        bx      lr
diff --git a/ld/testsuite/ld-arm/farcall-arm-arm.s b/ld/testsuite/ld-arm/farcall-arm-arm.s
new file mode 100644 (file)
index 0000000..bc77ad3
--- /dev/null
@@ -0,0 +1,19 @@
+@ Test to ensure that a ARM to ARM call exceeding 32Mb generates a stub.
+
+       .global _start
+       .syntax unified
+
+@ We will place the section .text at 0x1000.
+
+       .text
+
+_start:
+       bl bar
+
+@ We will place the section .foo at 0x2001020.
+
+       .section .foo, "xa"
+
+bar:
+       bx lr
+
diff --git a/ld/testsuite/ld-arm/farcall-arm-thumb-blx-pic-veneer.d b/ld/testsuite/ld-arm/farcall-arm-thumb-blx-pic-veneer.d
new file mode 100644 (file)
index 0000000..3ede5de
--- /dev/null
@@ -0,0 +1,16 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+    1000:      e59fc000        ldr     ip, \[pc, #0\]  ; 1008 <_start-0x8>
+    1004:      e08ff00c        add     pc, pc, ip
+    1008:      0200000d        .word   0x0200000d
+    100c:      00000000        .word   0x00000000
+
+00001010 <_start>:
+    1010:      ebfffffa        bl      1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014:      4770            bx      lr
diff --git a/ld/testsuite/ld-arm/farcall-arm-thumb-blx.d b/ld/testsuite/ld-arm/farcall-arm-thumb-blx.d
new file mode 100644 (file)
index 0000000..86aaf39
--- /dev/null
@@ -0,0 +1,14 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+    1000:      e51ff004        ldr     pc, \[pc, #-4\] ; 1004 <_start-0x4>
+    1004:      02001015        .word   0x02001015
+
+00001008 <_start>:
+    1008:      ebfffffc        bl      1000 <_start-0x8>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014:      4770            bx      lr
diff --git a/ld/testsuite/ld-arm/farcall-arm-thumb-pic-veneer.d b/ld/testsuite/ld-arm/farcall-arm-thumb-pic-veneer.d
new file mode 100644 (file)
index 0000000..3ede5de
--- /dev/null
@@ -0,0 +1,16 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+    1000:      e59fc000        ldr     ip, \[pc, #0\]  ; 1008 <_start-0x8>
+    1004:      e08ff00c        add     pc, pc, ip
+    1008:      0200000d        .word   0x0200000d
+    100c:      00000000        .word   0x00000000
+
+00001010 <_start>:
+    1010:      ebfffffa        bl      1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014:      4770            bx      lr
diff --git a/ld/testsuite/ld-arm/farcall-arm-thumb.d b/ld/testsuite/ld-arm/farcall-arm-thumb.d
new file mode 100644 (file)
index 0000000..c664eff
--- /dev/null
@@ -0,0 +1,16 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+    1000:      e59fc000        ldr     ip, \[pc, #0\]  ; 1008 <_start-0x8>
+    1004:      e12fff1c        bx      ip
+    1008:      02001015        .word   0x02001015
+    100c:      00000000        .word   0x00000000
+
+00001010 <_start>:
+    1010:      ebfffffa        bl      1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014:      4770            bx      lr
diff --git a/ld/testsuite/ld-arm/farcall-arm-thumb.s b/ld/testsuite/ld-arm/farcall-arm-thumb.s
new file mode 100644 (file)
index 0000000..c69f31c
--- /dev/null
@@ -0,0 +1,20 @@
+@ Test to ensure that a ARM to Thumb call exceeding 32Mb generates a stub.
+
+       .global _start
+       .global bar
+       .syntax unified
+
+@ We will place the section .text at 0x1000.
+
+       .text
+
+_start:
+       bl bar
+
+@ We will place the section .foo at 0x2001010.
+
+       .section .foo, "xa"
+       .thumb_func
+bar:
+       bx lr
+
diff --git a/ld/testsuite/ld-arm/farcall-thumb-arm-blx-pic-veneer.d b/ld/testsuite/ld-arm/farcall-thumb-arm-blx-pic-veneer.d
new file mode 100644 (file)
index 0000000..3e0cb39
--- /dev/null
@@ -0,0 +1,16 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+    1000:      e59fc000        ldr     ip, \[pc, #0\]  ; 1008 <_start-0x8>
+    1004:      e08ff00c        add     pc, pc, ip
+    1008:      0200000c        .word   0x0200000c
+    100c:      00000000        .word   0x00000000
+
+00001010 <_start>:
+    1010:      f7ff eff6       blx     1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014:      4770            bx      lr
diff --git a/ld/testsuite/ld-arm/farcall-thumb-arm-blx.d b/ld/testsuite/ld-arm/farcall-thumb-arm-blx.d
new file mode 100644 (file)
index 0000000..c4bdd3b
--- /dev/null
@@ -0,0 +1,14 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+    1000:      e51ff004        ldr     pc, \[pc, #-4\] ; 1004 <_start-0x4>
+    1004:      02001014        .word   0x02001014
+
+00001008 <_start>:
+    1008:      f7ff effa       blx     1000 <_start-0x8>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014:      4770            bx      lr
diff --git a/ld/testsuite/ld-arm/farcall-thumb-arm-pic-veneer.d b/ld/testsuite/ld-arm/farcall-thumb-arm-pic-veneer.d
new file mode 100644 (file)
index 0000000..932f28a
--- /dev/null
@@ -0,0 +1,5 @@
+#name: Thumb-Thumb farcall without BLX
+#source: farcall-thumb-thumb.s
+#as: -march=armv4t
+#ld: -Ttext 0x1000 --section-start .foo=0x2001014
+#error: .*\(.text\+0x0\): relocation truncated to fit: R_ARM_THM_CALL against `bar'
diff --git a/ld/testsuite/ld-arm/farcall-thumb-arm.d b/ld/testsuite/ld-arm/farcall-thumb-arm.d
new file mode 100644 (file)
index 0000000..90495b8
--- /dev/null
@@ -0,0 +1,19 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x18>:
+    1000:      b540            push    {r6, lr}
+    1002:      4e03            ldr     r6, \[pc, #12\] \(1010 <_start-0x8>\)
+    1004:      46fe            mov     lr, pc
+    1006:      4730            bx      r6
+    1008:      e8bd4040        pop     {r6, lr}
+    100c:      e12fff1e        bx      lr
+       ...
+
+00001018 <_start>:
+    1018:      f7ff eff2       blx     1000 <_start-0x18>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014:      4770            bx      lr
diff --git a/ld/testsuite/ld-arm/farcall-thumb-arm.s b/ld/testsuite/ld-arm/farcall-thumb-arm.s
new file mode 100644 (file)
index 0000000..82cfb95
--- /dev/null
@@ -0,0 +1,19 @@
+@ Test to ensure that a Thumb to ARM call exceeding 4Mb generates a stub.
+
+       .global _start
+       .syntax unified
+
+@ We will place the section .text at 0x1000.
+
+       .text
+       .thumb_func
+_start:
+       bl bar
+
+@ We will place the section .foo at 0x2001014.
+
+       .section .foo, "xa"
+
+bar:
+       bx lr
+
diff --git a/ld/testsuite/ld-arm/farcall-thumb-thumb-blx-pic-veneer.d b/ld/testsuite/ld-arm/farcall-thumb-thumb-blx-pic-veneer.d
new file mode 100644 (file)
index 0000000..cadb620
--- /dev/null
@@ -0,0 +1,16 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+    1000:      e59fc000        ldr     ip, \[pc, #0\]  ; 1008 <_start-0x8>
+    1004:      e08ff00c        add     pc, pc, ip
+    1008:      0200000d        .word   0x0200000d
+    100c:      00000000        .word   0x00000000
+
+00001010 <_start>:
+    1010:      f7ff eff6       blx     1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014:      4770            bx      lr
diff --git a/ld/testsuite/ld-arm/farcall-thumb-thumb-blx.d b/ld/testsuite/ld-arm/farcall-thumb-thumb-blx.d
new file mode 100644 (file)
index 0000000..11280c9
--- /dev/null
@@ -0,0 +1,14 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+    1000:      e51ff004        ldr     pc, \[pc, #-4\] ; 1004 <_start-0x4>
+    1004:      02001015        .word   0x02001015
+
+00001008 <_start>:
+    1008:      f7ff effa       blx     1000 <_start-0x8>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014:      4770            bx      lr
diff --git a/ld/testsuite/ld-arm/farcall-thumb-thumb-m-pic-veneer.d b/ld/testsuite/ld-arm/farcall-thumb-thumb-m-pic-veneer.d
new file mode 100644 (file)
index 0000000..932f28a
--- /dev/null
@@ -0,0 +1,5 @@
+#name: Thumb-Thumb farcall without BLX
+#source: farcall-thumb-thumb.s
+#as: -march=armv4t
+#ld: -Ttext 0x1000 --section-start .foo=0x2001014
+#error: .*\(.text\+0x0\): relocation truncated to fit: R_ARM_THM_CALL against `bar'
diff --git a/ld/testsuite/ld-arm/farcall-thumb-thumb-m.d b/ld/testsuite/ld-arm/farcall-thumb-thumb-m.d
new file mode 100644 (file)
index 0000000..691d4e0
--- /dev/null
@@ -0,0 +1,19 @@
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+    1000:      b540            push    {r6, lr}
+    1002:      4e02            ldr     r6, \[pc, #8\]  \(100c <_start-0x4>\)
+    1004:      46fe            mov     lr, pc
+    1006:      e7fe            b.n     1006 <_start-0xa>
+    1008:      bd40            pop     {r6, pc}
+    100a:      bf00            nop
+    100c:      02001015        .word   0x02001015
+
+00001010 <_start>:
+    1010:      f7ff eff6       blx     1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014:      4770            bx      lr
diff --git a/ld/testsuite/ld-arm/farcall-thumb-thumb-pic-veneer.d b/ld/testsuite/ld-arm/farcall-thumb-thumb-pic-veneer.d
new file mode 100644 (file)
index 0000000..932f28a
--- /dev/null
@@ -0,0 +1,5 @@
+#name: Thumb-Thumb farcall without BLX
+#source: farcall-thumb-thumb.s
+#as: -march=armv4t
+#ld: -Ttext 0x1000 --section-start .foo=0x2001014
+#error: .*\(.text\+0x0\): relocation truncated to fit: R_ARM_THM_CALL against `bar'
diff --git a/ld/testsuite/ld-arm/farcall-thumb-thumb.d b/ld/testsuite/ld-arm/farcall-thumb-thumb.d
new file mode 100644 (file)
index 0000000..932f28a
--- /dev/null
@@ -0,0 +1,5 @@
+#name: Thumb-Thumb farcall without BLX
+#source: farcall-thumb-thumb.s
+#as: -march=armv4t
+#ld: -Ttext 0x1000 --section-start .foo=0x2001014
+#error: .*\(.text\+0x0\): relocation truncated to fit: R_ARM_THM_CALL against `bar'
diff --git a/ld/testsuite/ld-arm/farcall-thumb-thumb.s b/ld/testsuite/ld-arm/farcall-thumb-thumb.s
new file mode 100644 (file)
index 0000000..650b1a6
--- /dev/null
@@ -0,0 +1,19 @@
+@ Test to ensure that a Thumb to Thumb call exceeding 4Mb generates a stub.
+
+       .global _start
+       .syntax unified
+
+@ We will place the section .text at 0x1000.
+
+       .text
+       .thumb_func
+_start:
+       bl bar
+
+@ We will place the section .foo at 0x02001014.
+
+       .section .foo, "xa"
+       .thumb_func
+bar:
+       bx lr
+
index 749b58f..1dfca21 100644 (file)
@@ -1,4 +1,14 @@
-#name: Thumb-2-as-Thumb-1 BL failure test
-#source: thumb2-bl-as-thumb1-bad.s
-#ld: -Ttext 0x1000 --section-start .foo=0x401004
-#error: .*\(.text\+0x0\): relocation truncated to fit: R_ARM_THM_CALL against `bar'
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+    1000:      e51ff004        ldr     pc, \[pc, #-4\] ; 1004 <_start-0x4>
+    1004:      0040100d        .word   0x0040100d
+
+00001008 <_start>:
+    1008:      f7ff effa       blx     1000 <_start-0x8>
+Disassembly of section .foo:
+
+0040100c <bar>:
+  40100c:      4770            bx      lr
index dae5d43..13f7f33 100644 (file)
@@ -1,4 +1,4 @@
-@ Test to ensure that a Thumb-1 BL with a Thumb-2-only offset fails.
+@ Test to ensure that a Thumb-1 BL with a Thumb-2-only offset makes the linker generate a stub.
 
        .arch armv5t
        .global _start
@@ -12,7 +12,7 @@
 _start:
        bl bar
 
-@ We will place the section .foo at 0x401004.
+@ We will place the section .foo at 0x40100c.
 
        .section .foo, "xa"
        .thumb_func
index 0fc6e04..ea38797 100644 (file)
@@ -1,4 +1,14 @@
-#name: Thumb-2 BL failure test
-#source: thumb2-bl-bad.s
-#ld: -Ttext 0x1000 --section-start .foo=0x1001004
-#error: .*\(.text\+0x0\): relocation truncated to fit: R_ARM_THM_CALL against `bar'
+.*:     file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+    1000:      e51ff004        ldr     pc, \[pc, #-4\] ; 1004 <_start-0x4>
+    1004:      0100100d        .word   0x0100100d
+
+00001008 <_start>:
+    1008:      f7ff effa       blx     1000 <_start-0x8>
+Disassembly of section .foo:
+
+0100100c <bar>:
+ 100100c:      4770            bx      lr
index 63e3fe7..7685860 100644 (file)
@@ -1,4 +1,4 @@
-@ Test to ensure that a Thumb-2 BL with an oversize offset fails.
+@ Test to ensure that a Thumb-2 BL with an oversize offset  makes the linker generate a stub.
 
        .arch armv7
        .global _start
@@ -12,7 +12,7 @@
 _start:
        bl bar
 
-@ We will place the section .foo at 0x1001004.
+@ We will place the section .foo at 0x100100c.
 
        .section .foo, "xa"
        .thumb_func