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 <eshatokhin@odin.com>
This commit is contained in:
Evgenii Shatokhin 2015-10-22 17:54:29 +03:00
parent 1cd59c6603
commit 393be6f8fc
1 changed files with 50 additions and 5 deletions

View File

@ -127,6 +127,7 @@ struct symbol {
int include; /* used in the patched elf */ int include; /* used in the patched elf */
int strip; /* used in the output elf */ int strip; /* used in the output elf */
}; };
int has_fentry_call;
}; };
struct rela { 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) 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_create_rela_list(kelf, sec);
} }
kpatch_find_fentry_calls(kelf);
return 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) void kpatch_verify_patchability(struct kpatch_elf *kelf)
{ {
struct section *sec; struct section *sec;
@ -2564,7 +2603,8 @@ void kpatch_create_mcount_sections(struct kpatch_elf *kelf)
nr = 0; nr = 0;
list_for_each_entry(sym, &kelf->symbols, list) 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++; nr++;
/* create text/rela section pair */ /* 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) if (sym->type != STT_FUNC || sym->status == SAME)
continue; 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 */ /* add rela in .rela__mcount_loc to fill in function pointer */
ALLOC_LINK(rela, &relasec->relas); ALLOC_LINK(rela, &relasec->relas);
rela->sym = sym; rela->sym = sym;
@ -2594,7 +2640,8 @@ void kpatch_create_mcount_sections(struct kpatch_elf *kelf)
sym->sec->data->d_buf = newdata; sym->sec->data->d_buf = newdata;
insn = newdata; insn = newdata;
if (insn[0] != 0xf) 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[0] = 0xe8;
insn[1] = 0; insn[1] = 0;
insn[2] = 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, rela = list_first_entry(&sym->sec->rela->relas, struct rela,
list); 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; rela->type = R_X86_64_PC32;
index++; index++;
@ -2923,6 +2967,7 @@ int main(int argc, char *argv[])
*/ */
kpatch_mark_ignored_sections(kelf_patched); kpatch_mark_ignored_sections(kelf_patched);
kpatch_compare_correlated_elements(kelf_patched); kpatch_compare_correlated_elements(kelf_patched);
kpatch_check_fentry_calls(kelf_patched);
kpatch_elf_teardown(kelf_base); kpatch_elf_teardown(kelf_base);
kpatch_elf_free(kelf_base); kpatch_elf_free(kelf_base);