GCC 和 STM32 的堆栈帧不正确

Stack Frame not correct with GCC and STM32

我正在尝试在 GNU GCC(而非 ARM GCC)中创建简单的 SVC 处理程序。如果调用 SVC 服务,它会正确输入 SVC_Handler(),但是当我尝试查找用于调用 SVC 处理程序的 SVC 编号时 (svc_number),我得到值 248 而不是 1。

我正在使用 CubeMX IDE 和 Nucleo-F401RE 开发板。

代码:

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))

我在 svc_number 之后使用带断点的 watch 表达式,它的值为 248,而不是它应该的 1。

Service Calls (SVC)主要用于RTOS设计,让软件进入特权模式。 ARM GCC 对 SV 调用有一些很好的支持,而在 GNU GCC 中你必须自己做这一切。理论是这样的: 进行 SV 调用(在我的示例中为 SVC_Service_Add() )时,它会调用 SVC_Handler()。 SVC_Handler() 检查使用了哪个堆栈(主 msp 或进程 psp),该信息通过读取 Link 寄存器 (LR) 的位 2 找到。取决于此,msp 或 psp 保存在 r0 中。之后,编译器将 r0、r1、r2、r3、r12、r14、return 地址和 xPSR 放在 svc_args 中,因此这些参数可以在 SVC_Handler_Main 中使用。 svc_args[0]=r0, svc_args[1]=r1,...svc_args[6]=SP(我们感兴趣的,保存SVC号的那个).由于 Cortex M4 堆栈完全递减,我们需要执行 [-2] 以获得我们感兴趣的 svc_args[6] 的字节。由于对 SVC_Handler 的调用是由 SVC_ADD 宏 (0x01), ((char *) svc_args[6])[-2] 应该等于 SVC_ADD, 因此可以从 SVC_Handler_Main( ).我没有得到 1,出于某种原因我得到了 248。

问题:为什么 svc_number 等于 248 而我期待的是 1

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

这将创建 svc #1 形式的汇编程序。请注意,“1”编码在指令操作码中。为了找到“1”,您必须查看 lr 并在该地址加载操作码,然后解析(通过屏蔽)该值。

例如,

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

这效率不高,因为您将 code 管道视为 data。这样做的正常方法是定义一个服务寄存器,如 r7,然后将 r7 设置为 svc #0 指令之前的“#1”。所以,宏是这样的,

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

您可以只使用 r0,但如果您的调用层次结构变得更加复杂,许多函数可能会将 args 放入 r0、r1、r2、r3,然后您将需要打乱它们。这就是通常使用 r7 的原因。


why is svc_number equal to 248 whereas I was expecting 1

您似乎认为它被放入了堆栈,但不是这种情况。值 248 只是堆栈中的随机值。