我正在尝试使用yasm来组装下面的代码。我在这里添加了‘这里’的注释,其中yasm报告了错误" error : 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发布于 2017-11-18 03:19:19
对于大多数指令,寄存器操作数的宽度意味着内存操作数的宽度,因为两个操作数必须是相同的大小。mov rdx, [d]意味着mov rdx, qword [d],因为您使用了64位寄存器。
但同样的movsx / movzx助记符用于字节源和字源代码,因此除非源是寄存器(如movzx eax, cl),否则它是不明确的。另一个例子是crc32 r32, r/m8对r/m16和r/m32。(与movsx/zx不同,它的源大小可以和操作数大小一样宽。)
具有内存源的movsx / movzx 总是需要显式指定的内存操作数的宽度。
movsxd助记符应该意味着32位的源大小.movsxd rcx, [c]与NASM汇合,但显然与YASM不装配。YASM要求您编写dword,即使它在那里不接受byte、word或qword,它也不接受movsx rcx, dword [c] (也就是说,它需要32位源操作数的movsxd助记符)。
在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,因为已经隐式地扩展到64位。。movsxd eax, ecx是可编码的,但不推荐使用(使用mov代替)。
在AT&T语法中,您需要在助记符中显式指定源和目标宽度,比如movsbq (%rsi), %rax。GAS不允许您编写movsb (%rsi), %eax来推断目标宽度(操作数大小),因为movsb/movsw/etc是具有隐式(%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 (它作为文本段的一部分进行链接)。ELF文件格式的区段和段有什么不同?。
你不能ret从_start。这不是函数,是你的ELF入口点。堆栈上的第一件事是argc,而不是有效的返回地址。使用这个可以干净地退出:
xor edi,edi
mov eax, 231
syscall ; sys_exit_group(0)请参阅x86标记wiki,以获得指向更多有用指南的链接(以及底部的调试技巧)。
https://stackoverflow.com/questions/47350568
复制相似问题