Chinaunix

标题: 讨论一下 x64 体系中 long mode 下的一个 bug [打印本页]

作者: mik    时间: 2009-04-10 00:52
标题: 讨论一下 x64 体系中 long mode 下的一个 bug
本来以为对 x64 还是很了解,但要非常透彻清晰地理解,还需要每个细节都要掌握透彻。


最近发现 x64 体系上的一个 bug,是否有这么一回事,讨论一下。


  按 x64 体系的设计思路: long mode 的 compatibility 模式应该与原来的 x86 行为一致,使用原有的 32 位软件能平滑地直接在 64 位 OS 中运行。
  事实上真能做到。比如:在我的 vista x64 上就能很好的运行原来的 32 位软件。

  64 位的 OS 的核心代码(kernel)及相关核心组件必定是运行在 long mode 的 64 bit 模式下。那么64位用户程序运行在 64 bit 模式下,而原来的 32 位软件则是运行在 compatibility 模式下。


对于 long mode 下的 system descriptor 及 gate descriptor  AMD 的文档中明确说明:

1、system descriptor(LDT & TSS)仅仅在 64 bit 模式下才被扩展为 64 位的 descriptor(共 16 个字节),在 compatibility 模式下还是原来的 32 位 descriptor(共 8 个字节)。

2、gate descriptor 在 long mode 下被扩展为 64 位的 descriptor,包括 compatibility 和 64 bit 模式。

--------------------------------------------------------------------

bug : (根据最新的 AMD 文档)
  LDT、TSS 和 gate descriptor 的 type 在 long mode 模式(compatibility 和 64 bit)被修改为:64bit-LDT、64bit-TSS 以及 64bit-gate(call、interrupt/trap)。
  按 AMD 的论述:仅在 64 bit 模式下 LDT 和 TSS 才被扩展为 64 位,不包括 compatibility 模式。在这里就前后矛盾了。若:也仅在 64 bit 模式下,它们的 type 才被修改为 64 LDT 和 64 TSS,在 compatibility 模式下还是原来的 types。这样才符合设计方案。
  这不知是 AMD 文档上论述上的一个 bug,还是确实是这样。确实这样的话:这可算是一个 bug。
         
在 compatiblity 模式下是否真的被定义为 64 TSS,这是一个很重要的问题:
  64 位的 TSS 与 32 位的 TSS 相差甚远,关键的一点是:64 位的 TSS 中 stack pointer 是 64 位的,并且没有 SS selector 存在。而 32 位的 TSS 中 stack pointer 包括了 SS 与 ESP,它们是 32 位的。
  所以,当原来的 32 位软件运行在 compatiblity 下,若有代码使用 call gate 来调用高权限的例程时,而 call gate 最终却使用的是 64 位的系统例程。发生 stack 切换时应该要使用了 64 位的 TSS 。但是居然使用了 32 位的 TSS,就会产生问题。


实际上:
  这个 bug 的产生来自“仅在 64 bit 模式下 TSS 才被扩展为 64 位”。

恰恰相反:
  “在 long mode 模式(包括 compatiblity 和 64 bit)下 TSS 被扩展为 64 位”---- 这是对的。

  gate(包括 call、interrupt/trap)在 long mode 下被扩展为 64 位,TSS 也应该是而“在 long mode 下被扩展”,而不是“仅仅在 64 bit 模式下才被扩展”。TSS 应该与 gate 相配套。

  问题的产生在于:使用 call gate 调用高权限的系统例程的时侯。系统例程是属性系统软件 OS 的部件。

  在 x64 体中的 long mode 下的 gate 必须是使用 64 位的 code,所以 long mode 下 gate 是 64 位--- 这个观点正确

  在 64 位的 OS 核心运行在 64 位下,那么由 call gate 调用的系统例程应该是 64 位代码。因此,发生 stack 切换时,必须使用 64 位的 TSS 来获取 64 位的 stack pointer(RSP)。

因此:
  应该是 TSS 在 long mode 下被扩展为 64 位,而不是仅在 64 bit 才被扩展为 64 位。


-----------------------------------------------------------------------------
  在这里,我宁可当 AMD 文档论述的 bug,而不是实际上的 bug。 实际上更不要指望 Intel 的手册能说清楚,Intel 的手册论述得更差劲!


  又或者:是我的理解错误。
作者: mik    时间: 2009-04-10 14:44
主贴里,确实是我理解错误了。 收回我的这方面的言论。


“TSS 仅仅在 64 bit 模式下才被扩展为 64 位”----- 这是对的。

-----------------------------------------------------------------------

因为:
  x64 体系中设计:gate 在 long mode (包括 compatibility 和 64 bit)下扩展为 64 位。这一设计理念也是正确的。

  gate(包括 call、interrupt / trap)目的是构建系统的服务例程,如:核心的服务例程,中断服务例程等。64 位的 gate 必须指向 64 位的 code segment。
  在 64 位的 OS 中,核心的组件运行在 64 bit 模式下,包括前面提到的系统服务例程。


  在 64 位的 OS 中,当运行原来 x86 的 32 位应用软件时,32 位的软件运行在 compatibility 模式下。如果:原来运行在 compatibility 的 32 位软件中,执行调用 call-gate 来调用系统服务例程。


1、由于 call-gate 最终执行 64 位的代码。

  那么就发生了:processor 由 compatibility 模式切换到  64 bit 模式。由于 processor 已切换到 64 bit 模式,那么此时的 TSS 也被切换到 64-TSS 模式。

  由于 CPL 的改变,导致了 stack 的切换。此时的 TSS 已经切换到 64-TSS 模式,因此在 64 bit 代码下,获取了正确的 64 位 stack pointer (RSP),此时的 stack 环境是正确的。

  故“仅在 64 bit 模式下 TSS 才被扩展为 64 TSS“   这个策略是正确的。


2、 在 long mode 的 compatibility 模式下,TSS 依然是原来的 16 或 32 位 TSS。

  若运行在 compatibility 模式下的 32 位代码当中:有指令使用 TSS selector 或者 task gate 进行任务的切换。那么此时的 TSS 必须是原来的 TSS,不能是 64 位的 TSS 。

---------------------------------------------------------------
基于上述 2 个方面:在 compatibility 模式下 TSS 不能被扩展为 64 位 TSS。而 64 bit 下必须被扩展为 64 位 TSS 。



  至于,AMD 文档上面说:在 long mode(包括 compatibility 和 64 bit)下,TSS descriptory 的 types 中:32-TSS 被重新定义为 64-TSS。
  ------- 这种前后矛盾的说法,相信这是文档论述方面的错误。
作者: mik    时间: 2009-04-10 20:42

仔细再看了看,AMD/Intel 文档中自相矛盾的地方太多了。MD 的太不认真,太不负责任的。

还是小平同志说得对:实验是检验真理的唯一标准。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2