This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch] ARM support for long calls


Hello,

So far GNU ld for ARM does not support automatic generation of stubs to handle long calls (+/- 32Mb in ARM mode, +/- 4Mb in Thumb mode).

I propose the attached patch to add this support, heavily derived from the existing HPPA code.

Christophe.
2008-03-31  Christophe Lyon  <christophe.lyon@st.com>
	Long call support.

	bfd/
	* 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.
	(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_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.
	(elf32_arm_final_link_relocate): Redirect calls to stub if range
	exceeds encoding capabilities.
	(bfd_elf32_bfd_link_hash_table_free): Define.

	include/
	* elf/arm.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-thumb, farcall-thumb-thumb-blx, farcall-thumb-arm.
	Change thumb2-bl-as-thumb1-bad, thumb2-bl-bad. Add
	farcall-thumb-thumb.
	* 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.
Index: ld/emultempl/armelf.em
===================================================================
--- ld/emultempl/armelf.em	(revision 637)
+++ ld/emultempl/armelf.em	(working copy)
@@ -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,225 @@ 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
+  PARAMS ((struct hook_stub_info *, lang_statement_union_type **));
+
+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
+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
-arm_elf_finish (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 == 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 (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 (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 (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)
     {
@@ -245,8 +458,44 @@ arm_elf_create_output_section_statements
 				   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", output_bfd);
+  if (stub_file->the_bfd == NULL
+      || ! bfd_set_arch_mach (stub_file->the_bfd,
+ 			      bfd_get_arch (output_bfd),
+ 			      bfd_get_mach (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
@@ -263,6 +512,7 @@ PARSE_AND_LIST_PROLOGUE='
 #define OPTION_VFP11_DENORM_FIX		308
 #define OPTION_NO_ENUM_SIZE_WARNING	309
 #define OPTION_PIC_VENEER		310
+#define OPTION_STUBGROUP_SIZE          (OPTION_PIC_VENEER + 1)
 '
 
 PARSE_AND_LIST_SHORTOPTS=p
@@ -279,6 +529,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='
@@ -293,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='
@@ -346,6 +606,15 @@ 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
@@ -359,4 +628,4 @@ LDEMUL_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: ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.d
===================================================================
--- ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.d	(revision 637)
+++ ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.d	(working copy)
@@ -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 	subeq	r1, r0, sp
+
+00001008 <_start>:
+    1008:	f7ff effa 	blx	1000 <_start-0x8>
+Disassembly of section .foo:
+
+0040100c <bar>:
+  40100c:	4770      	bx	lr
Index: ld/testsuite/ld-arm/arm-elf.exp
===================================================================
--- ld/testsuite/ld-arm/arm-elf.exp	(revision 637)
+++ ld/testsuite/ld-arm/arm-elf.exp	(working copy)
@@ -182,6 +182,24 @@ set armelftests {
     {"callweak" "-static -T arm.ld" "" {callweak.s}
      {{objdump -dr callweak.d}}
      "callweak"}
+    {"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=0x2001010" "" {farcall-arm-arm.s}
+     {{objdump -d farcall-arm-arm.d}}
+     "farcall-arm-arm"}
+    {"ARM-Thumb farcall" "-Ttext 0x1000 --section-start .foo=0x2001014" "" {farcall-arm-thumb.s}
+     {{objdump -d farcall-arm-thumb.d}}
+     "farcall-arm-thumb"}
+    {"Thumb-Thumb farcall with BLX" "-Ttext 0x1000 --section-start .foo=0x2001014 --use-blx" "-march=armv5t" {farcall-thumb-thumb.s}
+     {{objdump -d farcall-thumb-thumb-blx.d}}
+     "farcall-thumb-thumb-blx"}
+    {"Thumb-ARM farcall" "-Ttext 0x1000 --section-start .foo=0x2001014" "" {farcall-thumb-arm.s}
+     {{objdump -d farcall-thumb-arm.d}}
+     "farcall-thumb-arm"}
 }
 
 run_ld_link_tests $armelftests
@@ -189,9 +207,10 @@ 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 "thumb2-bl-as-thumb1-bad"
+#run_dump_test "thumb2-bl-bad"
 run_dump_test "emit-relocs1"
+run_dump_test "farcall-thumb-thumb"
 
 # Exclude non-ARM-EABI targets.
 
Index: ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.s
===================================================================
--- ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.s	(revision 637)
+++ ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.s	(working copy)
@@ -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: ld/testsuite/ld-arm/thumb2-bl-bad.d
===================================================================
--- ld/testsuite/ld-arm/thumb2-bl-bad.d	(revision 637)
+++ ld/testsuite/ld-arm/thumb2-bl-bad.d	(working copy)
@@ -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 	tsteq	r0, sp
+
+00001008 <_start>:
+    1008:	f7ff effa 	blx	1000 <_start-0x8>
+Disassembly of section .foo:
+
+0100100c <bar>:
+ 100100c:	4770      	bx	lr
Index: ld/testsuite/ld-arm/thumb2-bl-bad.s
===================================================================
--- ld/testsuite/ld-arm/thumb2-bl-bad.s	(revision 637)
+++ ld/testsuite/ld-arm/thumb2-bl-bad.s	(working copy)
@@ -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
Index: ld/ldlang.c
===================================================================
--- ld/ldlang.c	(revision 637)
+++ ld/ldlang.c	(working copy)
@@ -3563,7 +3563,10 @@ print_assignment (lang_assignment_statem
 static void
 print_input_statement (lang_input_statement_type *statm)
 {
-  if (statm->filename != NULL)
+  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);
     }
Index: include/elf/arm.h
===================================================================
--- include/elf/arm.h	(revision 637)
+++ include/elf/arm.h	(working copy)
@@ -234,11 +234,20 @@ START_RELOC_NUMBERS (elf_arm_reloc_type)
   FAKE_RELOC (R_ARM_GOT32,              R_ARM_GOT_BREL)   /* 32 bit GOT entry.  */
   FAKE_RELOC (R_ARM_ROSEGREL32,         R_ARM_SBREL31)    /* ??? */
   FAKE_RELOC (R_ARM_AMP_VCALL9,         R_ARM_BREL_ADJ)   /* Thumb-something.  Not used.  */
-END_RELOC_NUMBERS (R_ARM_max)
+END_RELOC_NUMBERS (R_ARM_max = 130)
 
 #ifdef BFD_ARCH_SIZE
 /* EABI object attributes.  */
 
+int elf32_arm_setup_section_lists (bfd *output_bfd, struct bfd_link_info *info);
+void elf32_arm_next_input_section (struct bfd_link_info *info, asection *isec);
+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) PARAMS ((const char *, asection *)),
+				  void (*layout_sections_again) PARAMS ((void)));
+bfd_boolean elf32_arm_build_stubs (struct bfd_link_info *info);
+
 enum
 {
   /* 0-3 are generic.  */
Index: bfd/elf32-arm.c
===================================================================
--- bfd/elf32-arm.c	(revision 637)
+++ bfd/elf32-arm.c	(working copy)
@@ -2006,6 +2006,65 @@ static const bfd_vma elf32_arm_symbian_p
     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_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_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.  */
 
@@ -2147,6 +2206,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.  */
@@ -2160,6 +2223,10 @@ 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
   {
@@ -2243,6 +2310,31 @@ struct elf32_arm_link_hash_table
 
     /* 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) PARAMS ((const char *, asection *));
+  void (*layout_sections_again) PARAMS ((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.  */
@@ -2274,11 +2366,50 @@ elf32_arm_link_hash_newfunc (struct bfd_
       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.  */
 
@@ -2493,9 +2624,870 @@ elf32_arm_link_hash_table_create (bfd *a
   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)))
+    return 0;
+
   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 the type of stub needed, if any, for a call.  */
+
+static int using_thumb2 (struct elf32_arm_link_hash_table *globals);
+
+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,
+		  bfd_vma destination)
+{
+  bfd_vma location;
+  bfd_signed_vma branch_offset;
+  unsigned int r_type;
+  struct elf32_arm_link_hash_table * globals;
+  int thumb2;
+
+  globals = elf32_arm_hash_table (info);
+
+  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 (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))
+	  )
+	)
+      {
+	if (st_type == STT_ARM_TFUNC) {
+	  /* thumb to thumb */
+	  return (info->shared)
+	    ? arm_stub_pic_long_branch
+	    : arm_stub_long_branch;
+	} else {
+	  /* thumb to arm */
+	  return (info->shared)
+	    ? arm_stub_pic_long_branch
+	    : arm_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)) {
+	return (info->shared)
+	  ? arm_stub_pic_long_branch
+	  : arm_stub_long_branch;
+      }
+    } else {
+      /* arm to arm */
+      if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
+	  || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)) {
+	return (info->shared)
+	  ? arm_stub_pic_long_branch
+	  : arm_stub_long_branch;
+      }
+    }
+  }
+
+  return arm_stub_none;
+}
+
+/* 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;
+}
+
+/* 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)
+{
+  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;
+  return stub_entry;
+}
+
+static bfd_boolean
+arm_build_one_stub (struct bfd_hash_entry *gen_entry,
+		   PTR 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;
+
+  /* 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;
+
+  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_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++)
+    {
+      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_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,
+		  PTR 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_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 (htab->root.root.creator->flavour != bfd_target_elf_flavour)
+    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 = (struct map_stub *) 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 = (asection **) 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) PARAMS ((const char *, asection *)),
+		      void (*layout_sections_again) PARAMS ((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,
+					       (Elf_Internal_Rela *) 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,
+					       destination);
+		  if (stub_type == arm_stub_none)
+		    continue;
+
+		  /* Support for grouping stub sections.  */
+		  id_sec = htab->stub_group[section->id].link_sec;
+
+		  /* 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;
+
+		  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;
+		    }
+
+		  stub_entry = elf32_arm_add_stub (stub_name, section, htab);
+		  if (stub_entry == NULL)
+		    {
+		      free (stub_name);
+		      goto error_ret_free_internal;
+		    }
+
+		  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);
+	    }
+	}
+
+      if (!stub_changed)
+	break;
+
+      /* 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;
+	}
+
+      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;
+    }
+
+  return TRUE;
+
+ error_ret_free_local:
+  return FALSE;
+}
+
+/* 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.  */
+
+bfd_boolean
+elf32_arm_build_stubs (struct bfd_link_info *info)
+{
+  asection *stub_sec;
+  struct bfd_hash_table *table;
+  struct elf32_arm_link_hash_table *htab;
+
+  htab = elf32_arm_hash_table (info);
+
+  for (stub_sec = htab->stub_bfd->sections;
+       stub_sec != NULL;
+       stub_sec = stub_sec->next)
+    {
+      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 = (unsigned char *) bfd_zalloc (htab->stub_bfd, size);
+      if (stub_sec->contents == NULL && size != 0)
+	return FALSE;
+      stub_sec->size = 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 TRUE;
+}
+
 /* Locate the Thumb encoded calling stub for NAME.  */
 
 static struct elf_link_hash_entry *
@@ -2998,6 +3990,10 @@ bfd_elf32_arm_add_glue_sections_to_bfd (
   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)
@@ -4755,6 +5751,16 @@ elf32_arm_final_link_relocate (reloc_how
 	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.  */
@@ -4771,6 +5777,13 @@ elf32_arm_final_link_relocate (reloc_how
 	      /* Check for Arm calling Thumb function.  */
 	      if (sym_flags == STT_ARM_TFUNC)
 		{
+		  /* If the call distance is large, a stub (ARM code)
+		     will be inserted, don't change mode in this
+ 		     case */
+		  if (r_type != R_ARM_CALL
+ 		      || (branch_offset <= ARM_MAX_FWD_BRANCH_OFFSET
+ 			  && branch_offset >= ARM_MAX_BWD_BRANCH_OFFSET) ) {
+
 		  if (elf32_arm_to_thumb_stub (info, sym_name, input_bfd,
 					       output_bfd, input_section,
 					       hit_data, sym_sec, rel->r_offset,
@@ -4781,6 +5794,28 @@ elf32_arm_final_link_relocate (reloc_how
 		    return bfd_reloc_dangerous;
 		}
 	    }
+	    }
+
+	  /* Check if a stub has to be inserted because the
+	     destination is too far. */
+
+	  if (r_type == R_ARM_CALL) {
+	  if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
+	      || branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)
+	    {
+	      /* 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:
@@ -4843,7 +5878,9 @@ elf32_arm_final_link_relocate (reloc_how
 	      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
 		    {
@@ -4852,6 +5889,7 @@ elf32_arm_final_link_relocate (reloc_how
 		    }
 		}
 	    }
+	  }
 	  break;
 
 	case R_ARM_ABS32:
@@ -5115,6 +6153,52 @@ elf32_arm_final_link_relocate (reloc_how
 	    *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 (globals->use_blx
+	    &&
+	    (
+	     ( !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, 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
@@ -10168,6 +11252,7 @@ const struct elf_size_info elf32_arm_siz
 #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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]