zoukankan      html  css  js  c++  java
  • 【Delphi】32位源码编译64位程序时的字节对齐问题

            delphi XE2新增对64位程序的支持,从框架到编译器都相当成熟,不过由于原有代码都是针对32位,在重新编译成64位,或者在编写64位程序时,由于32位代码的编写习惯,有时会出现一些莫名奇妙的问题。

            如最近在编写password filter的时候,发现同样的源码编译到32位可以正常工作,但编译成64位就出现的异常,最后发现是在实现其中PasswordFilter和PasswordChangeNotify时其参数为UNICODE_STRING的问题, 在访问Buffer字段时内存非法访问异常,该结构定义如下:

    _UNICODE_STRING=PACKED RECORD
         Length,
         MaximumLength:USHORT;
         Buffer:PWSTR;
    END;

    在32位系统中,CPU寻址按4字节对齐,该结构内存结构 PACKED后其实与没有PACKED是一样的,

    首先认识到寻址的对齐目的,是为了避免CPU访问数据时由于数据跨边界而需要2次寻址的问题,而32位单次寻址大小是4字节,

     Length加上MaximumLength 的大小只有4字节,刚好可以将该2字段存放同一边界内,

    在访问Length和MaximumLength时实际都分别只需要寻址一次, Buffer也是寻址一次,

    也就是说这里即使使用PACKED ,CPU访问所有字段的寻址次数也没有变化,因此PACKED后与没有PACKED大小是一样的。

    但是在64位系统,这里就出现差别了,因为64位的CPU寻址是按照8字节对齐的,由于Length加上MaximumLength 的大小只有4字节,可以存放在同一边界内,这时该边界内仍然剩余4字节, 结构使用PACKED后,将导致 Buffer紧跟着存放在剩余的4字节中,而Buffer为8字节的指针,因此还有4字节需要存放到下一个边界,也就是 Buffer 数据存跨越两个寻址边界,结果CPU要取得 Buffer数据就需要寻址2次。

    微软的64位系统通常不会出现这种存在跨边界字段的结构体,因为会增加寻址次数导致速度降低,因此他们在64位系统中对该结构进行修补,如下:

    _UNICODE_STRING=PACKED RECORD
          Length,
          MaximumLength:USHORT;
          {$IFDEF CPUX64}
           //64位机需要8字节对齐, 但packed会影响该对齐,导致Buffer指向位置错位
           //这里添加nReserve1,nReserve2来调整
           nReserve1: USHORT;
           nReserve2: USHORT;
          {$ENDIF}
          Buffer:PWSTR;
    END;

    这样一来,Buffer会被“挤到”下一边界,CPU只需寻址一次就够了,与32位的结构寻址次数相等,经过该结构体的修改,再编译成64位的

    password filter就可以正常工作了。

    以上可以看出,32位的源码要编译出正确的64位程序,通常需要修改各种系统结构体的定义。

  • 相关阅读:
    2016年 河南工业大学校赛 D题.rqy的键盘
    2016年 河南工业大学校赛 C题.魔法宝石
    jqueryMobile导航
    jqueryMobile列表
    jqueryMobile
    停止css3动画
    导航条
    移动端前面必须加的两行代码
    标签页
    file上传图片预览(此方法支持app)
  • 原文地址:https://www.cnblogs.com/caibirdy1985/p/4232957.html
Copyright © 2011-2022 走看看