Linux カーネル ソースコードのマクロを展開する

やりたいこと

カーネルのソースコードを読んでいると、まあまあな頻度でマクロに遭遇する。

core_param(pause_on_oops, pause_on_oops, int, 0644);

簡単なマクロならそのまま読めるが、マクロの定義中にマクロが使われてたりする。

#define core_param(name, var, type, perm)                               \
        param_check_##type(name, &(var));                               \
        __module_param_call("", name, &param_ops_##type, &var, perm, -1, 0)

#define __module_param_call(prefix, name, ops, arg, perm, level, flags) \
        /* Default value instead of permissions? */                     \
        static const char __param_str_##name[] = prefix #name;          \
        static struct kernel_param __moduleparam_const __param_##name   \
        __used                                                          \
    __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
        = { __param_str_##name, THIS_MODULE, ops,                       \
            VERIFY_OCTAL_PERMISSIONS(perm), level, flags, { arg } }

よく訓練された人はこれくらい平気で読みそうだが、自分レベルだとこうなる。

f:id:kimulla:20200517100635p:plain

そこで、どうにかしてマクロを展開したい。通常の C 言語のプログラムであれば gcc に -E オプションをつけて実行すれば、プリプロセッサの処理結果を得られる。

www.kimullaa.com

が、カーネルはビルドフローが複雑でインクルードするファイルも多いため(そして何ならビルド中に生成したファイルが必要だったりするので) gcc のオプションを正しく指定するのが非常に難しい。

解決策

カーネルビルド時のコマンドを表示させ、得られたコマンドに -E オプションを指定する。
参考 howto get kernel preprocessor output
参考 Documentation/admin-guide/README.rst

手順

カーネルソースコードをダウンロードする。
参考 Linux rpmパッケージのコードリーディングをしたい - SIerだけど技術やりたいブログ

次に make V=1 を実行する。

]# make V=1
...
  gcc -Wp,-MD,kernel/.panic.o.d  -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/8/include -I./arch/x86/include -I./arch/x86/include/generated  -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -Werror-implicit-function-declaration -Wno-format-security -std=gnu89 -fno-PIE -DCC_HAVE_ASM_GOTO -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -funit-at-a-time -DCONFIG_AS_CFI=1 -DCONFIG_AS_CFI_SIGNAL_FRAME=1 -DCONFIG_AS_CFI_SECTIONS=1 -DCONFIG_AS_FXSAVEQ=1 -DCONFIG_AS_SSSE3=1 -DCONFIG_AS_CRC32=1 -DCONFIG_AS_AVX=1 -DCONFIG_AS_AVX2=1 -DCONFIG_AS_AVX512=1 -DCONFIG_AS_SHA1_NI=1 -DCONFIG_AS_SHA256_NI=1 -pipe -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -fno-delete-null-pointer-checks -Wno-frame-address -Wno-format-truncation -Wno-format-overflow -Wno-int-in-bool-context -O2 -Werror --param=allow-store-data-races=0 -Wframe-larger-than=2048 -fstack-protector-strong -Wno-unused-but-set-variable -Wno-unused-const-variable -fno-var-tracking-assignments -g -gdwarf-4 -pg -mfentry -DCC_USING_FENTRY -fno-inline-functions-called-once -Wdeclaration-after-statement -Wno-pointer-sign -Wno-stringop-truncation -fno-strict-overflow -fno-merge-all-constants -fmerge-constants -fno-stack-check -fconserve-stack -Werror=implicit-int -Werror=strict-prototypes -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -fmacro-prefix-map=./= -Wno-packed-not-aligned -mrecord-mcount    -DKBUILD_BASENAME='"panic"' -DKBUILD_MODNAME='"panic"' -c -o kernel/.tmp_panic.o kernel/panic.c
   ./tools/objtool/objtool orc generate  --no-fp  --retpoline "kernel/.tmp_pan
...

お目当てのソースコード(今回は kernel/panic.c)のコマンドを取り出し、-o の出力先と -E を変更して実行する。

gcc -Wp,-MD,kernel/.panic.o.d  -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/8/include -I./arch/x86/include -I./arch/x86/include/generated  -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -Werror-implicit-function-declaration -Wno-format-security -std=gnu89 -fno-PIE -DCC_HAVE_ASM_GOTO -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -funit-at-a-time -DCONFIG_AS_CFI=1 -DCONFIG_AS_CFI_SIGNAL_FRAME=1 -DCONFIG_AS_CFI_SECTIONS=1 -DCONFIG_AS_FXSAVEQ=1 -DCONFIG_AS_SSSE3=1 -DCONFIG_AS_CRC32=1 -DCONFIG_AS_AVX=1 -DCONFIG_AS_AVX2=1 -DCONFIG_AS_AVX512=1 -DCONFIG_AS_SHA1_NI=1 -DCONFIG_AS_SHA256_NI=1 -pipe -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -fno-delete-null-pointer-checks -Wno-frame-address -Wno-format-truncation -Wno-format-overflow -Wno-int-in-bool-context -O2 -Werror --param=allow-store-data-races=0 -Wframe-larger-than=2048 -fstack-protector-strong -Wno-unused-but-set-variable -Wno-unused-const-variable -fno-var-tracking-assignments -g -gdwarf-4 -pg -mfentry -DCC_USING_FENTRY -fno-inline-functions-called-once -Wdeclaration-after-statement -Wno-pointer-sign -Wno-stringop-truncation -fno-strict-overflow -fno-merge-all-constants -fmerge-constants -fno-stack-check -fconserve-stack -Werror=implicit-int -Werror=strict-prototypes -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -fmacro-prefix-map=./= -Wno-packed-not-aligned -mrecord-mcount    -DKBUILD_BASENAME='"panic"' -DKBUILD_MODNAME='"panic"' -c -o kernel/panic.i -E  kernel/panic.c

すると、次のようにマクロが展開されたソースコードが手に入る。

static inline __attribute__((unused)) __attribute__((no_instrument_function)) int __attribute__((unused)) *__check_pause_on_oops(void) { return(&(pause_on_oops)); }; static const char __param_str_pause_on_oops[] = "" "pause_on_oops"; static struct kernel_param const __param_pause_on_oops __attribute__((__used__)) __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) = { __param_str_pause_on_oops, ((struct module *)0), &param_ops_int, ((sizeof(struct { int:(-!!((0644) < 0)); })) + (sizeof(struct { int:(-!!((0644) > 0777)); })) + (sizeof(struct { int:(-!!((((0644) >> 6) & 4) < (((0644) >> 3) & 4))); })) + (sizeof(struct { int:(-!!((((0644) >> 3) & 4) < ((0644) & 4))); })) + (sizeof(struct { int:(-!!((((0644) >> 6) & 2) < (((0644) >> 3) & 2))); })) + (sizeof(struct { int:(-!!((0644) & 2)); })) + (0644)), -1, 0, { &pause_on_oops } };

最後に

複雑なマクロが展開できたところで、自分の実力じゃどうにもならないことってありますよね。