STM32中栈填充循环未执行问题求助(GCC 5.3编译)
首先,你的循环没执行的核心原因是GCC编译器优化时没意识到汇编语句修改了Stack_ptr变量,导致它错误地认为Stack_ptr始终是初始值0,进而判定循环条件(uint32_t)Stack_ptr > Stack_bot不成立,直接把整个循环代码优化掉了。
具体原因拆解
你这段汇编代码:
asm volatile ("str sp, [%0]" :: "r" (&Stack_ptr));
虽然功能是把当前栈指针sp的值写入Stack_ptr变量,但你只声明了输入操作数("r" (&Stack_ptr)),没有告诉编译器这个汇编语句会修改Stack_ptr。在优化模式下(哪怕是-O0也会做基础的数据流分析),编译器会认为Stack_ptr的值从未改变,还是初始的0。而0显然远小于你的Stack_bot(0x20007C00),所以直接跳过了循环,n自然保持为0。
当你把这段代码放到单独函数并禁用优化时,编译器不会进行数据流分析,会严格执行每一条语句,包括汇编语句对Stack_ptr的修改,所以循环能正常运行。
另外,你的Stack_bot计算存在潜在错误:
Stack_bot = (uint32_t)(&_estack - &_Min_Stack_Size);
_estack和_Min_Stack_Size是链接脚本中定义的符号值(_estack是栈顶地址,_Min_Stack_Size是栈大小),不是内存中的变量。在C语言中,&_estack取的是这个符号在符号表中的地址,而不是它代表的栈顶数值。你当前得到正确结果只是巧合(可能链接脚本的布局让符号地址等于符号值),正确的写法应该直接使用符号的值。
修复方案
1. 告诉编译器汇编语句的副作用
给汇编语句添加输出操作数和内存约束,明确告知编译器Stack_ptr被修改了:
// 修正后的汇编语句 asm volatile ("str sp, [%0]" : "=r" (Stack_ptr) : : "memory");
这里:
"=r" (Stack_ptr)表示汇编语句会把结果写入Stack_ptr(输出操作数)"memory"约束告诉编译器这段汇编会修改内存,防止它对内存操作进行重排序或优化。
如果坚持用原来的写法(写入&Stack_ptr),至少要添加"memory"约束:
asm volatile ("str sp, [%0]" :: "r" (&Stack_ptr) : "memory");
2. 修正Stack_bot的计算
确保正确使用链接脚本中的符号值,先声明符号再计算:
// 声明链接脚本中的符号 extern uint32_t _estack; extern uint32_t _Min_Stack_Size; // 正确计算栈底地址(栈从_estack向下生长) Stack_bot = (uint32_t)&_estack - (uint32_t)_Min_Stack_Size; // 若你的链接脚本中_estack直接是地址值,用下面这句: // Stack_bot = (uint32_t)_estack - (uint32_t)_Min_Stack_Size;
3. 可选:禁用当前代码块的优化
如果不想修改汇编语句,可以用GCC的编译指令临时禁用这段代码的优化:
#pragma GCC push_options #pragma GCC optimize ("O0") // 你的栈填充代码放在这里 uint32_t* Stack_ptr = 0; uint32_t Stack_bot; uint32_t n = 0; asm volatile ("str sp, [%0]" :: "r" (&Stack_ptr)); Stack_bot = (uint32_t)_estack - (uint32_t)_Min_Stack_Size; n = 0; while ((uint32_t)(Stack_ptr) > Stack_bot) { Stack_ptr--; n++; *Stack_ptr = 0xAA55A55A; } #pragma GCC pop_options
完整修正后的代码示例
#include <stdint.h> // 声明链接脚本中的符号 extern uint32_t _estack; extern uint32_t _Min_Stack_Size; int main(void) { uint32_t* Stack_ptr = NULL; uint32_t Stack_bot; uint32_t n = 0; // 修正后的汇编语句:告知编译器修改了Stack_ptr和内存 asm volatile ("str sp, [%0]" : "=r" (Stack_ptr) : : "memory"); // 正确计算栈底地址 Stack_bot = (uint32_t)&_estack - (uint32_t)_Min_Stack_Size; // 栈填充循环 n = 0; while ((uint32_t)Stack_ptr > Stack_bot) { Stack_ptr--; n++; *Stack_ptr = 0xAA55A55A; } // 初始化硬件和UART // ... // 打印相关值 // printf("Stack_ptr: 0x%08X\n", (uint32_t)Stack_ptr); // printf("Stack_bot: 0x%08X\n", Stack_bot); // printf("n: %u\n", n); while(1) { // 主循环 } }
额外说明
之前这段代码能正常工作,可能是因为当时的编译选项没有开启优化,或者编译器版本对汇编语句的分析更宽松。GCC 5.3的优化器对数据流的分析已经比较严格,所以必须明确告知编译器汇编语句的副作用。
内容的提问来源于stack exchange,提问作者Mr_M_from_G

