Calling System Calls¶
There are two ways system calls are being called in the user space. Both of
them will eventually call the syscall
instruction but glibc
provides a
wrapper around that instruction using a function call.
glibc
library call - this moves the arguments to the right registers before calling thesyscall
instruction.syscall
assembly instruction - to actually hand over the work to the kernel.
Glibc syscall()
interface¶
- There is a library function in
glibc
named assyscall
, you can read about it in the man pages by the commandman 2 syscall
. - We already have the code of
glibc
with us. - See the function in the file
glibc-2.23/sysdeps/unix/sysv/linux/x86_64/syscall.S
- On reading the code you will see that the function is moving the argument
values to the registers and then calling the assembly instruction
syscall
. - As
syscall
here is a user spaceglibc
library function, first the arguments will be in the registers used for calling user space functions. Once this is done, as the system call is being called, the arguments will be used into the registers where the kernel wishes to find the arguments. See Reiterating The Above Again - Code for
syscall(2)
library function. File isglibc-2.24/sysdeps/unix/sysv/linux/x86_64/syscall.S
Note
Remember the note above. As syscall
is a function which we called
in user space, the registers are different. We now need to pick and place
the registers in a way that the system call understands it. This is shown in
the code below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | /* Copyright (C) 2001-2016 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <sysdep.h>
/* Please consult the file sysdeps/unix/sysv/linux/x86-64/sysdep.h for
more information about the value -4095 used below. */
/* Usage: long syscall (syscall_number, arg1, arg2, arg3, arg4, arg5, arg6)
We need to do some arg shifting, the syscall_number will be in
rax. */
.text
ENTRY (syscall)
movq %rdi, %rax /* Syscall number -> rax. */
movq %rsi, %rdi /* shift arg1 - arg5. */
movq %rdx, %rsi
movq %rcx, %rdx
movq %r8, %r10
movq %r9, %r8
movq 8(%rsp),%r9 /* arg6 is on the stack. */
syscall /* Do the system call. */
cmpq $-4095, %rax /* Check %rax for error. */
jae SYSCALL_ERROR_LABEL /* Jump to error handler if error. */
ret /* Return to caller. */
PSEUDO_END (syscall)
|
syscall
assembly instruction¶
We know now that for calling a system call we just need to set the right
arguments in the register and then call the syscall
instruction.
Register %rax
needs the system call number
. So where are the system
call numbers
defined? Here we can see the glibc
code to see the mapping
of the number and the system call. Or you can see this in a header file in the
system’s include directory.
Let us see a excerpt from the file /usr/include/x86_64-linux-gnu/asm/unistd_64.h
#define __NR_read 0
#define __NR_write 1
#define __NR_open 2
#define __NR_close 3
#define __NR_stat 4
Here you can see that the system calls have numbers associated with them.
Difference between syscall()
glibc interface and syscall
assembly instruction¶
In this section we will write some data to the STDOUT
(terminal) using three methods.
- First we will issue a
write()
system call. - Second we will use the
syscall()
function in glibc. - Third we will write assembly code and call the
syscall
instruction.
This will help us understand system calls
in more detail.
Now armed with the knowledge of how to call system calls let us write some assembly code where we call a system call.
write()
system call¶
We will start by exploring the write
system call a bit. In the
following code we will write hello world
on the screen. We will not use
printf
for this, rather we will use 1
(the standard descriptor for
writing to the terminal) and write
system call for it.
We need to do this so that we understand our assembly level program a bit better.
1 2 3 4 5 6 7 8 | #include <fcntl.h>
#include <unistd.h>
int main ()
{
write (1, "Hello World", 11);
return 0;
}
|
You should go through the assembly code of the C file. Use command gcc -S
filename.c
This will generate the assembly file with .s
extension. If you
go through the assembly code you will see a call to write
function. This
function is defined in the glibc
.
syscall()
function¶
Now we will do the same using the syscall
interface which the glibc
provides.
1 2 3 4 5 6 7 8 9 | #include <unistd.h>
#include <sys/syscall.h>
int main ()
{
syscall (1, 1, "Hello World", 11);
return 0;
}
|
Here is the assembly code for the above file. This is generated by using the gcc -S filename.c
command. This generates a file with name as filename.s
You can see how the arguments are been copied to the registers for calling the function syscall()
. This is being done so that in the syscall()
function the arguments can be moved to the
right registers for calling the syscall
instruction.
syscall
instruction¶
Now we will do the same in our assembly code. The idea here is to move the right values to the right registers
and then just call the syscall
instruction. The same is achieved is by calling the syscall()
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | section .text
global _start
_start: ; ELF entry point
; 1 is the number for syscall write ().
mov rax, 1
; 1 is the STDOUT file descriptor.
mov rdi, 1
; buffer to be printed.
mov rsi, message
; length of buffer
mov rdx, [messageLen]
; call the syscall instruction
syscall
; sys_exit
mov rax, 60
; return value is 0
mov rdi, 0
; call the assembly instruction
syscall
section .data
messageLen: dq message.end-message
message: db 'Hello World', 10
.end:
|
Makefile for assembling the code.
1 2 3 4 5 6 7 | all:
nasm -felf64 write.asm # Assemble the program.
ld write.o -o elf.write
clean:
rm -rf *.o
|
Run the make command and run the file elf.write
. You will see the output of your program on the screen.
$ make
nasm -felf64 write.asm # Assemble the program.
ld write.o -o elf.write
$ ./elf.write
Hello World
Conclusion¶
In this chapter we saw the different ways of calling a system call. The three ways are
- to call the function directly like calling
write
directly. - to call the
glibc
interface for calling system calls namelysyscall()
- to directly call the
syscall
instruction from any assembly file.