About System Calls:
System calls are the
routines written inside the Linux kernel.
System calls provide a general interface
between a user application and the kernel. Whenever a user application requires
OS services, it cannot directly invoke the kernel for that particular service.
For this purpose, system calls are invoked via API. Basic component of a Linux
operating system is kernel. It’s free and open source, hence Linux kernel can
be modified and customized depending upon different developing and designing
needs. In this project we will add a system call in the Linux kernel. This will
be a simple “Hello world” system call, which is pretty useless, but its main
purpose is to get to know the basics of adding a custom system call in Linux
kernel.
Following are the steps
for adding a system call in the Linux kernel:
Get the source of
your kernel
You can use any Linux
distribution, and any version of the kernel for this purpose. If you are
interested in downloading the kernel version of your Linux distribution, first
check the version of your kernel by typing the following command in the
terminal
uname –r
this
will display you the kernel version of your Linux distribution, I’m using
Ubuntu Linux 12.04, and it displays me the kernel version 3.2.46
to download the source for
the particular version of your kernel distribution type this command in the
terminal
sudo
apt-get source kernel
The
source of that particular kernel will begin to download, it will take some time
depending upon the speed of your internet connections.
You
can also go to www.kernel.org to download the kernel
of particular version or the latest stable kernel from the kernel archives. As
I mentioned if you’re not interested in working on the particular kernel
version of your Linux distribution, you can download the latest stable kernel
or any version that you wish. The most recent distributions of Linux such as
Ubuntu, Backtrack etc. have a big kernel, so if you are using any recent
version of Linux then it is advisable to download a smaller version of kernel.
Tip: If you want to use a
kernel other than your Linux distribution, try to choose a kernel which is
smaller in size, because bigger the kernel more it will take to compile it.
Once
you downloaded your kernel source, next step is to extract it. For this purpose
go to your home folder or root directory, you would see a zipped file named
“linux-source-x.y.z.tar.bz” (where x, y , z indicates the version such as
linux-source3.2.0),
Right
click on the file and then click “Extract here”. I have extracted this source
in the home folder(root directory) for my convenience so that I can easily
access it, you may extract it anywhere.
After
you extract, you would be able to see a folder named “linux-source-x.y.z” (where
x, y , z indicates the version such as linux-source3.2.0),
Now
the source of the kernel is available to you, you would be able to browse and
view the files and make changes.
You
can also extract this zipped file by typing the following command in the
terminal,
tar xvzf linux-source-x.y.z.tar.bz
(where x, y , z
indicates the version such as linux-source3.2.0),
Add the system
call to the system call table:
In
the folder “linux-source-x.y.z” (where x, y , z indicates the version such as
linux-source3.2.0), go the file "arch/x86/kernel/syscall_table_32.S" and add the following line
.long sys_hello
syscall_table_32 is the system call table which consists
of assembly instructions in which record of all the system calls in the system
is kept. You can assume it as an array having consecutive locations, on each
location there’s an entry of the system call. We have the entry of our system
call at the end of this table, if we add another system call it will be added
in the end as well and so on(just like filling the consecutive locations of an
array). sys_call_table is the base pointer and points to the start of the array in other words. If a user space program invokes sendmsg system call, the number passed is 345. Dispatcher routine adds this number to the sys_call_table base and arrives at the three forty fifth entry that hold the address of sys_sendmsg.
Define Macros
associated with the system call:
In the linux-source-x.y.z
folder go the file arch/x86/include/asm/unistd_32.h, you would notice that a number is been defined with each system call, that number is called "macro". Macro is a constant associated with each system call which indicates the location of the system call in the system call table, or in other words it simply defines the system call number.
Add the following line in the file
#define __NR_hello 349
This is the definition of our system call, and also increment the value of NR_SYSCALLS by 1, since we are adding one system call.
#define NR_syscalls 350
NR_syscall is another macro constant, which saves the total number of the system calls present int the system. In my system there were total 350 system calls, by adding one, the total number of system calls becomes 351 (NR_syscalls indicate 350 because it begins counting from 0, so from 0-350, total of 351 system calls in the system).
Caution:
Number of system call might vary from version to version in Linux kernel, so be careful when you are defining your system call and the macro associated with it and incrementing the NR_syscalls constant.
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicNW-D4TELh57_L3te5JqXI_GM0CdgZjTMT3b5rceBb461qBfYOoh5TJ6Adh9iHWQ3OMzQJA5RqaxV2L4gHj2R5Nrv-HO9fv60gRc9J8LOg96ldxQQr_PuNcnyyt6m4G5h3Tiy-4Vc-jl1/s640/Screenshot+from+2013-07-05+18_30_16.png)
Now go the file arch/x86/include/asm/unistd_64.h, and add the definition of your system call,
#define
__NR_hello 312
__SYSCALL(__NR_hello,
sys_hello)
Now to the file include/linux/syscalls.h, add the prototype of the system call,
asmilnkage long sys_hello(void);
(Look at the arrows in the figures)
Now
in the folder “linux-source-x.y.z” (where x, y , z indicates the version such as
linux-source3.2.0), create a new folder and name that folder “hello”. In that
folder create a file named “hello.c”, and add the following code to that file.
#include
<linux/kernel.h>
asmlinkage
long sys_hello(void)
{
printk("Hello world\n");
return 0;
}
This
is our system call which kernel will execute. asmlinkage is itself is afunction
which is must for the kernels otherwise the system call won’t work, and kernel
won’t be able to distinguish that either it is a user program or a kernel
program. You can see “printk” statement in the body of that function. printk is
just like “printf” or “cout” which displays the output, but printf and cout
display output on the console window, unlike these two statements printk will
write the output of the system call to the kernel log. On execution , the
system call returns a long integer, indicating the status of the system call,
for example if system call returned 1, then that means that system call was
terminated prematurely, or if it returns -1 it means system call does not
exist( kernel was unable to locate it in the system call table), if it returns
0, that means it executed normally as it should.
Now
in the same directory (hello folder), create another file, and name it “Makefile”,
and add the following text to it.
obj-y := hello.o
This will create the object file of the system call to execute it. Kernel will use this file to build the system call. Now go to the folder "linux-source-x.y.x" (where x,y,z indicates the version such as linux-source-3.2.0), and look for a make file.
Open that file and edit the following line to the text shown in the image
core-y :=jernel/ mm/ fs/ ipc/ security/ crypto/ block/
This
will make sure that our “hello” system call is in “hello” directory and it is
is also compiled and build when we will compile our kernel.
Now
we have added the system call, we need to compile our kernel and test this
system call.
Compiling the
kernel:
For
compiling the kernel following three packages must be installed on your system.
·
gcc compiler (latest version)
·
ncurses development kit
·
system packages(updates)
For
installing gcc compiler type the following command in the terminal
sudo apt-get install gcc
For
installing ncurses development packages type the following in the terminal
sudo apt-get install libncurses5-dev
Now
run the updates by typing the following command to the terminal
sudo apt-get update && sudo apt-get upgrade
Now
we are ready to compile the kernel, for compiling the kernel type the following
to the terminal
sudo make
That
will take some time depending upon the size of your kernel. Normally it takes
45-50 minutes to compile a smaller or medium kernel but in my case it took more
than one and a half an hour!
Something
like that will be displayed on the screen while compiling the kernel(be patient)
Tip:
If
you have multi-core, multi-processor machine then you can reduce the compile
time of the kernel by making use of cores available. For compiling on different
cores type the following to the terminal
sudo make -jX (where X is the number of cores available)
This
will reduce the compile time by the factor (compile time/number of cores).
(Note:
You might not be able to interact with your programs or applications during the
execution of this command, because this command make a high use of the
processor and its cores, so your interactive programs might become slow or not
responding)
After
successful compilation of your kernel, next step is to install the newly
modified kernel to your system.
For
installation type the following command in the terminal.
sudo make install_modules install
This
will install the modified kernel and all the necessary modules. One you have installed
your new kernel, reboot your system with this new kernel.
After
rebooting run the following test program to check either our system call is
working fine or not.
#include
<stdio.h>
#include
<linux/kernel.h>
#include
<sys/syscall.h>
#include
<unistd.h>
#define
__NR_hello 312 //349 if you are running a 32bit kernel and following my
tutorial
long
hello_syscall(void)
{
return syscall(__NR_hello);
}
int
main(int argc, char *argv[])
{
long int a = hello_syscall();
printf("System call returned %ld\n", a);
return 0;
}
The
output of the program should be:
System call returned 0
As
shown in the image below:
As I mentioned earlier that you
would not be able to see the output of the system call “Hello World” on the
console window, the output of that system call is written in the kernel log.
To
view the kernel log type the following command in the terminal:
dmesg
Output
will be displayed as shown in the image.
Most
probably, the output of the system call will be written at the end of the log.
In the image you would notice that “Hello world” appeared two times. This is
because I ran my test.c program twice or in other words I invoked the hello
system call two times, that means every time you invoke the system call, its output
is going to be written on the kernel log. That’s why “Hello word” appeared
twice on the log can be seen.
That's it ! You've added a system call in the linux kernel ! As I mentioned earlier, this is a pretty useless system call though it give an idea how you can make your own custom system calls in kernel. I hope you would've liked that !
Give your feedback in comments ! Thanks.