如何在x64上为我的进程启用alignment例外?

我很好奇,看看我的64位应用程序是否存在alignment错误。

从IPF,x86和x64上的Windows数据alignment :

在Windows中,生成alignment错误的应用程序将引发exceptionEXCEPTION_DATATYPE_MISALIGNMENT

  • 在x64体系结构中 ,默认情况下,alignment例外是禁用的,而修复由硬件完成。 应用程序可以通过设置一对寄存器位来启用alignmentexception ,在这种情况下,除非用户操作系统用SEM_NOALIGNMENTFAULTEXCEPT屏蔽exception,否则将引发exception。 (有关详细信息,请参阅“ AMD体系结构程序员手册”卷2:系统编程。

[编者按: 强调我的]

  • 在x86体系结构上 ,操作系统不会使alignment错误对应用程序可见。 在这两个平台上,alignment错误也会出现性能下降,但是与Itanium相比,性能会降低很多,因为硬件会使多次访问内存来检索未alignment的数据。

  • 在Itanium上 ,默认情况下,操作系统(OS)会使此例外对应用程序可见,并且在这些情况下终止处理程序可能会有用。 如果你没有设置处理程序,那么你的程序将挂起或崩溃。 在清单3中,我们提供了一个示例,显示如何捕获EXCEPTION_DATATYPE_MISALIGNMENTexception。

忽略了查阅“ AMD体系结构程序员手册”的方向,我将参考“ 英特尔64和IA-32体系结构软件开发人员手册”

5.10.5检查alignment

当CPL为3时,可以通过设置CR0寄存器中的AM标志和EFLAGS寄存器中的AC标志来检查内存引用的alignment情况。 未alignment的内存引用会生成alignmentexception(#AC)。 在特权级别0,1或2下运行时,处理器不会产生alignmentexception。请参阅表6-7,了解启用alignment检查时alignment要求的说明。

优秀。 我不确定那是什么意思,但很好。

那么还有:

2.5控制寄存器

控制寄存器(CR0,CR1,CR2,CR3和CR4;见图2-6)确定处理器的工作模式和当前正在执行的任务的特性。 这些寄存器在所有32位模式和兼容模式下均为32位。

在64位模式下,控制寄存器扩展到64位。 MOV CRn指令用于操作寄存器位。 这些指令的操作数前缀被忽略。

控制寄存器总结如下,并且这些控制寄存器中的每个架构定义的控制字段都是单独描述的。 在图2-6中,64位模式下的寄存器宽度用括号表示(CR0除外)。 – CR0 – 包含控制处理器工作模式和状态的系统控制标志

在这里输入图像说明

上午
校准掩码(CR0的第18位) – 设置时启用自动校准检查; 清除时禁用alignment检查。 只有当AM标志置位,EFLAGS寄存器中的AC标志置位,CPL为3,处理器工作在受保护模式或虚拟8086模式时,才进行alignment检查。

我试过了

我实际使用的语言是delphi,但假装它是语言不可知的伪代码:

 void UnmaskAlignmentExceptions() { asm mov rax, cr0; //copy CR0 flags into RAX or rax, 0x20000; //set bit 18 (AM) mov cr0, rax; //copy flags back } 

第一条指令

 mov rax, cr0; 

特权指令exception失败。

如何在x64上为我的进程启用alignment例外?

PUSHF

我发现x86有这样的说明:

  • PUSHFPOPF :按下/popup首个16位的EFLAGS开/关堆栈
  • PUSHFDPOPFD :按下/popup所有32位的EFLAGS开/关堆栈

在这里输入图像说明

然后,这导致我的x64版本:

  • PUSHFQPOPFQ :在堆栈上打开/closuresRFLAGS quad

(在64位的世界里, EFLAGS被重新命名为RFLAGS )。

所以我写道:

 void EnableAlignmentExceptions; { asm PUSHFQ; //Push RFLAGS quadword onto the stack POP RAX; //Pop them flags into RAX OR RAX, $20000; //set bit 18 (AC=Alignment Check) of the flags PUSH RAX; //Push the modified flags back onto the stack POPFQ; //Pop the stack back into RFLAGS; } 

它没有崩溃或触发保护例外。 我不知道它是否做到了我想要的。

在x64上运行的应用程序可以访问标志寄存器(有时称为EFLAGS )。 该寄存器中的位18允许应用程序在发生对齐错误时获得异常。 所以从理论上说,所有的程序都要做到使对齐错误的异常是修改标志寄存器。

然而

为了实际工作,操作系统内核必须设置cr0的位18来允许它。 而Windows操作系统不这样做。 为什么不? 谁知道?

应用程序不能在控制寄存器中设置值。 只有内核可以做到这一点。 设备驱动程序在内核中运行,所以他们也可以设置它。

可以通过创建一个设备驱动程序来解决这个问题,并尝试使用它(参见http://blogs.msdn.com/b/oldnewthing/archive/2004/07/27/198410.aspx#199239和注释以下)。 请注意,这篇文章已经超过十年了,所以有些链接已经死了。

你也可能会发现这个评论(和这个问题中的其他一些答案)是有用的:

Larry Osterman – 07-28-2004 2:22 AM

我们实际上构建了一个x86版本的对齐例外(你可以像Skywing所说的那样做)。

我们很快就把它关闭了,因为有多少应用程序打破了:)