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 只是堆栈中的随机值。
我正在尝试在 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 只是堆栈中的随机值。