项目需要打印一串浮点型数字,于是刚好用sprintf函数格式化,然后出现了意想不到的问题,float型数字全是0.00.。而后面的数字也出现错误。网上查找原因,才发现没有遵循AAPCS栈使用规约,看了之后发现之前写程序真是在冒险。
规约规定,栈任何时候都得4字节对齐,在调用入口得8字节对齐。在这个约定里,栈的4字节对齐确实得任何时候都遵守,而且你想不遵守都难,因为SP的最后两位是硬件上保持0的。而对于8字节对齐,这就需要码农和编译器配合着来。需要说明的一点是,8字节对齐即使不遵守,一些情况下也没问题,只要主调和被调用例程两边把堆栈使用,传参,返回等处理好就行,也就是说两边有自己的一套约定就行。但是有时候,主调这边在调用严格遵守AAPCS的函数时,没有将栈保持在8字节对齐上,那就会出问题。
贴两张图可以看到字节对齐后的效果,图片截取自.map文件
__align(8) char tcp_server_tramsvbuf[TCP_SERVER_TX_BUFSIZE];//8字节对齐
__align(4) char tcp_server_tramsvbuf[TCP_SERVER_TX_BUFSIZE]; //4字节对齐
char tcp_server_tramsvbuf[TCP_SERVER_TX_BUFSIZE];
对于一个char型的数组,栈顶在任意位置都可以,但是当它涉及到sprintf输出一个浮点数时,就可能出错。AAPCS规则要求堆栈保持8字节对齐。如果不对齐,调用一般的函数也是没问题的。但是当调用需要严格遵守AAPCS规则的函数时可能会出错。
例如调用sprintf输出一个浮点数时,栈必须是8字节对齐的,否则结果可能会出错。
什么是栈对齐?栈的字节对齐,实际是指栈顶指针须是某字节的整数倍。
字节对齐跟数据在内存中的位置有关,如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。比如在32位cpu下,假设一个整型变量的地址为0x00000004,那它就是自然对齐的。
编程时,对于AAPCS栈使用约定的遵守,总的来说就两条:
1. 汇编文件中需要我们亲自动手来保证遵守AAPCS栈使用约定。
(特别注意每次从汇编进入C的世界时,要保证汇编部分的编码在调用c接口时栈是8字节对齐的,不要疏忽了,因为c编译器可不负责调整。c编译器说你得送给我的SP就是8字节对齐的,我才能保证接下来的C部分没有结束之前,遵守AAPCS栈使用约定)
2. 在C文件中,由编译器来处理。
CORTEX-M3 中断控制器的栈对齐调整功能(该功能在r2p0版本以后的内核中均默认开启,STKALIGN位默认为1)
Cortex M3 NVIC CCR寄存器(控制与配置寄存器)的STKALIGN位置1,那么在发生中断时,进入中断响应函数前,内核会首先检查当前正在使用的栈指针是否8字节对齐,如果是,则正常将xPSR,PC,LR,SP,R0-R3入栈,如果不是,则先把SP-4,调整为8字节对齐,然后将xPSR第九位置1,接着把xPSR,PC,LR,SP,R0-R3入栈,再然后才进入中断响应函数。这样可以保证程序在运行过程中,如果在栈没有发生4字节对齐的地方发生中断了,进入到中断响应函数的时候也是遵守AAPCS栈使用约定的。如果中断服务程序是做任务切换的,那么前面的情况就是将任务栈调整为对齐,然后进入异常服务程序后使用系统栈,那如果系统栈本来就是不对齐的呢?通过中断来做任务切换的情况下,中断控制器并不会对系统栈进行调整,怎么办?其实这也不用担心,以μC/OS-II为例,在cortex-m3上通常使用PendSV异常来做任务切换,即将OSCtxSw以及OSIntCtxSw都设为仅完成PendSV异常触发功能,然后在PendSV异常服务程序中进行任务切换。由于上电时刻系统处于特权级模式,只要我们保证从上电开始到第一次系统调用,使用的栈都是系统栈MSP就可以了,这样即使第一次要进入任务切换时MSP不对齐,中断向量控制器也会给调整为8字节对齐状态,虽然这个第一次任务切换后除了中断再也不会使用MSP,但只要我们同时保证所有汇编部分都不会破坏8字节对齐规约,那么从此以后MSP都会是8字节对齐的。
__align(num) 和__packed
__align(num)这个用于修改最高级别对象的字节边界。在汇编中使用LDRD或者STRD时 就要用到此命令__align(8)进行修饰限制,来保证数据对象是相应对齐。这个修饰对象的命令最大是8个字节限制,可以让2字节的对象进行4字节 对齐,但是不能让4字节的对象2字节对齐。 __align是存储类修改,他只修饰最高级类型对象,不能用于结构或者函数对象。
__packed是进行一字节对齐,用于C语言结构的压缩,没有填充和对齐。
1.不能对packed的对象进行对齐
2.所有对象的读写访问都进行非对齐访问
3.float及包含float的结构联合及未用__packed的对象将不能字节对齐
4.__packed对局部整形变量无影响
5.强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定
义为packed。
在 Cotex-M3 programming manual 中有提到对齐问题
2.编译器自动分配出来结构体的内存(比如定义为全局变量或局部变量)肯定是对齐的。
参考:http://www.cnblogs.com/King-Gentleman/p/5940480.html
http://www.cnblogs.com/reload/p/3159053.html