我在汇编中有一段书面代码,在某些时候,我想跳到C
中的标签。所以我有以下代码(简化版,但仍然有同样的问题):
#include <stdio.h>
#define JE asm volatile("jmp end");
int main(){
printf("hi\n");
JE
printf("Invisible\n");
end:
printf("Visible\n");
return 0;
}
此代码已编译,但反汇编版本中没有end
标签。
如果我将标签名称从end
更改为其他任何东西(比如说l1
,无论是在asm代码(jmp l1
)中还是在C
代码中,编译器都会说
main.c:(.text+0x6b): undefined reference to `l1'
collect2: error: ld returned 1 exit status
Makefile:2: recipe for target 'main' failed
make: *** [main] Error 1
我尝试了不同的操作(不同的长度,不同的大小写,上,下等),我认为它只能使用end
标签进行编译。使用end
标签,我收到分段错误,因为在反汇编版本中没有end
标签。
编译: gcc -O0 main.c -o main
反汇编代码:
000000000000063a <main>:
63a: 55 push %rbp
63b: 48 89 e5 mov %rsp,%rbp
63e: 48 8d 3d af 00 00 00 lea 0xaf(%rip),%rdi # 6f4 <_IO_stdin_used+0x4>
645: e8 c6 fe ff ff callq 510 <puts@plt>
64a: e9 c9 09 20 00 jmpq 201018 <_end> # there is no _end label!
64f: 48 8d 3d a1 00 00 00 lea 0xa1(%rip),%rdi # 6f7 <_IO_stdin_used+0x7>
656: e8 b5 fe ff ff callq 510 <puts@plt>
65b: 48 8d 3d 9f 00 00 00 lea 0x9f(%rip),%rdi # 701 <_IO_stdin_used+0x11>
662: e8 a9 fe ff ff callq 510 <puts@plt>
667: b8 00 00 00 00 mov $0x0,%eax
66c: 5d pop %rbp
66d: c3 retq
66e: 66 90 xchg %ax,%ax
所以,问题是:
l1
但可以找到end
?这就是asm goto
的作用。 GCC Inline Assembly: Jump to label outside block
请注意,定义标签inside另一个asm语句有时会起作用(例如,禁用优化),但并不安全。
asm("end:"); // BROKEN; NEVER USE
// except for toy experiments to look at compiler output
[GNU C没有定义没有asm goto
的情况下从一个asm语句跳转到另一asm语句的行为。允许编译器假定执行是在asm
语句的末尾执行的,例如在它后面放一个商店。
给定函数中的C end:
标签不会仅具有end
或_end:
的asm符号名称-这没有意义,因为每个单独的C函数都允许具有各自的C函数拥有end:
标签。 可能类似于main.end
,但事实证明,GCC和clang仅使用其通常的自动编号标签,例如.L123
。
然后此代码的工作方式:https://github.com/IAIK/transientfail/blob/master/pocs/spectre/PHT/sa_oop/main.c
不会; end
引用的asm volatile("je end");
标签在.data
部分中,并且碰巧是由编译器或链接器定义的,以标记该部分的结尾。
[asm volatile("je end")
与该功能中的C标签无关。
我注释掉了其他函数中的某些代码,以使其在没有“ cacheutils.h”标头的情况下进行编译,但这并不影响oop()
函数的那部分;有关将JE_4k更改为JE_16的链接可执行文件的反汇编,请参见https://godbolt.org/z/jabYu3,因此它并不大。它是对链接的可执行文件的分解,因此您可以看到je 6010f0 <_end>
的数字地址,而oop
函数本身的起始地址为4006e0,结束于400750。(因此,它不包含分支目标)。
如果这恰好适用于Spectre漏洞,那是因为显然该分支从未被实际采用。