从内联汇编跳转到 C 的标签
Jump to a label from inline assembly to C
我有一段汇编代码,在其中的某些地方,我想跳转到 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 [=12=]x0,%eax
66c: 5d pop %rbp
66d: c3 retq
66e: 66 90 xchg %ax,%ax
所以,问题是:
- 我做错了什么吗?我见过这种跳跃(来自
在代码中汇编到 C)。我可以提供示例链接。
- 为什么compiler/linker找不到
l1
却能找到end
?
这就是 asm goto
的用途。 GCC Inline Assembly: Jump to label outside block
请注意,在另一个 asm 语句中定义标签 有时会起作用(例如禁用优化),但并不安全。
asm("end:"); // BROKEN; NEVER USE
// except for toy experiments to look at compiler output
GNU C 没有定义从一个 asm 语句跳到另一个没有 asm goto
的行为。允许编译器假设执行是在 asm
语句的末尾,例如在它后面放一个商店。
给定函数中的 C end:
标签 不会 仅具有 end
或 _end:
的 asm 符号名称 - 即没有意义,因为每个单独的 C 函数都允许有自己的 end:
标签。它 可能 类似于 main.end
但事实证明 GCC 和 clang 只是使用它们通常的自动编号标签,例如 .L123
.
Then how this code works: https://github.com/IAIK/transientfail/blob/master/pocs/spectre/PHT/sa_oop/main.c
没有; asm volatile("je end");
引用的 end
标签位于 .data
部分,并且恰好由编译器或链接器定义以标记该部分的结尾 .
asm volatile("je end")
与该函数中的 C 标签无关。
我注释掉了其他函数中的一些代码,使其在没有 "cacheutils.h" header 的情况下进行编译,但这并没有影响 oop()
函数的那部分;请参阅 https://godbolt.org/z/jabYu3 以了解链接可执行文件的反汇编,其中 JE_4k 已更改为 JE_16,因此它并不大。它是链接可执行文件的反汇编,因此您可以看到 je 6010f0 <_end>
的数字地址,而 oop
函数本身从 4006e0 开始到 400750 结束。(因此它不包含分支目标)。
如果这恰好适用于 Spectre 漏洞利用,那是因为显然分支从未真正被采用。
我有一段汇编代码,在其中的某些地方,我想跳转到 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 [=12=]x0,%eax
66c: 5d pop %rbp
66d: c3 retq
66e: 66 90 xchg %ax,%ax
所以,问题是:
- 我做错了什么吗?我见过这种跳跃(来自 在代码中汇编到 C)。我可以提供示例链接。
- 为什么compiler/linker找不到
l1
却能找到end
?
这就是 asm goto
的用途。 GCC Inline Assembly: Jump to label outside block
请注意,在另一个 asm 语句中定义标签 有时会起作用(例如禁用优化),但并不安全。
asm("end:"); // BROKEN; NEVER USE
// except for toy experiments to look at compiler output
GNU C 没有定义从一个 asm 语句跳到另一个没有 asm goto
的行为。允许编译器假设执行是在 asm
语句的末尾,例如在它后面放一个商店。
给定函数中的 C end:
标签 不会 仅具有 end
或 _end:
的 asm 符号名称 - 即没有意义,因为每个单独的 C 函数都允许有自己的 end:
标签。它 可能 类似于 main.end
但事实证明 GCC 和 clang 只是使用它们通常的自动编号标签,例如 .L123
.
Then how this code works: https://github.com/IAIK/transientfail/blob/master/pocs/spectre/PHT/sa_oop/main.c
没有; asm volatile("je end");
引用的 end
标签位于 .data
部分,并且恰好由编译器或链接器定义以标记该部分的结尾 .
asm volatile("je end")
与该函数中的 C 标签无关。
我注释掉了其他函数中的一些代码,使其在没有 "cacheutils.h" header 的情况下进行编译,但这并没有影响 oop()
函数的那部分;请参阅 https://godbolt.org/z/jabYu3 以了解链接可执行文件的反汇编,其中 JE_4k 已更改为 JE_16,因此它并不大。它是链接可执行文件的反汇编,因此您可以看到 je 6010f0 <_end>
的数字地址,而 oop
函数本身从 4006e0 开始到 400750 结束。(因此它不包含分支目标)。
如果这恰好适用于 Spectre 漏洞利用,那是因为显然分支从未真正被采用。