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.