从阵列进行转换会导致某些MCU崩溃,而不会导致其他崩溃

时间:2018-08-02 13:11:38

标签: c stm32

我有一段代码看起来像这样:

void update_clock(uint8_t *time_array)
{
    time_t time = *((time_t *) &time_array[0]); // <-- hangs
    /* ... more code ... */
}

其中time_array是4个字节的数组(即uint8_t time_array[4])。

我正在使用arm-none-eabi-gcc将其编译为STM32L4处理器。

几个月前进行编译时,我没有出现任何错误,并且代码在我所有的测试MCU上都运行良好。回到该项目时,我对环境(OpenSTM32)进行了一些更新,现在这段代码在某些MCU上崩溃,而在其他MCU上可以正常工作。

几个月前我仍然有我的二进制文件,并确认此代码路径在我的所有MCU上都可以正常工作(我有大约5个要测试),但是现在它在其中两个上可以工作,同时导致崩溃在其中三个上。

我通过重写如下代码缓解了这个问题:

time_t time = (
        ((uint32_t) time_array[0]) << 0 |
        ((uint32_t) time_array[1]) << 8 |
        ((uint32_t) time_array[2]) << 16 |
        ((uint32_t) time_array[3]) << 24
);

虽然现在可以使用,但我认为旧代码看起来更干净,我也担心如果此代码路径挂起,我可能在其他地方也会遇到类似的错误。

有人知道这是什么原因吗?我可以更改设置中的任何内容以使编译器再次以旧方式工作吗?

3 个答案:

答案 0 :(得分:3)

从版本7-2017-q4-major开始,arm gcc附带了用time_t编译为64位(long long)整数的newlib编译器,导致假定它是32位。您的代码正在读取源数组末尾,将存储在此处的所有内容作为时间值的高阶位,可能导致大爆炸之前或之后的热死。宇宙,这可能不是您的代码所期望的。

如果已知源数组包含32位数据,则​​首先将其复制到32位int32_t变量中,然后可以将其分配给time_t,这样就可以正确地对其进行转换,无论time_t的大小如何。

答案 1 :(得分:1)

您的开发环境OpenSTM32可能正在使用gcc编译器。如果是这样,gcc支持以下宏标志。

-fno-strict-aliasing

如果您使用的是-O2,则此标志可能会解决您的问题。

使用memcpy是标准建议,有时会被编译器优化:

memcpy(&time, time_array, sizeof time);

最后,您可以使用gcc的{​​{1}}和带有联合的复合文字来生成以下安全类型转换:

typeof

例如,以下代码在https://godbolt.org/g/eZRXxW处编译:

#define PUN_CAST4(a, x)  ((union {uint8_t src[4]; typeof(x) dst;}){{a[0],a[1],a[2],a[3]}}).dst
time_t time = PUN_CAST4(time_array, time);

gcc 8.1适用于所有四个示例:它使用#include <stdint.h> #include <time.h> #include <string.h> time_t update_clock(uint8_t *time_array) { time_t t = *((time_t *) &time_array[0]); // assumes no alignment problem return t; } time_t update_clock2(uint8_t *time_array) { time_t t = (uint32_t)time_array[0] << 0 | (uint32_t)time_array[1] << 8 | (uint32_t)time_array[2] << 16 | (uint32_t)time_array[3] << 24; return t; } time_t update_clock3(uint8_t *time_array) { time_t t; memcpy(&t, time_array, sizeof t); return t; } #define PUN_CAST4(a, x) ((union {uint8_t src[4]; typeof(x) dst;}){{a[0],a[1],a[2],a[3]}}).dst time_t update_clock4(uint8_t *time_array) { time_t t = PUN_CAST4(time_array, t); return t; } 生成琐碎的代码。但是gcc 7.3对于第四版是不利的。 lang语对所有四个人都很有好处,-O2为32位目标,但如果没有it语,则在第二和第四失败

答案 2 :(得分:0)

您的问题是由于访问不对齐或写入错误的区域引起的。

编译

Year Month count_active_events
2018 1     1
2018 2     2
2018 3     2
2018 4     1

在GCC 7.2.1中使用参数#include "stdint.h" #include "time.h" time_t myTime; void update_clock(uint8_t *time_array) { myTime = *((time_t *) &time_array[0]); // <-- hangs /* ... more code ... */ } 会生成以下

-march=armv7-m -Os

由于时间数组是8位类型,因此没有对齐规则,因此,如果链接器没有将其进行字对齐,则在尝试将其取消引用为update_clock(unsigned char*): ldr r3, .L2 ldrd r0, [r0] strd r0, [r3] bx lr .L2: .word .LANCHOR0 myTime: 时,time_t *指令为给定一个非单词对齐的地址,并导致使用错误。

LDRDLDRD指令正在加载和存储8个字节,而您的数组只有4个字节长。建议您在环境中检查STRD,并在对齐的区域放置足够长的时间来存储它。