745 lines
25 KiB
Diff
745 lines
25 KiB
Diff
From d3665b019100715e41b75493d31d51d602ca056a Mon Sep 17 00:00:00 2001
|
|
From: Zhipeng Xie <xiezhipeng1@huawei.com>
|
|
Date: Wed, 26 Feb 2020 07:09:50 -0500
|
|
Subject: [PATCH 02/23] kpatch-build: support build patch for aarch64
|
|
|
|
use R_AARCH64_ABS64 for aarch64
|
|
|
|
exclude line only change for arm64 by compare mov instruction
|
|
except immediate part.
|
|
|
|
add find_special_section_data_arm64 for arm64:
|
|
arm64 kernel have no paravirt_patch_site or orc_entry structure
|
|
in vmlinux, we don't need to check these two struct for arm64.
|
|
|
|
support cross compile for aarch64
|
|
|
|
Signed-off-by: Zhipeng Xie <xiezhipeng1@huawei.com>
|
|
---
|
|
kpatch-build/create-diff-object.c | 276 ++++++++++++++++++++++------
|
|
kpatch-build/create-kpatch-module.c | 25 ++-
|
|
kpatch-build/kpatch-build | 116 +++++++++++-
|
|
kpatch-build/kpatch-gcc | 4 +-
|
|
4 files changed, 352 insertions(+), 69 deletions(-)
|
|
|
|
diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c
|
|
index 2707cdb..b161fee 100644
|
|
--- a/kpatch-build/create-diff-object.c
|
|
+++ b/kpatch-build/create-diff-object.c
|
|
@@ -76,6 +76,32 @@ enum subsection {
|
|
|
|
enum loglevel loglevel = NORMAL;
|
|
|
|
+#ifndef EM_X86_64
|
|
+#define EM_X86_64 62
|
|
+#endif
|
|
+
|
|
+#ifndef EM_AARCH64
|
|
+#define EM_AARCH64 183
|
|
+#endif
|
|
+
|
|
+#ifndef R_AARCH64_ABS64
|
|
+#define R_AARCH64_NONE 0
|
|
+#define R_AARCH64_ABS64 257
|
|
+#define R_AARCH64_CALL26 283
|
|
+#endif
|
|
+
|
|
+static unsigned int arch;
|
|
+static unsigned int absolute_rela_type;
|
|
+
|
|
+static unsigned int arch_of_elf(Elf *elf)
|
|
+{
|
|
+ GElf_Ehdr eh;
|
|
+
|
|
+ if (!gelf_getehdr(elf, &eh))
|
|
+ ERROR("gelf_getehdr");
|
|
+ return eh.e_machine;
|
|
+}
|
|
+
|
|
/*******************
|
|
* Data structures
|
|
* ****************/
|
|
@@ -652,6 +678,67 @@ static int kpatch_line_macro_change_only(struct section *sec)
|
|
return 1;
|
|
}
|
|
#endif
|
|
+#define ARM64_INSTR_LEN 4
|
|
+static int arm64_kpatch_line_macro_change_only(struct section *sec)
|
|
+{
|
|
+ unsigned long start1, start2, size, offset;
|
|
+ struct rela *rela;
|
|
+ int lineonly = 0, found;
|
|
+ unsigned int mov_imm_mask = ((1<<16) - 1)<<5;
|
|
+
|
|
+ if (sec->status != CHANGED ||
|
|
+ is_rela_section(sec) ||
|
|
+ !is_text_section(sec) ||
|
|
+ sec->sh.sh_size != sec->twin->sh.sh_size ||
|
|
+ !sec->rela ||
|
|
+ sec->rela->status != SAME)
|
|
+ return 0;
|
|
+
|
|
+ start1 = (unsigned long)sec->twin->data->d_buf;
|
|
+ start2 = (unsigned long)sec->data->d_buf;
|
|
+ size = sec->sh.sh_size;
|
|
+ for (offset = 0; offset < size; offset += ARM64_INSTR_LEN) {
|
|
+ if (!memcmp((void *)start1 + offset, (void *)start2 + offset,
|
|
+ ARM64_INSTR_LEN))
|
|
+ continue;
|
|
+
|
|
+ /* verify it's a mov immediate to w1 */
|
|
+ if ((*(int *)(start1 + offset) & ~mov_imm_mask) !=
|
|
+ (*(int *)(start2 + offset) & ~mov_imm_mask))
|
|
+ return 0;
|
|
+
|
|
+ found = 0;
|
|
+ list_for_each_entry(rela, &sec->rela->relas, list) {
|
|
+ if (rela->offset < offset + ARM64_INSTR_LEN)
|
|
+ continue;
|
|
+ if (rela->string)
|
|
+ continue;
|
|
+ if (!strncmp(rela->sym->name, "__warned.", 9))
|
|
+ continue;
|
|
+ if (!strncmp(rela->sym->name, "warn_slowpath_", 14) ||
|
|
+ (!strcmp(rela->sym->name, "__warn_printk")) ||
|
|
+ (!strcmp(rela->sym->name, "__might_sleep")) ||
|
|
+ (!strcmp(rela->sym->name, "___might_sleep")) ||
|
|
+ (!strcmp(rela->sym->name, "__might_fault")) ||
|
|
+ (!strcmp(rela->sym->name, "printk")) ||
|
|
+ (!strcmp(rela->sym->name, "lockdep_rcu_suspicious"))) {
|
|
+ found = 1;
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+ if (!found)
|
|
+ return 0;
|
|
+
|
|
+ lineonly = 1;
|
|
+ }
|
|
+
|
|
+ if (!lineonly)
|
|
+ ERROR("no instruction changes detected for changed section %s",
|
|
+ sec->name);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
|
|
static void kpatch_compare_sections(struct list_head *seclist)
|
|
{
|
|
@@ -667,7 +754,16 @@ static void kpatch_compare_sections(struct list_head *seclist)
|
|
|
|
/* exclude WARN-only, might_sleep changes */
|
|
list_for_each_entry(sec, seclist, list) {
|
|
- if (kpatch_line_macro_change_only(sec)) {
|
|
+ int line_only;
|
|
+
|
|
+ if (arch == EM_X86_64)
|
|
+ line_only = kpatch_line_macro_change_only(sec);
|
|
+ else if (arch == EM_AARCH64)
|
|
+ line_only = arm64_kpatch_line_macro_change_only(sec);
|
|
+ else
|
|
+ line_only = 0;
|
|
+
|
|
+ if (line_only) {
|
|
log_debug("reverting macro / line number section %s status to SAME\n",
|
|
sec->name);
|
|
sec->status = SAME;
|
|
@@ -727,6 +823,8 @@ static void kpatch_compare_correlated_symbol(struct symbol *sym)
|
|
if ((sym2->sec->twin && sym2->sec->twin->ignore) ||
|
|
kpatch_subsection_changed(sym1->sec, sym2->sec))
|
|
sym->status = CHANGED;
|
|
+ else if (sym1->name[0] == '$') /* reserved symbols in aarch64 */
|
|
+ log_debug("maping symbols: %s", sym1->name); /* do nothing just ignogre */
|
|
else
|
|
DIFF_FATAL("symbol changed sections: %s", sym1->name);
|
|
}
|
|
@@ -1302,22 +1400,22 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
|
|
continue;
|
|
}
|
|
|
|
-#ifdef __powerpc64__
|
|
add_off = 0;
|
|
-#else
|
|
- if (rela->type == R_X86_64_PC32 ||
|
|
- rela->type == R_X86_64_PLT32) {
|
|
- struct insn insn;
|
|
- rela_insn(sec, rela, &insn);
|
|
- add_off = (unsigned int)((long)insn.next_byte -
|
|
- (long)sec->base->data->d_buf -
|
|
- rela->offset);
|
|
- } else if (rela->type == R_X86_64_64 ||
|
|
- rela->type == R_X86_64_32S)
|
|
- add_off = 0;
|
|
- else
|
|
- continue;
|
|
-#endif
|
|
+ if (arch == EM_X86_64) {
|
|
+ if (rela->type == R_X86_64_PC32 ||
|
|
+ rela->type == R_X86_64_PLT32) {
|
|
+ struct insn insn;
|
|
+
|
|
+ rela_insn(sec, rela, &insn);
|
|
+ add_off = (unsigned int)((long)insn.next_byte -
|
|
+ (long)sec->base->data->d_buf -
|
|
+ rela->offset);
|
|
+ } else if (rela->type == R_X86_64_64 ||
|
|
+ rela->type == R_X86_64_32S)
|
|
+ add_off = 0;
|
|
+ else
|
|
+ continue;
|
|
+ }
|
|
|
|
/*
|
|
* Attempt to replace references to unbundled sections
|
|
@@ -2415,7 +2513,7 @@ static void kpatch_create_kpatch_arch_section(struct kpatch_elf *kelf, char *obj
|
|
/* entries[index].sec */
|
|
ALLOC_LINK(rela, &karch_sec->rela->relas);
|
|
rela->sym = sec->secsym;
|
|
- rela->type = ABSOLUTE_RELA_TYPE;
|
|
+ rela->type = absolute_rela_type;
|
|
rela->addend = 0;
|
|
rela->offset = (unsigned int)(index * sizeof(struct kpatch_arch) + \
|
|
offsetof(struct kpatch_arch, sec));
|
|
@@ -2423,7 +2521,7 @@ static void kpatch_create_kpatch_arch_section(struct kpatch_elf *kelf, char *obj
|
|
/* entries[index].objname */
|
|
ALLOC_LINK(rela, &karch_sec->rela->relas);
|
|
rela->sym = strsym;
|
|
- rela->type = ABSOLUTE_RELA_TYPE;
|
|
+ rela->type = absolute_rela_type;
|
|
rela->addend = offset_of_string(&kelf->strings, objname);
|
|
rela->offset = (unsigned int)(index * sizeof(struct kpatch_arch) + \
|
|
offsetof(struct kpatch_arch, objname));
|
|
@@ -2622,7 +2720,7 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
|
*/
|
|
ALLOC_LINK(rela, &relasec->relas);
|
|
rela->sym = sym;
|
|
- rela->type = ABSOLUTE_RELA_TYPE;
|
|
+ rela->type = absolute_rela_type;
|
|
rela->addend = 0;
|
|
rela->offset = (unsigned int)(index * sizeof(*funcs));
|
|
|
|
@@ -2632,7 +2730,7 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
|
*/
|
|
ALLOC_LINK(rela, &relasec->relas);
|
|
rela->sym = strsym;
|
|
- rela->type = ABSOLUTE_RELA_TYPE;
|
|
+ rela->type = absolute_rela_type;
|
|
rela->addend = offset_of_string(&kelf->strings, sym->name);
|
|
rela->offset = (unsigned int)(index * sizeof(*funcs) +
|
|
offsetof(struct kpatch_patch_func, name));
|
|
@@ -2643,7 +2741,7 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
|
*/
|
|
ALLOC_LINK(rela, &relasec->relas);
|
|
rela->sym = strsym;
|
|
- rela->type = ABSOLUTE_RELA_TYPE;
|
|
+ rela->type = absolute_rela_type;
|
|
rela->addend = objname_offset;
|
|
rela->offset = (unsigned int)(index * sizeof(*funcs) +
|
|
offsetof(struct kpatch_patch_func,objname));
|
|
@@ -2701,7 +2799,10 @@ static int function_ptr_rela(const struct rela *rela)
|
|
rela_toc->addend == (int)rela_toc->sym->sym.st_value &&
|
|
(rela->type == R_X86_64_32S ||
|
|
rela->type == R_PPC64_TOC16_HA ||
|
|
- rela->type == R_PPC64_TOC16_LO_DS));
|
|
+ rela->type == R_PPC64_TOC16_LO_DS ||
|
|
+ rela->type == R_AARCH64_ADR_PREL_PG_HI21 ||
|
|
+ rela->type == R_AARCH64_ADD_ABS_LO12_NC));
|
|
+
|
|
}
|
|
|
|
static int may_need_dynrela(const struct rela *rela)
|
|
@@ -2955,7 +3056,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|
/* add rela to fill in ksyms[index].name field */
|
|
ALLOC_LINK(rela2, &ksym_sec->rela->relas);
|
|
rela2->sym = strsym;
|
|
- rela2->type = ABSOLUTE_RELA_TYPE;
|
|
+ rela2->type = absolute_rela_type;
|
|
rela2->addend = offset_of_string(&kelf->strings, rela->sym->name);
|
|
rela2->offset = (unsigned int)(index * sizeof(*ksyms) + \
|
|
offsetof(struct kpatch_symbol, name));
|
|
@@ -2963,7 +3064,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|
/* add rela to fill in ksyms[index].objname field */
|
|
ALLOC_LINK(rela2, &ksym_sec->rela->relas);
|
|
rela2->sym = strsym;
|
|
- rela2->type = ABSOLUTE_RELA_TYPE;
|
|
+ rela2->type = absolute_rela_type;
|
|
rela2->addend = offset_of_string(&kelf->strings, sym_objname);
|
|
rela2->offset = (unsigned int)(index * sizeof(*ksyms) + \
|
|
offsetof(struct kpatch_symbol, objname));
|
|
@@ -2984,7 +3085,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|
ERROR("can't create dynrela for section %s (symbol %s): no bundled or section symbol",
|
|
sec->name, rela->sym->name);
|
|
|
|
- rela2->type = ABSOLUTE_RELA_TYPE;
|
|
+ rela2->type = absolute_rela_type;
|
|
rela2->addend = rela->offset;
|
|
rela2->offset = (unsigned int)(index * sizeof(*krelas) + \
|
|
offsetof(struct kpatch_relocation, dest));
|
|
@@ -2992,7 +3093,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|
/* add rela to fill in krelas[index].objname field */
|
|
ALLOC_LINK(rela2, &krela_sec->rela->relas);
|
|
rela2->sym = strsym;
|
|
- rela2->type = ABSOLUTE_RELA_TYPE;
|
|
+ rela2->type = absolute_rela_type;
|
|
rela2->addend = offset_of_string(&kelf->strings, objname);
|
|
rela2->offset = (unsigned int)(index * sizeof(*krelas) + \
|
|
offsetof(struct kpatch_relocation, objname));
|
|
@@ -3000,7 +3101,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|
/* add rela to fill in krelas[index].ksym field */
|
|
ALLOC_LINK(rela2, &krela_sec->rela->relas);
|
|
rela2->sym = ksym_sec_sym;
|
|
- rela2->type = ABSOLUTE_RELA_TYPE;
|
|
+ rela2->type = absolute_rela_type;
|
|
rela2->addend = (unsigned int)(index * sizeof(*ksyms));
|
|
rela2->offset = (unsigned int)(index * sizeof(*krelas) + \
|
|
offsetof(struct kpatch_relocation, ksym));
|
|
@@ -3063,7 +3164,7 @@ static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char *
|
|
if (!strcmp(callbackp->name, sec->name)) {
|
|
ALLOC_LINK(rela, &sec->relas);
|
|
rela->sym = strsym;
|
|
- rela->type = ABSOLUTE_RELA_TYPE;
|
|
+ rela->type = absolute_rela_type;
|
|
rela->addend = objname_offset;
|
|
rela->offset = callbackp->offset;
|
|
break;
|
|
@@ -3116,40 +3215,79 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf)
|
|
/* add rela in .rela__mcount_loc to fill in function pointer */
|
|
ALLOC_LINK(rela, &relasec->relas);
|
|
rela->sym = sym;
|
|
- rela->type = R_X86_64_64;
|
|
- rela->addend = 0;
|
|
- rela->offset = (unsigned int)(index * sizeof(void*));
|
|
+ if (arch == EM_X86_64) {
|
|
+ rela->type = R_X86_64_64;
|
|
+ rela->addend = 0;
|
|
+ rela->offset = (unsigned int)(index * sizeof(void *));
|
|
|
|
- /*
|
|
- * Modify the first instruction of the function to "callq
|
|
- * __fentry__" so that ftrace will be happy.
|
|
- */
|
|
- newdata = malloc(sym->sec->data->d_size);
|
|
- if (!newdata)
|
|
- ERROR("malloc");
|
|
+ /*
|
|
+ * Modify the first instruction of the function to "callq
|
|
+ * __fentry__" so that ftrace will be happy.
|
|
+ */
|
|
+ newdata = malloc(sym->sec->data->d_size);
|
|
+ if (!newdata)
|
|
+ ERROR("malloc");
|
|
|
|
- memcpy(newdata, sym->sec->data->d_buf, sym->sec->data->d_size);
|
|
- sym->sec->data->d_buf = newdata;
|
|
- insn = newdata;
|
|
+ memcpy(newdata, sym->sec->data->d_buf, sym->sec->data->d_size);
|
|
+ sym->sec->data->d_buf = newdata;
|
|
+ insn = newdata;
|
|
|
|
- rela = list_first_entry(&sym->sec->rela->relas, struct rela,
|
|
+ rela = list_first_entry(&sym->sec->rela->relas, struct rela,
|
|
list);
|
|
|
|
- /*
|
|
- * R_X86_64_NONE is only generated by older versions of kernel/gcc
|
|
- * which use the mcount script.
|
|
- */
|
|
- if (rela->type == R_X86_64_NONE) {
|
|
- if (insn[0] != 0xf)
|
|
+ /*
|
|
+ * R_X86_64_NONE is only generated by older versions of kernel/gcc
|
|
+ * which use the mcount script.
|
|
+ */
|
|
+ if (rela->type == R_X86_64_NONE) {
|
|
+ if (insn[0] != 0xf)
|
|
+ ERROR("%s: unexpected instruction at the start of the function",
|
|
+ sym->name);
|
|
+ insn[0] = 0xe8;
|
|
+ insn[1] = 0;
|
|
+ insn[2] = 0;
|
|
+ insn[3] = 0;
|
|
+ insn[4] = 0;
|
|
+
|
|
+ rela->type = R_X86_64_PC32;
|
|
+ }
|
|
+ } else if (arch == EM_AARCH64) {
|
|
+ unsigned int *insnp;
|
|
+
|
|
+ rela->type = R_AARCH64_ABS64;
|
|
+ /* bl <__fentry__> is the second insn */
|
|
+ rela->addend = 4;
|
|
+ rela->offset = (unsigned int)(index * sizeof(void *));
|
|
+
|
|
+ newdata = malloc(sym->sec->data->d_size);
|
|
+ if (!newdata)
|
|
+ ERROR("malloc");
|
|
+
|
|
+ memcpy(newdata, sym->sec->data->d_buf, sym->sec->data->d_size);
|
|
+ sym->sec->data->d_buf = newdata;
|
|
+ insnp = newdata;
|
|
+
|
|
+ /*
|
|
+ * mov x9, x30
|
|
+ * nop //function in .text.<func>, so it be replaced with nop by
|
|
+ * recordmcount
|
|
+ *
|
|
+ * mov x30, x9
|
|
+ */
|
|
+ if (insnp[0] != 0xaa1e03e9 || insnp[1] != 0xd503201f
|
|
+ || insnp[2] != 0xaa0903fe)
|
|
ERROR("%s: unexpected instruction at the start of the function",
|
|
- sym->name);
|
|
- insn[0] = 0xe8;
|
|
- insn[1] = 0;
|
|
- insn[2] = 0;
|
|
- insn[3] = 0;
|
|
- insn[4] = 0;
|
|
-
|
|
- rela->type = R_X86_64_PC32;
|
|
+ sym->name);
|
|
+
|
|
+ /* change the nop to bl __fentry__ */
|
|
+ insnp[1] = 0x94000000;
|
|
+ rela = list_first_entry(&sym->sec->rela->relas, struct rela,
|
|
+ list);
|
|
+ rela->type = R_AARCH64_CALL26;
|
|
+ rela->offset = 4;
|
|
+
|
|
+ } else {
|
|
+ ERROR("unsupport arch %d\n", arch);
|
|
}
|
|
|
|
index++;
|
|
@@ -3349,6 +3489,8 @@ int main(int argc, char *argv[])
|
|
char *hint = NULL, *orig_obj, *patched_obj, *parent_name;
|
|
char *parent_symtab, *mod_symvers, *patch_name, *output_obj;
|
|
struct sym_compare_type *base_locals, *sym_comp;
|
|
+ char *no_profiling_calls = NULL;
|
|
+ char *gcc_add_option = NULL, *mlongcall = NULL;
|
|
|
|
arguments.debug = 0;
|
|
argp_parse (&argp, argc, argv, 0, NULL, &arguments);
|
|
@@ -3369,6 +3511,13 @@ int main(int argc, char *argv[])
|
|
|
|
kelf_base = kpatch_elf_open(orig_obj);
|
|
kelf_patched = kpatch_elf_open(patched_obj);
|
|
+ arch = arch_of_elf(kelf_base->elf);
|
|
+ if (arch == EM_X86_64)
|
|
+ absolute_rela_type = R_X86_64_64;
|
|
+ else if (arch == EM_AARCH64)
|
|
+ absolute_rela_type = R_AARCH64_ABS64;
|
|
+ else
|
|
+ ERROR("only arch x86_64 and arm64 be supported\n");
|
|
|
|
kpatch_compare_elf_headers(kelf_base->elf, kelf_patched->elf);
|
|
kpatch_check_program_headers(kelf_base->elf);
|
|
@@ -3407,7 +3556,12 @@ int main(int argc, char *argv[])
|
|
*/
|
|
kpatch_mark_ignored_sections(kelf_patched);
|
|
kpatch_compare_correlated_elements(kelf_patched);
|
|
- kpatch_check_func_profiling_calls(kelf_patched);
|
|
+ no_profiling_calls = getenv("NO_PROFILING_CALLS");
|
|
+ if (!no_profiling_calls)
|
|
+ kpatch_check_func_profiling_calls(kelf_patched);
|
|
+ else
|
|
+ log_debug("NO_PROFILING_CALLS set\n");
|
|
+
|
|
kpatch_elf_teardown(kelf_base);
|
|
kpatch_elf_free(kelf_base);
|
|
|
|
@@ -3467,7 +3621,13 @@ int main(int argc, char *argv[])
|
|
kpatch_create_callbacks_objname_rela(kelf_out, parent_name);
|
|
kpatch_build_strings_section_data(kelf_out);
|
|
|
|
- kpatch_create_mcount_sections(kelf_out);
|
|
+ gcc_add_option = getenv("GCC_ADD_OPTION");
|
|
+ if (gcc_add_option)
|
|
+ mlongcall = strstr(gcc_add_option, "-mlong-calls");
|
|
+ if (arch == EM_AARCH64 && mlongcall)
|
|
+ printf("-mlong-calls found, no need to create mcount section\n");
|
|
+ else
|
|
+ kpatch_create_mcount_sections(kelf_out);
|
|
|
|
/*
|
|
* At this point, the set of output sections and symbols is
|
|
diff --git a/kpatch-build/create-kpatch-module.c b/kpatch-build/create-kpatch-module.c
|
|
index 3d197a7..e8a235d 100644
|
|
--- a/kpatch-build/create-kpatch-module.c
|
|
+++ b/kpatch-build/create-kpatch-module.c
|
|
@@ -31,6 +31,18 @@
|
|
char *childobj;
|
|
enum loglevel loglevel = NORMAL;
|
|
|
|
+static unsigned int arch;
|
|
+static unsigned int absolute_rela_type;
|
|
+
|
|
+static unsigned int arch_of_elf(Elf *elf)
|
|
+{
|
|
+ GElf_Ehdr eh;
|
|
+
|
|
+ if (!gelf_getehdr(elf, &eh))
|
|
+ ERROR("gelf_getehdr");
|
|
+ return eh.e_machine;
|
|
+}
|
|
+
|
|
/*
|
|
* Create .kpatch.dynrelas from .kpatch.relocations and .kpatch.symbols sections
|
|
*
|
|
@@ -102,14 +114,14 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section
|
|
/* dest */
|
|
ALLOC_LINK(rela, &dynsec->rela->relas);
|
|
rela->sym = sym;
|
|
- rela->type = R_X86_64_64;
|
|
+ rela->type = absolute_rela_type;
|
|
rela->addend = dest_offset;
|
|
rela->offset = (unsigned int)(index * sizeof(*dynrelas));
|
|
|
|
/* name */
|
|
ALLOC_LINK(rela, &dynsec->rela->relas);
|
|
rela->sym = strsec->secsym;
|
|
- rela->type = R_X86_64_64;
|
|
+ rela->type = absolute_rela_type;
|
|
rela->addend = name_offset;
|
|
rela->offset = (unsigned int)(index * sizeof(*dynrelas) + \
|
|
offsetof(struct kpatch_patch_dynrela, name));
|
|
@@ -117,7 +129,7 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section
|
|
/* objname */
|
|
ALLOC_LINK(rela, &dynsec->rela->relas);
|
|
rela->sym = strsec->secsym;
|
|
- rela->type = R_X86_64_64;
|
|
+ rela->type = absolute_rela_type;
|
|
rela->addend = objname_offset;
|
|
rela->offset = (unsigned int)(index * sizeof(*dynrelas) + \
|
|
offsetof(struct kpatch_patch_dynrela, objname));
|
|
@@ -200,6 +212,13 @@ int main(int argc, char *argv[])
|
|
childobj = basename(arguments.args[0]);
|
|
|
|
kelf = kpatch_elf_open(arguments.args[0]);
|
|
+ arch = arch_of_elf(kelf->elf);
|
|
+ if (arch == EM_X86_64)
|
|
+ absolute_rela_type = R_X86_64_64;
|
|
+ else if (arch == EM_AARCH64)
|
|
+ absolute_rela_type = R_AARCH64_ABS64;
|
|
+ else
|
|
+ ERROR("only arch x86_64 and arm64 be supported\n");
|
|
|
|
/*
|
|
* Sanity checks:
|
|
diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build
|
|
index ae0733c..17a5e11 100755
|
|
--- a/kpatch-build/kpatch-build
|
|
+++ b/kpatch-build/kpatch-build
|
|
@@ -299,12 +299,72 @@ find_special_section_data_ppc64le() {
|
|
return
|
|
}
|
|
|
|
+find_special_section_data_arm64() {
|
|
+
|
|
+ [[ "$CONFIG_JUMP_LABEL" -eq 0 ]] && AWK_OPTIONS="$AWK_OPTIONS -vskip_j=1"
|
|
+
|
|
+ # If $AWK_OPTIONS are blank gawk would treat "" as a blank script
|
|
+ # shellcheck disable=SC2086
|
|
+ SPECIAL_VARS="$(readelf -wi "$VMLINUX" |
|
|
+ gawk --non-decimal-data $AWK_OPTIONS '
|
|
+ BEGIN { a = b = e = j = c = f = s = i = r = t = h = 0 }
|
|
+
|
|
+ # Set state if name matches
|
|
+ a == 0 && /DW_AT_name.* alt_instr[[:space:]]*$/ {a = 1; next}
|
|
+ b == 0 && /DW_AT_name.* bug_entry[[:space:]]*$/ {b = 1; next}
|
|
+ e == 0 && /DW_AT_name.* exception_table_entry[[:space:]]*$/ {e = 1; next}
|
|
+ j == 0 && /DW_AT_name.* jump_entry[[:space:]]*$/ {j = 1; next}
|
|
+ c == 0 && /DW_AT_name.* klp_func[[:space:]]*$/ {c = 1; next}
|
|
+ t == 0 && /DW_AT_name.* klp_object[[:space:]]*$/ {t = 1; next}
|
|
+
|
|
+ # Reset state unless this abbrev describes the struct size
|
|
+ a == 1 && !/DW_AT_byte_size/ { a = 0; next }
|
|
+ b == 1 && !/DW_AT_byte_size/ { b = 0; next }
|
|
+ e == 1 && !/DW_AT_byte_size/ { e = 0; next }
|
|
+ j == 1 && !/DW_AT_byte_size/ { j = 0; next }
|
|
+ c == 1 && /DW_TAG_structure_type/ { c = 3; next }
|
|
+ t == 1 && /DW_TAG_structure_type/ { t = 3; next }
|
|
+ c == 1 && /DW_AT_name.* force[[:space:]]*$/ {f = 2; next}
|
|
+ c == 1 && /DW_AT_name.* old_sympos[[:space:]]*$/ {s = 2; next}
|
|
+ i == 1 && /DW_AT_name.* immediate[[:space:]]*$/ {i = 2; next}
|
|
+ t == 1 && /DW_AT_name.* relocs[[:space:]]*$/ {r = 2; next}
|
|
+ t == 1 && /DW_AT_name.* hooks_load[[:space:]]*$/ {h = 2; next}
|
|
+
|
|
+ # Now that we know the size, stop parsing for it
|
|
+ a == 1 {printf("export ALT_STRUCT_SIZE=%d\n", $4); a = 2}
|
|
+ b == 1 {printf("export BUG_STRUCT_SIZE=%d\n", $4); b = 2}
|
|
+ e == 1 {printf("export EX_STRUCT_SIZE=%d\n", $4); e = 2}
|
|
+ j == 1 {printf("export JUMP_STRUCT_SIZE=%d\n", $4); j = 2}
|
|
+ f == 2 {printf("export KLP_SUPPORT_FORCE=y\n"); f = 3}
|
|
+ s == 2 {printf("export KLP_SUPPORT_OLD_SYMPOS=y\n"); s = 3}
|
|
+ i == 2 {printf("export KLP_SUPPORT_IMMEDIATE=y\n"); i = 3}
|
|
+ r == 2 {printf("export KLP_SUPPORT_RELOCS=y\n"); r = 3}
|
|
+ h == 2 {printf("export KLP_SUPPORT_LOADHOOKS=y\n"); h = 3}
|
|
+
|
|
+ # Bail out once we have everything
|
|
+ a == 2 && b == 2 && e == 2 && (j == 2 || skip_j) && c == 3 && t == 3 {exit}')"
|
|
+
|
|
+ [[ -n "$SPECIAL_VARS" ]] && eval "$SPECIAL_VARS"
|
|
+
|
|
+ [[ -z "$ALT_STRUCT_SIZE" ]] && die "can't find special struct alt_instr size"
|
|
+ [[ -z "$BUG_STRUCT_SIZE" ]] && die "can't find special struct bug_entry size"
|
|
+ [[ -z "$EX_STRUCT_SIZE" ]] && die "can't find special struct exception_table_entry size"
|
|
+ [[ -z "$JUMP_STRUCT_SIZE" && "$CONFIG_JUMP_LABEL" -ne 0 ]] && die "can't find special struct jump_entry size"
|
|
+
|
|
+ return
|
|
+}
|
|
+
|
|
find_special_section_data() {
|
|
if [[ "$ARCH" = "ppc64le" ]]; then
|
|
find_special_section_data_ppc64le
|
|
return
|
|
fi
|
|
|
|
+ if [[ "$ARCH" = "arm64" ]]; then
|
|
+ find_special_section_data_arm64
|
|
+ return
|
|
+ fi
|
|
+
|
|
[[ "$CONFIG_PARAVIRT" -eq 0 ]] && AWK_OPTIONS="-vskip_p=1"
|
|
[[ "$CONFIG_UNWINDER_ORC" -eq 0 ]] && AWK_OPTIONS="$AWK_OPTIONS -vskip_o=1"
|
|
[[ "$CONFIG_JUMP_LABEL" -eq 0 ]] && AWK_OPTIONS="$AWK_OPTIONS -vskip_j=1"
|
|
@@ -428,6 +488,29 @@ find_kobj() {
|
|
done
|
|
}
|
|
|
|
+arch_export() {
|
|
+ E_MACHINE=$(od -An -j18 -N2 -d $VMLINUX)
|
|
+ if [[ $E_MACHINE -eq 62 ]]; then
|
|
+ export ARCH=x86_64
|
|
+ export ARCH_COMPILE=
|
|
+ export ENDIAN=little
|
|
+ export GCC_ADD_OPTION=
|
|
+ elif [[ $E_MACHINE -eq 183 ]]; then
|
|
+ export ARCH=arm64
|
|
+ if [ $(arch) != "aarch64" ]; then
|
|
+ export ARCH_COMPILE=aarch64-linux-gnu-
|
|
+ fi
|
|
+ export ENDIAN=little
|
|
+ if grep "\-mlong-calls" /lib/modules/$ARCHVERSION/build//Makefile > /dev/null; then
|
|
+ export GCC_ADD_OPTION="-fno-section-anchors -mlong-calls"
|
|
+ else
|
|
+ export GCC_ADD_OPTION="-fno-section-anchors"
|
|
+ fi
|
|
+ else
|
|
+ die "only support arm64 or x86_64 architecture"
|
|
+ fi
|
|
+}
|
|
+
|
|
# Only allow alphanumerics and '_' and '-' in the module name. Everything else
|
|
# is replaced with '-'. Also truncate to 48 chars so the full name fits in the
|
|
# kernel's 56-byte module name array.
|
|
@@ -786,13 +869,13 @@ apply_patches
|
|
remove_patches
|
|
|
|
cp -LR "$DATADIR/patch" "$TEMPDIR" || die
|
|
-
|
|
+arch_export
|
|
if [[ "$ARCH" = "ppc64le" ]]; then
|
|
ARCH_KCFLAGS="-mcmodel=large -fplugin=$PLUGINDIR/ppc64le-plugin.so"
|
|
fi
|
|
|
|
export KCFLAGS="-I$DATADIR/patch -ffunction-sections -fdata-sections \
|
|
- $ARCH_KCFLAGS $DEBUG_KCFLAGS"
|
|
+ $ARCH_KCFLAGS $DEBUG_KCFLAGS ${GCC_ADD_OPTION}"
|
|
|
|
echo "Reading special section data"
|
|
find_special_section_data
|
|
@@ -806,7 +889,7 @@ echo "Building original source"
|
|
unset KPATCH_GCC_TEMPDIR
|
|
# $TARGETS used as list, no quotes.
|
|
# shellcheck disable=SC2086
|
|
-CROSS_COMPILE="$TOOLSDIR/kpatch-gcc " make "-j$CPUS" $TARGETS 2>&1 | logger || die
|
|
+CROSS_COMPILE="$TOOLSDIR/kpatch-gcc $ARCH_COMPILE" make "-j$CPUS" $TARGETS 2>&1 | logger || die
|
|
|
|
echo "Building patched source"
|
|
apply_patches
|
|
@@ -817,7 +900,7 @@ KPATCH_GCC_SRCDIR="$SRCDIR"
|
|
export KPATCH_GCC_SRCDIR
|
|
# $TARGETS used as list, no quotes.
|
|
# shellcheck disable=SC2086
|
|
-CROSS_COMPILE="$TOOLSDIR/kpatch-gcc " \
|
|
+CROSS_COMPILE="$TOOLSDIR/kpatch-gcc $ARCH_COMPILE" \
|
|
KBUILD_MODPOST_WARN=1 \
|
|
make "-j$CPUS" $TARGETS 2>&1 | logger || die
|
|
|
|
@@ -950,6 +1033,26 @@ if "$KPATCH_MODULE"; then
|
|
export KCPPFLAGS="-D__KPATCH_MODULE__"
|
|
fi
|
|
|
|
+if [[ -n "$KLP_SUPPORT_FORCE" ]];then
|
|
+ export KCPPFLAGS="-D__KLP_SUPPORT_FORCE__ $KCPPFLAGS"
|
|
+fi
|
|
+
|
|
+if [[ -n "$KLP_SUPPORT_OLD_SYMPOS" ]];then
|
|
+ export KCPPFLAGS="-DHAVE_SYMPOS $KCPPFLAGS"
|
|
+fi
|
|
+
|
|
+if [[ -n "$KLP_SUPPORT_IMMEDIATE" ]];then
|
|
+ export KCPPFLAGS="-DHAVE_IMMEDIATE $KCPPFLAGS"
|
|
+fi
|
|
+
|
|
+if [[ -n "$KLP_SUPPORT_RELOCS" ]];then
|
|
+ export KCPPFLAGS="-DHAVE_ELF_RELOCS $KCPPFLAGS"
|
|
+fi
|
|
+
|
|
+if [[ -n "$KLP_SUPPORT_LOADHOOKS" ]];then
|
|
+ export KCPPFLAGS="-DHAVE_LOADHOOKS $KCPPFLAGS"
|
|
+fi
|
|
+
|
|
echo "Building patch module: $MODNAME.ko"
|
|
|
|
if [[ -z "$USERSRCDIR" ]] && [[ "$DISTRO" = ubuntu ]]; then
|
|
@@ -962,12 +1065,12 @@ fi
|
|
cd "$TEMPDIR/output" || die
|
|
# $KPATCH_LDFLAGS and result of find used as list, no quotes.
|
|
# shellcheck disable=SC2086,SC2046
|
|
-ld -r $KPATCH_LDFLAGS -o ../patch/tmp_output.o $(find . -name "*.o") 2>&1 | logger || die
|
|
+${ARCH_COMPILE}ld -r $KPATCH_LDFLAGS -o ../patch/tmp_output.o $(find . -name "*.o") 2>&1 | logger || die
|
|
|
|
if "$KPATCH_MODULE"; then
|
|
# Add .kpatch.checksum for kpatch script
|
|
md5sum ../patch/tmp_output.o | awk '{printf "%s\0", $1}' > checksum.tmp || die
|
|
- objcopy --add-section .kpatch.checksum=checksum.tmp --set-section-flags .kpatch.checksum=alloc,load,contents,readonly ../patch/tmp_output.o || die
|
|
+ ${ARCH_COMPILE}objcopy --add-section .kpatch.checksum=checksum.tmp --set-section-flags .kpatch.checksum=alloc,load,contents,readonly ../patch/tmp_output.o || die
|
|
rm -f checksum.tmp
|
|
"$TOOLSDIR"/create-kpatch-module "$TEMPDIR"/patch/tmp_output.o "$TEMPDIR"/patch/output.o 2>&1 | logger 1
|
|
check_pipe_status create-kpatch-module
|
|
@@ -984,6 +1087,7 @@ fi
|
|
KPATCH_BUILD="$KPATCH_BUILD" KPATCH_NAME="$MODNAME" \
|
|
KBUILD_EXTRA_SYMBOLS="$KBUILD_EXTRA_SYMBOLS" \
|
|
KPATCH_LDFLAGS="$KPATCH_LDFLAGS" \
|
|
+CROSS_COMPILE="$ARCH_COMPILE" \
|
|
make 2>&1 | logger || die
|
|
|
|
if ! "$KPATCH_MODULE"; then
|
|
diff --git a/kpatch-build/kpatch-gcc b/kpatch-build/kpatch-gcc
|
|
index 9663290..35d7c1c 100755
|
|
--- a/kpatch-build/kpatch-gcc
|
|
+++ b/kpatch-build/kpatch-gcc
|
|
@@ -13,7 +13,7 @@ fi
|
|
|
|
declare -a args=("$@")
|
|
|
|
-if [[ "$TOOLCHAINCMD" =~ "gcc" ]] ; then
|
|
+if [[ "$TOOLCHAINCMD" =~ "${ARCH_COMPILE}gcc" ]] ; then
|
|
while [ "$#" -gt 0 ]; do
|
|
if [ "$1" = "-o" ]; then
|
|
obj="$2"
|
|
@@ -60,7 +60,7 @@ if [[ "$TOOLCHAINCMD" =~ "gcc" ]] ; then
|
|
fi
|
|
shift
|
|
done
|
|
-elif [[ "$TOOLCHAINCMD" = "ld" ]] ; then
|
|
+elif [[ "$TOOLCHAINCMD" = "${ARCH_COMPILE}ld" ]] ; then
|
|
while [ "$#" -gt 0 ]; do
|
|
if [ "$1" = "-o" ]; then
|
|
obj="$2"
|
|
--
|
|
2.18.1
|
|
|