为什么不能更改段寄存器的值? (MASM)
我决定自学汇编语言。
我已经意识到,如果尝试更改任何段寄存器的值,程序将无法编译。
我发现的每篇文章都说我确实可以更改至少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设计,而不是由于任何技术限制。也许会有嵌入式操作系统要求用户空间程序与段寄存器一起使用,尽管我不知道有任何嵌入式操作系统。

