为什么有两个重叠的数据段(例如在 Linux 内核中)?
Why have two overlapping data segments (e.g. in the Linux kernel)?
在Linux内核中,以及网上很多x86教程中,我看到有人推荐使用两个代码段和两个数据段。我理解需要两个代码段,因为 CPL 需要与 DPL 完全匹配(对于不一致的段)。
然而,none 这些教程(也不是 Whosebug 上的任何相关问题)明确说明了为什么我们需要两个数据段。它们与代码段的工作方式不同,因为 CPL=0 的进程可以访问 DPL=3 的数据段。
拥有两个数据段的缺点是,如果我们在不同特权级别的进程之间切换,则必须重新加载 DS、ES 等寄存器。
所以我的具体问题是:鉴于我们使用的是平面内存模型,因此所有代码和段都完全重叠,拥有一个用户和一个内核数据段有什么用,而不是只有一个用户数据段?
有解释here。
引用英特尔手册(第 5.7 节)
Privilege level checking also occurs when the SS register is loaded with the segment selector for a stack segment.
Here all privilege levels related to the stack segment must match the CPL; that is, the CPL, the RPL of the stacksegment selector, and the DPL of the stack-segment descriptor must be the same. If the RPL and DPL are not equal
to the CPL, a general-protection exception (#GP) is generated.
强调我的
也就是说,SS
需要一个数据段 DPL 从内核加载时(或切换期间)等于 0。
这适用于 32 位模式。
在 64 位模式下,it is possible to use a NULL selector 禁止任何运行时检查(包括前一个)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.
出于完整性考虑,执行堆栈操作时的所有相关信息,地址大小、操作数大小和堆栈地址大小,要么从代码段中恢复,要么隐式设置为 64 位。
1 如果我没记错的话,出于兼容性原因,64 位模式仍然使用内核数据段。
在Linux内核中,以及网上很多x86教程中,我看到有人推荐使用两个代码段和两个数据段。我理解需要两个代码段,因为 CPL 需要与 DPL 完全匹配(对于不一致的段)。
然而,none 这些教程(也不是 Whosebug 上的任何相关问题)明确说明了为什么我们需要两个数据段。它们与代码段的工作方式不同,因为 CPL=0 的进程可以访问 DPL=3 的数据段。
拥有两个数据段的缺点是,如果我们在不同特权级别的进程之间切换,则必须重新加载 DS、ES 等寄存器。
所以我的具体问题是:鉴于我们使用的是平面内存模型,因此所有代码和段都完全重叠,拥有一个用户和一个内核数据段有什么用,而不是只有一个用户数据段?
有解释here。
引用英特尔手册(第 5.7 节)
Privilege level checking also occurs when the SS register is loaded with the segment selector for a stack segment.
Here all privilege levels related to the stack segment must match the CPL; that is, the CPL, the RPL of the stacksegment selector, and the DPL of the stack-segment descriptor must be the same. If the RPL and DPL are not equal to the CPL, a general-protection exception (#GP) is generated.
强调我的
也就是说,SS
需要一个数据段 DPL 从内核加载时(或切换期间)等于 0。
这适用于 32 位模式。
在 64 位模式下,it is possible to use a NULL selector 禁止任何运行时检查(包括前一个)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.
出于完整性考虑,执行堆栈操作时的所有相关信息,地址大小、操作数大小和堆栈地址大小,要么从代码段中恢复,要么隐式设置为 64 位。
1 如果我没记错的话,出于兼容性原因,64 位模式仍然使用内核数据段。