GCC内联汇编中的标签

48
在我不断尝试GCC内联汇编的过程中,遇到了一个关于标签和内联代码的新问题。
考虑以下简单跳转:
__asm__
(
    "jmp out;"
    "out:;"
    :
    :
);

这段代码除了跳转到out标签外并没有做任何事情。虽然这段代码本身可以编译通过,但如果将其放在函数内,并使用优化标志进行编译,编译器会报错:"错误:符号'out'已经定义"。

看起来编译器每次内联函数时都会重复这个汇编代码。这导致标签out被复制,从而产生多个out标签。

那么我该如何解决这个问题呢?不能在内联汇编中使用标签吗?这篇GCC内联汇编教程提到:

因此,您可以将汇编代码放入CPP宏和内联C函数中,以便任何人都可以将其用作任何C函数/宏。内联函数非常类似于宏,但有时使用起来更加清晰。请注意,在所有这些情况下,代码将被复制,因此只应在该asm代码中定义局部标签(1:样式)。

我尝试查找有关这些“局部标签”的更多信息,但似乎找不到与内联汇编相关的内容。看起来这篇教程是说局部标签是一个数字后跟一个冒号(如1:),因此我尝试使用这样的标签。有趣的是,代码编译通过了,但在运行时它只触发了一个段错误。嗯...

那么有什么建议、提示或答案吗?

2个回答

60

本地标签的声明实际上是一个数字后跟一个冒号。 但是对于本地标签的引用需要一个fb的后缀,具体取决于您想要向前还是向后查看 - 例如,1f指的是向前方向中的下一个1:标签。

因此,将标签声明为1:是正确的;但是要引用它,您需要说jmp 1f(因为在这种情况下您正在向前跳转)。


2
@MichaelGraczyk 本地标签不是x86特有的功能。GAS支持它们,无论CPU或对象文件格式如何,几乎所有其他Unix汇编器也支持它们(即使在1995年我也从未见过不支持的)。 - zwol
2
实际上,Indeedjmp 1 都被视为跳转到位置 1,因此导致了段错误。 - greggo

40

这个问题已经存在一段时间了,但是还有两个有趣的解决方案。

1) 这个例子使用%=。在汇编模板中,%=会被替换为“在整个编译中每个insn都唯一的数字。这对于制作在给定insn中多次引用的局部标签非常有用。”请注意,要使用%=,您(显然)必须至少有一个输入(尽管您可能不必实际使用它)。

int a = 3;
asm (
    "test %0\n\t"
    "jnz to_here%=\n\t"
    "jz to_there%=\n\t"
    "to_here%=:\n\t"
    "to_there%=:"
    ::"r" (a));

这将输出:
test %eax
jnz to_here14
jz to_there14
to_here14:
to_there14:

或者你可以使用asm goto(我认为在v4.5中添加)。这实际上让你跳转到C标签而不仅仅是asm标签:

asm goto ("jmp %l0\n"
 : /* no output */
 : /* no input */
 : /* no clobber */
 : gofurther);

printf("Didn't jump\n");

// c label:
gofurther:
printf("Jumped\n");

这个"%="技巧在哪里有记录? - Omar Darwish
1
找到了。文档可以在这里的“6.47.2.2 汇编模板”部分找到:https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html - Omar Darwish
我可能会链接到[这里](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#AssemblerTemplate)代替。 - David Wohlferd
1
你不需要虚假的操作数,只需使用 asm ("..." : ),注意 :。此外,%= 在整个编译中并不是唯一的,它是每个编译单元独有的。但是你不能在顶层使用它(在任何函数或方法之外),因为这样的汇编不能有操作数。在这种情况下,你必须使用数字标签。 - emacs drives me nuts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接