x86_64在64位模式汇编指令的立即数扩展问题

问题介绍

该问题源自笔者在阅读《汇编语言——基于x86处理器》(基普·R.欧文 著 吴为民 译)(以下简称x86)一书时原作者提到的一个问题,也即在64位模式下处理32位立即数时存在的扩展模式不同的问题。
本文使用的是原书第8版的译本,对应页码以此书为准。
在该书第161页6.1.10 64位模式下的布尔指令,作者给出了如下一段程序

.data
    allones QWORD 0FFFFFFFFFFFFFFFFh
.code
    mov rax, allones						; RAX = FFFFFFFFFFFFFFFFh
    and rax, 80h							; RAX = 0000000000000080h
    mov rax, allones						; RAX = FFFFFFFFFFFFFFFFh
    and rax, 8080h							; RAX = 0000000000008080h
    mov rax allones							; RAX = FFFFFFFFFFFFFFFFh
    and rax, 808080h						; RAX = 0000000000808080h
    mov rax allones							; RAX = FFFFFFFFFFFFFFFFh
    and rax, 80808080h						; RAX = FFFFFFFF80808080h
    

可以看到,此处作者对32位进行and运算时,没有默认将高位看作全0,而是只对低32位进行了运算。当源操作数为32位以下的立即数时,通过0扩展将其扩充为32位。

奇怪的现象还出现在105页4.6.1 MOV指令,作者给出一系列程序

mov rax, 0ABCDEF0AFFFFFFFFh
mov rax, 0FFFFFFFFh							; RAX = 00000000FFFFFFFFh
mov rax, 06666h								; RAX = 0000000000006666h
mov rax, 055h								; RAX = 0000000000000055h

.data
    myDword DWORD 80000000h
.code
    mov rax, 0FFFFFFFFFFFFFFFFh
    mov eax, myDword

.data
	myByte BYTE 55h
	myWord WORD 6666h
.code
	mov ax, myWord							; RAX = 0ABCDEF0AFFFF6666h
	mov al, myByte							; RAX = 0ABCDEF0AFFFFFF55h

可以看到,mov操作和and操作对不同长度的立即数处理策略是不一致的。尤其以32位为甚。

问题分析

可以看到,上述提到的指令在处理立即数时反应并不一致,当and指令处理立即数时,存在32位以下和32位两种不同的情况,当mov指令处理立即数时,对立即数均进行0扩展。

mov指令的情况

当执行mov指令时,如果源操作数为立即数,而目的操作数为寄存器时,会首先进行size比较,如果立即数的size比目的寄存器的size大,汇编将不通过。可以尝试下面的指令:

mov eax, 1FFFFFFFFh

当立即数的size比目的寄存器的size小时,会将立即数扩展到与目的寄存器尺寸一致,此时为了保证扩展是安全的,自然会使用零扩展。且未使用的地址默认为0也符合我们的常识。
同时,由于进行了size对齐,对64位寄存器也支持超过32位的立即数,可以通过下列指令看出:

mov rax, 1FFFFFFFFh

上述指令是可以通过汇编且正确执行的。

and指令的情况

此处虽然只举出了and指令的例子,但并不仅限于and指令,背后突出的是一系列的算数运算指令。
当and指令处理上述立即数时,就会遇到无法扩展的情况——当and指令的源操作数大小超过32位时,汇编过程不通过,会报错constant value too large。也即上述mov指令对应的and版本无法通过汇编:

and rax, 1FFFFFFFFh

这就很有趣了,对超过32位的立即数无法进行扩展,甚至会导致汇编无法通过。

问题猜想

如果问题在于x64的汇编要求的立即数最大为32位,所以当立即数超过32位时,就会报错 constant value too large. 同样的当立即数小于32位时需要将其扩展为64位之后再进行计算。而当立即数达到32位时,无需进行扩展直接和目的操作数的低32位进行计算,因此会出现上述奇怪的现象,且该立即数长度限制对mov指令无效。
但是为什么x64的汇编要求最大32位呢?为什么小于32位的立即数会默认扩展为64位呢?

找到了x86书中推荐的intel官网的资料,其中有一部分与该文件相关的表述:
x86_64在64位模式汇编指令的立即数扩展问题_第1张图片
可以看到,上述提及immediate operands(立即数操作数时规定了立即数的范围不能超过2^32,也即DWORD范围。

相关材料

这篇文章提到了一些关于x64汇编的相关操作,但我并未看懂。尝试搜索报错信息时找到了一些相关的问答资料,分别贴在下面供参考。
资料1https://stackoverflow.com/questions/69916226/very-beginners-question-constant-value-too-large
资料2 https://masm32.com/board/index.php?topic=6434.0

你可能感兴趣的:(编程语言,汇编,windows)