Saturday, August 15, 2009

Adding system calls to the Linux kernel

Adding new system calls to the Linux kernel

This article talks about adding new system calls to the Linux kernel on an x86 machine , version 2.6.XX

Disclaimer : This is by no means a complete and/or definitive guide. It is based purely on whatever little experience I have.

In this article , we will implement a linked-list in the kernel space which stores one integer value per node and write new system calls to add new items to the list, delete existing items and to display the contents of the list.

Adding new system calls to the kernel involves editing some kernel files, the files that have to be edited are :

So, first of all, download a kernel source from http://kernel.org and extract in into a suitable location.

(The pathnames are relative to the source base directory)

1. /arch/x86/kernel/syscall_table_32.S

2. /arch/x86/include/asm/unistd_32.h

3. /include/linux/syscalls.h

4. /Makefile

Next, we are going to put all the new files that we create into a separate directory.

Let's call that directory as "lister" . Note that this is not at all necessary, we can simply add our system calls in the kernel/sys.c file

So, in the source base directory, we will create a directory named "lister" which will hold all the new files that we create.

The new files that we will be creating are :

5. lister/lister.c

6. lister/lister.h

7. lister/Makefile

Finally, we'll create some user-space files to test the system calls. We'll see more about these files later.

Now, lets get started !!

1. The first file to be changed is the arch/x86/kernel/syscall_table_32.S file.

This file contains the names of all the system calls, prefixed with "sys_"

Since, we will be adding 3 new system calls to the kernel, which we will be naming as listadd, listdel and listshow, we will have to append 3 lines to this file as :

.long sys_listadd

.long sys_listdel

.long sys_listshow

2. Next, we'll modify the arch/x86/include/asm/unistd_32.h file.

This file contains an unique number associated with the system call that is passed to the kernel through the EAX register (specific to x86) when a system call is invoked.

Again, we will have to append 3 lines to the file as follows :

The general structure of the line is

" #define __NR_<system_call_name><last_system_call_number +1>"

so, after adding the relevant lines to the files,the file will look somewhat like this :

----------------------- snippet ----------------------------

#define __NR_preadv 333

#define __NR_pwritev 334

#define __NR_listadd 335 /* newly */

#define __NR_listdel 336 /* added */

#define __NR_listshow 337 /* lines */

#ifdef __KERNEL__

#define __ARCH_WANT_IPC_PARSE_VERSION

----------------------- snippet ----------------------------

3. Next, we will edit the file include/linux/syscalls.h that holds the declarations of all the system calls.

We will add the declarations of our 3 system calls to this file.

For the listadd system call ,

asmlinkage long sys_listadd(int __user *);

Now the above declaration says that the listadd system call takes 1 parameter, which is a pointer, in the user-space, to an integer.

Similarly, we add the declarations of the other two system calls.

asmlinkage long sys_listdel(int __user *);

asmlinkage long sys_listshow();

4. Next, we modify the /Makefile so that the files that we will be creating in our "lister" folder get complied.

Add lister/ to core-y (Search for regex: core-y ).The "lister" directory will contain the source file, header file and the Makefile for our system call.

So, the line would look something like this :

core-y := usr/ lister/

5. Now we create a directory in the source base directory, that will hold our files that contain the code of our system calls.

Let's call that directory as "lister"

Now, we will write the actual implementation of the system calls.

It consists of making 2 files, in our case, we call those files, lister.c and lister.h

6. Create a file called lister.h .

The contents of the header file can be something like this :

------------------ LISTER.H ------------------------------

#ifndef LISTER_H

#define LISTER_H

#include<linux/list.h>

struct linked_list{

struct list_head list;

int data;

};

struct list_head linked_list;

int no_of_elements = 0;

#endif

------------------- END ------------------------------------

This header file defines the node structure of our linked-list.

It also declares the head of the linked-list (named linked_list) and a variable that holds the number of elements in the list.

7. Now, we will write the actual system calls.

We create a file called lister.c  The indentations in the code are eaten up by Blogger...

----------------------------- LISTER.C -------------------------------

#include<linux/syscalls.h>

#include<linux/list.h>

#include<linux/slab.h>

#include<linux/kernel.h>

#include<asm/uaccess.h>

#include<linux/linkage.h>

#include "lister.h"

