create-diff-object: handle reference to end of section

Deal with a special case where gcc needs a pointer to the address at the end of
a data section.

This is usually used with a compare instruction to determine when to end a
loop.  The code doesn't actually dereference the pointer so this is "normal"
and we just replace the section reference with a reference to the last symbol
in the section.

Note that this only catches the issue when it happens at the end of a section.
It can also happen in the middle of a section.  In that case, the wrong symbol
will be associated with the reference.  But that's ok because:

1) This situation only occurs when gcc is trying to get the address of the
   symbol, not the contents of its data; and

2) Because kpatch doesn't allow data sections to change, &(var1+sizeof(var1))
   will always be the same as &var2.

Fixes: #553
This commit is contained in:
Josh Poimboeuf 2015-11-13 16:39:55 -06:00
parent eb54876936
commit fffbb85b81

View File

@ -1369,7 +1369,44 @@ void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
start = sym->sym.st_value;
end = sym->sym.st_value + sym->sym.st_size;
if (rela->addend + add_off < start ||
if (!is_text_section(sym->sec) &&
rela->type == R_X86_64_32S &&
rela->addend == sym->sec->sh.sh_size &&
end == sym->sec->sh.sh_size) {
/*
* A special case where gcc needs a
* pointer to the address at the end of
* a data section.
*
* This is usually used with a compare
* instruction to determine when to end
* a loop. The code doesn't actually
* dereference the pointer so this is
* "normal" and we just replace the
* section reference with a reference
* to the last symbol in the section.
*
* Note that this only catches the
* issue when it happens at the end of
* a section. It can also happen in
* the middle of a section. In that
* case, the wrong symbol will be
* associated with the reference. But
* that's ok because:
*
* 1) This situation only occurs when
* gcc is trying to get the address
* of the symbol, not the contents
* of its data; and
*
* 2) Because kpatch doesn't allow data
* sections to change,
* &(var1+sizeof(var1)) will always
* be the same as &var2.
*/
} else if (rela->addend + add_off < start ||
rela->addend + add_off >= end)
continue;