【问题标题】:Pointer casting problem with struct array member结构数组成员的指针转换问题
【发布时间】:2012-05-31 19:40:07
【问题描述】:

我在旧代码库中遇到过这个源代码,但我真的不知道为什么它的行为方式如此。

在下面的代码中,pData 结构成员要么包含数据,要么包含指向共享内存中真实数据的指针。使用 IPC(msgsnd()msgrcv())发送消息。使用指针转换(当前已注释掉),在 ARM 目标上使用 GCC 4.4.1 失败,成员 uLen 被修改。使用 memcpy() 时,一切都按预期工作。我真的看不出指针转换有什么问题。这里有什么问题?

typedef struct {
    long mtype;
    unsigned short uRespQueue;
    unsigned short uID;
    unsigned short uLen;
    unsigned char pData[8000];
} message_t;

// changing the pointer in the struct
{
    unsigned char *pData = <some_pointer>;
#if 0
    *((unsigned int *)pMessage->pData) = (unsigned int)pData;
#else
    memcpy(pMessage->pData, &pData, sizeof(unsigned int));
#endif
}

// getting the pointer out
{
#if 0
    unsigned char *pData; (unsigned char *)(*((unsigned int *)pMessage->pData));
#else
    unsigned char *pData;
    memcpy(&pData, pMessage->pData, sizeof(int));
#endif
}

【问题讨论】:

  • 当您提到 uLen 已更改时,我不太确定您的意思。使用铸造时它会改变吗(更有可能)?或者你什么时候做memcopy()?我正在编辑你的问题,标点符号不是很清楚。

标签: c pointers casting arm


【解决方案1】:

我怀疑这是对齐问题,GCC 或处理器正在尝试补偿。结构定义为:

typedef struct {
    long mtype;
    unsigned short uRespQueue;
    unsigned short uID;
    unsigned short uLen;
    unsigned char pData[8000];
} message_t;

假设正常对齐限制和 32 位处理器,每个字段的偏移量为:

mtype         0   (alignment 4)
uRespQueue    4   (alignment 2)
uID           6   (alignment 2)
uLen          8   (alignment 2)
pData         10  (alignment 1)

在除最新版本的 ARM 处理器之外的所有版本上,内存访问必须在 ARM 处理器上与强制转换保持一致:

*((unsigned int *)pMessage->pData) = (unsigned int)pData;

您正试图在未对齐的地址上写入 32 位值。为了更正对齐,地址似乎已经截断了地址的 LSB 以具有正确的对齐。这样做恰好与导致问题的 uLen 字段重叠。

为了能够正确处理此问题,您需要确保将值写入正确对齐的地址。要么偏移指针以对齐它,要么确保 pData 对齐以能够处理 32 位数据。我将重新定义结构以对齐 pData 成员以进行 32 位访问。

typedef struct {
    long mtype;
    unsigned short uRespQueue;
    unsigned short uID;
    unsigned short uLen;
    union { /* this will add 2-bytes of padding */
        unsigned char *pData;
        unsigned char  rgData[8000];
    };
} message_t;

由于mtype 字段,该结构仍应占用相同数量的字节,因为它具有 4 字节对齐。

那么你应该可以访问指针了:

unsigned char *pData = ...;
/* setting the pointer */
pMessage->pData = pData;

/* getting the pointer */
pData = pMessage->pData;

【讨论】:

  • “必须对齐”并非严格意义上的正确。最新版本的 ARM 架构(ARMv6 和 ARMv7)具有允许未对齐访问的模式。此外,Linux 内核具有未对齐的异常陷阱处理程序,可以“纠正”错误编写的程序(自然会牺牲性能)。
  • @Igor:我觉得trenki 没有最新版本,所以我过度概括了这个说法。感谢您指出这一点。
【解决方案2】:

这是一件非常讨厌的事情(编译出来的事情)。您基本上是在尝试破解代码,而不是在消息中使用数据副本(在为其提供的 8000 字节中),您尝试放置一个指针,并通过 IPC 传递它。

主要问题是进程之间共享内存。谁知道发送后该指针会发生什么?谁知道它指向的数据会发生什么?发送指向不受您控制的数据的指针是一个非常糟糕的习惯(即:未受保护/未正确共享)。

另一件可能发生的事情,也可能是你真正在谈论的事情,就是对齐。该数组属于char,结构中的前一个成员是short,编译器可能会尝试打包它们。将char[] 重铸为int * 意味着您占用内存区域并将其表示为其他内容,而无需告诉编译器。你踩到了演员的uLen

memcopy 是正确的做法。

【讨论】:

    【解决方案3】:

    这里的重点是代码“int header = (((int)(txUserPtr) - 4))” UserTypes 和 struct 指针转换的图解很有帮助!

    typedef union UserTypes
    {
        SAUser           AUser;
        BUser            BUser;
        SCUser           CUser;
        SDUser           DUser;
    } UserTypes;
    
    typedef struct AUser
    {
        int              userId;
        int              dbIndex;
        ChannelType      ChanType;
     } AUser;
    typedef struct AUser
    {
        int              userId;
        int              dbIndex;
        ChannelType      ChanType;
     } AUser;
    
    typedef struct BUser
    {
        int              userId;
        int              dbIndex;
        ChannelType      ChanType;
     } BUser;
    
    typedef struct CUser
    {
        int              userId;
        int              dbIndex;
        ChannelType      ChanType;
     } CUser;
    
    typedef struct DUser
    {
        int              userId;
        int              dbIndex;
        ChannelType      ChanType;
     } DUser;
    
    //this is the function I want to test
    
    void Fun(UserTypes * txUserPtr)
    {
    
       int header = (*((int*)(txUserPtr) - 4));
    
       //the problem is here
       //how should i set incoming pointer "txUserPtr" so that 
       //Fun() would skip following lines.
       // I don't want to execute error()
    
            if((header & 0xFF000000) != (int)0xAA000000)
            {
                error("sth error\n");
            }
       /*the following is the rest */ 
    }
    

    【讨论】:

      猜你喜欢
      • 2021-08-05
      • 1970-01-01
      • 1970-01-01
      • 2015-01-24
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 2018-03-10
      相关资源
      最近更新 更多