0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

在堆嵌入式单片机编程中为什么大多时候要保证堆栈8字节对齐呢?

STM32嵌入式开发 来源:CSDN 2023-08-28 10:48 次阅读

本文主要介绍了嵌入式单片机编程中,为什么大多时候要保证堆栈8字节对齐的问题。

字节对齐原则

1. 结构(struct)(或联合(union)) 中的第一个数据成员放在 offset 为 0 的地方,以后每个数据成员存储的起始位置,要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如 int 型变量在 32 位编译环境下为 4 字节,则要从 4 的整数倍地址开始存储);

2. 如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.

如:struct a 里存有 struct b, b 里有 char, int , double 等元素,那 b 应该从 8 的整数倍开始存储.;

3. 结构体的总大小,也就是 sizeof 的结果,必须是其内部最大成员的整数倍,不足的要补齐;

dd396a9e-433e-11ee-a2ef-92fbcf53809c.png

总大小为最大成员变量大小的整数倍,sizeof(AA) = 24;sizeof(BB) = 32。

#pragma pack():

在代码前加一句 #pragma pack(1),会发现 sizeof(AA) = 17;sizeof(BB) = 24;

AA 是 8+4+1+4=17;

BB 是 5+4+8+4+2+1=24;

这就是理想中的没有内存对齐的情况,所以 #pragma pack(1) 是告诉编译器,所有的对齐都按照1的整数倍对齐,换句话说就是没有对齐规则。

即#pragma pack(n)就是所有的对齐都按照n的整数倍对齐。

Vc,Vs等编译器默认是 #pragma pack(8),所以测试我们的规则会正常;

gcc 默认是 #pragma pack(4),并且 gcc 只支持 1, 2, 4 对齐。套用三原则里计算的对齐值是不能大于 #pragma pack 指定的n值。

为什么要保证堆栈8字节对齐

AAPCS 规则要求堆栈保持 8 字节对齐。如果不对齐,调用一般的函数也是没问题的。但是当调用需要严格遵守 AAPCS 规则的函数时可能会出错。

例如调用 sprintf 输出一个浮点数时,栈必须是 8 字节对齐的,否则结果可能会出错。

实验验证:

dd545f52-433e-11ee-a2ef-92fbcf53809c.png


1.在 A 处设置断点,让程序全速运行至 A

2.在 MDK 中修改 MSP 的值使 MSP 满足 8 字节对齐

3.全速运行程序,观察 buf 中的字符为 1.234 结果正确

4.回到第 2 步,修改 MSP 使之只满足 4 字节对齐而不满足 8 字节对齐

5.全速运行程序,观察 buf 中的字符为 -2.000 结果错误

该实验证明了调用 sprintf 输出一个浮点数必须要保证栈 8 字节对齐。

编译器为我们做了什么

先看一个实验:

dd672d62-433e-11ee-a2ef-92fbcf53809c.png


保证初始的时候堆栈是 8 字节对齐的;

1.在 A 处设置断点;

2.全速运行至 A,观察 MSP=0x2000025c,没有 8 字节对齐;

3.略微修改一下 main 函数代码如下,其他部分代码不变;

dd810d90-433e-11ee-a2ef-92fbcf53809c.png


4.同样在 A 处设置断点;

5.全速运行至 A,观察 MSP=0x200002d8,这次 8 字节对齐了; 这个实验说明了如果编译器发现了某个函数需要调用浮点库时会自动调整编译生成的汇编代码,从而保证调用这些浮点库函数时堆栈是8字节对齐的。

换句话说如果我们保证了栈初始的时候是8字节对齐的,那么编译器可以保证以后调用浮点库时堆栈仍是8字节对齐的。

os下应该怎样设置任务堆栈

由上面的讨论可知给任务分配栈时需要保证栈是 8 字节对齐的,不然在该任务中凡是调用 sprintf 的函数均会出错,因为栈一开始就是不对齐的。

是否保证了栈初始是8字节对齐了就万事大吉了呢。no!大家请看一种特殊的情况:

dd92f834-433e-11ee-a2ef-92fbcf53809c.png

mian函数的反汇编如下:

ddad50d0-433e-11ee-a2ef-92fbcf53809c.png



