---
# System prepended metadata

title: HW1 - kernel compilation & syscall

---

---
hackmd:
  url: https://hackmd.io/miWA0jpiQbWTf4JpyZPDWg
  title: HW1 - kernel compilation & syscall
  lastSync: 2025-09-29T07:43:21.391Z
---
# HW1 - kernel compilation & syscall

## Compiling a custom Linux Kernel

![Compliation result](https://hackmd.io/_uploads/HJylDfL3ee.png)

- **List the steps you took** to compile the kernel
	1. export ARCH=riscv
	2. export CROSS_COMPILE=riscv64-linux-gnu-
	3. make menuconfig
	4. General setup  --->  () Local version - append to kernel release
	5. Enter -os-314551027 & save
	6. make -j$(nproc)

- **Answer the Q&As**
    1. What are the main differences between the RISC-V and x86 architectures?
		
		==Ans== The ISA type of the RISC-V is RISC(simple, open-source). The one of the x86(complex, legacy-heavy, proprietary) is CISC.

    2. Why do the architecture differences matter when building the kernel? What happens if you build the kernel without the correct RISC-V cross-compilation flag?

		==Ans== Kernel use different codes that fit the architecture. It is not portable, so different architect cannot run on different machine.

		==Ans== As the below experiment show, the compilation tool will still try to build the rsicv. But it during compilation, it use gcc in x86. There are unrecognized arguments and options.
		
		```bash
		ubuntu@1bcff34ebbbb:~/linux$ make -j$(( $(nproc) - 1 )) 
		  WRAP    arch/riscv/include/generated/uapi/asm/errno.h
		  WRAP    arch/riscv/include/generated/uapi/asm/fcntl.h
		  WRAP    arch/riscv/include/generated/uapi/asm/ioctl.h
		  WRAP    arch/riscv/include/generated/uapi/asm/ioctls.h
		  WRAP    arch/riscv/include/generated/uapi/asm/ipcbuf.h
		```
	
		Error show as following:
		
		```bash
		gcc: error: unrecognized argument in option ‘-mabi=lp64’
		gcc: note: valid arguments to ‘-mabi=’ are: ms sysv
		gcc: error: unrecognized argument in option ‘-mcmodel=medany’
		gcc: note: valid arguments to ‘-mcmodel=’ are: 32 kernel large medium small
		  CC      scripts/mod/devicetable-offsets.s
		gcc: error: unrecognized argument in option ‘-mabi=lp64’
		gcc: note: valid arguments to ‘-mabi=’ are: ms sysv
		gcc: error: unrecognized argument in option ‘-mcmodel=medany’
		gcc: note: valid arguments to ‘-mcmodel=’ are: 32 kernel large medium small
		gcc: error: unrecognized command-line option ‘-mno-save-restore’
		make[1]: *** [scripts/Makefile.build:250: scripts/mod/empty.o] Error 1
		make[1]: *** Waiting for unfinished jobs....
		gcc: error: unrecognized command-line option ‘-mno-save-restore’
		make[1]: *** [scripts/Makefile.build:118: scripts/mod/devicetable-offsets.s] Error 1
		make: *** [Makefile:1269: prepare0] Error 2
		```
		
    3. Why is Docker used in this assignment, and what advantages does it provide? Please list at least two of them.
	    1. Easy-duplicate.
	    2. Environment separation.

## Implementing new System Calls

![screenshot of revstr](https://hackmd.io/_uploads/S11x8hw3eg.png)

![screenshot of tempbuf](https://hackmd.io/_uploads/Hk7w_nP3xe.png)

![test tempbuf](https://hackmd.io/_uploads/HyGKO2P2ll.png)

- Explain **how you added system calls to your custom Linux kernel** and the **implementation details** of `sys_revstr` and `sys_tempbuf`
	- How to add syscalls
		1. Save custom syscalls in a folder inside kernel.
		2. Write Makefile in the folder that use the Kbuild.
		3. Add the additional folder you create into the root Makefile.
			
			```Makefile
			ifeq ($(KBUILD_EXTMOD),)
			# Objects we will link into vmlinux / subdirs we need to visit
			core-y := mSyscall/
			...
			endif # KBUILD_EXTMOD
			```
			
		4. Add prototypes in `include/linux/syscalls.h`
			
			```c
			/* my syscalls: mSyscall/ */ 
			asmlinkage long sys_revstr(char __user * str, size_t n); 
			asmlinkage long sys_tempbuf(int mode, void __user * data, size_t size);
			```

		5. Define the syscalls number in `arch/riscv/include/uapi/asm/unistd.h`  or `include/uapi/asm-generic/unistd.h`. I use `include/uapi/asm-generic/unistd.h` in this work.

			```c
			#define __NR_revstr  451
			__SYSCALL(__NR_revstr,  sys_revstr)
			
			#define __NR_tempbuf 452
			__SYSCALL(__NR_tempbuf, sys_tempbuf)
			```

	- **implementation details** of `sys_revstr` and `sys_tempbuf`
		- **sys_revstr**:  
			Copies a string from user space into a kernel buffer using `copy_from_user`, reverses it in place with a two-pointer swap, and copies it back using `copy_to_user`. Memory is allocated with `kmalloc` and freed with `kfree`. `printk` logs the original and reversed strings.

		- **sys_tempbuf**:  
			Maintains a kernel list of strings (`LIST_HEAD`) and embed the` struct list_head `into `tmp_node` structure.
			- **ADD**: allocates memory, copies a user string, and appends it to the list with `list_add_tail`.
			- **REMOVE**: use `list_for_each` to searches for a matching string. `memcmp` to compare the difference between the `data` and the string in `list_entry`. Deletes it with `list_del`, and frees memory.
			- **PRINT**: concatenates all stored strings into a space-separated buffer with `scnprintf` and copies it back.  

- **Answer the Q&As**
    1. What does the `include/linux/syscalls.h` do?

		==Ans== It is Linux syscall interfaces that act as a central declaration place for syscall prototypes.

    2. Explain the difference between a system call and a glibc library call, then give one example that maps a glibc function to the specific system call it ultimately invokes.

		==Ans== 
		1. glibc library call is high-level C library function. It is a wrapper around syscalls with extra logic, error handling, etc.
		2. syscall is low-level interface that make user programs work with kernel without really touching the fundamental system.
		3. E.g. `printf` is a glibc function to print text onto the terminal. It will call `write` syscall eventually. We can use strace to analyze.

			```cpp
			#include <stdio.h> 
			int main(void) { 
				printf("hello world\n"); 
				return 0; 
			}
			```

			```bash
			...
			fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
			...
			write(1, "hello world\n", 12hello world ) = 12
			```

		The `fstat` set stdout (fd 1). `write` syscall puts the characters onto the character device.

    2. Explain the difference between static linking and dynamic linking

		==Ans== 
		1. static linking is link the library in the compile time. The code in the library will be copy into the binary file.
		2. dynamic linking is link the library in the runtime. The code in library will be loaded into memory by dynamic loader when needed.

    3. In this assignment environment, why do we have to compile the test programs with the `-static` flag? What would happen if we omitted it?

		==Ans== As the previous statements, static linking will make the libraryt be copy into the binary file. Thus, the riscv environment won't need to have the library to make it work. As we compile another dynamic linked version, and runing inside the riscv, the error occurs.

		```shell
		/home $ ./test_revstr_dyLinked 
		sh: ./test_revstr_dyLinked: not found
		```

		view it with `readelf` with can see during the program running, it will try to find the interpreter in `lib/ld-linux-riscv64-lp64d.so.1`. However, there is no such thing in this riscv environment.

		```bash
		Program Headers:
		  Type           Offset             VirtAddr           PhysAddr
		                 FileSiz            MemSiz              Flags  Align
		  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
		                 0x0000000000000230 0x0000000000000230  R      0x8
		  INTERP         0x0000000000000270 0x0000000000000270 0x0000000000000270
		                 0x0000000000000021 0x0000000000000021  R      0x1
		      [Requesting program interpreter: /lib/ld-linux-riscv64-lp64d.so.1]

		```
		
		Compared with static one, there is no INTERP section.