Details: embedded C, cortex M4 with operating system (RTX Kernel), compiled with ARM Compiler V5.06 update6 and Microlib.
Inside a periodic task there are some functons call, one is a debug function:
/**
* @details
*/
void DEBUG_Utilities_IndirectTraceString
(
const char * moduleName,
const char * string
)
{
Debug_Message_t * dbgMsg;
char tempString[16];
...
Context switch, registers are pushed in the stack and the stack pointer gets updated (0x2000FE80 points now to0x2000FE58 as expected, 40 bytes for r0-r8 and LR ).
The local char array tempString is created and the first element is allocated at 0x2000FE58,second one at 0x2000FE59 etc, thus crashing the pushed registers.
Why?
I'd expect the array to grow downards be allocated below the stack pointer, not above. Any hint would be gladly appreciated.
Further clarification becouse I used the wrong wording: I am not concerned with the fact that array elements are indexed sequentially upwards, this is what I expect. I am concerned with the fact the first element of the array is allocated at the stack pointer after the push instead of allocating 20bytes of ram to the two variables (array and the custom type).
Also, I am not debugging the stack with the debug function. What happens is that the tempString array is populated and destroys the content of the pushed r0-r3.This is why I inspected the code, the assembly and noticed that the tempString is located at the wrong address.
Here is the assembly as per disassembler:
DEBUG_Utilities_IndirectTraceString
i.DEBUG_Utilities_IndirectTraceString
$Thumb
{
08006F44 PUSH.W {R0-R8, LR}
08006F48 MOV R4, R0
08006F4A MOV R5, R1
dbgMsg = DebugTask_CreateMessage();
before the push, this code is called:
08002C28 ADD R1, PC, #0x016C ; 0x08002D98
08002C2A ADD R0, PC, #0x0150 ; 0x08002D7C
08002C2C BL DEBUG_Utilities_IndirectTraceString ;0x08006F44
05/08/2024: adding image to clarify what I see pre and post push.
Here the registers and memory dump pre and post push.
I think you have misunderstood what is happening here. The stack frame already includes the local variables when it is established on entry to the function. The accesses you are seeing are to the memory allocated to the string, not the pushed registers. That is, the stack pointer includes the memory for the string, it is not separate
In any event in a context switch the scheduler stores the context of a preempted task and switches to the task stack of the preempting task. The previous context is stored on the stack of the preempted task, not the stack you are observing.
Here your stack frame will comprise of:
dbgMsg
So before any caller register save, that is 24 bytes, then the remaining 16 bytes is the caller saving caller registers that it will modify (4 registers saved). It does not blindly store all registers, so it is variable.
Note that the precise details may vary somewhat, but the above accounts for your observation without the misapprehension that it somehow involves context storage.