将 [symbol + constant] Intel 语法寻址模式转换为 AT&T 语法?
Converting [symbol + constant] Intel syntax addressing mode to AT&T syntax?
我只是不知道如何在移动值时向我的目的地添加偏移量,特别是在英特尔语法中我有:
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)
[gives an error] but using just gdtr(,1)
works fine.
AT&T寻址方式()
括号内的内容只能是寄存器(和比例因子):disp(base, index, scale)
。基数和索引是可选的,所以空是可以的,但无效(非注册)不是。
并且当您指定没有底数或索引的比例时,显然您只能使用一个逗号:(,,1)
是关于空比例因子的错误。
您可以将其写为 gdtr+2(,1)
以明确不使用寄存器。
无论语法如何,+2 都是寻址模式中位移的一部分,而不是基址或变址寄存器。请参阅 或 Intel 或 AMD 的手册,了解如何对寻址模式进行编码。 (你正在做的是 [disp32]
或 [disp16]
寻址模式,就如何将其编码为机器代码而言。)
正如 Nate 指出的那样,linker 负责将 assemble-time 文字常量 + link-time-constant 符号地址转换为机器代码中的最终地址,编码为 disp32
(或 disp16
用于 16 位地址大小)。或 RIP-relative rel32
for x86-64.
有趣的事实:一些 AT&T assemblers 接受 (gdtr)
作为 gdtr
的替代品,但不接受 2(gdtr)
.
自己解决这个问题的方法:
通常,如果您卡在 NASM -> AT&T,您可以 assemble NASM 来源(例如 nasm -felf
)和 disassemble AT&T disassembler 喜欢 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
on the Godbolt compiler explorer 编译成这个 asm:
foo:
movzbl gdtr+2, %eax
ret
gdtr:
.zero 1024
这是您的寻址模式,作为奖励,AT&T 助记符用于带有字节源的 movzx。当然你可以 fiddle 类型。
编译器是有用的资源;他们知道如何在编译简单的 C 函数时做很多事情 "the normal way",并且他们知道调用约定和类型宽度。以及包括函数指针在内的所有内容的 AT&T 语法。 如果您遇到困难,请咨询编译器。基本上,您唯一无法让编译器向您展示的是 jmp far
(AT&T ljmp
)
的语法
我只是不知道如何在移动值时向我的目的地添加偏移量,特别是在英特尔语法中我有:
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)
[gives an error] but using justgdtr(,1)
works fine.
AT&T寻址方式()
括号内的内容只能是寄存器(和比例因子):disp(base, index, scale)
。基数和索引是可选的,所以空是可以的,但无效(非注册)不是。
并且当您指定没有底数或索引的比例时,显然您只能使用一个逗号:(,,1)
是关于空比例因子的错误。
您可以将其写为 gdtr+2(,1)
以明确不使用寄存器。
无论语法如何,+2 都是寻址模式中位移的一部分,而不是基址或变址寄存器。请参阅 [disp32]
或 [disp16]
寻址模式,就如何将其编码为机器代码而言。)
正如 Nate 指出的那样,linker 负责将 assemble-time 文字常量 + link-time-constant 符号地址转换为机器代码中的最终地址,编码为 disp32
(或 disp16
用于 16 位地址大小)。或 RIP-relative rel32
for x86-64.
有趣的事实:一些 AT&T assemblers 接受 (gdtr)
作为 gdtr
的替代品,但不接受 2(gdtr)
.
自己解决这个问题的方法:
通常,如果您卡在 NASM -> AT&T,您可以 assemble NASM 来源(例如 nasm -felf
)和 disassemble AT&T disassembler 喜欢 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
on the Godbolt compiler explorer 编译成这个 asm:
foo:
movzbl gdtr+2, %eax
ret
gdtr:
.zero 1024
这是您的寻址模式,作为奖励,AT&T 助记符用于带有字节源的 movzx。当然你可以 fiddle 类型。
编译器是有用的资源;他们知道如何在编译简单的 C 函数时做很多事情 "the normal way",并且他们知道调用约定和类型宽度。以及包括函数指针在内的所有内容的 AT&T 语法。 如果您遇到困难,请咨询编译器。基本上,您唯一无法让编译器向您展示的是 jmp far
(AT&T ljmp
)