diff --git a/README.md b/README.md index 5054ccdb3..ff771344f 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Supported Architectures - [x] ppc64le - [x] arm64 - [x] s390 [upstream prerequisites](doc/s390-upstream-prerequisites.md) +- [x] loongarch64 Installation ------------ diff --git a/kpatch-build/Makefile b/kpatch-build/Makefile index 1f7ee4b16..c8f3bbaff 100644 --- a/kpatch-build/Makefile +++ b/kpatch-build/Makefile @@ -21,7 +21,7 @@ PLUGIN_CFLAGS := $(filter-out -std=gnu11 -Wconversion, $(CFLAGS)) PLUGIN_CFLAGS += -shared -I$(GCC_PLUGINS_DIR)/include \ -Igcc-plugins -fPIC -fno-rtti -O2 -Wall endif -ifeq ($(filter $(ARCH),s390x x86_64 ppc64le aarch64),) +ifeq ($(filter $(ARCH),s390x x86_64 ppc64le aarch64 loongarch64),) $(error Unsupported architecture ${ARCH}, check https://github.com/dynup/kpatch/#supported-architectures) endif diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 09160030c..a5cbd5327 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -182,6 +182,8 @@ static bool is_gcc6_localentry_bundled_sym(struct kpatch_elf *kelf, return false; case S390: return false; + case LOONGARCH64: + return false; default: ERROR("unsupported arch"); } @@ -247,6 +249,7 @@ static bool kpatch_is_mapping_symbol(struct kpatch_elf *kelf, struct symbol *sym case X86_64: case PPC64: case S390: + case LOONGARCH64: return false; default: ERROR("unsupported arch"); @@ -771,6 +774,11 @@ static bool insn_is_load_immediate(struct kpatch_elf *kelf, void *addr) break; + case LOONGARCH64: + /* to be done */ + + break; + case S390: /* arg2: lghi %r3, imm */ if (insn[0] == 0xa7 && insn[1] == 0x39) @@ -2598,22 +2606,22 @@ static bool static_call_sites_group_filter(struct lookup_table *lookup, static struct special_section special_sections[] = { { .name = "__bug_table", - .arch = AARCH64 | X86_64 | PPC64 | S390, + .arch = AARCH64 | X86_64 | PPC64 | S390 | LOONGARCH64, .group_size = bug_table_group_size, }, { .name = ".fixup", - .arch = AARCH64 | X86_64 | PPC64 | S390, + .arch = AARCH64 | X86_64 | PPC64 | S390 | LOONGARCH64, .group_size = fixup_group_size, }, { .name = "__ex_table", /* must come after .fixup */ - .arch = AARCH64 | X86_64 | PPC64 | S390, + .arch = AARCH64 | X86_64 | PPC64 | S390 | LOONGARCH64, .group_size = ex_table_group_size, }, { .name = "__jump_table", - .arch = AARCH64 | X86_64 | PPC64 | S390, + .arch = AARCH64 | X86_64 | PPC64 | S390 | LOONGARCH64, .group_size = jump_table_group_size, .group_filter = jump_table_group_filter, }, @@ -2634,7 +2642,7 @@ static struct special_section special_sections[] = { }, { .name = ".altinstructions", - .arch = AARCH64 | X86_64 | S390, + .arch = AARCH64 | X86_64 | S390 | LOONGARCH64, .group_size = altinstructions_group_size, }, { @@ -3057,6 +3065,11 @@ static void kpatch_mark_ignored_sections(struct kpatch_elf *kelf) !strcmp(sec->name, "__patchable_function_entries")) sec->ignore = 1; } + + if (kelf->arch == LOONGARCH64) { + if (!strncmp(sec->name, ".rela.orc_unwind_ip", 19)) + sec->ignore = 1; + } } sec = find_section_by_name(&kelf->sections, ".kpatch.ignore.sections"); @@ -4059,6 +4072,21 @@ static void kpatch_create_ftrace_callsite_sections(struct kpatch_elf *kelf) insn_offset = sym->sym.st_value; break; } + case LOONGARCH64: { + bool found = false; + unsigned char *insn = sym->sec->data->d_buf + sym->sym.st_value; + + /* 0x03400000 is NOP instruction for LoongArch. */ + if (insn[0] == 0x00 && insn[1] == 0x00 && insn[2] == 0x40 && insn[3] == 0x03 && + insn[4] == 0x00 && insn[5] == 0x00 && insn[6] == 0x40 && insn[7] == 0x03) + found = true; + + if (!found) + ERROR("%s: unexpected instruction at the start of the function", sym->name); + + insn_offset = 0; + break; + } default: ERROR("unsupported arch"); } @@ -4317,6 +4345,7 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) sym->has_func_profiling = 1; break; case AARCH64: + case LOONGARCH64: if (kpatch_symbol_has_pfe_entry(kelf, sym)) sym->has_func_profiling = 1; break; diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index e2aa0df16..996a866a4 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -319,6 +319,17 @@ find_core_symvers() { [[ -e "$SYMVERSFILE" ]] } +cc_option_check() { + local compiler="$1" + local option="$2" + + if "$compiler" -Werror "$option" -c -x c /dev/null -o /dev/null 2>/dev/null; then + return 0 + fi + + return 1 +} + gcc_version_from_file() { "$READELF" -p .comment "$1" | grep -m 1 -o 'GCC:.*' } @@ -341,6 +352,11 @@ gcc_version_check() { return 1 fi + if [[ "$ARCH" = "loongarch64" ]]; then + cc_option_check "$GCC" "-mno-direct-extern-access" || \ + die "gcc version ($gccver) doesn't support -mno-direct-extern-access" + fi + out="$("$GCC" -c -gz=none -o "$o" "$c" 2>&1)" if [[ -z "$out" ]]; then DEBUG_KCFLAGS="-gz=none" @@ -371,6 +387,11 @@ clang_version_check() { clangver=$("$CLANG" --version | grep -m 1 -Eo 'clang version [0-9.]+') kclangver="$(clang_version_from_file "$target")" + if [[ "$ARCH" = "loongarch64" ]]; then + cc_option_check "$CLANG" "-fno-direct-access-external-data" || \ + die "clang version ($clangver) doesn't support -fno-direct-access-external-data" + fi + # ensure clang version matches that used to build the kernel if [[ "$clangver" != "$kclangver" ]]; then warn "clang/kernel version mismatch" @@ -407,6 +428,9 @@ find_special_section_data() { "aarch64") check[a]=true # alt_instr ;; + "loongarch64") + check[a]=true # alt_instr + ;; esac # Kernel CONFIG_ features @@ -1255,6 +1279,7 @@ remove_patches cp -LR "$DATADIR/patch" "$TEMPDIR" || die chmod -R u+rw "$TEMPDIR" || die +declare -a ARCH_MAKEVARS if [[ "$ARCH" = "ppc64le" ]]; then ARCH_KCFLAGS="-mcmodel=large -fplugin=$PLUGINDIR/ppc64le-plugin.so" fi @@ -1264,6 +1289,15 @@ if [[ "$ARCH" = "s390x" ]]; then ! kernel_version_gte 6.10.0 && ARCH_KCFLAGS+=" -fPIE" fi +if [[ "$ARCH" = "loongarch64" ]]; then + ARCH_KCFLAGS="-fPIC" + if [[ -n "$CONFIG_CC_IS_CLANG" ]]; then + ARCH_MAKEVARS+=("KBUILD_CFLAGS_KERNEL+=-fno-direct-access-external-data") + else + ARCH_MAKEVARS+=("KBUILD_CFLAGS_KERNEL+=-mno-direct-extern-access") + fi +fi + export KCFLAGS="-I$DATADIR/patch -ffunction-sections -fdata-sections \ $ARCH_KCFLAGS $DEBUG_KCFLAGS" @@ -1302,7 +1336,7 @@ fi # $TARGETS used as list, no quotes. # shellcheck disable=SC2086 -make "${MAKEVARS[@]}" "-j$CPUS" $TARGETS 2>&1 | logger || die +make "${ARCH_MAKEVARS[@]}" "${MAKEVARS[@]}" "-j$CPUS" $TARGETS 2>&1 | logger || die # Save original module symvers cp -f "$BUILDDIR/Module.symvers" "$TEMPDIR/Module.symvers" || die @@ -1315,7 +1349,7 @@ export KPATCH_GCC_SRCDIR="$BUILDDIR" save_env # $TARGETS used as list, no quotes. # shellcheck disable=SC2086 -KBUILD_MODPOST_WARN=1 make "${MAKEVARS[@]}" "-j$CPUS" $TARGETS 2>&1 | logger || die +KBUILD_MODPOST_WARN=1 make "${ARCH_MAKEVARS[@]}" "${MAKEVARS[@]}" "-j$CPUS" $TARGETS 2>&1 | logger || die # source.c:(.section+0xFF): undefined reference to `symbol' grep "undefined reference" "$LOGFILE" | sed -r "s/^.*\`(.*)'$/\\1/" \ diff --git a/kpatch-build/kpatch-cc b/kpatch-build/kpatch-cc index fc9433ad6..ad23df932 100755 --- a/kpatch-build/kpatch-cc +++ b/kpatch-build/kpatch-cc @@ -43,6 +43,7 @@ if [[ "$TOOLCHAINCMD" =~ ^(.*-)?gcc$ || "$TOOLCHAINCMD" =~ ^(.*-)?clang$ ]] ; th arch/s390/boot/*|\ arch/s390/purgatory/*|\ arch/s390/kernel/vdso64/*|\ + arch/loongarch/vdso/*|\ drivers/firmware/efi/libstub/*|\ init/version.o|\ init/version-timestamp.o|\ diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 11ce45543..d418ecda5 100755 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -156,6 +156,8 @@ unsigned int absolute_rela_type(struct kpatch_elf *kelf) return R_390_64; case AARCH64: return R_AARCH64_ABS64; + case LOONGARCH64: + return R_LARCH_64; default: ERROR("unsupported arch"); } @@ -221,6 +223,7 @@ long rela_target_offset(struct kpatch_elf *kelf, struct section *relasec, switch(kelf->arch) { case PPC64: case AARCH64: + case LOONGARCH64: add_off = 0; break; case X86_64: @@ -278,6 +281,7 @@ unsigned int insn_length(struct kpatch_elf *kelf, void *addr) return decoded_insn.length; case PPC64: + case LOONGARCH64: return 4; case S390: @@ -349,6 +353,21 @@ static void kpatch_create_rela_list(struct kpatch_elf *kelf, rela->sym->name, rela->addend); } + if (kelf->arch == LOONGARCH64) { + /* + * LoongArch GCC creates local labels such as .LBB7266, + * replace them with section symbols. + */ + if (rela->sym->sec && rela->sym->type == STT_NOTYPE && + rela->sym->bind == STB_LOCAL) { + log_debug("local label: %s -> ", rela->sym->name); + rela->addend += rela->sym->sym.st_value; + rela->sym = rela->sym->sec->secsym; + log_debug("section symbol: %s\n", rela->sym->name); + } + } + + if (skip) continue; log_debug("offset %d, type %d, %s %s %ld", rela->offset, @@ -614,6 +633,9 @@ struct kpatch_elf *kpatch_elf_open(const char *name) case EM_AARCH64: kelf->arch = AARCH64; break; + case EM_LOONGARCH: + kelf->arch = LOONGARCH64; + break; default: ERROR("Unsupported target architecture"); } @@ -647,6 +669,18 @@ struct kpatch_elf *kpatch_elf_open(const char *name) } } + if (kelf->arch == LOONGARCH64) { + struct symbol *sym, *tmp; + + /* Delete local labels created by LoongArch GCC */ + list_for_each_entry_safe(sym, tmp, &kelf->symbols, list) { + if (sym->sec && !is_rela_section(sym->sec) && + sym->type == STT_NOTYPE && + sym->bind == STB_LOCAL) + list_del(&sym->list); + } + } + return kelf; } diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index 4a4c5a56e..92e3bb68b 100644 --- a/kpatch-build/kpatch-elf.h +++ b/kpatch-build/kpatch-elf.h @@ -31,6 +31,11 @@ #define SHF_RELA_LIVEPATCH 0x00100000 #define SHN_LIVEPATCH 0xff20 +#ifndef __loongarch__ +#define EM_LOONGARCH 258 /* LoongArch */ +#define R_LARCH_64 2 +#endif + /******************* * Data structures * ****************/ @@ -117,6 +122,7 @@ enum architecture { X86_64 = 0x1 << 1, S390 = 0x1 << 2, AARCH64 = 0x1 << 3, + LOONGARCH64 = 0x1 << 4, }; struct kpatch_elf { diff --git a/test/unit/Makefile b/test/unit/Makefile index e3ed7d718..91efbde3c 100644 --- a/test/unit/Makefile +++ b/test/unit/Makefile @@ -1,4 +1,4 @@ -ARCHES ?= aarch64 ppc64le x86_64 +ARCHES ?= loongarch64 aarch64 ppc64le x86_64 .PHONY: all clean submodule-check diff --git a/test/unit/objs b/test/unit/objs index 4ad64a06b..bf4636453 160000 --- a/test/unit/objs +++ b/test/unit/objs @@ -1 +1 @@ -Subproject commit 4ad64a06b6d0a9b779348f04823f82b03e91942e +Subproject commit bf463645367ec892b0c0ba265d2deacbd6289581