汇编程序如何计算符号地址的段和偏移量?

use*_*531 5 compiler-construction assembly masm memory-segmentation x86-16

我已经学习了编译器和汇编语言,所以我想编写自己的汇编程序作为练习。但我有一些问题;

如何计算@DATA 或 OFFSET/ADDR VarA 等段的地址?

以一个简单的汇编程序为例:

    .model small
    .stack 1024
    .data
          msg db 128 dup('A')
    .code
    start:
        mov ax,@data
        mov ax,ds
        mov dx, offset msg
                           ; DS:DX points at msg
        mov ah,4ch
        int 21h            ; exit program without using msg
    end
Run Code Online (Sandbox Code Playgroud)

那么汇编器是如何计算段的段地址的@data呢?

它如何知道将什么放入立即数mov dx, offset msg

Ros*_*dge 5

汇编器不知道在哪里@datamsg将在存储器结束这样产生的元数据被称为重定位(或“的修正”)在允许所述接头和操作系统填写正确的值的对象(.OBJ)文件。

让我们看一下稍微不同的示例程序会发生什么:

.model small
.stack 1024
.data
    msg db 'Hello, World!,'$'
.code
start:
    mov ax,SEG msg
    mov ds,ax
    mov dx,OFFSET msg
    mov ah,09h
    int 21h              ; write string in DS:DX to stdout
    mov ah,4ch
    int 21h              ; exit(AL)
end start
Run Code Online (Sandbox Code Playgroud)

汇编此文件时,汇编器无法知道链接器会将示例程序定义的任何内容放在哪里。这对您来说可能很明显,但汇编程序不能假设它看到了一个完整的程序。汇编器不知道您是否会将它与其他目标文件或库链接,这可能会导致链接器放置msg在数据段开头以外的位置。

因此,当这个示例程序被汇编成一个目标文件时,汇编器会生成两个重定位记录。如果您使用 MASM 组装文件,您可以在使用 /Fl 开关生成的列表文件中看到:

 ; listing of the .obj assembler output, before linking
 0000               start:
 0000  B8 ---- R            mov ax,SEG msg
 0003  8E D8                mov ds,ax
 0005  BA 0000 R            mov dx,OFFSET msg
 0008  B4 09                mov ah,09h
Run Code Online (Sandbox Code Playgroud)

R列表的机器代码列中操作数的旁边表示它们已重定位以引用它们。当链接器从目标文件创建 MS-DOS 格式的可执行文件时,它将能够为msg. 该值是链接时间常数,因此只有 而.obj不是.exe需要对其进行重定位。

然而,链接器将无法提供msg(数据段)段的位置,因为链接器不知道 MS-DOS 将在哪里将可执行文件加载到内存中。(在现代主流操作系统下,每个进程都有自己的虚拟地址空间,实模式只有一个地址空间,程序必须与设备驱动程序和 TSR 以及操作系统本身共享。)

因此,链接器将在生成的可执行文件中放置一个重定位,告诉 MS-DOS 根据加载位置调整立即操作数。


请注意,您可能希望通过编写一个仅适用于完整程序并仅生成 .COM 可执行文件的程序来简化您的汇编程序编写练习。这样就不用担心搬家了。您的汇编程序将决定在 .COM 格式允许的单个段中放置所有内容的位置。请注意,由于 .COM 文件不支持段重定位,因此无法使用mov ax,@datamov ax,SEG msg无法使用指令。相反,CS=DS=ES=SS 在程序启动时使用操作系统的程序加载器选择的值。(并且在组装时不知道该值。)