16位汇编程序(x86)中用于数据段的32位语法字

时间:2017-10-04 07:34:30

标签: assembly x86

在16位x86汇编中,您会写:

mov ax, @DATA
mov ds, ax

32位的extact代码转换如何? 我不知道32位的数据段。

mov eax, @DATA
mov ?  , eax

感谢您的回答!

2 个答案:

答案 0 :(得分:5)

简单的答案

32位代码看起来与16位代码完全相同:

mov ax, @DATA
mov ds, ax

...因为选择器寄存器(csdsesssfsgs)仍然只是32位代码中的16位宽。因此,“段值”也是16位宽,并且通用寄存器的低16位(例如ax)尚未重命名。

更复杂的答案

只有少数目标文件格式支持32位代码段的选择器!

汇编程序将拒绝行mov ax, @DATA,因为根本无法在目标文件中表示此行!

大多数32位操作系统使用“平面”内存布局。这意味着csdsesss指向物理地址0并且具有4GiB的限制。换句话说:可以直接寻址整个存储器,而无需更改选择器寄存器的值。

因此,大多数目标文件格式甚至不支持此功能。

很少有操作系统真正使用32位代码中的选择器寄存器。对于此类系统,您必须使用支持此目标文件格式的开发软件(汇编程序,编译器,链接器......)!

如果您有这样的软件(并且您使用这样的操作系统),则代码与16位代码相同(如上所示)。

修改

阅读完评论后,我想澄清以下句子:

  

很少有操作系统真正使用32位代码中的选择器寄存器......

这里的意思是:只有少数32位操作系统使用不同的值来选择寄存器来访问可执行文件中的不同部分(代码,数据,const,BSS ......)。 (然而,选择器寄存器中的不同值还有其他用途。)

大多数操作系统对可执行文件中的所有段/节使用相同的选择器值,这些操作系统已将选择器寄存器初始化为正确的值。因此,mov ax, @DATA指令并不真正有意义。与指令mov ax, ds(在这些条件下具有相同效果)不同,指令mov ax, @DATA还需要目标文件格式中的特殊功能(特殊的“重定位”),因为这样就不会实现一条指令毫无意义。

然而,很少有32位操作系统使用“平面”内存布局,但对程序中的不同数据段使用不同的选择器值。当然,这样的操作系统必须使用不同的目标文件格式。对于此类操作系统,肯定支持指令mov ax, @DATA。但是我怀疑是否有汇编器允许将选择器值分配给32位寄存器(mov eax, @DATA)。

答案 1 :(得分:2)

(您通常只在内核模式下执行此操作,作为保存/恢复用户空间上下文的一部分。在具有平坦内存的普通操作系统下的32位或64位用户空间中,使用{{1}修改wrfsbase的基地址以进行线程本地存储。或等效于fs。这些指令需要FSGSBASE CPU功能。)

英特尔建议编写gs以避免在32位或64位模式的操作数大小前缀上浪费空间。但是,NASM和YASM会为您进行此优化。 @Cody报告MASM不会收集mov ds, eaxmov fs, eax。 GNU汇编程序与NASM / YASM的作用相匹配。

mov eax, fs

走向另一个方向很重要

                    ;  NASM/YASM machine code output
mov   fs, ax        ;    8e e0
mov   fs, eax       ;    8e e0
mov   fs, rax       ;    8e e0
mov   fs, r10d      ; 41 8e e2 (rex.w=0)

但请注意, mov ax, fs ; 66 8c e0 only modifies AX, leaving upper bits mov eax, fs ; 8c e0 zeros whole rax (on P6 and later CPUs, undefined upper bytes on earlier) mov rax, fs ; 8c e0 zeros whole rax (YASM: 48 8c e0) mov r10d, fs ; 41 8c e2 (rex.w=0) mov r10, fs ; 49 8c e2 (rex.w=1) / mov eax, fs往返行程仍然总是安全的,即使在旧的CPU上可能会留下高垃圾(见下文),因为mov fs, eax忽略了高2个字节。

Intel's vol. 2 manual (instruction-set reference)mov fs, eax条目说明有关reg,Sreg或Sreg的注册表格:

  

在32位模式下操作并在段寄存器和之间移动数据时   一个通用寄存器,32位IA-32处理器不需要   使用16位操作数大小前缀(值为66H的字节)   指令,但大多数汇编程序将插入它,如果标准形式的   使用指令(例如,MOV DS,AX)。 [...]

     

当处理器执行32位通用指令时   寄存器,它假设16个最低有效位   通用寄存器是目标或源操作数。

这是一种令人困惑的方式,可以说段记录值本身是从低16中读取或存储的。

令人困惑,因为它似乎(错误地)暗示GP寄存器的高16b不是目的地的一部分。然后他们仍然只讨论32位(和64位)目标寄存器,而不是一般的GP目标寄存器。

  

如果寄存器是目标操作数,则结果值为2   寄存器的高位字节取决于实现。    对于Pentium 4,Intel Xeon和P6系列处理器,   两个高位字节用零填充;   对于早期的32位IA-32处理器,两个高位字节是未定义的。

请注意,在旧的CPU(如P5 Pentium和更早版本)上,mov可能零延伸,并且可能会留下垃圾。但mov eax, fs始终是安全的。

对于内存目的地,即使您使用REX.W = 1(NASM将对其进行编码),它总是 16位加载或存储使用mov fs, eax大小覆盖,但YASM会阻塞它。)

英特尔的手册中说qword形式,但这是虚假的; 零扩展到64位,带有内存目的地。

最近提出in a clang assembler bug关于使用MOV r/m64,Sreg操作数大小前缀(原来是多余的)来汇编movw %fs, (%rsi)。上面的一些内容是根据我在那里写的内容进行复制/粘贴的。

我通过使用调试器观察寄存器和内存进行调查,同时单步执行此NASM程序:

66