SYSCALL_DEFINE1(listadd,int __user*,buffer)

{

int data;

struct linked_list *new = (struct linked_list*) \

kmalloc(sizeof(struct linked_list),GFP_KERNEL);

if(no_of_elements == 0){

INIT_LIST_HEAD(&linked_list);

}

get_user(data,buffer);

new->data = data;

list_add_tail(&new->list,&linked_list);

no_of_elements++;

return 0;

}

SYSCALL_DEFINE1(listdel,int __user*,buffer)

{

int data;

struct list_head *ptr;

struct linked_list *entry;

get_user(data,buffer);

if(no_of_elements > 0){

list_for_each(ptr,&linked_list){

entry = list_entry(ptr,struct linked_list,list);

if(entry->data == data){

list_del(ptr);

return 0;

}

}

}

return -1;

}

SYSCALL_DEFINE0(listshow)

{

struct list_head *ptr;

struct linked_list *entry;

if(no_of_elements > 0){

list_for_each(ptr,&linked_list){

entry = list_entry(ptr,struct linked_list,list);

printk(KERN_ALERT "%d\n",entry->data);

}

}

}

---------------------------- END -------------------------------------

The SYSCALL_DEFINEx macro is defined in the syscalls.h file. It is, as the name suggests, used to define a system call.

Here, "x" is the number of parameters that the system call takes.

So the line :

SYSCALL_DEFINE1(listdel,int __user*,buffer)

says that : define a system call with the name "listdel", which takes 1 parameter and the parameter is a pointer, in user-space, to an integer.

The name of the pointer is the third argument.

so in general,

SYSCALL_DEFINE0(<name_of_system_call>)

SYSCALL_DEFINEx(<name_of_system_call>, <type_of_arg_1>, <name_of_arg_1>, ... , \ <type_of_arg_x>, <name_of_arg_x>)

The rest of the code is fairly straight-forward.

The get_user() macro copies the data from the user-space to the kernel-space.

The list_for_each() macro is used for iterating through the list.

The list_entry() macro returns a pointer to the linked-list node, given a pointer to the list_head struct within that node as the argument .

8. Now, we write a simple Makefile to compile our files

----------------------- Makefile -------------------------------------

obj-y += lister.o

----------------------- END -------------------------------------

This completes our kernel-space code.

Now, to test this code, we need to write some user-space code.

For this, we write 2 files, test.c and tester.h

9. Now, we will write wrapper functions for the system calls and put then together in a header file. Let's call that file as "tester.h"

Writing wrapper functions is not necessary, but it improves the readability of the code.

----------------------- TESTER.H ----------------------------

#include<linux/unistd.h>

#include<sys/syscall.h>

#define __NR_listadd 335

#define __NR_listdel 336

#define __NR_listshow 337

static long add_in_list(int * buff)

{

return syscall(__NR_listadd,buff);

}

static long del_from_list(int * buff)

{

return syscall(__NR_listdel,buff);

}

static long show_list(void)

{

return syscall(__NR_listshow);

}

------------------------- END --------------------------------

Note here that the numbers in the #define s have to be exactly the same as those in the unistd.h file

Here, in this header file, we wrote 3 wrapper functions for each of the system calls.

The _syscall() macro is the actual invocation of the system call.

Here, the arguments to the wrapper functions are simple integer pointers, instead of the int __user * that we used earlier. This is because this code is in the user-space, while the earlier code was in the kernel-space.

10. Now, we'll write a simple program to test the system calls. Let's call that file "test.c"

----------------------- TEST.C ----------------------------

#include<stdio.h>

#include "tester.h"

int main()

{

int data;

int i;

for(i=0;i<4;i++){

printf("\nEnter the number : ");

scanf("%d",&data);

add_in_list(&data);

show_list();

}

printf("\nDelete : ");

scanf("%d",&data);

del_from_list(&data);

}

---------------------- END ---------------------------------

The code is very simple, nothing really to explain.

Just compile and install the kernel so that system calls become available.

Then compile and execute "test.c" to test out the system calls.

Any kind of criticism is welcome ! :-)

3 comments:

  1. Nice one ! Very useful :)

    ReplyDelete
  2. Hey good one....
    Anyways add me to your blog list na....
    http://shweta-kaleidoscope.blogspot.com/

    ReplyDelete