System calls Linkage

advertisement
System calls
A system call is name of kernel function that is exported for user by user-space programs.
A call to a kernel function is processed differently from an ordinary function call. A
kernel function is called indirectly through a trap table. Thus if you write your own kernel
function you will create a new entry in the trap table for your new function. If you want
any user-space program to call your program you will have to provide a system call stub
function (that contains a trap instruction). You can also use system_call( ) and avoid
system call stub.
System calls Linkage
A system stub call in a user program causes an interrupt to be raised and the CPU
switches to supervisor mode and begins to execute a specific location in the kernel. What
would happen is that the CPU ends up executing function system_call( ) the argument to
this function is an offset to the sys_call_table (defined in arch/i386/kernel/entry.S). In
version 2.2.12 source code, the table is defined as follows:
.data
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_call)
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
…
…
// 0
// 1
// 2
Entry 1 contains address of the exit() function (the kernel function sys_exit), 2 is for
fork(), and so on.
Under normal process, the system_call() function saves the context of the calling
function. For example if you wrote a program ‘CallFork.exe’, and that program has call
to the fork() function say at line 27. Then system_call() would save the state of
CallFork.exe till line 26, generally the information stored is the value of all the variables
and the address of the line from where the call was made (line 27 in this case). After
finishing the execution of fork(), in this case, the original state of CallFork() would be
restored and the code will start executing from the next line.
You should keep one thing in mind that is that the compiler does not checks for correct
number of arguments or type of arguments. It simply retrieves the address of the function
from the system call table and executes the function. The kernel assumes that all the
arguments are correct, so it’s responsibility your responsibility to make sure you pass the
correct arguments.
Defining a system call number
When you want to add your own system call you will have to add an entry to
system_call_table for your function. To do it you would simply edit the
arch/i386/kernel/entry.S file, as follows.
.data
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_call)
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
…
…
.long SYMBOL_NAME(sys_ni_call)
.long SYMBOL_NAME(sys_my_new_call)
// 0
// 1
// 2
// 190
// 191
.endr
This would allow an interrupt with argument 191 to invoke a new kernel function,
sys_my_new_call(). Make sure that you make a copy of the file before editing it, as you
are making changes to the kernel code. You would also need superuser permission to do
the copy operation.
This new system call can be invoked by function syscall(), this function takes the system
call table entry number and arguments as parameter and then traps (creates an interrupt)
the kernel.
If you want an ordinary C program to be able to call the new system call, you need to
generate a system stub call. You can accomplish this by first editing
include/asm/unistd.h file, as follows.
#define __NR _exit
#define __NR _fork
#define __NR _read
#define __NR _open
…
…
#define __NR _vfork
#define __NR _my_new_call
1
2
3
4
190
191
After you have edited the table you can now generate a system call stub.
Generating a system call stub
For creating system call stubs you would use Linux macros. You can use this macro to
create stubs with zero or five parameters in kernel version 2.2 (six parameters in 2.4 ) .
Consider the following example
_syscall2(type, name, type1, arg1, type2, arg2);
will create a stub with two parameters. type is the return type of the stub, type1 is type of
arg1 and so on. These macros are defined in include/linux/uninstd.h (which includes the
file include/asm/unistd.h). To create a stub with three parameters you would use
_syscall3(..), and for three parameters use _syscall4(…).To create a stub with two
arguments this you would write a code similar to the following code.
#include
…
<linux/unistd.h>
// Generate system call for int foo(char *baz,double bar)
_syscall2(int,foo,char *,baz, double, bar);
…
As discussed earlier, you can use system_call(), defined in arch/i386/entry.S, to call a
kernel function without generating a stub. For example if the index in sys_call_table for
foo() is 193, then you call your foo() function like this:
#include
<sys/syscall.h>
…
syscall(193,&baz_arg,bar_arg);
…
Kernel Function Organization
A kernel function is an ordinary C function compiled to execute in supervisor mode with
the rest of the kernel. Other than it’s header, it requires no particular organization, since it
can perform any task that it’s author chooses.
Consider a simple kernel function:
asmlinkage void sys_foo(void){
// Write a value to the console
}
Suppose a sys_foo() is a real kernel function. If a user program calls it by using
system_call(), then sys_call_table[NR_foo] will have an entry for it (index value) , it
will also have the entry point address for the function in memory. Entry point address is
the address where the function starts in the memory. Entry point address tells the CPU
where the function starts in the memory so that CPU can start reading and executing the
function form that location.
Download