# 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()`;