我只是无法弄清楚如何在移动值时向目标添加偏移量,特别是在我拥有的英特尔语法中:
MOV [gdtr + 2], EAX
对于 AT&T 语法,我尝试将其转换为:
movl %eax, gdtr(2,1)
编译时会出错,junk '(2,1)' after expression
但使用gdtr(,1)
效果很好。
我不明白为什么我不能使用基本偏移,而只能使用比例因子。
我只是无法弄清楚如何在移动值时向目标添加偏移量,特别是在我拥有的英特尔语法中:
MOV [gdtr + 2], EAX
对于 AT&T 语法,我尝试将其转换为:
movl %eax, gdtr(2,1)
编译时会出错,junk '(2,1)' after expression
但使用gdtr(,1)
效果很好。
我不明白为什么我不能使用基本偏移,而只能使用比例因子。
简单地写
movl %eax, gdtr+2
基偏移寻址仅在偏移为寄存器时才有效。使用寻址模式将两个常数相加是没有意义的。其工作方式(无论语法如何)是汇编器/链接器解析symbol+constant
为指令编码的位移字段的单个数字。
只是gdtr+2
gdtr(2,1)
[给出错误]但使用gdtr(,1)
效果很好。
AT&T 寻址模式中括号内的内容()
只能是寄存器(和比例因子):disp(base, index, scale)
. 基数和索引是可选的,所以空是可以的,但无效(非注册)不是。
当您指定一个没有基数或索引的比例时,显然您必须只使用一个逗号:(,,1)
是关于空比例因子的错误。
您可以将其编写为gdtr+2(,1)
显式不使用寄存器。
+2 是寻址模式中位移的一部分,而不是基址或索引寄存器,无论语法如何。请参阅有关 [base + index*scale + disp]或 Intel 或 AMD 的有关寻址模式如何编码的手册的几个问题。(就如何将其编码为机器代码而言,您正在做的是[disp32]
或寻址模式。)[disp16]
正如 Nate 指出的那样,链接器负责将汇编时文字常量 + 链接时常量符号地址转换为机器代码中的最终地址,编码为 a disp32
(或disp16
16 位地址大小)。或 x86-64 的相对 RIP rel32
。
有趣的事实:一些 AT&T 组装商接受(gdtr)
作为 . 的替代品gdtr
,但不接受2(gdtr)
.
通常,如果您卡在 NASM -> AT&T 上,您可以组装 NASM 源代码(例如nasm -felf
)并使用 AT&T 反汇编程序(如objdump -drwC
. 但这对符号寻址模式语法没有帮助,因为充其量objdump -dr
只是用符号名称信息注释数字寻址模式。
因此,在这种情况下,您最好的选择是让 GCC 或 clang 发出使用符号名称和数字常量的指令,如下所示
char gdtr[1024]; // global var so it has a symbol
char foo() { return gdtr[2]; } // load from global symbol + constant.
gcc9.3 -O2 -m32
在 Godbolt 编译器资源管理器上编译到这个 asm:
foo:
movzbl gdtr+2, %eax
ret
gdtr:
.zero 1024
有你的寻址模式,作为奖励,带有字节源的 movzx 的 AT&T 助记符。当然,您可以摆弄类型。
编译器是有用的资源;在编译简单的 C 函数时,他们知道如何以“正常方式”做很多事情,并且他们知道调用约定和类型宽度。以及包括函数指针在内的所有内容的 AT&T 语法。 如果您遇到困难,请询问编译器。基本上,您唯一无法让编译器向您展示的是jmp far
(AT&T ljmp
)的语法