我有一段代码看起来像这样:
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
);
虽然现在可以使用,但我认为旧代码看起来更干净,我也担心如果此代码路径挂起,我可能在其他地方也会遇到类似的错误。
有人知道这是什么原因吗?我可以更改设置中的任何内容以使编译器再次以旧方式工作吗?
答案 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 *
指令为给定一个非单词对齐的地址,并导致使用错误。
LDRD
和LDRD
指令正在加载和存储8个字节,而您的数组只有4个字节长。建议您在环境中检查STRD
,并在对齐的区域放置足够长的时间来存储它。