gcc结构体对齐的误用

    最近对一段声卡裸板程序进行调试时发现了一个结构体对齐误用的错误。在接有wm8976的s3c2440开发板上调试裸板程序,用来解析并播放NandFlash上的wav二进制数据,使用dma传输数据到iis接口,并在传输完毕后触发中断再次传输。然而结果是既没有声音,dma中断也没有被触发,以为是dma的设置出错,经过反复试验后,将问题定位到了iis的部分代码:
struct i2s_regs
{
    unsigned long iiscon; //0x55000000
    unsigned long iismod;
    unsigned long iispsr;
    unsigned long iisfcon;
    unsigned long iisfifo;
}__attribute__((packed));

static volatile struct i2s_regs* i2sregs;
void i2s_init(unsigned int bits_per_channel,unsigned int samp_per_second)
{
    int i;
    int min = 0xffff;
    unsigned int tmp_fs;
    unsigned int pre = 0;

    i2sregs = (struct i2s_regs*)0x55000000;

    GPECON &= ~0x3ff;
    GPECON |=  0x2aa;

    if(bits_per_channel == 16)
    {
        i2sregs->iismod  =  (2<<6) | (1<<3) | (1<<2) | (1);
    }else{
        i2sregs->iismod  =  (2<<6) | (0<<3) | (1<<2) | (1);
    }

     for(i = 0;i < 32;i++)
     {
        tmp_fs = PCLK/384/(i+1);
        if(ABS(tmp_fs,samp_per_second) < min)
        {
            min = ABS(tmp_fs,samp_per_second);
            pre = i;
        }
     }

    i2sregs->iispsr  = (pre<<5) | pre ;
    i2sregs->iisfcon = (1<<15) | (1<<13) ;
    i2sregs->iiscon  =  (1<<5) | (1<<1);
}
   将结构体指针指向iis寄存器的首地址,通过该结构体指针来访问设置iis寄存器,去掉attribute__ ((packed)) 的属性描述符后成功运行了程序,那么为什么会出现这种问题呢?
