如何在用户模式下设置/清除x86 IA32 Intel CPU上的TF标志
我想知道在x86 IA32 Intel CPU上以用户模式设置/清除EFLAGS.TF的步骤 尝试在下面清除TF标志,但得到错误******未处理的中断向量*****如何在用户模式下设置/清除x86 IA32 Intel CPU上的TF标志,x86,inline-assembly,eflags,X86,Inline Assembly,Eflags,我想知道在x86 IA32 Intel CPU上以用户模式设置/清除EFLAGS.TF的步骤 尝试在下面清除TF标志,但得到错误******未处理的中断向量***** 使用下面的代码,它工作得很好。多谢各位 __asm__ volatile("pushl %eax;\ pushfl;\ popl %eax;\ andl $0xFFFFFEFF, %eax;\
使用下面的代码,它工作得很好。多谢各位
__asm__ volatile("pushl %eax;\
pushfl;\
popl %eax;\
andl $0xFFFFFEFF, %eax;\
pushl %eax;\
popfl;\
popl %eax;"
);
使用下面的代码,它工作得很好。多谢各位
__asm__ volatile("pushl %eax;\
pushfl;\
popl %eax;\
andl $0xFFFFFEFF, %eax;\
pushl %eax;\
popfl;\
popl %eax;"
);
XOR会翻转一点,而不是总是清除它。这是一种选择,也是另一种选择。有一个内存目标的BTR在寄存器源的情况下速度非常慢,但在Haswell上只有2个UOP,在Skylake上只有3个UOP,这一点也不坏。但在AMD上,即使btr$9、%eax,其成本也高达2 UOP 在Skylake上,popf的速度相当慢,为9 UOP,每20个周期1次。或在Ryzen,35个UOP,每13个循环一个。因此,优化周围的代码不会产生很大的差异,但是找到一种保持代码大小紧凑的方法是很有趣的 您不需要自己保存/还原EAX,只需告诉编译器您想使用:EAX作为clobber列表对其进行clobber,或者使用一个伪输出操作数。请注意,我使用的是GNU C扩展asm,而不是basic
static inline
void clear_tf(void) {
long dummy; // there's no type that's always 32-bit on 32-bit, and always 64 on 64-bit. x32 uses 32-bit pointers in long mode so uintptr_t or size_t doesn't work.
// if porting to x86-64 System V user-space: beware that push clobbers the red-zone
__asm__ volatile("pushf \n\t"
"pop %[tmp] \n\t"
"btr $9, %[tmp]\n\t" // reset bit 9
"push %[tmp] \n\t"
"popf"
: [tmp] "=r"(dummy)
: // no inputs
: // no clobbers. // "memory" // would block reordering with loads/stores.
);
}
或者干脆不要碰它的任何寄存器:这也是非常有效的,尤其是在AMD Ryzen上,那里没有堆栈同步uop和内存目标,只有一个uop
static inline
void clear_tf(void) {
// if porting to x86-64 System V user-space: beware that push clobbers the red-zone
__asm__ volatile("pushf \n\t"
"andl $0xFFFFFEFF, (%esp) \n\t" // 1 byte larger than the pop/btr/push version
"popf"
);
// Basic asm syntax: no clobbers.
}
对于较小的代码大小,btrl$9,%esp可能是好的。在天湖的Haswell 3上仍然只有2个UOP,但比andl小2字节。和B$0xfe,1%esp的大小也相同,但会导致存储转发暂停,在推送后使用时,在Intel上为2 uop+堆栈同步uop。pop%%eax;和$0xfe,%ah;推送%eax的大小也相同,还有3个uop加上一个部分寄存器合并uop,该uop在Haswell/SKL上以一个周期自行发出。但是它在AMD上很好
便携性
顺便说一句,在x86-64 System V用户空间代码中,如果不关闭编译器的红色区域,就无法安全地推送/弹出,因此您可能希望在推送之前添加$-128,%rsp,然后在推送之后恢复
在内核代码中没有红色区域,所以内联asm中的push/pop就可以了
Windows使用不同的ABI,没有红色区域。XOR会翻转一点,而不是总是清除它。这是一种选择,也是另一种选择。有一个内存目标的BTR在寄存器源的情况下速度非常慢,但在Haswell上只有2个UOP,在Skylake上只有3个UOP,这一点也不坏。但在AMD上,即使btr$9、%eax,其成本也高达2 UOP 在Skylake上,popf的速度相当慢,为9 UOP,每20个周期1次。或在Ryzen,35个UOP,每13个循环一个。因此,优化周围的代码不会产生很大的差异,但是找到一种保持代码大小紧凑的方法是很有趣的 您不需要自己保存/还原EAX,只需告诉编译器您想使用:EAX作为clobber列表对其进行clobber,或者使用一个伪输出操作数。请注意,我使用的是GNU C扩展asm,而不是basic
static inline
void clear_tf(void) {
long dummy; // there's no type that's always 32-bit on 32-bit, and always 64 on 64-bit. x32 uses 32-bit pointers in long mode so uintptr_t or size_t doesn't work.
// if porting to x86-64 System V user-space: beware that push clobbers the red-zone
__asm__ volatile("pushf \n\t"
"pop %[tmp] \n\t"
"btr $9, %[tmp]\n\t" // reset bit 9
"push %[tmp] \n\t"
"popf"
: [tmp] "=r"(dummy)
: // no inputs
: // no clobbers. // "memory" // would block reordering with loads/stores.
);
}
或者干脆不要碰它的任何寄存器:这也是非常有效的,尤其是在AMD Ryzen上,那里没有堆栈同步uop和内存目标,只有一个uop
static inline
void clear_tf(void) {
// if porting to x86-64 System V user-space: beware that push clobbers the red-zone
__asm__ volatile("pushf \n\t"
"andl $0xFFFFFEFF, (%esp) \n\t" // 1 byte larger than the pop/btr/push version
"popf"
);
// Basic asm syntax: no clobbers.
}
对于较小的代码大小,btrl$9,%esp可能是好的。在天湖的Haswell 3上仍然只有2个UOP,但比andl小2字节。和B$0xfe,1%esp的大小也相同,但会导致存储转发暂停,在推送后使用时,在Intel上为2 uop+堆栈同步uop。pop%%eax;和$0xfe,%ah;推送%eax的大小也相同,还有3个uop加上一个部分寄存器合并uop,该uop在Haswell/SKL上以一个周期自行发出。但是它在AMD上很好
便携性
顺便说一句,在x86-64 System V用户空间代码中,如果不关闭编译器的红色区域,就无法安全地推送/弹出,因此您可能希望在推送之前添加$-128,%rsp,然后在推送之后恢复
在内核代码中没有红色区域,所以内联asm中的push/pop就可以了
Windows使用不带红色区域的不同ABI。要清除,您需要确保它已设置或在未设置版本的位掩码上使用或。谢谢您,我最初使用过WIZZ,如下所示。谢谢您,我最初使用WIZZ,如下所示,它不起作用,$0xFEFF,%eax\n\t;但对于EFlags,我们需要使用andl correct,在这种情况下,我想下面可能是正确的,如果我错了,请纠正我andl$0xFFFFFEFF,%eax;有了这个变化,我也不会例外,谢谢你,你说得对;我不知道为什么我建议把它。。。如果你把下面的答案框贴出来作为答案,这会帮助很多其他人;这样,下次有人遇到这个问题时,他们会发现这个问题并立即得到答案。要清除,您需要确保它已设置或使用或在您的位掩码的NOTted版本上。谢谢WIZZ最初我尝试过,以及BelowAnk you WIZZ最初我尝试过,如下所示,它不工作并且$0xFEFF,%eax\n\t;但对于EFlags,我们需要使用andl correct,在这种情况下,我想下面的可能是正确的,请原谅
如果我错了,请纠正我,$0xFFFFFEFF,%eax;有了这个变化,我也不会例外,谢谢你,你说得对;我不知道为什么我建议把它。。。如果你把下面的答案框贴出来作为答案,这会帮助很多其他人;这样,下次有人遇到这个问题时,他们会发现这个问题并立即得到答案。如果你在EAX上声明了一个clobber,你就不必在asm语句周围保存/恢复它。asmblah blah使用%%eax扩展asm::%eax;或者更好,使用一个伪输出操作数让编译器选择一个寄存器供您弹出。或者不要触碰eax,使用andl$0xFFFFFEFF、%%esp.或用于较小的代码大小,但用于存储转发暂停,和B$0xFE、1%esp.或btrl$9、%esp是代码大小和UOP之间的一个有用的折衷,尽管popf非常慢,所以它控制着成本,您可能不会经常这样做。如果您在eax上声明一个失败,您不必围绕asm语句保存/恢复它。asmblah blah使用%%eax扩展asm::%eax;或者更好,使用一个伪输出操作数让编译器选择一个寄存器供您弹出。或者不要触碰eax,使用andl$0xFFFFFEFF、%%esp.或用于较小的代码大小,但用于存储转发暂停,使用B$0xFE、1%esp.或btrl$9、%esp是代码大小和UOP之间的一个有用的折衷,尽管popf非常慢,所以它控制着成本,您可能不会经常这样做。@MichaelPetch:x86-64系统V ABI指定了一个红色区域。我没有意识到许多使用它的非Linux操作系统不支持红色区域,谢谢你指出这一点。我想在注释中加入一些与代码内联的内容,但没有空间容纳全部细节。如果你只需要确认你的操作系统没有使用它,并且你的代码永远不会被移植到用户空间Linux,那就没关系了。@MichaelPetch:x86-64 System V ABI指定了一个红色区域。我没有意识到许多使用它的非Linux操作系统不支持红色区域,谢谢你指出这一点。我想在注释中加入一些与代码内联的内容,但没有空间容纳全部细节。如果你只需要确认你的操作系统没有使用它,并且你的代码永远不会被移植到用户空间Linux,那就没关系了。