关于操作系统:汇编中的标签如何知道它的运行时地址?

how can a label in assembly know its runtime address?

我正在学习汇编(z80 和 x86),现在开始掌握如何使用汇编器构建二进制文件。

如何使用带有绝对(相对于相对)地址的标签?

据我了解,汇编程序会在编译时将标签转换为内存地址,但是汇编程序如何知道标签在运行时将驻留在哪个地址?

对于 z80 裸机系统来说,这似乎很简单,因为您可以将程序加载到特定的内存地址并发送 RST 信号,将程序计数器指向 0000h。当操作系统运行时会发生什么?

在操作系统中执行的代码不会不知道它的起始地址(因此无法在标签上使用像 callret 这样的非相对操作码)?


如果机器码不是位置无关的,有两种常用的策略:

  • 在可执行文件中包含附加信息(AKA 重定位信息),告诉操作系统需要调整的绝对地址在哪里

  • 只需将可执行文件加载到它想要的位置,这意味着您可能需要先驱逐另一个可执行文件,或者您需要为每个可执行文件提供单独的地址空间,因此一开始就无需为正确的位置而战


CP/M 的解决方案可能值得一读,它很简单:二进制文件总是在一个固定地址加载,操作系统入口点总是在另一个固定地址。这在 8 位机器上是相当典型的,即使是那些具有正式操作系统的机器,并且被带到 MS-DOS 中。使用 MMU 的多任务操作系统在技术上也是可行的,因为每个进程都有自己的地址空间,因此每个二进制文件都可以认为它是在同一个地方加载的。

使用可重定位代码之间的生成。要么它与位置无关,因为 CPU 很容易支持(根据经典 Mac OS 和 68000 的相对于 PC 的寻址),要么实际上是在加载二进制文件时发生经典两遍汇编程序的第二遍。因此,二进制文件是编译后的代码,其中包含所有绝对地址的占位符以及这些占位符所在位置的列表,以便在知道实际地址后可以替换它们。

唯一的问题是它会阻止快速虚拟内存。使用非 MMU Mac OS 方法,程序被编译为 16kb 块,每个块内的跳转直接发生,远程跳转通过寻呼机进行。如果目标块已加载,则关闭它,如果没有,则加载它,然后发生跳转。如果需要在每次加载时计算和填写地址,这种按需加载是令人望而却步的。


我猜,您的程序将小于 64 kb。在这种情况下,程序只需知道标签的偏移量(称为近跳转)。操作系统每次都在相同的 OFFSET 处启动程序,但在另一个段。条件跳转和"jmp short"仅使用 jmp 命令和标签之间的区别。在某些特殊情况下,例如如果一个过程在执行之前存储到堆栈中,编译器会插入一个代码来更改 jmp 命令的参数。


汇编器使用偏移量。

1
2
3
4
LABEL
     . . . . .

     JMP LABEL // Knows the number of bytes to label. SO label can be anywhere.