保证初始的时候堆栈是 8 字节对齐的;

1.在 A 处设置断点;

2.全速运行至 A,观察此时 MSP=0x200002e4 未对齐;

3.在 MDK 中将 SVC 的挂起位置 1;

4.在 B 处设置断点;

5.全速运行至 B,观察此时 MSP=0x200002b4 未对齐;

6.继续全速执行,观察 buf 中的字符为: -2.000 出错了;

这个实验说明了即使保证栈初始是 8 字节对齐的,编译器也只能保证在调用 sprintf 那个时刻栈是 8 字节对齐的,但不能保证任意时刻栈都是 8 字节对齐的,如果恰巧在 MSP 没有 8 字节对齐的时刻发生了中断,而中断中又调用了 sprintf,这种情况下仍会出错。

Cortex-M3 内核为我们做了什么

Cortex-M3 内核提供了一种硬件机制来解决上述这种中断中栈不对齐问题。

CM3 中可以把 NVIC 配置控制寄存器的 STKALIGN 置位,来保证中断中的栈 8 字节对齐。

具体实现过程如下:当发生中断时由硬件自动检测 MSP 是否 8 字节对齐,如果对齐了,则不进行任何操作,如果没有对齐,则自动将 MSP 减 4 这样便对齐了,同时将 xPSR 的第 9 位置位来记录这个 MSP 的非正常的变化,在中断返回若发现 xPSR 的第 9 位是置位的则自动将 MSP 加 4 调整回原来的值。

实验验证:

ddc6380c-433e-11ee-a2ef-92fbcf53809c.png



mian函数的反汇编如下:

ddda4aea-433e-11ee-a2ef-92fbcf53809c.png



1.在 A 处设置断点;

2.全速运行至 A,观察此时 MSP=0x200002e4 未对齐;

3.在 MDK 中将 SVC 的挂起位置 1,同时将 0xE000ED14 处的值由 0x00000000 改为 0x00000200(即将 NVIC 配置控制寄存器的 STKALIGN 置位)

4.在 B 处设置断点;

5.全速运行至 B,观察此时 MSP=0x200002b0 对齐了;

6.观察中断返回时的 MSP=0x200002e4 调整回来了;

7.继续全速执行,观察 buf 中的字符为:1.234 正确;

这个实验说明了将NVIC配置控制寄存器的STKALIGN置位可以保护中断时栈仍是8字节对齐

总结

综上所述,为了能够安全的使用严格遵守AAPCS规则的函数(比如sprintf)需要做到以下几点:

保证MSP在初始的时候是8字节对齐的

如果用到OS的话,需要保证给每个任务分配的栈是保持8字节对齐的

如果用的是基于CM3内核的处理器,需将NVIC配置控制寄存器的STKALIGN置位。






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 处理器
    +关注

    关注

    68

    文章

    19243

    浏览量

    229594
  • 寄存器
    +关注

    关注

    31

    文章

    5334

    浏览量

    120208
  • 存储器
    +关注

    关注

    38

    文章

    7481

    浏览量

    163751
  • SVC控制器
    +关注

    关注

    0

    文章

    2

    浏览量

    5225
  • 嵌入式单片机

    关注

    0

    文章

    10

    浏览量

    2261

原文标题:对堆栈 8 字节对齐问题的讨论

