I'm debugging an ARM cortex M4 (STM32F4) running FreeRTOS. Inside the assembly FreeRTOS function vPortSVCHandler, there's a branch instruction

bx r14

using GDB, I step through instruction by instruction and find that r14 (lr) contains the value 0xfffffffd (not a valid address) immediately before the bx instruction is executed.

For some reason, GDB doesn't follow the bx instruction with si (hangs), but I'm still able to step via openOCD. I find that the function that's branched to is in fact a valid function at address 0x08012abc.

From the ARM docs on bx, its argument should be a register containing an address to branch to.

Clearly, I'm misunderstanding or looking at the wrong docs.

I tried tweaking lr with GDB just before the branch instruction. Changing it to 0x0 or 0xfffffff7 results in a hard fault shortly after the branch.

How does this branch instruction, when called with a value of 0xfffffffd, result in branching to a valid function at 0x08102abc?


Values around FFFFFFFF are used in Cortex-M for exception returns (ECX_RETURN). Currently defined values:

  1. 0xFFFFFFF1 - return to Handler mode, restore state from main stack

  2. 0xFFFFFFF9 - return to Thread mode, restore state from main stack

  3. 0xFFFFFFFD - return to Thread mode, restore state from process stack

So the actual branch address is taken from the stack (MSP or PSP, depending on the low bits of the value). See the linked document for more details.

Since GDB is mostly used for user-mode debugging, it does not expect such shenanigans and probably tries to set a breakpoint at the value of LR which naturally fails. OpenOCD knows about exceptions and is able to step properly.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.