# Linux Project 1
http://www.cjwind.idv.tw/Add-system-call-to-linux/
https://medium.com/@lars10192002/%E7%B7%A8%E8%AD%AFlinux-kernel-%E7%89%88%E6%9C%AC-system-call-83c8ebbae62f
https://wenyuangg.github.io/posts/linux/linux-add-system-call.html
hint
1. ctrl+alt+T 可快速叫出終端機
2. shift+↓(方向鍵) 可快速翻頁(用來找特定要修改的code)
```
1.cd /usr/src
2.ls
3.sudo wget https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.10.14.tar.xz --no-check-certificate
4.sudo tar xvf linux-4.10.14.tar.xz
5.sudo rm linux-4.10.14.tar.xz
```
```
cd /usr/src/linux-4.10.14
# 在裡面創建一個名叫 hello 的資料夾
mkdir hello
# 把目錄轉到 hello 資料夾
cd hello
vim hello.c
```
進入vim 後按a插入文字,編輯完按esc後輸入:wq
hello.c內容
```
/* hello.c */
#inlcude <linux/kernel.h>
asmlinkage long sys_hello(void){
printk("Hello world!\n");
return 0;
}
```
於同一個下再建立一個 Makefile
```
vim Makefile
```
Makefile 的內容
```
obj-y := hello.o
```
接著在 linux-4.10.14 目錄下改 Makefile 告訴你的 compiler ,新的 system call 可以在 hello 目錄中找到
```
# 把目錄轉回去
cd /usr/src/linux-4.10.14
# 打開此目錄下的 Makefile
vim Makefile
```
你可以在裡面找到一行, 如下我括起來的地方

請你在後面補上 hello 這樣 kernel 在編譯時才會到你的 hello 目錄 改成下面這樣
```
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ hello/
```
接著因為我的系統是 32 位元的 ((我想大部分的人都是)) 所以我要去修改 syscall_32.tbl 檔
```
cd arch/x86/entry/syscalls
vim syscall_32.tbl
```
請你在最後一行添加上你的 system call 然後請把編號記住, 等一下會用

接著編輯 syscalls.h 檔 將我們 syscall 的原型添加進檔案的最後一行 (#endif之前)
```
# 把目錄轉回去
cd /usr/src/linux-4.10.14
cd include/linux
vim syscalls.h
```

改完後當然是 compile 跟安裝啦!
```
$sudo apt-get install fakeroot build-essential kernel-package libncurses5 libncurses5-dev
$sudo apt-get install python-pip python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg8-dev zlib1g-dev
$ make menuconfig # 如果之前設定過可以不用
$ make -j4 # 可以加 -j N 來平行編譯
$ sudo make modules_install
$ sudo make install
$ reboot
```
重開機後選進階選項

選最新版本且kernel正確的版本

打開終端機後開啟一個hello.c
```
#include <unistd.h>
#include <sys/syscall.h>
#include <stdio.h>
int main() {
long ret = syscall(383); // 335 是剛剛的 system call 編號
printf("sys_hello return %ld\n", ret);
return 0;
}
```
將hello.c編譯
```
gcc hello.c
```
因為沒有指定編譯後的檔名,因此預設檔名為a.out
執行a.out
```
./a.out
```
執行正常會看到:
```
sys_hello return 0
```
在 kernel 裡用 printk() 印出的訊息可以用指令 dmesg(需要 root 權限)看到:
```
[ 3.211745] AVX2 version of gcm_enc/dec engaged.
[ 3.211748] AES CTR mode by8 optimization enabled
[ 3.459866] IPv6: ADDRCONF(NETDEV_CHANGE): ens3: link becomes ready
[ 81.608954] Hello World!
```
## 測試方法
https://blog.gtwang.org/linux/linux-tmux-terminal-multiplexer-tutorial/
測試使用tmux
首先寫一個需要輸入值的測試檔hello.c
編譯成a.out後
使用tmux建立兩個session(ctrl+b %)
一邊讓a.out執行 (./a.out)
一邊看正在執行的process
```
pa -a
```

可以看到正在執行的process id
接下來進入該process看位址分布
```
cd /proc/6436
ls
```
使用ls可以看到process內的資料
然後cat maps可以看到process 位址的分布
```
cat maps
```

其中第一行可執行的就是目標text段的開頭位置
## 作業的system call
```
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/init_task.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;
};
asmlinkage int pass_kernel_data(pid_t user_pid, void* __user user_address)
{
struct data_segment my_data_degment;
struct task_struct *task;
for_each_process(task)
{
if(task->pid == user_pid)
{
//what is x1, x2, x3
my_data_segment.start_code = task->mm->start_code;
//what is y1, y2, y3
my_data_segment.end_code = task->mm->end_code;
// xxx is key function
copy_to_user(user_address, &my_data_segment, sizeof(struct data_segment));
}
}
...
return 0;
```
編譯完kernel 後再開一個測試檔test.c
test.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;
int a = syscall(384, getpid(), (void*)&my_data_segment);
printf("Start: %lx\nEnd: %lx\n", my_data_segment.start_code, my_data_segment.end_code);
int b;
scanf("%d",&b);
return 0;
}
```


問題
1.這個function 怎麼去完成這件事情
2.有哪些system call可能有用到這個function
3.what is "asmlinkage"
當 system call handler 要呼叫相對應的 system call routine 時,便將一般用途暫存器的值 push 到 stack 裡,因此 system call routine 就要由 stack 來讀取 system call handler 傳遞的參數。這就是 asmlinkage 標籤的用意。
why 用stack 傳參數?
因為傳參數的方式需要有通用性,在32位元和64位元時的參數儲存方式不同,64位元是使用暫存器儲存參數,因此利用asmlinkage將

4.memcpy與copy_to_user差別/為啥不用memcpy
1. 因為copy_to_user會檢查access_ok是否使用者操作非法地址,例如避免輸入kernel space的位址會讓這個指令去修改其他kernel的資料。
2. 會自我修復page fault的問題
3. 硬體會有一個開關來確定是否開啟存取,copy_to_user會經過這個PAN,增加安全性
https://blog.csdn.net/lhl_blog/article/details/107030517
5.vm_area_struct與mm差別

vm_area_struct可以顯示較詳細的process分配內容,像cat maps所顯示出來的資料分配就是vm_area_struct
mm只能找到概略性的資源分配(如圖)

6.syscall define的方式(3.X版kernel不需要,為何4.X 5.X版後需要加)
為了通用性,直接在4.X版或5.X版直接加入sys_也不一定能夠讓程式成功執行,系統利用SYSCALL_DEFINE4先定義了不同個參數的systemcall function定義(巨集)
7.code_start跟code_end範圍這麼大為啥沒用這麼多
因為cat maps所顯示出來的範圍是指code text page的範圍,因為一次會開一個page給code使用,所以實際顯示的範圍會比所查到的還要大
而我們的程式顯示出來的是實際code所佔有的大小