如何在x64上为我的进程启用对齐异常?

Ian*_*oyd 23 windows assembly x86-64

我很想知道我的64位应用程序是否存在对齐错误.

IPF,x86和x64上的Windows数据对齐:

在Windows中,生成对齐错误的应用程序将引发异常,EXCEPTION_DATATYPE_MISALIGNMENT.

  • 在x64体系结构上,默认情况下禁用对齐异常,并且修复由硬件完成.应用程序可以通过设置几个寄存器位来启用对齐异常,在这种情况下,除非用户使操作系统屏蔽异常,否则将引发异常SEM_NOALIGNMENTFAULTEXCEPT.(有关详细信息,请参阅AMD体系结构程序员手册第2卷:系统编程.)

[编者按:强调我的]

  • 在x86体系结构中,操作系统不会使对齐故障对应用程序可见.在这两个平台上,您还会在对齐故障方面遇到性能下降,但它的严重程度将远远低于Itanium,因为硬件将对内存进行多次访问以检索未对齐的数据.

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

忽略了参考AMD架构程序员手册的方向,我将参考英特尔64和IA-32架构软件开发人员手册

5.10.5检查对齐

当CPL为3时,可以通过设置CR0寄存器中的AM标志和EFLAGS寄存器中的AC标志来检查存储器参考的对齐情况.未对齐的内存引用会生成对齐异常(#AC).在特权级别0,1或2下操作时,处理器不会生成对齐异常.有关在启用对齐检查时对齐要求的说明,请参阅表6-7.

优秀.我不确定这意味着什么,但非常好.

然后还有:

2.5控制寄存器

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

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

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

在此输入图像描述

AM
对齐掩码(CR0的第18位) - 设置时启用自动对齐检查; 清除时禁用对齐检查.仅当AM标志置位,EFLAGS寄存器中的AC标志置位,CPL为3,处理器工作在受保护或虚拟8086模式时,才执行对齐检查.

我试过了

我实际使用的语言是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
}
Run Code Online (Sandbox Code Playgroud)

第一条指令

mov rax, cr0;
Run Code Online (Sandbox Code Playgroud)

没有特权指令例外.

如何在x64上为我的进程启用对齐异常?

PUSHF

我发现x86有以下指令:

  • PUSHF,POPF:在堆栈上打开/关闭第一个16位的EFLAGS
  • PUSHFD,POPFD:在堆栈上打开/关闭所有32位EFLAGS

在此输入图像描述

那导致我进入x64版本:

  • PUSHFQ,POPFQ:在堆叠上打开/关闭RFLAGS 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;
}
Run Code Online (Sandbox Code Playgroud)

并且它没有崩溃或触发保护异常.我不知道它是否符合我的要求.

奖金阅读

Dav*_*erd 11

在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

我们实际上构建了一个版本的NT,并为x86启用了对齐例外(你可以像Skywing提到的那样).

我们很快将其关闭,因为破坏的应用数量:)

  • 在x86_64上它是RFLAGS (2认同)