From 393be6f8fc98dd953d221b88931c9846dc966d8a Mon Sep 17 00:00:00 2001 From: Evgenii Shatokhin Date: Thu, 22 Oct 2015 17:54:29 +0300 Subject: [PATCH] kpatch-build: revisit checking for fentry calls create-diff-object now checks if the original functions have fentry calls. If an original function to be affected by the patch does not have the fentry call, it cannot be patched. Error is reported in that case. kpatch_create_mcount_sections() now also takes into account if a changed or a new function has fentry call. If it does, mcount record is generated for it as before. If a changed or a new function has no fentry call, it is not an error in this case. All this fixes the following issues. 1. If an original function has no fentry call (e.g. a "notrace" function) but the patched function has it, the original function can not be patched, but it would only be detected when applying the patch. 2. kpatch_create_mcount_sections() crashed if a patched function had no relocation at all. I observed such crashes when experimenting with a modified version of the patch "tcp_cubic: better follow cubic curve after idle period" in CentOS 7 x64. Besides that, for a function with the first instruction starting with 0x0f, it would be incorrectly detemined that the function had fentry call. The first bytes of the function would be overwritten in that case. 3. create-diff-object output an error if a new (an added) function had no fentry call. This restriction is not necessary. v2: * Moved the check for fentry calls after the call to kpatch_compare_correlated_elements() and before info about the original ELF file is destroyed. The original symbols are now checked there (via sym->twin) rather than the patched ones. * Removed an excessive error check. Signed-off-by: Evgenii Shatokhin --- kpatch-build/create-diff-object.c | 55 ++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index a5aa2b7..392fe48 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -127,6 +127,7 @@ struct symbol { int include; /* used in the patched elf */ int strip; /* used in the output elf */ }; + int has_fentry_call; }; struct rela { @@ -472,6 +473,24 @@ void kpatch_create_symbol_list(struct kpatch_elf *kelf) } +/* Check which functions have fentry calls; save this info for later use. */ +static void kpatch_find_fentry_calls(struct kpatch_elf *kelf) +{ + struct symbol *sym; + struct rela *rela; + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type != STT_FUNC || !sym->sec->rela) + continue; + + rela = list_first_entry(&sym->sec->rela->relas, struct rela, + list); + if (rela->type != R_X86_64_NONE || + strcmp(rela->sym->name, "__fentry__")) + continue; + + sym->has_fentry_call = 1; + } +} struct kpatch_elf *kpatch_elf_open(const char *name) { @@ -510,6 +529,7 @@ struct kpatch_elf *kpatch_elf_open(const char *name) kpatch_create_rela_list(kelf, sec); } + kpatch_find_fentry_calls(kelf); return kelf; } @@ -1338,6 +1358,25 @@ next: } } +static void kpatch_check_fentry_calls(struct kpatch_elf *kelf) +{ + struct symbol *sym; + int errs = 0; + + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type != STT_FUNC || sym->status != CHANGED) + continue; + if (!sym->twin->has_fentry_call) { + log_normal("function %s has no fentry call, unable to patch\n", + sym->name); + errs++; + } + } + + if (errs) + DIFF_FATAL("%d function(s) can not be patched", errs); +} + void kpatch_verify_patchability(struct kpatch_elf *kelf) { struct section *sec; @@ -2564,7 +2603,8 @@ void kpatch_create_mcount_sections(struct kpatch_elf *kelf) nr = 0; list_for_each_entry(sym, &kelf->symbols, list) - if (sym->type == STT_FUNC && sym->status != SAME) + if (sym->type == STT_FUNC && sym->status != SAME && + sym->has_fentry_call) nr++; /* create text/rela section pair */ @@ -2578,6 +2618,12 @@ void kpatch_create_mcount_sections(struct kpatch_elf *kelf) if (sym->type != STT_FUNC || sym->status == SAME) continue; + if (!sym->has_fentry_call) { + log_debug("function %s has no fentry call, no mcount record is needed\n", + sym->name); + continue; + } + /* add rela in .rela__mcount_loc to fill in function pointer */ ALLOC_LINK(rela, &relasec->relas); rela->sym = sym; @@ -2594,7 +2640,8 @@ void kpatch_create_mcount_sections(struct kpatch_elf *kelf) sym->sec->data->d_buf = newdata; insn = newdata; if (insn[0] != 0xf) - ERROR("function '%s' has no fentry call; unable to patch", sym->name); + ERROR("%s: unexpected instruction at the start of the function", + sym->name); insn[0] = 0xe8; insn[1] = 0; insn[2] = 0; @@ -2603,9 +2650,6 @@ void kpatch_create_mcount_sections(struct kpatch_elf *kelf) rela = list_first_entry(&sym->sec->rela->relas, struct rela, list); - if (rela->type != R_X86_64_NONE || - strcmp(rela->sym->name, "__fentry__")) - ERROR("function '%s' has no fentry call; unable to patch", sym->name); rela->type = R_X86_64_PC32; index++; @@ -2923,6 +2967,7 @@ int main(int argc, char *argv[]) */ kpatch_mark_ignored_sections(kelf_patched); kpatch_compare_correlated_elements(kelf_patched); + kpatch_check_fentry_calls(kelf_patched); kpatch_elf_teardown(kelf_base); kpatch_elf_free(kelf_base);