aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Lyon <christophe.lyon@linaro.org>2024-06-19 12:35:30 +0000
committerChristophe Lyon <christophe.lyon@linaro.org>2024-09-04 13:35:10 +0000
commit31ed3a9d691493486f6e32357d89a55229dbdc0a (patch)
tree2f359f29f3f1684fc18a507b545df2e5709f0da1
parent9a3781f12032153e27a38ec0894f8b663ed67306 (diff)
downloadbinutils-31ed3a9d691493486f6e32357d89a55229dbdc0a.zip
binutils-31ed3a9d691493486f6e32357d89a55229dbdc0a.tar.gz
binutils-31ed3a9d691493486f6e32357d89a55229dbdc0a.tar.bz2
arm: Do not insert stubs needing Arm code on Thumb-only cores.
We recently fixed a bug in libgcc (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115360) where a symbol was missing a %function .type decoration. This meant the linker would silently pick the wrong type of 'farcall stub', involving Arm-mode instructions on Thumb-only CPUs. This patch emits an error instead, and warns in some other cases, to encourage users to add the missing '.type foo,%function' directive. In practice: in arm_type_of_stub() we no longer try to infer which stub to use if the destination is of unknown type and the CPU is Thumb-only; so we won't lie to elf32_arm_size_stubs() which does not check branch_type. If branch_type is ST_BRANCH_TO_ARM but the CPU is Thumb-only, we now convert it to ST_BRANCH_TO_THUMB only if the destination is of absolute type. This is to support the case where the destination of the branch is defined by the linker script (see thumb-b-lks-sym.s and thumb-bl-lks-sym.s testcases for instance). The motivating case is covered by the new farcall-missing-type testcase, where we now emit an error message. We do not emit an error when branch_type is ST_BRANCH_UNKNOWN and the CPU supports Arm-mode: a lot of legacy code (e.g. newlib's crt0.S) lacks the corresponding '.type foo, %function' directives and even a (too verbose) warning could be perceived as a nuisance. Existing testcases where such a warning would trigger: arm-static-app.s (app_func, app_func2) arm-rel32.s (foo) arm-app.s (app_func) rel32-reject.s () main) fix-arm1176.s (func_to_branch_to) but this list is not exhaustive. For the sake of clarity, the patch replaces occurrences of sym.st_target_internal = 0; with sym.st_target_internal = ST_BRANCH_TO_ARM; enum arm_st_branch_type is defined in include/elf/arm.h, and relies on ST_BRANCH_TO_ARM==0, as sym.st_target_internal is also initialized to 0 in other target-independent parts of BFD code. (For instance, swapping the ST_BRANCH_TO_ARM and ST_BRANCH_TO_THUMB entries in the enum definition leads to 'interesting' results...) Regarding the testsuite: * new expected warning for thumb-b-lks-sym and thumb-bl-lks-sym * new testcase farcall-missing-type to check the new error case * attr-merge-arch-2b.s, branch-futures (and bfs-1.s) updated to avoid a diagnostic Tested on arm-eabi and arm-pe with no regression.
-rw-r--r--bfd/elf32-arm.c99
-rw-r--r--ld/testsuite/ld-arm/arm-elf.exp5
-rw-r--r--ld/testsuite/ld-arm/attr-merge-arch-2b.s1
-rw-r--r--ld/testsuite/ld-arm/bfs-1.s1
-rw-r--r--ld/testsuite/ld-arm/branch-futures.d10
-rw-r--r--ld/testsuite/ld-arm/farcall-missing-type-bad.s7
-rw-r--r--ld/testsuite/ld-arm/farcall-missing-type-main.s9
-rw-r--r--ld/testsuite/ld-arm/farcall-missing-type.d5
-rw-r--r--ld/testsuite/ld-arm/farcall-missing-type.ld23
-rw-r--r--ld/testsuite/ld-arm/thumb-b-lks-sym.output1
-rw-r--r--ld/testsuite/ld-arm/thumb-bl-lks-sym.output1
11 files changed, 143 insertions, 19 deletions
diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c
index ca76bee..7441ee2 100644
--- a/bfd/elf32-arm.c
+++ b/bfd/elf32-arm.c
@@ -4226,12 +4226,33 @@ arm_type_of_stub (struct bfd_link_info *info,
r_type = ELF32_R_TYPE (rel->r_info);
+ /* Don't pretend we know what stub to use (if any) when we target a
+ Thumb-only target and we don't know the actual destination
+ type. */
+ if (branch_type == ST_BRANCH_UNKNOWN && thumb_only)
+ return stub_type;
+
/* 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
|| r_type == R_ARM_THM_JUMP19)
&& branch_type == ST_BRANCH_TO_ARM)
- branch_type = ST_BRANCH_TO_THUMB;
+ {
+ if (sym_sec == bfd_abs_section_ptr)
+ /* As an exception, assume that absolute symbols are of the
+ right kind (Thumb). They are presumably defined in the
+ linker script, where it is not possible to declare them as
+ Thumb (and thus are seen as Arm mode). We'll inform the
+ user with a warning, though, in
+ elf32_arm_final_link_relocate. */
+ branch_type = ST_BRANCH_TO_THUMB;
+ else
+ /* Otherwise do not silently build a stub, and let the users
+ know they have to fix their code. Indeed, we could decide
+ to insert a stub involving Arm code and/or BLX, leading to
+ a run-time crash. */
+ return stub_type;
+ }
/* For TLS call relocs, it is the caller's responsibility to provide
the address of the appropriate trampoline. */
@@ -10382,14 +10403,6 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
else
addend = signed_addend = rel->r_addend;
- /* ST_BRANCH_TO_ARM is nonsense to thumb-only targets when we
- are resolving a function call relocation. */
- if (using_thumb_only (globals)
- && (r_type == R_ARM_THM_CALL
- || r_type == R_ARM_THM_JUMP24)
- && branch_type == ST_BRANCH_TO_ARM)
- branch_type = ST_BRANCH_TO_THUMB;
-
/* Record the symbol information that should be used in dynamic
relocations. */
dynreloc_st_type = st_type;
@@ -10452,6 +10465,68 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
gotplt_offset = (bfd_vma) -1;
}
+ /* ST_BRANCH_TO_ARM is nonsense to thumb-only targets when we are
+ resolving a function call relocation. We want to inform the user
+ that something is wrong. */
+ if (using_thumb_only (globals)
+ && (r_type == R_ARM_THM_CALL
+ || r_type == R_ARM_THM_JUMP24)
+ && branch_type == ST_BRANCH_TO_ARM
+ /* Calls through a PLT are special: the assembly source code
+ cannot be annotated with '.type foo(PLT), %function', and
+ they handled specifically below anyway. */
+ && splt == NULL)
+ {
+ if (sym_sec == bfd_abs_section_ptr)
+ {
+ /* As an exception, assume that absolute symbols are of the
+ right kind (Thumb). They are presumably defined in the
+ linker script, where it is not possible to declare them as
+ Thumb (and thus are seen as Arm mode). Inform the user with
+ a warning, though. */
+ branch_type = ST_BRANCH_TO_THUMB;
+
+ if (sym_sec->owner)
+ _bfd_error_handler
+ (_("warning: %pB(%s): Forcing bramch to absolute symbol in Thumb mode (Thumb-only CPU)"
+ " in %pB"),
+ sym_sec->owner, sym_name, input_bfd);
+ else
+ _bfd_error_handler
+ (_("warning: (%s): Forcing branch to absolute symbol in Thumb mode (Thumb-only CPU)"
+ " in %pB"),
+ sym_name, input_bfd);
+ }
+ else
+ /* Otherwise do not silently build a stub, and let the users
+ know they have to fix their code. Indeed, we could decide
+ to insert a stub involving Arm code and/or BLX, leading to
+ a run-time crash. */
+ branch_type = ST_BRANCH_UNKNOWN;
+ }
+
+ /* Fail early if branch_type is ST_BRANCH_UNKNOWN and we target a
+ Thumb-only CPU. We could emit a warning on Arm-capable targets
+ too, but that would be too verbose (a lot of legacy code does not
+ use the .type foo, %function directive). */
+ if (using_thumb_only (globals)
+ && (r_type == R_ARM_THM_CALL
+ || r_type == R_ARM_THM_JUMP24)
+ && branch_type == ST_BRANCH_UNKNOWN)
+ {
+ if (sym_sec != NULL
+ && sym_sec->owner != NULL)
+ _bfd_error_handler
+ (_("%pB(%s): Unknown destination type (ARM/Thumb) in %pB"),
+ sym_sec->owner, sym_name, input_bfd);
+ else
+ _bfd_error_handler
+ (_("(%s): Unknown destination type (ARM/Thumb) in %pB"),
+ sym_name, input_bfd);
+
+ return bfd_reloc_notsupported;
+ }
+
resolved_to_zero = (h != NULL
&& UNDEFWEAK_NO_DYNAMIC_RELOC (info, h));
@@ -17838,7 +17913,7 @@ elf32_arm_output_map_sym (output_arch_syminfo *osi,
sym.st_other = 0;
sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE);
sym.st_shndx = osi->sec_shndx;
- sym.st_target_internal = 0;
+ sym.st_target_internal = ST_BRANCH_TO_ARM;
elf32_arm_section_map_add (osi->sec, names[type][1], offset);
return osi->func (osi->flaginfo, names[type], &sym, osi->sec, NULL) == 1;
}
@@ -17995,7 +18070,7 @@ elf32_arm_output_stub_sym (output_arch_syminfo *osi, const char *name,
sym.st_other = 0;
sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
sym.st_shndx = osi->sec_shndx;
- sym.st_target_internal = 0;
+ sym.st_target_internal = ST_BRANCH_TO_ARM;
return osi->func (osi->flaginfo, name, &sym, osi->sec, NULL) == 1;
}
@@ -19743,7 +19818,7 @@ elf32_arm_swap_symbol_in (bfd * abfd,
{
if (!bfd_elf32_swap_symbol_in (abfd, psrc, pshn, dst))
return false;
- dst->st_target_internal = 0;
+ dst->st_target_internal = ST_BRANCH_TO_ARM;
/* New EABI objects mark thumb function symbols by setting the low bit of
the address. */
diff --git a/ld/testsuite/ld-arm/arm-elf.exp b/ld/testsuite/ld-arm/arm-elf.exp
index 272d518..5f380e3 100644
--- a/ld/testsuite/ld-arm/arm-elf.exp
+++ b/ld/testsuite/ld-arm/arm-elf.exp
@@ -653,10 +653,10 @@ set armeabitests_nonacl {
{{objdump -d thumb2-bl-bad.d}}
"thumb2-bl-bad"}
{"Branch to linker script symbol with BL for thumb-only target" "-T branch-lks-sym.ld" "" "" {thumb-bl-lks-sym.s}
- {{objdump -d thumb-bl-lks-sym.d}}
+ {{ld thumb-bl-lks-sym.output} {objdump -d thumb-bl-lks-sym.d}}
"thumb-bl-lks-sym"}
{"Branch to linker script symbol with B for thumb-only target" "-T branch-lks-sym.ld" "" "" {thumb-b-lks-sym.s}
- {{objdump -d thumb-b-lks-sym.d}}
+ {{ld thumb-b-lks-sym.output} {objdump -d thumb-b-lks-sym.d}}
"thumb-b-lks-sym"}
{"erratum 760522 fix (default for v6z)" "--section-start=.foo=0x2001014" ""
@@ -1207,6 +1207,7 @@ run_dump_test "attr-merge-wchar-40-nowarn"
run_dump_test "attr-merge-wchar-42-nowarn"
run_dump_test "attr-merge-wchar-44-nowarn"
run_dump_test "farcall-section"
+run_dump_test "farcall-missing-type"
run_dump_test "attr-merge-unknown-1"
run_dump_test "attr-merge-unknown-2"
run_dump_test "attr-merge-unknown-2r"
diff --git a/ld/testsuite/ld-arm/attr-merge-arch-2b.s b/ld/testsuite/ld-arm/attr-merge-arch-2b.s
index 5771835..f20522f 100644
--- a/ld/testsuite/ld-arm/attr-merge-arch-2b.s
+++ b/ld/testsuite/ld-arm/attr-merge-arch-2b.s
@@ -4,5 +4,6 @@
.align 2
.global foo
.type foo, %function
+ .thumb_func
foo:
bx lr
diff --git a/ld/testsuite/ld-arm/bfs-1.s b/ld/testsuite/ld-arm/bfs-1.s
index 2b72819..1e31d44 100644
--- a/ld/testsuite/ld-arm/bfs-1.s
+++ b/ld/testsuite/ld-arm/bfs-1.s
@@ -4,6 +4,7 @@
.thumb
.global _start
.global target
+.type target, %function
_start:
target:
add r0, r0, r1
diff --git a/ld/testsuite/ld-arm/branch-futures.d b/ld/testsuite/ld-arm/branch-futures.d
index 427ecce..bc9ae8e 100644
--- a/ld/testsuite/ld-arm/branch-futures.d
+++ b/ld/testsuite/ld-arm/branch-futures.d
@@ -5,13 +5,13 @@
Disassembly of section .text:
0[0-9a-f]+ <future>:
- [0-9a-f]+: f2c0 e807 bf a, 8012 <_start>
- [0-9a-f]+: f182 e805 bfcsel 6, 8012 <_start>, a, eq
- [0-9a-f]+: f080 c803 bfl 2, 8012 <_start>
+ [0-9a-f]+: f2c0 e807 bf a, 8012 <target>
+ [0-9a-f]+: f182 e805 bfcsel 6, 8012 <target>, a, eq
+ [0-9a-f]+: f080 c803 bfl 2, 8012 <target>
[0-9a-f]+: 4408 add r0, r1
0[0-9a-f]+ <branch>:
- [0-9a-f]+: f000 b800 b.w 8012 <_start>
+ [0-9a-f]+: f000 b800 b.w 8012 <target>
-0[0-9a-f]+ <_start>:
+0[0-9a-f]+ <target>:
[0-9a-f]+: 4408 add r0, r1
diff --git a/ld/testsuite/ld-arm/farcall-missing-type-bad.s b/ld/testsuite/ld-arm/farcall-missing-type-bad.s
new file mode 100644
index 0000000..e087992
--- /dev/null
+++ b/ld/testsuite/ld-arm/farcall-missing-type-bad.s
@@ -0,0 +1,7 @@
+ .thumb
+ .cpu cortex-m33
+ .syntax unified
+ .section .text.bar
+ .global bad @ untyped
+# .type bad, function
+bad: bx lr
diff --git a/ld/testsuite/ld-arm/farcall-missing-type-main.s b/ld/testsuite/ld-arm/farcall-missing-type-main.s
new file mode 100644
index 0000000..c97df0c
--- /dev/null
+++ b/ld/testsuite/ld-arm/farcall-missing-type-main.s
@@ -0,0 +1,9 @@
+ .thumb
+ .cpu cortex-m33
+ .syntax unified
+ .global __start
+ .type __start, function
+__start:
+ push {r4, lr}
+ bl bad
+ pop {r4, pc}
diff --git a/ld/testsuite/ld-arm/farcall-missing-type.d b/ld/testsuite/ld-arm/farcall-missing-type.d
new file mode 100644
index 0000000..9766bf1
--- /dev/null
+++ b/ld/testsuite/ld-arm/farcall-missing-type.d
@@ -0,0 +1,5 @@
+#source: farcall-missing-type-main.s
+#source: farcall-missing-type-bad.s
+#as:
+#ld:-T farcall-missing-type.ld
+#error: .* .*/farcall-missing-type-bad.o\(bad\): Unknown destination type \(ARM/Thumb\) in .*/farcall-missing-type-main.o
diff --git a/ld/testsuite/ld-arm/farcall-missing-type.ld b/ld/testsuite/ld-arm/farcall-missing-type.ld
new file mode 100644
index 0000000..b1136de
--- /dev/null
+++ b/ld/testsuite/ld-arm/farcall-missing-type.ld
@@ -0,0 +1,23 @@
+/* Linker script to configure memory regions. */
+MEMORY
+{
+ FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x40000 /* 256k */
+ FLASH2 (rx) : ORIGIN = 0x200001e0, LENGTH = 0x4000
+}
+
+ENTRY(__start)
+
+SECTIONS
+{
+ .far_away_section :
+ {
+ *(.text.bar)
+ } > FLASH2
+
+ .text :
+ {
+ *(.text*)
+
+ } > FLASH
+
+}
diff --git a/ld/testsuite/ld-arm/thumb-b-lks-sym.output b/ld/testsuite/ld-arm/thumb-b-lks-sym.output
new file mode 100644
index 0000000..4961204
--- /dev/null
+++ b/ld/testsuite/ld-arm/thumb-b-lks-sym.output
@@ -0,0 +1 @@
+.* \(extFunc\): Forcing branch to absolute symbol in Thumb mode \(Thumb-only CPU\) in tmpdir/thumb-b-lks-sym.o
diff --git a/ld/testsuite/ld-arm/thumb-bl-lks-sym.output b/ld/testsuite/ld-arm/thumb-bl-lks-sym.output
new file mode 100644
index 0000000..7c5e354
--- /dev/null
+++ b/ld/testsuite/ld-arm/thumb-bl-lks-sym.output
@@ -0,0 +1 @@
+.* \(extFunc\): Forcing branch to absolute symbol in Thumb mode \(Thumb-only CPU\) in tmpdir/thumb-bl-lks-sym.o