mirror of
https://github.com/dynup/kpatch
synced 2025-01-12 16:19:26 +00:00
Merge pull request #283 from jpoimboe/mcount
create-diff-object: create __mcount_loc section
This commit is contained in:
commit
1ebae501ba
@ -414,7 +414,7 @@ kpatch_ftrace_handler(unsigned long ip, unsigned long parent_ip,
|
||||
}
|
||||
done:
|
||||
if (func)
|
||||
regs->ip = func->new_addr;
|
||||
regs->ip = func->new_addr + MCOUNT_INSN_SIZE;
|
||||
|
||||
preempt_enable_notrace();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user