/* Copyright (C) 1992, 93, 95, 96 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper, , August 1995. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _LINUX_I386_SYSDEP_H #define _LINUX_I386_SYSDEP_H 1 /* There is some commonality. */ #include /* For Linux we can use the system call table in the header file /usr/include/asm/unistd.h of the kernel. But these symbols do not follow the SYS_* syntax so we have to redefine the `SYS_ify' macro here. */ #undef SYS_ify #ifdef __STDC__ # define SYS_ify(syscall_name) __NR_##syscall_name #else # define SYS_ify(syscall_name) __NR_/**/syscall_name #endif #ifdef ASSEMBLER /* Linux uses a negative return value to indicate syscall errors, unlike most Unices, which use the condition codes' carry flag. */ #undef PSEUDO #define PSEUDO(name, syscall_name, args) \ .text; \ SYSCALL_ERROR_HANDLER \ ENTRY (name) \ DO_CALL (args, syscall_name); \ testl %eax, %eax; \ jl syscall_error; #ifndef PIC #define SYSCALL_ERROR_HANDLER /* Nothing here; code in sysdep.S is used. */ #else /* Store (- %eax) into errno through the GOT. */ #ifdef _LIBC_REENTRANT #define SYSCALL_ERROR_HANDLER \ .type syscall_error,@function; \ syscall_error: \ pushl %ebx; \ call 0f; \ 0:popl %ebx; \ xorl %edx, %edx; \ addl $_GLOBAL_OFFSET_TABLE_+[.-0b], %ebx; \ subl %eax, %edx; \ movl errno@GOT(%ebx), %ecx; \ movl %edx, (%ecx); \ pushl %edx; \ call __errno_location@PLT; \ popl %ecx; \ popl %ebx; \ movl %ecx, (%eax); \ movl $-1, %eax; \ ret; /* A quick note: it is assumed that the call to `__errno_location' does not modify the parameter value! */ #else #define SYSCALL_ERROR_HANDLER \ .type syscall_error,@function; \ syscall_error: \ call 0f; \ 0:popl %ecx; \ xorl %edx, %edx; \ addl $_GLOBAL_OFFSET_TABLE_+[.-0b], %ecx; \ subl %eax, %edx; \ movl errno@GOT(%ecx), %ecx; \ movl %edx, (%ecx); \ movl $-1, %eax; \ ret; #endif /* _LIBC_REENTRANT */ #endif /* PIC */ /* Linux takes system call arguments in registers: syscall number %eax call-clobbered arg 1 %ebx call-saved arg 2 %ecx call-clobbered arg 3 %edx call-clobbered arg 4 %esi call-saved arg 5 %edi call-saved The stack layout upon entering the function is: 20(%esp) Arg# 5 16(%esp) Arg# 4 12(%esp) Arg# 3 8(%esp) Arg# 2 4(%esp) Arg# 1 (%esp) Return address (Of course a function with say 3 arguments does not have entries for arguments 4 and 5.) The following code tries hard to be optimal. A general assuption (which is true according to the data books I have) is that 2 * xchg is more expensive than pushl + movl + popl Beside this a neat trick is used. The calling conventions for Linux tell that among the registers used for parameters %ecx and %edx need not be saved. Beside this we may clobber this registers even when they are not used for parameter passing. As a result one can see below that we save the content of the %ebx register in the %edx register when we have less than 3 arguments (2 * movl is less expensive than pushl + popl). Second unlike for the other registers we don't save the content of %ecx and %edx when we have than 1 and 2 registers resp. The code below might look a bit long but we have to take care for the pipelined processors (i586 and up). Here the `pushl' and `popl' instructions are marked as NP (not pairable) but the exception is two consecutive of these instruction. This gives no penalty on i386 and i486 processors though. */ #undef DO_CALL #define DO_CALL(args, syscall_name) \ PUSHARGS_##args \ DOARGS_##args \ movl $SYS_ify (syscall_name), %eax; \ int $0x80 \ POPARGS_##args #define PUSHARGS_0 /* No arguments to push. */ #define DOARGS_0 /* No arguments to frob. */ #define POPARGS_0 /* No arguments to pop. */ #define _PUSHARGS_0 /* No arguments to push. */ #define _DOARGS_0(n) /* No arguments to frob. */ #define _POPARGS_0 /* No arguments to pop. */ #define PUSHARGS_1 movl %ebx, %edx; PUSHARGS_0 #define DOARGS_1 _DOARGS_1 (4) #define POPARGS_1 POPARGS_0; movl %edx, %ebx #define _PUSHARGS_1 pushl %ebx; _PUSHARGS_0 #define _DOARGS_1(n) movl n(%esp), %ebx; _DOARGS_0(n-4) #define _POPARGS_1 _POPARGS_0; popl %ebx #define PUSHARGS_2 PUSHARGS_1 #define DOARGS_2 _DOARGS_2 (8) #define POPARGS_2 POPARGS_1 #define _PUSHARGS_2 _PUSHARGS_1 #define _DOARGS_2(n) movl n(%esp), %ecx; _DOARGS_1 (n-4) #define _POPARGS_2 _POPARGS_1 #define PUSHARGS_3 _PUSHARGS_2 #define DOARGS_3 _DOARGS_3 (16) #define POPARGS_3 _POPARGS_3 #define _PUSHARGS_3 _PUSHARGS_2 #define _DOARGS_3(n) movl n(%esp), %edx; _DOARGS_2 (n-4) #define _POPARGS_3 _POPARGS_2 #define PUSHARGS_4 _PUSHARGS_4 #define DOARGS_4 _DOARGS_4 (24) #define POPARGS_4 _POPARGS_4 #define _PUSHARGS_4 pushl %esi; _PUSHARGS_3 #define _DOARGS_4(n) movl n(%esp), %esi; _DOARGS_3 (n-4) #define _POPARGS_4 _POPARGS_3; popl %esi #define PUSHARGS_5 _PUSHARGS_5 #define DOARGS_5 _DOARGS_5 (32) #define POPARGS_5 _POPARGS_5 #define _PUSHARGS_5 pushl %edi; _PUSHARGS_4 #define _DOARGS_5(n) movl n(%esp), %edi; _DOARGS_4 (n-4) #define _POPARGS_5 _POPARGS_4; popl %edi #endif /* ASSEMBLER */ #endif /* linux/i386/sysdep.h */