我不认为仅通过编译选项就有可靠的方法来解决这个问题。更好的机制是能够完成任务并且可以在将来编译器的任何选项下工作。
对接受答案的评论
在被接受的答案中,对原始回答进行了编辑,建议采用以下解决方案:
int main(void) {
__asm__ ("jmp exit");
handler:
__asm__ __volatile__("jmp $0x0");
exit:
return 0;
}
首先,jmp $0x0
应该改为 jmp 0x0
。其次,C标签通常会被翻译成本地标签。 jmp exit
实际上不会跳转到 C 函数中的 exit
标签,它会跳转到 C 库中的 exit
函数,从而绕过了 main
底部的 return 0
。使用Godbolt 和 GCC 4.6.4,我们可以得到这个未经优化的输出(我已经删除了我们不关心的标签):
main:
pushl %ebp
movl %esp, %ebp
jmp exit
jmp 0x0
.L3:
movl $0, %eax
popl %ebp
ret
.L3
实际上是exit
的本地标签。在生成的汇编中,您将找不到exit
标签。如果存在C库,则可能会编译和链接。请勿在内联汇编中使用C本地goto标签。
使用asm goto作为解决方案
自GCC 4.5以来(OP正在使用4.6.x),支持asm goto
扩展汇编模板。 asm goto
允许您指定内联汇编可能使用的跳转目标:
6.45.2.7 Goto Labels
asm goto允许汇编代码跳转到一个或多个C标签。 asm goto语句中的GotoLabels部分包含一个逗号分隔的所有C标签列表,汇编器代码可以跳转到这些标签。 GCC假定asm执行落入下一条语句(如果不是这种情况,请考虑在asm语句之后使用__builtin_unreachable内置函数)。通过使用热和冷标签属性(请参见标签属性),可以改进asm goto的优化。
asm goto语句不能具有输出。这是由于编译器的内部限制:控制转移指令不能具有输出。如果汇编器代码确实进行了修改,请使用"memory" clobber强制优化器将所有寄存器值刷新到内存,并在必要时重新加载它们。
还请注意,asm goto语句始终被隐式视为易失性。
要在汇编模板中引用标签,请在标签前加上“%l”(小写的“L”),后跟其在GotoLabels中的(从零开始的)位置加上输入操作数的数量。例如,如果asm具有三个输入并引用两个标签,请将第一个标签称为“%l3”,第二个标签称为“%l4”。
或者,您可以使用括号括起来的实际C标签名称引用标签。例如,要引用名为carry的标签,可以使用“%l [carry]”。使用此方法时,标签仍必须在GotoLabels部分中列出。
代码可以这样编写:
int main(void) {
__asm__ goto ("jmp %l[exit]" :::: exit);
handler:
__asm__ __volatile__("jmp 0x0");
exit:
return 0;
}
我们可以使用
asm goto
。我更喜欢
__asm__
而不是
asm
,因为它不会在使用
-ansi
或
-std =?
选项编译时引发警告。
在输入输出参数之后,您可以列出内联汇编可能使用的跳转目标。
C实际上不知道我们是否跳过了,因为GCC不分析内联汇编模板中的实际代码。它不能删除此跳转,也不能假定其后面是死代码。使用
Godbolt使用GCC 4.6.4,未经优化的代码(已削减)如下:
main:
pushl %ebp
movl %esp, %ebp
jmp .L2 # <------ this is the goto exit
jmp 0x0
.L2: # <------ exit label
movl $0, %eax
popl %ebp
ret
The
Godbolt与GCC 4.6.4输出仍然是正确的,显示为:
main:
jmp .L2 # <------ this is the goto exit
jmp 0x0
.L2: # <------ exit label
xorl %eax, %eax
ret
无论您打开还是关闭优化,这种机制都应该起作用,并且不应该影响您为64位或32位x86目标进行编译。
其他观察
When there are no output constraints in an extended inline assembly template the asm
statement is implicitly volatile. The line
__asm__ __volatile__("jmp 0x0");
Can be written as:
__asm__ ("jmp 0x0");
asm goto
statements are considered implicitly volatile. They don't require a volatile
modifier either.
__asm__
中指定了__volatile__
?这似乎是多余的,不是吗? - Eitan Tused
和always_inline
。将asm
存根放置在一个always_inline,used
函数中。 - artless noise