# RPI4 MAC 模組無法載入問題


## 問題描述

我在 RPI4 使用 FreeBSD 15.0 的時候，編譯 MAC 模組的時候，開機的時候並不會自動載入。

如下我在自己的 mac module 目錄使用 Makefile 編譯：

```sh
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`](https://github.com/freebsd/freebsd-src/blob/main/sys/conf/kmod.mk) 的 Makefile 編譯。

然後我們看一下 `sudo make all install` 編譯的流程。

```sh
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
```

可以認真看一下上面的輸出：

1. `vnode_if.src` 是一個長得像清單的原始定義檔，裡面記錄了各種檔案操作（如 open, read, write）
   1. 請 `awk` 讀取 `vnode_if.src` 的邏輯，並按照模板 自動生出 `vnode_if.h` 這個 C 語言標頭檔
2. "opt_global.h" 是核心的編譯選項
3. 將 `.c` 編譯成 `.o`
   1. `-fPIC`：aarch64 被強制使用的位置無關代碼（Position Independent Code）參數
   2. `-D_KERNEL` / `-DKLD_MODULE`：定義這是要跑在核心空間的載入模組
   3. `-mbranch-protection=standard`：aarch64 特有的分支保護機制（防範 BTI/PAC 攻擊）
4. `ld -r -o mac_casper.kld` 將多個 .o 檔案變成 .kld 檔案
5. `:> export_syms`: 用來建立一個空的 export_syms 檔案，這個檔案是用來存放你想要「公開」給其他核心模組使用的函數清單
6. `awk -f ...kmod_syms.awk mac_casper.kld export_syms | xargs ... objcopy`: 掃描你的 mac_casper.kld 檔案，並對照 export_syms，`objcopy` 接手 `awk` 傳過來的清單，把這些內部符號通通變成「局部符號」（Local Symbols），讓外面的人看不到
7. `ld -m aarch64elf -Bshareable -znotext ...`: 這是產生最終檔案 `.ko` 的步驟
   1. `-Bshareable`: 將模組封裝成「共享物件」（Shared Object）格式
   2. `-znotext`: 允許在唯讀段（text）中存在重定位資訊，這能縮小檔案體積
   3. `--no-relax`: ，用來關閉連結器的指令優化（Relaxation），避免引發 VNET 等核心功能的 Bug
8. `ld: warning: -z norelro ignored`: 代表連結器不理會「關閉唯讀重定位（norelro）」的要求。這通常是因為 aarch64 的連結器有自己的安全邏輯，這不影響功能，但顯示了系統正在「強迫」執行某些安全規範
9. `objcopy --strip-debug mac_casper.ko`: 把檔案中巨大的「偵錯資訊（Debug Symbols）」通通切掉
10. `install ...` 安裝到 `/boot/modules/` 底下
11. `kldxref /boot/modules`: 系統會掃描該目錄下所有的 `.ko` 檔案，提取它們的身分資訊，並將其彙整成一個名為 `linker.hints` 的二進位索引檔
    1. 當 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 格式的奇妙之旅](https://bbs.kanxue.com/thread-291050.htm)

## 解法

先跑 `make all` 在跑 `sudo make install`。

```sh
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
```

可以看到他們的差別是

1. 失敗版，多了 `-d`，然後沒有 `-zbti-report=error`
2. 成功版本，則是相反

所以 `-d` 是問題，"Force allocation of common symbols" (強制分配公共符號)。簡單來說，這個選項的存在是因為早期連結器在處理「未初始化全域變數」（Common Symbols）時比較懶惰，而 -d 則是強迫它立刻動手處理。

使用 -d 選項：連結器被要求「即便現在只是在做中間產物（如 .kld 檔案），也要立刻幫這些變數分配空間」。使用 -d 選項：連結器被要求「即便現在只是在做中間產物（如 .kld 檔案），也要立刻幫這些變數分配空間」。

那為什麼多了 `-d`，可以看如下：

![alt text](image.png)

```sh
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。

```sh
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
```

![alt text](image-1.png)

```sh
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。

