ldr wrong relative address calculation

Asked by luuka on 2020-11-06

First of all - I'm a beginner in arm assembler. I'm trying to implement saving exception info in c project. Handler is written as an inline assembler inside c code. Idea is to extract relevant information from the exception stack frame and store it for later inspection. After that I would reset the CPU.

Everything seems to work fine except that I discovered strange behaviour with generated asm code which made me think if I have some ground level misunderstanding.

CPU: EFM32JG1B200F256GM48 (Cortex M3)
Compiler: arm-none-eabi-g++ (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 7.3.1 20180622 (release) [ARM/embedded-7-branch revision 261907]

This is a function that I'm using to extract an active stack (from EXC_RETURN that is stored in LR during exception) and then another function 'save_stack_frame' (source not shown here) is responsible for extracting and storing stack frame info.

__attribute__((naked))
__USED static void
handle_exception(void)
{
    __asm volatile(
    "tst lr, #4                                    \n"
    "ite eq                                        \n"
    "mrseq r0, msp                                 \n"
    "mrsne r0, psp                                 \n"
    "ldr r1, save_stack_frame_address              \n"
    "bx r1                                         \n"
    "save_stack_frame_address: .word save_stack_frame  \n");
}

Sometime above code will produce valid and sometimes invalid ldr pc relative offset. After experimentation I discovered that if a linker puts function on word boundary then instruction is ok but if function ends on half word address then the result becomes invalid. Calling invalid version will end up as jump (BX) to the wrong address.

If I put ".align 2\n" as a first instruction in handle_exception function or if linker puts function in word aligned address then everything works.

I would like to know the reason why this is happening? Is it a bug in the compiler or do I have to manage function and/or data alignment for myself? And in that case which alignment is wrong here?

000085ce <handle_exception()>: -> function on half word boundary
    "ite eq                                        \n"
    "mrseq r0, msp                                 \n"
    "mrsne r0, psp                                 \n"
    "ldr r1, save_stack_frame_address              \n"
    "bx r1                                         \n"
    "save_stack_frame_address: .word save_stack_frame  \n");
    85ce: f01e 0f04 tst.w lr, #4
    85d2: bf0c       ite eq
    85d4: f3ef 8008 mrseq r0, MSP
    85d8: f3ef 8009 mrsne r0, PSP
    85dc: f8df 1004 ldr.w r1, [pc, #4] ; 85e4 <save_stack_frame_address+0x2>     --> This loads wrong data to r1. If I put "ldr r1, [pc, #2]   \n" in source then it is valid
    85e0: 4708       bx r1

000085e2 <save_stack_frame_address>:
    85e2: 8549       .short 0x8549
    85e4: 0000       .short 0x0000
}

Valid version:

000085d0 <handle_exception()>: -> function on word boundary
    "ite eq                                        \n"
    "mrseq r0, msp                                 \n"
    "mrsne r0, psp                                 \n"
    "ldr r1, save_stack_frame_address              \n"
    "bx r1                                         \n"
    "save_stack_frame_address: .word save_stack_frame  \n");
    85d0: f01e 0f04 tst.w lr, #4
    85d4: bf0c       ite eq
    85d6: f3ef 8008 mrseq r0, MSP
    85da: f3ef 8009 mrsne r0, PSP
    85de: f8df 1004 ldr.w r1, [pc, #4] ; 85e4 <save_stack_frame_address> --> valid address
    85e2: 4708       bx r1

000085e4 <save_stack_frame_address>:
    85e4: 00008549 .word 0x00008549
}

Question information

Language:
English Edit question
Status:
Expired
For:
GNU Arm Embedded Toolchain Edit question
Assignee:
No assignee Edit question
Last query:
2020-11-06
Last reply:
2020-11-22
Launchpad Janitor (janitor) said : #1

This question was expired because it remained in the 'Open' state without activity for the last 15 days.