【问题标题】:Jump to a label from inline assembly to C从内联汇编跳转到标签到 C
【发布时间】:2019-12-27 16:03:36
【问题描述】:

我在汇编中有一段书面代码,在某些地方,我想跳转到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

所以,问题是:

  1. 我做错了吗?我见过这种跳跃(从 汇编为 C) 的代码。我可以提供示例链接。
  2. 为什么编译器/链接器找不到l1,但可以找到end

【问题讨论】:

  • 编译器会在某些符号前添加下划线。所以 C 中的标签 end 将是汇编器中的标签 _end
  • 这就是asm goto 的用途。 GCC Inline Assembly: Jump to label outside block
  • @PaulOgilvie:不仅仅是_end。它可能类似于_main.end,或者更可能只是一个常见的自动编号标签,例如.L123:
  • 它没有;内联 asm 引用的 end 标签位于 .data 部分中,并且恰好由编译器或链接器定义以标记该部分的结尾。 asm volatile("je end") 与该函数中的 C 标签无关。我注释掉了其他函数中的一些代码,让它在没有“cacheutils.h”头文件的情况下编译,但这并不影响oop()函数的那部分;见godbolt.org/z/jabYu3
  • @kyurt7 可能适用于禁用优化的一些玩具实验,但它是未定义的行为,并且可能仅通过启用优化而中断。

标签: c gcc x86 inline-assembly goto


【解决方案1】:

这就是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 goto 的情况下从一个 asm 语句跳转到另一个的行为。允许编译器假设执行出现在 asm 语句的末尾,例如在它之后放置一个商店。


给定函数中的 C end: 标签不会只有 end_end: 的 asm 符号名称 - 这是没有意义的,因为每个单独的 C 函数允许拥有自己的end: 标签。它可能类似于main.end,但事实证明GCC和clang只是使用他们通常的自动编号标签,如.L123

那么这段代码是如何工作的: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”头文件的情况下编译,但这并不影响oop()函数的那部分;请参阅https://godbolt.org/z/jabYu3,了解将 JE_4k 更改为 JE_16 的链接可执行文件的反汇编,因此它并不大。它是一个链接可执行文件的反汇编,因此您可以看到je 6010f0 &lt;_end&gt; 的数字地址,而oop 函数本身从4006e0 开始,到400750 结束。(因此它不包含分支目标)。

如果这恰好适用于 Spectre 漏洞利用,那是因为显然该分支从未真正被占用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-19
    • 2017-11-01
    • 1970-01-01
    • 2012-04-22
    • 1970-01-01
    • 2015-04-08
    • 2013-07-23
    • 2019-10-28
    相关资源
    最近更新 更多