在 gcc 内联 x86_64 程序集中约束 r10 寄存器

标签 c gcc x86-64 system-calls inline-assembly

我正在尝试编写一个非常轻量级的 libc 替换库,以便我可以更好地理解内核 - 应用程序接口(interface)。第一项任务显然是准备一些系统调用包装器。我已经成功地让 1 到 3 个参数包装器工作,但我正在努力处理 4 个参数变体。这是我的出发点:

long _syscall4(long type, long a1, long a2, long a3, long a4)
{
    long ret;
    asm
    (
        "syscall"
        : "=a"(ret)    // return value
        : "a"(type), "D"(a1), "S"(a2), "d"(a3), "r10"(a4)
        : "c", "r11", "memory"  // some syscalls read or write memory
                   // the syscall insn instruction itself destroys RCX and R11
    );
    return ret;
}

(编者注:在应用 answer 的方法处理 r10 之后,这是安全可用的,也是一个很好的例子。MUSL libc 有一些类似的宏。)

编译器给我以下错误:

error: matching constraint references invalid operand number

我的 _syscall3 函数工作正常,但不使用 r10 或有一个破坏列表。

(编者注:没有 clobber 列表是不安全的:您需要告诉编译器 RCX 和 R11 已被覆盖,并且“内存”应该在可能读取或写入的系统调用之前同步内存。如果你想为特定的系统调用编写特定的包装器,你可以有选择地省略 "memory" 破坏,或者根据哪些参数是该系统调用的指针来使用虚拟内存操作数。

如果此 _syscall4 函数无法内联,则寄存器和 “内存” 破坏实际上不会造成任何问题,但您应该 使这些能够内联;与调用非内联包装函数相比,内联此系统调用将在调用站点占用更少的代码。)

最佳答案

寄存器没有限制:%r8 .. %15。然而,更新的(如在 gcc-4.x 中)应该接受:

register long r10 asm("r10") = a4;

然后使用输入约束:"r"(r10) 作为您的 asm 语句。
https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html


请注意,强制选择扩展 asm 的 “r” 约束是 GCC 为寄存器 asm 局部变量保证的唯一行为。 register void *rsp asm("rsp");void *stack_pointer = rsp; 之类的东西有时会起作用,但不能保证并且不再推荐。


你会希望你的系统调用包装器 asm 语句是 volatile 并且有一个 "memory" 破坏,除非你写特定的特定系统调用的包装器,使用虚拟内存输入或输出(根据 How can I indicate that the memory *pointed* to by an inline ASM argument may be used? )了解哪些参数是指针

它需要 volatile 因为执行 write(1, buf, 16) 应该打印缓冲区两次,而不仅仅是 CSE返回值!系统调用通常不是其输入的纯函数,因此您需要volatile

(一些特定的系统调用包装器,如 getpid 可能是非 volatile 的,因为它们每次都返回相同的东西,除非你也使用 fork。但是 getpid 是如果通过 VDSO 完成效率更高,因此如果您在 Linux 上,它不必首先进入内核,因此如果您正在为 getpid 制作自定义包装器clock_gettime 您可能一开始就不想使用 syscall。请参阅 The Definitive Guide to Linux System Calls )

“内存” 破坏是必需的,因为寄存器中的指针暗示指向的内存也是输入或输出。仅由 write 系统调用读取的缓冲区存储需要作为死存储进行优化。或者对于 munmap,编译器最好在内存未映射之前 完成所有加载/存储。一些系统调用不接受任何指针输入,也不需要 “内存”,但通用包装器必须做出最坏情况的假设。

register ... asm("r10") 一般需要 asm volatile"memory" 破坏,但系统调用包装器会破坏。

关于在 gcc 内联 x86_64 程序集中约束 r10 寄存器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15997759/

相关文章:

c - 为什么变量存储的值与用户输入的值不同?

c - 使用 Visual Studio 2015 调试 C DLL

c++ - 预编译 header 给出杂散的 216 错误

c - 附加到 __VA_ARGS__

c - 如何通过单个 Makefile 使用不同的标志组合自动编译代码

c++ - sizeof(atomic<T>) 并不总是等于 sizeof<T>

x86-64 - 在 x86-64 上, "movnti"指令是原子的吗?

c - printf(..) 参数的执行顺序是什么?

c - 如何为堆栈实现调试此 C 代码?

assembly - 缺少uop缓存的粒度