From 6c5058658d9e7ad9ba9f54b451461aaab4e5c4d1 Mon Sep 17 00:00:00 2001 From: George Guo Date: Thu, 8 Aug 2024 09:04:22 +0800 Subject: [PATCH 1/8] kpatch/LoongArch: Add LoongArch specific features Add section alt_instr check support for LoongArch. Signed-off-by: George Guo --- kpatch-build/kpatch-build | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index bc112fc9..a9ea853d 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -393,6 +393,9 @@ find_special_section_data() { "s390x") check[a]=true # alt_instr ;; + "loongarch64") + check[a]=true # alt_instr + ;; esac # Kernel CONFIG_ features From 9a3d0e9b4e140156d3ee809a272fb649e1629606 Mon Sep 17 00:00:00 2001 From: George Guo Date: Sat, 17 Aug 2024 13:04:22 +0800 Subject: [PATCH 2/8] kpatch/LoongArch: Add initial support for kpatch Add initial support for LoongArch. Signed-off-by: George Guo --- kpatch-build/create-diff-object.c | 17 ++++++++++++----- kpatch-build/kpatch-elf.c | 7 +++++++ kpatch-build/kpatch-elf.h | 1 + 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 25710e92..66f229d9 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -180,6 +180,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"); } @@ -689,6 +691,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) @@ -2415,22 +2422,22 @@ static bool static_call_sites_group_filter(struct lookup_table *lookup, static struct special_section special_sections[] = { { .name = "__bug_table", - .arch = X86_64 | PPC64 | S390, + .arch = X86_64 | PPC64 | S390 | LOONGARCH64, .group_size = bug_table_group_size, }, { .name = ".fixup", - .arch = X86_64 | PPC64 | S390, + .arch = X86_64 | PPC64 | S390 | LOONGARCH64, .group_size = fixup_group_size, }, { .name = "__ex_table", /* must come after .fixup */ - .arch = X86_64 | PPC64 | S390, + .arch = X86_64 | PPC64 | S390 | LOONGARCH64, .group_size = ex_table_group_size, }, { .name = "__jump_table", - .arch = X86_64 | PPC64 | S390, + .arch = X86_64 | PPC64 | S390 | LOONGARCH64, .group_size = jump_table_group_size, .group_filter = jump_table_group_filter, }, @@ -2451,7 +2458,7 @@ static struct special_section special_sections[] = { }, { .name = ".altinstructions", - .arch = X86_64 | S390, + .arch = X86_64 | S390 | LOONGARCH64, .group_size = altinstructions_group_size, }, { diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 374d424c..b712b114 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -142,6 +142,8 @@ unsigned int absolute_rela_type(struct kpatch_elf *kelf) return R_X86_64_64; case S390: return R_390_64; + case LOONGARCH64: + return R_LARCH_64; default: ERROR("unsupported arch"); } @@ -206,6 +208,7 @@ long rela_target_offset(struct kpatch_elf *kelf, struct section *relasec, switch(kelf->arch) { case PPC64: + case LOONGARCH64: add_off = 0; break; case X86_64: @@ -261,6 +264,7 @@ unsigned int insn_length(struct kpatch_elf *kelf, void *addr) return decoded_insn.length; case PPC64: + case LOONGARCH64: return 4; case S390: @@ -593,6 +597,9 @@ struct kpatch_elf *kpatch_elf_open(const char *name) case EM_S390: kelf->arch = S390; break; + case EM_LOONGARCH: + kelf->arch = LOONGARCH64; + break; default: ERROR("Unsupported target architecture"); } diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index e32209b7..f881ead4 100644 --- a/kpatch-build/kpatch-elf.h +++ b/kpatch-build/kpatch-elf.h @@ -115,6 +115,7 @@ enum architecture { PPC64 = 0x1 << 0, X86_64 = 0x1 << 1, S390 = 0x1 << 2, + LOONGARCH64 = 0x1 << 3, }; struct kpatch_elf { From 411bf6c03a40e0cbd4230b3e38b6d07b918187ab Mon Sep 17 00:00:00 2001 From: George Guo Date: Sun, 8 Sep 2024 12:17:05 +0800 Subject: [PATCH 3/8] kpatch/LoongArch: create and process section __patchable_function_entries Generate 2 NOPs right at the beginning of each function with -fpatchable-function-entry=2 in LoongArch. So here create section __patchable_function_entries and check this situation. Co-developed-by: zhanghongchen Signed-off-by: George Guo --- kpatch-build/create-diff-object.c | 35 ++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 66f229d9..76ac3b79 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -3706,7 +3706,10 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) nr++; /* create text/rela section pair */ - sec = create_section_pair(kelf, "__mcount_loc", sizeof(void*), nr); + if (kelf->arch == LOONGARCH64) + sec = create_section_pair(kelf, "__patchable_function_entries", sizeof(void *), nr); + else + sec = create_section_pair(kelf, "__mcount_loc", sizeof(void *), nr); relasec = sec->rela; /* populate sections */ @@ -3786,6 +3789,20 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) insn_offset = sym->sym.st_value; break; } + case LOONGARCH64: { +#define LOONGARCH_NOP 0x03400000 + bool found = false; + unsigned int *insn = sym->sec->data->d_buf + sym->sym.st_value; + + if (*insn == LOONGARCH_NOP && *(insn + 1) == LOONGARCH_NOP) + found = true; + + if (!found) + ERROR("%s: unexpected instruction at the start of the function", sym->name); + + insn_offset = 0; + break; + } default: ERROR("unsupported arch"); } @@ -3991,6 +4008,22 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) insn[4] == 0x00 && insn[5] == 0x00) sym->has_func_profiling = 1; break; + case LOONGARCH64: + struct section *sec; + + sec = find_section_by_name(&kelf->sections, + "__patchable_function_entries"); + if (sec) { + list_for_each_entry(rela, &sec->rela->relas, list) { + if (rela->sym->sec == sym->sec && + (rela->sym->sym.st_value + + rela->addend) == sym->sym.st_value) { + sym->has_func_profiling = 1; + break; + } + } + } + break; default: ERROR("unsupported arch"); } From bd6e184186aa3095c6949e22ce8c6a67dc1e3900 Mon Sep 17 00:00:00 2001 From: George Guo Date: Thu, 12 Sep 2024 14:17:05 +0800 Subject: [PATCH 4/8] kpatch/LoongArch: change local labels with sections symbols Here fix error like: "tcp.o: symbol changed sections: .LBB7266. create-diff-object: unreconcilable difference". Due to LoongArch GCC generating local labels such as .LBB7266, it is difficult to compare the modified sections in the corresponding object files of the two files before and after the patch, so change them with sections symbols in rela section, and delete them in other sections. Co-developed-by: zhanghongchen Signed-off-by: George Guo --- kpatch-build/kpatch-elf.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index b712b114..f5667ca0 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -336,6 +336,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, @@ -615,6 +630,18 @@ struct kpatch_elf *kpatch_elf_open(const char *name) kpatch_create_rela_list(kelf, relasec); } + 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; } From 4ce275484aa7684057a03e2c5e78c81973726c42 Mon Sep 17 00:00:00 2001 From: George Guo Date: Thu, 31 Oct 2024 17:48:07 +0800 Subject: [PATCH 5/8] kpatch/LoongArch: fix ld error Take tcp.o as an example. An ld error occurs as follows: "ld: __patchable_function_entries has both ordered and unordered sections" when linking patch-hook.o and output.o. The link error is triggered by running the command: "ld -r --unique=.parainstructions --unique=.altinstructions -o patch/tmp_output.o output/net/ipv4/tcp.o". With -fpatchable-function-entry, LoongArch GCC sets WAL flags for section __patchable_function_entries in object file (which includes patch-hook.o) by default. Meanwhile, Kpatch sets A flags for output.o in create_section_pair(). To resolve the ld error, we set sh_flags = WAL. Signed-off-by: George Guo --- kpatch-build/create-diff-object.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 76ac3b79..97a5d201 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -3706,10 +3706,22 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) nr++; /* create text/rela section pair */ - if (kelf->arch == LOONGARCH64) + if (kelf->arch == LOONGARCH64) { sec = create_section_pair(kelf, "__patchable_function_entries", sizeof(void *), nr); - else + /* + * With -fpatchable-function-entry, LoongArch GCC sets WAL flags + * for section __patchable_function_entries in object file + * (certainly include patch-hook.o) by default. + * Kpatch sets A flags for output.o in create_section_pair(). + * So here assign sh_flags = WAL to fix error: + * "ld: __patchable_function_entries has both ordered and + * unordered sections" when link patch-hook.o and output.o. + */ + sec->sh.sh_flags = SHF_WRITE | SHF_ALLOC | SHF_LINK_ORDER; + sec->sh.sh_link = 1; + } else sec = create_section_pair(kelf, "__mcount_loc", sizeof(void *), nr); + relasec = sec->rela; /* populate sections */ From 06f87de0adc960247266bc804a62887b6ced3dbc Mon Sep 17 00:00:00 2001 From: George Guo Date: Thu, 7 Nov 2024 11:13:50 +0800 Subject: [PATCH 6/8] kpatch/LoongArch: enable kpatch build Since kpatch now supports LoongArch basically, enable the build. Signed-off-by: George Guo --- kpatch-build/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kpatch-build/Makefile b/kpatch-build/Makefile index 4e964b7f..0f019698 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),) +ifeq ($(filter $(ARCH),s390x x86_64 ppc64le loongarch64),) $(error Unsupported architecture ${ARCH}, check https://github.com/dynup/kpatch/#supported-architectures) endif From 1f527405a23c02343e6f2bc57278e733cfd2bbc3 Mon Sep 17 00:00:00 2001 From: George Guo Date: Thu, 7 Nov 2024 14:26:28 +0800 Subject: [PATCH 7/8] kpatch/LoongArch: short circuit for patchable sections Omit patchable sections compare processing, as we will rebuild it. Signed-off-by: George Guo --- kpatch-build/create-diff-object.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 97a5d201..3575204b 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -617,9 +617,11 @@ static void kpatch_compare_correlated_section(struct section *sec) !is_text_section(sec1))) DIFF_FATAL("%s section header details differ from %s", sec1->name, sec2->name); - /* Short circuit for mcount sections, we rebuild regardless */ + /* Short circuit for mcount/patchable sections, we rebuild regardless */ if (!strcmp(sec->name, ".rela__mcount_loc") || - !strcmp(sec->name, "__mcount_loc")) { + !strcmp(sec->name, "__mcount_loc") || + !strcmp(sec->name, ".rela__patchable_function_entries") || + !strcmp(sec->name, "__patchable_function_entries")) { sec->status = SAME; goto out; } From 58267765c82531a4408a1e792c47b4d4927fce7c Mon Sep 17 00:00:00 2001 From: George Guo Date: Fri, 8 Nov 2024 15:03:26 +0800 Subject: [PATCH 8/8] kpatch/LoongArch: fix build error on non-LoongArch architectures Added conditional compilation to prevent 'R_LARCH_64' and 'EM_LOONGARCH' from being referenced on x86 and other non-LoongArch architectures. This ensures the code works across different architectures without errors. Signed-off-by: George Guo --- kpatch-build/kpatch-elf.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index f881ead4..f998aef0 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 * ****************/