哪种类型的代码可以动态触发未对齐的数据访问sigbus陷阱?

时间:2019-04-29 20:55:53

标签: gcc arm memory-alignment qnx qnx-neutrino

我正在寻找有关未对齐数据访问的SIGBUS。我正在跟踪此错误之一,我想知道这在西塔拉am335x上是如何发生的。有人可以给我一个示例代码来描述或确保触发它。

添加代码段:

int Read( void *value, uint32_t *size, const uint32_t baseAddress )
{
    uint8_t *userDataAddress = (uint8_t *)( baseAddress + sizeof( DBANode ));
    memcpy( value, userDataAddress, ourDataSize );
    *size = ourDataSize;
    return 0;
}

DBA节点是一个20字节的类对象。 baseAddress再次是对共享内存文件的mmap,它是强制转换为uint32_t的DBANode类对象类型的,因此可以完成算法。

这是本节的重点:

    91a8:   e51b3010    ldr r3, [fp, #-16]
    91ac:   e5933000    ldr r3, [r3]
    91b0:   e51b0014    ldr r0, [fp, #-20]  ; 0xffffffec
    91b4:   e51b1008    ldr r1, [fp, #-8]
    91b8:   e1a02003    mov r2, r3
    91bc:   ebffe72b    bl  2e70 <memcpy@plt>
    91c0:   e51b3010    ldr r3, [fp, #-16]
    91c4:   e5932000    ldr r2, [r3]
    91c8:   e51b3018    ldr r3, [fp, #-24]  ; 0xffffffe8
    91cc:   e5832000    str r2, [r3]

00002e70 <memcpy@plt>:
    2e70:   e28fc600    add ip, pc, #0, 12
    2e74:   e28cca08    add ip, ip, #8, 20  ; 0x8000
    2e78:   e5bcf868    ldr pc, [ip, #2152]!    ; 0x868

当重建完全相同的代码库时,问题就消失了。 gcc可以使用为gcc指定的-O0进行相同的优化来创建2个不同版本的指令吗?

我还比较了库,因此在两个编译中都将文件obj转储。他们是完全一样的。该API经常使用。但是,崩溃只有在长时间使用后才会发生。我每500毫秒读取一次相同的节点。因此,这是不一致的。 我应该看看指针损坏吗?

2 个答案:

答案 0 :(得分:1)

摘自《 Cortex-A8技术参考手册》:

  

处理器支持未对齐字词和   半字。处理器进行所需的内存访问次数   并透明地传输相邻字节。

     

注意跨越单词边界的数据访问会增加访问时间。

     

将CP15 c1控制寄存器中的A位置1可启用对齐   检查。当A位设置为1时,两种类型的内存访问   产生数据中止信号和对准故障状态代码:

     
      
  • 不是半字对齐的16位访问

  •   
  • 不是字对齐的32位加载或存储

  •   
     

对齐错误检测是强制的地址生成功能   而不是外部存储器的可选功能   管理硬件。有关详细信息,请参见 ARM体系结构参考手册。   有关未对齐的数据访问支持的更多信息。

在ARM ARM中,如果未与传输大小对齐,则总是的指令会生成对齐错误: LDREX,STREX,LDREXD,STREXD,LDM,STM,LDRD,RFE,SRS,STRD,SWP,LDC,LDC2,STC,STC2,VLDM,VLDR,VPOP,VPUSH,VSTM,VSTR。

此外,大多数PUSH,POP和VLDx都指定了:align:。

进一步

  

在包含虚拟化扩展的实现中,   对设备的未对齐访问或内存排序不正确总是会导致   对齐错误数据中止异常

就像在链接的问题中一样,结构是导致“预定的”未对齐访问的最明显方法,但是堆栈指针或其他变量指针的任何损坏也将产生相同的结果。取决于内核的配置方式,会影响正常的单字/半字访问只是缓慢还是触发故障。

如果您有权访问ETM跟踪,则可以识别确切的访问。看来该零件具有ETM / ETB(因此不需要花哨的跟踪捕获设备),但是我不知道获得使用它的工具有多么容易。

关于什么代码可以触发此操作,是的,即使memcpy()也可能是problem。由于ARM指令集具有用于传输多个寄存器(或AA64中的寄存器对)的优化,因此优化的库函数将更喜欢“流式传输”数据,而不是逐字节地进行加载和存储。根据数据结构和编译目标,完全有可能以非法的LDM结束未对齐的地址。

答案 1 :(得分:1)

原来是baseAddress问题。正如我所提到的,它是到mmap可能失败的共享内存位置的mmap。失败的mmap返回-1,并且代码正在检查NULL并继续写入-1,即0xFFFFFFFF导致sigbus。 使用memcpy时会看到代码1。尝试任何其他访问方式(例如直接字节寻址)都会使sigbus的代码为3。

我仍然不确定为什么它会触发SIGBUS而不是SIGSEGV。难道这不应该是内存冲突吗? 这是一个示例:

int main(int argc, char **argv)
{
    // Shared memory example                                                    
     const char *NAME = "SharedMemory";                                          
     const int SIZE = 10 * sizeof(uint8_t);                                      
     uint8_t src[]={0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00};          
     int shm_fd = -1;                                                            

     shm_fd = shm_open(NAME, O_CREAT | O_RDONLY, 0666);                          
     ftruncate(shm_fd, SIZE);                                                    

    // Map shared memory segment to address space                               
     uint8_t *ptr = (uint8_t *) mmap(0, SIZE, PROT_READ | PROT_WRITE | _NOCACHE, MAP_SHARED, shm_fd, 0);
     if(ptr == MAP_FAILED)                                                       
     {                                                                           
          std::cerr << "ERROR in mmap()" << std::endl;                            
      //  return -1;                                                              
      }                                                                           
      printf("ptr = 0x%08x\n",ptr);                                               
      std::cout << "Now storing data to mmap() memory" << std::endl;              
      #if 0                                                                           
      ptr[0] = 0x11;                                                              
      ptr[1] = 0x22;                                                              
      ptr[2] = 0x33;                                                              
      ptr[3] = 0x44;                                                              
      ptr[4] = 0x55;                                                              
      ptr[5] = 0x66;                                                              
      ptr[6] = 0x77;                                                              
      ptr[7] = 0x88;                                                              
      ptr[8] = 0x99;                                                              
      ptr[9] = 0x00;                                                              
      #endif                                                                          

      memcpy(ptr,src,SIZE);   //causes sigbus code 1                              
      shm_unlink(NAME);
}

即使我有100MB的可用内存,并且所有资源限制都设置为无限制,而1000 fds限制中仍然有超过400 fds(文件描述符)可用,我仍然不知道为什么mmap不能在shm上失败。 !!!