Open main menu

CDOT Wiki β

Syscalls

Revision as of 22:19, 25 January 2014 by Chris Tyler (talk | contribs)

The system call (syscall) mechanism is used by an application to access kernel services. High-level languages will wrap the syscall interface in basic wrappers or more advanced mechanisms. For example, in C, the write syscall can be accessed through the write() wrapper, or through more complex functions such as printf().

Syscall Mechanism

To invoke a syscall, load the syscall number and arguments into the appropriate registers, and then invoke the platform-specific syscall mechanism.

x86 32-bit

  • The syscall number is placed in eax.
  • Arguments are placed in edi, esi, edx, ecx with spill over to the stack.
  • Return value is in eax.
  • The syscall is invoked with int 0x80

x86 64-bit

  • The syscall number is placed in rax.
  • Arguments are placed in rdi, rsi, rdx, rcx, r8d, r9d with spill over to the stack.
  • Return value is in rax.
  • The syscall is invoked with syscall

aarch64

  • The syscall number is placed in r8.
  • Arguments are placed in r0-r7.
  • Return value is in r0.
  • The syscall is invoked with svc 0

Syscall information, numbers, and arguments

Syscall numbers

Syscall numbers are defined in <asm/unistd.h> (/usr/include/asm/unistd.h). If you examine this file, you'll find that it includes different files depending on the platform, because the syscall numbers are platform-specific (on newer platforms, there has been an attempt to use consistent syscall numbers). In this included file, you will find macros for each syscall named __NR_syscall which identifies the number for each syscall. For example, aarch64 includes the file /usr/include/asm-generic/unistd.h, which lists this macro for the write syscall:

#define __NR_write 64

But on x86_64 the macro definition is taken from /usr/include/asm/unistd_64.h:

#define __NR_write 1

Syscall arguments and return values

The details of each syscall's arguments and return value are listed in <unistd.h> (/usr/include/unistd.h) in a combination of comments and C extern declarations.For example, for the write syscall:

/* Write N bytes of BUF to FD.  Return the number written, or -1.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern ssize_t write (int __fd, const void *__buf, size_t __n) __wur;

So this documents that the write syscall takes three arguments: the file descriptor, a pointer to the message buffer, and the message size, and returns the number of bytes written or -1 if there was an error.

Documentation

A list of syscalls can be found in the manpage for syscalls(2) (note: this is different from the manpage for syscall(2) which is a generic syscall wrapper).

Documentation for the C wrappers for most syscalls can be found in the manpage for that wrapper, usually in section 2 of the manual (e.g., write(2), which is accessed using the command man 2 write).

Using the syscall macro definitions in asm code

The syscall macros can be used in a C program if that program is processed by the C preprocessor (cpp). The extension ,S is used to designate an assembler source file that must be processed by cpp.

There are two ways to do this for a pure assembler source file:

1. Invoke cpp explicitly:

cpp foo.S > foo.s
as foo.s -o foo.o
ld foo.o -o foo

2. Use gcc to invoke the assembler and linker:

gcc foo.S -o foo
Initialization Code and Entry Point
When the assembler is invoked through gcc, C initialization code is inserted into the binary, which increases its size. This initialization uses the entry point _start and expects your code to have the entry point main.

Using either of these approaches, you can use the __NR_xxxx macros in your assembler code:

#include <asm/unistd.h
...
        mov $__NR_write,%rax