# Linux kernel-native Makefile ("Kbuild") for libwolfssl.ko # # Copyright (C) 2006-2025 wolfSSL Inc. # # This file is part of wolfSSL. # # wolfSSL is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # wolfSSL is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA .ONESHELL: SHELL=bash ifeq "$(KERNEL_ARCH)" "x86" KERNEL_ARCH_X86 := yes else ifeq "$(KERNEL_ARCH)" "x86_64" KERNEL_ARCH_X86 := yes else KERNEL_ARCH_X86 := no endif ifeq "$(WOLFSSL_OBJ_FILES)" "" $(error $$WOLFSSL_OBJ_FILES is unset.) endif ifeq "$(WOLFSSL_CFLAGS)" "" $(error $$WOLFSSL_CFLAGS is unset.) endif WOLFSSL_CFLAGS += -ffreestanding -Wframe-larger-than=$(MAX_STACK_FRAME_SIZE) -isystem $(shell $(CC) -print-file-name=include) ifeq "$(KERNEL_ARCH)" "aarch64" WOLFSSL_CFLAGS += -mno-outline-atomics else ifeq "$(KERNEL_ARCH)" "arm64" WOLFSSL_CFLAGS += -mno-outline-atomics else ifeq "$(KERNEL_ARCH)" "arm" # avoids R_ARM_THM_JUMP11 relocations, including a stubborn tail recursion # optimization from wc_sp_cmp to wc_sp_cmp_mag: WOLFSSL_CFLAGS += -fno-optimize-sibling-calls -Os endif obj-m := libwolfssl.o WOLFSSL_OBJ_TARGETS := $(patsubst %, $(obj)/%, $(WOLFSSL_OBJ_FILES)) ifeq "$(ENABLED_LINUXKM_PIE)" "yes" WOLFCRYPT_PIE_FILES := $(patsubst %, $(obj)/%, $(WOLFCRYPT_PIE_FILES)) endif $(obj)/linuxkm/module_exports.o: $(WOLFSSL_OBJ_TARGETS) ifndef KERNEL_THREAD_STACK_SIZE ifdef CROSS_COMPILE KERNEL_THREAD_STACK_SIZE=16384 endif endif # this mechanism only works in kernel 5.x+ (fallback to hardcoded value) ifndef KERNEL_THREAD_STACK_SIZE hostprogs := linuxkm/get_thread_size always-y := $(hostprogs) endif HOST_EXTRACFLAGS += $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(KBUILD_CFLAGS) -static -fno-omit-frame-pointer # "-mindirect-branch=keep -mfunction-return=keep" to avoid "undefined reference # to `__x86_return_thunk'" on CONFIG_RETHUNK kernels (5.19.0-rc7) ifeq "$(KERNEL_ARCH_X86)" "yes" HOST_EXTRACFLAGS += -mindirect-branch=keep -mfunction-return=keep endif # this rule is needed to get build to succeed in 4.x (get_thread_size still doesn't get built) $(obj)/linuxkm/get_thread_size: $(src)/linuxkm/get_thread_size.c ifndef KERNEL_THREAD_STACK_SIZE $(WOLFSSL_OBJ_TARGETS): | $(obj)/linuxkm/get_thread_size KERNEL_THREAD_STACK_SIZE=$(shell test -x $(obj)/linuxkm/get_thread_size && $(obj)/linuxkm/get_thread_size || echo 16384) endif MAX_STACK_FRAME_SIZE=$(shell echo $$(( $(KERNEL_THREAD_STACK_SIZE) / 4))) libwolfssl-y := $(WOLFSSL_OBJ_FILES) linuxkm/module_hooks.o linuxkm/module_exports.o ifeq "$(FIPS_OPTEST)" "1" libwolfssl-y += linuxkm/optest-140-3/linuxkm_optest_wrapper.o endif WOLFSSL_CFLAGS_NO_VECTOR_INSNS := $(CFLAGS_SIMD_DISABLE) $(CFLAGS_FPU_DISABLE) ifeq "$(ENABLED_ASM)" "yes" WOLFSSL_CFLAGS_YES_VECTOR_INSNS := $(CFLAGS_SIMD_ENABLE) $(CFLAGS_FPU_DISABLE) $(CFLAGS_AUTO_VECTORIZE_DISABLE) else WOLFSSL_CFLAGS_YES_VECTOR_INSNS := $(WOLFSSL_CFLAGS_NO_VECTOR_INSNS) endif ccflags-y = $(WOLFSSL_CFLAGS) $(WOLFSSL_CFLAGS_NO_VECTOR_INSNS) ifeq "$(ENABLED_LINUXKM_PIE)" "yes" # note, we need -fno-stack-protector to avoid references to # "__stack_chk_fail" from the wolfCrypt container. PIE_FLAGS := -fPIE -fno-stack-protector -fno-toplevel-reorder # the kernel sanitizers generate external references to # __ubsan_handle_out_of_bounds(), __ubsan_handle_shift_out_of_bounds(), etc. KASAN_SANITIZE := n UBSAN_SANITIZE := n ifeq "$(KERNEL_ARCH_X86)" "yes" PIE_FLAGS += -mcmodel=small # eliminate external references to __x86_return_thunk and # __x86_indirect_thunk_foo implementations. _all_ references must be # eliminated, not just those in PIE objects, otherwise some kernels will # false-positively complain about unpatched thunks. ifeq "$(CONFIG_MITIGATION_RETPOLINE)" "y" PIE_SUPPORT_FLAGS += -mfunction-return=thunk-inline else PIE_SUPPORT_FLAGS += -mfunction-return=keep endif ifeq "$(CONFIG_MITIGATION_RETHUNK)" "y" PIE_SUPPORT_FLAGS += -mindirect-branch=thunk-inline else PIE_SUPPORT_FLAGS += -mindirect-branch=keep endif OBJECT_FILES_NON_STANDARD := y endif ifeq "$(KERNEL_ARCH)" "arm" PIE_FLAGS += -fno-unwind-tables endif ifeq "$(KERNEL_ARCH)" "mips" PIE_FLAGS += -mabicalls endif ccflags-y += $(PIE_SUPPORT_FLAGS) $(WOLFCRYPT_PIE_FILES): ccflags-y += $(PIE_FLAGS) $(WOLFCRYPT_PIE_FILES): ccflags-remove-y += -pg ifdef FORCE_GLOBAL_OBJTOOL_OFF undefine CONFIG_OBJTOOL endif endif ifdef KERNEL_EXTRA_CFLAGS_REMOVE ccflags-remove-y += $(KERNEL_EXTRA_CFLAGS_REMOVE) endif $(obj)/libwolfssl.mod.o: ccflags-y := $(PIE_SUPPORT_FLAGS) $(obj)/wolfcrypt/test/test.o: ccflags-y += -DNO_MAIN_DRIVER -DWOLFSSL_NO_OPTIONS_H $(obj)/wolfcrypt/src/aes.o: ccflags-y := $(WOLFSSL_CFLAGS) $(WOLFSSL_CFLAGS_YES_VECTOR_INSNS) $(PIE_FLAGS) $(PIE_SUPPORT_FLAGS) $(obj)/wolfcrypt/benchmark/benchmark.o: ccflags-y := $(WOLFSSL_CFLAGS) $(CFLAGS_FPU_ENABLE) $(CFLAGS_SIMD_ENABLE) $(PIE_SUPPORT_FLAGS) -DNO_MAIN_FUNCTION -DWOLFSSL_NO_OPTIONS_H $(obj)/wolfcrypt/benchmark/benchmark.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_ENABLE_SIMD_DISABLE) asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPUSIMD_DISABLE) # vectorized implementations that are kernel-safe are listed here. # these are known kernel-compatible, but need the vector instructions enabled in the assembler, # and most of them still irritate objtool. $(obj)/wolfcrypt/src/aes_asm.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE) $(obj)/wolfcrypt/src/aes_asm.o: OBJECT_FILES_NON_STANDARD := y $(obj)/wolfcrypt/src/aes_gcm_asm.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE) $(obj)/wolfcrypt/src/aes_gcm_asm.o: OBJECT_FILES_NON_STANDARD := y $(obj)/wolfcrypt/src/aes_xts_asm.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE) $(obj)/wolfcrypt/src/aes_xts_asm.o: OBJECT_FILES_NON_STANDARD := y $(obj)/wolfcrypt/src/sp_x86_64_asm.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE) $(obj)/wolfcrypt/src/sp_x86_64_asm.o: OBJECT_FILES_NON_STANDARD := y $(obj)/wolfcrypt/src/sha256_asm.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE) $(obj)/wolfcrypt/src/sha256_asm.o: OBJECT_FILES_NON_STANDARD := y $(obj)/wolfcrypt/src/sha512_asm.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE) $(obj)/wolfcrypt/src/sha512_asm.o: OBJECT_FILES_NON_STANDARD := y $(obj)/wolfcrypt/src/sha3_asm.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE) $(obj)/wolfcrypt/src/sha3_asm.o: OBJECT_FILES_NON_STANDARD := y $(obj)/wolfcrypt/src/chacha_asm.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE) $(obj)/wolfcrypt/src/chacha_asm.o: OBJECT_FILES_NON_STANDARD := y $(obj)/wolfcrypt/src/poly1305_asm.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE) $(obj)/wolfcrypt/src/poly1305_asm.o: OBJECT_FILES_NON_STANDARD := y $(obj)/wolfcrypt/src/wc_mlkem_asm.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE) $(obj)/wolfcrypt/src/wc_mlkem_asm.o: OBJECT_FILES_NON_STANDARD := y $(obj)/wolfcrypt/src/wc_mldsa_asm.o: asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE) $(obj)/wolfcrypt/src/wc_mldsa_asm.o: OBJECT_FILES_NON_STANDARD := y ifndef READELF READELF := readelf endif ifeq "$(ENABLED_LINUXKM_PIE)" "yes" ldflags-y += -T $(src)/wolfcrypt.lds ifndef NM NM := nm endif ifndef OBJCOPY OBJCOPY := objcopy endif RENAME_PIE_TEXT_AND_DATA_SECTIONS := \ if [[ "$(quiet)" != "silent_" ]]; then \ echo -n ' Checking wolfCrypt for unresolved symbols and forbidden relocations... '; \ fi; \ cd "$(obj)" || exit $$?; \ $(LD) -relocatable -o wolfcrypt_test_link.o $(WOLFCRYPT_PIE_FILES) || exit $$?; \ undefined=$$($(NM) --undefined-only wolfcrypt_test_link.o) || exit $$?; \ GOT_relocs=$$($(READELF) --relocs --wide wolfcrypt_test_link.o | grep -E '^[^ ]+ +[^ ]+ +[^ ]*GOT[^ ]* ') || [ $$? = 1 ] || exit 2; \ rm wolfcrypt_test_link.o; \ if [ -n "$$undefined" ]; then \ echo "wolfCrypt container has unresolved symbols:" 1>&2; \ echo "$$undefined" 1>&2; \ exit 1; \ fi; \ if [ -n "$$GOT_relocs" ]; then \ echo "wolfCrypt container has GOT relocations (non-local function address used as operand?):" 1>&2; \ echo "$$GOT_relocs" 1>&2; \ exit 1; \ fi; \ if [[ "$(quiet)" != "silent_" ]]; then \ echo 'OK.'; \ fi; \ cd "$(obj)" || exit $$?; \ for file in $(WOLFCRYPT_PIE_FILES); do \ $(OBJCOPY) $$($(READELF) --sections --wide "$$file" | \ $(AWK) ' \ { \ if (match($$0, "^ *\\[ *[0-9]+\\] +\\.(text|rodata|data|bss)(\\.[^ ]+)? ", a)) \ { \ printf("--rename-section .%s%s=.%s_wolfcrypt ", \ a[1], a[2], a[1]); \ } \ else if (match($$0, "^ *\\[ *[0-9]+\\] +\\.([^ ]+)\\.(text|rodata|data|bss) ", a)) \ { \ printf("--rename-section .%s.%s=.%s_wolfcrypt ", a[1], a[2], a[2]); \ } \ }') "$$file" || exit $$?; \ done; \ { $(READELF) --sections --syms --wide $(WOLFCRYPT_PIE_FILES) | \ $(AWK) -v obj="$(obj)" ' \ /^File:/ { \ phase = 0; \ delete wolfcrypt_data_sections; \ delete wolfcrypt_text_sections; \ delete other_sections; \ if (substr($$2, 1, length(obj)) == obj) { \ curfile = substr($$2, length(obj) + 2); \ } else { \ curfile=$$2; \ } \ next; \ } \ /^Section Headers:/ { \ phase = 1; \ next; \ } \ /^Symbol table / { \ phase = 2; \ next; \ } \ { \ if (phase == 1) { \ if (match($$0, "^ *\\[ *([0-9]+)\\] +([^ ]+) ", a)) {\ switch (a[2]) { \ case ".text_wolfcrypt": \ { \ wolfcrypt_text_sections[a[1]] = a[2]; \ next; \ } \ case /^\.(data|rodata|bss)_wolfcrypt$$/: \ { \ wolfcrypt_data_sections[a[1]] = a[2]; \ next; \ } \ default: \ { \ other_sections[a[1]] = a[2]; \ } \ } \ next; \ } \ next; \ } \ else if (phase == 2) { \ if ($$4 == "FUNC") { \ if (! ($$7 in wolfcrypt_text_sections)) { \ print curfile ": " $$4 " " $$8 " " other_sections[$$7] >"/dev/stderr"; \ ++warnings; \ } \ next; \ } \ else if ($$4 == "OBJECT") { \ if (! ($$7 in wolfcrypt_data_sections)) { \ if ((other_sections[$$7] == ".printk_index") || \ (($$8 ~ /^_entry\.[0-9]+$$|^kernel_read_file_str$$/) && \ (other_sections[$$7] == ".data.rel.ro.local"))) \ next; \ print curfile ": " $$4 " " $$8 " " other_sections[$$7] >"/dev/stderr"; \ ++warnings; \ } \ next; \ } \ } \ } \ END { \ if (warnings) { \ exit(1); \ } else { \ exit(0); \ }}'; } || \ { echo 'Error: symbol(s) missed by containerization.' >&2; exit 1; }; \ if [[ "$(quiet)" != "silent_" ]]; then \ echo ' wolfCrypt .{text,data,rodata,bss} sections containerized to .{text,data,rodata}_wolfcrypt'; \ fi endif # auto-generate the exported symbol list, leveraging the WOLFSSL_API visibility tags. # exclude symbols that don't match wc_* or wolf*. EXPORT_SYMBOL := EXPORT_SYMBOL_NS_GPL $(obj)/linuxkm/module_exports.c: $(src)/module_exports.c.template $(WOLFSSL_OBJ_TARGETS) $(obj)/linuxkm/module_hooks.o @$(RENAME_PIE_TEXT_AND_DATA_SECTIONS) @cp $< $@ || exit $$? if [[ "$${VERSION}" -gt 6 || ("$${VERSION}" -eq 6 && "$${PATCHLEVEL}" -ge 13) ]]; then # use ASCII octal escape to avoid syntax disruption in the awk script. ns='\042WOLFSSL\042' else ns='WOLFSSL' fi $(READELF) --symbols --wide $(filter %.o,$^) | $(AWK) '/^ *[0-9]+: / { if ($$8 !~ /^(wc_|wolf|WOLF|TLSX_)/){next;} if (($$4 == "FUNC") && ($$5 == "GLOBAL") && ($$6 == "DEFAULT")) { print "$(EXPORT_SYMBOL)(" $$8 ", '"$$ns"');"; } }' >> $@ || exit $$? echo -e "#ifndef NO_CRYPT_TEST\n$(EXPORT_SYMBOL)(wolfcrypt_test, $${ns});\n#endif" >> $@ clean-files := linuxkm src wolfcrypt