我正在尝试使用yasm汇编下面的代码。我已经把这里放在了#39;注释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
答案 0 :(得分:2)
对于大多数指令,寄存器操作数的宽度表示内存操作数的宽度,因为两个操作数必须大小相同。例如mov rdx, [d]
隐含mov rdx, qword [d]
,因为您使用的是64位寄存器。
但是相同的movsx
/ movzx
助记符用于字节源和字源操作码,因此除非源是寄存器(如movzx eax, cl
),否则它是不明确的。 / p>
具有内存源的
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位)是{{1的目标宽度} / movsx
。不同的源大小使用不同的操作码。
如果不明显,则没有movzx
,因为32-bit mov
already zero-extends to 64-bit implicitly。 movzxd
是可编码的,但不推荐(改为使用movsxd eax, ecx
。)
在AT& T语法中,不同的源宽度具有不同的助记符,例如mov
或movsb
。目标大小是通常的后缀,因此您可以将movsw
写为显式或movsbq (%rsi), %rax
以使汇编程序从movsb (%rsi), %rax
推断目标大小。
关于源代码的其他内容:
NASM / YASM允许您使用%rax
关键字而不是segment
,但实际上您提供的是ELF部分名称,而不是可执行部分名称。此外,您可以将只读数据放在section
中(作为文本段的一部分链接)。 What's the difference of section and segment in ELF file format
section .rodata
你不能ret
。它不是一个功能,它是你的ELF切入点。堆栈中的第一件事是_start
,而不是有效的返回地址。用它来彻底退出:
argc
请参阅x86标记wiki,获取更多有用指南的链接(以及底部的调试技巧)。