Merge pull request #283 from jpoimboe/mcount

create-diff-object: create __mcount_loc section
This commit is contained in:
Seth Jennings 2014-07-01 11:10:59 -05:00
commit 1ebae501ba
3 changed files with 122 additions and 1 deletions

View File

@ -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();
}

View File

@ -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

View File

@ -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.