yasm movsx、movsxd 操作数 2 的大小无效

3 assembly x86-64 nasm yasm

我正在尝试使用 yasm 汇编下面的代码。我在 yasm 报告错误“错误:操作数 2 的大小无效”的地方添加了“此处”注释。为什么会发生这个错误?

segment .data
    a db 25
    b dw 0xffff
    c dd 3456
    d dq -14

segment .bss
    res resq 1

segment .text
    global _start

_start:
    movsx rax, [a] ; here
    movsx rbx, [b] ; here 
    movsxd rcx, [c] ; here
    mov rdx, [d]
    add rcx, rdx
    add rbx, rcx
    add rax, rbx
    mov [res], rax
    ret
Run Code Online (Sandbox Code Playgroud)

Pet*_*des 5

对于大多数指令,寄存器操作数的宽度意味着存储器操作数的宽度,因为两个操作数必须具有相同的大小。例如mov rdx, [d]表示mov rdx, qword [d]因为您使用了 64 位寄存器。

但是相同的movsx/movzx助记符用于字节源和字源操作码,因此除非源是寄存器(如movzx eax, cl),否则它是不明确的。另一个例子是crc32 r32, r/m8vs. r/m16vs. r/m32。(与 movsx/zx 不同,它的源大小可以与操作数大小一样宽。)

movsx/movzx具有内存源总是需要显式指定内存操作数的宽度。

movsxd助记符应该暗示 32 位源大小。 movsxd rcx, [c]与 NASM 组装,但显然不与 YASM 组装。YASM 要求您编写dword,即使它不接受byteword、 或qword,而且它也不接受其中movsx rcx, dword [c]任何一个(即它需要movsxd32 位源操作数的助记符)。

在 NASM 中,movsx rcx, dword [c]组装到movsxd,但movsxd rcx, word [c]仍然被拒绝。即在 NASM 中,plainmovsx是完全灵活的,但movsxd仍然是刚性的。为了人类的利益,我仍然建议使用dword明确的负载宽度。

movsx    rax,  byte [a]
movsx    rbx,  word [b]
movsxd   rcx, dword [c]
Run Code Online (Sandbox Code Playgroud)

请注意,指令的“操作数大小”(由操作数大小前缀决定,使其成为 16 位,或由 REX.W=1 使其成为 64 位)是movsx/的目标宽度movzx。不同的源大小使用不同的操作码。


如果不是很明显,那就没有movzxd,因为32 位mov已经隐式零扩展到 64 位movsxd eax, ecx是可编码的,但不推荐(mov改为使用)。

在 AT&T 语法中,您需要在助记符中显式指定源宽度和目标宽度,例如movsbq (%rsi), %rax. GAS 不会让您通过写入movsb (%rsi), %eax来推断目标宽度(操作数大小),因为movsb/ /etc 是带有隐式 (%rsi)、(%rdi) 操作数的字符串移动指令movsw的助记符。

有趣的事实:GAS 和 clang 确实允许使用诸如movzb (%rsi), %eaxas 之类的东西movzbl,但 GAS 仅具有额外的逻辑来允许在必要时基于操作数进行歧义消除(不仅仅是推断大小),例如movsd (%rsi), %xmm0vs. movsd。(Clang12.0.1 实际上接受movsb (%rcx), %eaxas movsbl,但 GAS 2.36.1 不接受,因此为了可移植性,最好明确使用符号扩展,对于零扩展来说也是一个不错的主意。)


有关源代码的其他内容:

NASM/YASM 允许您使用segment关键字而不是section,但实际上您给出的是 ELF 节名称,而不是可执行段名称。此外,您还可以放入只读数据section .rodata(作为文本段的一部分链接)。 ELF文件格式中的节和段有什么区别

你不能ret_start. 它不是一个函数,它是你的 ELF 入口点。堆栈上的第一件事是argc,不是有效的返回地址。使用它可以干净地退出:

xor    edi,edi
mov    eax, 231
syscall            ; sys_exit_group(0)
Run Code Online (Sandbox Code Playgroud)

请参阅标签 wiki,获取更多有用指南的链接(以及底部的调试技巧)。

  • @SepRoland:好点。我认为操作数会像“movsd”与“movsd (%rdi), %xmm0”那样消除歧义。但不是。当前 GAS 接受 `movzb %al, %ecx` (推断 `l` 目标大小),但在 `movsb %al, %ecx` 上出错。如果您尝试使用“movsb (%rdi), %ecx”,它会警告它期望“(%rsi)”作为源,然后再因操作数类型不匹配(可能是寄存器目标)而出错;我猜想 AT&T 语法允许显式操作数版本。所以,是的,对于“movsb”,它与字符串助记符匹配,并且不会返回并将其视为具有隐式 dst 大小的符号扩展。 (2认同)