对于大多数指令,寄存器操作数的宽度意味着内存操作数的宽度,因为两个操作数的大小必须相同。例如,mov rdx, [d]暗示mov rdx, qword [d]因为您使用了 64 位寄存器。
但是相同的movsx/movzx助记符用于字节源和字源操作码,因此除非源是寄存器(如movzx eax, cl),否则它是模棱两可的。
movsx/movzx带有内存源的总是需要明确指定的内存操作数的宽度。
movsxd助记符应该暗示 32 位源大小 。movsxd rcx, [c]与 NASM 组装,但显然不是与 YASM。YASM 要求您编写dword, 即使它不接受byte, word, 或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]
请注意,指令的“操作数大小”(由操作数大小前缀决定,使其成为 16 位,或 REX.W=1 使其成为 64 位)是movsx/的目标宽度movzx。不同的源大小使用不同的操作码。
如果不明显,则没有,movzxd因为32-bitmov已经零扩展为 64-bit 隐式。 movsxd eax, ecx是可编码的,但不推荐(mov改用)。
在 AT&T 语法中,您需要在助记符中明确指定源宽度和目标宽度,例如movsbq (%rsi), %rax. GAS 不允许您编写movsb (%rsi), %eax来推断目标宽度(操作数大小),因为movsb/ movsw/etc 是带有隐式 (%rsi)、(%rdi) 操作数的字符串移动指令的助记符。
有趣的事实: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文件格式中的section和segment有什么区别。
你不能ret从_start. 这不是一个函数,它是你的 ELF 入口点。堆栈上的第一件事是argc, 不是有效的返回地址。使用它干净地退出:
xor edi,edi
mov eax, 231
syscall ; sys_exit_group(0)
请参阅x86标签 wiki 以获取更多有用指南的链接(以及底部的调试提示)。