x86 保留 EFLAGS 位 1 == 0:这怎么会发生?
Posted
技术标签:
【中文标题】x86 保留 EFLAGS 位 1 == 0:这怎么会发生?【英文标题】:x86 reserved EFLAGS bit 1 == 0: how can this happen? 【发布时间】:2014-05-11 17:53:36 【问题描述】:我正在使用 Win32 API 来停止/启动/检查/更改线程状态。一般来说效果很好。有时它会失败,我正在尝试追查原因。
我有一个线程通过以下方式强制在其他线程上切换上下文:
thread stop
fetch processor state into windows context block
read thread registers from windows context block to my own context block
write thread registers from another context block into windows context block
restart thread
这工作得非常好......但是......很少,上下文切换似乎会失败。 (症状:我的多线程系统用奇怪的寄存器内容执行一个奇怪的地方)。
上下文控制通过以下方式完成:
if ((suspend_count=SuspendThread(WindowsThreadHandle))<0)
printf("TimeSlicer Suspend Thread failure");
...
...
Context.ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_FLOATING_POINT);
if (!GetThreadContext(WindowsThreadHandle,&Context))
printf("Context fetch failure");
...
call ContextSwap(&Context); // does the context swap
if (ResumeThread(WindowsThreadHandle)<0)
printf("Thread resume failure");
...
没有任何打印语句被执行。我的结论是,Windows 认为上下文操作都可靠地发生了。
哦,是的,我确实知道何时停止的线程不计算 [例如,在系统函数中]并且不会尝试停止/上下文切换它。我知道这一点是因为每个执行除计算之外的任何事情的线程都设置了一个特定于线程的“不要碰我”标志,而它正在执行除计算之外的任何事情。 (设备驱动程序程序员会认为这相当于“中断禁用”指令)。
所以,我想知道上下文块内容的可靠性。 我对从上下文块中提取的各种寄存器值添加了各种健全性测试;您实际上可以确定 ESP 是否正常(在 TIB 中定义的堆栈区域范围内),PC 在我期望的程序中或在系统调用中,等等。这并不奇怪。
我决定检查条件代码位 (EFLAGS) 是否被正确读出;如果这是错误的,它将导致切换任务在其状态为 恢复。因此,我添加了以下代码来验证所谓的 EFLAGS 寄存器是否包含根据英特尔参考手册 (http://en.wikipedia.org/wiki/FLAGS_register) 仅看起来像 EFLAGS 的内容。
mov eax, Context.EFlags[ebx] ; ebx points to Windows Context block
mov ecx, eax ; check that we seem to have flag bits
and ecx, 0FFFEF32Ah ; where we expect constant flag bits to be
cmp ecx, 000000202h ; expected state of constant flag bits
je @f
breakpoint ; trap if unexpected flag bit status
@@:
在我的 Win 7 AMD Phenom II X6 1090T(六核)上, 它偶尔会使用断点捕获,ECX = 0200h。在我的 Win 7 Intel i7 系统上以同样的方式失败。我会忽略这一点, 除了它暗示 EFLAGS 没有被正确存储,正如我所怀疑的那样。
根据我对 Intel(以及 AMD)参考手册的阅读,第 1 位是保留的,并且始终具有值“1”。不是我在这里看到的。
显然,MS 通过在线程停止时执行复杂的操作来填充上下文块。我希望他们准确地存储状态。该位未正确存储。 如果他们没有正确存储这个位,他们还没有存储什么?
任何解释为什么这个位的值有时可能/应该为零?
编辑:我的代码在捕获断点时转储寄存器和堆栈。 堆栈区包含上下文块作为局部变量。 EAX 和堆栈中上下文块中 EFLAGS 的正确偏移处的值都包含值 0244h。所以上下文块中的值确实是错误的。
EDIT2:我将掩码和比较值更改为
and ecx, 0FFFEF328h ; was FFEF32Ah where we expect flag bits to be
cmp ecx, 000000200h
这似乎运行可靠,没有任何抱怨。显然 Win7 没有正确处理 eflags 的第 1 位,这似乎无关紧要。
仍然对解释感兴趣,但显然这不是我偶尔的上下文切换崩溃的原因。
【问题讨论】:
+1 只是为了犯错......“勇气和勇敢”。 检查CONTEXT_CONTROL
(位0)是否设置在ContextFlags
字段中。
您是否在重新发明光纤 BTW?
Russinovich 的“Windows 内部”书籍提供了有关如何使用调试工具挖掘系统级信息的大量信息。他的 sysinternals 站点也有一个livekd
tool,让您可以在“实时系统”上执行一些有限的内核调试,而无需像通常为内核调试所做的那样在主机和目标之间设置串行、USB 或火线链接。另一种选择是使用 VMware 来宾作为内核调试目标:msdn.microsoft.com/en-us/library/windows/hardware/ff538143.aspx
您在实际的 x86 硬件上获得相同的行为吗?我肯定见过模拟器随意使用各种寄存器标志。
【参考方案1】:
Microsoft 长期以来一直在不真正使用的地方隐藏一些位。 Raymond Chen 举了很多例子,例如:使用不是字节对齐的指针的低位。
在这种情况下,Windows 可能需要将其一些线程上下文存储在现有的CONTEXT
结构中,并决定使用EFLAGS
中其他未使用的位。无论如何,你不能用那个位做任何事情,当你调用 SetThreadContext
时,Windows 会取回那个位。
【讨论】:
所以,这是一个脆弱的设计理念。在 CONTEXT 块的 EFLAGS 寄存器中的未使用位中存储对线程正确操作至关重要的未记录位。假设用户程序不会改变它。 (MS 不会对 CONTEXT 块中的任何其他寄存器或实际上由处理器更改的 EFLAG 中的任何位做出该假设:Z,O,P,N,DIR,...我知道是因为我改变了那些很多,事情似乎工作正常)。现在用户改变了那个关键位;关键功能现在必须以未记录的方式失败。如果我有一个程序员这样做,我会让他被枪杀。 ...很多人使用低位的非字节对齐指针。这至少是显而易见的(非零低位),如果记录在案,它就可以了。这似乎很不一样。 [不要误会我的意思;感谢您的反馈]。以上是关于x86 保留 EFLAGS 位 1 == 0:这怎么会发生?的主要内容,如果未能解决你的问题,请参考以下文章