RPI4 MAC 模組無法載入問題
問題描述
我在 RPI4 使用 FreeBSD 15.0 的時候,編譯 MAC 模組的時候,開機的時候並不會自動載入。
如下我在自己的 mac module 目錄使用 Makefile 編譯:
kola@generic:~/proj/mac_casper $ ls
Makefile checker.h mac_casper.c mac_casper.ko.full test
README.md checker.o mac_casper.h mac_casper.o test_program
a.txt document mac_casper.kld machine vnode_if.h
casper_src export_syms mac_casper.ko opt_global.h vnode_if_newproto.h
checker.c label.h mac_casper.ko.debug script vnode_if_typedef.h
kola@generic:~/proj/mac_casper $ cat Makefile
.include <bsd.own.mk>
KMOD= mac_casper
SRCS= mac_casper.c
SRCS+= mac_casper.h
SRCS+= checker.c
SRCS+= checker.h
SRCS+= label.h
SRCS+= vnode_if.h
.include <bsd.kmod.mk>如上,Makefile 基本上就是使用 sys/conf/kmod.mk 的 Makefile 編譯。
然後我們看一下 sudo make all install 編譯的流程。
kola@generic:~/proj/mac_casper $ sudo make all install
machine -> /usr/src/sys/arm64/include
awk -f /usr/src/sys/tools/vnode_if.awk /usr/src/sys/kern/vnode_if.src -p
awk -f /usr/src/sys/tools/vnode_if.awk /usr/src/sys/kern/vnode_if.src -q
awk -f /usr/src/sys/tools/vnode_if.awk /usr/src/sys/kern/vnode_if.src -h
touch opt_global.h
Warning: Object directory not changed from original /home/kola/proj/mac_casper
cc -O2 -pipe -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc -include /home/kola/proj/mac_casper/opt_global.h -I. -I/usr/src/sys -I/usr/src/sys/contrib/ck/include -fno-common -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -fPIC -ffile-prefix-map=/usr/src/sys=/usr/src/sys -ffile-prefix-map=/home/kola/proj/mac_casper=/usr/obj/usr/src/arm64.aarch64/sys/modules/mac_casper -fdebug-prefix-map=./machine=/usr/src/sys/arm64/include -MD -MF.depend.mac_casper.o -MTmac_casper.o -mgeneral-regs-only -ffixed-x18 -mbranch-protection=standard -mno-outline-atomics -ffreestanding -fwrapv -fstack-protector -Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wcast-qual -Wundef -Wno-pointer-sign -D__printf__=__freebsd_kprintf__ -Wmissing-include-dirs -fdiagnostics-show-option -Wno-unknown-pragmas -Wswitch -Wno-error=tautological-compare -Wno-error=empty-body -Wno-error=parentheses-equality -Wno-error=unused-function -Wno-error=pointer-sign -Wno-error=shift-negative-value -Wno-address-of-packed-member -Wno-format-zero-length -std=gnu17 -c mac_casper.c -o mac_casper.o
cc -O2 -pipe -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc -include /home/kola/proj/mac_casper/opt_global.h -I. -I/usr/src/sys -I/usr/src/sys/contrib/ck/include -fno-common -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -fPIC -ffile-prefix-map=/usr/src/sys=/usr/src/sys -ffile-prefix-map=/home/kola/proj/mac_casper=/usr/obj/usr/src/arm64.aarch64/sys/modules/mac_casper -fdebug-prefix-map=./machine=/usr/src/sys/arm64/include -MD -MF.depend.checker.o -MTchecker.o -mgeneral-regs-only -ffixed-x18 -mbranch-protection=standard -mno-outline-atomics -ffreestanding -fwrapv -fstack-protector -Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wcast-qual -Wundef -Wno-pointer-sign -D__printf__=__freebsd_kprintf__ -Wmissing-include-dirs -fdiagnostics-show-option -Wno-unknown-pragmas -Wswitch -Wno-error=tautological-compare -Wno-error=empty-body -Wno-error=parentheses-equality -Wno-error=unused-function -Wno-error=pointer-sign -Wno-error=shift-negative-value -Wno-address-of-packed-member -Wno-format-zero-length -std=gnu17 -c checker.c -o checker.o
ld -m aarch64elf -d -warn-common --build-id=sha1 --no-relax -r -o mac_casper.kld mac_casper.o checker.o
:> export_syms
awk -f /usr/src/sys/conf/kmod_syms.awk mac_casper.kld export_syms | xargs -J % objcopy % mac_casper.kld
ld -m aarch64elf -Bshareable -znotext -znorelro -d -warn-common --build-id=sha1 --no-relax -o mac_casper.ko mac_casper.kld
ld: warning: -z norelro ignored
objcopy --strip-debug mac_casper.ko
install -T release -o root -g wheel -m 444 mac_casper.ko /boot/modules/
kldxref /boot/modules
kola@generic:~/proj/mac_casper $ ls /boot/modules/
mac_casper.ko可以認真看一下上面的輸出:
vnode_if.src是一個長得像清單的原始定義檔,裡面記錄了各種檔案操作(如 open, read, write)- 請
awk讀取vnode_if.src的邏輯,並按照模板 自動生出vnode_if.h這個 C 語言標頭檔
- 請
- “opt_global.h” 是核心的編譯選項
- 將
.c編譯成.o-fPIC:aarch64 被強制使用的位置無關代碼(Position Independent Code)參數-D_KERNEL/-DKLD_MODULE:定義這是要跑在核心空間的載入模組-mbranch-protection=standard:aarch64 特有的分支保護機制(防範 BTI/PAC 攻擊)
ld -r -o mac_casper.kld將多個 .o 檔案變成 .kld 檔案:> export_syms: 用來建立一個空的 export_syms 檔案,這個檔案是用來存放你想要「公開」給其他核心模組使用的函數清單awk -f ...kmod_syms.awk mac_casper.kld export_syms | xargs ... objcopy: 掃描你的 mac_casper.kld 檔案,並對照 export_syms,objcopy接手awk傳過來的清單,把這些內部符號通通變成「局部符號」(Local Symbols),讓外面的人看不到ld -m aarch64elf -Bshareable -znotext ...: 這是產生最終檔案.ko的步驟-Bshareable: 將模組封裝成「共享物件」(Shared Object)格式-znotext: 允許在唯讀段(text)中存在重定位資訊,這能縮小檔案體積--no-relax: ,用來關閉連結器的指令優化(Relaxation),避免引發 VNET 等核心功能的 Bug
ld: warning: -z norelro ignored: 代表連結器不理會「關閉唯讀重定位(norelro)」的要求。這通常是因為 aarch64 的連結器有自己的安全邏輯,這不影響功能,但顯示了系統正在「強迫」執行某些安全規範objcopy --strip-debug mac_casper.ko: 把檔案中巨大的「偵錯資訊(Debug Symbols)」通通切掉install ...安裝到/boot/modules/底下kldxref /boot/modules: 系統會掃描該目錄下所有的.ko檔案,提取它們的身分資訊,並將其彙整成一個名為linker.hints的二進位索引檔- 當 loader (也就是你看到的 Revision 3.0 Loader) 在開機階段要載入模組時,它不必逐一打開目錄下的幾百個檔案去檢查誰是誰。它只要讀取 linker.hints 這張「地圖」,就能立刻知道 mac_casper 躲在 mac_casper.ko 檔案裡。
所以問題就是,我在開機的時候沒辦法載入此模組,可以看到 /boot/modules 底下沒有 .hints 檔案,所以代表此 .ko 檔案怪怪的
-Bshareable
在 FreeBSD,一般的 Kernel Module 在 AMD64 是用 Relocatable Object (ET_REL),但在其他架構下,就是一般的 Shared Object (ET_DYN),像一個完整的 .so 程式庫。
可以參考 [原创]把 .so 变成 .ko:一次 ELF 格式的奇妙之旅
解法
先跑 make all 在跑 sudo make install。
kola@generic:~/proj/mac_casper $ make all
machine -> /usr/src/sys/arm64/include
awk -f /usr/src/sys/tools/vnode_if.awk /usr/src/sys/kern/vnode_if.src -p
awk -f /usr/src/sys/tools/vnode_if.awk /usr/src/sys/kern/vnode_if.src -q
awk -f /usr/src/sys/tools/vnode_if.awk /usr/src/sys/kern/vnode_if.src -h
touch opt_global.h
Warning: Object directory not changed from original /home/kola/proj/mac_casper
cc -O2 -pipe -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc -include /home/kola/proj/mac_casper/opt_global.h -I. -I/usr/src/sys -I/usr/src/sys/contrib/ck/include -fno-common -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -fPIC -ffile-prefix-map=/usr/src/sys=/usr/src/sys -ffile-prefix-map=/home/kola/proj/mac_casper=/usr/obj/usr/src/arm64.aarch64/sys/modules/mac_casper -fdebug-prefix-map=./machine=/usr/src/sys/arm64/include -MD -MF.depend.mac_casper.o -MTmac_casper.o -mgeneral-regs-only -ffixed-x18 -mbranch-protection=standard -mno-outline-atomics -ffreestanding -fwrapv -fstack-protector -Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wcast-qual -Wundef -Wno-pointer-sign -D__printf__=__freebsd_kprintf__ -Wmissing-include-dirs -fdiagnostics-show-option -Wno-unknown-pragmas -Wswitch -Wno-error=tautological-compare -Wno-error=empty-body -Wno-error=parentheses-equality -Wno-error=unused-function -Wno-error=pointer-sign -Wno-error=shift-negative-value -Wno-address-of-packed-member -Wno-format-zero-length -std=gnu17 -c mac_casper.c -o mac_casper.o
cc -O2 -pipe -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc -include /home/kola/proj/mac_casper/opt_global.h -I. -I/usr/src/sys -I/usr/src/sys/contrib/ck/include -fno-common -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -fPIC -ffile-prefix-map=/usr/src/sys=/usr/src/sys -ffile-prefix-map=/home/kola/proj/mac_casper=/usr/obj/usr/src/arm64.aarch64/sys/modules/mac_casper -fdebug-prefix-map=./machine=/usr/src/sys/arm64/include -MD -MF.depend.checker.o -MTchecker.o -mgeneral-regs-only -ffixed-x18 -mbranch-protection=standard -mno-outline-atomics -ffreestanding -fwrapv -fstack-protector -Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wcast-qual -Wundef -Wno-pointer-sign -D__printf__=__freebsd_kprintf__ -Wmissing-include-dirs -fdiagnostics-show-option -Wno-unknown-pragmas -Wswitch -Wno-error=tautological-compare -Wno-error=empty-body -Wno-error=parentheses-equality -Wno-error=unused-function -Wno-error=pointer-sign -Wno-error=shift-negative-value -Wno-address-of-packed-member -Wno-format-zero-length -std=gnu17 -c checker.c -o checker.o
ld -m aarch64elf -warn-common --build-id=sha1 --no-relax -zbti-report=error -r -o mac_casper.kld mac_casper.o checker.o
:> export_syms
awk -f /usr/src/sys/conf/kmod_syms.awk mac_casper.kld export_syms | xargs -J % objcopy % mac_casper.kld
ld -m aarch64elf -Bshareable -znotext -znorelro -warn-common --build-id=sha1 --no-relax -zbti-report=error -o mac_casper.ko mac_casper.kld
objcopy --strip-debug mac_casper.ko
kola@generic:~/proj/mac_casper $ sudo make install
install -T release -o root -g wheel -m 444 mac_casper.ko /boot/modules/
kldxref /boot/modules
kola@generic:~/proj/mac_casper $ ls /boot/modules/
linker.hints mac_casper.ko可以看到他們的差別是
- 失敗版,多了
-d,然後沒有-zbti-report=error - 成功版本,則是相反
所以 -d 是問題,“Force allocation of common symbols” (強制分配公共符號)。簡單來說,這個選項的存在是因為早期連結器在處理「未初始化全域變數」(Common Symbols)時比較懶惰,而 -d 則是強迫它立刻動手處理。
使用 -d 選項:連結器被要求「即便現在只是在做中間產物(如 .kld 檔案),也要立刻幫這些變數分配空間」。使用 -d 選項:連結器被要求「即便現在只是在做中間產物(如 .kld 檔案),也要立刻幫這些變數分配空間」。
那為什麼多了 -d,可以看如下:

