为何printf打印浮点数时main函数需压入r4寄存器才可正常工作?
根本原因
这个现象和r4寄存器本身没有任何关系,本质是你违反了32位ARM平台的**AAPCS(ARM架构过程调用标准)**的强制要求:所有对外可见的函数在入口点,栈指针sp必须保持8字节对齐。
- 程序启动进入main函数时,系统给的初始栈是天然满足8字节对齐的。
- 你第一版代码只执行了
push {lr},单寄存器压栈占4字节,直接把栈搞成了4字节对齐,不符合调用约定。 - 为什么乱码恰好出现在浮点数打印?因为printf处理
double类型时,内部实现会用到要求8字节对齐的浮点访存指令(比如vldr/vstr),或是直接在栈上存储double值,4字节对齐的栈会导致浮点值读取错位,把相邻内存的垃圾数据拼成浮点数输出,就是你看到的那串超长乱码数字。 - 你修改后的版本压入了r4和lr两个寄存器,合计占8字节,压栈后栈偏移刚好是8的整数倍,误打误撞满足了8字节对齐要求,所以printf能正常运行。
你可以自己做验证:哪怕完全不操作r4寄存器,只要把栈调整到8字节对齐,程序一样能正常输出1.4,参考代码:
.global main main: push {lr} sub sp, sp, #4 @ 额外预留4字节栈空间,凑够8字节偏移 bl foo add sp, sp, #4 mov r0, #0 pop {pc}
GCC编译C代码生成的汇编不会出这个问题,是因为编译器默认严格遵守AAPCS约定,生成函数序言时会自动保证栈对齐,不会出现只压入1个4字节寄存器就发起子函数调用的写法。
内容的提问来源于stack exchange,提问作者Jonatan
相关产品推荐
相关产品推荐

