From f0d00a9290e7d0c97a6b1bf134384859dd224bf7 Mon Sep 17 00:00:00 2001 From: Sumanth Korikkar Date: Tue, 29 Mar 2022 10:20:32 +0200 Subject: [PATCH] kpatch/s390: Add initial support for kpatch * Add s390 specific checks * Identify patchable functions. * Dont mark expolines as dynrelas. These expolines are always included in final kernel module. This ensures that expoline functions and the kernel itself are not too far apart and avoids out of range relocation. However, this isnt a problem for other functions, as these relocations are performed via R_390_PLT32DBL using gcc option -mno-pic-data-is-text-relative. * s390 maintains expoline tables to locate the expoline thunks. If needed, the module loader could later replace these expoline thunks with normal indirect branch. Each element in the expoline table is of 4 bytes. If there is a changed function in rela.s390_return*, then mark that specific rela symbol as included. This is already performed in the processing of special sections. Hence include it. Signed-off-by: Sumanth Korikkar --- kpatch-build/create-diff-object.c | 78 +++++++++++++++++++++++++++---- kpatch-build/kpatch-build | 19 +++++--- kpatch-build/kpatch-elf.c | 22 +++++++++ 3 files changed, 105 insertions(+), 14 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index bebe3bd..4a76654 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -163,6 +163,8 @@ static bool is_gcc6_localentry_bundled_sym(struct kpatch_elf *kelf, sym->sym.st_value == 8); case X86_64: return false; + case S390: + return false; default: ERROR("unsupported arch"); } @@ -304,8 +306,7 @@ static bool is_dynamic_debug_symbol(struct symbol *sym) static bool is_string_literal_section(struct section *sec) { - return !strncmp(sec->name, ".rodata.", 8) && - strstr(sec->name, ".str1."); + return !strncmp(sec->name, ".rodata.", 8) && strstr(sec->name, ".str"); } /* @@ -2104,6 +2105,17 @@ static int fixup_barrier_nospec_group_size(struct kpatch_elf *kelf, int offset) return 8; } +/* + * .s390_indirect_jump, .s390_indirect_call, .s390_indirect_branches, + * .s390_return_reg, .s390_return_mem contains indirect branch locations. This + * is an array of 32 bit elements. These sections could be used during runtime + * to replace the expolines with the normal indirect jump. + */ +static int s390_expolines_group_size(struct kpatch_elf *kelf, int offset) +{ + return 4; +} + /* * The rela groups in the .fixup section vary in size. The beginning of each * .fixup rela group is referenced by the __ex_table section. To find the size @@ -2157,27 +2169,27 @@ static int fixup_group_size(struct kpatch_elf *kelf, int offset) static struct special_section special_sections[] = { { .name = "__bug_table", - .arch = X86_64 | PPC64, + .arch = X86_64 | PPC64 | S390, .group_size = bug_table_group_size, }, { .name = ".fixup", - .arch = X86_64 | PPC64, + .arch = X86_64 | PPC64 | S390, .group_size = fixup_group_size, }, { .name = "__ex_table", /* must come after .fixup */ - .arch = X86_64 | PPC64, + .arch = X86_64 | PPC64 | S390, .group_size = ex_table_group_size, }, { .name = "__jump_table", - .arch = X86_64 | PPC64, + .arch = X86_64 | PPC64 | S390, .group_size = jump_table_group_size, }, { .name = ".printk_index", - .arch = X86_64 | PPC64, + .arch = X86_64 | PPC64 | S390, .group_size = printk_index_group_size, }, { @@ -2192,7 +2204,7 @@ static struct special_section special_sections[] = { }, { .name = ".altinstructions", - .arch = X86_64, + .arch = X86_64 | S390, .group_size = altinstructions_group_size, }, { @@ -2230,6 +2242,31 @@ static struct special_section special_sections[] = { .arch = PPC64, .group_size = fixup_barrier_nospec_group_size, }, + { + .name = ".s390_return_mem", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + { + .name = ".s390_return_reg", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + { + .name = ".s390_indirect_call", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + { + .name = ".s390_indirect_branches", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + { + .name = ".s390_indirect_jump", + .arch = S390, + .group_size = s390_expolines_group_size, + }, {}, }; @@ -3024,6 +3061,11 @@ static bool kpatch_is_core_module_symbol(char *name) !strcmp(name, "kpatch_shadow_get")); } +static bool is_expoline(struct kpatch_elf *kelf, char *name) +{ + return kelf->arch == S390 && !strncmp(name, "__s390_indirect_jump_r", 22); +} + static int function_ptr_rela(const struct rela *rela) { const struct rela *rela_toc = toc_rela(rela); @@ -3082,6 +3124,13 @@ static bool need_dynrela(struct kpatch_elf *kelf, struct lookup_table *table, if (kpatch_is_core_module_symbol(rela->sym->name)) return false; + /* + * Allow references to s390 expolines to remain as normal relas. They + * will be generated in the module by the kernel module link. + */ + if (is_expoline(kelf, rela->sym->name)) + return false; + if (rela->sym->sec) { /* * Internal symbols usually don't need dynrelas, because they @@ -3548,6 +3597,10 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) rela->type = R_X86_64_PC32; break; } + case S390: { + insn_offset = sym->sym.st_value; + break; + } default: ERROR("unsupported arch"); } @@ -3713,6 +3766,7 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) { struct symbol *sym; struct rela *rela; + unsigned char *insn; list_for_each_entry(sym, &kelf->symbols, list) { if (sym->type != STT_FUNC || !sym->sec || !sym->sec->rela) continue; @@ -3737,6 +3791,14 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) sym->has_func_profiling = 1; break; + case S390: + /* Check for compiler generated fentry nop - jgnop 0 */ + insn = sym->sec->data->d_buf; + if (insn[0] == 0xc0 && insn[1] == 0x04 && + insn[2] == 0x00 && insn[3] == 0x00 && + insn[4] == 0x00 && insn[5] == 0x00) + sym->has_func_profiling = 1; + break; default: ERROR("unsupported arch"); } diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index 7cdae87..02504e6 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -343,12 +343,18 @@ find_special_section_data() { check[e]=true # exception_table_entry # Arch-specific features, without kernel CONFIG_ toggle - if [[ "$ARCH" = "x86_64" ]]; then - check[a]=true # alt_instr - kernel_version_gte 5.10.0 && check[s]=true # static_call_site - elif [[ "$ARCH" = "ppc64le" ]]; then - check[f]=true # fixup_entry - fi + case "$ARCH" in + "x86_64") + check[a]=true # alt_instr + kernel_version_gte 5.10.0 && check[s]=true # static_call_site + ;; + "ppc64le") + check[f]=true # fixup_entry + ;; + "s390x") + check[a]=true # alt_instr + ;; + esac # Kernel CONFIG_ features [[ -n "$CONFIG_PRINTK_INDEX" ]] && check[i]=true # pi_entry @@ -856,6 +862,7 @@ trace_on die "openEuler kernel doesn't have 'CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY' enabled" [[ -z "$CONFIG_DEBUG_INFO" ]] && die "kernel doesn't have 'CONFIG_DEBUG_INFO' enabled" +[[ "$ARCH" = "s390x" ]] && [[ -z "$CONFIG_EXPOLINE_EXTERN" ]] && [[ -n "$CONFIG_EXPOLINE" ]] && die "kernel doesn't have 'CONFIG_EXPOLINE_EXTERN' enabled" # Build variables - Set some defaults, then adjust features # according to .config and kernel version diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 6a43ef5..92881e9 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -140,6 +140,8 @@ unsigned int absolute_rela_type(struct kpatch_elf *kelf) return R_PPC64_ADDR64; case X86_64: return R_X86_64_64; + case S390: + return R_390_64; default: ERROR("unsupported arch"); } @@ -222,6 +224,23 @@ long rela_target_offset(struct kpatch_elf *kelf, struct section *relasec, } else ERROR("unhandled rela type %d", rela->type); break; + case S390: + /* + * For branch and relative load instructions, + * add_off is -2. + */ + if (rela->type == R_390_GOTENT || + rela->type == R_390_PLT32DBL || + rela->type == R_390_PC32DBL) + add_off = -2; + else if (rela->type == R_390_32 || + rela->type == R_390_64 || + rela->type == R_390_PC32 || + rela->type == R_390_PC64) + add_off = 0; + else + ERROR("unhandled rela type %d", rela->type); + break; default: ERROR("unsupported arch\n"); } @@ -479,6 +498,9 @@ struct kpatch_elf *kpatch_elf_open(const char *name) case EM_X86_64: kelf->arch = X86_64; break; + case EM_S390: + kelf->arch = S390; + break; default: ERROR("Unsupported target architecture"); }