mirror of
https://github.com/dynup/kpatch
synced 2025-04-26 04:57:58 +00:00
create-diff-object: create __mcount_loc section
For ftrace to be able to trace a patched function, it requires that the __mcount_loc section contains a pointer to the function, and that the first instruction of the function is "callq __fentry__". Normally that work is done by the recordmcount script, but it ignores functions that aren't in a few standard sections (.text and a few others).
This commit is contained in:
parent
e154296e0d
commit
79bd9f03ee
kpatch-build
@ -1826,6 +1826,114 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
|
|||||||
sec->sh.sh_size = sec->data->d_size;
|
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
|
* This function strips out symbols that were referenced by changed rela
|
||||||
* sections, but the rela entries that referenced them were converted to
|
* 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_create_dynamic_rela_sections(kelf_out, lookup, hint, name);
|
||||||
kpatch_build_strings_section_data(kelf_out);
|
kpatch_build_strings_section_data(kelf_out);
|
||||||
|
|
||||||
|
kpatch_create_mcount_sections(kelf_out);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* At this point, the set of output sections and symbols is
|
* At this point, the set of output sections and symbols is
|
||||||
* finalized. Reorder the symbols into linker-compliant
|
* 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) \
|
#define list_entry(ptr, type, member) \
|
||||||
container_of(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
|
* list_for_each_entry - iterate over list of given type
|
||||||
* @pos: the type * to use as a loop counter.
|
* @pos: the type * to use as a loop counter.
|
||||||
|
Loading…
Reference in New Issue
Block a user