Ключевые слова:sysctl, lib, linux, assembler, gcc, (найти похожие документы)
From: uncle Bob <ubob at mail.ru>
Newsgroups: http://www.lowlevel.ru
Date: Mon, 24 May 2004 14:31:37 +0000 (UTC)
Subject: Создание нового системного вызова в ОС Linux
Оригинал: http://www.lowlevel.ru/articles/new_call.htm
Создание нового системного вызова в ОС Linux
Автор: uncle Bob <ubob at mail.ru>
Дата: 11.01.2004
Раздел: Низкоуровневое программирование в Linux
В статье рассмотрена методика добавления в состав ядра ОС Linux нового системного
вызова.
Общий механизм выполнения системных вызовов рассмотрен здесь:
http://zaya.spb.ru/intercept_lnx.txt
Задача - добавить в состав ядра версии 2.4.23 новый системный вызов, который
будет выполнять следующие действия:
- принимает от приложения пользователя указатель на строку ASCII-символов с
кодами 32 - 127 и длину этой строки в байтах;
- преобразует символы строки, находящиеся в диапазоне 0x61 - 0x7A (a - z)
в верхний регистр и возвращает эту строку обратно;
Для решения этой задачи необходимо добавить запись о новом системном вызове в
таблицу системных вызовов ядра sys_call_table. Эта таблица находится в файле
/usr/src/linux/arch/i386/kernel/entry.S. Новый вызов добавляем в самый конец
таблицы:
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/
.long SYMBOL_NAME(sys_exit) /* 1 */
.
.
.
.long SYMBOL_NAME(sys_upcase) /* новый системный вызов, 259 */
Новый системный вызов назовем sys_upcase. Его порядковый номер (для ядра
версии 2.4.23) равен 259.
Теперь необходимо добавить запись о новом вызове в файл
/usr/src/linux/include/asm-i386/unistd.h:
#define __NR_upcase 259
и в файл /usr/include/bits/syscall.h:
#define SYS_upcase __NR_upcase
Теперь осталось написать код, реализующий новый системный вызов. Вот как он
выглядит:
asmlinkage int sys_upcase(char *src, char *dst, int lenght)
{
int i = 0;
char *tmp_buff;
tmp_buff = (char *)kmalloc(lenght, GFP_KERNEL);
memset(tmp_buff, 0, lenght);
copy_from_user(tmp_buff, src, lenght);
printk(KERN_INFO "%s\n", tmp_buff);
printk(KERN_INFO "%d\n", lenght);
for(; i < lenght; i++)
if((tmp_buff[i] >= 0x61) && (tmp_buff[i] <= 0x7A)) tmp_buff[i] -= 0x20;
printk(KERN_INFO "%s (after)\n", tmp_buff);
copy_to_user(dst, tmp_buff, lenght);
kfree(tmp_buff);
return lenght;
}
Системный вызов принимает три параметра - указатель на строку, которую
необходимо преобразовать, длину этой строки и указатель на область памяти,
куда необходимо поместить результат. Функция copy_from_user() копирует
исходную строку из адресного пространства пользователя в адресное пространство
ядра, а затем в цикле производится смена регистра символов, находящихся в
диапазоне 0x61 - 0x7A. Результат (преобразованная строка) копируется из
адресного пространства ядра в пространство пользователя при помощи функции
copy_to_user().
Эту функцию поместим в файл /usr/src/linux/fs/open.c, хотя место размещения
особой роли не играет.
После внесения всех изменений в ядро необходимо перекомпилировать.
Для того, чтобы процесс пользователя мог обращаться к новому системному вызову,
создадим в каталоге /usr/include заголовочный файл upcase.h следующего содержания:
/* Заголовочный файл upcase.h */
#ifndef _UPCASE_H
#define _UPCASE_H 1
#include unistd.h>
static inline _syscall3(int,upcase,char *,src,char *,dst,int,lenght)
#endif
Макрос _syscall3 определен в файле usr/src/linux/include/asm-i386/unistd.h:
#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
type name(type1 arg1,type2 arg2,type3 arg3) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
"d" ((long)(arg3))); \
__syscall_return(type,__res); \
}
Таким образом, системный вызов sys_upcase будет выполняться стандартным для
всех системных вызовов способом: сначала в регистры процессора загружаются
параметры вызова, а затем следует вызов прерывания int 0x80. __NR_##name будет
преобразована в номер системного вызова.
Рассмотрим пример обращения к новому системному вызову из приложения
пользователя:
#include
#include
int main()
{
char *src = "ab12cd34ef"; // эту строку будем преобразовывать
char *dst; // сюда будет помещен результат
int lenght = 0, rez = 0;
lenght = strlen(src); // определяем длину строки
dst = (char *)malloc(lenght);
memset(dst, 0, lenght);
Выведем для контроля информацию:
printf("Lenght - %d\n", lenght);
printf("Source - %s\n", src);
printf("Destin - %s\n", dst);
Выполняем обращение к новому системному вызову sys_upcase для преобразования
символов исходной строки в верхний регистр:
rez = upcase(src, dst, lenght);
Отобразим результаты:
printf("Source - %s\n", src);
printf("Destin - %s\n", dst);
printf("rez - %d\n", rez);
return 0;
}
Для получения исполняемого модуля создадим Makefile:
INCDIR = /usr/src/linux/include
.PHONY = clean
new_call: new_call.o
gcc -I$(INCDIR) $^ -o $@
%.o: %.c
gcc -I$(INCDIR) -c $^
clean:
rm -f *.o
rm -f ./new_call