• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

【Delphi】32位源码编译64位程序时的字节对齐问题

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

        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位程序,通常需要修改各种系统结构体的定义。

 


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Matlab pcg函数的句柄形式之参数传递发布时间:2022-07-18
下一篇:
MatLab的函数句柄发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap