add correlation and compare support for GROUP sections

GROUP section are rare and are a mechanism in the ELF to indicated that
certain groups of section must be included or excluded (stripped)
together.

It is valid to have more than one of these section with the same
".group" name.  This currently messes up the section correlation code
with correlates based solely on name.

This commit adds additional correlation criteria for GROUP sections;
namely, the section content must be the same.  Changing of groups
sections (i.e. reindexing of the section indexes the GROUP section
includes in their section data) is not supported and will result in a
"new/changed section not included" error.

Signed-off-by: Seth Jennings <sjenning@redhat.com>
This commit is contained in:
Seth Jennings 2014-08-11 14:24:14 -05:00
parent 2990bb790a
commit bf86555e06

View File

@ -101,6 +101,7 @@ struct section {
enum status status;
int include;
int ignore;
int grouped;
union {
struct { /* if (is_rela_section()) */
struct section *base;
@ -681,6 +682,17 @@ void kpatch_correlate_sections(struct list_head *seclist1, struct list_head *sec
list_for_each_entry(sec2, seclist2, list) {
if (strcmp(sec1->name, sec2->name))
continue;
/*
* Group sections must match exactly to be correlated.
* Changed group sections are currently not supported.
*/
if (sec1->sh.sh_type == SHT_GROUP) {
if (sec1->data->d_size != sec2->data->d_size)
continue;
if (memcmp(sec1->data->d_buf, sec2->data->d_buf,
sec1->data->d_size))
continue;
}
sec1->twin = sec2;
sec2->twin = sec1;
/* set initial status, might change */
@ -698,6 +710,11 @@ void kpatch_correlate_symbols(struct list_head *symlist1, struct list_head *syml
list_for_each_entry(sym2, symlist2, list) {
if (!strcmp(sym1->name, sym2->name) &&
sym1->type == sym2->type) {
/* group section symbols must have correlated sections */
if (sym1->sec &&
sym1->sec->sh.sh_type == SHT_GROUP &&
sym1->sec->twin != sym2->sec)
continue;
sym1->twin = sym2;
sym2->twin = sym1;
/* set initial status, might change */
@ -742,6 +759,31 @@ void kpatch_check_program_headers(Elf *elf)
DIFF_FATAL("ELF contains program header");
}
void kpatch_mark_grouped_sections(struct kpatch_elf *kelf)
{
struct section *groupsec, *sec;
unsigned int *data, *end;
list_for_each_entry(groupsec, &kelf->sections, list) {
if (groupsec->sh.sh_type != SHT_GROUP)
continue;
data = groupsec->data->d_buf;
end = groupsec->data->d_buf + groupsec->data->d_size;
data++; /* skip first flag word (e.g. GRP_COMDAT) */
while (data < end) {
sec = find_section_by_index(&kelf->sections, *data);
if (!sec)
ERROR("group section not found");
sec->grouped = 1;
log_debug("marking section %s (%d) as grouped\n",
sec->name, sec->index);
data++;
}
}
}
/*
* When gcc makes compiler optimizations which affect a function's calling
* interface, it mangles the function's name. For example, sysctl_print_dir is
@ -1066,6 +1108,17 @@ void kpatch_verify_patchability(struct kpatch_elf *kelf)
errs++;
}
if (sec->status == CHANGED && sec->grouped) {
log_normal("changed section %s is part of a section group\n",
sec->name);
errs++;
}
if (sec->sh.sh_type == SHT_GROUP && sec->status == NEW) {
log_normal("new/changed group sections are not supported\n");
errs++;
}
/* ensure we aren't including .data.* or .bss.* */
if (sec->include &&
(!strncmp(sec->name, ".data", 5) ||
@ -2516,6 +2569,7 @@ int main(int argc, char *argv[])
kpatch_check_program_headers(kelf_base->elf);
kpatch_check_program_headers(kelf_patched->elf);
kpatch_mark_grouped_sections(kelf_patched);
kpatch_replace_sections_syms(kelf_base);
kpatch_replace_sections_syms(kelf_patched);
kpatch_rename_mangled_functions(kelf_base, kelf_patched);