如何通过内联汇编使用syscall或sysenter来调用系统调用?
如何通过内联汇编使用syscall或sysenter来调用系统调用?
在x86 Linux中,我们如何直接使用sysenter/syscall来实现系统调用?有人能提供帮助吗?如果您还能为amd64平台提供代码,那就更好了。
我知道在x86中,我们可以使用以下代码间接地路由到sysenter:
__asm__( " movl $1, %eax \n" " movl $0, %ebx \n" " call *%gs:0x10 \n" );
但我们如何直接使用sysenter/syscall来发起系统调用呢?我在这里找到了一些资料http://damocles.blogbus.com/tag/sysenter/。但仍然感到困惑。
如何通过syscall或sysenter在内联汇编中调用系统调用?
问题的出现原因:
- 在GCC中,register约束的方式无法表示所有寄存器,包括用于系统调用参数的r8、r9和r10寄存器。
- 对于其他的ISA(指令集体系结构)例如ARM,没有魔术寄存器约束名称,因此register约束无法使用。
解决方法:
- 可以使用显式寄存器变量来解决这个问题。
- 显式寄存器变量可以表示所有寄存器,包括r8、r9和r10。
- 对于其他ISA,如ARM,显式寄存器变量是唯一的最佳选择。
以下是一个使用显式寄存器变量调用系统调用的示例代码:
#define _XOPEN_SOURCE 700 #include#include ssize_t my_write(int fd, const void *buf, size_t size) { register int64_t rax __asm__ ("rax") = 1; register int rdi __asm__ ("rdi") = fd; register const void *rsi __asm__ ("rsi") = buf; register size_t rdx __asm__ ("rdx") = size; __asm__ __volatile__ ( "syscall" : "+r" (rax) : "r" (rdi), "r" (rsi), "r" (rdx) : "rcx", "r11", "memory" ); return rax; } void my_exit(int exit_status) { register int64_t rax __asm__ ("rax") = 60; register int rdi __asm__ ("rdi") = exit_status; __asm__ __volatile__ ( "syscall" : "+r" (rax) : "r" (rdi) : "rcx", "r11", "memory" ); } void _start(void) { char msg[] = "hello world\n"; my_exit(my_write(1, msg, sizeof(msg)) != sizeof(msg)); }
在这个示例代码中,通过显式寄存器变量来指定系统调用的参数,然后使用syscall指令来调用系统调用。这样可以实现在内联汇编中调用系统调用的功能。
这种方法可以在GCC中使用,并且可以表示所有寄存器,同时也适用于其他ISA,如ARM。
如何通过syscall或sysenter在内联汇编中调用系统调用?
问题的原因:
- 在GNU C基本asm("")语法中使用此问题是不安全的。你需要使用扩展的asm来告诉编译器你修改的寄存器。
- 需要使用asm volatile,因为对于具有一个或多个输出操作数的扩展asm语句,这不是隐含的。
解决方法:
- 对于i386 Linux,系统调用是使用第128个中断向量实现的,例如在汇编代码中调用int 0x80,并在此之前设置相应的参数。
- 对于amd64架构,使用新的syscall指令。系统调用的编号仍然传递给rax寄存器,但用于保存参数的寄存器现在几乎与函数调用约定一致。
下面是一个示例代码:
// i386 Linux #include// compile with -m32 for 32 bit call numbers //#define __NR_write 4 ssize_t my_write(int fd, const void *buf, size_t size) { ssize_t ret; asm volatile ( "int $0x80" : "=a" (ret) : "0"(__NR_write), "b"(fd), "c"(buf), "d"(size) : "memory" // the kernel dereferences pointer args ); return ret; } // x86-64 Linux #include // compile without -m32 for 64 bit call numbers // #define __NR_write 1 ssize_t my_write(int fd, const void *buf, size_t size) { ssize_t ret; asm volatile ( "syscall" : "=a" (ret) // EDI RSI RDX : "0"(__NR_write), "D"(fd), "S"(buf), "d"(size) : "rcx", "r11", "memory" ); return ret; }
这些代码分别演示了在i386和amd64架构上如何调用系统调用。在i386上,使用int 0x80指令,而在amd64上,使用syscall指令。在这些代码中,系统调用的编号传递给寄存器rax,并将参数分别存储在寄存器ebx、ecx、edx、esi、edi和ebp中。
需要注意的是,不同的操作系统(如MacOS)使用不同的系统调用编号和参数传递约定。
本文介绍了如何在内联汇编中通过syscall或sysenter调用系统调用。通过使用适当的寄存器和指令,可以在不同的架构上实现系统调用。但是需要注意不同操作系统的系统调用编号和参数传递约定的差异。