为什么我会出现未对齐内存访问错误?[Cortex M4]

3
我正在调试一段代码,使用Keil的编译器会导致一些崩溃,但是使用ARM-GCC却不会。我对代码的历史不太熟悉,但是在阅读更多内容后,我意识到代码中肯定存在至少一个严格别名违规问题。然而,最初吸引我注意这段代码的错误是运行此片段时出现了非对齐访问故障。
我很尴尬地承认,我对这种严格别名违规问题的问题意识不太清楚,并且想让自己相信解决这个问题将解决该问题,而不仅仅掩盖导致非对齐故障的原因。
1. 有人能帮我理解为什么我看到非对齐访问故障吗?这是否与严格别名违规有关,如果是,它如何表现为这样?
2. 解决这个问题的最佳方法是什么?我现在已经将sampleStruct声明为__packed,这似乎可以解决问题,但是使用union来处理sampleStruct是否是更好的解决方案?或者进一步说,我需要复制结构体的单个成员吗?
我仍在学习有关严格别名规则的知识,但是希望能得到正确的指导。
*注意:这在Cortex-M4上的嵌入式系统中运行,永远不会在其他平台或硬件上运行。
反汇编:
    0x00031DF0 9802      LDR           r0,[sp,#0x08]
        64:             structA->SessionCount = params.SessionCount; 
    0x00031DF2 60A0      STR           r0,[r4,#0x08]
        65:             structA->SessionValueAverage = params.ValueNum != 0 ? params.ValueTotal / params.ValueNum : 0.0f; 
    0x00031DF4 9806      LDR           r0,[sp,#0x18]
        65:             structA->SessionValueAverage = params.ValueNum != 0 ? params.ValueTotal / params.ValueNum : 0.0f; 
    0x00031DF6 B140      CBZ           r0,0x00031E0A
        65:             structA->SessionValueAverage = params.ValueNum != 0 ? params.ValueTotal / params.ValueNum : 0.0f; 
    0x00031DF8 EDDD0A05  VLDR          s1,[sp,#0x14]
    0x00031DFC ED9D0A06  VLDR          s0,[sp,#0x18]
    0x00031E00 EEB81A40  VCVT.F32.U32  s2,s0
    0x00031E04 EE800A81  VDIV.F32      s0,s1,s2
    0x00031E08 E001      B             0x00031E0E
        65:             structA->SessionValueAverage = params.ValueNum != 0 ? params.ValueTotal / params.ValueNum : 0.0f; 
    0x00031E0A ED9F0A1E  VLDR          s0,[pc,#0x78]
>   0x00031E0E ED840A03  VSTR          s0,[r4,#0x0C]
        66:             structA->Value = params.ValueLast; 
    0x00031E12 ED9D0A04  VLDR          s0,[sp,#0x10]
    0x00031E16 ED840A04  VSTR          s0,[r4,#0x10]
        67:             structA->SessionValueLow = params.ValueLow; 
    0x00031E1A ED9D0A07  VLDR          s0,[sp,#0x1C]
    0x00031E1E ED840A05  VSTR          s0,[r4,#0x14]
        68:             structA->SessionValueHigh = params.ValueHigh; 
    0x00031E22 ED9D0A08  VLDR          s0,[sp,#0x20]



58  case SESSION_INFO_HDL: {
59      AppParams_t params;
60      AppParamsRead(&params);
61      sampleStruct_t *structA      = (sampleStruct_t *) &pData->pValue[offset];
62      structA->TotalCount          = params.TotalCount;
63      structA->SessionId           = params.SessionId;
64      structA->SessionCount        = params.SessionCount;
65      structA->SessionValueAverage = params.ValueNum != 0 ? params.ValueTotal / params.ValueNum : 0.0f;
66      structA->Value               = params.ValueLast;
67      structA->SessionValueLow     = params.ValueLow;
68      structA->SessionValueHigh    = params.ValueHigh;
69      structA->Reserved = 0;
70      AttsSetAttr(SESSION_INFO_HDL, sizeof(*structA), &pData->pValue[offset]);
71      break;
72  }

Code Snippet: https://godbolt.org/z/Djebj2

typedef struct
{
    uint8_t           *pValue;          /*!< \brief Pointer to the data's value */
    uint16_t          *pLen;            /*!< \brief Pointer to the length of the data's value */
} data_t;

typedef struct sampleStruct {
    uint32_t TotalCount;
    uint32_t SessionId;
    uint32_t SessionCount;
    float    SessionValueAverage;
    float    Value;
    float    SessionValueLow;
    float    SessionValueHigh;
    uint32_t Reserved;
} sampleStruct_t;

typedef struct AppParams {
    uint32_t TotalCount;
    uint32_t SessionId;
    uint32_t SessionCount;
    uint32_t CalibrationThreshold;
    float    ValueLast;
    float    ValueTotal;
    uint32_t ValueNum;
    float    ValueLow;
    float    ValueHigh;
} AppParams_t;


void function ( uint16_t offset, data_t * pData )
{
    AppParams_t params;

    sampleStruct_t *structA      = (sampleStruct_t *) &pData->pValue[offset];
    structA->TotalCount          = params.TotalCount;
    structA->SessionId           = params.SessionId;
    structA->SessionCount        = params.SessionCount;
    structA->SessionValueAverage = params.ValueNum != 0 ? params.ValueTotal / params.ValueNum : 0.0f;
    structA->Value               = params.ValueLast;
    structA->SessionValueLow     = params.ValueLow;
    structA->SessionValueHigh    = params.ValueHigh;
    structA->Reserved = 0;
    AttsSetAttr(SESSION_INFO_HDL, sizeof(*structA), &pData->pValue[offset]);

    send_data ( SESSION_INFO_HDL, &pData->pValue[offset], sizeof(*structA) );
}

1
首先,你应该始终展示实际的代码。这段代码甚至无法编译通过。 - undefined
2个回答

2

您的问题出现在这一行:

sampleStruct_t * structA = (sampleStruct_t *) &pData->pValue[offset];

pData->pValue 是指向 uint8_t 的指针——也就是说,它可以指向内存中的任何一个字节。由此得到,&pData->pValue[offset] 也是指向内存中任何(未对齐的)字节的指针。强制类型转换和赋值操作使得 structA 成为指向任何未对齐字节的指针。之后你会执行这个操作:

structA->intA   = get_int_A();

这段文字涉及到IT技术相关内容,讲述了如何解决在structA->intA中进行uint32_t的非对齐存储问题。

如果您想要解决这个问题,需要确保指针的对齐方式符合特定的对齐要求。

通过声明结构体为紧凑型,可以避免结构体隐式对齐的问题,从而解决此问题。


谢谢,这样对齐问题更加清晰了。我说得对吗,这里也存在严格别名违规吗?根据我对规则的理解,两种不同的数据类型不应该共享同一块内存。在我的情况下,sampleStruct_t * 和 uint8_t * 共享同一个缓冲区。 - undefined
是的 - 没错。字节缓冲区可以与您的结构体进行别名操作,但反之则不行。https://dev59.com/43VD5IYBdhLWcg3wE3Ro - undefined
@CarlNorum 这个答案相对而言比较泛泛而谈,与CORTEX M4无关。 - undefined
@CarlNorum Cortex M4支持非对齐访问。只有一些(编译器较少使用的)仅支持对齐访问。 - undefined

1

Cortex M4支持以下指令的非对齐访问:

•LDR,LDRT•LDRH,LDRHT•LDRSH,LDRSHT•STR,STRT•STRH,STRHT

你发布的代码很可能不会使用不接受非对齐访问的指令(甚至无法编译)。

你已修正的代码版本(只是为了能够编译)https://godbolt.org/z/X_77Bc

那么为什么会出现HF-s呢?

有两个可能的答案。

  1. “配置和控制寄存器”中的UNALIGN_TRP位被设置。如果设置了任何非对齐访问都将触发异常

  2. 你未显示的代码使用LDM、STM、LDRD或STRD指令,如果访问不对齐,则始终会出现HF


我已经更新了问题,并附上了更接近我实际运行且可以编译的代码。根据我的调试器,我已经检查了UNALIGNED_TRP位未设置,并且跳转到错误处理程序的指令是上面指示的VSTR。看起来这不应该发生。 - undefined
这是因为数学的原因。你的编译器使用FPU,而FPU指令需要对齐访问。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接