FreeBSD loadable kernel modules
Loadable Kernel Module
実行中のカーネルに対してコードを導入する方法として一般的なLoadable Kernel Moduleは当然FreeBSDにも存在する.
FreeBSDではKLD(Dynamic Kernel Linker)と呼ばれる.
Environment
Vagrantでfreebsd/FreeBSD-11.4-STABLE
を使用している.
vagrant@freebsd
OS: FreeBSD
Kernel: amd64 FreeBSD 11.4-STABLE
Uptime: 1h 14m
Packages: 105
Shell: csh (csh)
Disk: 192G / 495G (39%)
CPU: AMD Ryzen 7 3700X 8-Core
GPU: VirtualBox Graphics Adapter
RAM: 619MiB / 4096MiB
クソデカソースコード落としたりビルドしたりするからストレージもCPUも強めに.
Get kernel source code
FreeBSDではソースコードの入手にsvn
を用いるのが一般的らしい.知らんけど.
このVMは11.4-STABLEなので
cd /usr/src
svnlite checkout https://svn.freebsd.org/base/releng/11.4 .
gitはここにある.
バージョン毎のブランチがあったりするからいい感じにアレできそう.
Hello kernel
module event handler
モジュールがカーネルにloadされる,カーネルからunloadされる度,module event handlerと呼ばれる関数が呼び出される.
全てのモジュールはmodule event handlerを実装する必要があり,それは<sys/module.h>
で定義されている.
typedef int (*modeventhand_t)(module_t, int /* modeventhand_t */, void *);
module_t
はmodule
へのポインタ.
また,modeventtype_t
は<sys/module.h>
で以下のように定義されている
typedef enum modeventtype {
const char *name; /* module name */
modeventhand_t evhand; /* event handler */
void *priv; /* extra data */
} moduledata_t;
イベントハンドラの実装はこんな感じ
static int
load(struct module *module, int cmd, void *arg)
{
int error = 0;
switch (cmd) {
case MOD_LOAD:
uprintf("Hello, world!\n");
break;
case MOD_UNLOAD:
uprintf("Good-bye, cruel world!\n");
break;
default:
error = EOPNOTSUPP;
break;
}
return error;
}
自明だけど,ロードされるとHello, world!
,アンロードされるとGood-bye, cruel world!
と出力される.
DECLARE_MODULE
KLDがkldload
によってロードされると,カーネルにリンクし,登録されるが,
これは<sys/module.h>
で定義されているDECLARE_MODULE
で簡単に実行できる.
#define MODULE_KERNEL_MAXVER (roundup(__FreeBSD_version, 100000) - 1)
#define DECLARE_MODULE_WITH_MAXVER(name, data, sub, order, maxver) \
MODULE_DEPEND(name, kernel, __FreeBSD_version, \
__FreeBSD_version, maxver); \
MODULE_METADATA(_md_##name, MDT_MODULE, &data, #name); \
SYSINIT(name##module, sub, order, module_register_init, &data); \
struct __hack
#define DECLARE_MODULE(name, data, sub, order) \
DECLARE_MODULE_WITH_MAXVER(name, data, sub, order, MODULE_KERNEL_MAXVER)
各引数(name
, data
, sub
, order
)
name
モジュール名
data
モジュール名とイベントハンドラを含むmoduledata
typedef struct moduledata {
const char *name;
modeventhand_t evhand;
void *priv;
} moduledata_t;
sub
モジュールの種類
<sys/kernel.h>
のsysinit_sub_id
のどれかを指定する.
とりあえずSI_SUB_DRIVERS
にしておく.
order
KLDによる初期化の順序
<sys/kernel.h>
のsysinit_elem-order
のどれかを指定する.
とりあえずSI_ORDER_MIDDLE
にしておくと,途中で初期化される.
somewhere in the middle
“Hello, world!”
#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernle.h>
#include <sys/systm.h>
static int
load(struct module *module, int cmd, void *arg)
{
int error = 0;
switch (cmd) {
case MOD_LOAD:
uprintf("Hello, world!\n");
break;
case MOD_UNLOAD:
uprintf("Good-bye, cruel world!\n");
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
static moduledata_t hello_mod = {
"hello",
load,
NULL
};
DECLARE_MODULE(hello, hello_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
Makefileは
KMOD= hello
SRCS= hello.c
SYSDIR= /usr/src/11/sys
.include <bsd.kmod.mk>
make
で普通にビルドできる.
$ make
machine -> /usr/src/11/sys/amd64/include
x86 -> /usr/src/11/sys/x86/include
Warning: Object directory not changed from original /home/vagrant/src/hello_kernel
cc -O2 -pipe -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc -I. -I/usr/src/11/sys -fno-common -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -MD -MF.depend.hello.o -MThello.o -mcmodel=kernel -mno-red-zone -mno-mmx -mno-sse -msoft-float -fno-asynchronous-unwind-tables -ffreestanding -fwrapv -fstack-protector -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual -Wundef -Wno-pointer-sign -D__printf__=__freebsd_kprintf__ -Wmissing-include-dirs -fdiagnostics-show-option -Wno-unknown-pragmas -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 -mno-aes -mno-avx -std=iso9899:1999 -c hello.c -o hello.o
ld -m elf_x86_64_fbsd -d -warn-common -r -d -o hello.ko hello.o
:> export_syms
awk -f /usr/src/11/sys/conf/kmod_syms.awk hello.ko export_syms | xargs -J% objcopy % hello.ko
objcopy --strip-debug hello.ko
$ l
export_syms hello.c hello.ko hello.o machine Makefile x86
load, unloadもできる
$ sudo kldload ./hello.ko
Hello, world!
$ sudo kldunload hello.ko
Good-bye, cruel world
参考文献
非常に良書だが,情報が古いので,自分でやりながら現在の環境に合わせてメモをしている.
Comments