我正在努力将示例从复杂的间接寻址模式更改为简单的间接寻址模式片段。但是,我遇到了基于模式的示例,我无法"转换"。
movzbl string+8, %eax
我试过这个:
addl $8, string
movzbl string, %eax
编译此代码后,会弹出错误消息。
string+8
不是基于索引的寻址模式。 它组装到一个没有基本寄存器的disp32绝对地址。 +8 在组装/链接时解析。 (请参阅引用内存位置的内容。(x86 寻址模式((
movzbl string+8, %eax
组装成机器代码,具有与movzbl string, %eax
相同的寻址模式(ModR/M字节(,只是disp32
位移不同。请参阅C++链接在实践中如何工作?,了解有关组装 + 链接如何处理 +8 以便在运行时没有额外工作的一些详细信息。
您可以这样做,因为string+8
不是寻址模式,而是可以用作即时操作数的链接时间常量。
mov $string+8, %edx
movzbl (%edx), %eax
使用mov
而不是lea
清楚地表明了这一点,IMO。 使用lea
将静态地址放入寄存器的唯一原因是在 x86-64 中,您可以将其用于与位置无关的代码(或低 2 GiB 以外的代码,如在 OS X 上(的 RIP 相对寻址。 例如lea string+8(%rip), %rdx
.
在运行时而不是组装时做最无用的事情的最复杂的方法是
mov $string, %edx
add $8, %edx
movzbl (%edx), %eax
我想使用lea
会更加复杂,或者你可以inc
8 次,或者编写一个循环来inc
8 次,但这以不同的方式过于复杂。
例如,给定此源:
.globl _start
_start:
mov $string, %eax
mov $string+8, %eax
movzbl string+8, %eax
.section .rodata
string:
我用gcc -m32 foo.S -c
组装并用objdump -drwC foo.o
拆卸(选项-r
显示重新定位(:
foo.o: file format elf32-i386
Disassembly of section .text:
00000000 <_start>:
0: b8 00 00 00 00 mov $0x0,%eax 1: R_386_32 .rodata
5: b8 08 00 00 00 mov $0x8,%eax 6: R_386_32 .rodata
a: 0f b6 05 08 00 00 00 movzbl 0x8,%eax d: R_386_32 .rodata
0 和 0x8 占位符不是实际地址,而是该重新定位的符号值的偏移量。 他们反对对象文件的.rodata
部分,而不是string
,因为我没有使用.globl _string
使该符号全局化。
如果我组装+链接gcc -static -m32 -nostdlib foo.S
并反汇编,我会得到:
8048098: b8 a9 80 04 08 mov $0x80480a9,%eax
804809d: b8 b1 80 04 08 mov $0x80480b1,%eax
80480a2: 0f b6 05 b1 80 04 08 movzbl 0x80480b1,%eax
请注意,要从中加载的绝对地址是如何在movzbl
的最后 4 个字节(以小端序为单位(中存在的,与b8
操作码(mov-imm32-to-eax(的直接 4 字节值相同。
还要注意string
和string+8
如何只产生不同的地址字节但相同的操作码。
lea string, %eax
add $8, %eax
movzbl (%eax), %eax
但是 movzbl string+8,%eax 不是"复杂寻址模式",它由汇编器/链接器解析。