Wednesday, July 9, 2014

Adding a system call in X86 QEMU Environment


Generally Linux programmers always curious about system calls and the way it is implemented. As a Linux programmer, I just wanted to learn system call implementation by actually using it in my application. This will completes the cycle.
The environment is i386 QEMU with 3.8 kernel.
Here is the problem statement.
"Just add a system call which takes two integer as input and returns addition of those two".
Step 1: Download the Linux kernel from kernel.org (3.8 version) and extract it.
Step 2: Adding system call by modifying kernel
Step 2.1: In linux-3.8/arch/x86/syscalls/syscall_32.tbl file add an entry.
#
# 32-bit system call numbers and entry vectors
#
# The format is:
# <number> <abi> <name> <entry point> <compat entry point>
#
# The abi is always "i386" for this file.
#
0       i386    restart_syscall         sys_restart_syscall
1       i386    exit                    sys_exit
2       i386    fork                    sys_fork                        stub32_fork
3       i386    read                    sys_read
.............
.............
348     i386    process_vm_writev       sys_process_vm_writev           compat_sys_process_vm_writev
349     i386    kcmp                    sys_kcmp
350     i386    finit_module            sys_finit_module
351     i386    jeyAdd                  sys_jeyAdd
jeyAdd is the newly added system call. This file contains system call numbers and corresponding entry vectors for i386(Of course, file name itself explains!!)
Step 2.2: In linux-3.8/include/linux/syscalls.h add declaration of sys_jeyAdd() like
asmlinkage long sys_jeyAdd(int a, int b);
Step 2.3: Add a new C file say JeyCalc.c with the following content
/* This file is added for testing system calls flow. */
#include <linux/syscalls.h>

SYSCALL_DEFINE2(jeyAdd, int, arg1, int, arg2)
{
        return (arg1 + arg2);
}
Step 2.4: In kernel Makefile(linux-3.8/kernel/Makefile), include the newly added file JeyCalc.c
#
# Makefile for the linux kernel.
#

obj-y     = ...
            ... 
            async.o range.o groups.o lglock.o smpboot.o JeyCalc.o
Step 2.5: Compile the kernel for bzImage.
Step 3: Now lets write an application for using our newly created system call. 
constraints 
  1. Our C library doesn't know about new system call 
  2. My file system don't have C libraries(Forgot to compile busybox with C libs). 
Solutions 
syscall() function in unistd.h can be used with syscall number to invoke it. In our case, 351. 

Here is the sample application
#include <stdio.h>
#include <unistd.h>

int main()
{
        int a = 0;

        printf("\n Test Application for My Syscalls \n");
        a = syscall(351, 10, 20);

        printf("\n Result: %d\n", a);

        return 0;
}
If this application is compiled with -static then there is no need for C libs. It works fine.


Step 4: Mount the file system and copy the statically linked application to it.
Step 5: Load the QEMU with modified kernel's bzImage and file system.
Step 6: Go to Application's directory and run it. Yeah!!! You made it.
References