因为attribute__ ((packed)) 的属性描述符的作用是不进行结构体成员的对齐,在内存中按成员的大小依次排列各成员,按理说 这五个成员都是unsigned long 类型,本身就是4字节对齐的,无论加不加上attribute__ ((packed)) 的属性描述符编译出来的结果都应该是一样的,也就是说结构体的所占大小应该都是20,加上打印语句后也是20。那么会不会是指针赋值的时候出了问题呢?i2sregs = (struct i2s_regs*)0x55000000;寄存器的首地址本身也是4字节对齐的,也不会出错,对每个结构体成员进行取址,得到的结果也都和芯片手册上写的一致,那么说明结构体指针映射没有问题,只可能是对它们进行赋值的时候出了问题了,这种情况下也只有查看反汇编的代码来看看究竟了。使用arm-linux-objdump对elf进行反汇编,分别得到加上属性描述符与不加的代码,来进行分析。

    这是不加上attribute__ ((packed) 属性描述符的代码,忽略掉值的计算等指令,只寻找最后赋值的指令可以大致发现,对每个结构体成员(寄存器)进行最后的写入时,使用的都是str指令。
33f80860 <i2s_init>:
33f80860:   e92d41f0    push    {r4, r5, r6, r7, r8, lr}
33f80864:   e3a03455    mov r3, #1426063360 ; 0x55000000
33f80868:   e59f80d0    ldr r8, [pc, #208]  ; 33f80940 <i2s_init+0xe0>
33f8086c:   e1a04000    mov r4, r0
33f80870:   e1a06001    mov r6, r1
33f80874:   e59f00c8    ldr r0, [pc, #200]  ; 33f80944 <i2s_init+0xe4>
33f80878:   e1a01003    mov r1, r3
33f8087c:   e5883000    str r3, [r8]
33f80880:   eb000279    bl  33f8126c <printf>
33f80884:   e3a01456    mov r1, #1442840576 ; 0x56000000
33f80888:   e5913040    ldr r3, [r1, #64]
33f8088c:   e3c33fff    bic r3, r3, #1020   ; 0x3fc
33f80890:   e3c33003    bic r3, r3, #3  ; 0x3
33f80894:   e5813040    str r3, [r1, #64]
33f80898:   e5912040    ldr r2, [r1, #64]
33f8089c:   e3822faa    orr r2, r2, #680    ; 0x2a8
33f808a0:   e3822002    orr r2, r2, #2  ; 0x2
33f808a4:   e3540010    cmp r4, #16 ; 0x10
33f808a8:   e5812040    str r2, [r1, #64]
33f808ac:   0a00001f    beq 33f80930 <i2s_init+0xd0>
33f808b0:   e5982000    ldr r2, [r8]
33f808b4:   e3a03085    mov r3, #133    ; 0x85
33f808b8:   e5823004    str r3, [r2, #4]
33f808bc:   e3a04000    mov r4, #0  ; 0x0
33f808c0:   e3a05801    mov r5, #65536  ; 0x10000
33f808c4:   e2455001    sub r5, r5, #1  ; 0x1
33f808c8:   e1a07004    mov r7, r4
33f808cc:   e2844001    add r4, r4, #1  ; 0x1
33f808d0:   e3a00b7f    mov r0, #130048 ; 0x1fc00
33f808d4:   e28000a0    add r0, r0, #160    ; 0xa0
33f808d8:   e1a01004    mov r1, r4
33f808dc:   eb0001bb    bl  33f80fd0 <__aeabi_idiv>
33f808e0:   e1500006    cmp r0, r6
33f808e4:   80663000    rsbhi   r3, r6, r0
33f808e8:   90603006    rsbls   r3, r0, r6
33f808ec:   e1530005    cmp r3, r5
33f808f0:   2a000003    bcs 33f80904 <i2s_init+0xa4>
33f808f4:   e1500006    cmp r0, r6
33f808f8:   e0605006    rsb r5, r0, r6
33f808fc:   e2447001    sub r7, r4, #1  ; 0x1
33f80900:   80665000    rsbhi   r5, r6, r0
33f80904:   e3540020    cmp r4, #32 ; 0x20
33f80908:   1affffef    bne 33f808cc <i2s_init+0x6c>
33f8090c:   e5981000    ldr r1, [r8]
33f80910:   e1873287    orr r3, r7, r7, lsl #5
33f80914:   e5813008    str r3, [r1, #8]
33f80918:   e3a02a0a    mov r2, #40960  ; 0xa000
33f8091c:   e3a03022    mov r3, #34 ; 0x22
33f80920:   e581200c    str r2, [r1, #12]
33f80924:   e5813000    str r3, [r1]
33f80928:   e8bd41f0    pop {r4, r5, r6, r7, r8, lr}
33f8092c:   e12fff1e    bx  lr
33f80930:   e5982000    ldr r2, [r8]
33f80934:   e3a0308d    mov r3, #141    ; 0x8d
33f80938:   e5823004    str r3, [r2, #4]
33f8093c:   eaffffde    b   33f808bc <i2s_init+0x5c>
33f80940:   33f83248    .word   0x33f83248
33f80944:   33f830f0    .word   0x33f830f0
    再看看加上attribute__ ((packed) 属性描述符的代码,同样查看给寄存器赋值的指令,发现使用的却是strb指令。
33f80828 <i2s_init>:
33f80828:   e3a0c456    mov ip, #1442840576 ; 0x56000000
33f8082c:   e59c3040    ldr r3, [ip, #64]
33f80830:   e3c33fff    bic r3, r3, #1020   ; 0x3fc
33f80834:   e3c33003    bic r3, r3, #3  ; 0x3
33f80838:   e58c3040    str r3, [ip, #64]
33f8083c:   e59c2040    ldr r2, [ip, #64]
33f80840:   e59f3168    ldr r3, [pc, #360]  ; 33f809b0 <i2s_init+0x188>
33f80844:   e3822faa    orr r2, r2, #680    ; 0x2a8
33f80848:   e3500010    cmp r0, #16 ; 0x10
33f8084c:   e3822002    orr r2, r2, #2  ; 0x2
33f80850:   e3a00455    mov r0, #1426063360 ; 0x55000000
33f80854:   e92d41f0    push    {r4, r5, r6, r7, r8, lr}
33f80858:   e58c2040    str r2, [ip, #64]
33f8085c:   e5830000    str r0, [r3]
33f80860:   e1a06001    mov r6, r1
33f80864:   0a000042    beq 33f80974 <i2s_init+0x14c>
33f80868:   e5d03004    ldrb    r3, [r0, #4]
33f8086c:   e3e0307a    mvn r3, #122    ; 0x7a
33f80870:   e5c03004    strb    r3, [r0, #4]
33f80874:   e3a02000    mov r2, #0  ; 0x0
33f80878:   e5d03005    ldrb    r3, [r0, #5]
33f8087c:   e5c02005    strb    r2, [r0, #5]
33f80880:   e5d03006    ldrb    r3, [r0, #6]
33f80884:   e5c02006    strb    r2, [r0, #6]
33f80888:   e3a05801    mov r5, #65536  ; 0x10000
33f8088c:   e5d03007    ldrb    r3, [r0, #7]
33f80890:   e5c02007    strb    r2, [r0, #7]
33f80894:   e1a04002    mov r4, r2
33f80898:   e2455001    sub r5, r5, #1  ; 0x1
33f8089c:   e1a07002    mov r7, r2
33f808a0:   e2844001    add r4, r4, #1  ; 0x1
33f808a4:   e3a00b7f    mov r0, #130048 ; 0x1fc00
33f808a8:   e28000a0    add r0, r0, #160    ; 0xa0
33f808ac:   e1a01004    mov r1, r4
33f808b0:   eb0001ce    bl  33f80ff0 <__aeabi_idiv>
33f808b4:   e1500006    cmp r0, r6
33f808b8:   80663000    rsbhi   r3, r6, r0
33f808bc:   90603006    rsbls   r3, r0, r6
33f808c0:   e1530005    cmp r3, r5
33f808c4:   2a000003    bcs 33f808d8 <i2s_init+0xb0>
33f808c8:   e1500006    cmp r0, r6
33f808cc:   e0605006    rsb r5, r0, r6
33f808d0:   e2447001    sub r7, r4, #1  ; 0x1
33f808d4:   80665000    rsbhi   r5, r6, r0
33f808d8:   e3540020    cmp r4, #32 ; 0x20
33f808dc:   1affffef    bne 33f808a0 <i2s_init+0x78>
33f808e0:   e1870287    orr r0, r7, r7, lsl #5
33f808e4:   e3a03455    mov r3, #1426063360 ; 0x55000000
33f808e8:   e20010ff    and r1, r0, #255    ; 0xff
33f808ec:   e5d32008    ldrb    r2, [r3, #8]
33f808f0:   e5c31008    strb    r1, [r3, #8]
33f808f4:   e1a02420    lsr r2, r0, #8
33f808f8:   e20220ff    and r2, r2, #255    ; 0xff
33f808fc:   e5d31009    ldrb    r1, [r3, #9]
33f80900:   e5c32009    strb    r2, [r3, #9]
33f80904:   e1a01820    lsr r1, r0, #16
33f80908:   e20110ff    and r1, r1, #255    ; 0xff
33f8090c:   e5d3200a    ldrb    r2, [r3, #10]
33f80910:   e5c3100a    strb    r1, [r3, #10]
33f80914:   e1a00c20    lsr r0, r0, #24
33f80918:   e5d3200b    ldrb    r2, [r3, #11]
33f8091c:   e5c3000b    strb    r0, [r3, #11]
33f80920:   e3a01000    mov r1, #0  ; 0x0
33f80924:   e5d3200c    ldrb    r2, [r3, #12]
33f80928:   e5c3100c    strb    r1, [r3, #12]
33f8092c:   e5d3200d    ldrb    r2, [r3, #13]
33f80930:   e3e0205f    mvn r2, #95 ; 0x5f
33f80934:   e5c3200d    strb    r2, [r3, #13]
33f80938:   e5d3200e    ldrb    r2, [r3, #14]
33f8093c:   e5c3100e    strb    r1, [r3, #14]
33f80940:   e5d3200f    ldrb    r2, [r3, #15]
33f80944:   e5c3100f    strb    r1, [r3, #15]
33f80948:   e5d32000    ldrb    r2, [r3]
33f8094c:   e3a02022    mov r2, #34 ; 0x22
33f80950:   e5c32000    strb    r2, [r3]
33f80954:   e5d32001    ldrb    r2, [r3, #1]
33f80958:   e5c31001    strb    r1, [r3, #1]
33f8095c:   e5d32002    ldrb    r2, [r3, #2]
33f80960:   e5c31002    strb    r1, [r3, #2]
33f80964:   e5d32003    ldrb    r2, [r3, #3]
33f80968:   e5c31003    strb    r1, [r3, #3]
33f8096c:   e8bd41f0    pop {r4, r5, r6, r7, r8, lr}
33f80970:   e12fff1e    bx  lr
33f80974:   e5d03004    ldrb    r3, [r0, #4]
33f80978:   e3e03072    mvn r3, #114    ; 0x72
33f8097c:   e5c03004    strb    r3, [r0, #4]
33f80980:   e3a02000    mov r2, #0  ; 0x0
33f80984:   e5d03005    ldrb    r3, [r0, #5]
33f80988:   e5c02005    strb    r2, [r0, #5]
33f8098c:   e5d03006    ldrb    r3, [r0, #6]
33f80990:   e5c02006    strb    r2, [r0, #6]
33f80994:   e3a05801    mov r5, #65536  ; 0x10000
33f80998:   e5d03007    ldrb    r3, [r0, #7]
33f8099c:   e1a04002    mov r4, r2
33f809a0:   e5c02007    strb    r2, [r0, #7]
33f809a4:   e2455001    sub r5, r5, #1  ; 0x1
33f809a8:   e1a07002    mov r7, r2
33f809ac:   eaffffbb    b   33f808a0 <i2s_init+0x78>
33f809b0:   33f83254    .word   0x33f83254
    那么问题基本就大概明白了,确实是给寄存器赋值的时候出了问题,cpu在访问非4字节对齐的地址时,会访问相邻区域的内容,将读到的值进行丢弃、拼接组合后读回,写入的情况下也会先读取原来的值,修改掉要修改的部分后再写回,在访问内存等区域时会使用ldrb 、strb等指令,但是在操作寄存器时是否可以使用这些指令就不得而知了,为此,修改代码,同时定义加上对齐描述符的结构体和不加的,并让它们的指针都指向iis寄存器的首地址,在原代码中加上这些步骤
  利用不加描述符的结构体指针访问iis寄存器,读出修改之前的值
  在进行写入前打印下将要写入的值
  利用加上描述符的结构体指针访问iis寄存器,读出修改之后的值
  利用不加描述符的结构体指针访问iis寄存器,读出修改之后的值

iiscon np :0x00000100
iismod np :0x00000000
iispsr np :0x00000000
iisfcon np :0x00000000

iismod wr :0x0000008d
iispsr wr :0x000000a5
iisfcon wr :0x0000a000
iiscon wr :0x00000022

iismod rd :0x00000000
iispsr rd :0x00000000
iisfcon rd :0x00000000
iiscon rd :0x00000100

iismod np :0x00000000
iispsr np :0x00000000
iisfcon np :0x00000000
iiscon np :0x00000100

    可以看到寄存器的值并没有任何改变,说明加上了不对齐的属性描述符后,编译器会进行一些优化,而这些处理恰恰使我们对寄存器的访问失败,导致了错误,因此,在进行寄存器映射时,因为寄存器本身就是4字节对齐的,可不要画蛇添足对 对齐方式进行特殊处理,否则得到的结果无法预料。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值