在浏览一些具有内联汇编的 C 代码时,我遇到了 .byte (开头有一个点)指令。
在检查网络上的程序集参考时,我发现它用于在内存中保留一个字节。
但是在代码中,语句之前没有标签。因此,我想知道未标记的 .byte 指令或任何其他数据存储指令的用途是什么。
例如,如果我编码.byte 0x0a
,我该如何使用它?
在浏览一些具有内联汇编的 C 代码时,我遇到了 .byte (开头有一个点)指令。
在检查网络上的程序集参考时,我发现它用于在内存中保留一个字节。
但是在代码中,语句之前没有标签。因此,我想知道未标记的 .byte 指令或任何其他数据存储指令的用途是什么。
例如,如果我编码.byte 0x0a
,我该如何使用它?
有几种可能性......这里有几个我能想到的:
您可以相对于指令之后的标签访问它。.byte
例子:
.byte 0x0a
label:
mov (label - 1), %eax
根据程序的最终链接布局,.byte
指令可能会作为代码执行。通常,在这种情况下你也会有一个标签,虽然......
一些汇编器不支持为操作数大小等生成 x86 指令前缀。在为这些汇编器编写的代码中,您经常会看到如下内容:
.byte 0x66
mov $12, %eax
让汇编器发出你想要的代码。
最小可运行示例
.byte
无论您身在何处,都会吐出字节。是否有标签指向字节,无关紧要。
如果您碰巧在文本段中,那么该字节可能会像代码一样运行。
Carl 提到了它,但这里有一个完整的例子让它进一步深入:一个 Linux x86_64 实现,true
其中包含nop
:
.global _start
_start:
mov $60, %rax
nop
mov $0, %rdi
syscall
产生完全相同的可执行文件:
.global _start
_start:
mov $60, %rax
.byte 0x90
mov $0, %rdi
syscall
因为nop
被编码为字节0x90
。
一个用例:新指令
一个用例是将新指令添加到 CPU ISA 中,但只有非常边缘的汇编程序版本会支持它。
因此项目维护者可以选择直接内联字节以使其可在较旧的汇编器上编译。
.inst
例如,使用类似指令在 Linux 内核上查看这个 Spectre 解决方法: https ://github.com/torvalds/linux/blob/94710cac0ef4ee177a63b5227664b38c95bbf703/arch/arm/include/asm/barrier.h#L23
#define CSDB ".inst 0xe320f014"
为 Spectre 添加了一条新指令,内核决定暂时对其进行硬编码。
这是一个内联汇编的例子:
#include <stdio.h>
void main() {
int dst;
// .byte 0xb8 0x01 0x00 0x00 0x00 = mov $1, %%eax
asm (".byte 0xb8, 0x01, 0x00, 0x00, 0x00\n\t"
"mov %%eax, %0"
: "=r" (dst)
: : "eax" // tell the compiler we clobber eax
);
printf ("dst value : %d\n", dst);
return;
}
(请参阅编译器 asm 输出以及 Godbolt 编译器资源管理器上最终二进制文件的反汇编。)
您可以替换.byte 0xb8, 0x01, 0x00, 0x00, 0x00
为mov $1, %%eax
运行结果将是相同的。这表明它可以是一个字节,可以表示一些指令,例如移动或其他。
.byte 是一个指令,它允许您声明一个仅通过检查才知道的常量字节,而无需任何上下文。
来自 GNU 汇编器指南:
.byte 74, 0112, 092, 0x4A, 0X4a, 'J, '\J # All the same value.