diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c index e6f4a9f..a40f9f1 100644 --- a/bfd/elf32-arm.c +++ b/bfd/elf32-arm.c @@ -2283,6 +2283,8 @@ static const bfd_vma elf32_arm_nacl_plt_entry [] = #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) +#define THM2_MAX_FWD_COND_BRANCH_OFFSET (((1 << 20) -2) + 4) +#define THM2_MAX_BWD_COND_BRANCH_OFFSET (-(1 << 20) + 4) enum stub_insn_type { @@ -3667,7 +3669,8 @@ arm_type_of_stub (struct bfd_link_info *info, /* ST_BRANCH_TO_ARM is nonsense to thumb-only targets when we are considering a function call relocation. */ - if (thumb_only && (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24) + if (thumb_only && (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24 + || r_type == R_ARM_THM_JUMP19) && branch_type == ST_BRANCH_TO_ARM) branch_type = ST_BRANCH_TO_THUMB; @@ -3711,7 +3714,7 @@ arm_type_of_stub (struct bfd_link_info *info, branch_offset = (bfd_signed_vma)(destination - location); if (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24 - || r_type == R_ARM_THM_TLS_CALL) + || r_type == R_ARM_THM_TLS_CALL || r_type == R_ARM_THM_JUMP19) { /* Handle cases where: - this call goes too far (different Thumb/Thumb2 max @@ -3727,10 +3730,15 @@ arm_type_of_stub (struct bfd_link_info *info, || (thumb2 && (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET))) + || (thumb2 + && (branch_offset > THM2_MAX_FWD_COND_BRANCH_OFFSET + || (branch_offset < THM2_MAX_BWD_COND_BRANCH_OFFSET)) + && (r_type == R_ARM_THM_JUMP19)) || (branch_type == ST_BRANCH_TO_ARM && (((r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_TLS_CALL) && !globals->use_blx) - || (r_type == R_ARM_THM_JUMP24)) + || (r_type == R_ARM_THM_JUMP24) + || (r_type == R_ARM_THM_JUMP19)) && !use_plt)) { if (branch_type == ST_BRANCH_TO_THUMB) @@ -5347,7 +5355,8 @@ elf32_arm_size_stubs (bfd *output_bfd, /* For historical reasons, use the existing names for ARM-to-Thumb and Thumb-to-ARM stubs. */ if ((r_type == (unsigned int) R_ARM_THM_CALL - || r_type == (unsigned int) R_ARM_THM_JUMP24) + || r_type == (unsigned int) R_ARM_THM_JUMP24 + || r_type == (unsigned int) R_ARM_THM_JUMP19) && branch_type == ST_BRANCH_TO_ARM) sprintf (stub_entry->output_name, THUMB2ARM_GLUE_ENTRY_NAME, sym_name); @@ -9125,6 +9134,9 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto, bfd_signed_vma reloc_signed_max = 0xffffe; bfd_signed_vma reloc_signed_min = -0x100000; bfd_signed_vma signed_check; + enum elf32_arm_stub_type stub_type = arm_stub_none; + struct elf32_arm_stub_hash_entry *stub_entry; + struct elf32_arm_link_hash_entry *hash; /* Need to refetch the addend, reconstruct the top three bits, and squish the two 11 bit pieces together. */ @@ -9156,8 +9168,25 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto, *unresolved_reloc_p = FALSE; } - /* ??? Should handle interworking? GCC might someday try to - use this for tail calls. */ + hash = (struct elf32_arm_link_hash_entry *)h; + + stub_type = arm_type_of_stub (info, input_section, rel, + st_type, &branch_type, + hash, value, sym_sec, + input_bfd, sym_name); + if (stub_type != arm_stub_none) + { + stub_entry = elf32_arm_get_stub_entry (input_section, + sym_sec, h, + rel, globals, + stub_type); + if (stub_entry != NULL) + { + value = (stub_entry->stub_offset + + stub_entry->stub_sec->output_offset + + stub_entry->stub_sec->output_section->vma); + } + } relocation = value + signed_addend; relocation -= (input_section->output_section->vma diff --git a/ld/testsuite/ld-arm/arm-elf.exp b/ld/testsuite/ld-arm/arm-elf.exp index 0576847..3289dfd 100644 --- a/ld/testsuite/ld-arm/arm-elf.exp +++ b/ld/testsuite/ld-arm/arm-elf.exp @@ -449,6 +449,16 @@ set armeabitests_nonacl { {{objdump -d farcall-thumb-arm-pic-veneer.d}} "farcall-thumb-arm-pic-veneer"} + {"Thumb-ARM farcall cond" "-Ttext 0x8000 --section-start .foo=0x118000" "" "-W" {farcall-cond-thumb-arm.s} + {{objdump -d farcall-cond-thumb-arm.d}} + "farcall-cond-thumb-arm"} + {"Thumb-ARM farcall cond (BE8)" "-Ttext 0x8000 --section-start .foo=0x118000 -EB --be8" "" "-W -EB" {farcall-cond-thumb-arm.s} + {{objdump -d farcall-cond-thumb-arm.d}} + "farcall-cond-thumb-arm-be8"} + {"Thumb-ARM farcall cond (BE)" "-Ttext 0x8000 --section-start .foo=0x118000 -EB" "" "-W -EB" {farcall-cond-thumb-arm.s} + {{objdump -d farcall-cond-thumb-arm.d}} + "farcall-cond-thumb-arm-be"} + {"Multiple farcalls" "-Ttext 0x1000 --section-start .foo=0x2002020" "" "" {farcall-mix.s} {{objdump -d farcall-mix.d}} "farcall-mix"} @@ -547,6 +557,31 @@ set armeabitests_nonacl { {{objdump -d jump-reloc-veneers-long.d}} "jump-reloc-veneers-long"} + {"R_ARM_THM_JUMP19 Relocation veneers: Short" + "--section-start destsect=0x000108002 --section-start .text=0x8000" "" + "-march=armv7-m -mthumb" + {jump-reloc-veneers-cond.s} + {{objdump -d jump-reloc-veneers-cond-short.d}} + "jump-reloc-veneers-cond-short"} + {"R_ARM_THM_JUMP19 Relocation veneers: Long" + "--section-start destsect=0x00108004 --section-start .text=0x8000" "" + "-march=armv7-m -mthumb" + {jump-reloc-veneers-cond.s} + {{objdump -d jump-reloc-veneers-cond-long.d}} + "jump-reloc-veneers-cond-long"} + {"R_ARM_THM_JUMP19 Relocation veneers: Short backward" + "--section-start destsect=0x8004 --section-start .text=0x108000" "" + "-march=armv7-m -mthumb" + {jump-reloc-veneers-cond.s} + {{objdump -d jump-reloc-veneers-cond-short-backward.d}} + "jump-reloc-veneers-cond-short-backward"} + {"R_ARM_THM_JUMP19 Relocation veneers: Long backward" + "--section-start destsect=0x8002 --section-start .text=0x108000" "" + "-march=armv7-m -mthumb" + {jump-reloc-veneers-cond.s} + {{objdump -d jump-reloc-veneers-cond-long-backward.d}} + "jump-reloc-veneers-cond-long-backward"} + {"Default group size" "-Ttext 0x1000 --section-start .foo=0x2003020" "" "" {farcall-group.s farcall-group2.s} {{objdump -d farcall-group.d}} "farcall-group-default"} diff --git a/ld/testsuite/ld-arm/farcall-cond-thumb-arm.d b/ld/testsuite/ld-arm/farcall-cond-thumb-arm.d new file mode 100644 index 0000000..0b0172b --- /dev/null +++ b/ld/testsuite/ld-arm/farcall-cond-thumb-arm.d @@ -0,0 +1,18 @@ +.*: file format .* + +Disassembly of section .text: + +00008000 <_start>: + 8000: f050 a002 bne.w 58008 <__bar_from_thumb> + \.\.\. + 58004: f040 8000 bne.w 58008 <__bar_from_thumb> + +00058008 <__bar_from_thumb>: + 58008: 4778 bx pc + 5800a: 46c0 nop ; \(mov r8, r8\) + 5800c: ea02fffb b 118000 + +Disassembly of section .foo: + +00118000 : + 118000: e12fff1e bx lr diff --git a/ld/testsuite/ld-arm/farcall-cond-thumb-arm.s b/ld/testsuite/ld-arm/farcall-cond-thumb-arm.s new file mode 100644 index 0000000..809f2fc --- /dev/null +++ b/ld/testsuite/ld-arm/farcall-cond-thumb-arm.s @@ -0,0 +1,27 @@ +@ Test to ensure that a Thumb to ARM call exceeding 4Mb generates a stub. +@ Check that we can generate two types of stub in the same section. + + .global _start + .syntax unified + +@ We will place the section .text at 0x1c01010. + + .text + .thumb_func +_start: + .global bar + bne bar +@ This call is close enough to generate a "short branch" stub +@ or no stub if blx is available. + .space 0x050000 + bne bar + +@ We will place the section .foo at 0x2001014. + + .section .foo, "xa" + + .arm + .type bar, %function +bar: + bx lr + diff --git a/ld/testsuite/ld-arm/jump-reloc-veneers-cond-long-backward.d b/ld/testsuite/ld-arm/jump-reloc-veneers-cond-long-backward.d new file mode 100644 index 0000000..ee0709a --- /dev/null +++ b/ld/testsuite/ld-arm/jump-reloc-veneers-cond-long-backward.d @@ -0,0 +1,24 @@ + +.*: file format.* + + +Disassembly of section destsect: + +00008002 <[^>]*>: + 8002: f7ff fffe bl 8002 + +Disassembly of section .text: + +001080.. <[^>]*>: + 1080..: f040 8002 bne.w 108008 <__dest_veneer> + 1080..: 0000 movs r0, r0 + ... + +001080.. <[^>]*>: + 1080..: b401 push {r0} + 1080..: 4802 ldr r0, \[pc, #8\] ; \(108014 <__dest_veneer\+0xc>\) + 1080..: 4684 mov ip, r0 + 1080..: bc01 pop {r0} + 1080..: 4760 bx ip + 1080..: bf00 nop + 1080..: 00008003 .word 0x00008003 diff --git a/ld/testsuite/ld-arm/jump-reloc-veneers-cond-long.d b/ld/testsuite/ld-arm/jump-reloc-veneers-cond-long.d new file mode 100644 index 0000000..276a24e --- /dev/null +++ b/ld/testsuite/ld-arm/jump-reloc-veneers-cond-long.d @@ -0,0 +1,24 @@ + +.*: file format.* + + +Disassembly of section destsect: + +00108004 <[^>]*>: + 108004: f7ff fffe bl 108004 + +Disassembly of section .text: + +000080.. <[^>]*>: + 80..: (8002f040|f0408002) .word 0x(8002f040|f0408002) + 80..: 0000 movs r0, r0 + ... + +000080.. <[^>]*>: + 80..: b401 push {r0} + 80..: 4802 ldr r0, \[pc, #8\] ; \(80.. <__dest_veneer\+0xc>\) + 80..: 4684 mov ip, r0 + 80..: bc01 pop {r0} + 80..: 4760 bx ip + 80..: bf00 nop + 80..: 00108005 .word 0x00108005 diff --git a/ld/testsuite/ld-arm/jump-reloc-veneers-cond-short-backward.d b/ld/testsuite/ld-arm/jump-reloc-veneers-cond-short-backward.d new file mode 100644 index 0000000..d05425b --- /dev/null +++ b/ld/testsuite/ld-arm/jump-reloc-veneers-cond-short-backward.d @@ -0,0 +1,13 @@ + +.*: file format.* + + +Disassembly of section destsect: + +00008004 <[^>]*>: + 8004: f7ff fffe bl 8004 + +Disassembly of section .text: + +001080.. <_start>: + 1080..: f440 8000 bne.w 8004 diff --git a/ld/testsuite/ld-arm/jump-reloc-veneers-cond-short.d b/ld/testsuite/ld-arm/jump-reloc-veneers-cond-short.d new file mode 100644 index 0000000..08c2212 --- /dev/null +++ b/ld/testsuite/ld-arm/jump-reloc-veneers-cond-short.d @@ -0,0 +1,13 @@ + +.*: file format.* + + +Disassembly of section destsect: + +00108002 <[^>]*>: + 108002: f7ff fffe bl 108002 + +Disassembly of section .text: + +000080.. <[^>]*>: + 80..: f07f afff bne.w 108002 diff --git a/ld/testsuite/ld-arm/jump-reloc-veneers-cond.s b/ld/testsuite/ld-arm/jump-reloc-veneers-cond.s new file mode 100644 index 0000000..83f969c --- /dev/null +++ b/ld/testsuite/ld-arm/jump-reloc-veneers-cond.s @@ -0,0 +1,12 @@ + .text + .syntax unified + .thumb_func + .global _start + .type _start,%function +_start: + bne dest + + .section destsect, "x" + .thumb_func +dest: + bl dest