First, i would like you to build the linux kernel manually and boot the system with that new kernel.
Once you were able to boot with your custom built kernel, you can follow the same procedure after adding new files or modifying existing kernel files.
If you modified any kernel files, you have to build, install and reboot the machine with new kernel. Then only your kernel modifications take effect.
If you add any new-files to kernel, you have to change the kernel’s Makefiles too. Every directory in kernel source-tree contains a file with name Makefile. Its format is easy, if you read it once, you will be able to add your new-files to kernel’s build process easily. Unless you add your file into one of the Makefiles your files will not be part of the kernel build.
Now i have told you the basic procedure to modify the kernel files or add new files to the kernel. Now lets talk about adding new system-call. From this point i assume know how to build the kernel and boot from it.
Whenever a program does a system-call, kernel looks for the system-call function in a system-call table. This table contains function-pointers to all system-call function definitions. This table used to be in entry.S file, but in latest kernels, it is moved into syscall_table.S file in arch/i386/kernel directory. By looking at it you will be able to figure out how to add one more entry into that table. You should add new entry into that table AT THE END.
What are those sys_* names? They are C function names for the system-calls. Look at include/linux/syscalls.h file. It contains the C function declarations for _almost_ all system-calls. So you should also add your system-calls declaration there.
OK, i assume you have added your system-call’s declaration in include/linux/syscalls.h and an entry in syscall_table.S files. Note down the index of your system-call’s entry in system-call table. wtf?
Now a million-dollar question: when a program calls read system-call how does kernel identify its a read system-call, not a write or a fork? This is where that __NR_???? symbol comes into picture. Have a look at include/asm-i386/unistd.h file.
Each system-call is given an unique number in include/asm-i386/unistd.h file. When a program does read system-call it puts __NR_read number in EAX CPU register and invokes system-call instruction (int 0x80). Linux kernel looks at this EAX register to identify which system-call the program has called for. This value is used as an index into the system-call table defined in syscall_table.S file. So your system-call also needs an unique number, and your system-calls address *must* be at the same index in the table (in syscall_table.S file). BTW don’t forget to add +1 to NR_syscalls value defined in unistd.h file. Got it? Read the above four paragraphs again :-).
Now pick any kernel source file or write a new-file and add your system-calls definition to it. If you choose to write in a new-file edit the Makefile to include it in the kernel build. Thats it. Now go ahead build the kernel and fix any errors/warnings you face. Install the new kernel and reboot your machine.
Now your kernel has a new system-call implemented. But how can user-space programs make use of it? Library files that are shipped with our OS don’t know about existance of our new system-call, so we have to write assembly stub code which can invoke our new system-call. How do we do it? Fortunately linux kernel source tree contains macros which can generate assembly stubs for new system-calls and we can make use of them or copy them. They are available in include/asm-i386/unistd.h file under #ifdef __KERNEL__ macro. We could use this information as below:
#include <linux/unistd.h>
#include <errno.h>
// generates assembly stub with prototype
// long sayhello (void) // no parameters
_syscall0(long,sayhello);
int main(int argc, char *argv[])
{
sayhello();
return 0;
}
You should compile it with special gcc flags as below:
gcc -nostdinc -I /path/to/linux-2.6.18/include -I /usr/include -D__KERNEL__ sayhello.c
This will put our modified linux kernel header files before system’s header files in the header files search path. The -D__KERNEL__ flag is necessary because _syscall0 is #ifdef guarded by it.