├── .gitignore ├── README.md ├── compile_commands.py ├── v4.16 ├── README.md └── build-linux-kernel-using-clang.patch ├── v5.12 ├── KernelBitcode.go └── README.md ├── v6.1 ├── KernelBitcode.go └── README.md └── v6.6 ├── KernelBitcode.go └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | cmake-build-debug 3 | test -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The repo includes scripts to build Linux kernel into LLVM IR bitcode. 2 | And the scripts have been tested on the target Linux kernel versions. -------------------------------------------------------------------------------- /compile_commands.py: -------------------------------------------------------------------------------- 1 | import json 2 | import subprocess 3 | from concurrent.futures import ThreadPoolExecutor 4 | from tqdm import tqdm 5 | 6 | 7 | def preprocess_command(command): 8 | modified_command = command 9 | # Modify the command as needed 10 | flag = '' 11 | flag += ' -mllvm -disable-llvm-optzns' 12 | flag += ' -fno-discard-value-names' 13 | flag += ' -emit-llvm' 14 | flag += ' -w -g -c ' 15 | modified_command = modified_command.replace(' -c ', flag) 16 | modified_command = modified_command.replace('.o ', '.bc ') 17 | modified_command = modified_command.replace(' -Os ', ' -O0 ') 18 | modified_command = modified_command.replace(' -O3 ', ' -O0 ') 19 | modified_command = modified_command.replace(' -O2 ', ' -O0 ') 20 | modified_command = modified_command.replace( 21 | ' -fno-var-tracking-assignments ', ' ') 22 | modified_command = modified_command.replace(' -fconserve-stack ', ' ') 23 | modified_command = modified_command.replace(' -march=armv8-a+crypto ', ' ') 24 | modified_command = modified_command.replace(' -mno-fp-ret-in-387 ', ' ') 25 | modified_command = modified_command.replace(' -mskip-rax-setup ', ' ') 26 | modified_command = modified_command.replace( 27 | ' -ftrivial-auto-var-init=zero ', ' ') 28 | return modified_command 29 | 30 | 31 | def execute_command(command): 32 | if not command.startswith('clang '): 33 | return 34 | modified_command = preprocess_command(command) 35 | # print(modified_command) 36 | subprocess.run(modified_command, shell=True) 37 | 38 | 39 | def execute_commands_concurrently(commands, max_workers): 40 | with ThreadPoolExecutor(max_workers=max_workers) as executor: 41 | results = list(tqdm(executor.map(execute_command, commands), 42 | total=len(commands), desc='Executing Commands')) 43 | 44 | 45 | # Read compile_commands.json file 46 | with open('compile_commands.json', 'r') as file: 47 | compile_commands = json.load(file) 48 | 49 | # Extract commands from compile_commands.json 50 | commands = [entry['command'] for entry in compile_commands] 51 | 52 | # Adjust the number of concurrent threads 53 | max_workers = 32 # Example: Set the maximum number of concurrent threads to 4 54 | 55 | # Execute the commands concurrently with a progress bar 56 | execute_commands_concurrently(commands, max_workers) 57 | -------------------------------------------------------------------------------- /v4.16/README.md: -------------------------------------------------------------------------------- 1 | [TOC] 2 | # Build Linux Kernel Using Clang and Get LLVM bitcode 3 | 4 | # Getting Started 5 | ## Prerequisites 6 | + git 7 | + llvm 7.0 8 | + clang 7.0 9 | 10 | ## Clone the Linux source tree 11 | ```shell 12 | git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git 13 | cd linux-stable 14 | git reset --hard v4.16 15 | ``` 16 | > After linux kernel v4.16, it needs compiler support asm-goto and clang do not. 17 | 18 | ## Configure the kernel 19 | O0 can not be used. 20 | 21 | ## Apply the patch 22 | ```shell 23 | git apply path-of-patch/build-linux-kernel-using-clang.patch 24 | ``` 25 | + ignore the file generated by clang 26 | ```diff 27 | diff --git a/.gitignore b/.gitignore 28 | index 1be78fd8163b..6b4d8dec8e97 100644 29 | --- a/.gitignore 30 | +++ b/.gitignore 31 | @@ -33,6 +33,9 @@ 32 | *.order 33 | *.patch 34 | *.s 35 | +*.bc 36 | +*.ll 37 | +*.i 38 | *.so 39 | *.so.dbg 40 | *.su 41 | ``` 42 | + change gcc to clang in makefile 43 | ```diff 44 | diff --git a/Makefile b/Makefile 45 | index 363dd096e46e..ca51c9f7747e 100644 46 | --- a/Makefile 47 | +++ b/Makefile 48 | @@ -359,8 +359,8 @@ HOST_LFS_CFLAGS := $(shell getconf LFS_CFLAGS) 49 | HOST_LFS_LDFLAGS := $(shell getconf LFS_LDFLAGS) 50 | HOST_LFS_LIBS := $(shell getconf LFS_LIBS) 51 | 52 | -HOSTCC = gcc 53 | -HOSTCXX = g++ 54 | +HOSTCC = clang 55 | +HOSTCXX = clang 56 | HOSTCFLAGS := -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 \ 57 | -fomit-frame-pointer -std=gnu89 $(HOST_LFS_CFLAGS) 58 | HOSTCXXFLAGS := -O2 $(HOST_LFS_CFLAGS) 59 | @@ -370,7 +370,7 @@ HOST_LOADLIBES := $(HOST_LFS_LIBS) 60 | # Make variables (CC, etc...) 61 | AS = $(CROSS_COMPILE)as 62 | LD = $(CROSS_COMPILE)ld 63 | -CC = $(CROSS_COMPILE)gcc 64 | +CC = $(CROSS_COMPILE)clang 65 | CPP = $(CC) -E 66 | AR = $(CROSS_COMPILE)ar 67 | NM = $(CROSS_COMPILE)nm 68 | ``` 69 | + get the llvm bit code 70 | ```diff 71 | diff --git a/scripts/Makefile.build b/scripts/Makefile.build 72 | index 4f2b25d43ec9..ea1709d89144 100644 73 | --- a/scripts/Makefile.build 74 | +++ b/scripts/Makefile.build 75 | ``` 76 | > add target for built-in 77 | ```diff 78 | @@ -78,13 +78,13 @@ endif 79 | 80 | ifneq ($(strip $(obj-y) $(need-builtin)),) 81 | builtin-target := $(obj)/built-in.o 82 | +builtin-target_bc := $(obj)/built-in.bc 83 | endif 84 | 85 | modorder-target := $(obj)/modules.order 86 | 87 | # We keep a list of all modules in $(MODVERDIR) 88 | - 89 | -__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \ 90 | +__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(builtin-target_bc) $(lib-target) $(extra-y)) \ 91 | $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \ 92 | $(subdir-ym) $(always) 93 | @: 94 | ``` 95 | > add `-save-temps=obj` to get every single llvm bitcode 96 | > add `-fembed-bitcode` to get the llvm bitcode after optimization 97 | > because some files can not do this so disable in those files 98 | ```diff 99 | @@ -176,7 +176,7 @@ $(obj)/%.symtypes : $(src)/%.c FORCE 100 | # LLVM assembly 101 | # Generate .ll files from .c 102 | quiet_cmd_cc_ll_c = CC $(quiet_modtag) $@ 103 | - cmd_cc_ll_c = $(CC) $(c_flags) -emit-llvm -S -o $@ $< 104 | + cmd_cc_ll_c = $(CC) $(c_flags) -fembed-bitcode -save-temps=obj -S -o $@ $< 105 | 106 | $(obj)/%.ll: $(src)/%.c FORCE 107 | $(call if_changed_dep,cc_ll_c) 108 | @@ -188,7 +188,12 @@ $(obj)/%.ll: $(src)/%.c FORCE 109 | quiet_cmd_cc_o_c = CC $(quiet_modtag) $@ 110 | 111 | ifndef CONFIG_MODVERSIONS 112 | -cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< 113 | +cmd_cc_o_c = \ 114 | +if [[ $(@) != "scripts/mod/empty.o" ]] && [[ ! $(@) =~ "arch/x86/boot"* ]] && [[ ! $(@) =~ "arch/x86/realmode/rm"* ]] ; then \ 115 | + $(CC) $(c_flags) -fembed-bitcode -save-temps=obj -o $(patsubst %.bc, %.o, $(@)) -c $<; \ 116 | +else \ 117 | + $(CC) $(c_flags) -c -o $@ $<; \ 118 | +fi 119 | 120 | else 121 | # When module versioning is enabled the following steps are executed: 122 | ``` 123 | > add rule from c to bc and from s to bc 124 | ```diff 125 | @@ -324,6 +329,9 @@ $(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE 126 | $(call cmd,force_checksrc) 127 | $(call if_changed_rule,cc_o_c) 128 | 129 | +$(obj)/%.bc: $(src)/%.c 130 | + $(call if_changed_rule,cc_o_c) 131 | + 132 | # Single-part modules are special since we need to mark them in $(MODVERDIR) 133 | 134 | $(single-used-m): $(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE 135 | @@ -390,6 +398,13 @@ $(obj)/%.s: $(src)/%.S FORCE 136 | 137 | quiet_cmd_as_o_S = AS $(quiet_modtag) $@ 138 | 139 | +quiet_cmd_as_bc_S = $(CC) -fembed-bitcode -save-temps=obj $(quiet_modtag) $@ 140 | +cmd_as_bc_S = $(CC) $(a_flags) -fembed-bitcode -save-temps=obj -c -o $@ $< 141 | +asm_ign := "" 142 | +$(obj)/%.bc: $(src)/%.S FORCE 143 | + $(eval asm_ign += $@) 144 | + $(call if_changed_dep,as_bc_S) 145 | + 146 | ifndef CONFIG_MODVERSIONS 147 | cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< 148 | ``` 149 | > add rule for link llvm bitcode 150 | ```diff 151 | @@ -474,9 +489,23 @@ cmd_link_o_target = $(if $(strip $(obj-y)),\ 152 | $(cmd_secanalysis),\ 153 | $(cmd_make_empty_builtin) $@) 154 | 155 | +tmp = $(patsubst %.o, %.bc, $(obj-y)) 156 | +bc-y = $(filter-out $(asm_ign), $(tmp)) 157 | +ifneq ($(bc-y),"") 158 | + cmd_link_bc_target = $(if $(strip $(bc-y)),\ 159 | + llvm-link -o $@ $(filter $(bc-y), $^) \ 160 | + $(cmd_secanalysis),\ 161 | + echo "" > $@) 162 | +else 163 | + cmd_link_bc_target = echo "" > $@ 164 | +endif 165 | + 166 | $(builtin-target): $(obj-y) FORCE 167 | $(call if_changed,link_o_target) 168 | 169 | +$(builtin-target_bc): $(bc-y) FORCE 170 | + $(call if_changed,link_bc_target) 171 | + 172 | targets += $(builtin-target) 173 | endif # builtin-target 174 | ``` 175 | > deal with some special case 176 | > when meet some files that can not be built, use this way 177 | ```diff 178 | @@ -512,6 +541,7 @@ targets += $(lib-target) 179 | 180 | dummy-object = $(obj)/.lib_exports.o 181 | ksyms-lds = $(dot-target).lds 182 | +dummy-object-bc = $(obj)/.lib_exports.o.bc 183 | ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX 184 | ref_prefix = EXTERN(_ 185 | else 186 | @@ -524,6 +554,7 @@ cmd_export_list = $(OBJDUMP) -h $< | \ 187 | rm -f $(dummy-object);\ 188 | echo | $(CC) $(a_flags) -c -o $(dummy-object) -x assembler -;\ 189 | $(LD) $(ld_flags) -r -o $@ -T $(ksyms-lds) $(dummy-object);\ 190 | + echo "" > $(patsubst %.o, %.bc, $(@));\ 191 | rm $(dummy-object) $(ksyms-lds) 192 | 193 | $(obj)/lib-ksyms.o: $(lib-target) FORCE 194 | ``` 195 | > add rule for link llvm bitcode 196 | ```diff 197 | @@ -562,8 +593,12 @@ endif 198 | quiet_cmd_link_multi-m = LD [M] $@ 199 | cmd_link_multi-m = $(cmd_link_multi-link) 200 | 201 | +quiet_cmd_link_bc_multi-y = llvm-link $@ 202 | +cmd_link_bc_multi-y = llvm-link -o $(patsubst %.o, %.bc, $@) $(patsubst %.o, %.bc, $(link_multi_deps)) 203 | + 204 | $(multi-used-y): FORCE 205 | $(call if_changed,link_multi-y) 206 | + $(call if_changed,link_bc_multi-y) 207 | $(call multi_depend, $(multi-used-y), .o, -objs -y) 208 | 209 | $(multi-used-m): FORCE 210 | ``` 211 | + in order to make kcov insert in every basic block. notice that this is only useful in clang. 212 | ```diff 213 | diff --git a/scripts/Makefile.kcov b/scripts/Makefile.kcov 214 | index 5cc72037e423..256ae15c6f03 100644 215 | --- a/scripts/Makefile.kcov 216 | +++ b/scripts/Makefile.kcov 217 | @@ -1,5 +1,6 @@ 218 | ifdef CONFIG_KCOV 219 | CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,) 220 | +CFLAGS_KCOV += $(call cc-option,-fsanitize-coverage=no-prune,) 221 | ifeq ($(CONFIG_KCOV_ENABLE_COMPARISONS),y) 222 | CFLAGS_KCOV += $(call cc-option,-fsanitize-coverage=trace-cmp,) 223 | endif 224 | ``` 225 | + in order to make `fembed-bitcode` work well, need to disable or remove kcov and kasan. 226 | ```diff 227 | diff --git a/kernel/Makefile b/kernel/Makefile 228 | index f85ae5dfa474..25c6b6d0fc8b 100644 229 | --- a/kernel/Makefile 230 | +++ b/kernel/Makefile 231 | @@ -79,7 +79,7 @@ obj-$(CONFIG_AUDITSYSCALL) += auditsc.o 232 | obj-$(CONFIG_AUDIT_WATCH) += audit_watch.o audit_fsnotify.o 233 | obj-$(CONFIG_AUDIT_TREE) += audit_tree.o 234 | obj-$(CONFIG_GCOV_KERNEL) += gcov/ 235 | -obj-$(CONFIG_KCOV) += kcov.o 236 | +# obj-$(CONFIG_KCOV) += kcov.o 237 | obj-$(CONFIG_KPROBES) += kprobes.o 238 | obj-$(CONFIG_FAIL_FUNCTION) += fail_function.o 239 | obj-$(CONFIG_KGDB) += debug/ 240 | ``` 241 | ```diff 242 | diff --git a/mm/Makefile b/mm/Makefile 243 | index e669f02c5a54..a2b925c69315 100644 244 | --- a/mm/Makefile 245 | +++ b/mm/Makefile 246 | @@ -69,7 +69,7 @@ obj-$(CONFIG_KSM) += ksm.o 247 | obj-$(CONFIG_PAGE_POISONING) += page_poison.o 248 | obj-$(CONFIG_SLAB) += slab.o 249 | obj-$(CONFIG_SLUB) += slub.o 250 | -obj-$(CONFIG_KASAN) += kasan/ 251 | +# obj-$(CONFIG_KASAN) += kasan/ 252 | obj-$(CONFIG_FAILSLAB) += failslab.o 253 | obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o 254 | obj-$(CONFIG_MEMTEST) += memtest.o 255 | ``` 256 | ## Build the kernel 257 | ```shell 258 | cd linux-stable 259 | make SHELL=/bin/bash V=1 2>&1 | tee make.log 260 | ``` 261 | ## Link the bc file 262 | ```shell 263 | llvm-link -o built-in.bc arch/x86/kernel/head64.bc arch/x86/kernel/ebda.bc arch/x86/kernel/platform-quirks.bc init/built-in.bc usr/built-in.bc arch/x86/built-in.bc kernel/built-in.bc certs/built-in.bc mm/built-in.bc fs/built-in.bc ipc/built-in.bc security/built-in.bc crypto/built-in.bc block/built-in.bc lib/built-in.bc arch/x86/lib/built-in.bc drivers/built-in.bc sound/built-in.bc firmware/built-in.bc arch/x86/pci/built-in.bc arch/x86/power/built-in.bc arch/x86/video/built-in.bc net/built-in.bc virt/built-in.bc 264 | ``` 265 | ## Get file used for analysis 266 | ``` 267 | llvm-dis built-in.bc 268 | cat `find -name "*.s"` >> built-in.s 269 | ``` 270 | 271 | # Acknowledgments 272 | + [k-miner](https://github.com/ssl-tud/k-miner) 273 | 274 | 275 | 276 | -------------------------------------------------------------------------------- /v4.16/build-linux-kernel-using-clang.patch: -------------------------------------------------------------------------------- 1 | diff --git a/.gitignore b/.gitignore 2 | index 1be78fd8163b..6b4d8dec8e97 100644 3 | --- a/.gitignore 4 | +++ b/.gitignore 5 | @@ -33,6 +33,9 @@ 6 | *.order 7 | *.patch 8 | *.s 9 | +*.bc 10 | +*.ll 11 | +*.i 12 | *.so 13 | *.so.dbg 14 | *.su 15 | diff --git a/Makefile b/Makefile 16 | index 363dd096e46e..ca51c9f7747e 100644 17 | --- a/Makefile 18 | +++ b/Makefile 19 | @@ -359,8 +359,8 @@ HOST_LFS_CFLAGS := $(shell getconf LFS_CFLAGS) 20 | HOST_LFS_LDFLAGS := $(shell getconf LFS_LDFLAGS) 21 | HOST_LFS_LIBS := $(shell getconf LFS_LIBS) 22 | 23 | -HOSTCC = gcc 24 | -HOSTCXX = g++ 25 | +HOSTCC = clang 26 | +HOSTCXX = clang 27 | HOSTCFLAGS := -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 \ 28 | -fomit-frame-pointer -std=gnu89 $(HOST_LFS_CFLAGS) 29 | HOSTCXXFLAGS := -O2 $(HOST_LFS_CFLAGS) 30 | @@ -370,7 +370,7 @@ HOST_LOADLIBES := $(HOST_LFS_LIBS) 31 | # Make variables (CC, etc...) 32 | AS = $(CROSS_COMPILE)as 33 | LD = $(CROSS_COMPILE)ld 34 | -CC = $(CROSS_COMPILE)gcc 35 | +CC = $(CROSS_COMPILE)clang 36 | CPP = $(CC) -E 37 | AR = $(CROSS_COMPILE)ar 38 | NM = $(CROSS_COMPILE)nm 39 | diff --git a/kernel/Makefile b/kernel/Makefile 40 | index f85ae5dfa474..25c6b6d0fc8b 100644 41 | --- a/kernel/Makefile 42 | +++ b/kernel/Makefile 43 | @@ -79,7 +79,7 @@ obj-$(CONFIG_AUDITSYSCALL) += auditsc.o 44 | obj-$(CONFIG_AUDIT_WATCH) += audit_watch.o audit_fsnotify.o 45 | obj-$(CONFIG_AUDIT_TREE) += audit_tree.o 46 | obj-$(CONFIG_GCOV_KERNEL) += gcov/ 47 | -obj-$(CONFIG_KCOV) += kcov.o 48 | +# obj-$(CONFIG_KCOV) += kcov.o 49 | obj-$(CONFIG_KPROBES) += kprobes.o 50 | obj-$(CONFIG_FAIL_FUNCTION) += fail_function.o 51 | obj-$(CONFIG_KGDB) += debug/ 52 | diff --git a/mm/Makefile b/mm/Makefile 53 | index e669f02c5a54..a2b925c69315 100644 54 | --- a/mm/Makefile 55 | +++ b/mm/Makefile 56 | @@ -69,7 +69,7 @@ obj-$(CONFIG_KSM) += ksm.o 57 | obj-$(CONFIG_PAGE_POISONING) += page_poison.o 58 | obj-$(CONFIG_SLAB) += slab.o 59 | obj-$(CONFIG_SLUB) += slub.o 60 | -obj-$(CONFIG_KASAN) += kasan/ 61 | +# obj-$(CONFIG_KASAN) += kasan/ 62 | obj-$(CONFIG_FAILSLAB) += failslab.o 63 | obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o 64 | obj-$(CONFIG_MEMTEST) += memtest.o 65 | diff --git a/scripts/Makefile.build b/scripts/Makefile.build 66 | index 4f2b25d43ec9..ea1709d89144 100644 67 | --- a/scripts/Makefile.build 68 | +++ b/scripts/Makefile.build 69 | @@ -78,13 +78,13 @@ endif 70 | 71 | ifneq ($(strip $(obj-y) $(need-builtin)),) 72 | builtin-target := $(obj)/built-in.o 73 | +builtin-target_bc := $(obj)/built-in.bc 74 | endif 75 | 76 | modorder-target := $(obj)/modules.order 77 | 78 | # We keep a list of all modules in $(MODVERDIR) 79 | - 80 | -__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \ 81 | +__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(builtin-target_bc) $(lib-target) $(extra-y)) \ 82 | $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \ 83 | $(subdir-ym) $(always) 84 | @: 85 | @@ -176,7 +176,7 @@ $(obj)/%.symtypes : $(src)/%.c FORCE 86 | # LLVM assembly 87 | # Generate .ll files from .c 88 | quiet_cmd_cc_ll_c = CC $(quiet_modtag) $@ 89 | - cmd_cc_ll_c = $(CC) $(c_flags) -emit-llvm -S -o $@ $< 90 | + cmd_cc_ll_c = $(CC) $(c_flags) -fembed-bitcode -save-temps=obj -S -o $@ $< 91 | 92 | $(obj)/%.ll: $(src)/%.c FORCE 93 | $(call if_changed_dep,cc_ll_c) 94 | @@ -188,7 +188,12 @@ $(obj)/%.ll: $(src)/%.c FORCE 95 | quiet_cmd_cc_o_c = CC $(quiet_modtag) $@ 96 | 97 | ifndef CONFIG_MODVERSIONS 98 | -cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< 99 | +cmd_cc_o_c = \ 100 | +if [[ $(@) != "scripts/mod/empty.o" ]] && [[ ! $(@) =~ "arch/x86/boot"* ]] && [[ ! $(@) =~ "arch/x86/realmode/rm"* ]] ; then \ 101 | + $(CC) $(c_flags) -fembed-bitcode -save-temps=obj -o $(patsubst %.bc, %.o, $(@)) -c $<; \ 102 | +else \ 103 | + $(CC) $(c_flags) -c -o $@ $<; \ 104 | +fi 105 | 106 | else 107 | # When module versioning is enabled the following steps are executed: 108 | @@ -324,6 +329,9 @@ $(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE 109 | $(call cmd,force_checksrc) 110 | $(call if_changed_rule,cc_o_c) 111 | 112 | +$(obj)/%.bc: $(src)/%.c 113 | + $(call if_changed_rule,cc_o_c) 114 | + 115 | # Single-part modules are special since we need to mark them in $(MODVERDIR) 116 | 117 | $(single-used-m): $(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE 118 | @@ -390,6 +398,13 @@ $(obj)/%.s: $(src)/%.S FORCE 119 | 120 | quiet_cmd_as_o_S = AS $(quiet_modtag) $@ 121 | 122 | +quiet_cmd_as_bc_S = $(CC) -fembed-bitcode -save-temps=obj $(quiet_modtag) $@ 123 | +cmd_as_bc_S = $(CC) $(a_flags) -fembed-bitcode -save-temps=obj -c -o $@ $< 124 | +asm_ign := "" 125 | +$(obj)/%.bc: $(src)/%.S FORCE 126 | + $(eval asm_ign += $@) 127 | + $(call if_changed_dep,as_bc_S) 128 | + 129 | ifndef CONFIG_MODVERSIONS 130 | cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< 131 | 132 | @@ -474,9 +489,23 @@ cmd_link_o_target = $(if $(strip $(obj-y)),\ 133 | $(cmd_secanalysis),\ 134 | $(cmd_make_empty_builtin) $@) 135 | 136 | +tmp = $(patsubst %.o, %.bc, $(obj-y)) 137 | +bc-y = $(filter-out $(asm_ign), $(tmp)) 138 | +ifneq ($(bc-y),"") 139 | + cmd_link_bc_target = $(if $(strip $(bc-y)),\ 140 | + llvm-link -o $@ $(filter $(bc-y), $^) \ 141 | + $(cmd_secanalysis),\ 142 | + echo "" > $@) 143 | +else 144 | + cmd_link_bc_target = echo "" > $@ 145 | +endif 146 | + 147 | $(builtin-target): $(obj-y) FORCE 148 | $(call if_changed,link_o_target) 149 | 150 | +$(builtin-target_bc): $(bc-y) FORCE 151 | + $(call if_changed,link_bc_target) 152 | + 153 | targets += $(builtin-target) 154 | endif # builtin-target 155 | 156 | @@ -512,6 +541,7 @@ targets += $(lib-target) 157 | 158 | dummy-object = $(obj)/.lib_exports.o 159 | ksyms-lds = $(dot-target).lds 160 | +dummy-object-bc = $(obj)/.lib_exports.o.bc 161 | ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX 162 | ref_prefix = EXTERN(_ 163 | else 164 | @@ -524,6 +554,7 @@ cmd_export_list = $(OBJDUMP) -h $< | \ 165 | rm -f $(dummy-object);\ 166 | echo | $(CC) $(a_flags) -c -o $(dummy-object) -x assembler -;\ 167 | $(LD) $(ld_flags) -r -o $@ -T $(ksyms-lds) $(dummy-object);\ 168 | + echo "" > $(patsubst %.o, %.bc, $(@));\ 169 | rm $(dummy-object) $(ksyms-lds) 170 | 171 | $(obj)/lib-ksyms.o: $(lib-target) FORCE 172 | @@ -562,8 +593,12 @@ endif 173 | quiet_cmd_link_multi-m = LD [M] $@ 174 | cmd_link_multi-m = $(cmd_link_multi-link) 175 | 176 | +quiet_cmd_link_bc_multi-y = llvm-link $@ 177 | +cmd_link_bc_multi-y = llvm-link -o $(patsubst %.o, %.bc, $@) $(patsubst %.o, %.bc, $(link_multi_deps)) 178 | + 179 | $(multi-used-y): FORCE 180 | $(call if_changed,link_multi-y) 181 | + $(call if_changed,link_bc_multi-y) 182 | $(call multi_depend, $(multi-used-y), .o, -objs -y) 183 | 184 | $(multi-used-m): FORCE 185 | diff --git a/scripts/Makefile.kcov b/scripts/Makefile.kcov 186 | index 5cc72037e423..256ae15c6f03 100644 187 | --- a/scripts/Makefile.kcov 188 | +++ b/scripts/Makefile.kcov 189 | @@ -1,5 +1,6 @@ 190 | ifdef CONFIG_KCOV 191 | CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,) 192 | +CFLAGS_KCOV += $(call cc-option,-fsanitize-coverage=no-prune,) 193 | ifeq ($(CONFIG_KCOV_ENABLE_COMPARISONS),y) 194 | CFLAGS_KCOV += $(call cc-option,-fsanitize-coverage=trace-cmp,) 195 | endif 196 | -------------------------------------------------------------------------------- /v5.12/KernelBitcode.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | // Build one module or whole kernel, e.g., module, kernel 14 | var cmd = flag.String("cmd", "kernel", "Build one module or whole kernel, e.g., module, kernel") 15 | 16 | // The path of kernel, e.g., linux 17 | var path = flag.String("path", ".", "the path of kernel") 18 | 19 | // IsSaveTemps : use -save-temps or emit-llvm to generate LLVM Bitcode 20 | // two kinds of two to generate bitcode 21 | var IsSaveTemps = flag.Bool("isSaveTemp", false, "use -save-temps or -emit-llvm") 22 | 23 | // tools used in build kernel 24 | var CC = flag.String("CC", "clang", "Name of CC") 25 | var LD = flag.String("LD", "llvm-link", "Name of LD") 26 | var AR = flag.String("AR", "llvm-ar", "Name of AR") 27 | var LLD = flag.String("LLD", "ld.lld", "Name of LD") 28 | var OBJCOPY = flag.String("OBJCOPY", "llvm-objcopy", "Name of OBJCOPY") 29 | var STRIP = flag.String("STRIP", "llvm-strip", "Name of STRIP") 30 | 31 | // ToolChain of clang and llvm-link 32 | var ToolChain = flag.String("toolchain", "", "Path of clang and llvm-link") 33 | 34 | var FlagCC = FlagAll + FlagCCNoNumber 35 | 36 | const ( 37 | PrefixCmd = "cmd_" 38 | SuffixCmd = ".cmd" 39 | SuffixCC = ".o.cmd" 40 | 41 | SuffixLD = ".a.cmd" 42 | SuffixLTO = ".lto.o.cmd" 43 | SuffixKO = ".ko.cmd" 44 | NameScript = "build.sh" 45 | 46 | NameClang = "clang" 47 | 48 | // FlagAll : -w disable warning 49 | // FlagAll : -g debug info 50 | FlagAll = " -w -g" 51 | 52 | // FlagCCNoOptzns disable all optimization 53 | FlagCCNoOptzns = " -mllvm -disable-llvm-optzns" 54 | 55 | // FlagCCNoNumber add label to basic blocks and variables 56 | FlagCCNoNumber = " -fno-discard-value-names" 57 | 58 | NameLD = "llvm-link" 59 | FlagLD = " -v " 60 | FlagOutLD = " -o " 61 | 62 | CmdLinkVmlinux = " -v -o built-in.bc" 63 | 64 | // CmdTools skip the cmd with CmdTools 65 | CmdTools = "BUILD_STR(s)=$(pound)s" 66 | ) 67 | 68 | var bitcodes map[string]bool 69 | var linkedBitcodes map[string]bool 70 | var builtinModules map[string]bool 71 | 72 | // get cmd from *.cmd file 73 | func getCmd(cmdFilePath string) string { 74 | res := "" 75 | if _, err := os.Stat(cmdFilePath); os.IsNotExist(err) { 76 | fmt.Printf(cmdFilePath + " does not exist\n") 77 | } else { 78 | file, err := os.Open(cmdFilePath) 79 | if err != nil { 80 | log.Fatal(err) 81 | } 82 | defer func(file *os.File) { 83 | err := file.Close() 84 | if err != nil { 85 | 86 | } 87 | }(file) 88 | 89 | scanner := bufio.NewScanner(file) 90 | scanner.Split(bufio.ScanLines) 91 | 92 | var text []string 93 | for scanner.Scan() { 94 | text = append(text, scanner.Text()) 95 | } 96 | for _, eachLine := range text { 97 | if strings.HasPrefix(eachLine, PrefixCmd) { 98 | i := strings.Index(eachLine, ":=") 99 | if i > -1 { 100 | cmd := eachLine[i+3:] 101 | res = cmd 102 | break 103 | } else { 104 | fmt.Println("Cmd Index not found") 105 | fmt.Println(eachLine) 106 | } 107 | } 108 | } 109 | if err := scanner.Err(); err != nil { 110 | log.Fatal(err) 111 | } 112 | } 113 | res += "\n" 114 | res = res[strings.Index(res, ""):] 115 | return res 116 | } 117 | 118 | // for CC cmd, use " -save-temps=obj" or " -emit-llvm" to generate llvm bitcode 119 | func handleCC(cmd string) string { 120 | res := "" 121 | if i := strings.Index(cmd, " -c "); i > -1 { 122 | 123 | if j := strings.Index(cmd, CmdTools); j > -1 { 124 | return res 125 | } 126 | 127 | res += cmd[:i] 128 | res += FlagCC 129 | if *IsSaveTemps { 130 | res += " -save-temps=obj" 131 | } else { 132 | res += " -emit-llvm" 133 | } 134 | res += cmd[i:] 135 | 136 | // replace .o to .bc 137 | if *IsSaveTemps { 138 | 139 | } else { 140 | res = strings.Replace(res, ".o ", ".bc ", -1) 141 | } 142 | 143 | // for multiply ";" 144 | if strings.Count(res, " ; ") == 1 { 145 | i := strings.Index(res, ";") 146 | res = res[:i] + "\n" 147 | } 148 | res = strings.TrimSpace(res) + "\n" 149 | 150 | // can not compile .S, so just make a empty bitcode file 151 | if strings.HasSuffix(res, ".S\n") { 152 | s1 := strings.Split(res, " ") 153 | s2 := s1[len(s1)-2] 154 | s4 := strings.Replace(s2, ".o ", ".bc ", -1) 155 | res = "echo \"\" > " + s4 + "\n" 156 | } 157 | } else { 158 | fmt.Println("CC Index not found") 159 | fmt.Println(cmd) 160 | } 161 | res = " " + res 162 | // use -O0 install of other optimization 163 | res = strings.Replace(res, " "+*CC+" ", " "+filepath.Join(*ToolChain, NameClang)+" ", -1) 164 | res = strings.Replace(res, " -Os ", " -O0 ", -1) 165 | res = strings.Replace(res, " -O3 ", " -O0 ", -1) 166 | res = strings.Replace(res, " -O2 ", " -O0 ", -1) 167 | res = strings.Replace(res, " -fno-var-tracking-assignments ", " ", -1) 168 | res = strings.Replace(res, " -fconserve-stack ", " ", -1) 169 | res = strings.Replace(res, " -march=armv8-a+crypto ", " ", -1) 170 | res = strings.Replace(res, " -mno-fp-ret-in-387 ", " ", -1) 171 | res = strings.Replace(res, " -mskip-rax-setup ", " ", -1) 172 | res = strings.Replace(res, " -ftrivial-auto-var-init=zero ", " ", -1) 173 | 174 | return res 175 | } 176 | 177 | // handler LD cmd in *.o.cmd 178 | // @file_name in *.o.cmd includes the related file 179 | // need to get the name from that file 180 | func handleSuffixCCWithLD(cmd string, path string) string { 181 | res := "" 182 | if strings.Index(cmd, "@") > -1 { 183 | fileName := cmd[strings.Index(cmd, "@")+1 : len(cmd)-1] 184 | filePath := filepath.Join(path, fileName) 185 | file, err := os.Open(filePath) 186 | if err != nil { 187 | log.Println("handleSuffixCCWithLD file error: ") 188 | log.Fatal(err) 189 | } 190 | defer func(file *os.File) { 191 | err := file.Close() 192 | if err != nil { 193 | 194 | } 195 | }(file) 196 | 197 | scanner := bufio.NewScanner(file) 198 | scanner.Split(bufio.ScanLines) 199 | 200 | var text []string 201 | for scanner.Scan() { 202 | text = append(text, scanner.Text()) 203 | } 204 | 205 | if err := scanner.Err(); err != nil { 206 | log.Fatal(err) 207 | } 208 | 209 | res += filepath.Join(*ToolChain, NameLD) 210 | res += FlagLD 211 | res += FlagOutLD 212 | res += cmd[strings.Index(cmd, FlagOutLD)+len(FlagOutLD) : strings.Index(cmd, "@")] 213 | 214 | for _, s := range text { 215 | res += s + " " 216 | } 217 | 218 | res = strings.Replace(res, ".o ", ".bc ", -1) 219 | res += "\n" 220 | 221 | } else if strings.HasPrefix(cmd, *LLD) { 222 | res += filepath.Join(*ToolChain, NameLD) 223 | res += FlagLD 224 | res += FlagOutLD 225 | 226 | cmd = cmd[:len(cmd)-1] 227 | s1 := strings.Split(cmd, " ") 228 | obj := "" 229 | for _, s := range s1 { 230 | if strings.HasSuffix(s, ".o") { 231 | obj = " " + strings.Replace(s, ".o", ".bc", -1) + obj 232 | } 233 | } 234 | res += obj 235 | res += "\n" 236 | } else { 237 | fmt.Println("handleSuffixCCWithLD cmd error: " + cmd) 238 | } 239 | return res 240 | } 241 | 242 | // handle llvm-objcopy cmd 243 | func handleOBJCOPY(cmd string) string { 244 | res := filepath.Join(*ToolChain, NameLD) + FlagLD + FlagOutLD 245 | cmd = cmd[:len(cmd)-1] 246 | s1 := strings.Split(cmd, " ") 247 | obj := "" 248 | for _, s := range s1 { 249 | if strings.HasSuffix(s, ".o") { 250 | obj = " " + strings.Replace(s, ".o", ".bc", -1) + obj 251 | } 252 | } 253 | res += obj 254 | res += "\n" 255 | return res 256 | } 257 | 258 | // handle llvm-strip cmd 259 | func handleSTRIP(cmd string) string { 260 | res := filepath.Join(*ToolChain, NameLD) + FlagLD + FlagOutLD 261 | s1 := strings.Split(cmd, ";") 262 | cmd = s1[0] 263 | s1 = strings.Split(cmd, " ") 264 | for _, s := range s1 { 265 | if strings.HasSuffix(s, ".o") { 266 | res = res + " " + strings.Replace(s, ".o", ".bc", -1) 267 | } 268 | } 269 | res += "\n" 270 | return res 271 | } 272 | 273 | // use llvm-link to link all bitcode 274 | func handleLD(cmd string) string { 275 | replace := func(cmd string, i int, length int) string { 276 | res := "" 277 | cmd = cmd[i+length:] 278 | if strings.Count(cmd, ".") > 1 { 279 | res += filepath.Join(*ToolChain, NameLD) 280 | res += FlagLD 281 | res += FlagOutLD 282 | res += cmd 283 | if strings.Contains(res, "drivers/of/unittest-data/built-in.o") { 284 | res = "" 285 | } 286 | res = strings.Replace(res, ".o", ".bc", -1) 287 | } else { 288 | res = "echo \"\" > " + cmd 289 | res = strings.Replace(res, ".o", ".bc ", -1) 290 | } 291 | res = strings.Replace(res, ".a ", ".bc ", -1) 292 | res = strings.Replace(res, ".a\n", ".bc\n", -1) 293 | // for this drivers/misc/lkdtm/rodata.bc 294 | res = strings.Replace(res, "rodata_objcopy.bc", "rodata.bc", -1) 295 | res = strings.Replace(res, " drivers/of/unittest-data/built-in.bc", "", -1) 296 | 297 | // for multiply cmd or ";" pick the first one 298 | if strings.Count(res, ";") > 1 { 299 | i := strings.Index(res, ";") 300 | res = res[:i] + "\n" 301 | } 302 | return res 303 | } 304 | 305 | res := "" 306 | // fmt.Println("Index: ", i) 307 | if i := strings.Index(cmd, " rcSTPD "); i > -1 { 308 | res = replace(cmd, i, len(" rcSTPD ")) 309 | } else if i := strings.Index(cmd, " cDPrST "); i > -1 { 310 | res = replace(cmd, i, len(" cDPrST ")) 311 | } else if i := strings.Index(cmd, " cDPrsT "); i > -1 { 312 | res = replace(cmd, i, len(" cDPrsT ")) 313 | } else if i := strings.Index(cmd, " rcsD "); i > -1 { 314 | res = replace(cmd, i, len(" rcsD ")) 315 | } else if i := strings.Index(cmd, *LD); i > -1 { 316 | res = replace(cmd, i, len(*LD)) 317 | } else { 318 | fmt.Println("LD Index not found") 319 | fmt.Println(cmd) 320 | } 321 | 322 | return res 323 | } 324 | 325 | // handler *.lto for external modules 326 | func handleLTO(cmd string) string { 327 | res := "" 328 | res += filepath.Join(*ToolChain, NameLD) 329 | res += FlagLD 330 | res += FlagOutLD 331 | 332 | cmd = cmd[strings.Index(cmd, FlagOutLD) : len(cmd)-1] 333 | objs := strings.Split(cmd, " ") 334 | output := false 335 | for _, obj := range objs { 336 | if obj == "-o" { 337 | output = true 338 | } else if output && obj != "" { 339 | res += strings.Replace(obj, ".o", ".bc", -1) 340 | output = false 341 | } else if strings.HasSuffix(obj, ".o") { 342 | res += " " + strings.Replace(obj, ".o", ".bc", -1) 343 | } 344 | } 345 | res += "\n" 346 | return res 347 | } 348 | 349 | // handle .ko for external modules 350 | func handleKO(cmd string) (string, string) { 351 | res := "" 352 | res += filepath.Join(*ToolChain, NameLD) 353 | res += FlagLD 354 | res += FlagOutLD 355 | 356 | // for multiply cmd or ";" pick the first one 357 | if strings.Count(cmd, ";") > 1 { 358 | i := strings.Index(cmd, ";") 359 | cmd = cmd[:i] + "\n" 360 | } 361 | 362 | cmd = cmd[strings.Index(cmd, FlagOutLD)+len(FlagOutLD):] 363 | cmd = strings.Replace(cmd, ".ko", ".ko.bc", -1) 364 | cmd = strings.Replace(cmd, ".o", ".bc", -1) 365 | 366 | moduleFile := cmd[:strings.Index(cmd, ".ko.bc")+len(".ko.bc")] 367 | res += cmd 368 | 369 | return res, moduleFile 370 | } 371 | 372 | // find all *.cmd file and handle the cmd in them 373 | func build(kernelPath string) (string, string) { 374 | cmdCC := "" 375 | cmdLDInCC := "" 376 | 377 | err := filepath.Walk(kernelPath, 378 | func(path string, info os.FileInfo, err error) error { 379 | if err != nil { 380 | return err 381 | } 382 | // handle, all *.o.cmd files. 383 | // do not include *.lto.o.cmd files 384 | if strings.HasSuffix(info.Name(), SuffixCC) && !strings.HasSuffix(info.Name(), SuffixLTO) { 385 | // get cmd from the file 386 | cmd := getCmd(path) 387 | if strings.HasPrefix(cmd, *CC) { 388 | cmd := handleCC(cmd) 389 | cmdCC += cmd 390 | } else if strings.Index(cmd, *AR) > -1 { 391 | cmd = handleLD(cmd) 392 | cmdLDInCC = cmd + cmdLDInCC 393 | } else if strings.Index(cmd, *LLD) > -1 { 394 | cmd = handleSuffixCCWithLD(cmd, kernelPath) 395 | cmdLDInCC = cmd + cmdLDInCC 396 | } else if strings.HasPrefix(cmd, *OBJCOPY) { 397 | cmd = handleOBJCOPY(cmd) 398 | cmdLDInCC = cmd + cmdLDInCC 399 | } else if strings.HasPrefix(cmd, *STRIP) { 400 | cmd = handleSTRIP(cmd) 401 | cmdLDInCC = cmd + cmdLDInCC 402 | } else { 403 | fmt.Println(*CC + " not found") 404 | fmt.Println(path) 405 | fmt.Println(cmd) 406 | } 407 | } 408 | return nil 409 | }) 410 | if err != nil { 411 | log.Println(err) 412 | } 413 | 414 | cmdLink := "" 415 | moduleFiles := "" 416 | err = filepath.Walk(kernelPath, 417 | func(path string, info os.FileInfo, err error) error { 418 | if err != nil { 419 | return err 420 | } 421 | if strings.HasSuffix(info.Name(), SuffixLD) { 422 | //for built-in module built-in.a 423 | cmd := getCmd(path) 424 | cmd = handleLD(cmd) 425 | cmdLink = cmd + cmdLink 426 | if strings.Index(cmd, FlagOutLD) > -1 { 427 | cmd = cmd[strings.Index(cmd, FlagOutLD)+len(FlagOutLD):] 428 | obj := cmd[:strings.Index(cmd, " ")] 429 | if _, ok := linkedBitcodes[obj]; ok { 430 | 431 | } else { 432 | builtinModules[obj] = true 433 | 434 | } 435 | 436 | objs := strings.Split(cmd[strings.Index(cmd, " "):len(cmd)-1], " ") 437 | for _, bc := range objs { 438 | linkedBitcodes[bc] = true 439 | } 440 | } 441 | 442 | } else if strings.HasSuffix(info.Name(), SuffixLTO) { 443 | //for external module *.lto 444 | cmd := getCmd(path) 445 | cmdLink = handleLTO(cmd) + cmdLink 446 | 447 | } else if strings.HasSuffix(info.Name(), SuffixKO) { 448 | //for external module *.ko 449 | cmd, moduleFile := handleKO(getCmd(path)) 450 | cmdLink = cmd + cmdLink 451 | moduleFiles = moduleFile + " " + moduleFiles 452 | } 453 | 454 | return nil 455 | }) 456 | 457 | if err != nil { 458 | log.Println(err) 459 | } 460 | 461 | fmt.Println("moduleFiles: ") 462 | fmt.Println(moduleFiles) 463 | 464 | var resFinal string 465 | for module, _ := range builtinModules { 466 | resFinal += " " + module 467 | } 468 | 469 | return cmdCC + cmdLDInCC + cmdLink + "\n# external modules: " + moduleFiles + "\n", resFinal 470 | } 471 | 472 | func generateScript(path string, cmd string) { 473 | res := "#!/bin/bash\n" 474 | res += cmd 475 | 476 | pathScript := filepath.Join(NameScript) 477 | _ = os.RemoveAll(pathScript) 478 | fmt.Printf("script path : bash %s\n", pathScript) 479 | f, err := os.OpenFile(pathScript, os.O_WRONLY|os.O_CREATE, 0666) 480 | if err != nil { 481 | log.Println(err) 482 | } 483 | defer func(f *os.File) { 484 | err := f.Close() 485 | if err != nil { 486 | 487 | } 488 | }(f) 489 | 490 | _, _ = f.WriteString(res) 491 | _, _ = f.WriteString("\n# path: " + path + "\n") 492 | } 493 | 494 | // collect compile cmd from *.cmd files in kernel 495 | // get cmd to generate llvm bitcode 496 | func main() { 497 | flag.Parse() 498 | *path, _ = filepath.Abs(*path) 499 | 500 | bitcodes = make(map[string]bool) 501 | linkedBitcodes = make(map[string]bool) 502 | builtinModules = make(map[string]bool) 503 | 504 | switch *cmd { 505 | case "module": 506 | { 507 | fmt.Printf("Build module\n") 508 | res, _ := build(*path) 509 | generateScript(*path, res) 510 | } 511 | case "kernel": 512 | { 513 | fmt.Printf("Build kernel and external module\n") 514 | res, res5 := build(*path) 515 | res += filepath.Join(*ToolChain, NameLD) + CmdLinkVmlinux + res5 + "\n" 516 | generateScript(*path, res) 517 | } 518 | default: 519 | fmt.Printf("cmd is invalid\n") 520 | } 521 | } 522 | -------------------------------------------------------------------------------- /v5.12/README.md: -------------------------------------------------------------------------------- 1 | # Requirement 2 | - build kernel using clang into binary successfully 3 | - golang 4 | - The related kernel module are enabled in Linux kernel configuration 5 | 6 | # Build Linux kernel into LLVM Bitcode 7 | 8 | ```shell 9 | go run path/of/v5.12/KernelBitcode.go --help 10 | ``` 11 | 12 | 13 | 1. Build Kernel with clang 14 | ```shell 15 | make LLVM=1 -j16 16 | ``` 17 | 2. Generate script to build kernel into LLVM Bitcode 18 | ```shell 19 | go run path/of/v5.12/KernelBitcode.go 20 | ``` 21 | 3. Get LLVM Bitcode 22 | ```shell 23 | bash build.sh 24 | ``` 25 | `built-in.bc` is in each directory. 26 | All external modules are in the end of build.sh. 27 | Now you can perform analysis on those LLVM bitcode files. -------------------------------------------------------------------------------- /v6.1/KernelBitcode.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | // Build one module or whole kernel, e.g., module, kernel 14 | var cmd = flag.String("cmd", "kernel", "Build one module or whole kernel, e.g., module, kernel") 15 | 16 | // The path of kernel, e.g., linux 17 | var path = flag.String("path", ".", "the path of kernel") 18 | 19 | // IsSaveTemps : use -save-temps or emit-llvm to generate LLVM Bitcode 20 | // two kinds of two to generate bitcode 21 | var IsSaveTemps = flag.Bool("isSaveTemp", false, "use -save-temps or -emit-llvm") 22 | 23 | // tools used in build kernel 24 | var CC = flag.String("CC", "clang", "Name of CC") 25 | var LD = flag.String("LD", "llvm-link", "Name of LD") 26 | var AR = flag.String("AR", "llvm-ar", "Name of AR") 27 | var LLD = flag.String("LLD", "ld.lld", "Name of LD") 28 | var OBJCOPY = flag.String("OBJCOPY", "llvm-objcopy", "Name of OBJCOPY") 29 | var STRIP = flag.String("STRIP", "llvm-strip", "Name of STRIP") 30 | 31 | // ToolChain of clang and llvm-link 32 | var ToolChain = flag.String("toolchain", "", "Path of clang and llvm-link") 33 | 34 | var FlagCC = FlagAll + FlagCCNoNumber + FlagCCNoOptzns 35 | 36 | const ( 37 | PrefixCmd = "cmd_" 38 | SuffixCmd = ".cmd" 39 | SuffixCC = ".o.cmd" 40 | 41 | SuffixLD = ".a.cmd" 42 | SuffixLTO = ".lto.o.cmd" 43 | SuffixKO = ".ko.cmd" 44 | NameScript = "build.sh" 45 | 46 | NameClang = "clang" 47 | 48 | // FlagAll : -w disable warning 49 | // FlagAll : -g debug info 50 | FlagAll = " -w -g" 51 | 52 | // FlagCCNoOptzns disable all optimization 53 | FlagCCNoOptzns = " -mllvm -disable-llvm-optzns" 54 | 55 | // FlagCCNoNumber add label to basic blocks and variables 56 | FlagCCNoNumber = " -fno-discard-value-names" 57 | 58 | NameLD = "llvm-link" 59 | FlagLD = " -v " 60 | FlagOutLD = " -o " 61 | 62 | CmdLinkVmlinux = " -v -o built-in.bc" 63 | 64 | // CmdTools skip the cmd with CmdTools 65 | CmdTools = "BUILD_STR(s)=$(pound)s" 66 | ) 67 | 68 | var bitcodes map[string]bool 69 | var linkedBitcodes map[string]bool 70 | var builtinModules map[string]bool 71 | 72 | // get cmd from *.cmd file 73 | func getCmd(cmdFilePath string) string { 74 | res := "" 75 | if _, err := os.Stat(cmdFilePath); os.IsNotExist(err) { 76 | fmt.Printf(cmdFilePath + " does not exist\n") 77 | } else { 78 | file, err := os.Open(cmdFilePath) 79 | if err != nil { 80 | log.Fatal(err) 81 | } 82 | defer func(file *os.File) { 83 | err := file.Close() 84 | if err != nil { 85 | 86 | } 87 | }(file) 88 | 89 | scanner := bufio.NewScanner(file) 90 | scanner.Split(bufio.ScanLines) 91 | 92 | var text []string 93 | for scanner.Scan() { 94 | text = append(text, scanner.Text()) 95 | } 96 | for _, eachLine := range text { 97 | if strings.HasPrefix(eachLine, PrefixCmd) { 98 | i := strings.Index(eachLine, ":=") 99 | if i > -1 { 100 | cmd := eachLine[i+3:] 101 | res = cmd 102 | break 103 | } else { 104 | fmt.Println("Cmd Index not found") 105 | fmt.Println(eachLine) 106 | } 107 | } 108 | } 109 | if err := scanner.Err(); err != nil { 110 | log.Fatal(err) 111 | } 112 | } 113 | res += "\n" 114 | res = res[strings.Index(res, ""):] 115 | return res 116 | } 117 | 118 | // for CC cmd, use " -save-temps=obj" or " -emit-llvm" to generate llvm bitcode 119 | func handleCC(cmd string) string { 120 | res := "" 121 | if i := strings.Index(cmd, " -c "); i > -1 { 122 | 123 | if j := strings.Index(cmd, CmdTools); j > -1 { 124 | return res 125 | } 126 | 127 | res += cmd[:i] 128 | res += FlagCC 129 | if *IsSaveTemps { 130 | res += " -save-temps=obj" 131 | } else { 132 | res += " -emit-llvm" 133 | } 134 | res += cmd[i:] 135 | 136 | // replace .o to .bc 137 | if *IsSaveTemps { 138 | 139 | } else { 140 | res = strings.Replace(res, ".o ", ".bc ", -1) 141 | } 142 | 143 | // for multiply ";" 144 | if strings.Count(res, " ; ") == 1 { 145 | i := strings.Index(res, ";") 146 | res = res[:i] + "\n" 147 | } 148 | res = strings.TrimSpace(res) + "\n" 149 | 150 | // can not compile .S, so just make a empty bitcode file 151 | if strings.HasSuffix(res, ".S\n") { 152 | s1 := strings.Split(res, " ") 153 | s2 := s1[len(s1)-2] 154 | s4 := strings.Replace(s2, ".o ", ".bc ", -1) 155 | res = "echo \"\" > " + s4 + "\n" 156 | } 157 | } else { 158 | fmt.Println("CC Index not found") 159 | fmt.Println(cmd) 160 | } 161 | res = " " + res 162 | // use -O0 install of other optimization 163 | res = strings.Replace(res, " "+*CC+" ", " "+filepath.Join(*ToolChain, NameClang)+" ", -1) 164 | res = strings.Replace(res, " -Os ", " -O0 ", -1) 165 | res = strings.Replace(res, " -O3 ", " -O0 ", -1) 166 | res = strings.Replace(res, " -O2 ", " -O0 ", -1) 167 | res = strings.Replace(res, " -fno-var-tracking-assignments ", " ", -1) 168 | res = strings.Replace(res, " -fconserve-stack ", " ", -1) 169 | res = strings.Replace(res, " -march=armv8-a+crypto ", " ", -1) 170 | res = strings.Replace(res, " -mno-fp-ret-in-387 ", " ", -1) 171 | res = strings.Replace(res, " -mskip-rax-setup ", " ", -1) 172 | res = strings.Replace(res, " -ftrivial-auto-var-init=zero ", " ", -1) 173 | 174 | return res 175 | } 176 | 177 | // handler LD cmd in *.o.cmd 178 | // @file_name in *.o.cmd includes the related file 179 | // need to get the name from that file 180 | func handleSuffixCCWithLD(cmd string, path string) string { 181 | 182 | // for multiply ";" 183 | if strings.Count(cmd, " ; ") == 1 { 184 | i := strings.Index(cmd, ";") 185 | cmd = cmd[:i] + "\n" 186 | } 187 | cmd = strings.TrimSpace(cmd) + "\n" 188 | 189 | res := "" 190 | if strings.Index(cmd, "@") > -1 { 191 | fileName := cmd[strings.Index(cmd, "@")+1 : len(cmd)-1] 192 | filePath := filepath.Join(path, fileName) 193 | file, err := os.Open(filePath) 194 | if err != nil { 195 | log.Println("handleSuffixCCWithLD file error: ") 196 | log.Fatal(err) 197 | } 198 | defer func(file *os.File) { 199 | err := file.Close() 200 | if err != nil { 201 | 202 | } 203 | }(file) 204 | 205 | scanner := bufio.NewScanner(file) 206 | scanner.Split(bufio.ScanLines) 207 | 208 | var text []string 209 | for scanner.Scan() { 210 | text = append(text, scanner.Text()) 211 | } 212 | 213 | if err := scanner.Err(); err != nil { 214 | log.Fatal(err) 215 | } 216 | 217 | res += filepath.Join(*ToolChain, NameLD) 218 | res += FlagLD 219 | res += FlagOutLD 220 | res += cmd[strings.Index(cmd, FlagOutLD)+len(FlagOutLD) : strings.Index(cmd, "@")] 221 | 222 | for _, s := range text { 223 | res += s + " " 224 | } 225 | 226 | res = strings.Replace(res, ".o ", ".bc ", -1) 227 | res += "\n" 228 | 229 | } else if strings.HasPrefix(cmd, *LLD) { 230 | res += filepath.Join(*ToolChain, NameLD) 231 | res += FlagLD 232 | res += FlagOutLD 233 | 234 | cmd = cmd[:len(cmd)-1] 235 | s1 := strings.Split(cmd, " ") 236 | obj := "" 237 | for _, s := range s1 { 238 | if strings.HasSuffix(s, ".o") { 239 | obj = " " + strings.Replace(s, ".o", ".bc", -1) + obj 240 | } 241 | } 242 | res += obj 243 | res += "\n" 244 | } else { 245 | fmt.Println("handleSuffixCCWithLD cmd error: " + cmd) 246 | } 247 | return res 248 | } 249 | 250 | // handle llvm-objcopy cmd 251 | func handleOBJCOPY(cmd string) string { 252 | res := filepath.Join(*ToolChain, NameLD) + FlagLD + FlagOutLD 253 | cmd = cmd[:len(cmd)-1] 254 | s1 := strings.Split(cmd, " ") 255 | obj := "" 256 | for _, s := range s1 { 257 | if strings.HasSuffix(s, ".o") { 258 | obj = " " + strings.Replace(s, ".o", ".bc", -1) + obj 259 | } 260 | } 261 | res += obj 262 | res += "\n" 263 | return res 264 | } 265 | 266 | // handle llvm-strip cmd 267 | func handleSTRIP(cmd string) string { 268 | res := filepath.Join(*ToolChain, NameLD) + FlagLD + FlagOutLD 269 | s1 := strings.Split(cmd, ";") 270 | cmd = s1[0] 271 | s1 = strings.Split(cmd, " ") 272 | for _, s := range s1 { 273 | if strings.HasSuffix(s, ".o") { 274 | res = res + " " + strings.Replace(s, ".o", ".bc", -1) 275 | } 276 | } 277 | res += "\n" 278 | return res 279 | } 280 | 281 | // use llvm-link to link all bitcode 282 | func handleLD(cmd string) string { 283 | replace := func(cmd string) string { 284 | res := "" 285 | if strings.Count(cmd, ".") > 1 { 286 | res = cmd 287 | res = strings.Replace(res, " "+*AR+" ", " "+filepath.Join(*ToolChain, NameLD)+FlagLD+FlagOutLD, -1) 288 | } else { 289 | res = "echo \"\" > " + cmd 290 | res = strings.Replace(res, " "+*AR+" ", " ", -1) 291 | } 292 | 293 | res = strings.Replace(res, " rcSTPD ", " ", -1) 294 | res = strings.Replace(res, " cDPrST ", " ", -1) 295 | res = strings.Replace(res, " cDPrsT ", " ", -1) 296 | res = strings.Replace(res, " rcsD ", " ", -1) 297 | 298 | res = strings.Replace(res, ".o", ".bc", -1) 299 | res = strings.Replace(res, ".a ", ".bc ", -1) 300 | res = strings.Replace(res, ".a\n", ".bc\n", -1) 301 | 302 | // for this drivers/misc/lkdtm/rodata.bc 303 | res = strings.Replace(res, "rodata_objcopy.bc", "rodata.bc", -1) 304 | res = strings.Replace(res, " drivers/of/unittest-data/built-in.bc", "", -1) 305 | res = strings.Replace(res, " unittest-data/built-in.bc", "", -1) 306 | 307 | return res 308 | } 309 | 310 | res := "" 311 | cmds := strings.Split(cmd, "; ") 312 | for _, cmd := range cmds { 313 | if i := strings.Index(cmd, " "+*AR+" "); i > -1 { 314 | res += replace(cmd) 315 | } 316 | } 317 | return res 318 | } 319 | 320 | // handler *.lto for external modules 321 | func handleLTO(cmd string) string { 322 | res := "" 323 | res += filepath.Join(*ToolChain, NameLD) 324 | res += FlagLD 325 | res += FlagOutLD 326 | 327 | cmd = cmd[strings.Index(cmd, FlagOutLD) : len(cmd)-1] 328 | objs := strings.Split(cmd, " ") 329 | output := false 330 | for _, obj := range objs { 331 | if obj == "-o" { 332 | output = true 333 | } else if output && obj != "" { 334 | res += strings.Replace(obj, ".o", ".bc", -1) 335 | output = false 336 | } else if strings.HasSuffix(obj, ".o") { 337 | res += " " + strings.Replace(obj, ".o", ".bc", -1) 338 | } 339 | } 340 | res += "\n" 341 | return res 342 | } 343 | 344 | // handle .ko for external modules 345 | func handleKO(cmd string) (string, string) { 346 | res := "" 347 | res += filepath.Join(*ToolChain, NameLD) 348 | res += FlagLD 349 | res += FlagOutLD 350 | 351 | cmd = cmd[strings.Index(cmd, FlagOutLD)+len(FlagOutLD):] 352 | cmd = strings.Replace(cmd, ".ko", ".ko.bc", -1) 353 | cmd = strings.Replace(cmd, ".o", ".bc", -1) 354 | 355 | // for multiply cmd or ";" pick the first one 356 | if strings.Count(cmd, ";") >= 1 { 357 | i := strings.Index(cmd, ";") 358 | cmd = cmd[:i] + "\n" 359 | } 360 | 361 | moduleFile := cmd[:strings.Index(cmd, ".ko.bc")+len(".ko.bc")] 362 | res += cmd 363 | 364 | return res, moduleFile 365 | } 366 | 367 | // find all *.cmd file and handle the cmd in them 368 | func build(kernelPath string) (string, string) { 369 | cmdCC := "" 370 | cmdLDInCC := "" 371 | 372 | err := filepath.Walk(kernelPath, 373 | func(path string, info os.FileInfo, err error) error { 374 | if err != nil { 375 | return err 376 | } 377 | // handle, all *.o.cmd files. 378 | // do not include *.lto.o.cmd files 379 | if strings.HasSuffix(info.Name(), SuffixCC) && !strings.HasSuffix(info.Name(), SuffixLTO) { 380 | // get cmd from the file 381 | cmd := getCmd(path) 382 | if strings.HasPrefix(cmd, *CC) { 383 | cmd := handleCC(cmd) 384 | cmdCC += cmd 385 | } else if strings.Index(cmd, *AR) > -1 { 386 | cmd = handleLD(cmd) 387 | cmdLDInCC = cmd + cmdLDInCC 388 | } else if strings.Index(cmd, *LLD) > -1 { 389 | cmd = handleSuffixCCWithLD(cmd, kernelPath) 390 | cmdLDInCC = cmd + cmdLDInCC 391 | } else if strings.HasPrefix(cmd, *OBJCOPY) { 392 | cmd = handleOBJCOPY(cmd) 393 | cmdLDInCC = cmd + cmdLDInCC 394 | } else if strings.HasPrefix(cmd, *STRIP) { 395 | cmd = handleSTRIP(cmd) 396 | cmdLDInCC = cmd + cmdLDInCC 397 | } else { 398 | fmt.Println(*CC + " not found") 399 | fmt.Println(path) 400 | fmt.Println(cmd) 401 | } 402 | } 403 | return nil 404 | }) 405 | if err != nil { 406 | log.Println(err) 407 | } 408 | 409 | cmdLink := "" 410 | moduleFiles := "" 411 | err = filepath.Walk(kernelPath, 412 | func(path string, info os.FileInfo, err error) error { 413 | if err != nil { 414 | return err 415 | } 416 | if strings.HasSuffix(info.Name(), SuffixLD) { 417 | //for built-in module built-in.a 418 | cmd := getCmd(path) 419 | cmd = handleLD(cmd) 420 | cmdLink = cmd + cmdLink 421 | if strings.Index(cmd, FlagOutLD) > -1 { 422 | obj := cmd[strings.Index(cmd, FlagOutLD)+len(FlagOutLD) : len(cmd)-1] 423 | if _, ok := linkedBitcodes[obj]; ok { 424 | 425 | } else { 426 | // do not insert "built-in.bc" 427 | if obj == "built-in.bc" { 428 | 429 | } else { 430 | builtinModules[obj] = true 431 | } 432 | 433 | } 434 | 435 | objs := strings.Split(cmd[:strings.Index(cmd, "|")], " ") 436 | base := "" 437 | for _, bc := range objs { 438 | // handle printf 439 | if i := strings.Index(bc, "%s"); i > -1 { 440 | base = bc[1:strings.Index(bc, "%s")] 441 | if base == "./" { 442 | base = "" 443 | } 444 | } 445 | if strings.Index(bc, ".bc") > -1 { 446 | linkedBitcodes[base+bc] = true 447 | } 448 | } 449 | } 450 | 451 | } else if strings.HasSuffix(info.Name(), SuffixLTO) { 452 | //for external module *.lto 453 | cmd := getCmd(path) 454 | cmdLink = handleLTO(cmd) + cmdLink 455 | 456 | } else if strings.HasSuffix(info.Name(), SuffixKO) { 457 | //for external module *.ko 458 | cmd, moduleFile := handleKO(getCmd(path)) 459 | cmdLink = cmd + cmdLink 460 | moduleFiles = moduleFile + " " + moduleFiles 461 | } 462 | 463 | return nil 464 | }) 465 | 466 | if err != nil { 467 | log.Println(err) 468 | } 469 | 470 | fmt.Println("moduleFiles: ") 471 | fmt.Println(moduleFiles) 472 | 473 | var resFinal string 474 | for module := range builtinModules { 475 | resFinal += " " + module 476 | } 477 | 478 | return cmdCC + cmdLDInCC + cmdLink + "\n# external modules: " + moduleFiles + "\n", resFinal 479 | } 480 | 481 | func generateScript(path string, cmd string) { 482 | res := "#!/bin/bash\n" 483 | res += cmd 484 | 485 | pathScript := filepath.Join(NameScript) 486 | _ = os.RemoveAll(pathScript) 487 | fmt.Printf("script path : bash %s\n", pathScript) 488 | f, err := os.OpenFile(pathScript, os.O_WRONLY|os.O_CREATE, 0666) 489 | if err != nil { 490 | log.Println(err) 491 | } 492 | defer func(f *os.File) { 493 | err := f.Close() 494 | if err != nil { 495 | 496 | } 497 | }(f) 498 | 499 | _, _ = f.WriteString(res) 500 | _, _ = f.WriteString("\n# path: " + path + "\n") 501 | } 502 | 503 | // collect compile cmd from *.cmd files in kernel 504 | // get cmd to generate llvm bitcode 505 | func main() { 506 | flag.Parse() 507 | *path, _ = filepath.Abs(*path) 508 | 509 | bitcodes = make(map[string]bool) 510 | linkedBitcodes = make(map[string]bool) 511 | builtinModules = make(map[string]bool) 512 | 513 | switch *cmd { 514 | case "module": 515 | { 516 | fmt.Printf("Build module\n") 517 | res, _ := build(*path) 518 | generateScript(*path, res) 519 | } 520 | case "kernel": 521 | { 522 | fmt.Printf("Build kernel and external module\n") 523 | res, res5 := build(*path) 524 | if res5 != "" { 525 | res += filepath.Join(*ToolChain, NameLD) + CmdLinkVmlinux + res5 + "\n" 526 | } 527 | generateScript(*path, res) 528 | } 529 | default: 530 | fmt.Printf("cmd is invalid\n") 531 | } 532 | } 533 | -------------------------------------------------------------------------------- /v6.1/README.md: -------------------------------------------------------------------------------- 1 | # Requirement 2 | - build kernel using clang into binary successfully 3 | - golang 4 | - The related kernel module are enabled in Linux kernel configuration 5 | 6 | # Build Linux kernel into LLVM Bitcode 7 | 8 | ```shell 9 | go run path/of/v5.12/KernelBitcode.go --help 10 | ``` 11 | 12 | 13 | 1. Build Kernel with clang 14 | ```shell 15 | make LLVM=1 -j16 16 | ``` 17 | 2. Generate script to build kernel into LLVM Bitcode 18 | ```shell 19 | go run path/of/v5.12/KernelBitcode.go 20 | ``` 21 | 3. Get LLVM Bitcode 22 | ```shell 23 | bash build.sh 24 | ``` 25 | `built-in.bc` is in each directory. 26 | All external modules are in the end of build.sh. 27 | Now you can perform analysis on those LLVM bitcode files. -------------------------------------------------------------------------------- /v6.6/KernelBitcode.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | // Build one module or whole kernel, e.g., module, kernel 14 | var cmd = flag.String("cmd", "kernel", "Build one module or whole kernel, e.g., module, kernel") 15 | 16 | // The path of kernel, e.g., linux 17 | var path = flag.String("path", ".", "the path of kernel") 18 | 19 | // IsSaveTemps : use -save-temps or emit-llvm to generate LLVM Bitcode 20 | // two kinds of two to generate bitcode 21 | var IsSaveTemps = flag.Bool("isSaveTemp", false, "use -save-temps or -emit-llvm") 22 | 23 | // tools used in build kernel 24 | var CC = flag.String("CC", "clang", "Name of CC") 25 | var LD = flag.String("LD", "llvm-link", "Name of LD") 26 | var AR = flag.String("AR", "llvm-ar", "Name of AR") 27 | var LLD = flag.String("LLD", "ld.lld", "Name of LD") 28 | var OBJCOPY = flag.String("OBJCOPY", "llvm-objcopy", "Name of OBJCOPY") 29 | var STRIP = flag.String("STRIP", "llvm-strip", "Name of STRIP") 30 | 31 | // ToolChain of clang and llvm-link 32 | var ToolChain = flag.String("toolchain", "", "Path of clang and llvm-link") 33 | 34 | var FlagCC = FlagAll + FlagCCNoNumber + FlagCCNoOptzns 35 | 36 | const ( 37 | PrefixCmd = "savedcmd_" 38 | SuffixCmd = ".cmd" 39 | SuffixCC = ".o.cmd" 40 | 41 | SuffixLD = ".a.cmd" 42 | SuffixLTO = ".lto.o.cmd" 43 | SuffixKO = ".ko.cmd" 44 | NameScript = "build.sh" 45 | 46 | NameClang = "clang" 47 | 48 | // FlagAll : -w disable warning 49 | // FlagAll : -g debug info 50 | FlagAll = " -w -g" 51 | 52 | // FlagCCNoOptzns disable all optimization 53 | FlagCCNoOptzns = " -mllvm -disable-llvm-optzns" 54 | 55 | // FlagCCNoNumber add label to basic blocks and variables 56 | FlagCCNoNumber = " -fno-discard-value-names" 57 | 58 | NameLD = "llvm-link" 59 | FlagLD = " -v " 60 | FlagOutLD = " -o " 61 | 62 | CmdLinkVmlinux = " -v -o built-in.bc" 63 | 64 | // CmdTools skip the cmd with CmdTools 65 | CmdTools = "BUILD_STR(s)=$(pound)s" 66 | ) 67 | 68 | var bitcodes map[string]bool 69 | var linkedBitcodes map[string]bool 70 | var builtinModules map[string]bool 71 | 72 | // get cmd from *.cmd file 73 | func getCmd(cmdFilePath string) string { 74 | res := "" 75 | if _, err := os.Stat(cmdFilePath); os.IsNotExist(err) { 76 | fmt.Printf(cmdFilePath + " does not exist\n") 77 | } else { 78 | file, err := os.Open(cmdFilePath) 79 | if err != nil { 80 | log.Fatal(err) 81 | } 82 | defer func(file *os.File) { 83 | err := file.Close() 84 | if err != nil { 85 | 86 | } 87 | }(file) 88 | 89 | scanner := bufio.NewScanner(file) 90 | scanner.Split(bufio.ScanLines) 91 | 92 | var text []string 93 | for scanner.Scan() { 94 | text = append(text, scanner.Text()) 95 | } 96 | for _, eachLine := range text { 97 | if strings.HasPrefix(eachLine, PrefixCmd) { 98 | i := strings.Index(eachLine, ":=") 99 | if i > -1 { 100 | cmd := eachLine[i+3:] 101 | res = cmd 102 | break 103 | } else { 104 | fmt.Println("Cmd Index not found") 105 | fmt.Println(eachLine) 106 | } 107 | } 108 | } 109 | if err := scanner.Err(); err != nil { 110 | log.Fatal(err) 111 | } 112 | } 113 | res += "\n" 114 | res = res[strings.Index(res, ""):] 115 | return res 116 | } 117 | 118 | // for CC cmd, use " -save-temps=obj" or " -emit-llvm" to generate llvm bitcode 119 | func handleCC(cmd string) string { 120 | res := "" 121 | if i := strings.Index(cmd, " -c "); i > -1 { 122 | 123 | if j := strings.Index(cmd, CmdTools); j > -1 { 124 | return res 125 | } 126 | 127 | res += cmd[:i] 128 | res += FlagCC 129 | if *IsSaveTemps { 130 | res += " -save-temps=obj" 131 | } else { 132 | res += " -emit-llvm" 133 | } 134 | res += cmd[i:] 135 | 136 | // replace .o to .bc 137 | if *IsSaveTemps { 138 | 139 | } else { 140 | res = strings.Replace(res, ".o ", ".bc ", -1) 141 | } 142 | 143 | // for multiply ";" 144 | if strings.Count(res, " ; ") == 1 { 145 | i := strings.Index(res, ";") 146 | res = res[:i] + "\n" 147 | } 148 | res = strings.TrimSpace(res) + "\n" 149 | 150 | // can not compile .S, so just make a empty bitcode file 151 | if strings.HasSuffix(res, ".S\n") { 152 | s1 := strings.Split(res, " ") 153 | s2 := s1[len(s1)-2] 154 | s4 := strings.Replace(s2, ".o ", ".bc ", -1) 155 | res = "echo \"\" > " + s4 + "\n" 156 | } 157 | } else { 158 | fmt.Println("CC Index not found") 159 | fmt.Println(cmd) 160 | } 161 | res = " " + res 162 | // use -O0 install of other optimization 163 | res = strings.Replace(res, " "+*CC+" ", " "+filepath.Join(*ToolChain, NameClang)+" ", -1) 164 | res = strings.Replace(res, " -Os ", " -O0 ", -1) 165 | res = strings.Replace(res, " -O3 ", " -O0 ", -1) 166 | res = strings.Replace(res, " -O2 ", " -O0 ", -1) 167 | res = strings.Replace(res, " -fno-var-tracking-assignments ", " ", -1) 168 | res = strings.Replace(res, " -fconserve-stack ", " ", -1) 169 | res = strings.Replace(res, " -march=armv8-a+crypto ", " ", -1) 170 | res = strings.Replace(res, " -mno-fp-ret-in-387 ", " ", -1) 171 | res = strings.Replace(res, " -mskip-rax-setup ", " ", -1) 172 | res = strings.Replace(res, " -ftrivial-auto-var-init=zero ", " ", -1) 173 | 174 | return res 175 | } 176 | 177 | // handler LD cmd in *.o.cmd 178 | // @file_name in *.o.cmd includes the related file 179 | // need to get the name from that file 180 | func handleSuffixCCWithLD(cmd string, path string) string { 181 | 182 | // for multiply ";" 183 | if strings.Count(cmd, " ; ") == 1 { 184 | i := strings.Index(cmd, ";") 185 | cmd = cmd[:i] + "\n" 186 | } 187 | cmd = strings.TrimSpace(cmd) + "\n" 188 | 189 | res := "" 190 | if strings.Index(cmd, "@") > -1 { 191 | fileName := cmd[strings.Index(cmd, "@")+1 : len(cmd)-1] 192 | filePath := filepath.Join(path, fileName) 193 | file, err := os.Open(filePath) 194 | if err != nil { 195 | log.Println("handleSuffixCCWithLD file error: ") 196 | log.Fatal(err) 197 | } 198 | defer func(file *os.File) { 199 | err := file.Close() 200 | if err != nil { 201 | 202 | } 203 | }(file) 204 | 205 | scanner := bufio.NewScanner(file) 206 | scanner.Split(bufio.ScanLines) 207 | 208 | var text []string 209 | for scanner.Scan() { 210 | text = append(text, scanner.Text()) 211 | } 212 | 213 | if err := scanner.Err(); err != nil { 214 | log.Fatal(err) 215 | } 216 | 217 | res += filepath.Join(*ToolChain, NameLD) 218 | res += FlagLD 219 | res += FlagOutLD 220 | res += cmd[strings.Index(cmd, FlagOutLD)+len(FlagOutLD) : strings.Index(cmd, "@")] 221 | 222 | for _, s := range text { 223 | res += s + " " 224 | } 225 | 226 | res = strings.Replace(res, ".o ", ".bc ", -1) 227 | res += "\n" 228 | 229 | } else if strings.HasPrefix(cmd, *LLD) { 230 | res += filepath.Join(*ToolChain, NameLD) 231 | res += FlagLD 232 | res += FlagOutLD 233 | 234 | cmd = cmd[:len(cmd)-1] 235 | s1 := strings.Split(cmd, " ") 236 | obj := "" 237 | for _, s := range s1 { 238 | if strings.HasSuffix(s, ".o") { 239 | obj = " " + strings.Replace(s, ".o", ".bc", -1) + obj 240 | } 241 | } 242 | res += obj 243 | res += "\n" 244 | } else { 245 | fmt.Println("handleSuffixCCWithLD cmd error: " + cmd) 246 | } 247 | return res 248 | } 249 | 250 | // handle llvm-objcopy cmd 251 | func handleOBJCOPY(cmd string) string { 252 | res := filepath.Join(*ToolChain, NameLD) + FlagLD + FlagOutLD 253 | cmd = cmd[:len(cmd)-1] 254 | s1 := strings.Split(cmd, " ") 255 | obj := "" 256 | for _, s := range s1 { 257 | if strings.HasSuffix(s, ".o") { 258 | obj = " " + strings.Replace(s, ".o", ".bc", -1) + obj 259 | } 260 | } 261 | res += obj 262 | res += "\n" 263 | return res 264 | } 265 | 266 | // handle llvm-strip cmd 267 | func handleSTRIP(cmd string) string { 268 | res := filepath.Join(*ToolChain, NameLD) + FlagLD + FlagOutLD 269 | s1 := strings.Split(cmd, ";") 270 | cmd = s1[0] 271 | s1 = strings.Split(cmd, " ") 272 | for _, s := range s1 { 273 | if strings.HasSuffix(s, ".o") { 274 | res = res + " " + strings.Replace(s, ".o", ".bc", -1) 275 | } 276 | } 277 | res += "\n" 278 | return res 279 | } 280 | 281 | // use llvm-link to link all bitcode 282 | func handleLD(cmd string) string { 283 | replace := func(cmd string) string { 284 | res := "" 285 | if strings.Count(cmd, ".") > 1 { 286 | res = cmd 287 | res = strings.Replace(res, " "+*AR+" ", " "+filepath.Join(*ToolChain, NameLD)+FlagLD+FlagOutLD, -1) 288 | } else { 289 | res = "echo \"\" > " + cmd 290 | res = strings.Replace(res, " "+*AR+" ", " ", -1) 291 | } 292 | 293 | res = strings.Replace(res, " rcSTPD ", " ", -1) 294 | res = strings.Replace(res, " cDPrST ", " ", -1) 295 | res = strings.Replace(res, " cDPrsT ", " ", -1) 296 | res = strings.Replace(res, " rcsD ", " ", -1) 297 | 298 | res = strings.Replace(res, ".o", ".bc", -1) 299 | res = strings.Replace(res, ".a ", ".bc ", -1) 300 | res = strings.Replace(res, ".a\n", ".bc\n", -1) 301 | 302 | // for this drivers/misc/lkdtm/rodata.bc 303 | res = strings.Replace(res, "rodata_objcopy.bc", "rodata.bc", -1) 304 | res = strings.Replace(res, " drivers/of/unittest-data/built-in.bc", "", -1) 305 | res = strings.Replace(res, " unittest-data/built-in.bc", "", -1) 306 | 307 | return res 308 | } 309 | 310 | res := "" 311 | cmds := strings.Split(cmd, "; ") 312 | for _, cmd := range cmds { 313 | if i := strings.Index(cmd, " "+*AR+" "); i > -1 { 314 | res += replace(cmd) 315 | } 316 | } 317 | return res 318 | } 319 | 320 | // handler *.lto for external modules 321 | func handleLTO(cmd string) string { 322 | res := "" 323 | res += filepath.Join(*ToolChain, NameLD) 324 | res += FlagLD 325 | res += FlagOutLD 326 | 327 | cmd = cmd[strings.Index(cmd, FlagOutLD) : len(cmd)-1] 328 | objs := strings.Split(cmd, " ") 329 | output := false 330 | for _, obj := range objs { 331 | if obj == "-o" { 332 | output = true 333 | } else if output && obj != "" { 334 | res += strings.Replace(obj, ".o", ".bc", -1) 335 | output = false 336 | } else if strings.HasSuffix(obj, ".o") { 337 | res += " " + strings.Replace(obj, ".o", ".bc", -1) 338 | } 339 | } 340 | res += "\n" 341 | return res 342 | } 343 | 344 | // handle .ko for external modules 345 | func handleKO(cmd string) (string, string) { 346 | res := "" 347 | res += filepath.Join(*ToolChain, NameLD) 348 | res += FlagLD 349 | res += FlagOutLD 350 | 351 | cmd = cmd[strings.Index(cmd, FlagOutLD)+len(FlagOutLD):] 352 | cmd = strings.Replace(cmd, ".ko", ".ko.bc", -1) 353 | cmd = strings.Replace(cmd, ".o", ".bc", -1) 354 | 355 | // for multiply cmd or ";" pick the first one 356 | if strings.Count(cmd, ";") >= 1 { 357 | i := strings.Index(cmd, ";") 358 | cmd = cmd[:i] + "\n" 359 | } 360 | 361 | moduleFile := cmd[:strings.Index(cmd, ".ko.bc")+len(".ko.bc")] 362 | res += cmd 363 | 364 | return res, moduleFile 365 | } 366 | 367 | // find all *.cmd file and handle the cmd in them 368 | func build(kernelPath string) (string, string) { 369 | cmdCC := "" 370 | cmdLDInCC := "" 371 | 372 | err := filepath.Walk(kernelPath, 373 | func(path string, info os.FileInfo, err error) error { 374 | if err != nil { 375 | return err 376 | } 377 | // handle, all *.o.cmd files. 378 | // do not include *.lto.o.cmd files 379 | if strings.HasSuffix(info.Name(), SuffixCC) && !strings.HasSuffix(info.Name(), SuffixLTO) { 380 | // get cmd from the file 381 | cmd := getCmd(path) 382 | if strings.HasPrefix(cmd, *CC) { 383 | cmd := handleCC(cmd) 384 | cmdCC += cmd 385 | } else if strings.Index(cmd, *AR) > -1 { 386 | cmd = handleLD(cmd) 387 | cmdLDInCC = cmd + cmdLDInCC 388 | } else if strings.Index(cmd, *LLD) > -1 { 389 | cmd = handleSuffixCCWithLD(cmd, kernelPath) 390 | cmdLDInCC = cmd + cmdLDInCC 391 | } else if strings.HasPrefix(cmd, *OBJCOPY) { 392 | cmd = handleOBJCOPY(cmd) 393 | cmdLDInCC = cmd + cmdLDInCC 394 | } else if strings.HasPrefix(cmd, *STRIP) { 395 | cmd = handleSTRIP(cmd) 396 | cmdLDInCC = cmd + cmdLDInCC 397 | } else { 398 | fmt.Println(*CC + " not found") 399 | fmt.Println(path) 400 | fmt.Println(cmd) 401 | } 402 | } 403 | return nil 404 | }) 405 | if err != nil { 406 | log.Println(err) 407 | } 408 | 409 | cmdLink := "" 410 | moduleFiles := "" 411 | err = filepath.Walk(kernelPath, 412 | func(path string, info os.FileInfo, err error) error { 413 | if err != nil { 414 | return err 415 | } 416 | if strings.HasSuffix(info.Name(), SuffixLD) { 417 | //for built-in module built-in.a 418 | cmd := getCmd(path) 419 | cmd = handleLD(cmd) 420 | cmdLink = cmd + cmdLink 421 | if strings.Index(cmd, FlagOutLD) > -1 { 422 | obj := cmd[strings.Index(cmd, FlagOutLD)+len(FlagOutLD) : len(cmd)-1] 423 | if _, ok := linkedBitcodes[obj]; ok { 424 | 425 | } else { 426 | // do not insert "built-in.bc" 427 | if obj == "built-in.bc" { 428 | 429 | } else { 430 | builtinModules[obj] = true 431 | } 432 | 433 | } 434 | 435 | objs := strings.Split(cmd[:strings.Index(cmd, "|")], " ") 436 | base := "" 437 | for _, bc := range objs { 438 | // handle printf 439 | if i := strings.Index(bc, "%s"); i > -1 { 440 | base = bc[1:strings.Index(bc, "%s")] 441 | if base == "./" { 442 | base = "" 443 | } 444 | } 445 | if strings.Index(bc, ".bc") > -1 { 446 | linkedBitcodes[base+bc] = true 447 | } 448 | } 449 | } 450 | 451 | } else if strings.HasSuffix(info.Name(), SuffixLTO) { 452 | //for external module *.lto 453 | cmd := getCmd(path) 454 | cmdLink = handleLTO(cmd) + cmdLink 455 | 456 | } else if strings.HasSuffix(info.Name(), SuffixKO) { 457 | //for external module *.ko 458 | cmd, moduleFile := handleKO(getCmd(path)) 459 | cmdLink = cmd + cmdLink 460 | moduleFiles = moduleFile + " " + moduleFiles 461 | } 462 | 463 | return nil 464 | }) 465 | 466 | if err != nil { 467 | log.Println(err) 468 | } 469 | 470 | fmt.Println("moduleFiles: ") 471 | fmt.Println(moduleFiles) 472 | 473 | var resFinal string 474 | for module := range builtinModules { 475 | resFinal += " " + module 476 | } 477 | 478 | return cmdCC + cmdLDInCC + cmdLink + "\n# external modules: " + moduleFiles + "\n", resFinal 479 | } 480 | 481 | func generateScript(path string, cmd string) { 482 | res := "#!/bin/bash\n" 483 | res += cmd 484 | 485 | pathScript := filepath.Join(NameScript) 486 | _ = os.RemoveAll(pathScript) 487 | fmt.Printf("script path : bash %s\n", pathScript) 488 | f, err := os.OpenFile(pathScript, os.O_WRONLY|os.O_CREATE, 0666) 489 | if err != nil { 490 | log.Println(err) 491 | } 492 | defer func(f *os.File) { 493 | err := f.Close() 494 | if err != nil { 495 | 496 | } 497 | }(f) 498 | 499 | _, _ = f.WriteString(res) 500 | _, _ = f.WriteString("\n# path: " + path + "\n") 501 | } 502 | 503 | // collect compile cmd from *.cmd files in kernel 504 | // get cmd to generate llvm bitcode 505 | func main() { 506 | flag.Parse() 507 | *path, _ = filepath.Abs(*path) 508 | 509 | bitcodes = make(map[string]bool) 510 | linkedBitcodes = make(map[string]bool) 511 | builtinModules = make(map[string]bool) 512 | 513 | switch *cmd { 514 | case "module": 515 | { 516 | fmt.Printf("Build module\n") 517 | res, _ := build(*path) 518 | generateScript(*path, res) 519 | } 520 | case "kernel": 521 | { 522 | fmt.Printf("Build kernel and external module\n") 523 | res, res5 := build(*path) 524 | if res5 != "" { 525 | res += filepath.Join(*ToolChain, NameLD) + CmdLinkVmlinux + res5 + "\n" 526 | } 527 | generateScript(*path, res) 528 | } 529 | default: 530 | fmt.Printf("cmd is invalid\n") 531 | } 532 | } 533 | -------------------------------------------------------------------------------- /v6.6/README.md: -------------------------------------------------------------------------------- 1 | # Requirement 2 | - build kernel using clang into binary successfully 3 | - golang 4 | - The related kernel module are enabled in Linux kernel configuration 5 | 6 | # Build Linux kernel into LLVM Bitcode 7 | 8 | ```shell 9 | go run path/of/v5.12/KernelBitcode.go --help 10 | ``` 11 | 12 | 13 | 1. Build Kernel with clang 14 | ```shell 15 | make LLVM=1 -j16 16 | ``` 17 | 2. Generate script to build kernel into LLVM Bitcode 18 | ```shell 19 | go run path/of/v5.12/KernelBitcode.go 20 | ``` 21 | 3. Get LLVM Bitcode 22 | ```shell 23 | bash build.sh 24 | ``` 25 | `built-in.bc` is in each directory. 26 | All external modules are in the end of build.sh. 27 | Now you can perform analysis on those LLVM bitcode files. --------------------------------------------------------------------------------