Kee*_*ate 3 x86 x86-64 processor cpu-architecture memory-segmentation
根据AMD manual细分不能禁用。我的问题是为什么,为什么不可能?另一个问题是说64位将其禁用,这是什么意思?在64位模式下完全禁用分段功能吗?
在64位模式下,每当将非空段选择器加载到任何段寄存器中时,处理器都会自动将相应的段描述符加载到段寄存器的隐藏部分中,就像在保护/兼容模式下一样。但是,完全忽略了DS,ES或SS选择器选择的段描述符。FS和GS选择器选择的段描述符的限制和属性字段也将被忽略。
英特尔手册V3 3.4.4:
由于ES,DS和SS段寄存器未在64位模式下使用,因此段描述符寄存器中的它们的字段(基数,限制和属性)将被忽略。某些形式的段加载指令也无效(例如,LDS,POP ES)。引用ES,DS或SS段的地址计算将视为段基为零。
...
在64位模式下,不检查使用FS段和GS段替代的内存访问的运行时限制,也不会进行属性检查。
除此之外,假设每个段的基地址为0,长度为2 64。但是,由CS,FS或GS选择器选择的段描述符的某些部分仍然有效。特别是,使用在它们各自的描述符中指定的FS和GS的基地址。
英特尔手册V3 3.4.4:
在64位模式下使用FS和GS段替代时,它们各自的基址将用于线性地址计算。
此外,还使用了CS描述符的以下字段:D(默认位),L(64位子模式位),AVL(OS位),P(当前位),DPL(特权级位),S (系统位),D / C(数据/代码位)和C(合格位)。注意,CS的基地址固定为0,CS,FS和GS的长度都固定为2 64。正如彼得在其评论中指出的那样,要求CS描述符的L和D位能够在长模式的不同子模式之间进行切换。CS的其他活动字段也很有用。支持FS和GS的不同基址对于诸如线程本地存储之类的事情很有用。
英特尔手册V3 5.2.1:
即使对于地址计算,代码段基址被视为零,代码段仍继续以64位模式存在。一些代码段(CS)描述符内容(基地址和限制字段)将被忽略;其余字段正常工作(类型字段中的可读位除外)。
在IA-32e模式下,需要代码段描述符和选择器来建立处理器的操作模式和执行特权级别。
我认为在64位模式下,可读位和访问位都将被忽略。这些属性被分页结构中的相应属性替换。尽管我在Intel手册中找不到任何地方说访问的位被忽略。但是,AMD手册确实明确指出了这一点。
描述符表限制检查仍在执行。
英特尔手册V3 5.3.1:
在64位模式下,处理器不对代码或数据段执行运行时限制检查。但是,处理器会检查描述符表限制。
因此,您可以说DS,ES和SS细分完全禁用了细分。但对于其他三个细分市场而言却不完全相同。那是什么segmentation cannot be completely disabled意思
我引用POP指令的描述。
64位模式异常
#GP(0)如果内存地址为非规范形式。
#SS(0)如果堆栈地址为非规范形式。
#GP(selector)如果描述符超出描述符表限制。
如果正在加载FS或GS寄存器,并且指向的段不是数据段或可读代码段。
如果正在加载FS或GS寄存器,并且指向的段是数据段或不合格代码段,但RPL和CPL都大于DPL。
#AC(0)如果在启用对齐检查时进行了未对齐的内存引用。
#PF(故障代码)如果发生页面错误。
#NP 如果正在加载FS或GS寄存器且指向的段标记为不存在。
#UD如果使用LOCK前缀。
请注意,到DS,ES,SS的POP在64位模式下无效,并且没有POP CS。这就是为什么只谈论FS和GS的原因。尽管这意味着FS和GS选择的描述符的属性没有被完全忽略。
同样,MOV指令的描述为:
64位模式异常
#GP(0)
如果内存地址为非规范形式。
如果在CPL = 3时
尝试使用NULL段选择器加载SS寄存器,如果在CPL <3且CPL?RPL。
#GP(selector)
如果段选择器索引超出描述符表限制。如果对描述符表的内存访问是非规范的。
如果正在加载SS寄存器,并且段选择器的RPL和段描述符的DPL不等于CPL。
如果正在加载SS寄存器,并且指向的段是不可写的数据段。
如果正在加载DS,ES,FS或GS寄存器,并且指向的段不是数据段或可读代码段。
如果正在加载DS,ES,FS或GS寄存器,并且指向的段是数据段或不合格代码段,但RPL和CPL都大于DPL。
#SS(0)如果堆栈地址为非规范形式。
#SS(selector)如果正在加载SS寄存器并且指向的段标记为不存在。
#PF(故障代码)如果发生页面错误。
#AC(0)如果启用了对齐检查,并且在当前特权级别为3时进行了未对齐的内存引用。
#UD如果尝试加载CS寄存器。如果使用LOCK前缀。
但是请注意,此处不会出现#NP!这表明只检查FS,GS,CS和SS的当前位(P),而不检查DS和ES。(但是我认为所有段都检查了P位。)这些引号也表明,也使用了任何段寄存器的选择器的RPL部分。
空段选择器是值为0x0000、0x0001、0x0002或0x0003的选择器。对于处理器而言,所有这些值始终具有相同的效果。这些都选择相同的描述符,即GDT的条目0。
空段选择器不能以任何使用分段的模式(包括64位模式)加载到CS中,因为CS必须始终包含一个实际的选择器。尝试这样做会生成GP异常。
可以将空段选择器以64位模式(与其他模式相比)加载到SS中,但仅在某些情况下才可以。有关更多信息,请参阅英特尔手册V3 6.15的“常规保护异常(#GP)”部分。
空段选择器可以加载到DS,ES,GS和FS中。
英特尔手册V3 5.4.1.1:
在64位模式下,处理器不对NULL段选择器执行运行时检查。尝试访问引用的段寄存器具有NULL段选择器的内存时,处理器不会导致#GP错误。
我觉得这很有趣,我将在后面解释。(我也发现,专门用于细分的第3章没有声明这一点很奇怪)。
对我来说,这还不是很清楚,当用null选择器加载时,处理器是否将内存中的null描述符加载到段寄存器的不可见部分。
英特尔手册V3 3.4.2:
GDT的第一个条目未被处理器使用。
这是否意味着处理器将不会加载空描述符?也许这仅意味着不使用描述符的内容。后来在3.4.4中说:
为了设置应用程序的兼容模式,段加载指令(MOV到Sreg,POP Sreg)在64位模式下正常工作。从系统描述符表(GDT或LDT)中读取一个条目,并将其加载到段寄存器的隐藏部分中。描述符寄存器的基数,限制和属性字段均已加载。但是,数据和堆栈段选择器以及描述符寄存器的内容将被忽略。
英特尔手册V2中的POP指令说明如下:
64位模式
如果FS或GS加载了NULL选择器;
然后
SegmentRegister?段选择器;
SegmentRegister?段描述符;
FI;
英特尔手册V2中的MOV指令说明如下:
如果DS,ES,FS或GS加载了NULL选择器,
那么
SegmentRegister?段选择器;
SegmentRegister?段描述符;
FI;
这表明null描述符实际上确实已加载,但是其内容将被忽略。Linux内核将空描述符定义为所有位为零。我在许多文章和教科书中都读到,这是强制性的。但是,柯林斯说这不是必须的:
全局描述符表(GDT)中的第一个条目称为空描述符。NULL描述符对于GDT而言是唯一的,因为它具有TI = 0和INDEX = 0。大多数印刷文档指出,该描述符表条目必须为0。即使Intel在此主题上也有些模棱两可,从来没有说过它不能用作什么。英特尔确实声明处理器永远不会引用第0个描述符表条目。
AFAIK,Intel对空描述符的内容不加任何限制。所以我想柯林斯是对的。
因为这意味着可以在64位模式下使用DS,ES,GS和GS来保存常数0x0000、0x0001、0x0002或0x0003。确保GDT至少包含空描述符,因此描述符表限制检查将通过(对于其他选择器可能不正确)。此外,对任何这些段的所有引用仍将成功执行。MOV指令可用于将值从段寄存器移至GPR,然后对其执行操作。
待写。