【问题标题】:What is the purpose of segment registers in x86 protected mode?x86保护模式下段寄存器的目的是什么?
【发布时间】:2013-11-22 20:28:28
【问题描述】:

我需要修改一些 dll,但我不知道,段寄存器 (DS, SS, ...) 在保护模式下有什么作用。我在学校学习了真正的 16 位模式,其中段寄存器乘以 16 加上普通寄存器中的偏移量给出物理内存中的有效地址。在保护模式下,有一些平面内存模型和虚拟内存,其中每个进程“拥有”4GB 内存,所以如果寄存器有 32 位,那么我只能通过“偏移”寄存器来寻址虚拟内存的每个字节。那么哪些puproses在保护模式下有段寄存器,例如

mov eax, dword ptr ds:[20037DA0] 

【问题讨论】:

  • 您使用的是哪个反汇编程序?也许它默认总是根据操作码推断和显示段寄存器,即使内存没有分段。
  • @Michael:我正在使用 ollydbg
  • 内存仍然是分段的,尽管最流行的操作系统只使用一个段并将所有段寄存器设置为相同的值。
  • 是 Win32 DLL 还是 Win16 dll? Win32 使用平面内存,几乎忽略了段寄存器。寄存器名称eax 有点暗示Win32。
  • 这个问题似乎离题了,因为它不是一个实际的编程问题。它看起来像是一个更适合 cs.stackexchange.com 的理论概念问题。

标签: assembly x86 cpu-registers memory-segmentation


【解决方案1】:

一些历史背景

8086 总是使用每个段的固定 64KiB 窗口,其起始地址由(段寄存器 * 16)计算。由于 80286 内存中有一些特殊的表(GDT 和 LDT)。这些表包含段的起始地址、长度和访问权限。段寄存器(CS、DS、ES、SS - 以及自 80386 起:FS、GS)包含这些表的索引。

所以理论上操作系统可以按照它想要的方式设置段的偏移量和长度:在 8086 上 DS=0x0123 意味着:段是从地址 0x01230 开始的 64KiB。在 32 位模式下,DS=0x0123 可能意味着:段从地址 0xABCD 开始,长度为 0xEF 字节 - 这取决于操作系统创建的 GDT 和 LDT 表的内容。尝试访问超出此范围的段(如果长度

现状

但是,大多数现代 32 位操作系统不再真正使用段寄存器。由于访问权限问题,它们的值是根据模式(内核或用户)设置的。起始地址一般为0,长度为4GiB。

真正的内存保护是使用 MMU 完成的,因此在用户模式下无法访问某些内存区域。在现代操作系统中,MMU 绝对是必不可少的。它将“绝对”虚拟地址映射到真实物理地址,检查访问权限是否违规。

有一个例外:某些操作系统(例如 Windows 和 Linux)使用 FS 和/或 GS 段来真正指向不同的内存区域。

因此,在 64 位模式下,x86 处理器仅将 CS 寄存器用于访问权限问题,而 FS 和 GS 可用于为每个地址添加偏移量。据我所知,不使用 DS、ES 和 SS,而寄存器 FS 和 GS 的内容无关紧要,但是有一些特殊的寄存器明确给出了要添加到使用 FS 或 GS​​ 的操作的偏移量。

【讨论】:

  • FS 用于 Windows 上的异常处理。堆栈帧保存在那里,因此异常处理程序可以找到它的返回方式。
  • @Devolus 这不仅仅是异常处理。更一般地说,FS 指向TIB
  • 在 64 位模式下,cs、ds、es 和 ss 不再存在。只有 fs 和 gs 可以。
  • @Nubok 在 64 位模式下 CS 和 SS 仍然存在(我不确定 DS 和 ES)但它们的唯一目的是管理访问权限:例如使用 CS 的 CPL检查 CPU 是在 Ring 0 还是 Ring 3 模式下运行。
【解决方案2】:

基本上目的与实模式相同,只是它们的工作方式略有不同。您的示例中的 DS 在您的 GDT 中选择一个内存描述符(如果您真的想了解这一点,请谷歌这个术语,“全局描述符表”),其中包含基地址、结束地址、粒度等信息。然后将您的偏移量添加到基地址, 结束。如果您在 Windows 上(我打赌在 linux 上也一样),您通常不必担心这些段寄存器,正如您所说的平面模型,这意味着所有内存应该只有一个描述符,所以如果你不改变这些寄存器应该像它们不存在一样工作。

【讨论】:

  • 实模式和保护模式在段寄存器的使用上有一些重要的区别需要考虑。例如,在保护模式下使用ss: 段覆盖可能会产生分段错误,因此除了espebp 之外,不能使用任何其他寄存器作为堆栈寻址中的基址寄存器。在实模式下,您可以使用 ss: 段覆盖来使用所需的任何寄存器寻址堆栈。
  • 嗯,有效点,但为了读/写堆栈的目的,我相信你也可以使用 ds:offset ,同时拥有你想要的任何寄存器作为偏移量。当然为堆栈提供了偏移点。
  • @nrz 虽然ds/es.base 肯定与ss.base 不同(这是您不能互换使用这些段的最基本原因),但在 32- 中这将是一个非常罕见的设置位保护模式,并且可能需要额外的指令来传递 C/C++ 中局部变量的地址(您需要考虑段基数差异,因此可以通过调用函数中的 ds 访问局部变量,就像全局变量)。
  • @AlexeyFrunze 感谢您提供有关 32 位保护模式的信息,了解这些信息很有用。
【解决方案3】:

我会给你一个简单的答案,但要了解更多信息,我推荐下面的链接到 AMD 的架构文档,非常容易阅读。 PS:这里我没有介绍 Xeon 或 PAE..

IA-32 (x86) 架构具有用于 RAM 的 32 位物理地址总线。

32 位总线进一步分为 2 个 16 位段,每个段能够访问 2GB 的 RAM,总共 4GB。
这称为存储库切换。

为了提供保护,Intel 和 MS 决定将一个段用于内核模式,另一个用于用户模式 ​​- 这就是 Windows 历史上拥有 2GB 用户模式地址空间的原因。这是 x86 硬件限制,而不是 Windows 限制。

段寄存器分隔内核空间和用户空间地址。 这就是内存保护的实现方式。

此外,IA-32 通用也有 32 位内部寄存器,所以它不能分页。这是实模式(无地址转换)。

分页需要 36 位我认为(不要引用我的话)这就是 IA32e 的用武之地。IA-32e 上的额外位允许从 HDD 分页,这是它可以在 x64 Windows 上运行的唯一方式,因为 x64 需要 NX 和它位于第 63 位。

请阅读 AMD 架构文档,我个人认为它们比 Intel 版本提供更多信息。

http://developer.amd.com/wordpress/media/2012/10/24593_APM_v21.pdf

引入了带有 AMD64 平面内存的 PS,取消了分段。

但是 32 位进程仍然需要段寄存器。 在 AMD64 上,当 32 位进程到达栈顶时,会抛出一个指向新段寄存器的基地址的指针。这样 32 位应用程序可以有效地消耗尽可能多的 RAM,没有限制。完全符合c...:)

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-02
    • 2016-11-05
    • 2023-03-23
    • 2020-12-24
    • 2018-02-09
    • 2013-04-21
    相关资源
    最近更新 更多