kola@generic:~/proj/mac_casper $ make -V LINKER_TYPE -V LINKER_VERSION
lld
190107
kola@generic:~/proj/mac_casper $ sudo make -V LINKER_TYPE -V LINKER_VERSION
bfd
24400
kola@generic:~/proj/mac_casper $ 原來是 sudo 底下會抓到錯誤的 ld,不是用 lld,而是 bfd,到至最後的 .ko 的 .text 偏移量沒有對其 4k。
kola@generic:~/proj $ readelf -S no_root.ko | grep .text
[10] .text PROGBITS 0000000000011ac8 001ac8 004098 00 AX 0 0 4
kola@generic:~/proj $ readelf -S root.ko | grep .text
[ 9] .text PROGBITS 00000000000017f8 0017f8 004098 00 AX 0 0 4
kola@generic:~/proj/mac_casper $ which ld
/usr/bin/ld
kola@generic:~/proj/mac_casper $ sudo which ld
/usr/local/bin/ld
kola@generic:~/proj/mac_casper $ sudo ls -alh /usr/local/bin/ld
-r-xr-xr-x 4 root wheel 2.7M Apr 23 02:35 /usr/local/bin/ld
kola@generic:~/proj/mac_casper $ echo $PATH
/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/kola/bin
kola@generic:~/proj/mac_casper $ sudo env | grep PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bi因為 PATH 導致我們優先使用 local 底下的 ld。