c - 为什么会出现未对齐的内存访问错误? [皮质 M4]

标签 c

我正在调试一段使用 Keil 编译器而不是 ARM-GCC 导致崩溃的代码。我不熟悉代码的历史,但是当我阅读更多内容时,我意识到代码中肯定至少存在严格的别名违规。然而,首先将我吸引到代码的错误是运行此代码段时未对齐的访问错误。

我很尴尬地承认我不是很清楚这种严格的别名违规的问题,并且想说服自己修复将解决问题,而不仅仅是掩盖导致未对齐错误的任何内容。

  • 有人能帮我理解为什么我看到未对齐的访问错误吗?这是否与严格别名违规有关,如果是,那是如何表现出来的?
  • 解决这个问题的最佳方法是什么?我已经将 sampleStruct 声明为 __packed 现在它似乎正在工作,但是对 sampleStruct 使用 union 会是更好的解决方案吗?或者更进一步,我需要复制结构的各个成员吗?

  • 我仍在学习更多关于严格混叠规则的知识,但如果能朝正确的方向推进,我将不胜感激。

    *注意:这是在 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  }
    

    代码片段: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) );
    }
    

    最佳答案

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

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

    您发布的代码(甚至不会编译)使用不接受未对齐访问的指令的可能性很小。

    您的代码的修改版本(只是为了使其编译)https://godbolt.org/z/X_77Bc

    那么你为什么要得到 HF-s。

    两个可能的答案。

  • “配置和控制寄存器”中的 UNALIGN_TRP 位已设置。如果设置了任何未对齐的访问将触发异常(exception)
  • 您未显示的代码使用 LDM、STM、LDRD 或 STRD 指令,如果访问未对齐,则这些指令始终为 HF
  • 关于c - 为什么会出现未对齐的内存访问错误? [皮质 M4],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59592724/

    相关文章:

    c - C语言中如何知道何时按下回车键

    c - 检测5秒内鼠标是否移动的函数

    c - 基本的 Makefile 和头文件依赖

    c - 是否有检测硬件位宽的标准方法?

    c - 如何将目录添加到 autoconf 的库搜索路径

    c - 存储 BMP 文件的像素值

    c - C中填充链表后的额外节点

    mysql - 检查值是否不在表中..help..please (Mysql + C)

    c - C 中的位移位返回错误结果

    c - Switch Case 的工作方式不同