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
?
汇编器不知道在哪里@data
和msg
将在存储器结束这样产生的元数据被称为重定位(或“的修正”)在允许所述接头和操作系统填写正确的值的对象(.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,@data
或mov ax,SEG msg
无法使用指令。相反,CS=DS=ES=SS 在程序启动时使用操作系统的程序加载器选择的值。(并且在组装时不知道该值。)