# Linux Project 1 ## Environment - Kernel vesion : 5.15.137 - 作業系統 : ubuntu 22.04 ## Question Description ![staff.csie.ncu.edu.tw_hsufh_COURSES_FALL2023_linux_project_1.html](https://hackmd.io/_uploads/HyiMqwGFp.png) ## Kernel 編譯過程 下載欲編譯的kernel source code, 新增system call後執行以下指令: ```bash= sudo make -j4 sudo make modules_install -j4 sudo make install -j4 ``` ## 編譯時遇到的問題 - 我們有遇到無法編譯成功的情況,發生的問題每次、每個組員都遇到不同問題,但是經過幾次更換版本之後問題就大部分解決了 - 執行sudo make時遇到其中一個問題:"FAILED: load BTF from vmlinux: No such file or directory", 只要下載套件dwarves即可解決 ``` sudo apt install dwarves ``` ## 新增System Call過程 #### 在linux-5.15.137 directory中創建資料夾vtop, 並在創建的資料夾中新增system call: ``` cd /usr/src/linux-5.15.137 mkdir vtop cd vtop vim vtop.c ``` #### 同時創建Makefile並寫入: ``` obj-y := vtop.o ``` #### 接著回上個目錄改 Makefile, 將剛剛新增的資料夾加到路徑中: ``` cd .. vim Makefile ``` ![image](https://hackmd.io/_uploads/rk03pyfSa.png) #### 修改syscall_64.tbl: ``` vim arch/x86/entry/syscalls/syscall_64.tbl ``` ![image](https://hackmd.io/_uploads/ryCm0kMHT.png) #### 修改syscalls.h(添加syscall原型) ``` vim include/linux/syscalls.h ``` ![image](https://hackmd.io/_uploads/r1x81eMr6.png) ## System Call - Linux 在 2.6.10 中引入了四層的頁表輔助虛擬地址的轉換(PGD、PUD、PMD、PTE),在 4.11 中引入了五層的頁表結構(多了一層P4D,也就是:PGD、P4D、PUD、PMD、PTE)。 - 與pud_none(pud_t), pmd_none(pmd_t)不同的是,p4d_none(p4d_t)=True並不一定代表程式出錯,linux kernel會自動幫我們映射到下一層,即回傳(p4d_t)*pgd。 - 依照題目要求,大多system call失敗時會回傳-1,這邊我們改成:失敗return 0,成功則return傳入function之virtual address所對應的physical address。 ```C= #include <linux/kernel.h> #include <linux/uaccess.h> #include <linux/syscalls.h> #include <linux/mm_types.h> #include <linux/sched.h> #include <linux/mm.h> #include <asm/pgtable.h> #include <asm/current.h> SYSCALL_DEFINE1(vtop,unsigned long ,vaddr){ pgd_t *pgd; p4d_t *p4d; pud_t *pud; pmd_t *pmd; pte_t *pte; unsigned long p_index=0,p_offset=0; unsigned long paddr = 0; printk("va : %lx",vaddr); pgd = pgd_offset(current->mm,vaddr); if (pgd_none(*pgd) || pgd_bad (*pgd)){ return 0; } p4d = p4d_offset(pgd,vaddr); if(p4d_bad(*p4d)){ return 0; } pud = pud_offset(p4d,vaddr); if(pud_none(*pud) || pud_bad(*pud)){ return 0; } pmd = pmd_offset(pud,vaddr); if(pmd_none(*pmd) || pmd_bad(*pmd)){ return 0; } pte = pte_offset_kernel(pmd,vaddr); if(!pte){ return 0; } p_index = pte_val(*pte) & PAGE_MASK; p_offset = vaddr & ~PAGE_MASK; paddr = p_index | p_offset; printk("pa :%lx",paddr); return paddr; } ``` ## Multithread.c ```C= #include <stdio.h> #include <sys/syscall.h> #include <unistd.h> #include <stdlib.h> #include <pthread.h> #include <string.h> #include <semaphore.h> sem_t sem; __thread int TLS = 0; int data = 0; int bss ; void code(){ return ; } void* thread_function(void* thread_num){ sem_wait(&sem); printf("----------------Thread %d -------------------\n", (int) thread_num); int stack = 7; char* sname[] = {"TLS","data","bss","code","stack","heap","lib"}; int *heap = malloc(sizeof(int)); unsigned long linear_address[] = { (unsigned long) &TLS, (unsigned long) &data, (unsigned long) &bss, (unsigned long) &code, (unsigned long) &stack, (unsigned long) heap, (unsigned long) &printf }; for(int i=0;i<stack;i++){ unsigned long vaddr = linear_address[i]; unsigned long paddr = syscall(449,vaddr); printf("%s: Linear address: %#lx , Physical address: %#lx \n", sname[i],vaddr,paddr); } sem_post(&sem); return NULL; } void main(){ pthread_t thread1 , thread2, thread3; sem_init(&sem,0,1); pthread_create(&thread1, NULL, thread_function, (void*)1); pthread_create(&thread2, NULL, thread_function, (void*)2); pthread_create(&thread3, NULL, thread_function, (void*)3); pthread_join(thread1, NULL); pthread_join(thread2, NULL); pthread_join(thread3, NULL); sem_destroy(&sem); return; } ``` ### 編譯 ``` gcc -o mt multithread.c -pthread ``` ## multithread.c 執行結果 ![image](https://hackmd.io/_uploads/rkJ-UuhE6.png) ## 各Thread在Physical Memory的Layout ### Thread1 ![image](https://hackmd.io/_uploads/ByMgZK3ET.png) ### Thread2 ![image](https://hackmd.io/_uploads/HyGW-K246.png) ### Thread3 ![image](https://hackmd.io/_uploads/r1jb-K346.png) ## Example Code - 增加了heap區段 - 加入semaphore,避免thread交互執行導致難以觀察結果 ```C= #include <stdlib.h> #include <stdio.h> #include <pthread.h> #include <string.h> #include <sys/syscall.h> /* Definition of SYS_* constants */ #include <unistd.h> #include <semaphore.h> extern void *func1(void *); extern void *func2(void *); extern int main(); void * my_get_physical_addresses(void *); void * my_get_physical_addresses(void *v_addr){ unsigned long vaddr = (unsigned long)v_addr; unsigned long p_addr = syscall(449, vaddr); unsigned long *p_ptr = (unsigned long *)p_addr; return p_ptr; } struct data_ { int id ; char name[16] ; }; typedef struct data_ sdata ; static __thread sdata tx ; //thread local variable sem_t sem; int a=123; //global variable void hello(int pid) { int b=10; //local varialbe int *heap = malloc(sizeof(int)); //heap area b=b+pid; //global variable printf("In thread %d \nthe value of gloable varialbe a is %d, the offset of the logical address of a is %p, ", pid, a, &a); printf("the physical address of global variable a is %p\n", my_get_physical_addresses(&a) ); //local variable printf("the value of local varialbe b is %d, the offset of the logical address of b is %p, ", b, &b); printf("the physical address of local variable b is %p\n", my_get_physical_addresses(&b)); //heap printf("the value of the logical address of heap area is %p, ", heap); printf("the physical address of heap area is %p\n", my_get_physical_addresses(heap)); //thread local variable printf("the offset of the logical address of thread local varialbe tx is %p, ", &tx); printf("the physical address of thread local variable tx is %p\n", my_get_physical_addresses(&tx)); //function printf("the offset of the logical address of function hello is %p, ", hello); printf("the physical address of function hello is %p\n", my_get_physical_addresses(hello)); printf("the offset of the logical address of function func1 is %p, ", func1); printf("the physical address of function func1 is %p\n", my_get_physical_addresses(func1)); printf("the offset of the logical address of function func2 is %p, ", func2); printf("the physical address of function func2 is %p\n", my_get_physical_addresses(func2)); printf("the offset of the logical address of function main is %p, ", main); printf("the physical address of function main is %p\n", my_get_physical_addresses(main)); //library function printf("the offset of the logical address of library function printf is %p, ", printf); printf("the physical address of library function printf is %p\n", my_get_physical_addresses(printf)); printf("====================================================================================================================\n"); } void *func1(void *arg) { char *p = (char*) arg ; int pid ; pid = syscall( __NR_gettid ); tx.id = pid ; strcpy(tx.name,p) ; sem_wait(&sem); printf("I am thread with ID %d executing func1().\n",pid); hello(pid); sem_post(&sem); while(1) { //printf("(%d)(%s)\n",tx.id,tx.name) ; sleep(1) ; } } void *func2(void *arg) { char *p = (char*) arg ; int pid ; pid = syscall( __NR_gettid ); tx.id = pid ; strcpy(tx.name,p) ; sem_wait(&sem); printf("I am thread with ID %d executing func2().\n",pid); hello(pid); sem_post(&sem); while(1) { //printf("(%d)(%s)\n",tx.id,tx.name) ; sleep(2) ; } } int main() { pthread_t id[2]; sem_init(&sem, 0, 1); char p[2][16] ; strcpy(p[0],"Thread1") ; pthread_create(&id[0],NULL,func1,(void *)p[0]); strcpy(p[1],"Thread2") ; pthread_create(&id[1],NULL,func2,(void *)p[1] ); int pid ; pid = syscall( __NR_gettid ); tx.id = pid ; strcpy(tx.name,"MAIN") ; sem_wait(&sem); printf("I am main thread with ID %d.\n", pid); hello(pid); sem_post(&sem); sem_destroy(&sem); while(1) { //printf("(%d)(%s)\n",tx.id,tx.name) ; sleep(5) ; } } ``` ### Example Code 執行結果 ![image](https://hackmd.io/_uploads/HkFZBKhVp.png) ## Q&A #### Q : copy_to_user() and copy_from_user()會有什麼問題? A : 因為function會將data copy to kernel space,所以如果kernel memory size過小的話會出現問題。 #### Q : system call define方式為何要改變?用舊的會有什麼問題? A : 詳請請看[Confusion about CVE-2009-0029](https://stackoverflow.com/questions/18885045/confusion-about-cve-2009-0029-linux-kernel-insecure-64-bit-system-call-argument) #### Q : pthread_create()會call哪一個kernel systemcall? A : clone() #### Q : thread是通過何種方式傳遞訊息? A : signal #### Q : 為何paging要分4, 5個level? 為何不分2個, 甚至1個? A : level過少會造成"儲存paging table memory過大"的問題,且多層架構在操作即設計上也比較靈活 ## Reference 1. [add a system call教學網址 by 助教](https://hackmd.io/aist49C9R46-vaBIlP3LDA?both) 2. [新增system call](https://hackmd.io/@combo-tw/Linux-%E8%AE%80%E6%9B%B8%E6%9C%83/%2F%40combo-tw%2FBJPoAcqQS#%E5%A6%82%E4%BD%95%E6%96%B0%E5%A2%9E%E4%B8%80%E5%80%8B-System-Call) 3. [Linux轉址以及Virtural Memory](https://iter01.com/510311.html) 4. [Linux Page Tables](https://docs.kernel.org/mm/page_tables.html) 5. [copy_to_user&copy_from_user](https://blog.51cto.com/u_16087831/6223592) 6. [Linux Project 1](https://staff.csie.ncu.edu.tw/hsufh/COURSES/FALL2023/linux_project_1.html)