将Linux内核模块编译为LLVM .bc bitcode

时间:2013-05-31 22:04:52

标签: makefile linux-kernel llvm clang

背景

我正在尝试编译Linux内核中的某些驱动程序:drm(drivers/gpu/drm/drm_drv.o)和radeon(drivers/gpu/drm/radeon/)gpu驱动程序。我正在使用LLVM进行静态分析(跟踪copy_to/from_user()调用中使用的参数)。

到目前为止,我可以使用Makefile编译实际的模块,如下所示:

make CC=clang CFLAGS=-emit-llvm drivers/gpu/drm/radeon/ 

但这实际上并没有发出任何llvm bitcode - 我需要.bc个文件来运行opt的传递。

我只知道如何在直接使用clang时生成.bc文件(如下所示),但不能使用Makefile ...

clang -emit-llvm hello.c -c -o hello.bc 

由于这种方法有效,我抓住了GNU make操作的详细输出,将gcc更改为clang,然后运行它来创建.bc文件,该文件也有效:

 clang -emit-llvm  [[tons of CFLAGS]]  -c -o drm_drv.bc drivers/gpu/drm/drm_drv.c 

唯一的问题是我一次只能处理内核模块中的单个C文件。这种做法也非常繁琐......

主要问题

这让我想到了我的主要问题:如何使用内核的Makefile发布llvm .bc bitcode文件?

或者,如果必须在每个文件的基础上创建.bc bitcode,那么我将如何将它们全部链接在一起,以便我可以在聚合上运行LLVM opt传递内核模块中的所有.bc文件?

2 个答案:

答案 0 :(得分:2)

从Clang中获取bitcode形式的LLVM IR的最佳方法是使用-flto命令行标志执行LTO。

如果您有多个翻译单元,可以使用llvm-lto工具将它们组合在一起以“链接”bitcode文件。通常它会生成代码,但您可以使用-save-merged-module标记删除合并的LLVM IR模块。

但这些都不是真正受支持的界面。如果这是一个非常有用的工作流程,您应该与LLVM开发人员讨论支持更类似于ld的-r的内容。

答案 1 :(得分:0)

我用clang来测试ext2模块。你想做3件事: 1)将.c转换为.bc 2)在.bc文件上运行优化器,并创建一个ext2-opt.o文件 3)从ext2-opt.o文件创建ext2-instrumented.ko文件。

与此同时,您还需要使用clang编译的linux版本。在禁用几个模块后,我可以使用clang版本3.8.1编译linux 4.17。你可以得到它here

现在,让我们转到第1步 - 如您所说,以详细模式运行Make

make V=1 M=fs/ext2

并获取Makefile吐出的所有选项。默认编译器将是gcc。用clang替换gcc,添加--emit-llvm命令,并用.bc替换.o。在我的机器上生成的compilataion命令如下所示:

cd ../../ && clang -emit-llvm -Wp,-MD,fs/ext2/.all.o.d  -nostdinc -isystem /usr/local/bin/../lib/clang/3.8.1/include -I./arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated  -Iinclude -I./arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I./include/uapi -Iinclude/generated/uapi -include ./include/linux/kconfig.h -D__KERNEL__ -Qunused-arguments -Wno-unknown-warning-option -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-declaration -Wno-format-security -std=gnu89 -no-integrated-as -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -m64 -mtune=generic -mno-red-zone -mcmodel=kernel -funit-at-a-time -DCONFIG_X86_X32_ABI -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_SHA1_NI=1 -DCONFIG_AS_SHA256_NI=1 -pipe -Wno-sign-compare -fno-asynchronous-unwind-tables -O2 -Wframe-larger-than=1024 -fno-stack-protector -Wno-unused-variable -Wno-format-invalid-specifier -Wno-gnu -Wno-asm-operand-widths -Wno-initializer-overrides -fno-builtin -Wno-tautological-compare -mno-global-merge -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -Werror=implicit-int -Werror=strict-prototypes -Werror=date-time -Wno-initializer-overrides -Wno-unused-value -Wno-format -Wno-unknown-warning-option -Wno-sign-compare -Wno-format-zero-length -Wno-uninitialized  -DMODULE  -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(all)"  -D"KBUILD_MODNAME=KBUILD_STR(ext2)" -mcmodel=kernel -c -o fs/ext2_instrumented/all.bc fs/ext2/all.c && cd -

这将为您创建一个.bc文件。请注意,我已将结果.bc代码目录更改为ext2_instrumented,即我的bc文件不是在fs / ext2中创建的,而是在另一个名为fs / ext2_instrumented的文件夹中创建的。这是我的工作文件夹。我希望这个文件夹没有任何.c文件,只有.bc文件。我需要这个,因为默认的KBuild系统在文件夹中查找.c文件。稍后会详细介绍。

步骤2:使用opt命令对生成的ext2-instrumented.bc文件运行所有优化传递,如下所示:

opt -load $(DIR)/build/FSlice.so -constprop -sccp -mergereturn -sink -licm -reg2mem all.bc -o all.inst.bc llvm-link -o all.inst2.bc $(DIR)/build/libFSlice.bc all.inst.bc clang -mcmodel = kernel -c all.inst2.bc -o all.o

这将导致.o文件,我们现在将在下一步编译成.ko文件:

第3步:

make -C /lib/modules/$(shell uname -r)/build M=$(PWD) ext2instrumented.ko

Kbuild系统很难理解,甚至更难以进行修改。我建议使用这2个文件夹hack(第一个文件夹创建.bc文件和第二个文件夹来创建.o和.ko文件)。总结一下,这是我在ext2_instrumented文件夹中生成的makefile:

DIR=/home/fslice/fslice

obj-m += ext2instrumented.o

ext2instrumented-objs := all.o

all:
        cd ../../ && clang -emit-llvm -Wp,-MD,fs/ext2/.all.o.d  -nostdinc -isystem /usr/local/bin/../lib/clang/3.8.1/include -I./arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated  -Iinclude -I./arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I./include/uapi -Iinclude/generated/uapi -include ./include/linux/kconfig.h -D__KERNEL__ -Qunused-arguments -Wno-unknown-warning-option -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-declaration -Wno-format-security -std=gnu89 -no-integrated-as -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -m64 -mtune=generic -mno-red-zone -mcmodel=kernel -funit-at-a-time -DCONFIG_X86_X32_ABI -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_SHA1_NI=1 -DCONFIG_AS_SHA256_NI=1 -pipe -Wno-sign-compare -fno-asynchronous-unwind-tables -O2 -Wframe-larger-than=1024 -fno-stack-protector -Wno-unused-variable -Wno-format-invalid-specifier -Wno-gnu -Wno-asm-operand-widths -Wno-initializer-overrides -fno-builtin -Wno-tautological-compare -mno-global-merge -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -Werror=implicit-int -Werror=strict-prototypes -Werror=date-time -Wno-initializer-overrides -Wno-unused-value -Wno-format -Wno-unknown-warning-option -Wno-sign-compare -Wno-format-zero-length -Wno-uninitialized  -DMODULE  -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(all)"  -D"KBUILD_MODNAME=KBUILD_STR(ext2)" -mcmodel=kernel -c -o fs/ext2_instrumented/all.bc fs/ext2/all.c && cd -
        opt -load $(DIR)/build/FSlice.so -constprop -sccp -mergereturn -sink -licm -reg2mem all.bc -o all.inst.bc
        llvm-link -o all.inst2.bc $(DIR)/build/libFSlice.bc all.inst.bc
        clang -mcmodel=kernel -c all.inst2.bc -o all.o
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) ext2instrumented.ko

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
        rm -rf *.bc