我需要在嵌入式硬件上进行运行时堆栈分析,以确保我已经分配了足够的堆栈空间。我理解基本理论。在启动时,使用已知模式(例如所有0xFF或0xAA)初始化堆栈,然后允许程序运行。随着它的运行,堆栈会增长和缩小,但它永远无法恢复原始模式。经过足够长的时间后,检查堆栈并找到要更改的最后一个堆栈值的地址。
我不需要便携式解决方案,只需要dsPIC33EP512MU810和PIC32MX795F512的解决方案。我的后备计划是逐渐减少堆栈大小,直到我得到一个堆栈溢出,但这只有在我有一个候选版本时才有效,我更喜欢在整个开发和生产过程中持续监控的解决方案。
答案 0 :(得分:1)
这是我用于带有XC16编译器的dsPIC33EP512MU810的代码。通过创建一个自动变量,我可以访问堆栈顶部附近,然后递增几个字节(STACK_VAR_PAD
),并用保护字节填充堆栈的其余部分。
#define STACK_VAR_PAD 0x0020
#define TOP_OF_STACK_ADDR 0x4000
#define STACK_GUARD_BYTE 0xEE
void WriteStackGuardBytes(void)
{
static __eds__ char * ptr;
static unsigned int start_addr;
static unsigned int addr;
//Use a variable on the stack
char stack_var;
ptr = &stack_var;
//Casting to 32-bit first supresses complier warning
//This chip uses a 16-bit address space
start_addr = (unsigned long int)ptr + STACK_VAR_PAD;
for (addr = start_addr; addr < TOP_OF_STACK_ADDR; addr++)
{
*((unsigned int *)(addr)) = STACK_GUARD_BYTE;
}
}
unsigned int ReadStackGuardBytes(void)
{
static __eds__ char * ptr;
static unsigned int start_addr;
static unsigned int addr;
static unsigned int top_addr;
//Use a variable on the stack here
char stack_var;
ptr = &stack_var;
//Casting to 32-bit first supresses complier warning
//This chip uses a 16-bit address space
start_addr = (unsigned long int)ptr;
top_addr = start_addr;
for (addr = start_addr; addr < TOP_OF_STACK_ADDR; addr++)
{
if (*((unsigned int *)(addr)) != STACK_GUARD_BYTE)
{
top_addr = addr;
}
}
return TOP_OF_STACK_ADDR - top_addr;
}
链接器声称堆栈在0x8000
处停止(我将设置设置为生成映射文件)但我看到零到0x4000
,然后是垃圾数据。我完全理解堆栈的内容是不确定的,但我也确定使用这种方法我的堆栈永远不会超过0x1500
左右,因此只检查0x4000
对我有效。
答案 1 :(得分:1)
我在dsPICEP系列中的“DrRobotNinja”代码中遇到了一些错误,大多数情况下,所有内容都使用16位变量+将地址增加2而不是1来通过保持字对齐来停止地址错误
#define STACK_VAR_PAD 0x0020
#define TOP_OF_STACK_ADDR SPLIM
#define STACK_GUARD_BYTE 0xA1DE
void WriteStackGuardBytes(void)
{
static __eds__ int * ptr;
static unsigned int start_addr;
static unsigned int addr;
//Use a variable on the stack
int stack_var;
ptr = &stack_var;
//Casting to 32-bit first supresses complier warning
//This chip uses a 16-bit address space
start_addr = (unsigned long int)ptr + STACK_VAR_PAD;
for (addr = start_addr; addr < TOP_OF_STACK_ADDR; addr += 2)
{
*((unsigned int *)(addr)) = STACK_GUARD_BYTE;
}
}
unsigned int ReadStackGuardBytes(void)
{
static __eds__ int * ptr;
static unsigned int start_addr;
static unsigned int addr;
static unsigned int top_addr;
//Use a variable on the stack here
int stack_var;
ptr = &stack_var;
//Casting to 32-bit first supresses complier warning
//This chip uses a 16-bit address space
start_addr = (unsigned long int)ptr;
top_addr = start_addr;
for (addr = start_addr; addr < TOP_OF_STACK_ADDR; addr += 2)
{
if (*((unsigned int *)(addr)) != STACK_GUARD_BYTE)
{
top_addr = addr;
}
else
{
break; //make this more robust and search for 2-3 consecutive
}
}
return TOP_OF_STACK_ADDR - top_addr;
}
答案 2 :(得分:0)
我相信你知道,等待堆栈溢出是最糟糕的解决方案。溢出可能导致任何行为,包括什么都不做(!),反转数字的符号,改变逻辑流程,以及仅在星期二下雨星期五下雨时做所有这些。它是随机的,你无法知道它是否发生在你身上。
你有OS(即使是简单的OS)吗?如果是这样,您可以创建一个低优先级的任务来监视其他任务堆栈的保护字节(您已知的模式),如果它们没有达到预期值,它会执行某些操作。
如果没有操作系统,那么您将转向静态分析,而不是动态分析。您应该能够从链接器输出中清除有关最坏情况深度的有用信息,这应该为每个调用提供堆栈使用,并且知道调用依赖关系图。它不会告诉你一切,因为它不计算在函数调用之上堆叠的中断,但它应该让你非常接近。你拿最坏的情况编号,添加最坏情况的中断号码,然后填上它,无论如何只是为了确保...