文章出处:【微信号:c-stm32,微信公众号:STM32嵌入式开发】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    AAPCS规则要求堆栈保持8字节对齐(转)

    我们保证了栈初始的时候8字节对齐的,那么编译器可以保证以后调用浮点库时
    发表于 01-19 11:43

    请问F28335字节对齐能不能改为1字节

    大家好,C2000成员TMS320F28335CCS3.3下是2字节对齐的,能不能改为1字节对齐?怎么改
    发表于 08-20 06:41

    align为什么8字节对齐

    我知道数据储存的起始地址%对齐字节(N)=0才行,但是我不明白有两点问题1:UCOSIII的系统的浮点数打印任务的堆栈大小
    发表于 04-23 00:21

    请问嵌入式单片机之间的关系是什么?

    时间回到十五年前,大部分人搞嵌入式,其实是做单片机,那时单片机资源少(我曾用过128字节RAM的MCU,仔细扣每一
    发表于 06-24 14:35

    详解STM32单片机堆栈

    学习STM32单片机时候,总是能遇到“堆栈”这个概念。分享本文,希望对你理解堆栈有帮助。 对于了解一点汇编编程的人,就可以知道,
    发表于 01-12 11:30

    【原创】嵌入式系统中大小端和对齐问题

    作者:黄忠老师(张飞实战电子高级工程师)C语言是一种高级语言,大多数情况下C语言的代码是和具体的处理器体系结构无关的。然而,嵌入式系统的编程
    发表于 07-30 09:34

    8单片机全速USB集成128 k字节Flash ROM和8192字节的RAM

    LC87F17C8A是一个8单片机和USB全速主机/设备控制器。128 k字节闪速存储器/ 8192字节的RAM / 48-pin。
    发表于 04-06 09:15 3次下载
    <b class='flag-5'>8</b>位<b class='flag-5'>单片机</b>全速USB集成128 k<b class='flag-5'>字节</b>Flash ROM和8192<b class='flag-5'>字节</b>的RAM

    什么是嵌入式单片机?嵌入式单片机详情汇总

    嵌入式单片机,即嵌入式微控制器,指以微控制器为核心控制单元的嵌入到对象体系的专用计算机系统,是应用十分广泛的一种
    发表于 11-13 09:39 6084次阅读

    什么是嵌入式编程

    什么是嵌入式编程?对于嵌入式系统,许多人很容易将它与单片机编程混淆。其实,嵌入式
    发表于 06-29 11:05 1.1w次阅读
    什么是<b class='flag-5'>嵌入式</b><b class='flag-5'>编程</b>

    单片机or嵌入式linux

    最近很多童鞋投票并咨询如何从单片机转做嵌入式Linux开发。看来读者圈单片机,RTOS的不少。尽管我目前从事Linux/Android方面的嵌入
    发表于 11-01 16:26 17次下载
    <b class='flag-5'>单片机</b>or<b class='flag-5'>嵌入式</b>linux

    单片机到底是不是嵌入式

    01 问题很多同学一直纠结:我是学单片机还是学嵌入式?还有人说单片机也是
    发表于 11-04 11:21 14次下载
    <b class='flag-5'>单片机</b>到底是不是<b class='flag-5'>嵌入式</b>?

    是否要从单片机转为嵌入式Linux

    最近很多童鞋投票并咨询如何从单片机转为嵌入式Linux开发。看来读者圈单片机,RTOS的不少。尽管小编目前从事Linux/Android方面的
    发表于 11-13 20:51 11次下载
    是否要从<b class='flag-5'>单片机</b>转为<b class='flag-5'>嵌入式</b>Linux

    基于51单片机设计N字节十六进制除法——以6字节除以3字节为例

    基于51单片机设计N字节十六进制除法——以6字节除以3字节为例1、思路借用十进制除法竖计算的思路,作十六进制的除法竖
    发表于 11-23 16:51 7次下载
    基于51<b class='flag-5'>单片机</b>设计N<b class='flag-5'>字节</b>十六进制除法——以6<b class='flag-5'>字节</b>除以3<b class='flag-5'>字节</b>为例

    STM32 终极字节对齐解析

    一、全局变量对齐问题:基本上用户定义的变量是几个字节就是几字节对齐,这个比较好理解。uint8_t定义变量地址
    发表于 11-23 18:06 11次下载
    STM32 终极<b class='flag-5'>字节</b><b class='flag-5'>对齐</b>解析

    【M3内核篇】关于“堆栈指针的最低两位永远是0,这意味着堆栈总是4字节对齐的”的理解

    堆栈指针的最低两位永远是0,这意味着堆栈总是4字节对齐的”理解在看到《Cortex-M3权威指南》堆栈指针的这句话刚开始一直摸不着头脑,通
    发表于 12-01 14:36 11次下载
    【M3内核篇】关于“<b class='flag-5'>堆栈</b>指针的最低两位永远是0,这意味着<b class='flag-5'>堆栈</b>总是4<b class='flag-5'>字节</b><b class='flag-5'>对齐</b>的”的理解