This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Re: [patch] ARM support for long calls
Hello,
Here is a new version that hopefully addresses your comments.
Regards,
Christophe.
2008-04-25 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.
(arm_thumb_v4t_long_branch_stub): Define.
(arm_thumb_thumb_long_branch_stub): Define.
(arm_thumb_arm_v4t_long_branch_stub): Define.
(STUB_SUFFIX): Define.
(elf32_arm_stub_type): Define.
(elf32_arm_stub_hash_entry): Define.
(elf32_arm_link_hash_entry): Add stub_cache field.
(arm_stub_hash_lookup): Define.
(elf32_arm_link_hash_table): Add stub_hash_table, stub_bfd,
add_stub_section, layout_sections_again, stub_group, bfd_count,
top_index, input_list fields.
(elf32_arm_link_hash_newfunc): Init new field.
(stub_hash_newfunc): New function.
(elf32_arm_link_hash_table_create): Init stub_hash_table.
(elf32_arm_hash_table_free): New function.
(arm_type_of_stub): New function.
(elf32_arm_stub_name): New function.
(elf32_arm_get_stub_entry): New function.
(elf32_arm_stub_add_mapping_symbol): New function.
(elf32_arm_add_stub): New function.
(arm_build_one_stub): New function.
(arm_size_one_stub): New function.
(elf32_arm_setup_section_lists): New function.
(elf32_arm_next_input_section): New function.
(group_sections): New function.
(elf32_arm_size_stubs): New function.
(elf32_arm_build_stubs): New function.
(bfd_elf32_arm_add_glue_sections_to_bfd): Skip stub sections.
(bfd_elf32_arm_process_before_allocation): No longer handle
R_ARM_CALL and R_ARM_THM_CALL.
(using_thumb_only): New function.
(elf32_arm_final_link_relocate): Redirect calls to stub if range
exceeds encoding capabilities.
(bfd_elf32_bfd_link_hash_table_free): Define.
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-arm-pic-veneer, farcall-arm-arm-be8 farcall-arm-thumb,
farcall-arm-thumb-blx, farcall-arm-thumb-pic-veneer,
farcall-arm-thumb-blx-pic-veneer, farcall-thumb-thumb,
farcall-thumb-thumb-pic-veneer, farcall-thumb-thumb-blx,
farcall-thumb-thumb-m, farcall-thumb-thumb-m-pic-veneer,
farcall-thumb-thumb-blx-pic-veneer, farcall-thumb-arm,
farcall-thumb-arm-pic-veneer, farcall-thumb-arm-blx,
farcall-thumb-arm-blx-pic-veneer.
Change thumb2-bl-as-thumb1-bad, thumb2-bl-bad.
* ld-arm/thumb2-bl-as-thumb1-bad.d: Reflects farcall stub
generation.
* ld-arm/thumb2-bl-bad.d: Likewise.
* ld-arm/thumb2-bl-as-thumb1-bad.s: Update comments.
* ld-arm/thumb2-bl-bad.s: Likewise.
Index: ld/emultempl/armelf.em
===================================================================
--- ld/emultempl/armelf.em (.../vendor/tags/code_sourcery-2007q3-53) (revision 680)
+++ ld/emultempl/armelf.em (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -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/farcall-arm-arm.d
===================================================================
--- ld/testsuite/ld-arm/farcall-arm-arm.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-arm-arm.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,14 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+ 1000: e51ff004 ldr pc, \[pc, #-4\] ; 1004 <_start-0x4>
+ 1004: 02001020 .word 0x02001020
+
+00001008 <_start>:
+ 1008: ebfffffc bl 1000 <_start-0x8>
+Disassembly of section .foo:
+
+02001020 <bar>:
+ 2001020: e12fff1e bx lr
Index: ld/testsuite/ld-arm/farcall-arm-thumb.d
===================================================================
--- ld/testsuite/ld-arm/farcall-arm-thumb.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-arm-thumb.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,16 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+ 1000: e59fc000 ldr ip, \[pc, #0\] ; 1008 <_start-0x8>
+ 1004: e12fff1c bx ip
+ 1008: 02001015 .word 0x02001015
+ 100c: 00000000 .word 0x00000000
+
+00001010 <_start>:
+ 1010: ebfffffa bl 1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014: 4770 bx lr
Index: ld/testsuite/ld-arm/farcall-arm-arm.s
===================================================================
--- ld/testsuite/ld-arm/farcall-arm-arm.s (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-arm-arm.s (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,19 @@
+@ Test to ensure that a ARM to ARM call exceeding 32Mb generates a stub.
+
+ .global _start
+ .syntax unified
+
+@ We will place the section .text at 0x1000.
+
+ .text
+
+_start:
+ bl bar
+
+@ We will place the section .foo at 0x2001020.
+
+ .section .foo, "xa"
+
+bar:
+ bx lr
+
Index: ld/testsuite/ld-arm/farcall-arm-thumb.s
===================================================================
--- ld/testsuite/ld-arm/farcall-arm-thumb.s (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-arm-thumb.s (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,20 @@
+@ Test to ensure that a ARM to Thumb call exceeding 32Mb generates a stub.
+
+ .global _start
+ .global bar
+ .syntax unified
+
+@ We will place the section .text at 0x1000.
+
+ .text
+
+_start:
+ bl bar
+
+@ We will place the section .foo at 0x2001010.
+
+ .section .foo, "xa"
+ .thumb_func
+bar:
+ bx lr
+
Index: ld/testsuite/ld-arm/farcall-arm-thumb-blx-pic-veneer.d
===================================================================
--- ld/testsuite/ld-arm/farcall-arm-thumb-blx-pic-veneer.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-arm-thumb-blx-pic-veneer.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,16 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+ 1000: e59fc000 ldr ip, \[pc, #0\] ; 1008 <_start-0x8>
+ 1004: e08ff00c add pc, pc, ip
+ 1008: 0200000d .word 0x0200000d
+ 100c: 00000000 .word 0x00000000
+
+00001010 <_start>:
+ 1010: ebfffffa bl 1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014: 4770 bx lr
Index: ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.d
===================================================================
--- ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 680)
+++ ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -1,4 +1,14 @@
-#name: Thumb-2-as-Thumb-1 BL failure test
-#source: thumb2-bl-as-thumb1-bad.s
-#ld: -Ttext 0x1000 --section-start .foo=0x401004
-#error: .*\(.text\+0x0\): relocation truncated to fit: R_ARM_THM_CALL against `bar'
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+ 1000: e51ff004 ldr pc, \[pc, #-4\] ; 1004 <_start-0x4>
+ 1004: 0040100d .word 0x0040100d
+
+00001008 <_start>:
+ 1008: f7ff effa blx 1000 <_start-0x8>
+Disassembly of section .foo:
+
+0040100c <bar>:
+ 40100c: 4770 bx lr
Index: ld/testsuite/ld-arm/farcall-thumb-thumb-m.d
===================================================================
--- ld/testsuite/ld-arm/farcall-thumb-thumb-m.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-thumb-thumb-m.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,19 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+ 1000: b540 push {r6, lr}
+ 1002: 4e02 ldr r6, \[pc, #8\] \(100c <_start-0x4>\)
+ 1004: 46fe mov lr, pc
+ 1006: e7fe b.n 1006 <_start-0xa>
+ 1008: bd40 pop {r6, pc}
+ 100a: bf00 nop
+ 100c: 02001015 .word 0x02001015
+
+00001010 <_start>:
+ 1010: f7ff eff6 blx 1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014: 4770 bx lr
Index: ld/testsuite/ld-arm/farcall-thumb-thumb-pic-veneer.d
===================================================================
--- ld/testsuite/ld-arm/farcall-thumb-thumb-pic-veneer.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-thumb-thumb-pic-veneer.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,5 @@
+#name: Thumb-Thumb farcall without BLX
+#source: farcall-thumb-thumb.s
+#as: -march=armv4t
+#ld: -Ttext 0x1000 --section-start .foo=0x2001014
+#error: .*\(.text\+0x0\): relocation truncated to fit: R_ARM_THM_CALL against `bar'
Index: ld/testsuite/ld-arm/farcall-thumb-arm-pic-veneer.d
===================================================================
--- ld/testsuite/ld-arm/farcall-thumb-arm-pic-veneer.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-thumb-arm-pic-veneer.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,5 @@
+#name: Thumb-Thumb farcall without BLX
+#source: farcall-thumb-thumb.s
+#as: -march=armv4t
+#ld: -Ttext 0x1000 --section-start .foo=0x2001014
+#error: .*\(.text\+0x0\): relocation truncated to fit: R_ARM_THM_CALL against `bar'
Index: ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.s
===================================================================
--- ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.s (.../vendor/tags/code_sourcery-2007q3-53) (revision 680)
+++ ld/testsuite/ld-arm/thumb2-bl-as-thumb1-bad.s (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -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 (.../vendor/tags/code_sourcery-2007q3-53) (revision 680)
+++ ld/testsuite/ld-arm/thumb2-bl-bad.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -1,4 +1,14 @@
-#name: Thumb-2 BL failure test
-#source: thumb2-bl-bad.s
-#ld: -Ttext 0x1000 --section-start .foo=0x1001004
-#error: .*\(.text\+0x0\): relocation truncated to fit: R_ARM_THM_CALL against `bar'
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+ 1000: e51ff004 ldr pc, \[pc, #-4\] ; 1004 <_start-0x4>
+ 1004: 0100100d .word 0x0100100d
+
+00001008 <_start>:
+ 1008: f7ff effa blx 1000 <_start-0x8>
+Disassembly of section .foo:
+
+0100100c <bar>:
+ 100100c: 4770 bx lr
Index: ld/testsuite/ld-arm/farcall-thumb-thumb-blx.d
===================================================================
--- ld/testsuite/ld-arm/farcall-thumb-thumb-blx.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-thumb-thumb-blx.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,14 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+ 1000: e51ff004 ldr pc, \[pc, #-4\] ; 1004 <_start-0x4>
+ 1004: 02001015 .word 0x02001015
+
+00001008 <_start>:
+ 1008: f7ff effa blx 1000 <_start-0x8>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014: 4770 bx lr
Index: ld/testsuite/ld-arm/farcall-thumb-arm-blx.d
===================================================================
--- ld/testsuite/ld-arm/farcall-thumb-arm-blx.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-thumb-arm-blx.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,14 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+ 1000: e51ff004 ldr pc, \[pc, #-4\] ; 1004 <_start-0x4>
+ 1004: 02001014 .word 0x02001014
+
+00001008 <_start>:
+ 1008: f7ff effa blx 1000 <_start-0x8>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014: 4770 bx lr
Index: ld/testsuite/ld-arm/thumb2-bl-bad.s
===================================================================
--- ld/testsuite/ld-arm/thumb2-bl-bad.s (.../vendor/tags/code_sourcery-2007q3-53) (revision 680)
+++ ld/testsuite/ld-arm/thumb2-bl-bad.s (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -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/testsuite/ld-arm/farcall-thumb-thumb-m-pic-veneer.d
===================================================================
--- ld/testsuite/ld-arm/farcall-thumb-thumb-m-pic-veneer.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-thumb-thumb-m-pic-veneer.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,5 @@
+#name: Thumb-Thumb farcall without BLX
+#source: farcall-thumb-thumb.s
+#as: -march=armv4t
+#ld: -Ttext 0x1000 --section-start .foo=0x2001014
+#error: .*\(.text\+0x0\): relocation truncated to fit: R_ARM_THM_CALL against `bar'
Index: ld/testsuite/ld-arm/farcall-arm-arm-pic-veneer.d
===================================================================
--- ld/testsuite/ld-arm/farcall-arm-arm-pic-veneer.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-arm-arm-pic-veneer.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,16 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+ 1000: e59fc000 ldr ip, \[pc, #0\] ; 1008 <_start-0x8>
+ 1004: e08ff00c add pc, pc, ip
+ 1008: 02000018 .word 0x02000018
+ 100c: 00000000 .word 0x00000000
+
+00001010 <_start>:
+ 1010: ebfffffa bl 1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001020 <bar>:
+ 2001020: e12fff1e bx lr
Index: ld/testsuite/ld-arm/farcall-arm-thumb-pic-veneer.d
===================================================================
--- ld/testsuite/ld-arm/farcall-arm-thumb-pic-veneer.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-arm-thumb-pic-veneer.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,16 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+ 1000: e59fc000 ldr ip, \[pc, #0\] ; 1008 <_start-0x8>
+ 1004: e08ff00c add pc, pc, ip
+ 1008: 0200000d .word 0x0200000d
+ 100c: 00000000 .word 0x00000000
+
+00001010 <_start>:
+ 1010: ebfffffa bl 1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014: 4770 bx lr
Index: ld/testsuite/ld-arm/farcall-arm-thumb-blx.d
===================================================================
--- ld/testsuite/ld-arm/farcall-arm-thumb-blx.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-arm-thumb-blx.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,14 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+ 1000: e51ff004 ldr pc, \[pc, #-4\] ; 1004 <_start-0x4>
+ 1004: 02001015 .word 0x02001015
+
+00001008 <_start>:
+ 1008: ebfffffc bl 1000 <_start-0x8>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014: 4770 bx lr
Index: ld/testsuite/ld-arm/farcall-arm-arm-be8.d
===================================================================
--- ld/testsuite/ld-arm/farcall-arm-arm-be8.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-arm-arm-be8.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,14 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x8>:
+ 1000: 04f01fe5 .*
+ 1004: 02001020 .word 0x02001020
+
+00001008 <_start>:
+ 1008: fcffffeb .*
+Disassembly of section .foo:
+
+02001020 <bar>:
+ 2001020: 1eff2fe1 .*
Index: ld/testsuite/ld-arm/arm-elf.exp
===================================================================
--- ld/testsuite/ld-arm/arm-elf.exp (.../vendor/tags/code_sourcery-2007q3-53) (revision 680)
+++ ld/testsuite/ld-arm/arm-elf.exp (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -182,6 +182,55 @@ 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=0x2001020" "" {farcall-arm-arm.s}
+ {{objdump -d farcall-arm-arm.d}}
+ "farcall-arm-arm"}
+ {"ARM-ARM farcall (PIC veneer)" "-Ttext 0x1000 --section-start .foo=0x2001020 --pic-veneer" "" {farcall-arm-arm.s}
+ {{objdump -d farcall-arm-arm-pic-veneer.d}}
+ "farcall-arm-arm-pic-veneer"}
+ {"ARM-ARM farcall (BE8)" "-Ttext 0x1000 --section-start .foo=0x2001020 -EB --be8" "-EB" {farcall-arm-arm.s}
+ {{objdump -d farcall-arm-arm-be8.d}}
+ "farcall-arm-arm-be8"}
+
+ {"ARM-Thumb farcall" "-Ttext 0x1000 --section-start .foo=0x2001014" "" {farcall-arm-thumb.s}
+ {{objdump -d farcall-arm-thumb.d}}
+ "farcall-arm-thumb"}
+ {"ARM-Thumb farcall with BLX" "-Ttext 0x1000 --section-start .foo=0x2001014" "-march=armv5t" {farcall-arm-thumb.s}
+ {{objdump -d farcall-arm-thumb-blx.d}}
+ "farcall-arm-thumb-blx"}
+ {"ARM-Thumb farcall (PIC veneer)" "-Ttext 0x1000 --section-start .foo=0x2001014 --pic-veneer" "" {farcall-arm-thumb.s}
+ {{objdump -d farcall-arm-thumb-pic-veneer.d}}
+ "farcall-arm-thumb-pic-veneer"}
+ {"ARM-Thumb farcall with BLX (PIC veneer)" "-Ttext 0x1000 --section-start .foo=0x2001014 --pic-veneer" "-march=armv5t" {farcall-arm-thumb.s}
+ {{objdump -d farcall-arm-thumb-blx-pic-veneer.d}}
+ "farcall-arm-thumb-blx-pic-veneer"}
+
+ {"Thumb-Thumb farcall with BLX" "-Ttext 0x1000 --section-start .foo=0x2001014" "-march=armv5t" {farcall-thumb-thumb.s}
+ {{objdump -d farcall-thumb-thumb-blx.d}}
+ "farcall-thumb-thumb-blx"}
+ {"Thumb-Thumb farcall M profile" "-Ttext 0x1000 --section-start .foo=0x2001014" "-march=armv7m" {farcall-thumb-thumb.s}
+ {{objdump -d farcall-thumb-thumb-m.d}}
+ "farcall-thumb-thumb-m"}
+ {"Thumb-Thumb farcall with BLX (PIC veneer)" "-Ttext 0x1000 --section-start .foo=0x2001014 --pic-veneer" "-march=armv5t" {farcall-thumb-thumb.s}
+ {{objdump -d farcall-thumb-thumb-blx-pic-veneer.d}}
+ "farcall-thumb-thumb-blx-pic-veneer"}
+
+ {"Thumb-ARM farcall" "-Ttext 0x1000 --section-start .foo=0x2001014" "" {farcall-thumb-arm.s}
+ {{objdump -d farcall-thumb-arm.d}}
+ "farcall-thumb-arm"}
+ {"Thumb-ARM farcall with BLX" "-Ttext 0x1000 --section-start .foo=0x2001014" "-march=armv5t" {farcall-thumb-arm.s}
+ {{objdump -d farcall-thumb-arm-blx.d}}
+ "farcall-thumb-arm-blx"}
+ {"Thumb-ARM farcall with BLX (PIC veneer)" "-Ttext 0x1000 --section-start .foo=0x2001014 --pic-veneer" "-march=armv5t" {farcall-thumb-arm.s}
+ {{objdump -d farcall-thumb-arm-blx-pic-veneer.d}}
+ "farcall-thumb-arm-blx-pic-veneer"}
}
run_ld_link_tests $armelftests
@@ -189,9 +238,11 @@ run_dump_test "group-relocs-alu-bad"
run_dump_test "group-relocs-ldr-bad"
run_dump_test "group-relocs-ldrs-bad"
run_dump_test "group-relocs-ldc-bad"
-run_dump_test "thumb2-bl-as-thumb1-bad"
-run_dump_test "thumb2-bl-bad"
run_dump_test "emit-relocs1"
+run_dump_test "farcall-thumb-thumb"
+run_dump_test "farcall-thumb-thumb-pic-veneer"
+run_dump_test "farcall-thumb-thumb-m-pic-veneer"
+run_dump_test "farcall-thumb-arm-pic-veneer"
# Exclude non-ARM-EABI targets.
Index: ld/testsuite/ld-arm/farcall-thumb-thumb.d
===================================================================
--- ld/testsuite/ld-arm/farcall-thumb-thumb.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-thumb-thumb.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,5 @@
+#name: Thumb-Thumb farcall without BLX
+#source: farcall-thumb-thumb.s
+#as: -march=armv4t
+#ld: -Ttext 0x1000 --section-start .foo=0x2001014
+#error: .*\(.text\+0x0\): relocation truncated to fit: R_ARM_THM_CALL against `bar'
Index: ld/testsuite/ld-arm/farcall-thumb-arm.d
===================================================================
--- ld/testsuite/ld-arm/farcall-thumb-arm.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-thumb-arm.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,19 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x18>:
+ 1000: b540 push {r6, lr}
+ 1002: 4e03 ldr r6, \[pc, #12\] \(1010 <_start-0x8>\)
+ 1004: 46fe mov lr, pc
+ 1006: 4730 bx r6
+ 1008: e8bd4040 pop {r6, lr}
+ 100c: e12fff1e bx lr
+ ...
+
+00001018 <_start>:
+ 1018: f7ff eff2 blx 1000 <_start-0x18>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014: 4770 bx lr
Index: ld/testsuite/ld-arm/farcall-thumb-thumb.s
===================================================================
--- ld/testsuite/ld-arm/farcall-thumb-thumb.s (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-thumb-thumb.s (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,19 @@
+@ Test to ensure that a Thumb to Thumb call exceeding 4Mb generates a stub.
+
+ .global _start
+ .syntax unified
+
+@ We will place the section .text at 0x1000.
+
+ .text
+ .thumb_func
+_start:
+ bl bar
+
+@ We will place the section .foo at 0x02001014.
+
+ .section .foo, "xa"
+ .thumb_func
+bar:
+ bx lr
+
Index: ld/testsuite/ld-arm/farcall-thumb-arm.s
===================================================================
--- ld/testsuite/ld-arm/farcall-thumb-arm.s (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-thumb-arm.s (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,19 @@
+@ Test to ensure that a Thumb to ARM call exceeding 4Mb generates a stub.
+
+ .global _start
+ .syntax unified
+
+@ We will place the section .text at 0x1000.
+
+ .text
+ .thumb_func
+_start:
+ bl bar
+
+@ We will place the section .foo at 0x2001014.
+
+ .section .foo, "xa"
+
+bar:
+ bx lr
+
Index: ld/testsuite/ld-arm/farcall-thumb-arm-blx-pic-veneer.d
===================================================================
--- ld/testsuite/ld-arm/farcall-thumb-arm-blx-pic-veneer.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-thumb-arm-blx-pic-veneer.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,16 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+ 1000: e59fc000 ldr ip, \[pc, #0\] ; 1008 <_start-0x8>
+ 1004: e08ff00c add pc, pc, ip
+ 1008: 0200000c .word 0x0200000c
+ 100c: 00000000 .word 0x00000000
+
+00001010 <_start>:
+ 1010: f7ff eff6 blx 1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014: 4770 bx lr
Index: ld/testsuite/ld-arm/farcall-thumb-thumb-blx-pic-veneer.d
===================================================================
--- ld/testsuite/ld-arm/farcall-thumb-thumb-blx-pic-veneer.d (.../vendor/tags/code_sourcery-2007q3-53) (revision 0)
+++ ld/testsuite/ld-arm/farcall-thumb-thumb-blx-pic-veneer.d (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -0,0 +1,16 @@
+.*: file format .*
+
+Disassembly of section .text:
+
+00001000 <_start-0x10>:
+ 1000: e59fc000 ldr ip, \[pc, #0\] ; 1008 <_start-0x8>
+ 1004: e08ff00c add pc, pc, ip
+ 1008: 0200000d .word 0x0200000d
+ 100c: 00000000 .word 0x00000000
+
+00001010 <_start>:
+ 1010: f7ff eff6 blx 1000 <_start-0x10>
+Disassembly of section .foo:
+
+02001014 <bar>:
+ 2001014: 4770 bx lr
Index: ld/ldlang.c
===================================================================
--- ld/ldlang.c (.../vendor/tags/code_sourcery-2007q3-53) (revision 680)
+++ ld/ldlang.c (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -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 (.../vendor/tags/code_sourcery-2007q3-53) (revision 680)
+++ include/elf/arm.h (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -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 (.../vendor/tags/code_sourcery-2007q3-53) (revision 680)
+++ bfd/elf32-arm.c (.../branches/dev-lyon-farcalls-cs-2007q3-53) (revision 680)
@@ -2006,6 +2006,97 @@ 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_thumb_v4t_long_branch_stub[] =
+ {
+ 0xe59fc000, /* ldr ip, [pc, #0] */
+ 0xe12fff1c, /* bx ip */
+ 0x00000000, /* dcd R_ARM_ABS32(X) */
+ };
+
+static const bfd_vma arm_thumb_thumb_long_branch_stub[] =
+ {
+ 0x4e02b540, /* push {r6, lr} */
+ /* ldr r6, [pc, #8] */
+ 0xe7fe46fe, /* mov lr, pc */
+ /* b.n r6 */
+ 0xbf00bd40, /* pop {r6, pc} */
+ /* nop */
+ 0x00000000, /* dcd R_ARM_ABS32(X) */
+ };
+
+static const bfd_vma arm_thumb_arm_v4t_long_branch_stub[] =
+ {
+ 0x4e03b540, /* push {r6, lr} */
+ /* ldr r6, [pc, #12] */
+ 0x473046fe, /* mov lr, pc */
+ /* bx r6 */
+ 0xe8bd4040, /* pop {r6, pc} */
+ 0xe12fff1e, /* bx lr */
+ 0x00000000, /* dcd R_ARM_ABS32(X) */
+ };
+
+static const bfd_vma arm_pic_long_branch_stub[] =
+ {
+ 0xe59fc000, /* ldr r12, [pc] */
+ 0xe08ff00c, /* add pc, pc, ip */
+ 0x00000000, /* dcd R_ARM_ABS32(X) */
+ };
+
+/* Section name for stubs is the associated section name plus this
+ string. */
+#define STUB_SUFFIX ".stub"
+
+enum elf32_arm_stub_type {
+ arm_stub_none,
+ arm_stub_long_branch,
+ arm_thumb_v4t_stub_long_branch,
+ arm_thumb_thumb_stub_long_branch,
+ arm_thumb_arm_v4t_stub_long_branch,
+ arm_stub_pic_long_branch,
+};
+
+struct elf32_arm_stub_hash_entry {
+
+ /* Base hash table entry structure. */
+ struct bfd_hash_entry root;
+
+ /* The stub section. */
+ asection *stub_sec;
+
+ /* Offset within stub_sec of the beginning of this stub. */
+ bfd_vma stub_offset;
+
+ /* Given the symbol's value and its section we can determine its final
+ value when building the stubs (so the stub knows where to jump). */
+ bfd_vma target_value;
+ asection *target_section;
+
+ enum elf32_arm_stub_type stub_type;
+
+ /* The symbol table entry, if any, that this was derived from. */
+ struct elf32_arm_link_hash_entry *h;
+
+ /* Destination symbol type (STT_ARM_TFUNC, ...) */
+ unsigned char st_type;
+
+ /* Where this stub is being called from, or, in the case of combined
+ stub sections, the first input section in the group. */
+ asection *id_sec;
+};
+
/* Used to build a map of a section. This is required for mixed-endian
code/data. */
@@ -2147,6 +2238,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 +2255,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 +2342,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 +2398,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 +2656,997 @@ 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 int using_thumb_only (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,
+ struct elf32_arm_link_hash_entry *hash,
+ bfd_vma destination)
+{
+ bfd_vma location;
+ bfd_signed_vma branch_offset;
+ unsigned int r_type;
+ struct elf32_arm_link_hash_table * globals;
+ int thumb2;
+ int thumb_only;
+ enum elf32_arm_stub_type stub_type = arm_stub_none;
+
+ globals = elf32_arm_hash_table (info);
+
+ thumb_only = using_thumb_only(globals);
+
+ thumb2 = using_thumb2 (globals);
+
+ /* Determine where the call point is. */
+ location = (input_sec->output_offset
+ + input_sec->output_section->vma
+ + rel->r_offset);
+
+ branch_offset = (bfd_signed_vma)(destination - location);
+
+ r_type = ELF32_R_TYPE (rel->r_info);
+
+ /* If the call will go through a PLT entry then we do not need
+ glue. */
+ if (globals->splt != NULL && hash->root.plt.offset != (bfd_vma) -1)
+ return stub_type;
+
+ if (r_type == R_ARM_THM_CALL) {
+ if (( !thumb2
+ &&
+ (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
+ || (branch_offset < THM_MAX_BWD_BRANCH_OFFSET))
+ )
+ ||
+ ( thumb2
+ &&
+ (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
+ || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET))
+ )
+ || ((st_type != STT_ARM_TFUNC) && !globals->use_blx)
+ )
+ {
+ if (st_type == STT_ARM_TFUNC) {
+ /* thumb to thumb */
+ if (!thumb_only) {
+ stub_type = (info->shared | globals->pic_veneer)
+ ? ((globals->use_blx)
+ ? arm_stub_pic_long_branch
+ : arm_stub_none)
+ : (globals->use_blx)
+ ? arm_stub_long_branch
+ : arm_stub_none;
+ } else {
+ stub_type = (info->shared | globals->pic_veneer)
+ ? arm_stub_none
+ : (globals->use_blx)
+ ? arm_thumb_thumb_stub_long_branch
+ : arm_stub_none;
+ }
+ } else {
+ /* thumb to arm */
+ stub_type = (info->shared | globals->pic_veneer)
+ ? ((globals->use_blx)
+ ? arm_stub_pic_long_branch
+ : arm_stub_none)
+ : (globals->use_blx)
+ ? arm_stub_long_branch
+ : arm_thumb_arm_v4t_stub_long_branch;
+ }
+ }
+ } else if (r_type == R_ARM_CALL) {
+ if (st_type == STT_ARM_TFUNC) {
+ /* arm to thumb */
+ /* we have an extra 2-bytes reach because of the mode change
+ (bit 24 (H) of BLX encoding) */
+ if (branch_offset > (ARM_MAX_FWD_BRANCH_OFFSET+2)
+ || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)
+ || !globals->use_blx) {
+ stub_type = (info->shared | globals->pic_veneer)
+ ? arm_stub_pic_long_branch
+ : (globals->use_blx)
+ ? arm_stub_long_branch
+ : arm_thumb_v4t_stub_long_branch;
+ }
+ } else {
+ /* arm to arm */
+ if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
+ || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)) {
+ stub_type = (info->shared | globals->pic_veneer)
+ ? arm_stub_pic_long_branch
+ : arm_stub_long_branch;
+ }
+ }
+ }
+
+ return stub_type;
+}
+
+/* Build a name for an entry in the stub hash table. */
+
+static char *
+elf32_arm_stub_name (const asection *input_section,
+ const asection *sym_sec,
+ const struct elf32_arm_link_hash_entry *hash,
+ const Elf_Internal_Rela *rel)
+{
+ char *stub_name;
+ bfd_size_type len;
+
+ if (hash)
+ {
+ len = 8 + 1 + strlen (hash->root.root.root.string) + 1 + 8 + 1;
+ stub_name = bfd_malloc (len);
+ if (stub_name != NULL)
+ {
+ sprintf (stub_name, "%08x_%s+%x",
+ input_section->id & 0xffffffff,
+ hash->root.root.root.string,
+ (int) rel->r_addend & 0xffffffff);
+ }
+ }
+ else
+ {
+ len = 8 + 1 + 8 + 1 + 8 + 1 + 8 + 1;
+ stub_name = bfd_malloc (len);
+ if (stub_name != NULL)
+ {
+ sprintf (stub_name, "%08x_%x:%x+%x",
+ input_section->id & 0xffffffff,
+ sym_sec->id & 0xffffffff,
+ (int) ELF32_R_SYM (rel->r_info) & 0xffffffff,
+ (int) rel->r_addend & 0xffffffff);
+ }
+ }
+ return stub_name;
+}
+
+/* Look up an entry in the stub hash. Stub entries are cached because
+ creating the stub name takes a bit of time. */
+
+static struct elf32_arm_stub_hash_entry *
+elf32_arm_get_stub_entry (const asection *input_section,
+ const asection *sym_sec,
+ struct elf_link_hash_entry *hash,
+ const Elf_Internal_Rela *rel,
+ struct elf32_arm_link_hash_table *htab)
+{
+ struct elf32_arm_stub_hash_entry *stub_entry;
+ struct elf32_arm_link_hash_entry *h = (struct elf32_arm_link_hash_entry *) hash;
+ const asection *id_sec;
+
+ if ((input_section->flags & SEC_CODE) == 0)
+ return NULL;
+
+ /* If this input section is part of a group of sections sharing one
+ stub section, then use the id of the first section in the group.
+ Stub names need to include a section id, as there may well be
+ more than one stub used to reach say, printf, and we need to
+ distinguish between them. */
+ id_sec = htab->stub_group[input_section->id].link_sec;
+
+ if (h != NULL && h->stub_cache != NULL
+ && h->stub_cache->h == h
+ && h->stub_cache->id_sec == id_sec)
+ {
+ stub_entry = h->stub_cache;
+ }
+ else
+ {
+ char *stub_name;
+
+ stub_name = elf32_arm_stub_name (id_sec, sym_sec, h, rel);
+ if (stub_name == NULL)
+ return NULL;
+
+ stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table,
+ stub_name, FALSE, FALSE);
+ if (h != NULL)
+ h->stub_cache = stub_entry;
+
+ free (stub_name);
+ }
+
+ return stub_entry;
+}
+
+static void elf32_arm_stub_add_mapping_symbol(struct bfd_link_info * link_info,
+ asection *stub_sec, char* name,
+ bfd_vma val)
+{
+ struct bfd_link_hash_entry * bh = NULL;
+ struct elf_link_hash_entry * myh;
+
+ _bfd_generic_link_add_one_symbol (link_info,
+ stub_sec->owner, name,
+ BSF_LOCAL, stub_sec, stub_sec->size + val,
+ NULL, TRUE, FALSE, &bh);
+
+ myh = (struct elf_link_hash_entry *) bh;
+ myh->type = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE);
+ myh->forced_local = 1;
+}
+
+/* Add a new stub entry to the stub hash. Not all fields of the new
+ stub entry are initialised. */
+
+static struct elf32_arm_stub_hash_entry *
+elf32_arm_add_stub (const char *stub_name,
+ asection *section,
+ struct elf32_arm_link_hash_table *htab,
+ struct bfd_link_info * link_info,
+ enum elf32_arm_stub_type stub_type)
+{
+ asection *link_sec;
+ asection *stub_sec;
+ struct elf32_arm_stub_hash_entry *stub_entry;
+
+ link_sec = htab->stub_group[section->id].link_sec;
+ stub_sec = htab->stub_group[section->id].stub_sec;
+ if (stub_sec == NULL)
+ {
+ stub_sec = htab->stub_group[link_sec->id].stub_sec;
+ if (stub_sec == NULL)
+ {
+ size_t namelen;
+ bfd_size_type len;
+ char *s_name;
+
+ namelen = strlen (link_sec->name);
+ len = namelen + sizeof (STUB_SUFFIX);
+ s_name = bfd_alloc (htab->stub_bfd, len);
+ if (s_name == NULL)
+ return NULL;
+
+ memcpy (s_name, link_sec->name, namelen);
+ memcpy (s_name + namelen, STUB_SUFFIX, sizeof (STUB_SUFFIX));
+ stub_sec = (*htab->add_stub_section) (s_name, link_sec);
+ if (stub_sec == NULL)
+ return NULL;
+ htab->stub_group[link_sec->id].stub_sec = stub_sec;
+ }
+ htab->stub_group[section->id].stub_sec = stub_sec;
+ }
+
+ /* Enter this entry into the linker stub hash table. */
+ stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table, stub_name,
+ TRUE, FALSE);
+ if (stub_entry == NULL)
+ {
+ (*_bfd_error_handler) (_("%s: cannot create stub entry %s"),
+ section->owner,
+ stub_name);
+ return NULL;
+ }
+
+ stub_entry->stub_sec = stub_sec;
+ stub_entry->stub_offset = 0;
+ stub_entry->id_sec = link_sec;
+
+ switch(stub_type) {
+ case arm_stub_long_branch:
+ elf32_arm_stub_add_mapping_symbol(link_info, stub_sec, "$a", 0);
+ elf32_arm_stub_add_mapping_symbol(link_info, stub_sec, "$d", 4);
+ break;
+ case arm_thumb_v4t_stub_long_branch:
+ elf32_arm_stub_add_mapping_symbol(link_info, stub_sec, "$a", 0);
+ elf32_arm_stub_add_mapping_symbol(link_info, stub_sec, "$d", 8);
+ break;
+ case arm_thumb_thumb_stub_long_branch:
+ elf32_arm_stub_add_mapping_symbol(link_info, stub_sec, "$t", 0);
+ elf32_arm_stub_add_mapping_symbol(link_info, stub_sec, "$d", 12);
+ break;
+ case arm_thumb_arm_v4t_stub_long_branch:
+ elf32_arm_stub_add_mapping_symbol(link_info, stub_sec, "$t", 0);
+ elf32_arm_stub_add_mapping_symbol(link_info, stub_sec, "$a", 8);
+ elf32_arm_stub_add_mapping_symbol(link_info, stub_sec, "$d", 16);
+ break;
+ case arm_stub_pic_long_branch:
+ elf32_arm_stub_add_mapping_symbol(link_info, stub_sec, "$a", 0);
+ elf32_arm_stub_add_mapping_symbol(link_info, stub_sec, "$d", 8);
+ break;
+ default:
+ BFD_FAIL ();
+ }
+
+ return stub_entry;
+}
+
+static void put_arm_insn (struct elf32_arm_link_hash_table *htab,
+ bfd * output_bfd, bfd_vma val, void * ptr);
+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;
+ struct elf32_arm_link_hash_table * globals;
+
+ /* Massage our args to the form they really have. */
+ stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
+ info = (struct bfd_link_info *) in_arg;
+
+ globals = elf32_arm_hash_table (info);
+
+ htab = elf32_arm_hash_table (info);
+ stub_sec = stub_entry->stub_sec;
+
+ /* Make a note of the offset within the stubs for this entry. */
+ stub_entry->stub_offset = stub_sec->size;
+ loc = stub_sec->contents + stub_entry->stub_offset;
+
+ stub_bfd = stub_sec->owner;
+
+ /* This is the address of the start of the stub */
+ stub_addr = stub_sec->output_section->vma + stub_sec->output_offset
+ + stub_entry->stub_offset;
+
+ /* This is the address of the stub destination */
+ sym_value = (stub_entry->target_value
+ + stub_entry->target_section->output_offset
+ + stub_entry->target_section->output_section->vma);
+
+ switch (stub_entry->stub_type)
+ {
+ case arm_stub_long_branch:
+ template = arm_long_branch_stub;
+ template_size = (sizeof(arm_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_thumb_v4t_stub_long_branch:
+ template = arm_thumb_v4t_long_branch_stub;
+ template_size = (sizeof(arm_thumb_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_thumb_thumb_stub_long_branch:
+ template = arm_thumb_thumb_long_branch_stub;
+ template_size = (sizeof(arm_thumb_thumb_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_thumb_arm_v4t_stub_long_branch:
+ template = arm_thumb_arm_v4t_long_branch_stub;
+ template_size = (sizeof(arm_thumb_arm_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_stub_pic_long_branch:
+ template = arm_pic_long_branch_stub;
+ template_size = (sizeof(arm_pic_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ default:
+ BFD_FAIL ();
+ return FALSE;
+ }
+
+ size = 0;
+ for (i = 0; i < (template_size / 4); i++)
+ {
+ /* A 0 pattern is a placeholder, every other pattern is an
+ instruction */
+ if (template[i] != 0) {
+ put_arm_insn (globals, stub_bfd, template[i], loc + size);
+ } else {
+ bfd_put_32 (stub_bfd, template[i], loc + size);
+ }
+ size += 4;
+ }
+ stub_sec->size += size;
+
+ /* Destination is Thumb. Force bit 0 to 1 to reflect this. */
+ if (stub_entry->st_type == STT_ARM_TFUNC) {
+ sym_value |= 1;
+ }
+
+ switch (stub_entry->stub_type)
+ {
+ case arm_stub_long_branch:
+ _bfd_final_link_relocate (elf32_arm_howto_from_type(R_ARM_ABS32),
+ stub_bfd, stub_sec, stub_sec->contents + 4,
+ stub_entry->stub_offset, sym_value, 0);
+ break;
+ case arm_thumb_v4t_stub_long_branch:
+ _bfd_final_link_relocate (elf32_arm_howto_from_type(R_ARM_ABS32),
+ stub_bfd, stub_sec, stub_sec->contents + 8,
+ stub_entry->stub_offset, sym_value, 0);
+ break;
+ case arm_thumb_thumb_stub_long_branch:
+ _bfd_final_link_relocate (elf32_arm_howto_from_type(R_ARM_ABS32),
+ stub_bfd, stub_sec, stub_sec->contents + 12,
+ stub_entry->stub_offset, sym_value, 0);
+ break;
+ case arm_thumb_arm_v4t_stub_long_branch:
+ _bfd_final_link_relocate (elf32_arm_howto_from_type(R_ARM_ABS32),
+ stub_bfd, stub_sec, stub_sec->contents + 20,
+ stub_entry->stub_offset, sym_value, 0);
+ break;
+ case arm_stub_pic_long_branch:
+ /* We want the value relative to the address 8 bytes from the
+ start of the stub */
+ sym_value -= stub_addr + 8;
+
+ _bfd_final_link_relocate (elf32_arm_howto_from_type(R_ARM_ABS32),
+ stub_bfd, stub_sec, stub_sec->contents + 8,
+ stub_entry->stub_offset, sym_value, 0);
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+/* As above, but don't actually build the stub. Just bump offset so
+ we know stub section sizes. */
+
+static bfd_boolean
+arm_size_one_stub (struct bfd_hash_entry *gen_entry,
+ 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_thumb_v4t_stub_long_branch:
+ template = arm_thumb_v4t_long_branch_stub;
+ template_size = (sizeof(arm_thumb_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_thumb_thumb_stub_long_branch:
+ template = arm_thumb_thumb_long_branch_stub;
+ template_size = (sizeof(arm_thumb_thumb_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_thumb_arm_v4t_stub_long_branch:
+ template = arm_thumb_arm_v4t_long_branch_stub;
+ template_size = (sizeof(arm_thumb_arm_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_stub_pic_long_branch:
+ template = arm_pic_long_branch_stub;
+ template_size = (sizeof(arm_pic_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ default:
+ BFD_FAIL ();
+ return FALSE;
+ break;
+ }
+
+ size = 0;
+ for (i = 0; i < (template_size/4); i++)
+ size += 4;
+ size = (size + 7) & ~7;
+ stub_entry->stub_sec->size += size;
+ return TRUE;
+}
+
+/* External entry points for sizing and building linker stubs. */
+
+/* Set up various things so that we can make a list of input sections
+ for each output section included in the link. Returns -1 on error,
+ 0 when no stubs will be needed, and 1 on success. */
+
+int
+elf32_arm_setup_section_lists (bfd *output_bfd,
+ struct bfd_link_info *info)
+{
+ bfd *input_bfd;
+ unsigned int bfd_count;
+ int top_id, top_index;
+ asection *section;
+ asection **input_list, **list;
+ bfd_size_type amt;
+ struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
+
+ if (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,
+ hash, 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, info, stub_type);
+ 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 +4149,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)
@@ -3167,9 +4322,7 @@ bfd_elf32_arm_process_before_allocation
/* These are the only relocation types we care about. */
if ( r_type != R_ARM_PC24
&& r_type != R_ARM_PLT32
- && r_type != R_ARM_CALL
&& r_type != R_ARM_JUMP24
- && r_type != R_ARM_THM_CALL
&& r_type != R_ARM_THM_JUMP24)
continue;
@@ -3213,7 +4366,6 @@ bfd_elf32_arm_process_before_allocation
{
case R_ARM_PC24:
case R_ARM_PLT32:
- case R_ARM_CALL:
case R_ARM_JUMP24:
/* This one is a call from arm code. We need to look up
the target of the call. If it is a thumb target, we
@@ -3223,7 +4375,6 @@ bfd_elf32_arm_process_before_allocation
record_arm_to_thumb_glue (link_info, h);
break;
- case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
/* This one is a call from thumb code. We look
up the target of the call. If it is not a thumb
@@ -4492,6 +5643,26 @@ static int using_thumb2 (struct elf32_ar
return arch == TAG_CPU_ARCH_V6T2 || arch >= TAG_CPU_ARCH_V7;
}
+/* Determine if we're dealing with a Thumb only architecture. */
+
+static int using_thumb_only (struct elf32_arm_link_hash_table *globals)
+{
+ int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+ Tag_CPU_arch);
+ int profile;
+
+ if (arch != TAG_CPU_ARCH_V7) return FALSE;
+
+ profile = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+ Tag_CPU_arch_profile);
+
+ if (profile == 'M') {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
/* Perform a relocation as part of a final link. */
static bfd_reloc_status_type
@@ -4755,6 +5926,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. */
@@ -4766,7 +5947,7 @@ elf32_arm_final_link_relocate (reloc_how
input_bfd,
h ? h->root.root.string : "(local)");
}
- else if (r_type != R_ARM_CALL || !globals->use_blx)
+ else if (r_type != R_ARM_CALL)
{
/* Check for Arm calling Thumb function. */
if (sym_flags == STT_ARM_TFUNC)
@@ -4782,6 +5963,27 @@ elf32_arm_final_link_relocate (reloc_how
}
}
+ /* Check if a stub has to be inserted because the
+ destination is too far or we are changing mode */
+ if (r_type == R_ARM_CALL) {
+ if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
+ || branch_offset < ARM_MAX_BWD_BRANCH_OFFSET
+ || sym_flags == STT_ARM_TFUNC)
+ {
+ /* The target is out of reach, so redirect the
+ branch to the local stub for this function.
+ */
+
+ stub_entry = elf32_arm_get_stub_entry (input_section,
+ sym_sec, h,
+ rel, globals);
+ if (stub_entry != NULL)
+ value = (stub_entry->stub_offset
+ + stub_entry->stub_sec->output_offset
+ + stub_entry->stub_sec->output_section->vma);
+ }
+ }
+
/* The ARM ELF ABI says that this reloc is computed as: S - P + A
where:
S is the address of the symbol in the relocation.
@@ -4843,7 +6045,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 +6056,7 @@ elf32_arm_final_link_relocate (reloc_how
}
}
}
+ }
break;
case R_ARM_ABS32:
@@ -5115,6 +6320,49 @@ 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 (
+ ( !thumb2
+ &&
+ (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
+ || (branch_offset < THM_MAX_BWD_BRANCH_OFFSET))
+ )
+ ||
+ ( thumb2
+ &&
+ (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
+ || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET))
+ )
+ )
+ {
+ /* The target is out of reach or we are changing modes, so
+ redirect the branch to the local stub for this
+ function.
+ */
+ stub_entry = elf32_arm_get_stub_entry (input_section,
+ sym_sec, h,
+ rel, globals);
+ if (stub_entry != NULL)
+ value = (stub_entry->stub_offset
+ + stub_entry->stub_sec->output_offset
+ + stub_entry->stub_sec->output_section->vma);
+
+ /* This call becomes a call to Arm for sure. Force BLX */
+ lower_insn = (lower_insn & ~0x1000) | 0x0800;
+ }
+ }
+
relocation = value + signed_addend;
relocation -= (input_section->output_section->vma
@@ -10168,6 +11416,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