从内联汇编跳转到 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

所以,问题是:

  1. 我做错了什么吗?我见过这种跳跃(来自 在代码中汇编到 C)。我可以提供示例链接。
  2. 为什么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 漏洞利用,那是因为显然分支从未真正被采用。