简体   繁体   中英

Stack Frame not correct with GCC and STM32

I am trying to create simple SVC Handler in GNU GCC (Not ARM GCC). If SVC service is called, it enters SVC_Handler() correctly, but when I try to find the SVC number used to call SVC Handler ( svc_number ), I get value 248 instead of 1.

I'm using CubeMX IDE and Nucleo-F401RE board.

Code:

void SVC_Handler(void)
{
     asm volatile (
            "TST lr, #4\t\n"
            "ITE EQ\t\n"
            "MRSEQ r0, msp\t\n"
            "MRSNE r0, psp\t\n"
            "B %[SVC_Handler_Main]\t\n"
            :
            : [SVC_Handler_Main] "i" (SVC_Handler_Main)
            : "r0"
    );

}

void SVC_Handler_Main(unsigned int* svc_args) {
     uint8_t svc_number;

     svc_number = ((char* )svc_args[6])[-2];
     switch(svc_number) { // <- that's where I get 248 instead of 1
     case SVC_ADD:
         SVC_Add_Handler(svc_args[0], svc_args[1]);
         break;
     default:
         break;
     }
}

int __attribute__ ((noinline)) SVC_Service_Add(int x, int y) {
    svc(SVC_ADD);
}

#define SVC_ADD 1

#define svc(code) asm volatile ("SVC %[immediate]"::[immediate] "I" (code))

I used watch expression with breakpoint after svc_number and it's value is 248, instead of 1 that it should be.

Service Calls (SVC) are used mainly in RTOS design for software to enter privileged mode. ARM GCC has some nice support for SV calls, whereas in GNU GCC you have to do it all yourself. The theory goes like this: When a SV call (in my case SVC_Service_Add() ) is made, it invokes the SVC_Handler(). SVC_Handler() checks which stack is used (main msp or process psp), that information is found by reading bit 2 of Link Register (LR). Depending on that, either msp or psp is saved in r0. After that, compiler puts r0,r1,r2,r3,r12,r14, return address and xPSR in svc_args, so those parameters can be used in SVC_Handler_Main. svc_args[0]=r0, svc_args[1]=r1,...svc_args[6]=SP(the one we are interested in, the one where SVC number is kept). Since Cortex M4 stack is full descending, we need to do [-2] to get the byte of svc_args[6] we are interested in. Since the call to the SVC_Handler was done by the SVC_ADD macro (0x01), ((char *) svc_args[6])[-2] should equal to SVC_ADD, so a proper function can be called from SVC_Handler_Main(). I'm not getting 1, I'm getting 248 for some reason.

Question : why is svc_number equal to 248 whereas I was expecting 1

#define svc(code) asm volatile ("SVC %[immediate]"::[immediate] "I" (code))

This creates the assembler of the form svc #1 . Note that the '1' is encoded in the instruction op-code. In order to find the '1', you have to look at the lr and load the op-code at that address and then parse (by masking) the value.

For instance,

ldr r1,[lr]            ; load svc opcode to r1 (might need an offset)
and r1, r1, #SVC_MASK  ; may need shifts as well.

This is not efficient as you are treating a code pipe as data . The normal way to do this is to define an service register, like r7 and then set r7 to the '#1' before the svc #0 instruction. So, the macro is something like,

#define svc(code) asm volatile ("mov r7, %[immediate]\n" \
                                " SVC #0"::[immediate] "I" (code) \
                                 : "r7" /*clobbers r7*)

You can use just r0 , but if your call hierarchy gets more complex, many functions may put args in r0,r1,r2,r3 and then you will need to shuffle them. That is why r7 is typically used.


why is svc_number equal to 248 whereas I was expecting 1

It looks like you thought it was put on the stack and this is NOT the case. The value 248 is just something random on the stack.

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