diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index b14b613..f17f500 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -3322,9 +3322,6 @@ static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char * } } -#ifdef __powerpc64__ -void kpatch_create_mcount_sections(struct kpatch_elf *kelf) { } -#else /* * This function basically reimplements the functionality of the Linux * recordmcount script, so that patched functions can be recognized by ftrace. @@ -3337,9 +3334,9 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) int nr, index; struct section *sec, *relasec; struct symbol *sym; - struct rela *rela; - void *newdata; - unsigned char *insn; + struct rela *rela, *mcount_rela; + void **funcs; + unsigned long insn_offset; nr = 0; list_for_each_entry(sym, &kelf->symbols, list) @@ -3363,36 +3360,40 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) continue; } - /* 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*)); +#ifdef __x86_64__ + + rela = list_first_entry(&sym->sec->rela->relas, struct rela, list); /* - * Modify the first instruction of the function to "callq - * __fentry__" so that ftrace will be happy. + * For "call fentry", the relocation points to 1 byte past the + * beginning of the instruction. */ - newdata = malloc(sym->sec->data->d_size); - if (!newdata) - ERROR("malloc"); + insn_offset = rela->offset - 1; - 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, - 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) { + void *newdata; + unsigned char *insn; + + /* + * R_X86_64_NONE is only generated by older versions of + * kernel/gcc which use the mcount script. There's a + * NOP instead of a call to fentry. + */ + + /* Make a writable copy of the text section data */ + newdata = malloc(sym->sec->data->d_size); + memcpy(newdata, sym->sec->data->d_buf, sym->sec->data->d_size); + sym->sec->data->d_buf = newdata; + insn = newdata; + + /* + * Replace the NOP with a call to fentry. The fentry + * rela symbol is already there, just need to change + * the relocation type accordingly. + */ + insn = sym->sec->data->d_buf; if (insn[0] != 0xf) - ERROR("%s: unexpected instruction at the start of the function", - sym->name); + ERROR("%s: unexpected instruction at the start of the function", sym->name); insn[0] = 0xe8; insn[1] = 0; insn[2] = 0; @@ -3402,6 +3403,33 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) rela->type = R_X86_64_PC32; } +#else /* __powerpc64__ */ +{ + bool found = false; + + list_for_each_entry(rela, &sym->sec->rela->relas, list) + if (!strcmp(rela->sym->name, "_mcount")) { + found = true; + break; + } + + if (!found) + ERROR("%s: unexpected missing call to _mcount()", __func__); + + insn_offset = rela->offset; +} +#endif + /* + * 'rela' points to the mcount/fentry call. + * + * Create a .rela__mcount_loc entry which also points to it. + */ + ALLOC_LINK(mcount_rela, &relasec->relas); + mcount_rela->sym = sym; + mcount_rela->type = ABSOLUTE_RELA_TYPE; + mcount_rela->addend = insn_offset - sym->sym.st_value; + mcount_rela->offset = (unsigned int) (index * sizeof(*funcs)); + index++; } @@ -3409,7 +3437,6 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) if (index != nr) ERROR("size mismatch in funcs sections"); } -#endif /* * This function strips out symbols that were referenced by changed rela diff --git a/test/unit/objs b/test/unit/objs index 42df5fb..59b5841 160000 --- a/test/unit/objs +++ b/test/unit/objs @@ -1 +1 @@ -Subproject commit 42df5fbc85632c4bab836e104210fe5fd52ae1e5 +Subproject commit 59b5841598dc2d01e750a2b21547ffa243a09894