UNIX&;的调用约定是什么;i386和x86-64上的Linux系统调用(和用户空间函数)
以下链接解释了UNIX(BSD风格)和Linux的x86-32系统调用约定:UNIX&;的调用约定是什么;i386和x86-64上的Linux系统调用(和用户空间函数),linux,assembly,x86-64,calling-convention,abi,x86,Linux,Assembly,X86 64,Calling Convention,Abi,X86,以下链接解释了UNIX(BSD风格)和Linux的x86-32系统调用约定: 但是UNIX和Linux上的x86-64系统调用约定是什么呢?也许您正在寻找x86\u 64 ABI (2018年11月24日第404页) (2018年11月24日通过回程机) -保持最新(由ABI维护人员之一HJ Lu提供),并提供官方最新版本PDF的链接 如果您的目标并非如此,请在首选搜索引擎中使用“x86_64 abi”查找其他参考资料。有关此处任何主题的进一步阅读: 我在Linux上使用GNU汇编
但是UNIX和Linux上的x86-64系统调用约定是什么呢?也许您正在寻找x86\u 64 ABI
- (2018年11月24日第404页)
- (2018年11月24日通过回程机)
- -保持最新(由ABI维护人员之一HJ Lu提供),并提供官方最新版本PDF的链接
如果您的目标并非如此,请在首选搜索引擎中使用“x86_64 abi”查找其他参考资料。有关此处任何主题的进一步阅读:
我在Linux上使用GNU汇编程序(gas)验证了这些 内核接口 x86-32又名i386 Linux系统调用约定: 在x86-32中,Linux系统调用的参数使用寄存器传递<代码>%eax用于系统调用号。%ebx、%ecx、%edx、%esi、%edi、%ebp用于向系统调用传递6个参数 返回值位于
%eax
中。所有其他寄存器(包括EFLAG)都在int$0x80
中保留
我从中提取了以下片段,但我对此表示怀疑。如果有人能举个例子,那就太好了
如果有六个以上的论点,
%ebx
必须包含内存
参数列表所在的位置
已存储—但不要担心这一点
因为你不太可能用
超过六个的系统调用
争论
有关示例和更多阅读,请参阅。使用int 0x80
的i386 Linux Hello World的另一个示例:
进行32位系统调用有一种更快的方法:使用sysenter
。内核将一页内存映射到每个进程(vDSO),并使用sysenter
dance的用户空间端,它必须与内核协作才能找到返回地址。Arg到寄存器的映射与int$0x80的映射相同。您通常应该调用vDSO,而不是直接使用sysenter
。(有关链接和调用vDSO的信息,以及有关系统调用的更多信息,请参阅。)
x86-32[Free | Open | Net | drangfly]BSD UNIX系统调用约定:
参数在堆栈上传递。将参数(最后一个先推的参数)推送到堆栈上。然后推送额外的32位伪数据(实际上不是伪数据。有关更多信息,请参阅以下链接),然后给出系统调用指令int$0x80
x86-64 Linux系统调用约定: (注意:来自Linux。TODO:检查*BSD的功能) 请参阅的“A.2 AMD64Linux内核约定”一节。可以找到i386和x86-64 System V psABIs的最新版本。(有关最新的ABI链接和许多其他关于x86 asm的好东西,请参见tag wiki。) 以下是本节的片段:
syscall
指令完成。这与%rax返回值一样,但保留了其他寄存器-errno
int$0x80
ABI可用于64位代码(但强烈不推荐)。它仍然将其输入截断为32位,因此不适用于指针,并将r8-r11置零
用户界面:函数调用
x86-32函数调用约定:
在x86-32中,参数在堆栈上传递。最后一个参数首先被推送到堆栈上,直到完成所有参数,然后执行call
指令。这用于从程序集调用Linux上的C库(libc)函数
现代版本的i386 System V ABI(在Linux上使用)需要在调用之前对%esp
进行16字节对齐,就像x86-64 System V ABI一直需要的那样。被调用者可以假设并使用SSE 16字节加载/存储未对齐上的故障。但从历史上看,Linux只需要4字节的堆栈对齐,因此需要额外的工作才能保留自然对齐的空间,即使是8字节的double
之类的东西
其他一些现代32位系统仍然不需要超过4字节的堆栈对齐
x86-64 System V用户空间函数调用约定:
x86-64 System V在寄存器中传递参数,这比i386 System V的堆栈参数约定更有效。它避免了将参数存储到内存(缓存),然后在被调用方中重新加载这些参数的延迟和额外指令。这是因为有更多的寄存器可用,对于延迟和无序执行很重要的现代高性能CPU来说效果更好。(i386 ABI非常古老)
在这种新机制中:首先将参数划分为类。每个参数的类确定将其传递给被调用函数的方式
有关完整信息,请参阅“3.2函数调用序列”,其中部分内容如下:
一旦参数是c
/*
* 64-bit SYSCALL instruction entry. Up to 6 arguments in registers.
*
* This is the only entry point used for 64-bit system calls. The
* hardware interface is reasonably well designed and the register to
* argument mapping Linux uses fits well with the registers that are
* available when SYSCALL is used.
*
* SYSCALL instructions can be found inlined in libc implementations as
* well as some other programs and libraries. There are also a handful
* of SYSCALL instructions in the vDSO used, for example, as a
* clock_gettimeofday fallback.
*
* 64-bit SYSCALL saves rip to rcx, clears rflags.RF, then saves rflags to r11,
* then loads new ss, cs, and rip from previously programmed MSRs.
* rflags gets masked by a value from another MSR (so CLD and CLAC
* are not needed). SYSCALL does not save anything on the stack
* and does not change rsp.
*
* Registers on entry:
* rax system call number
* rcx return address
* r11 saved rflags (note: r11 is callee-clobbered register in C ABI)
* rdi arg0
* rsi arg1
* rdx arg2
* r10 arg3 (needs to be moved to rcx to conform to C ABI)
* r8 arg4
* r9 arg5
* (note: r12-r15, rbp, rbx are callee-preserved in C ABI)
*
* Only called from user space.
*
* When user can change pt_regs->foo always force IRET. That is because
* it deals with uncanonical addresses better. SYSRET has trouble
* with them due to bugs in both AMD and Intel CPUs.
*/
/*
* 32-bit SYSENTER entry.
*
* 32-bit system calls through the vDSO's __kernel_vsyscall enter here
* if X86_FEATURE_SEP is available. This is the preferred system call
* entry on 32-bit systems.
*
* The SYSENTER instruction, in principle, should *only* occur in the
* vDSO. In practice, a small number of Android devices were shipped
* with a copy of Bionic that inlined a SYSENTER instruction. This
* never happened in any of Google's Bionic versions -- it only happened
* in a narrow range of Intel-provided versions.
*
* SYSENTER loads SS, ESP, CS, and EIP from previously programmed MSRs.
* IF and VM in RFLAGS are cleared (IOW: interrupts are off).
* SYSENTER does not save anything on the stack,
* and does not save old EIP (!!!), ESP, or EFLAGS.
*
* To avoid losing track of EFLAGS.VM (and thus potentially corrupting
* user and/or vm86 state), we explicitly disable the SYSENTER
* instruction in vm86 mode by reprogramming the MSRs.
*
* Arguments:
* eax system call number
* ebx arg1
* ecx arg2
* edx arg3
* esi arg4
* edi arg5
* ebp user stack
* 0(%ebp) arg6
*/
/* The Linux/x86-64 kernel expects the system call parameters in
registers according to the following table:
syscall number rax
arg 1 rdi
arg 2 rsi
arg 3 rdx
arg 4 r10
arg 5 r8
arg 6 r9
The Linux kernel uses and destroys internally these registers:
return address from
syscall rcx
eflags from syscall r11
Normal function call, including calls to the system call stub
functions in the libc, get the first six parameters passed in
registers and the seventh parameter and later on the stack. The
register use is as follows:
system call number in the DO_CALL macro
arg 1 rdi
arg 2 rsi
arg 3 rdx
arg 4 rcx
arg 5 r8
arg 6 r9
We have to take care that the stack is aligned to 16 bytes. When
called the stack is not aligned since the return address has just
been pushed.
Syscalls of more than 6 arguments are not supported. */
/* Registers clobbered by syscall. */
# define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"
#undef internal_syscall6
#define internal_syscall6(number, err, arg1, arg2, arg3, arg4, arg5, arg6) \
({ \
unsigned long int resultvar; \
TYPEFY (arg6, __arg6) = ARGIFY (arg6); \
TYPEFY (arg5, __arg5) = ARGIFY (arg5); \
TYPEFY (arg4, __arg4) = ARGIFY (arg4); \
TYPEFY (arg3, __arg3) = ARGIFY (arg3); \
TYPEFY (arg2, __arg2) = ARGIFY (arg2); \
TYPEFY (arg1, __arg1) = ARGIFY (arg1); \
register TYPEFY (arg6, _a6) asm ("r9") = __arg6; \
register TYPEFY (arg5, _a5) asm ("r8") = __arg5; \
register TYPEFY (arg4, _a4) asm ("r10") = __arg4; \
register TYPEFY (arg3, _a3) asm ("rdx") = __arg3; \
register TYPEFY (arg2, _a2) asm ("rsi") = __arg2; \
register TYPEFY (arg1, _a1) asm ("rdi") = __arg1; \
asm volatile ( \
"syscall\n\t" \
: "=a" (resultvar) \
: "0" (number), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4), \
"r" (_a5), "r" (_a6) \
: "memory", REGISTERS_CLOBBERED_BY_SYSCALL); \
(long int) resultvar; \
})
.text
.global _start
_start:
asm_main_after_prologue:
/* write */
mov $1, %rax /* syscall number */
mov $1, %rdi /* stdout */
mov $msg, %rsi /* buffer */
mov $len, %rdx /* len */
syscall
/* exit */
mov $60, %rax /* syscall number */
mov $0, %rdi /* exit status */
syscall
msg:
.ascii "hello\n"
len = . - msg