# 2019q1 Homework2 (fibdrv)
contributed by < `TWPLrh` >
## 測試模組
* Linux核心版本
```
$uname -r
4.18.0-16-generic
```
* 確認 linux-header 套件是否存在
```
$ dpkg -L linux-headers-4.18.0-16-generic | grep "/lib/modules"
/lib/modules
/lib/modules/4.18.0-16-generic
/lib/modules/4.18.0-16-generic/build
```
* 產生之模組資訊
```
filename: /home/shihruhung/Desktop/linux核心設計/fibdrv/fibdrv.ko
version: 0.1
description: Fibonacci engine driver
author: National Cheng Kung University, Taiwan
license: Dual MIT/GPL
srcversion: 24DC5FB7E7608AF16B0CC1F
depends:
retpoline: Y
name: fibdrv
vermagic: 4.18.0-16-generic SMP mod_unload
```
## 自我檢查清單
1. 檔案 `fibdrv.c` 裡頭的 `MODULE_LICENSE`, `MODULE_AUTHOR`, `MODULE_DESCRIPTION`, `MODULE_VERSION` 等巨集做了什麼事,可以讓核心知曉呢? `insmod` 這命令背後,對應 Linux 核心內部有什麼操作呢?請舉出相關 Linux 核心原始碼並解讀
- `MODULE_LICENSE`
```c=
#define MODULE_LICENSE(_license) MODULE_INFO(license, _license)
```
- `MODULE_AUTHOR`
```c=
#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author)
```
- `MODULE_DESCRIPTION`
```c=
#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description)
```
- `MODULE_VERSION`
```c=
#if defined(MODULE) || !defined(CONFIG_SYSFS)
#define MODULE_VERSION(_version) MODULE_INFO(version, _version)
#else
#define MODULE_VERSION(_version) \
static struct module_version_attribute ___modver_attr = { \
.mattr = { \
.attr = { \
.name = "version", \
.mode = S_IRUGO, \
}, \
.show = __modver_version_show, \
}, \
.module_name = KBUILD_MODNAME, \
.version = _version, \
}; \
static const struct module_version_attribute \
__used __attribute__ ((__section__ ("__modver"))) \
* __moduleparam_const __modver_attr = &___modver_attr
#endif
```
上面四個Macro都會執行`MODULE_INFO`,下面以`MODULE_VERSION("0.1")`為例,和程式碼一起往下執行。
```c=
MODULE_VERSION("0.1")
```
```c=237
#define MODULE_VERSION("0.1") MODULE_INFO(version, "0.1")
```
```c=161
#define MODULE_INFO(version, "0.1") __MODULE_INFO(version, version, "0.1")
```
```c=21
#define __MODULE_INFO(version, version, "0.1") \
static const char __UNIQUE_ID(version)[] \
__used __attribute__((section(".modinfo"), unused, aligned(1))) \
= __stringify(version) "=" "0.1"
```
執行到這裡看到`__stringify()`,`__UNIQUE_ID()`和`__attribute__`這三個`marco`
- `__stringify()`
```c=
#ifndef __LINUX_STRINGIFY_H
#define __LINUX_STRINGIFY_H
/* Indirect stringification. Doing two levels allows the parameter to be a
* macro itself. For example, compile with -DFOO=bar, __stringify(FOO)
* converts to "bar".
*/
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
#endif /* !__LINUX_STRINGIFY_H */
```
將參數轉成字串
- `__UNIQUE_ID()`
```c=164
/* Not-quite-unique ID. */
#ifndef __UNIQUE_ID
# define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __LINE__)
#endif
```
- `__PASTE()` - `__UNIQUE_ID()`之延伸
```c=
/* Indirect macros required for expanded argument pasting, eg. __LINE__. */
#define ___PASTE(a,b) a##b
#define __PASTE(a,b) ___PASTE(a,b)
```
`a##b`的作用是將`char[]a, b`連結在一起,幫助`__UNIQUE_ID()`整合`__attribute__()`和`"version"`
- `__attribute__()`
這個用法在很多的`.h`都有看到。這裡的用法為`(section(".modinfo"), unused, aligned(1))`,`section`是連結`modinfo`用的,`unused`讓`compiler`不會發出警告,`aligned(1)`對齊後面stringify的字串。
- `利用 sudo strace insmod fibdrv.ko`
```=
execve("/sbin/insmod", ["insmod", "fibdrv.ko"], 0x7ffd803e3978 /* 27 vars */) = 0
brk(NULL) = 0x560ddc2bb000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=89368, ...}) = 0
mmap(NULL, 89368, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f8dd36b8000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8dd36b6000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8dd30b6000
mprotect(0x7f8dd329d000, 2097152, PROT_NONE) = 0
mmap(0x7f8dd349d000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f8dd349d000
mmap(0x7f8dd34a3000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8dd34a3000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f8dd36b7540) = 0
mprotect(0x7f8dd349d000, 16384, PROT_READ) = 0
mprotect(0x560ddbf26000, 8192, PROT_READ) = 0
mprotect(0x7f8dd36ce000, 4096, PROT_READ) = 0
munmap(0x7f8dd36b8000, 89368) = 0
brk(NULL) = 0x560ddc2bb000
brk(0x560ddc2dc000) = 0x560ddc2dc000
uname({sysname="Linux", nodename="shihruhung-GL552VW", ...}) = 0
openat(AT_FDCWD, "/lib/modules/4.18.0-16-generic/modules.softdep", O_RDONLY|O_CLOEXEC) = 3
fcntl(3, F_GETFL) = 0x8000 (flags O_RDONLY|O_LARGEFILE)
fstat(3, {st_mode=S_IFREG|0644, st_size=511, ...}) = 0
read(3, "# Soft dependencies extracted fr"..., 4096) = 511
read(3, "", 4096) = 0
close(3) = 0
openat(AT_FDCWD, "/proc/cmdline", O_RDONLY|O_CLOEXEC) = 3
read(3, "BOOT_IMAGE=/boot/vmlinuz-4.18.0-"..., 4095) = 119
read(3, "", 3976) = 0
close(3) = 0
getcwd("/home/shihruhung/Desktop/linux\346\240\270\345\277\203\350\250\255\350\250\210/fibdrv", 4096) = 50
stat("/home/shihruhung/Desktop/linux\346\240\270\345\277\203\350\250\255\350\250\210/fibdrv/fibdrv.ko", {st_mode=S_IFREG|0644, st_size=8288, ...}) = 0
openat(AT_FDCWD, "/home/shihruhung/Desktop/linux\346\240\270\345\277\203\350\250\255\350\250\210/fibdrv/fibdrv.ko", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=8288, ...}) = 0
mmap(NULL, 8288, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f8dd36cb000
finit_module(3, "", 0) = 0
munmap(0x7f8dd36cb000, 8288) = 0
close(3) = 0
exit_group(0) = ?
+++ exited with 0 +++
```
2. 當我們透過 insmod 去載入一個核心模組時,為何 module_init 所設定的函式得以執行呢?Linux 核心做了什麼事呢?
```c=128
/* Each module must use one module_init(). */
#define module_init(initfn) \
static inline initcall_t __maybe_unused __inittest(void) \
{ return initfn; } \
int init_module(void) __copy(initfn) __attribute__((alias(#initfn)));
```
在`linux/module.h`中有這個macro,`__attribute__()`會執行initfn,也就是使用者自定義的`initfn()`;