This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[patch] ARM support for long calls
- From: Christophe LYON <christophe dot lyon at st dot com>
- To: binutils at sourceware dot org
- Date: Mon, 31 Mar 2008 17:36:58 +0200
- Subject: [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