查看: 1228|回复: 35
收起左侧

[STM32F1] 关于内存对齐的疑问

[复制链接]
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 09:58 | 显示全部楼层 |返回版面|阅读模式
本帖最后由 yearnext 于 2017-4-12 10:06 编辑

本人最近在调试程序的时候发现一个因为内存对齐的问题导致单片机进入HardFault工作平台:stm32f103vb
代码如下:
#include <stdint.h>
#include <stdlib.h>

struct test_t
{
    void (*func)(void *);
    void *param;
};

static uint8_t buffer[10];
static uint8_t i;

void *p1, *p2;

void inc(void *param)
{
    uint8_t *num = param;

    if(param == NULL)
    {
        return;
    }

    (*num)++;
}

int main(void)
{
    struct test_t *test = (struct test_t *)&buffer[1];

    p1 = (void *)&test->func;
    p2 = (void *)&test->param;

    test->func  = inc;
    test->param = (void *)&i;

    if( test->func != NULL && test->param != NULL )
    {
        test->func(test->param);
    }

    while(1)
    {
    }
}

test指向的地址为奇数,当程序执行 test->func(test->param); 就会进入HardFault



21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 10:01 | 显示全部楼层 |返回版面
可以判断test->func这个指针的地址时奇数,test->param的地址也为奇数
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 10:03 | 显示全部楼层 |返回版面
如果把test精简一下,改成

  1. struct test_t
  2. {
  3.     void (*func)(void);
  4. };

  5. void inc(void)
  6. {
  7.     uint32_t num;

  8.     num++;
  9. }
复制代码


在执行test->func(test->param); 的时候不会进入HardFault
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 10:09 | 显示全部楼层 |返回版面
可以从下图看到函数inc的地址是奇数,变量i的地址是偶数

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 10:14 | 显示全部楼层 |返回版面
如果我将

  1.     if( test->func != NULL && test->param != NULL )
  2.     {
  3.         test->func(test->param);
  4.     }
复制代码


改成

  1.     if( test->func != NULL && test->param != NULL )
  2.     {
  3.         test->func((void *)&i);
  4.     }
复制代码

最终也不会进入HardFault
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 10:20 | 显示全部楼层 |返回版面
也就是说在这个程序中导致单片机进入HardFault的原因在于test->func的参数地址是否为奇数
但是后来我在验证的时候发现上面的结论是不正确的

   
  1. static uint8_t buffer2[10];

  2.     if( test->func != NULL && test->param != NULL )
  3.     {
  4.         test->func((void *)&buffer2[1]);
  5.     }
复制代码


可以从中看出test->func的参数地址是否为奇数无关

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 10:24 | 显示全部楼层 |返回版面
本帖最后由 yearnext 于 2017-4-12 10:35 编辑

那么问题很有可能是在压栈的时候发生的,追踪汇编代码,发现在执行
  1. LDRD r1,r0,[r4,#0]
复制代码
时发生了错误最终进入到HardFault

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 10:57 | 显示全部楼层 |返回版面
本帖最后由 yearnext 于 2017-4-16 09:40 编辑

于是需要对ldrd指令做一些了解,入下图所示:
  1. LDRD r1,r0,[r4,#0]
复制代码

可以等效为
此处等效不正确
r1 = r4;
r0 = r5;

由linqing171指出,应等效为:
r1 = r4寄存器存储的地址
r0 = r4寄存器存储的地址 + 4 byte





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0

2

主题

159

帖子

477

积分

资深技术员

发表于 2017-4-12 11:02 | 显示全部楼层 |返回版面
楼主把 struct test_t *test = (struct test_t *)&buffer[1]改成 struct test_t *test = NULL;试试可否
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0

2

主题

159

帖子

477

积分

资深技术员

发表于 2017-4-12 11:03 | 显示全部楼层 |返回版面
我刚才分析了半天,也弄不清楚,只是感觉 struct test_t *test = (struct test_t *)&buffer[1] 这个赋值非常奇怪。
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 11:16 | 显示全部楼层 |返回版面
通过查看cortex m3的内存地址可以看出触发hardfault的事件可能与BusFault、MemManage或UsageFault有关

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 11:18 | 显示全部楼层 |返回版面
本帖最后由 yearnext 于 2017-4-12 11:19 编辑
feelhyq 发表于 2017-4-12 11:03
我刚才分析了半天,也弄不清楚,只是感觉 struct test_t *test = (struct test_t *)&buffer[1] 这个赋值非 ...
  1. struct test_t *test = (struct test_t *)&buffer[1]
复制代码
这个只是为了让test指向的内存地址为奇数,模拟当时的调试环境
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 11:29 | 显示全部楼层 |返回版面
从下图中可以看到UsageFault状态寄存器的第九位置位了,看手册上说除法运算时除数为0且在DIV_0_TRP置位时才有效
但是从汇编代码来看并没有看到使用了除法指令

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 11:35 | 显示全部楼层 |返回版面
通过观察发现DIV_0_TRP并未置位,那么DIVBYZERO置位的原因又是什么?
通过内存的查看只有DIVBYZERO置位才触发了hardfalut

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 12:36 | 显示全部楼层 |返回版面
yearnext 发表于 2017-4-12 10:57
于是需要对ldrd指令做一些了解,入下图所示:

可以等效为

参考

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 12:42 | 显示全部楼层 |返回版面
有看到介绍是这样的

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 12:43 | 显示全部楼层 |返回版面
yearnext 发表于 2017-4-12 12:42
有看到介绍是这样的

不知道是否能解释产生此现象的原因
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 12:47 | 显示全部楼层 |返回版面
yearnext 发表于 2017-4-12 12:43
不知道是否能解释产生此现象的原因

test->param的内存地址没有8字节对齐
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

3

主题

58

帖子

184

积分

中级技术员

 楼主| 发表于 2017-4-12 12:54 | 显示全部楼层 |返回版面
yearnext 发表于 2017-4-12 12:43
不知道是否能解释产生此现象的原因

我觉得这样test->func((void *)&i);没出错和编译器编译的结果有关,可以说是运气好吧
汇编代码如下图所示


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里 http://open.21ic.com 移步更多21ic独家微课:http://open.21ic.com/list/3/0/0
     

1071

主题

1万

帖子

4万

积分

版主

发表于 2017-4-12 13:07 | 显示全部楼层 |返回版面
lz 很有意思.研究的挺深刻....
但是解决办法是写规范的代码让编译器理解,别给你编译错误.
如果编译器不确定你是不是奇数,IAR就会用复杂的方式去避免这种错误.所以有些代码 keil 出错但 iar 不出错.
编译器是有犯傻的时候,iar 也一样.
您需要登录后才可以回帖 登录 | 注册

本版积分规则

分享 快速回复 返回顶部 返回列表