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.

Reply via email to