STM32对memcpy的调用导致hardfault(对memcpy本身的调用,而不是memcpy的执行)
Posted
技术标签:
【中文标题】STM32对memcpy的调用导致hardfault(对memcpy本身的调用,而不是memcpy的执行)【英文标题】:STM32 call to memcpy causes hardfault (the call to memcpy itself, not the execution of memcpy) 【发布时间】:2021-08-30 05:29:06 【问题描述】:情况:我正在使用一个名为嵌入式迪斯科的加密库,我有一个在我的 PC 上运行的演示,但是在将它移植到 MCU 时,我在执行库过程时遇到了一个硬故障。在错误代码中,库试图简单地将一个strobe_s
结构的内容复制到另一个strobe_s
。这样做了两次:一次用于s1
,一次用于s2
。对于s1
,库只分配dest。结构到源结构。然而,对于s2
,这样的分配给出了一个硬错误。由于 Cortex-M ISA 需要对齐的内存访问,我认为用 memcpy 替换分配应该可以解决问题。然而,简单地使用调试器进入 memcpy 会导致硬故障! IE。我在 memcpy 的行上有一个断点,当进入故障处理程序时会被调用!我已经使用 memcpy 修复了代码其他部分中未对齐的内存访问......
MCU: STM32L552ZET6QU
错误代码:
下面的代码是我对原始库代码的修改,其中对*s2
的赋值被替换为memcpy。 library's github 的原始代码是:
// s1 = our current strobe state
*s1 = ss->strobe;
if (!half_duplex)
// s2 = s1
*s2 = ss->strobe;
我的修改版:
// s1 = our current strobe state
*s1 = ss->strobe;
if (!half_duplex)
// s2 = s1
// WARNING: The below code will give a HARD FAULT ON THE STM32L552ZE!
// *s2 = ss->strobe;
// Fix I tried: Use memcpy instead!
memcpy((void*) s2, (void*)(&(ss -> strobe)), sizeof(strobe_s));
memcpy的参数值:
就在执行 memcpy 之前,调试器向我显示了相关变量的以下值:
Expr. Type Value
----------------------------------------------------
s1 strobe_s * 0x800c374
s2 strobe_s * 0x800a497 <_fflush_r+66>
ss symmetricState * 0x2002f988
&s1 strobe_s ** 0x2002f690
&s2 strobe_s ** 0x2002f68c
&ss symmetricState ** 0x2002f694
类型定义:
typedef struct symmetricState_
strobe_s strobe;
bool isKeyed;
symmetricState;
/** Keccak's domain: 25 words of size b/25, or b/8 bytes. */
typedef union
kword_t w[25];
uint8_t b[25 * sizeof(kword_t) / sizeof(uint8_t)];
kdomain_s;
/** The main strobe state object. */
typedef struct strobe_s_
kdomain_s state;
uint8_t position;
uint8_t pos_begin;
uint8_t flags;
uint8_t initiator;
uint8_t initialized; // strobe is initialized if this value is set to 111.
// This is because we cannot assume that a boolean would
// be set to false initially (C stuff). A uint8_t is a
// short value but here we do not care about security
// much, rather catching bugs early in a development
// environement.
strobe_s;
问题:
-
怎么可能只是 调用 到 memcpy 而没有在 memcpy 中实际执行一条指令会产生硬故障?
我该如何解决这个问题?
【问题讨论】:
删除void*
强制转换,它们是不必要的,可能会抑制有用的警告。
查看导致硬故障的原因。您需要从堆栈中读取寄存器和值。或者下载 atollic studio 或 CubeMxIDE,因为它们有一个非常方便的插件,称为 nomen omen faultAnalyzer
github 上的代码前面有一个断言s1
和s2
都不是NULL
- 令人费解的是没有检查ss
不是NULL
.我还建议使用sizeof(*s2)
而不是sizeof(strobe_s)
- 这只是一个更安全的习惯。
@Clifford 我移除了铸件,没有出现进一步的警告。 github 上的代码确实有断言,我的代码中也有它们。我只是没有将它们带到问题中以保持代码块小。断言得到满足,ss
为非 NULL,ss == 0x2000f988
。
@iMrFelix :我没想到会修复它 - 否则我会发布答案。只是观察和良好实践。请注意,除非您明确启用选择性异常处理,否则任何异常都会转换为硬故障。在汇编指令级别单步进入函数调用可能会有所帮助。可能是在调用之前对参数的评估失败。
【参考方案1】:
这里:
s2 strobe_s * 0x800a497 <_fflush_r+66>
s2
是一个闪存(只读)地址。复制到只读内存在语义上都是错误的,如果该区域设置为只读,则可能会触发 MPU 故障。
我不清楚原始代码是如何工作的,或者实际上是如何工作的:
*s1 = ss->strobe;
但是也没有引起问题。即使没有异常,它也肯定不会按预期工作。
【讨论】:
我确实注意到,在更新问题时,事实证明在调用层次结构的更高层传递了一个错误的参数(一个 strobe_s 而不是一个 ptr. 到一个 strobe_s)。由于该错误发生在离故障发生的地方很远的地方,所以我错过了! @iMrFelix 看到这个电话会很有趣。除非你被一个不明智的演员挫败,否则编译器应该已经注意到了。这就是为什么我建议不要在这种情况下进行不必要的演员表 - 它告诉编译器“不要抱怨这个,我知道我在做什么” ...除非你不这样做.通过强制转换抑制编译器警告或错误应该是最后的手段,而不是习惯性 我可以在进一步的问题编辑中添加错误代码。我总是投的原因是,例如当使用带有 char* 而不是 uint8_t* 作为有效负载的 HAL_UART_Transmit(...) 时,我碰巧总是收到签名警告。 @iMrFelix 也许不相关,只是我的好奇心。你永远不需要强制转换为 void*;这就是 void* 的目的。 STM32 HAL 接口很烂,但习惯性地铸造一切并不是解决办法。只投射你需要投射的东西,否则更喜欢类型协议。否则,正如您发现的那样,您最终会遇到难以理解的运行时错误,您可能会遇到更直接的构建错误。如果您仅在编译器需要而不是习惯性的情况下进行转换,您可以将每个转换视为您应该的偏差,而不是全局隐藏错误。 disco 库的所有握手逻辑都包含在自定义函数server_disco_connect(c_write, c_read)
中,其中c_write
、c_read
应该是指向选通状态的指针。我错误地直接用结构调用disco_connect
。为什么编译器没有抱怨我不能说,但它肯定没有(我什至没有强制转换它们!)。如果您准备迎接挑战,我刚刚发布了一个新问题,非常感谢您的帮助! :)以上是关于STM32对memcpy的调用导致hardfault(对memcpy本身的调用,而不是memcpy的执行)的主要内容,如果未能解决你的问题,请参考以下文章
修复可能由于 STM32 Nucleo-F334R8 上的 malloc 导致的内存覆盖错误
如何在 Cortex-M3 (STM32) 上从 RAM 执行功能?