https://sourceware.org/bugzilla/show_bug.cgi?id=32974
Bug ID: 32974
Summary: GNU assembler for ARM Thumb2 generates invalid branch
offset when changing a BLX instruction to a BL
instruction
Product: binutils
Version: 2.44
Status: UNCONFIRMED
Severity: critical
Priority: P2
Component: gas
Assignee: unassigned at sourceware dot org
Reporter: honarbacht at ubisys dot de
Target Milestone: ---
Created attachment 16098
--> https://sourceware.org/bugzilla/attachment.cgi?id=16098&action=edit
Assembler source file that demonstrates the issue.
We had a C++ application targeting the ARM Cortex-A7, which crashed during
initialization. We debugged the issue and found the following root-cause:
0000143c <_Z41__static_initialization_and_destruction_0v.lto_priv.7>:
143c: b510 push {r4, lr}
143e: 4c08 ldr r4, [pc, #32] @ (1460
<_Z41__static_initialization_and_destruction_0v.lto_priv.7+0x24>)
1440: 4620 mov r0, r4
1442: f7ff ff90 bl 1366
<_ZN20CZigBeeManufacturersC1Ev+0x2>
1446: f240 0200 movw r2, #0
1446: R_ARM_THM_MOVW_ABS_NC __dso_handle
144a: f2c0 0200 movt r2, #0
144a: R_ARM_THM_MOVT_ABS __dso_handle
144e: f240 0100 movw r1, #0
144e: R_ARM_THM_MOVW_ABS_NC
_ZN20CZigBeeManufacturersD1Ev
1452: f2c0 0100 movt r1, #0
1452: R_ARM_THM_MOVT_ABS
_ZN20CZigBeeManufacturersD1Ev
1456: 4620 mov r0, r4
1458: f7ff fffe bl 0 <__aeabi_atexit>
1458: R_ARM_THM_CALL __aeabi_atexit
145c: bd10 pop {r4, pc}
145e: bf00 nop
1460: 00000004 .word 0x00000004
1460: R_ARM_ABS32 .bss
In above function generated by GCC 15 the branch to symbol
_ZN20CZigBeeManufacturersC1Ev (constructor of a global object) is offset by 2.
This results in the stack frame being corrupted, because the push { ..., lr }
instruction gets skipped, and a subsequent pop { ..., pc } results in a
wild-jump into nirvana.
We further tracked this down and used the -save-temps LD option to keep the
assembler files generated by GCC during the LTO stage. Here everything was
still fine:
_Z41__static_initialization_and_destruction_0v.lto_priv.7:
.fnstart
.LFB110:
.loc 29 38 1 view -0
.cfi_startproc
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
push {r4, lr}
.cfi_def_cfa_offset 8
.cfi_offset 4, -8
.cfi_offset 14, -4
.loc 29 19 50 view .LVU1115
ldr r4, .L540
mov r0, r4
blx (_ZN20CZigBeeManufacturersC1Ev)
.loc 29 19 28 discriminator 1 view .LVU1116
movw r2, #:lower16:__dso_handle
movt r2, #:upper16:__dso_handle
movw r1, #:lower16:_ZN20CZigBeeManufacturersD1Ev
movt r1, #:upper16:_ZN20CZigBeeManufacturersD1Ev
mov r0, r4
bl __aeabi_atexit
.loc 29 38 1 view .LVU1117
pop {r4, pc}
As you can see, there is no +2 offset in the assembler source generated by
GCC/LD.
We noticed the following warning messages from the assembler:
ubisys-g1-v2/mcgsd-dbg-unstripped.ltrans36.ltrans.s:24095: Warning: blx to
Thumb func '_ZN20CZigBeeManufacturersC1Ev' from Thumb ISA state changed to bl
There were many such messages, hundreds for this application.
We concluded that the error occurred when changing BLX to BL (in the Thumb2 ISA
-> Thumb2 ISA situation).
Changing the assembler source from BLX to BL manually and running the assembler
on this file yielded the correct/expected output (no offset +2 in the branch
target).
In the specific example:
1442: f7ff ff90 bl 1366
<_ZN20CZigBeeManufacturersC1Ev+0x2>
the output should have been:
1442: f7ff ff8f bl 1364 <_ZN20CZigBeeManufacturersC1Ev>
We further analyzed the genesis of the issue and offset computed by as turned
out to be incorrect in some cases (probably a 50:50 chance, depending on
alignment of the branch target).
We tracked this down to the function that applies various fix-ups,
md_apply_fix(). It turned out that the value passed to md_apply_fix() was off
by 2 in some cases.
As a fix, we compute the relative offset as offset = target - PC - 4 and
compare this to the value passed in as argument to above-mentioned function. In
many cases it is the same value, in some cases, it differs by 2. In such cases
we print a warning message and apply the correct offset, instead.
case BFD_RELOC_THUMB_PCREL_BLX:
/* If there is a blx from a thumb state function to
another thumb function flip this to a bl and warn
about it. */
if (fixP->fx_addsy
&& !S_FORCE_RELOC (fixP->fx_addsy, true)
&& (S_GET_SEGMENT (fixP->fx_addsy) == seg)
&& THUMB_IS_FUNC (fixP->fx_addsy))
{
const char *name = S_GET_NAME (fixP->fx_addsy);
as_warn_where (fixP->fx_file, fixP->fx_line,
_("blx to Thumb func '%s' from Thumb ISA state changed
to bl"),
name);
newval = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
newval = newval | 0x1000;
md_number_to_chars (buf+THUMB_SIZE, newval, THUMB_SIZE);
fixP->fx_r_type = BFD_RELOC_THUMB_PCREL_BRANCH23;
fixP->fx_done = 1;
// ubisys BLX/BL bug-fix >>>>>>>>>>>>>>>
// Compute the relative offset as target - pc - 4
const offsetT offset_ = S_GET_VALUE(fixP->fx_addsy) -
(fixP->fx_where + fixP->fx_frag->fr_address) - 4;
if (value != offset_)
{
as_warn_where (fixP->fx_file, fixP->fx_line,
_("ubisys: applying BLX/BL bug-fix: %d -> %d"), value, offset_);
value = offset_;
}
// >>>>>>>>>>>>>>> ubisys BLX/BL bug-fix
}
With this fix in place, the application ran as expected. We also confirmed by
looking at the generated object code, which was now correct and as expected.
Attached is a generated assembler source file that illustrates the issue. In
nine places the +2 difference exists. There are also two object dumps for
reference: vanilla (without the suggested fix above) and fixed (with the
suggested fix above).
There might be other locations where the same issue exits. And there might be
better ways of applying the fix (e.g. simply subtracting fixP->fx_where to
value).
--
You are receiving this mail because:
You are on the CC list for the bug.