2013年4月4日 星期四

如何在Linux系統裡新增一個System Call




環境版本: Gentoo 2007
核心版本: linux-2.6.28.5
硬體: x86  單核心CPU

各核心版本間大同小異, 但還是會稍有不同的部分, 例如檔案路徑更動, 或者 include 不同的標頭檔, 則需要另行上網查找資料或以 LXR 軟體搜尋 ; 另外, 不同的 Linux 系統之間指令可能會有所差異, 以下是一個簡單的 system call 範例.
步驟:    . Kernel 設定
                   1. 註記 system call
                   2. 加上 systme call 的定義
                   3. 定義 system call 函式
                   4. 新增 system call 的實做
                   5. 設定 kernel/Makefile
6. 編譯核心, 安裝模組, 安裝核心
7. 重新開機
二. User 設定
      1. 在 unistd.h 定義 system call
      2. 在 syscall.h 定義 system call
      3. 測試


一.   kernel 部份

首先到要修改的 kernel 目錄
ex:  cd /usr/src/linux-2.6.28.5
1.    註記 system call
核心中有一份 table 用來註記 system call 的呼叫介面, 首先我們要再這裡新增 system call 名稱以及 unique ID:
( 路徑:  arch/x86/kernel/syscall_table_32.S 或者 syscall_table_64.S , 視硬體而定 )

: ......
.long sys_request_key
.long sys_keyctl
.long sys_hello  < - 新增的system call

這裡的".long"是必須的, 它並不是代表回傳的型態, 而是 Linux Assembly 的一個語法, 而所加的定義在檔案中的順序, 其實也就是這個 system call system call number , 此例中的 number 333


2. 加上 systme call 的定義
unistd_32.h 裡面會有一段跟 syscall_table_32.S 很像的 define , 不過在這裡 system call 是以"__NR_"開頭, 而其後跟著的數值則是用來代表它的 system call number
( 路徑:  arch/x86/include/asm/unistd_32.h )
( Ubuntu:      include/asm-generic/unistd.h )

: .......
#define __NR_pipe2          331
#define __NR_inotify_init1     332
#define __NR_hello                   333

#ifdef __KERNEL__




3. 定義 system call 函式
( 路徑:   arch/x86/include/asm/syscalls.h )
( Ubuntu:      include/linux/syscalls.h )
: .......
asmlinkage long sys_olduname(struct oldold_utsname __user *);

asmlinkage int sys_hello(void);


4. /kernel/ 目錄下新增 system call 的實作
#include
#include
asmlinkage int sys_hello(void)
{
       printk("hello system call!\n");
       return 0;
}

( 值得注意的是, 檔名需跟 system call 名稱一樣, 如上例檔名就必須為 hello.c , 否則系統無法找到正確的 system call 對應 )


5. 設定 kernel/Makefile
接著將 system call 的實作檔放入 source code tree , obj-y 後面加上自己新增要編譯的檔, 變數 obj-y 表示需要編繹到內核中的目標檔案名集合
: ............
........... sys_ni.o  hello.o  <- span="">新增hello.o.



6. 編譯核心, 安裝模組, 安裝核心
# make && make modules_install && make install
( Ubuntu:  # make bzImage  # make install )


7. 重新開機
( 這裡值得注意的是, 如果你的開機程式是使用 lilo 的話, 別忘記先更新 lilo 以後再重新開機, 否則開機的時候 lilo 會讀不到你剛剛編譯好的映象檔 ,如果使用的是 grub 則不需要 )
# /sbin/lilo  <- lilo="" span="">更新
# reboot




二、User 部分

核心編譯且安裝完成之後, 我們要修改一般程式 include 進來的檔( 路徑:  /usr/include/ )
1. unistd.h 定義 system call
asm/unistd.h 一樣加上 system call 的 define, 並且最底下的 NR_syscalls 必須要改成最大的 system call number 加 1
因為要讓 user 可以去呼叫 kernel 裡面的 system call, 兩端的 system call number 必須相互對應, 以下是新增對應的 system call number
:
#define __NR_vmsplice                      316
#define __NR_hello                         333  <- span="">新增

#define NR_syscalls                          334



2. bits/syscall.h 最後面加上自己的 define
:
#define SYS_writev __NR_writev
#define SYS_helloy __NR_hello  <- span="">新增的



3.  最後, 我們寫個小程式來測式 system call 吧, 寫個 Test_Syscall.c

#include
#include
#include
#define __NR_hello  333
int hello(void)
{
       return (int) syscall(__NR_hello);
}
main()
{
       printf("The return code from the hello system call is %d\n", hello());
}


寫完之後, 幫這個檔案作編譯, 然後執行
# gcc Test_Syscal.c
# ./a.out

如果正確的話, 回傳值會是 0, 失敗的話會是-1, 如果正確, 我們就可以利用 dmesg 指令打開系統訊息查看結果, 可以看到 ”hello system call!” 的字串
# dmesg | tail –n 5



其實, 也可以寫的更簡單一點

#include
#include
#include
#define __NR_hello  333
main()
{
       syscall(__NR_hello);
}




沒有留言: