【问题标题】:Use raw binary blob of instructions in C code在 C 代码中使用原始二进制指令块
【发布时间】:2021-02-04 10:32:25
【问题描述】:

我正在使用 TCC(Tiny C 编译器)进行 jit 编译,但它对汇编的支持有限,我经常被这个卡住......我想知道是否有某种技巧可以插入 raw指令到内联汇编?如:

mov -0x18(%rbp), %rax
finit
flds (%rax)

/* Custom unsupported binary instructions here */

flds (%rcx)

我知道这不是一件容易维护的事情,但我希望 TCC 保持不变。

【问题讨论】:

  • 如果它支持像.byte 0x12这样的标准GAS / unix-assembler指令,你可以发出任何你想要的字节序列。
  • 完美!谢谢!
  • 现在我只需要找到设置将其标记为解决方案...

标签: c assembly inline-assembly jit tcc


【解决方案1】:

如果它支持标准 GAS / unix-assembler 指令,如.byte 0x00, 0x12,您可以发出任何您想要的字节序列。 (如果您想将 16 位或 32 位立即数写入单个 32 位数字,也可以使用 .word.long。)

GNU as manual

【讨论】:

    【解决方案2】:

    GNU as 支持两种处理原始指令的好方法。有.byte.short.longsimilar directives可用于直接在输出中发出原始数字。

    另一种方式(仅针对少数架构实现)是.insn 指令,它允许在普通汇编指令和上述原始数字之间提供一种中间方式。例如,请参阅RISC-V .insn formats 的文档。

    要在 C 中使用它们中的任何一个,您可以使用(非标准)asm 语句(或其变体)。以下示例使用 GCC 的 naked 函数属性(也仅在某些架构上可用)来防止编译器发出任何其他指令(例如,函数序言/结尾),这很容易破坏寄存器。

    此示例适用于 RISC-V,显示标准的 32 位指令以及压缩的 16 位指令:

    void __attribute__ ((naked)) rawinst (void) {
      __asm__ volatile ("li t5,1");
      __asm__ volatile (".short 0x4f05"); // li t5,1
      __asm__ volatile ("lui t5, 0x1c000");
      __asm__ volatile (".word 0x1c000f37"); // lui t5, 0x1c000
      __asm__ volatile (".insn r 0x33, 0, 0, a0, a1, a2"); // add a0, a1, a2
      __asm__ volatile ("ret");
    }
    

    如果您想在非naked 函数中使用它,请使用带有约束/破坏器的 GNU C Extended asm 来告诉编译器您的 asm 做什么以及输入/输出在哪里。当然不要使用你自己的ret。至少使用 gcc/clang;如果您实际使用的是 TCC,它根本不会在 C 语句之间进行优化,因此对我们 Basic asm 语句来说应该是安全的。

    【讨论】:

    • __attribute__((naked)) 使用像这样的 GNU C Basic asm 语句所必需的,除了不修改寄存器或内存的指令。如果在非裸函数中使用此类指令,您可能希望 GNU C Extended asm 指定 "memory" clobber 或不控制排序 wrt。通过周围的 C 语句访问内存。 (就像您禁用中断或执行内存屏障指令一样)。
    • 如果您在naked 函数之外修改寄存器,您必须告诉编译器。否则,当此函数内联到更大的调用者时,您的代码可能会完全中断。 (在这种情况下,您不得自己使用 ret 之类的指令。)请参阅 gcc.gnu.org/wiki/ConvertBasicAsmToExtended 回复:为什么除了 naked 函数之外的任何东西都很糟糕。
    • 我在原始答案中简要提到了这些问题(顺便说一句,类似的问题也适用于您的问题),但是,并非所有指令都适用于寄存器或内存。我正在设计一个带有自定义指令的设计,这些指令与这些问题为零的外部模块交互。所以这在很大程度上取决于具体的应用。我不认为任何不知道自己在做什么的人会因为这个问题而绊倒,但如果是这样,这是一个很好的机会,为了教育目的而在脚下开枪;)
    • 使用自定义指令进行设计,这些指令与这些问题为零的外部模块交互 - 有道理。但是,对于您在答案中包含的示例,情况并非如此,该示例确实写入了寄存器,因此您的答案需要修复。关于 SO 的 许多 内联 asm 问题真的很幼稚,以至于他们认为 asm("stuff") 应该只是在正常功能中工作,就像 MSVC 风格的 asm{} 块一样。鉴于问题标题,我可以很容易地想象天真的未来读者会在这里登陆,不知道 GNU C 内联汇编是如何工作的。
    • 在我的回答中,我没有提到它,但我只是在谈论 GAS 语法,而不是如何使用内联 asm。另外,这个问题被标记为 [TCC],我什至不确定它是否支持 GNU C 扩展 asm,所以我只关注 GAS 的详细信息。但是考虑到问题的标题和标签,它很可能会引起其他读者的注意(因为 TCC 非常好),所以我很高兴你发布了这个答案,我们可以在其中讨论 GNU C 扩展。
    猜你喜欢
    • 2017-07-19
    • 1970-01-01
    • 2011-08-08
    • 1970-01-01
    • 2010-10-22
    • 1970-01-01
    • 1970-01-01
    • 2021-02-20
    • 2017-08-06
    相关资源
    最近更新 更多