首页 > 解决方案 > GCC 和 STM32 的堆栈帧不正确

问题描述

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

服务调用 (SVC) 主要用于 RTOS 设计,以使软件进入特权模式。ARM GCC 对 SV 调用有一些很好的支持,而在 GNU GCC 中你必须自己做这一切。理论是这样的:当一个 SV 调用(在我的例子中是 SVC_Service_Add() )被调用时,它会调用 SVC_Handler()。SVC_Handler() 检查使用了哪个堆栈(主 msp 或进程 psp),该信息通过读取链接寄存器 (LR) 的位 2 找到。取决于此,msp 或 psp 保存在 r0 中。之后,编译器将 r0,r1,r2,r3,r12,r14、返回地址和 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

标签: gccassemblyarmstm32svc

解决方案


#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.

这效率不高,因为您将代码管道视为数据。执行此操作的正常方法是定义一个服务寄存器,例如r7,然后在指令r7之前设置为“#1” 。svc #0所以,宏是这样的,

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

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


为什么svc_number等于 248 而我期待 1

看起来您认为它已放入堆栈,但事实并非如此。值 248 只是堆栈中的随机值。


推荐阅读