[英]Referencing memory operands in .intel_syntax GNU C inline assembly
I'm catching a link error when compiling and linking a source file with inline assembly.使用内联汇编编译和链接源文件时,我发现链接错误。
Here are the test files:以下是测试文件:
via:$ cat test.cxx
extern int libtest();
int main(int argc, char* argv[])
{
return libtest();
}
$ cat lib.cxx
#include <stdint.h>
int libtest()
{
uint32_t rnds_00_15;
__asm__ __volatile__
(
".intel_syntax noprefix ;\n\t"
"mov DWORD PTR [rnds_00_15], 1 ;\n\t"
"cmp DWORD PTR [rnds_00_15], 1 ;\n\t"
"je done ;\n\t"
"done: ;\n\t"
".att_syntax noprefix ;\n\t"
:
: [rnds_00_15] "m" (rnds_00_15)
: "memory", "cc"
);
return 0;
}
Compiling and linking the program results in:编译和链接程序会导致:
via:$ g++ -fPIC test.cxx lib.cxx -c
via:$ g++ -fPIC lib.o test.o -o test.exe
lib.o: In function `libtest()':
lib.cxx:(.text+0x1d): undefined reference to `rnds_00_15'
lib.cxx:(.text+0x27): undefined reference to `rnds_00_15'
collect2: error: ld returned 1 exit status
The real program is more complex.真正的程序更复杂。 The routine is out of registers so the flag rnds_00_15
must be a memory operand.该例程没有寄存器,因此标志rnds_00_15
必须是内存操作数。 Use of rnds_00_15
is local to the asm block. rnds_00_15
使用是 asm 块本地的。 It is declared in the C code to ensure the memory is allocated on the stack and nothing more.它在 C 代码中声明,以确保在堆栈上分配内存,仅此而已。 We don't read from it or write to it as far as the C code is concerned.就 C 代码而言,我们不读取它或写入它。 We list it as a memory input so GCC knows we use it and wire up the "C variable name" in the extended ASM.我们将它列为内存输入,以便 GCC 知道我们使用它并在扩展 ASM 中连接“C 变量名称”。
Why am I receiving a link error, and how do I fix it?为什么我会收到链接错误,我该如何解决?
Compile with gcc -masm=intel
and don't try to switch modes inside the asm template string.使用gcc -masm=intel
编译并且不要尝试在 asm 模板字符串中切换模式。 AFAIK there's no equivalent for clang (note that MacOS installs clang as gcc
/ g++
by default.) AFAIK 没有等效的 clang(请注意,MacOS 默认将 clang 安装为gcc
/ g++
。)
Also, of course you need to use valid GNU C inline asm, using operands to tell the compiler which C objects you want to read and write.此外,当然您需要使用有效的 GNU C 内联 asm,使用操作数告诉编译器您想要读取和写入哪些 C 对象。
I don't believe Intel syntax uses the percent sign.我不相信英特尔语法使用百分号。 Perhaps I am missing something?也许我错过了什么?
You're getting mixed up between %operand
substitutions into the Extended-Asm template (which use a single %
) , vs. the final asm that the assembler sees.您在%operand
替换到 Extended-Asm 模板(使用单个%
)与汇编程序看到的最终 asm之间混淆了。
You need %%
to use a literal %
in the final asm.您需要%%
在最终汇编中使用文字%
。 You wouldn't use "mov %%eax, 1"
in Intel-syntax inline asm, but you do still use "mov %0, 1"
or %[named_operand]
.您不会在英特尔语法内联汇编中使用"mov %%eax, 1"
,但您仍然使用"mov %0, 1"
或%[named_operand]
。
See https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html .请参阅https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html 。 In Basic asm (no operands), there is no substitution and % isn't special in the template, so you'd write mov $1, %eax
in Basic asm vs. mov $1, %%eax
in Extended, if for some reason you weren't using an operand like mov $1, %[tmp]
or mov $1, %0
.在 Basic asm(无操作数)中,没有替换并且 % 在模板中并不特殊,因此如果出于某种原因,您可以在 Basic asm 中编写mov $1, %eax
与在 Extended 中编写mov $1, %%eax
你没有使用像mov $1, %[tmp]
或mov $1, %0
这样的操作数。
uint32_t rnds_00_15;
is a local with automatic storage.是具有自动存储功能的本地。 Of course it there's no asm symbol with that name.当然,它没有带有该名称的 asm 符号。
Use %[rnds_00_15]
and compile with -masm=intel
(And remove the .att_syntax
at the end; that would break the compiler-generate asm that comes after.)使用%[rnds_00_15]
并使用-masm=intel
编译(并在最后删除.att_syntax
;这会破坏后面的编译器生成的 asm。)
You also need to remove the DWORD PTR
, because the operand-expansion already includes that, eg DWORD PTR [rsp - 4]
, and clang errors on DWORD PTR DWORD PTR [rsp - 4]
.您还需要删除DWORD PTR
,因为操作数扩展已经包含了它,例如DWORD PTR [rsp - 4]
和DWORD PTR DWORD PTR [rsp - 4]
上的 clang 错误。 (GAS accepts it just fine, but the 2nd one takes precendence so it's pointless and potentially misleading.) (GAS 接受它就好了,但第二个优先,所以它毫无意义并且可能会产生误导。)
And you'll want a "=m"
output operand if you want the compiler to reserve you some scratch space on the stack.如果您希望编译器在堆栈上为您保留一些暂存空间,您将需要一个"=m"
输出操作数。 You must not modify input-only operands, even if it's unused in the C. Maybe the compiler decides it can overlap something else because it's not written and not initialized (ie UB).您不能修改仅输入操作数,即使它在 C 中未使用。也许编译器决定它可以与其他内容重叠,因为它没有被写入并且没有初始化(即 UB)。 (I'm not sure if your "memory"
clobber makes it safe, but there's no reason not to use an early-clobber output operand here.) (我不确定你的"memory"
clobber 是否使它安全,但没有理由不在这里使用早期的clobber 输出操作数。)
And you'll want to avoid label name conflicts by using %=
to get a unique number.并且您将希望通过使用%=
获取唯一编号来避免标签名称冲突。
Working example (GCC and ICC, but not clang unfortunately) , on the Godbolt compiler explorer (which uses -masm=intel
depending on options in the dropdown).工作示例(GCC 和 ICC,但不幸的是不是 clang) , 在 Godbolt 编译器资源管理器(根据下拉列表中的选项使用-masm=intel
)。 You can use "binary mode" (the 11010 button) to prove that it actually assembles after compiling to asm without warnings.您可以使用“二进制模式”(11010 按钮)来证明它在编译为 asm 后实际组装而没有警告。
int libtest_intel()
{
uint32_t rnds_00_15;
// Intel syntax operand-size can only be overridden with operand modifiers
// because the expansion includes an explicit DWORD PTR
__asm__ __volatile__
( // ".intel_syntax noprefix \n\t"
"mov %[rnds_00_15], 1 \n\t"
"cmp %[rnds_00_15], 1 \n\t"
"je .Ldone%= \n\t"
".Ldone%=: \n\t"
: [rnds_00_15] "=&m" (rnds_00_15)
:
: // no clobbers
);
return 0;
}
Compiles (with gcc -O3 -masm=intel
) to this asm.编译(使用gcc -O3 -masm=intel
)到这个 asm。 Also works with gcc -m32 -masm=intel
of course:当然也适用于gcc -m32 -masm=intel
:
libtest_intel:
mov DWORD PTR [rsp-4], 1
cmp DWORD PTR [rsp-4], 1
je .Ldone8
.Ldone8:
xor eax, eax
ret
I couldn't get this to work with clang: It choked on .intel_syntax noprefix
when I left that in explicitly .我无法.intel_syntax noprefix
与 clang 一起工作:当我将其明确留在.intel_syntax noprefix
时,它会窒息。
You have to use %b[tmp]
to get the compiler to substitute in BYTE PTR [rsp-4]
to only access the low byte of a dword input operand.您必须使用%b[tmp]
来让编译器在BYTE PTR [rsp-4]
替换以仅访问双字输入操作数的低字节。 I'd recommend AT&T syntax if you want to do much of this.如果你想做很多事情,我会推荐 AT&T 语法。
Using
%[rnds_00_15]
results inError: junk '(%ebp)' after expression.
使用%[rnds_00_15]
导致Error: junk '(%ebp)' after expression.
That's because you switched to Intel syntax without telling the compiler.那是因为您在没有告诉编译器的情况下切换到 Intel 语法。 If you want it to use Intel addressing modes, compile with -masm=intel
so the compiler can substitute into the template with the correct syntax.如果您希望它使用 Intel 寻址模式,请使用-masm=intel
编译,以便编译器可以使用正确的语法替换模板。
This is why I avoid that crappy GCC inline assembly at nearly all costs.这就是为什么我几乎不惜一切代价避免糟糕的 GCC 内联汇编。 Man I despise this crappy tool.伙计,我鄙视这种蹩脚的工具。
You're just using it wrong.你只是用错了。 It's a bit cumbersome, but makes sense and mostly works well if you understand how it's designed.这有点麻烦,但很有意义,而且如果您了解它的设计方式,则大多数情况下都能很好地工作。
Repeat after me: The compiler doesn't parse the asm string at all , except to do text substitutions of %operand
.之后我再说一遍:编译器不分析所有的ASM字符串,除了做的文本替换%operand
。 This is why it doesn't notice your .intel_syntax noprefex
and keeps substituting AT&T syntax.这就是为什么它没有注意到您的.intel_syntax noprefex
并不断替换 AT&T 语法的原因。
It does work better and more easily with AT&T syntax though, eg for overriding the operand-size of a memory operand, or adding an offset.不过,使用 AT&T 语法确实可以更好、更轻松地工作,例如用于覆盖内存操作数的操作数大小,或添加偏移量。 (eg 4 + %[mem]
works in AT&T syntax). (例如4 + %[mem]
在 AT&T 语法中工作)。
If you want to write inline asm that doesn't depend on -masm=intel
or not, use Dialect alternatives (which makes your code super-ugly; not recommended for anything other than wrapping one or two instructions):如果您想编写不依赖于-masm=intel
或不依赖于的内联汇编,请使用方言替代方案(这会使您的代码超级丑陋;除了包装一两个指令外,不建议用于任何其他用途):
Also demonstrates operand-size overrides还演示了操作数大小覆盖
#include <stdint.h>
int libtest_override_operand_size()
{
uint32_t rnds_00_15;
// Intel syntax operand-size can only be overriden with operand modifiers
// because the expansion includes an explicit DWORD PTR
__asm__ __volatile__
(
"{movl $1, %[rnds_00_15] | mov %[rnds_00_15], 1} \n\t"
"{cmpl $1, %[rnds_00_15] | cmp %k[rnds_00_15], 1} \n\t"
"{cmpw $1, %[rnds_00_15] | cmp %w[rnds_00_15], 1} \n\t"
"{cmpb $1, %[rnds_00_15] | cmp %b[rnds_00_15], 1} \n\t"
"je .Ldone%= \n\t"
".Ldone%=: \n\t"
: [rnds_00_15] "=&m" (rnds_00_15)
);
return 0;
}
With Intel syntax, gcc compiles it to:使用 Intel 语法,gcc 将其编译为:
mov DWORD PTR [rsp-4], 1
cmp DWORD PTR [rsp-4], 1
cmp WORD PTR [rsp-4], 1
cmp BYTE PTR [rsp-4], 1
je .Ldone38
.Ldone38:
xor eax, eax
ret
With AT&T syntax, compiles to:使用 AT&T 语法,编译为:
movl $1, -4(%rsp)
cmpl $1, -4(%rsp)
cmpw $1, -4(%rsp)
cmpb $1, -4(%rsp)
je .Ldone38
.Ldone38:
xorl %eax, %eax
ret
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.