mirror of https://github.com/dynup/kpatch
kpatch-build: Look for local static variables in child functions
A symbol in the original object might get split in several sub-functions in the patched object, which can themselves be bundled (and use a separate rela section). References to local static variables from the original function, might have been moved in one of the sub-functions in the patched object. Look for references to local static variables in the rela section of child symbols in the patched object. Signed-off-by: Julien Thierry <jthierry@redhat.com>
This commit is contained in:
parent
42128ff78c
commit
b548ba153f
|
@ -757,6 +757,13 @@ static int kpatch_subsection_changed(struct section *sec1, struct section *sec2)
|
|||
return kpatch_subsection_type(sec1) != kpatch_subsection_type(sec2);
|
||||
}
|
||||
|
||||
static struct symbol *kpatch_get_correlated_parent(struct symbol *sym)
|
||||
{
|
||||
while (sym->parent && !sym->parent->twin)
|
||||
sym = sym->parent;
|
||||
return sym->parent;
|
||||
}
|
||||
|
||||
static void kpatch_compare_correlated_symbol(struct symbol *sym)
|
||||
{
|
||||
struct symbol *sym1 = sym, *sym2 = sym->twin;
|
||||
|
@ -1008,21 +1015,13 @@ static char *kpatch_section_function_name(struct section *sec)
|
|||
return sec->sym ? sec->sym->name : sec->name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a static local variable symbol and a rela section which references it
|
||||
* in the base object, find a corresponding usage of a similarly named symbol
|
||||
* in the patched object.
|
||||
*/
|
||||
static struct symbol *kpatch_find_static_twin(struct section *sec,
|
||||
struct symbol *sym)
|
||||
static struct symbol *kpatch_find_uncorrelated_rela(struct section *rela_sec,
|
||||
struct symbol *sym)
|
||||
{
|
||||
struct rela *rela, *rela_toc;
|
||||
|
||||
if (!sec->twin)
|
||||
return NULL;
|
||||
|
||||
/* find the patched object's corresponding variable */
|
||||
list_for_each_entry(rela, &sec->twin->relas, list) {
|
||||
list_for_each_entry(rela, &rela_sec->relas, list) {
|
||||
struct symbol *patched_sym;
|
||||
|
||||
rela_toc = toc_rela(rela);
|
||||
|
@ -1048,6 +1047,67 @@ static struct symbol *kpatch_find_static_twin(struct section *sec,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static struct symbol *kpatch_find_static_twin_in_children(struct symbol *parent,
|
||||
struct symbol *sym)
|
||||
{
|
||||
struct symbol *child;
|
||||
|
||||
list_for_each_entry(child, &parent->children, subfunction_node) {
|
||||
struct symbol *res;
|
||||
|
||||
/* Only look in children whose rela section differ from the parent's */
|
||||
if (child->sec->rela == parent->sec->rela || !child->sec->rela)
|
||||
continue;
|
||||
|
||||
res = kpatch_find_uncorrelated_rela(child->sec->rela, sym);
|
||||
/* Need to go deeper */
|
||||
if (!res)
|
||||
res = kpatch_find_static_twin_in_children(child, sym);
|
||||
if (res != NULL)
|
||||
return res;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a static local variable symbol and a rela section which references it
|
||||
* in the base object, find a corresponding usage of a similarly named symbol
|
||||
* in the patched object.
|
||||
*/
|
||||
static struct symbol *kpatch_find_static_twin(struct section *sec,
|
||||
struct symbol *sym)
|
||||
{
|
||||
struct symbol *res;
|
||||
|
||||
if (!sec->twin && sec->base->sym) {
|
||||
struct symbol *parent = NULL;
|
||||
|
||||
/*
|
||||
* The static twin might have been in a .part. symbol in the
|
||||
* original object that got removed in the patched object.
|
||||
*/
|
||||
parent = kpatch_get_correlated_parent(sec->base->sym);
|
||||
if (parent)
|
||||
sec = parent->sec->rela;
|
||||
|
||||
}
|
||||
|
||||
if (!sec->twin)
|
||||
return NULL;
|
||||
|
||||
res = kpatch_find_uncorrelated_rela(sec->twin, sym);
|
||||
if (res != NULL)
|
||||
return res;
|
||||
|
||||
/* Look if reference might have moved to child functions' sections */
|
||||
if (sec->twin->base->sym)
|
||||
return kpatch_find_static_twin_in_children(sec->twin->base->sym,
|
||||
sym);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int kpatch_is_normal_static_local(struct symbol *sym)
|
||||
{
|
||||
if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL)
|
||||
|
@ -1062,6 +1122,35 @@ static int kpatch_is_normal_static_local(struct symbol *sym)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static struct rela *kpatch_find_static_twin_ref(struct section *rela_sec, struct symbol *sym)
|
||||
{
|
||||
struct rela *rela;
|
||||
|
||||
list_for_each_entry(rela, &rela_sec->relas, list) {
|
||||
if (rela->sym == sym->twin)
|
||||
return rela;
|
||||
}
|
||||
|
||||
/* Reference to static variable might have moved to child function section */
|
||||
if (rela_sec->base->sym) {
|
||||
struct symbol *parent = rela_sec->base->sym;
|
||||
struct symbol *child;
|
||||
|
||||
list_for_each_entry(child, &parent->children, subfunction_node) {
|
||||
/* Only look in children whose rela section differ from the parent's */
|
||||
if (child->sec->rela == parent->sec->rela ||
|
||||
!child->sec->rela)
|
||||
continue;
|
||||
|
||||
rela = kpatch_find_static_twin_ref(child->sec->rela, sym);
|
||||
if (rela)
|
||||
return rela;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* gcc renames static local variables by appending a period and a number. For
|
||||
* example, __foo could be renamed to __foo.31452. Unfortunately this number
|
||||
|
@ -1091,8 +1180,8 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *base,
|
|||
{
|
||||
struct symbol *sym, *patched_sym;
|
||||
struct section *sec;
|
||||
struct rela *rela, *rela2;
|
||||
int bundled, patched_bundled, found;
|
||||
struct rela *rela;
|
||||
int bundled, patched_bundled;
|
||||
|
||||
/*
|
||||
* First undo the correlations for all static locals. Two static
|
||||
|
@ -1192,28 +1281,29 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *base,
|
|||
continue;
|
||||
|
||||
list_for_each_entry(rela, &sec->relas, list) {
|
||||
struct section *target_sec = sec;
|
||||
|
||||
sym = rela->sym;
|
||||
if (!kpatch_is_normal_static_local(sym))
|
||||
continue;
|
||||
|
||||
if (!sym->twin || !sec->twin)
|
||||
DIFF_FATAL("reference to static local variable %s in %s was removed",
|
||||
sym->name,
|
||||
kpatch_section_function_name(sec));
|
||||
if (!sec->twin && sec->base->sym) {
|
||||
struct symbol *parent = NULL;
|
||||
|
||||
found = 0;
|
||||
list_for_each_entry(rela2, &sec->twin->relas, list) {
|
||||
if (rela2->sym == sym->twin) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
parent = kpatch_get_correlated_parent(sec->base->sym);
|
||||
if (parent)
|
||||
target_sec = parent->sec->rela;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
if (!sym->twin || !target_sec->twin)
|
||||
DIFF_FATAL("reference to static local variable %s in %s was removed",
|
||||
sym->name,
|
||||
kpatch_section_function_name(target_sec));
|
||||
|
||||
if (!kpatch_find_static_twin_ref(target_sec->twin, sym))
|
||||
DIFF_FATAL("static local %s has been correlated with %s, but patched %s is missing a reference to it",
|
||||
sym->name, sym->twin->name,
|
||||
kpatch_section_function_name(sec->twin));
|
||||
kpatch_section_function_name(target_sec->twin));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue