快速提问。此代码将无法编译:
mov eax, dword [rbx+rsi*5]
我不希望这样,因为mov和multiplication是两个不同的CPU操作。可以实现的唯一原因是通过移位。
但是,确实可以编译:
mov eax, dword [lst+rsi*5]
“ lst”为变量数组。当在上下文中使用时,它也会产生输出(因此代码可以编译并运行)。为何起作用的解释是什么?
yasm -Worphan-labels -g dwarf2 -f elf64 NAME.asm -l NAME.lst
x86寻址模式必须符合格式[base + idx*scale + disp0/8/32]
。 (或相对于RIP。)
*scale
实际上被编码为2位移位计数,因此它可以是1、2、4或8。请参见A couple of questions about [base + index*scale + disp]和Referencing the contents of a memory location. (x86 addressing modes)
这里发生的是您的汇编器分解了[lst + rsi*5]
为您输入[lst + rsi + rsi*4]
。(或1 + (1<<0..3)
形式的其他比例因子)(其中lst
是一个4字节(32位)的绝对地址,其符号扩展为64位。是的,它在Linux non-PIE executables中有效;静态代码+数据正好进入虚拟地址空间的低2GiB这样就可以了。)
但是,如果您已经有了基址寄存器,则无法拆分它,而仍然具有可编码的寻址模式。 [rbx + rsi + rsi*4]
是不可能的。
[类似地,NASM和YASM允许您编写类似vaddps xmm0, [rbp]
而不是vaddps xmm0, xmm0, [rbp+0]
的内容(即使将RBP作为基址寄存器是not encodeable without a displacement。当第一个源操作数与目的地相同时,也将其省略)。例如,写[rbp + rax]
而不是[rbp + rax*1]
-一种寻址模式每个基址或索引最多只能有1个。
[当您的代码表示的操作明确且可编码时,汇编程序有时会具有便捷的功能,以使源代码看起来与机器代码/您从反汇编中获得的代码有所不同。
移动和乘法是两个不同的CPU操作
寻址模式do包括加法和移位,即使shl
和add
也是分开的指令。那不是原因。同样,imul ecx, [lst + rsi + rsi*4], 12345
是有效的指令。用内存源或目标操作数进行的类似移位或加法也是如此。
但是是的,x86寻址模式不能对任意乘法进行编码,只能对2移位计数进行编码。