【发布时间】:2020-06-07 02:02:49
【问题描述】:
下面的宏可以执行lstat系统调用。
#include <stdint.h>
#define m_lstat(PATH, FS){ \
long _rax = 6; /*sys_newlstat*/ \
uint8_t* _path = PATH; \
struct stat* _fs = FS; \
asm volatile( \
"movq %0, %%rax\n" \
"movq %1, %%rdi\n" \
"movq %2, %%rsi\n" \
"syscall" \
: \
:"m"(_rax), "m"(_path), "m"(_fs) \
:"rax", "rdi", "rsi" \
); \
}
可以像 glibc 包装器一样称呼它lstat:
#include <sys/stat.h>
#include <stdio.h>
int main(){
struct stat fs0; m_lstat("a.out", &fs0); printf("nbytes %d\n", fs0.st_size);
struct stat fs1; lstat( "a.out", &fs1); printf("nbytes %d\n", fs1.st_size);
}
但是,如果我想访问返回值怎么办?我认为它被写入rax,但我不知道如何从 C 代码中检索它...
例如,以下操作不起作用:
#define m_lstat(PATH, FS){ \
long _rax = 6; /*sys_newlstat*/ \
u8* _path = PATH; \
struct stat* _fs = FS; \
int ret; \
asm volatile( \
"movq %0, %%rax\n" \
"movq %1, %%rdi\n" \
"movq %2, %%rsi\n" \
"syscall" \
:"=m"(ret) \
:"m"(_rax), "m"(_path), "m"(_fs) \
:"rax", "rdi", "rsi" \
); \
printf("ret %d\n", ret); \
}
【问题讨论】:
-
您的代码不完整;如何重现它?
-
godbolt 链接上的这段代码应该可以工作:godbolt.org/z/yfpbE2。
syscall指令本身会破坏 RCX 和 R11。我对约束使用适当的寄存器,而不是使用m(S=RSI,D=RDI)。我使用输出和输入约束,将输入约束绑定到与输出约束相同的寄存器 (RAX)。这允许通过一个变量传入值并通过另一个变量传出值。我还使用了内存破坏器,因为我正在传递数据的地址(在 RSI 和 RDI 中)并且必须确保编译器在调用内联程序集之前意识到内存中的数据。 -
@tansy 我添加了一些样板,以便您可以实际编译它。
-
您几乎从不想在内联 asm 中使用显式 mov 指令——它充其量是浪费和低效的,而且如果您没有正确使用 clobbers,通常会损坏。相反,您想使用约束。试试
asm volatile("syscall" : "=a" (ret) : "a" (_rax), "D" (_path), "S" (_fs)); -
作为 Michael 所做的补充(看起来不错),我可能会为 _rax 和 _path 添加一个
const。它不会更改生成的代码,但您的路径可能已经是一个 const 并且(不必要地)将其转换回非常量以进行此调用会很愚蠢。 lstat 不会为您获取返回值(errno)吗?使用内联 asm 执行此操作有什么价值?
标签: linux gcc x86-64 system-calls inline-assembly