# 中央資工 Linux 作業系統 Project 1(2021)
###### tags: `Linux`
> 內容 : 寫一個system call從kernel space找出指定 process(task)的code(text)段的位址
[task_struct](https://elixir.bootlin.com/linux/v4.14/source/include/linux/sched.h#L519)
## **Add a system call & compile Linux Kernel**
### (1) Get the Linux kernel source code
到 [The Linux Kernel Archives](https://www.kernel.org/)查詢版本,這裡下載的版本為5.14.16
```
$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.14.16.tar.xz
```
### (2) decompress file
tar指令參數意義 : [tar指令使用](https://blog.gtwang.org/linux/tar-command-examples-in-linux-1/)
解壓縮後**linux-5.14.16**會在你的Home directory
```
$ tar -xvf linux-5.14.16.tar.xz -C ~/
```
### (3) Install the Compile Tools
安裝編譯kernel所需的工具 (以下參考不同來源)
```
$ sudo apt install build-essential libncurses-dev libssl-dev libelf-dev bison flex -y
$ sudo apt-get install git ccache libssl-dev fakeroot bison gcc flex
```
### (4) Clean up your installed packages.
```
$ sudo apt clean && sudo apt autoremove -y
```
### (5) Add a new system call number to kernel's system call table
#### Add it to the file **syscall_64.tbl**
(你是64bit系統就修改這個,32bit修改下面那個)
```
$ vim ~/linux-5.14.16/arch/x86/entry/syscalls/syscall_64.tbl
```
新增如下,並記住System Number = 448。

#### Add it to the file **syscall_32.tbl**
```
$ vim ~/linux-5.14.16/arch/x86/entry/syscalls/syscall_32.tbl
```

### (6) Define my system call
```
$ cd linux-5.14.16
$ mkdir pass_kernel_data
$ cd pass_kernel_data
$ vim pass_kernel_data.c
```
在 **pass_kernel_data.c** Define:
- system call 程式
從 [`init_task`](https://elixir.bootlin.com/linux/v4.14/source/init/init_task.c#L20) 開始遍歷 task_list 至指定 task,傳出指定資料至指定記憶體段。
```c=
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/init_task.h>
#include <linux/syscalls.h>
// you need to pass this data struct to userspace and print on terminal
struct data_segment
{
unsigned long start_code;
unsigned long end_code;
};
SYSCALL_DEFINE2(pass_kernel_data, pid_t, user_pid, void* __user, user_address)
{
struct data_segment my_data_segment;
struct task_struct *task;
for_each_process(task)
{
if(task->pid == user_pid)
{
my_data_segment.start_code = task->mm->start_code;
my_data_segment.end_code = task->mm->end_code;
copy_to_user(user_address, &my_data_segment, sizeof(struct data_segment));
}
}
return 0;
}
```
建立 pass_kernel_data 資料夾的 makefile
`$ vim Makefile`
```c=
obj-y := pass_kernel_data.o
```
返回上一層修改makefile
```
$ cd .. // to linux-5.14.16/
$ vim Makefile
```
找到 **core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/**
在最後面新增 **pass_kernel_data/**
### (7) Add corresponding function prototype for your system call to the header file of **syscalls.h**
```
$ vim ~/linux-5.14.16/include/linux/syscalls.h
```
在最下面(#endif前)新增一行 :
`asmlinkage int pass_kernel_data(pid_t user_pid, void* __user user_address);`
### (8) Clean all the previous configurations of the kernel
可先使用下列指令查看當前的kernel version
```
$ uname -a
```
清理上次編譯時產生的目標檔、暫存檔、模組檔 ..,以及核心組態檔和備份檔
```
$ cd linux-5.14.16
$ sudo make mrproper
```
### (9) Copy our current kernel configuration to the new kernel
將使用中的核心組態檔複製過來
```
$ cp /boot/config-`uname -r` .config
$ make oldconfig
```
### (10) Compile Kernel
將使用中的核心組態檔複製過來
```
$ sudo make menuconfig // 用預設的就好
$ sudo make -j$(nproc) // 包含make bzImage、make modules...等動作
$ sudo make modules_install -j$(nproc)
$ sudo make install -j$(nproc) // 安裝kernel
$ sudo update-grub
$ sudo reboot
```
:warning: 若遇到報錯`No rule to make target 'debian/certs/debian-uefi-certs.pem`
將 **linux-5.14.16/.config** 中下列兩項設為空值
*CONFIG_SYSTEM_TRUSTEDKEYS=""*
*CONFIG_SYSTEM_REVOCATIONKEYS=""*
:warning: 重新編譯前請先make clean
### (11) Creat a .c file (user process) to call mysyscall
```
$ vim test.c
$ gcc test.c -o test
$ ./test
$ dmesg | tail // see the kernel log info
```
user space程式
```c=
#include <syscall.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
struct data_segment
{
unsigned long start_code;
unsigned long end_code;
};
int main()
{
struct data_segment my_data_segment;
// no.448的System call
int a = syscall(448, getpid(), (void*)&my_data_segment);
printf("system call return %d\n",a);
printf("Start: %lx\nEnd: %lx\n", my_data_segment.start_code, my_data_segment.end_code);
int b;
scanf("%d",&b); // 卡住程式
return 0;
}
```
---
## **<font color="#f00">tmux</font>**
參考 [https://hackmd.io/@ADLlearning/BJp1I6RLK](https://hackmd.io/@ADLlearning/BJp1I6RLK)
[tmux使用](https://blog.gtwang.org/linux/linux-tmux-terminal-multiplexer-tutorial/)
[ps指令](http://www.happystreet.com.tw/index.php/system-dynamic-teaching/unix-linux/410-a-day-of-school-a-unix-command-ps)
### <font color="blue">安裝tmux</font>
```
$ sudo apt install tmux
```
下圖為pane的操作常用鍵:

下圖為window的操作常用鍵:

### <font color="blue">測試使用tmux</font>
先ctrl+b %開啟兩個pane
一邊使用 test.c (先修改成可讓程式卡著執行)
一邊使用 `$ ps -a` 指令可觀看目前同個終端下的執行程式

可以看到正在執行的process pid
```
$ cd /proc/5588 //每次執行分配的pid不同,如下圖pid=5603
$ cat maps
```
這裡可以看到process的位址分布

第一列就是目標text段的開頭位置
## Q&A
### 1. what is “asmlinkage”
當 system call handler 要呼叫相對應的 system call routine 時,便將一般用途暫存器的值 push 到 stack 裡,因此 system call routine 就要由 stack 來讀取 system call handler 傳遞的參數。這就是 asmlinkage 標籤的用意。
加上 “asmlinkage” 後,C function 就會由 stack 取參數,而不是從 register 取參數(可能發生在程式碼最佳化後)。
[https://www.twblogs.net/a/5e4fc053bd9eee101df86de7](https://www.twblogs.net/a/5e4fc053bd9eee101df86de7)
### 2. memcpy與copy_to_user差別
[https://blog.csdn.net/lhl_blog/article/details/107030517](https://blog.csdn.net/lhl_blog/article/details/107030517)
[https://stackoverflow.com/questions/14970698/copy-to-user-vs-memcpy](https://stackoverflow.com/questions/14970698/copy-to-user-vs-memcpy)
### 3. vm_area_struct與mm_struct差別
[https://blog.csdn.net/weixin_41028621/article/details/104455327](https://blog.csdn.net/weixin_41028621/article/details/104455327)
### 4. sys_前綴問題(3.X版kernel不需要,為何4.X 5.X版後需要加)
syscalls.h定義
[https://elixir.bootlin.com/linux/v5.14.16/source/include/linux/syscalls.h#L211](https://elixir.bootlin.com/linux/v5.14.16/source/include/linux/syscalls.h#L211)
[http://gityuan.com/2016/05/21/syscall/](http://gityuan.com/2016/05/21/syscall/)
### 5. code_start跟code_end範圍這麼大為啥沒用這麼多
分配空間大小時,OS不知道程式會用多少空間,分配以PAGE為單位,所以通常會超出範圍,其實沒用那麼多。
### 6. for_each_process要遍歷所有process不優,有沒有別的替代方式?
可以使用get_current(asm/current.h)來得知目前正在執行的程序(可直接include`asm/current.h`使用"current" keyword。
這裡的current process指的是呼叫這個system call的process
[https://stackoverflow.com/questions/12434651/what-is-the-current-in-linux-kernel-source](https://stackoverflow.com/questions/12434651/what-is-the-current-in-linux-kernel-source)
