在 C 代码中使用原始二进制指令块
Use raw binary blob of instructions in C code
我正在使用 TCC(Tiny C 编译器)进行 jit 编译,但它对汇编的支持有限,我经常被这个问题困住...我想知道是否有某种技巧可以将原始指令插入内联汇编?如:
mov -0x18(%rbp), %rax
finit
flds (%rax)
/* Custom unsupported binary instructions here */
flds (%rcx)
我知道这不是一件容易维护的事情,但我想保持 TCC 不变。
如果它支持标准 GAS / unix-assembler 指令,例如 .byte 0x00, 0x12
,您可以发出任何您想要的字节序列。 (还有 .word
或 .long
,如果你想将 16 位或 32 位立即数写为单个 32 位数字。)
GNU as
支持两种处理原始指令的好方法。有 .byte
、.short
、.long
和 similar directives 可用于直接在输出中发出原始数字。
另一种方式(仅针对少数架构实现)是 .insn
指令,它允许普通汇编指令和上述原始数字之间的中间方式。例如,参见 RISC-V .insn
formats.
的文档
要在 C 中使用它们中的任何一个,您可以使用 (non-standard) asm
语句(或其变体)。以下示例使用 GCC 的 naked
函数属性(也仅在某些体系结构上可用)来防止编译器发出任何其他指令(例如,函数 prologue/epilogue),这很容易破坏寄存器。
此示例适用于 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 with constraints / clobbers to tell the compiler what your asm does and where the inputs/outputs are. And of course don't use your own ret
. At least with gcc/clang; if you're actually using TCC, it doesn't optimize at all between C statements so it should be safe to us Basic asm 语句。
我正在使用 TCC(Tiny C 编译器)进行 jit 编译,但它对汇编的支持有限,我经常被这个问题困住...我想知道是否有某种技巧可以将原始指令插入内联汇编?如:
mov -0x18(%rbp), %rax
finit
flds (%rax)
/* Custom unsupported binary instructions here */
flds (%rcx)
我知道这不是一件容易维护的事情,但我想保持 TCC 不变。
如果它支持标准 GAS / unix-assembler 指令,例如 .byte 0x00, 0x12
,您可以发出任何您想要的字节序列。 (还有 .word
或 .long
,如果你想将 16 位或 32 位立即数写为单个 32 位数字。)
GNU as
支持两种处理原始指令的好方法。有 .byte
、.short
、.long
和 similar directives 可用于直接在输出中发出原始数字。
另一种方式(仅针对少数架构实现)是 .insn
指令,它允许普通汇编指令和上述原始数字之间的中间方式。例如,参见 RISC-V .insn
formats.
要在 C 中使用它们中的任何一个,您可以使用 (non-standard) asm
语句(或其变体)。以下示例使用 GCC 的 naked
函数属性(也仅在某些体系结构上可用)来防止编译器发出任何其他指令(例如,函数 prologue/epilogue),这很容易破坏寄存器。
此示例适用于 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 with constraints / clobbers to tell the compiler what your asm does and where the inputs/outputs are. And of course don't use your own ret
. At least with gcc/clang; if you're actually using TCC, it doesn't optimize at all between C statements so it should be safe to us Basic asm 语句。