diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 8450657..64580e9 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -1826,6 +1826,114 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf, sec->sh.sh_size = sec->data->d_size; } +/* + * This function basically reimplements the functionality of the Linux + * recordmcount script, so that patched functions can be recognized by ftrace. + * + * TODO: Eventually we can modify recordmount so that it recognizes our bundled + * sections as valid and does this work for us. + */ +void kpatch_create_mcount_sections(struct kpatch_elf *kelf) +{ + int nr, size, index; + struct section *sec, *relasec; + struct symbol *sym; + struct rela *rela; + void **funcs, *newdata; + unsigned char *insn; + + nr = 0; + list_for_each_entry(sym, &kelf->symbols, list) + if (sym->type == STT_FUNC && sym->status != SAME) + nr++; + + /* create __mcount_loc */ + + /* allocate section resources */ + ALLOC_LINK(sec, &kelf->sections); + size = nr * sizeof(*funcs); + funcs = malloc(size); + if (!funcs) + ERROR("malloc"); + sec->name = "__mcount_loc"; + + /* set data */ + sec->data = malloc(sizeof(*sec->data)); + if (!sec->data) + ERROR("malloc"); + sec->data->d_buf = funcs; + sec->data->d_size = size; + sec->data->d_type = ELF_T_BYTE; + + /* set section header */ + sec->sh.sh_type = SHT_PROGBITS; + sec->sh.sh_entsize = sizeof(*funcs); + sec->sh.sh_addralign = 8; + sec->sh.sh_flags = SHF_ALLOC; + sec->sh.sh_size = size; + + /* create .rela__mcount_loc */ + + /* allocate section resources */ + ALLOC_LINK(relasec, &kelf->sections); + relasec->name = ".rela__mcount_loc"; + relasec->base = sec; + INIT_LIST_HEAD(&relasec->relas); + + /* set data, buffers generated by kpatch_rebuild_rela_section_data() */ + relasec->data = malloc(sizeof(*relasec->data)); + if (!relasec->data) + ERROR("malloc"); + + /* set section header */ + relasec->sh.sh_type = SHT_RELA; + relasec->sh.sh_entsize = sizeof(GElf_Rela); + relasec->sh.sh_addralign = 8; + + /* populate sections */ + index = 0; + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type != STT_FUNC || sym->status == SAME) + 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 = index * sizeof(*funcs); + + /* + * Modify the first instruction of the function to "callq + * __fentry__" so that ftrace will be happy. + */ + 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; + if (insn[0] != 0xf) + ERROR("bad first instruction in %s", sym->name); + insn[0] = 0xe8; + insn[1] = 0; + insn[2] = 0; + insn[3] = 0; + insn[4] = 0; + + rela = list_first_entry(&sym->sec->rela->relas, struct rela, + list); + if (rela->type != R_X86_64_NONE || + strcmp(rela->sym->name, "__fentry__")) + ERROR("bad first rela in %s", sym->sec->rela->name); + rela->type = R_X86_64_PC32; + + index++; + } + + /* sanity check, index should equal nr */ + if (index != nr) + ERROR("size mismatch in funcs sections"); +} + /* * This function strips out symbols that were referenced by changed rela * sections, but the rela entries that referenced them were converted to @@ -2195,6 +2303,8 @@ int main(int argc, char *argv[]) kpatch_create_dynamic_rela_sections(kelf_out, lookup, hint, name); kpatch_build_strings_section_data(kelf_out); + kpatch_create_mcount_sections(kelf_out); + /* * At this point, the set of output sections and symbols is * finalized. Reorder the symbols into linker-compliant diff --git a/kpatch-build/list.h b/kpatch-build/list.h index 825fd95..877aa85 100644 --- a/kpatch-build/list.h +++ b/kpatch-build/list.h @@ -161,6 +161,17 @@ static inline void list_replace(struct list_head *old, #define list_entry(ptr, type, member) \ container_of(ptr, type, member) +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop counter.