# 2019q1 Homework2 (fibdrv) contributed by< `yicheng11` > ## 開發環境 ```shell $ uname -a Linux c44044064-Thinkpad 4.18.0-15-generic #16~18.04.1-Ubuntu SMP Thu Feb 7 14:06:04 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux $ gcc --version gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0 ``` ## 自我檢查清單 * 檔案檔案 fibdrv.c 裡頭的 MODULE_LICENSE, MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_VERSION 等巨集做了什麼事,可以讓核心知曉呢? insmod 這命令背後,對應 Linux 核心內部有什麼操作呢?請舉出相關 Linux 核心原始碼並解讀 `MODULE_LICENSE`:在 kernel module 裡面加上版權宣告 `MODULE_AUTHOR`:表示作者,使用 modinfo 時顯示 `MODULE_DESCRIPTION`:對 module 進行一個簡單的描述 `MODULE_VERSION`:表示 module 的版本 以下程式碼節錄自 [linux/module.h](https://elixir.bootlin.com/linux/v4.18/source/include/linux/module.h) ```clike= #define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info) #define MODULE_LICENSE(_license) MODULE_INFO(license, _license) #define MODULE_AUTHOR(_author) MODULE_INFO(author, _author) #define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description) #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,他們都是將不同的 `tag` 與 `info` 帶入 `MODULE_INFO` `MODULE_INFO` 又會帶入 `__MODULE_INFO` 而在 [linux/moduleparam.h](https://elixir.bootlin.com/linux/v4.18/source/include/linux/moduleparam.h) 中,可以看到對 `__MODULE_INFO` 的描述 ```clike= #ifdef MODULE #define __MODULE_INFO(tag, name, info) \ static const char __UNIQUE_ID(name)[] \ __used __attribute__((section(".modinfo"), unused, aligned(1))) \ = __stringify(tag) "=" info #else /* !MODULE */ /* This struct is here for syntactic coherency, it is not used */ #define __MODULE_INFO(tag, name, info) \ struct __UNIQUE_ID(name) {} #endif ``` 在 [linux/compiler.h](https://elixir.bootlin.com/linux/v4.18/source/include/linux/compiler.h#L166) 找到關於 `__UNIQUE_ID(name)` 的敘述 ```clike= #ifndef __UNIQUE_ID # define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __LINE__) #endif ``` 又在 [linux/compiler_types.h](https://elixir.bootlin.com/linux/v4.18/source/include/linux/compiler_types.h#L53) 中可以看到對`__PASTE` 的描述如下 ```clike= #define ___PASTE(a,b) a##b #define __PASTE(a,b) ___PASTE(a,b) ``` 可以看出`__PASTE(a,b)`的功用即是把 a 與 b 串連起來 參考 [Specifying Attributes of Variables](https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes) `__attribute__`用來設置變量的屬性,`section(".modinfo")`表示該變量被連接到`.modinfo`部分,`unused` 是告訴 compiler,該變量可能不會被引用,不要發出警告,`aligned(1)`表示按1 byte 對齊 在 [linux/stringify.h](https://elixir.bootlin.com/linux/v4.18/source/include/linux/stringify.h#L10) 找到關於`__stringify`的描述 ```clike= /* 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) ``` * 當我們透過 insmod 去載入一個核心模組時,為何 module_init 所設定的函式得以執行呢?Linux 核心做了什麼事呢? ```shell= $ sudo strace insmod fibdrv.ko ... mmap(NULL, 8288, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fb609272000 finit_module(3, "", 0) = 0 munmap(0x7fb609272000, 8288) = 0 close(3) = 0 exit_group(0) = ? +++ exited with 0 +++ ``` 在 [man init_module](http://man7.org/linux/man-pages/man2/init_module.2.html) 中找到對 `finit_module`的描述,發現其行為與 `init_module()`相近 ```shell= The finit_module() system call is like init_module(), but reads the module to be loaded from the file descriptor fd.It is useful when the authenticity of a kernel module can be determined from its location in the filesystem; in cases where that is possible, the overhead of using cryptographically signed modules to determine the authenticity of a module can be avoided. The param_values argument is as for init_module(). ``` 來看看 ` init_module()` 的行為 ```shell= init_module() loads an ELF image into kernel space, performs any necessary symbol relocations, initializes module parameters to values provided by the caller, and then runs the module's init function. This system call requires privilege. The module_image argument points to a buffer containing the binary image to be loaded; len specifies the size of that buffer. The module image should be a valid ELF image, built for the running kernel. The param_values argument is a string containing space-delimited specifications of the values for module parameters (defined inside the module using module_param() and module_param_array()). The kernel parses this string and initializes the specified parameters. Each of the parameter specifications has the form: name[=value[,value...]] The parameter name is one of those defined within the module using module_param() (see the Linux kernel source file include/linux/moduleparam.h). The parameter value is optional in the case of bool and invbool parameters. Values for array parameters are specified as a comma-separated list. ``` * 試著執行 $ readelf -a fibdrv.ko, 觀察裡頭的資訊和原始程式碼及 modinfo 的關聯,搭配上述提問,解釋像 fibdrv.ko 這樣的 ELF 執行檔案是如何「植入」到 Linux 核心 ELF(Executable and Linking Format)是一種 object fule 的格式,用於定義不同類型的 object file 中放了甚麼東西,以及以甚麼樣的格式去放這些東西。 ELF 中有三種類型 `Relocatable file` : Assembler 產生的 .o 檔 `Executable file` : 可執行檔 `Shared object file` : Dynamic Link Library file .so file 來看看 ELF HEADER 提供給我們什麼訊息 ```shell= LC_ALL=C readelf -h fibdrv.ko ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: REL (Relocatable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x0 Start of program headers: 0 (bytes into file) Start of section headers: 6688 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 0 (bytes) Number of program headers: 0 Size of section headers: 64 (bytes) Number of section headers: 25 Section header string table index: 24 ``` 從`class` `type` `machine` 可以看出來 `fibdrv.ko` 是在 X86-64 位機器上生成的64位 relocatable file 從 `Entry point address`,可以知道程序從 vitural adddress 0x0 的地方開始執行 查看 section header 訊息 ```shell= LC_ALL=C readelf -S fibdrv.ko There are 25 section headers, starting at offset 0x1a20: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .note.gnu.build-i NOTE 0000000000000000 00000040 0000000000000024 0000000000000000 A 0 0 4 [ 2] .text PROGBITS 0000000000000000 00000070 000000000000015c 0000000000000000 AX 0 0 16 [ 3] .rela.text RELA 0000000000000000 00001218 0000000000000120 0000000000000018 I 22 2 8 [ 4] .init.text PROGBITS 0000000000000000 000001cc 0000000000000153 0000000000000000 AX 0 0 1 [ 5] .rela.init.text RELA 0000000000000000 00001338 00000000000003a8 0000000000000018 I 22 4 8 [ 6] .exit.text PROGBITS 0000000000000000 0000031f 0000000000000040 0000000000000000 AX 0 0 1 [ 7] .rela.exit.text RELA 0000000000000000 000016e0 00000000000000d8 0000000000000018 I 22 6 8 [ 8] __mcount_loc PROGBITS 0000000000000000 0000035f 0000000000000030 0000000000000000 A 0 0 1 [ 9] .rela__mcount_loc RELA 0000000000000000 000017b8 0000000000000090 0000000000000018 I 22 8 8 [10] .rodata.str1.1 PROGBITS 0000000000000000 0000038f 000000000000006e 0000000000000001 AMS 0 0 1 [11] .rodata.str1.8 PROGBITS 0000000000000000 00000400 0000000000000058 0000000000000001 AMS 0 0 8 [12] .rodata PROGBITS 0000000000000000 00000460 0000000000000100 0000000000000000 A 0 0 32 [13] .rela.rodata RELA 0000000000000000 00001848 0000000000000090 0000000000000018 I 22 12 8 [14] .modinfo PROGBITS 0000000000000000 00000560 00000000000000ec 0000000000000000 A 0 0 8 [15] .data PROGBITS 0000000000000000 00000660 0000000000000020 0000000000000000 WA 0 0 32 [16] .rela.data RELA 0000000000000000 000018d8 0000000000000030 0000000000000018 I 22 15 8 [17] .gnu.linkonce.thi PROGBITS 0000000000000000 00000680 0000000000000340 0000000000000000 WA 0 0 64 [18] .rela.gnu.linkonc RELA 0000000000000000 00001908 0000000000000030 0000000000000018 I 22 17 8 [19] .bss NOBITS 0000000000000000 000009c0 0000000000000014 0000000000000000 WA 0 0 8 [20] .comment PROGBITS 0000000000000000 000009c0 0000000000000056 0000000000000001 MS 0 0 1 [21] .note.GNU-stack PROGBITS 0000000000000000 00000a16 0000000000000000 0000000000000000 0 0 1 [22] .symtab SYMTAB 0000000000000000 00000a18 0000000000000588 0000000000000018 23 39 8 [23] .strtab STRTAB 0000000000000000 00000fa0 0000000000000271 0000000000000000 0 0 1 [24] .shstrtab STRTAB 0000000000000000 00001938 00000000000000e7 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), l (large), p (processor specific) ``` `.text` 除存的是 program 的 machine code 現在來反組譯查看 `.text` 的內容 ```shell= objdump -d -j .text fibdrv.ko fibdrv.ko: 檔案格式 elf64-x86-64 Disassembly of section .text: 0000000000000000 <fib_write>: 0: e8 00 00 00 00 callq 5 <fib_write+0x5> 5: 55 push %rbp 6: b8 01 00 00 00 mov $0x1,%eax b: 48 89 e5 mov %rsp,%rbp e: 5d pop %rbp f: c3 retq 0000000000000010 <fib_device_lseek>: ... 0000000000000060 <fib_release>: ... 0000000000000080 <fib_open>: ... 00000000000000b0 <fib_read>: ... ``` 查看`.bss`的内容 ```shell= objdump -d -j .bss fibdrv.ko fibdrv.ko: 檔案格式 elf64-x86-64 Disassembly of section .bss: 0000000000000000 <fib_class>: ... 0000000000000008 <fib_cdev>: ... 0000000000000010 <fib_dev>: 10: 00 00 00 00 .... ``` `.bss` 保存的是未初始化的 global variable 查看`.data`的内容 ```shell= objdump -d -j .data fibdrv.ko fibdrv.ko: 檔案格式 elf64-x86-64 Disassembly of section .data: 0000000000000000 <fib_mutex>: ... ``` `.data` 保存的是初始化的 global variable 查看 `.modinfo` 內容 ```shell= objdump -d -j .modinfo fibdrv.ko fibdrv.ko: 檔案格式 elf64-x86-64 Disassembly of section .modinfo: 0000000000000000 <__UNIQUE_ID_version26>: 0: 76 65 72 73 69 6f 6e 3d 30 2e 31 00 version=0.1. 000000000000000c <__UNIQUE_ID_description25>: c: 64 65 73 63 72 69 70 74 69 6f 6e 3d 46 69 62 6f description=Fibo 1c: 6e 61 63 63 69 20 65 6e 67 69 6e 65 20 64 72 69 nacci engine dri 2c: 76 65 72 00 ver. 0000000000000030 <__UNIQUE_ID_author24>: 30: 61 75 74 68 6f 72 3d 4e 61 74 69 6f 6e 61 6c 20 author=National 40: 43 68 65 6e 67 20 4b 75 6e 67 20 55 6e 69 76 65 Cheng Kung Unive 50: 72 73 69 74 79 2c 20 54 61 69 77 61 6e 00 rsity, Taiwan. 000000000000005e <__UNIQUE_ID_license23>: 5e: 6c 69 63 65 6e 73 65 3d 44 75 61 6c 20 4d 49 54 license=Dual MIT 6e: 2f 47 50 4c 00 00 00 00 00 00 /GPL...... 0000000000000078 <__UNIQUE_ID_srcversion16>: 78: 73 72 63 76 65 72 73 69 6f 6e 3d 32 34 44 43 35 srcversion=24DC5 88: 46 42 37 45 37 36 30 38 41 46 31 36 42 30 43 43 FB7E7608AF16B0CC 98: 31 46 00 00 00 00 00 00 1F...... 00000000000000a0 <__module_depends>: a0: 64 65 70 65 6e 64 73 3d 00 depends=. 00000000000000a9 <__UNIQUE_ID_retpoline15>: a9: 72 65 74 70 6f 6c 69 6e 65 3d 59 00 retpoline=Y. 00000000000000b5 <__UNIQUE_ID_name14>: b5: 6e 61 6d 65 3d 66 69 62 64 72 76 00 name=fibdrv. 00000000000000c1 <__UNIQUE_ID_vermagic13>: c1: 76 65 72 6d 61 67 69 63 3d 34 2e 31 38 2e 30 2d vermagic=4.18.0- d1: 31 36 2d 67 65 6e 65 72 69 63 20 53 4d 50 20 6d 16-generic SMP m e1: 6f 64 5f 75 6e 6c 6f 61 64 20 00 od_unload . ``` 可見 `MODULE_XXX` 會將資訊放入 `.modinfo` 中