简体   繁体   中英

ARM assembly - bl is branching to wrong address

SOLVED: a power-cycle fixed it. Must have been electrical issue with the upper flash block that caused the HW execution unit to read wrong value from flash

I am assembly-stepping and something weird is happening.

I have two separate flash blocks and they both contain code. Because they are so far apart, function calls from one block to the other requires an intermediate jump aka veneer which is automatically generated by the linker.

This has worked up until now and I did something - no idea what - to break it.

0x1001a9ca: 0x0cf0c5fe bl 0x10027758 <__atiupy_PutOutput2_veneer>

After that is executed, the PC is 0x100275ce which is wrong .

If I scroll in the disassembly window to 0x10027758 , I see the label __atiupy_PutOutput2_veneer .

Why is it jumping to wrong address?

UPDATE

It's jumping from C-code to C++ code. Yes I have extern "C" in the .h for the functions in C++ code called from C code.

C compile options are: -xc -Wall -Werror -std=c99 -nostdlib -mthumb -mtune=cortex-m4 -mlittle-endian -Wdouble-promotion -DNDEBUG -fdata-sections -ffunction-sections -c -save-temps=obj -g3 -gdwarf-2

C++ compile options are: -mthumb -mlittle-endian -x c++ -gdwarf-2 -g3 -fomit-frame-pointer -fnothrow-opt -ffreestanding -fverbose-asm -std=c++11 -c -fno-rtti -ffunction-sections -fdata-sections -fno-exceptions

By request, some disassembly using objdump -d .

Where it's about to jump to 0x10027758 (but never gets there)

