为什么我无法更改段寄存器的值? (MASM)

发布于 2024-07-06 12:44:21 字数 154 浏览 7 评论 0原文

我决定自学汇编语言。

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

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

我真的只对为什么在这一点上感兴趣,我没有任何改变这些地址的真正目的。

I decided to teach myself assembly language.

I have realized that my program will not compile if I attempt to change the value of any segment register.

Every article that I have found says that I can indeed change the value of at least 4 segment registers, so what gives?

I am really only interested in the why at this point, I don't have any real purpose in changing these addresses.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

说谎友 2024-07-13 12:44:21

您说您对原因感兴趣,所以:

在实模式下,段是物理内存的 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

我认为应该指出的是,该架构允许存在大量段。 但据我所知,当涉及到主流的 x86 操作系统时,段寄存器仅用于几个目的:

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

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

这是由于操作系统设计,而不是任何技术限制。 可能有一些嵌入式操作系统需要用户空间程序来使用段寄存器,但我不知道。

You said you were interested in why, so:

In real mode, a segment is a 64K "window" to physical memory and these windows are spaced 16 bytes apart. In protected mode, a segment is a window to either physical or virtual memory, whose size and location is determined by the OS, and it has many other properties, including what privilege level a process must have to access it.

From here on, everything I say refers to protected mode.

There is a table in memory called the global descriptor table (GDT), which is where the information about these window sizes and locations and other properties are kept. There may also be local descriptor tables on a per-process basis, and they work in a similar way, so I'll just focus on the GDT.

The value you load into a segment register is known as a segment selector. It is an index into the GDT or LDT, with a bit of extra security information. Naturally if a program tries to load a descriptor which is outside the bounds of the GDT, an exception occurs. Also if the process does not have enough privilege to access the segment, or something else is invalid, an exception occurs.

When an exception occurs, the kernel handles it. This sort of exception would probably be classed as a segmentation fault. So the OS kills your program.

There's one final caveat: in the x86 instruction set, you can't load immediate values into segment registers. You must use an intermediate register or a memory operand or POP into the segment register.

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

I think it should be pointed out that the architecture allows for heaps of segments. But AFAIK, when it comes to the mainstream x86 operating systems, segment registers serve only a few purposes:

  • Security mechanisms, such as keeping user space processes from harming each other or the OS
  • Dealing with multiple/multi-core processors
  • Thread-local storage: as an optimization, some operating systems (including Linux and Windows) use segment registers for thread-local storage (TLS). Since threads share the same address space, it is hard for a thread to "know" where its TLS region is without using a system call or wasting a register... but since segment registers are practically useless, there's no harm in "wasting" them for the sake of fast TLS. Note that when setting this up, an OS might skip the segment registers and write directly to descriptor cache registers, which are "hidden" registers used to cache the GDT/LDT lookups triggered by references to the segment registers, in which case if you try to read from the segment registers you won't see it.

Apart from a segment per thread for TLS, really only a handful of segments (times the number of processors) are used, and only by the OS. Application programs can completely ignore the segment registers.

This is due to OS design, not to any technical limitations. There may be embedded operating systems that require user-space programs to work with the segment registers, though I don't know of any.

蓝海似她心 2024-07-13 12:44:21

您正在编写 Windows 可执行文件吗?

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

参考

内存模型也大幅提升
与往日不同
16位世界。 在Win32下,我们不需要
关心内存模型或
不再细分了! 只有一个
内存模型:扁平内存模型。
不再有 64K 段。 这
内存是一个很大的连续空间
4GB。 这也意味着你没有
玩段寄存器。 你
可以使用任何段寄存器
寻址内存空间中的任意点。
这对程序员来说是一个很大的帮助。
这就是 Win32 汇编的原因
编程就像 C 语言一样简单。

Are you writing windows executables?

In protected-mode (Win32), segment registers are not used any more.

Reference:

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文