为什么不能更改段寄存器的值? (MASM)

时间:2020-03-06 14:38:37  来源:igfitidea点击:

我决定自学汇编语言。

我已经意识到,如果尝试更改任何段寄存器的值,程序将无法编译。

我发现的每篇文章都说我确实可以更改至少4个段寄存器的值,那有什么用呢?

我真的只对为什么在这一点上感兴趣,为什么在更改这些地址方面没有任何实际目的。

解决方案

我们在编写Windows可执行文件吗?

在保护模式(Win32)中,不再使用段寄存器。

参考:

Memory model is also drastically
  different from the old days of the
  16-bit world. Under Win32, we need not
  be concerned with memory model or
  segment anymore! There's only one
  memory model: Flat memory model.
  There's no more 64K segments. The
  memory is a large continuous space of
  4 GB. That also means you don't have
  to play with segment registers. You
  can use any segment register to
  address any point in the memory space.
  That's a GREAT help to programmers.
  This is what makes Win32 assembly
  programming as easy as C.

我们说我们对为什么感兴趣,所以:

在实模式下,段是物理内存的64K"窗口",这些窗口间隔16个字节。在保护模式下,段是物理或者虚拟内存的窗口,其大小和位置由操作系统确定,并且具有许多其他属性,包括进程访问该进程必须具有的特权级别。

从这里开始,我所说的一切都指保护模式。

内存中有一个称为全局描述符表(GDT)的表,该表保留了有关这些窗口大小和位置以及其他属性的信息。在每个进程的基础上可能还会有局部描述符表,它们的工作方式类似,因此我将重点介绍GDT。

我们加载到段寄存器中的值称为段选择器。它是GDT或者LDT的索引,并带有一些额外的安全性信息。自然,如果程序尝试加载GDT范围之外的描述符,则会发生异常。同样,如果该进程没有足够的特权来访问该段,或者其他无效,则会发生异常。

发生异常时,内核将对其进行处理。这种异常可能被归类为细分错误。因此,操作系统会杀死程序。

最后一个警告:在x86指令集中,我们不能将立即值加载到段寄存器中。我们必须在段寄存器中使用中间寄存器或者内存操作数或者POP。

MOV DS, 160  ;INVALID - won't assemble

MOV AX, 160  ;VALID - assembles, but will probably result in an
MOV DS, AX   ;exception, and thus the death of your program

我认为应该指出,该体系结构允许段的堆。但是AFAIK在主流的x86操作系统上,段寄存器仅用于以下几个目的:

  • 安全机制,例如防止用户空间进程相互伤害或者操作系统
  • 处理多/多核处理器
  • 线程本地存储:作为一种优化,某些操作系统(包括Linux和Windows)将段寄存器用于线程本地存储(TLS)。由于线程共享相同的地址空间,因此线程很难在不使用系统调用或者浪费寄存器的情况下"知道"其TLS区域的位置...但是由于段寄存器实际上是无用的,因此"浪费"没有任何危害为了快速TLS而使用它们。请注意,进行此设置时,操作系统可能会跳过段寄存器并直接写入描述符缓存寄存器,这是"隐藏"寄存器,用于缓存由对段寄存器的引用触发的GDT / LDT查找从段寄存器中读取我们不会看到它。

除了用于TLS的每个线程一个段之外,实际上仅使用了少数段(乘以处理器数量),并且仅由OS使用。应用程序可以完全忽略段寄存器。

这是由于OS设计,而不是由于任何技术限制。也许会有嵌入式操作系统要求用户空间程序与段寄存器一起使用,尽管我不知道有任何嵌入式操作系统。