1001a9bc <mp_hal_stdout_tx_strn_cooked>:
1001a9bc:   b580        push    {r7, lr}
1001a9be:   b082        sub sp, #8
1001a9c0:   af00        add r7, sp, #0
1001a9c2:   6078        str r0, [r7, #4]
1001a9c4:   6039        str r1, [r7, #0]
1001a9c6:   6878        ldr r0, [r7, #4]
1001a9c8:   6839        ldr r1, [r7, #0]
1001a9ca:   f00c fec5   bl  10027758 <__atiupy_PutOutput2_veneer>
1001a9ce:   f107 0708   add.w   r7, r7, #8
1001a9d2:   46bd        mov sp, r7
1001a9d4:   bd80        pop {r7, pc}
1001a9d6:   bf00        nop

Where it should jump to

10027758 <__atiupy_PutOutput2_veneer>:
10027758:   b401        push    {r0}
1002775a:   4802        ldr r0, [pc, #8]    ; (10027764 <__atiupy_PutOutput2_veneer+0xc>)
1002775c:   4684        mov ip, r0
1002775e:   bc01        pop {r0}
10027760:   4760        bx  ip
10027762:   bf00        nop
10027764:   00035abd    .word   0x00035abd

It actually jumps to 0x100275ce but I'm pasting the entire function located here

100275c8 <___ZN21CKinetisI2CController7DisableEv_veneer>:
100275c8:   b401        push    {r0}
100275ca:   4802        ldr r0, [pc, #8]    ; (100275d4 <___ZN21CKinetisI2CController7DisableEv_veneer+0xc>)
100275cc:   4684        mov ip, r0
100275ce:   bc01        pop {r0}
100275d0:   4760        bx  ip
100275d2:   bf00        nop
100275d4:   00029b2d    .word   0x00029b2d

I set a breakpoint at mp_hal_stdout_tx_strn_cooked and then assembly-stepped using stepi

p/x $pc
$2 = 0x1001a9c6
disass
Dump of assembler code for function mp_hal_stdout_tx_strn_cooked:
   0x1001a9bc <+0>: push    {r7, lr}
   0x1001a9be <+2>: sub sp, #8
   0x1001a9c0 <+4>: add r7, sp, #0
   0x1001a9c2 <+6>: str r0, [r7, #4]
   0x1001a9c4 <+8>: str r1, [r7, #0]
=> 0x1001a9c6 <+10>:    ldr r0, [r7, #4]
   0x1001a9c8 <+12>:    ldr r1, [r7, #0]
   0x1001a9ca <+14>:    bl  0x10027758 <__atiupy_PutOutput2_veneer>
   0x1001a9ce <+18>:    add.w   r7, r7, #8
   0x1001a9d2 <+22>:    mov sp, r7
   0x1001a9d4 <+24>:    pop {r7, pc}
End of assembler dump.
stepi
stepi
0x1001a9c8  23      atiupy_PutOutput2(str, len);
disass
Dump of assembler code for function mp_hal_stdout_tx_strn_cooked:
   0x1001a9bc <+0>: push    {r7, lr}
   0x1001a9be <+2>: sub sp, #8
   0x1001a9c0 <+4>: add r7, sp, #0
   0x1001a9c2 <+6>: str r0, [r7, #4]
   0x1001a9c4 <+8>: str r1, [r7, #0]
   0x1001a9c6 <+10>:    ldr r0, [r7, #4]
=> 0x1001a9c8 <+12>:    ldr r1, [r7, #0]
   0x1001a9ca <+14>:    bl  0x10027758 <__atiupy_PutOutput2_veneer>
   0x1001a9ce <+18>:    add.w   r7, r7, #8
   0x1001a9d2 <+22>:    mov sp, r7
   0x1001a9d4 <+24>:    pop {r7, pc}
End of assembler dump.
stepi
stepi
0x1001a9ca  23      atiupy_PutOutput2(str, len);
diasass
Undefined command: "diasass".  Try "help".
disass
Dump of assembler code for function mp_hal_stdout_tx_strn_cooked:
   0x1001a9bc <+0>: push    {r7, lr}
   0x1001a9be <+2>: sub sp, #8
   0x1001a9c0 <+4>: add r7, sp, #0
   0x1001a9c2 <+6>: str r0, [r7, #4]
   0x1001a9c4 <+8>: str r1, [r7, #0]
   0x1001a9c6 <+10>:    ldr r0, [r7, #4]
   0x1001a9c8 <+12>:    ldr r1, [r7, #0]
=> 0x1001a9ca <+14>:    bl  0x10027758 <__atiupy_PutOutput2_veneer>
   0x1001a9ce <+18>:    add.w   r7, r7, #8
   0x1001a9d2 <+22>:    mov sp, r7
   0x1001a9d4 <+24>:    pop {r7, pc}
End of assembler dump.
stepi
stepi
0x100275ce in ___ZN21CKinetisI2CController7DisableEv_veneer ()
disass
Dump of assembler code for function ___ZN21CKinetisI2CController7DisableEv_veneer:
   0x100275c8 <+0>: push    {r0}
   0x100275ca <+2>: ldr r0, [pc, #8]    ; (0x100275d4 <___ZN21CKinetisI2CController7DisableEv_veneer+12>)
   0x100275cc <+4>: mov r12, r0
=> 0x100275ce <+6>: pop {r0}
   0x100275d0 <+8>: bx  r12
   0x100275d2 <+10>:    nop
   0x100275d4 <+12>:    ldr r3, [sp, #180]  ; 0xb4
   0x100275d6 <+14>:    movs    r2, r0
End of assembler dump.
stepi
stepi
0x100275d0 in ___ZN21CKinetisI2CController7DisableEv_veneer ()
disass
Dump of assembler code for function ___ZN21CKinetisI2CController7DisableEv_veneer:
   0x100275c8 <+0>: push    {r0}
   0x100275ca <+2>: ldr r0, [pc, #8]    ; (0x100275d4 <___ZN21CKinetisI2CController7DisableEv_veneer+12>)
   0x100275cc <+4>: mov r12, r0
   0x100275ce <+6>: pop {r0}
=> 0x100275d0 <+8>: bx  r12
   0x100275d2 <+10>:    nop
   0x100275d4 <+12>:    ldr r3, [sp, #180]  ; 0xb4
   0x100275d6 <+14>:    movs    r2, r0
End of assembler dump.

Assembly file before compilation

Someone wanted the assembly file output from the compiler, rather than objdump 's interpretation. This output is a result of the compile option -save-temps=obj

.LFE14:
    .size   mp_hal_stdout_tx_strn, .-mp_hal_stdout_tx_strn
    .section    .text.mp_hal_stdout_tx_strn_cooked,"ax",%progbits
    .align  2
    .global mp_hal_stdout_tx_strn_cooked
    .thumb
    .thumb_func
    .type   mp_hal_stdout_tx_strn_cooked, %function
mp_hal_stdout_tx_strn_cooked:
.LFB15:
    .loc 1 22 0
    .cfi_startproc
    @ args = 0, pretend = 0, frame = 8
    @ frame_needed = 1, uses_anonymous_args = 0
    push    {r7, lr}
.LCFI11:
    .cfi_def_cfa_offset 8
    .cfi_offset 7, -8
    .cfi_offset 14, -4
    sub sp, sp, #8
.LCFI12:
    .cfi_def_cfa_offset 16
    add r7, sp, #0
.LCFI13:
    .cfi_def_cfa_register 7
    str r0, [r7, #4]
    str r1, [r7, #0]
    .loc 1 23 0
    ldr r0, [r7, #4]
    ldr r1, [r7, #0]
    bl  atiupy_PutOutput2
    .loc 1 25 0
    add r7, r7, #8
    mov sp, r7
    pop {r7, pc}
    .cfi_endproc

(Sad) update

I created the minimal example but the issue doesn't occur. Therefore, I think it's something in my environment. I will start reverting local changes and will update. I wouldn't call the below code complete because you'd need the linker file but since it doesn't reproduce the issue, I don't see a point in providing everything.

main.cpp

#include "common.h"

int main()
{
    test1();
}

int main_test()
{
    int a = 5;
    a = 23424 * a;
    return a;
}

test.c

#include "common.h"

void test1()
{
    int a;
    a = main_test();
    a *= 2;
}

common.h

#ifdef __cplusplus
extern "C" {
#endif //#ifdef __cplusplus


int main_test();
void test1();


#ifdef __cplusplus
}
#endif //#ifdef __cplusplus

main.obj is placed in "lower" flash block while test.obj is in upper block. When jumping from upper to lower, the PC correctly jumps to the right address for veneer .

SOLVED: a power-cycle fixed it. Must have been electrical issue with the upper flash block that caused the HW execution unit to read wrong value from flash.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM