为什么不能完全禁用分段?

Why segmentation cannot be completely disable?

根据AMD manual分段不能被禁用。 我的问题是为什么,为什么这是不可能的? 另一个问题,它说64位禁用它,这是什么意思?分段是否在 64 位模式下完全禁用?

AMD 手册:https://s7.postimg.cc/hk15o6swr/Capture.png

简介

在64位模式下,每当一个非空段selector被加载到任何一个段寄存器中时,处理器会自动在段寄存器的隐藏部分加载相应的段描述符,只是就像在 protected/compatibility 模式下一样。但是,由 DS、ES 或 SS select 编辑的段描述符 select 将被完全忽略。此外,由 FS 和 GS select编辑的段描述符的限制和属性字段 select 被忽略。

Intel Manual V3 3.4.4:

Because ES, DS, and SS segment registers are not used in 64-bit mode, their fields (base, limit, and attribute) in segment descriptor registers are ignored. Some forms of segment load instructions are also invalid (for example, LDS, POP ES). Address calculations that reference the ES, DS, or SS segments are treated as if the segment base is zero.

...

In 64-bit mode, memory accesses using FS-segment and GS-segment overrides are not checked for a runtime limit nor subjected to attribute-checking.

除此之外,假设每个段的基地址为0,长度为264。但是,由 CS、FS 或 GS​​ select编辑的部分描述符 select 仍然有效。特别是使用了各自描述符中指定的FS和GS的基地址。

Intel Manual V3 3.4.4:

When FS and GS segment overrides are used in 64-bit mode, their respective base addresses are used in the linear address calculation.

此外,还使用了CS描述符的以下字段:D(默认位)、L(64位子模式位)、AVL(OS位)、P(当前位) 、DPL(特权级位)、S(系统位)、D/C(data/code位)和 C(一致性位)。注意CS的基地址固定为0,CS、FS、GS的长度都固定为264。正如 Peter 在他的评论中指出的那样,CS 描述符的 L 和 D 位需要能够在长模式的不同子模式之间切换。 CS 的其他活跃领域也很有用。支持 FS 和 GS 的不同基地址对于线程本地存储之类的东西很有用。

Intel Manual V3 5.2.1:

Code segments continue to exist in 64-bit mode even though, for address calculations, the segment base is treated as zero. Some code-segment (CS) descriptor content (the base address and limit fields) is ignored; the remaining fields function normally (except for the readable bit in the type field).

Code segment descriptors and selectors are needed in IA-32e mode to establish the processor’s operating mode and execution privilege-level.

我认为在64位模式下可读位和访问位都被忽略了。这些属性被分页结构中的相应属性替换。尽管我在 Intel 手册中找不到任何地方说访问的位被忽略。但是AMD的手册确实说得很清楚。

仍然执行描述符 table 限制检查。

Intel Manual V3 5.3.1:

In 64-bit mode, the processor does not perform runtime limit checking on code or data segments. However, the processor does check descriptor-table limits.

因此您可以说 DS、ES 和 SS 段的分段已完全禁用。但不完全适用于其他三个细分市场。这就是 segmentation cannot be completely disabled 的意思。

英特尔手册 V2 另有说明

我引用POP指令的描述

64-Bit Mode Exceptions

#GP(0) If the memory address is in a non-canonical form.
#SS(0) If the stack address is in a non-canonical form.
#GP(selector) If the descriptor is outside the descriptor table limit.
If the FS or GS register is being loaded and the segment pointed to is not a data or readable code segment.
If the FS or GS register is being loaded and the segment pointed to is a data or nonconforming code segment, but both the RPL and the CPL are greater than the DPL.
#AC(0) If an unaligned memory reference is made while alignment checking is enabled.
#PF(fault-code) If a page fault occurs.
#NP If the FS or GS register is being loaded and the segment pointed to is marked not present.
#UD If the LOCK prefix is used.

请注意,到 DS、ES、SS 的 POP 在 64 位模式下无效,并且没有 POP CS。这就是为什么它只谈论 FS 和 GS。虽然这意味着由 FS 和 GS 编辑的描述符 select 的属性并没有被完全忽略。

同理,MOV指令的说明中说:

64-Bit Mode Exceptions

#GP(0)
If the memory address is in a non-canonical form.
If an attempt is made to load SS register with NULL segment selector when CPL = 3.
If an attempt is made to load SS register with NULL segment selector when CPL < 3 and CPL ≠ RPL.
#GP(selector)
If segment selector index is outside descriptor table limits. If the memory access to the descriptor table is non-canonical.
If the SS register is being loaded and the segment selector's RPL and the segment descriptor’s DPL are not equal to the CPL.
If the SS register is being loaded and the segment pointed to is a nonwritable data segment.
If the DS, ES, FS, or GS register is being loaded and the segment pointed to is not a data or readable code segment.
If the DS, ES, FS, or GS register is being loaded and the segment pointed to is a data or nonconforming code segment, but both the RPL and the CPL are greater than the DPL.
#SS(0) If the stack address is in a non-canonical form.
#SS(selector) If the SS register is being loaded and the segment pointed to is marked not present.
#PF(fault-code) If a page fault occurs.
#AC(0) If alignment checking is enabled and an unaligned memory reference is made while the current privilege level is 3.
#UD If attempt is made to load the CS register. If the LOCK prefix is used.

但请注意#NP 不会出现在这里!这表明当前位 (P) 仅针对 FS、GS、CS 和 SS 检查,而不针对 DS 和 ES 检查。 (但我认为所有段都检查了 P 位。)这些引用还表明还使用了 select 或任何段寄存器的 RPL 部分。

空段选择器

空段 selector 是一个 selector,其值为 0x0000、0x0001、0x0002 或 0x0003。对于处理器,所有这些值总是具有相同的效果。这些都是 select 相同的描述符,GDT 的条目 0。

空段 selector 无法在任何使用分段的模式(包括 64 位模式)中加载到 CS,因为 CS 必须始终包含实际的 selector。尝试这样做会生成 GP 异常。

空段 selector 可以在 64 位模式下加载到 SS(与其他模式相反),但仅限于某些情况。有关详细信息,请参阅英特尔手册 V3 6.15 的 "General Protection Exception (#GP)" 部分。

空段select或者可以加载到DS、ES、GS和FS中。

Intel Manual V3 5.4.1.1:

In 64-bit mode, the processor does not perform runtime checking on NULL segment selectors. The processor does not cause a #GP fault when an attempt is made to access memory where the referenced segment register has a NULL segment selector.

我觉得这很有趣,我稍后会解释。 (我也觉得很奇怪,专用于分割的第3章没有说明)。

我不太清楚处理器在用空 selector.

加载时是否将空描述符从内存加载到段寄存器的不可见部分

Intel Manual V3 3.4.2:

The first entry of the GDT is not used by the processor.

这是否意味着处理器不会加载空描述符?或者它可能仅意味着未使用描述符的内容。后来它在 3.4.4 中说:

In order to set up compatibility mode for an application, segment-load instructions (MOV to Sreg, POP Sreg) work normally in 64-bit mode. An entry is read from the system descriptor table (GDT or LDT) and is loaded in the hidden portion of the segment register. The descriptor-register base, limit, and attribute fields are all loaded. However, the contents of the data and stack segment selector and the descriptor registers are ignored.

英特尔手册 V2 中 POP 指令的描述是这样的:

64-BIT_MODE

IF FS, or GS is loaded with a NULL selector;
THEN
SegmentRegister ← segment selector;
SegmentRegister ← segment descriptor;
FI;

英特尔手册 V2 中对 MOV 指令的描述说:

IF DS, ES, FS, or GS is loaded with NULL selector
THEN
SegmentRegister ← segment selector;
SegmentRegister ← segment descriptor;
FI;

这表明空描述符确实被加载了,但它的内容被忽略了。 Linux 内核将空描述符定义为所有位都为零。我在许多文章和教科书中都读到这是强制性的。然而,柯林斯 says 认为这是没有必要的:

The first entry in the Global Descriptor Table (GDT) is called the null descriptor. The NULL descriptor is unique to the GDT, as it has a TI=0, and INDEX=0. Most printed documentation states that this descriptor table entry must be 0. Even Intel is somewhat ambiguous on this subject, never saying what it CAN'T be used for. Intel does state that the 0'th descriptor table entry is never referenced by the processor.

据我所知,英特尔不对空描述符的内容施加任何限制。所以我想柯林斯是对的。

为什么 5.4.1.1 很有趣?

因为这意味着可以在 64 位模式下使用 DS、ES、GS 和 GS 来保存任何常量 0x0000、0x0001、0x0002 或 0x0003。保证 GDT 至少包含空描述符,因此描述符 table 限制检查将通过(这可能不适用于其他 select 或)。此外,对任何这些段的所有引用仍将成功执行。 MOV 指令可用于将值从段寄存器移动到 GPR,然后对其执行操作。

AMD 手册

待写。