Fork me on GitHub

结构体强制转换导致的内存对齐问题

在开发ethercat协议栈邮箱通讯的过程中遇到一个BUG,主站协议栈传过来的邮箱数据包是正确的,但是到FOE服务处理时,使用结构体引用的方式处理时发现数据是不对的。

如下所示

 1 UINT8 MailboxServiceInd(TMBX MBXMEM *pMbx)
 2 {
 3     UINT8 result;
 4 
 5     /*only FoE is allowed in Boot mode*/
 6     if(bBootMode == TRUE && (MBX_TYPE_FOE != ((pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] & MBX_MASK_TYPE) >> MBX_SHIFT_TYPE )))
 7         return MBXERR_UNSUPPORTEDPROTOCOL;
 8     switch ( (pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] & MBX_MASK_TYPE) >> MBX_SHIFT_TYPE )
 9     {
10     case MBX_TYPE_COE:
11         /* CoE datagram received */
12         result = COE_ServiceInd((TCOEMBX MBXMEM *) pMbx);
13         break;
14     case MBX_TYPE_FOE:
15         /* FoE datagram received */
16         result = FOE_ServiceInd((TFOEMBX MBXMEM *) pMbx);        
17 
18     default:
19 
20         result = MBXERR_UNSUPPORTEDPROTOCOL;
21         break;
22     }
23 
24     return result;
25 }

在第1行传入了一个结构体类型为TMBX 的*pMbx,在第16行将其强制转换成了 TFOEMBX类型的结构体。

两个结构体定义如下:

TMBX

1 typedef struct MBX_STRUCT_PACKED_START
2 {
3     TMBXHEADER                      MbxHeader; /**< \brief Mailbox header*/
4     UINT16                          Data[(MAX_MBX_DATA_SIZE >> 1)]; /**< \brief Mailbox data*/
5 }MBX_STRUCT_PACKED_END
6 TMBX;

1 typedef struct MBX_STRUCT_PACKED_START
2 {
3       TMBXHEADER        MbxHeader; /**< \brief Mailbox header*/    
4       TFOEHEADER        FoeHeader; /**< \brief FoE header*/
5             UINT16            Data[((MAX_MBX_DATA_SIZE)-(FOE_HEADER_SIZE)) >> 1]; /**< \brief FoE Data buffer*/
6 }MBX_STRUCT_PACKED_END
7 TFOEMBX;
 1 typedef struct  MBX_STRUCT_PACKED_START
 2 {
 3     UINT16        OpCode; /**< \brief OpCode
 4                              *
 5                              * 1 : RRQ<br>
 6                              * 2 : WRQ<br>
 7                              * 3 : DATA<br>
 8                              * 4 : ACK<br>
 9                              * 5 : ERR<br>
10                              * 6 : BUSY*/
11     union MBX_STRUCT_PACKED_START
12     {
13         UINT32        Password; /**< \brief Password (used in Read request and Write request). 0 if unknown*/
14         UINT32        PacketNo; /**< \brief Packet number (used in DATA and ACK datagram)*/
15         UINT32        ErrorCode; /**< \brief Error code (used in ERR datagram)*/
16         struct MBX_STRUCT_PACKED_START
17         {
18             UINT16    Done; /**< \brief Done indication (used in BUSY datagram)*/
19             UINT16    Entire;  /**< \brief Entire indication (used in BUSY datagram)*/
20         }MBX_STRUCT_PACKED_END
21         Busy; /**< \brief Busy variable*/
22     }MBX_STRUCT_PACKED_END
23     Cmd; /**< \brief Command field*/
24 }MBX_STRUCT_PACKED_END
25 TFOEHEADER;
 1 typedef struct MBX_STRUCT_PACKED_START
 2 {
 3 UINT16 Length; /**< \brief Length*/
 4 UINT16 Address; /**< \brief Address*/
 5 
 6 UINT16 Flags[1]; /**< \brief Flags*/
 7 #define MBX_OFFS_TYPE 0 /**< \brief Protocol type offset*/
 8 #define MBX_MASK_TYPE 0x0F00 /**< \brief Protocol type mask*/
 9 #define MBX_SHIFT_TYPE 8 /**< \brief Protocol type shift*/
10 #define MBX_OFFS_COUNTER 0 /**< \brief Protocol counter offset*/
11 #define MBX_MASK_COUNTER 0xF000 /**< \brief Protocol counter mask*/
12 #define MBX_SHIFT_COUNTER 12 /**< \brief Protocol counter shift*/
13 }MBX_STRUCT_PACKED_END
14 TMBXHEADER;

可以看到出问题的是在TFOEHEADER结构体中,联合体为4个字节,编译时系统会把该头文件按照4字节对齐,从而实际占用8个字节而不是6个字节,使用sizeof()验证也确实如此,当把函数传入的结构体*pMbx强制转换成TFOEMBX结构体就会出现问题。

解决方式使用#pragma pack(2) 强制编译器按照2字节对齐

将TFOEHEADER结构体强制为2字节对齐,代码如下:

 1 #pragma pack(2)    //此结构体在强制邮箱结构体类型转换时内存对齐发生错误
 2 typedef struct  MBX_STRUCT_PACKED_START
 3 {
 4     UINT16        OpCode; /**< \brief OpCode
 5                              *
 6                              * 1 : RRQ<br>
 7                              * 2 : WRQ<br>
 8                              * 3 : DATA<br>
 9                              * 4 : ACK<br>
10                              * 5 : ERR<br>
11                              * 6 : BUSY*/
12     union MBX_STRUCT_PACKED_START
13     {
14         UINT32        Password; /**< \brief Password (used in Read request and Write request). 0 if unknown*/
15         UINT32        PacketNo; /**< \brief Packet number (used in DATA and ACK datagram)*/
16         UINT32        ErrorCode; /**< \brief Error code (used in ERR datagram)*/
17         struct MBX_STRUCT_PACKED_START
18         {
19             UINT16    Done; /**< \brief Done indication (used in BUSY datagram)*/
20             UINT16    Entire;  /**< \brief Entire indication (used in BUSY datagram)*/
21         }MBX_STRUCT_PACKED_END
22         Busy; /**< \brief Busy variable*/
23     }MBX_STRUCT_PACKED_END
24     Cmd; /**< \brief Command field*/
25 }MBX_STRUCT_PACKED_END
26 TFOEHEADER;
27 #pragma pack()

 

posted @ 2023-05-25 10:12  _浮尘  阅读(265)  评论(0)    收藏  举报