yasm movsx、movsxd 操作数 2 的大小无效
yasm movsx, movsxd invalid size for operand 2
我正在尝试使用 yasm assemble 下面的代码。我在 yasm 报告错误 "error: invalid size for operand 2" 的地方添加了 'here' 注释。为什么会出现此错误?
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
对于大多数指令,寄存器操作数的宽度意味着内存操作数的宽度,因为两个操作数的大小必须相同。例如mov rdx, [d]
意味着 mov rdx, qword [d]
因为您使用了 64 位寄存器。
但是相同的 movsx
/ movzx
助记符用于字节源和字源操作码,因此除非源是寄存器(如 movzx eax, cl
),否则它是不明确的。另一个例子是 crc32 r32, r/m8
vs. r/m16
vs. r/m32
。 (与 movsx/zx 不同,它的源大小可以与操作数大小一样宽。)
movsx
/ movzx
内存源总是需要显式指定内存操作数的宽度。
movsxd
助记符应该表示 32 位源大小。 movsxd rcx, [c]
使用 NASM 进行汇编,但显然不是使用 YASM。 YASM 要求您写 dword
,即使它不接受 byte
、word
或 qword
,也不接受 movsx rcx, dword [c]
(即它需要 movsxd
32 位源操作数的助记符)。
在NASM中,movsx rcx, dword [c]
汇编为movsxd
,但movsxd rcx, word [c]
仍被拒绝。即在 NASM 中,普通 movsx
是完全灵活的,但 movsxd
仍然是刚性的。为了人类的利益,我仍然建议使用 dword
使负载的宽度明确。
movsx rax, byte [a]
movsx rbx, word [b]
movsxd rcx, dword [c]
请注意,指令的“操作数大小”(由操作数大小前缀确定使其成为 16 位,或 REX.W=1 使其成为 64 位)是目标宽度对于 movsx
/ movzx
。不同的源大小使用不同的操作码。
如果不明显,没有movzxd
因为32-bit mov
already zero-extends to 64-bit implicitly。 movsxd eax, ecx
是可编码的,但不推荐(使用 mov
代替)。
在 AT&T 语法中,您需要在助记符中明确指定源和目标宽度,如 movsbq (%rsi), %rax
。 GAS 不会让你写 movsb (%rsi), %eax
来推断目标宽度(操作数大小),因为 movsb
/movsw
/etc 是 string-move instructions 的助记符,带有隐式 (%rsi ), (%rdi) 个操作数。
有趣的事实:GAS 和 clang 确实允许它用于 movzb (%rsi), %eax
和 movzbl
之类的事情,但 GAS 只有额外的逻辑允许在必要时基于操作数消除歧义(不仅仅是推断大小) ,例如 movsd (%rsi), %xmm0
与 movsd
。 (Clang12.0.1 实际上确实接受 movsb (%rcx), %eax
作为 movsbl
,但 GAS 2.36.1 不接受,因此为了可移植性,最好明确使用符号扩展,零扩展也不错也是。)
关于您的源代码的其他内容:
NASM/YASM 允许您使用 segment
关键字而不是 section
,但实际上您提供的是 ELF 段名称,而不是可执行段名称。此外,您可以将只读数据放入 section .rodata
(作为文本段的一部分链接)。 What's the difference of section and segment in ELF file format.
您不能 ret
来自 _start
。它不是函数,它是您的 ELF 入口点。堆栈上的第一件事是 argc
,而不是有效的 return 地址。使用它可以干净地退出:
xor edi,edi
mov eax, 231
syscall ; sys_exit_group(0)
请参阅 x86 标签 wiki 以获取更多有用指南的链接(以及底部的调试提示)。
我正在尝试使用 yasm assemble 下面的代码。我在 yasm 报告错误 "error: invalid size for operand 2" 的地方添加了 'here' 注释。为什么会出现此错误?
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
对于大多数指令,寄存器操作数的宽度意味着内存操作数的宽度,因为两个操作数的大小必须相同。例如mov rdx, [d]
意味着 mov rdx, qword [d]
因为您使用了 64 位寄存器。
但是相同的 movsx
/ movzx
助记符用于字节源和字源操作码,因此除非源是寄存器(如 movzx eax, cl
),否则它是不明确的。另一个例子是 crc32 r32, r/m8
vs. r/m16
vs. r/m32
。 (与 movsx/zx 不同,它的源大小可以与操作数大小一样宽。)
movsx
/ movzx
内存源总是需要显式指定内存操作数的宽度。
movsxd
助记符应该表示 32 位源大小。 movsxd rcx, [c]
使用 NASM 进行汇编,但显然不是使用 YASM。 YASM 要求您写 dword
,即使它不接受 byte
、word
或 qword
,也不接受 movsx rcx, dword [c]
(即它需要 movsxd
32 位源操作数的助记符)。
在NASM中,movsx rcx, dword [c]
汇编为movsxd
,但movsxd rcx, word [c]
仍被拒绝。即在 NASM 中,普通 movsx
是完全灵活的,但 movsxd
仍然是刚性的。为了人类的利益,我仍然建议使用 dword
使负载的宽度明确。
movsx rax, byte [a]
movsx rbx, word [b]
movsxd rcx, dword [c]
请注意,指令的“操作数大小”(由操作数大小前缀确定使其成为 16 位,或 REX.W=1 使其成为 64 位)是目标宽度对于 movsx
/ movzx
。不同的源大小使用不同的操作码。
如果不明显,没有movzxd
因为32-bit mov
already zero-extends to 64-bit implicitly。 movsxd eax, ecx
是可编码的,但不推荐(使用 mov
代替)。
在 AT&T 语法中,您需要在助记符中明确指定源和目标宽度,如 movsbq (%rsi), %rax
。 GAS 不会让你写 movsb (%rsi), %eax
来推断目标宽度(操作数大小),因为 movsb
/movsw
/etc 是 string-move instructions 的助记符,带有隐式 (%rsi ), (%rdi) 个操作数。
有趣的事实:GAS 和 clang 确实允许它用于 movzb (%rsi), %eax
和 movzbl
之类的事情,但 GAS 只有额外的逻辑允许在必要时基于操作数消除歧义(不仅仅是推断大小) ,例如 movsd (%rsi), %xmm0
与 movsd
。 (Clang12.0.1 实际上确实接受 movsb (%rcx), %eax
作为 movsbl
,但 GAS 2.36.1 不接受,因此为了可移植性,最好明确使用符号扩展,零扩展也不错也是。)
关于您的源代码的其他内容:
NASM/YASM 允许您使用 segment
关键字而不是 section
,但实际上您提供的是 ELF 段名称,而不是可执行段名称。此外,您可以将只读数据放入 section .rodata
(作为文本段的一部分链接)。 What's the difference of section and segment in ELF file format.
您不能 ret
来自 _start
。它不是函数,它是您的 ELF 入口点。堆栈上的第一件事是 argc
,而不是有效的 return 地址。使用它可以干净地退出:
xor edi,edi
mov eax, 231
syscall ; sys_exit_group(0)
请参阅 x86 标签 wiki 以获取更多有用指南的链接(以及底部的调试提示)。