A friend of mine would like to try to implement a new system call based on the example in Chapter 8 of Linux Kernel Module Programming Guide 2.6 Series (http://www.dirac.org/linux/writing/lkmpg/2.6/lkmpg-2.6.0.html) in Linux kernel 2.6.21.5. The example requires that the kernel exports `sys_call_table' symbol. Although Linux kernel 2.4.x, on which the guide is based, still exposes that symbol, Linux kernel 2.6.x does not do that anymore for it is harmful (e.g., it is unimaginable if a module replace the system call of `link' with `unlink' via the table). The guide comes with a patch to expose the table in Linux kernel 2.6.x. However, the patch does not work in particular for Linux kernel 2.6.21.5. So, I helped him to do so.
Before that, as I have ever read somewhere on the Internet, implementing a new system call is not the right way to control a module. The right way is to use ioctl() instead. More importantly, it is a silly thing to expose `sys_call_table' for modules to fiddle with it. As stated in the guide, do not do this unless you know what you are doing. I am not responsible for any consequence that may result from your being following my story and try it out.
Having said that, the following is the patch for Linux 2.6.21.5 that will expose the table for modules to fiddle with it (CAUTION: The patch is covered by GNU GPL version 2 since it is derived from code under such license. If you do not agree with the licensing terms (http://www.gnu.org/licenses/gpl-2.0.html), do not even read the patch and the rest of this post):
Filename: linux-2.6.21.5-expose_sys_call_table.patch
--- arch/i386/kernel/entry.S 2007-06-12 01:37:06.000000000 +0700 +++ ../src/linux-2.6.21.5/arch/i386/kernel/entry.S 2008-10-13 14:47:33.000000000 +0700 @@ -1034,7 +1034,7 @@ CFI_ENDPROC ENDPROC(kernel_thread_helper) -.section .rodata,"a" +.section .data,"aw" #include "syscall_table.S" syscall_table_size=(.-sys_call_table)
Filename: linux-2.6.21.5-export_sys_call_table_sym.patch
--- kernel/kallsyms.c 2007-06-12 01:37:06.000000000 +0700 +++ ../src/linux-2.6.21.5/kernel/kallsyms.c 2008-10-13 23:03:13.000000000 +0700 @@ -452,3 +452,6 @@ __initcall(kallsyms_init); EXPORT_SYMBOL(__print_symbol); + +extern void *sys_call_table; +EXPORT_SYMBOL(sys_call_table);
To apply the patch, cut the patches above and paste them in a new file named X and Y. After that, go to your preferably pristine, unless you know what you are doing, linux-2.6.21.5 directory and type `patch -p0 < X' and `patch -p0 < Y'. Finally, just recompile your kernel.
To test whether or not this patch works, I make a module that will put a custom system call at index 285 of `sys_call_table' because, in arch/i386/kernel/syscall_table.S, it is commented as available. The custom system call will be called from the user-space via `syscall()'. The system call will receive one integer argument that the module will save in a vector. The module will give report on all integers that have ever been stored in the vector via procfs (WARNING: procfs is deprecated. You may want to try sysfs instead).
The code of the module is as follows (the module is thread-safe, but give_report() will need further development to accommodate large data entries):
Filename: eus_syscall.c
/*****************************************************************************
* Copyright (C) 2008 Tadeus Prastowo (eus at member dot fsf dot org) *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/stat.h>
#define MODULE_NAME "Eus's Module"
struct int_node {
struct list_head link;
int value;
unsigned count;
};
extern void *sys_call_table[];
static void *old_entry;
static LIST_HEAD(rcv_int);
static DEFINE_RWLOCK(rcv_int_lock);
static struct proc_dir_entry *report;
asmlinkage long sys_storeint(int int_to_store)
{
struct int_node *n;
struct int_node *ptr;
int rc = 0;
write_lock(&rcv_int_lock);
list_for_each_entry(ptr, &rcv_int, link) {
if (ptr->value == int_to_store) {
ptr->count++;
ptr = NULL; /* flag that a node is found */
break;
}
}
if (ptr) {
n = kmalloc(sizeof(struct int_node), GFP_ATOMIC);
if (!n) {
rc = -ENOMEM;
} else {
n->value = int_to_store;
n->count = 1;
list_add_tail(&n->link, &rcv_int);
}
}
write_unlock(&rcv_int_lock);
return rc;
}
static int give_report(char *page, char **start, off_t off, int count, int *eof,
void *data)
{
int len = 0;
struct int_node *ptr;
len += sprintf(page
, "+-------------+-------------+\n"
"| Integer | Count |\n"
"+-------------+-------------+\n");
read_lock(&rcv_int_lock);
list_for_each_entry(ptr, &rcv_int, link) {
len += sprintf(page + len, "| %11d | %11u |\n", ptr->value,
ptr->count);
}
read_unlock(&rcv_int_lock);
len += sprintf(page + len, "+-------------+-------------+\n");
*eof = -1;
return len;
}
static void __exit destroy_rcv_int(void)
{
struct list_head *n;
unsigned c = 0;
while (!list_empty(&rcv_int)) {
n = rcv_int.next;
list_del(n);
kfree(n);
c++;
}
}
static int __init create_reporting_file(void)
{
report = create_proc_read_entry("reporting_rcv_int", S_IRUGO
, &proc_root, give_report, NULL);
if (!report) {
return -1;
}
report->owner = THIS_MODULE;
return 0;
}
static int __init eus_init(void)
{
rwlock_init(&rcv_int_lock);
old_entry = sys_call_table[285];
sys_call_table[285] = sys_storeint;
if (create_reporting_file()) {
goto error;
}
return 0;
error:
sys_call_table[285] = old_entry;
return -1;
}
module_init(eus_init);
static void __exit eus_exit(void)
{
remove_proc_entry("reporting_rcv_int", &proc_root);
sys_call_table[285] = old_entry;
destroy_rcv_int();
}
module_exit(eus_exit);
The code for the user-space program to enter integers with the custom system call is as follows:
Filename: eus_userspace.c
/*****************************************************************************
* Copyright (C) 2008 Tadeus Prastowo (eus at member dot fsf dot org) *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void
sigint_handler (int signum)
{
printf ("\n");
exit (EXIT_SUCCESS);
}
int
main (int argc, char **argv, char **envp)
{
int number;
struct sigaction act = {
.sa_handler = sigint_handler
};
if (sigaction (SIGINT, &act, NULL) == -1)
{
perror ("Cannot install signal handler");
exit (EXIT_FAILURE);
}
while (1)
{
printf ("Input an integer (CTRL-C to exit): ");
scanf ("%d", &number);
if (syscall (285, number) == -1)
{
perror ("Failure in syscall()");
}
}
}
Finally, the Makefile is as follows:
Filename: Makefile
.PHONY: all install clean
SOURCE_DIR := /lib/modules/`uname -r`/build
CFLAGS += -Wall
all: eus_syscall.ko eus_userspace
eus_syscall.ko: eus_syscall.c
@$(MAKE) -C $(SOURCE_DIR) M=`pwd` modules
eus_userspace: eus_userspace.c
$(CC) $(CFLAGS) -o eus_userspace eus_userspace.c
install:
@$(MAKE) -C $(SOURCE_DIR) M=`pwd` modules_install
clean:
@rm -f *.o *.ko *.mod.c *.symvers
@find ./ -name '.*' -mindepth 1 -maxdepth 1 -exec rm -R '{}' ';'
@rm -f eus_userspace
And, the Kbuild is as follows:
Kbuild: Kbuild
obj-m := eus_syscall.o
Keywords: system call, linux kernel 2.6.21.5, sys_call_table, patch, module.
May i ask why is
May i ask why is this:
-.section .rodata,"a"
+.section .data,"aw"
needed? I can imagine why, but would like an definite explonation. Just out of curiosity.
It's an instruction to GAS (GNU as)
Hi Ho!
In http://sourceware.org/binutils/docs/as/Section.html#Section under section `ELF Version', it is mentioned that `w' means `writable'.
Yes, it needs to be so in order to change the content of sys_call_table.
Best regards,
Eus (FSF member #4445)
In this digital era, where computing technology is pervasive,
your freedom depends on the software controlling those computing devices.
Join free software movement today!
It is free as in freedom, not as in free beer!
Join: http://www.fsf.org/jf?referrer=4445
Sorry, it's not for GAS
Hi Ho!
The instruction is for the GNU linker (i.e., `ld'), not for GAS.
Best regards,
Eus (FSF member #4445)
In this digital era, where computing technology is pervasive,
your freedom depends on the software controlling those computing devices.
Join free software movement today!
It is free as in freedom, not as in free beer!
Join: http://www.fsf.org/jf?referrer=4445