fix undefined symbols for future loaded modules

When patching module A, if one of the new function's relas reference a
symbol in module B, we currently just leave it as a normal rela.  But if
module B hasn't been loaded yet, the patch module will fail to load due
to the rela's reference to an undefined symbol.

The fix is to convert these relas to dynrelas, which can be resolved
later in the module notifier when A is loaded.

Also added support for the R_X86_64_NONE relocation type, needed for
dynrelas which reference __fentry__.
This commit is contained in:
Josh Poimboeuf 2014-06-18 09:48:51 -05:00
parent f4bba70412
commit 34cc258a31
5 changed files with 51 additions and 23 deletions

View File

@ -526,11 +526,15 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod,
} else {
/* module, dynrela->src needs to be discovered */
src = kpatch_find_module_symbol(object->mod,
dynrela->name);
if (dynrela->exported)
src = (unsigned long)__symbol_get(dynrela->name);
else
src = kpatch_find_module_symbol(object->mod,
dynrela->name);
if (!src) {
pr_err("unable to find symbol '%s' in module '%s'\n",
dynrela->name, object->name);
pr_err("unable to find symbol '%s'\n",
dynrela->name);
return -EINVAL;
}
@ -538,6 +542,8 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod,
}
switch (dynrela->type) {
case R_X86_64_NONE:
continue;
case R_X86_64_PC32:
loc = dynrela->dest;
val = (u32)(dynrela->src + dynrela->addend -
@ -592,6 +598,7 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod,
static int kpatch_unlink_object(struct kpatch_object *object)
{
struct kpatch_func *func;
struct kpatch_dynrela *dynrela;
int ret;
list_for_each_entry(func, &object->funcs, list) {
@ -605,6 +612,10 @@ static int kpatch_unlink_object(struct kpatch_object *object)
}
}
list_for_each_entry(dynrela, &object->dynrelas, list)
if (dynrela->src && dynrela->exported)
__symbol_put(dynrela->name);
if (object->mod)
module_put(object->mod);

View File

@ -54,6 +54,7 @@ struct kpatch_dynrela {
const char *name;
const char *objname;
int addend;
bool exported;
struct list_head list;
};

View File

@ -287,6 +287,7 @@ static int patch_make_dynrelas_list(struct list_head *objects)
dynrela->type = p_dynrela->type;
dynrela->name = p_dynrela->name;
dynrela->objname = p_dynrela->objname;
dynrela->exported = p_dynrela->exported;
dynrela->addend = p_dynrela->addend;
list_add_tail(&dynrela->list, &object->dynrelas);
}

View File

@ -19,6 +19,10 @@
* Contains the structs used for the patch module special sections
*/
#ifndef __KERNEL__
#include <stdbool.h>
#endif
struct kpatch_patch_func {
unsigned long new_addr;
unsigned long new_size;
@ -34,5 +38,6 @@ struct kpatch_patch_dynrela {
unsigned long type;
char *name;
char *objname;
bool exported;
int addend;
};

View File

@ -1654,6 +1654,9 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
struct symbol *strsym;
struct lookup_result result;
struct kpatch_patch_dynrela *dynrelas;
bool vmlinux, exported;
vmlinux = !strcmp(objname, "vmlinux");
/* count rela entries that need to be dynamic */
nr = 0;
@ -1726,48 +1729,55 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
list_for_each_entry_safe(rela, safe, &sec2->relas, list) {
if (rela->sym->sec)
continue;
exported = false;
if (rela->sym->bind == STB_LOCAL) {
/* An unchanged local symbol */
if (lookup_local_symbol(table, rela->sym->name,
hint, &result))
ERROR("lookup_local_symbol %s (%s) needed for %s",
rela->sym->name, hint, sec2->base->name);
}
else if (vmlinux) {
/*
* We have a patch to vmlinux which references
* a global symbol. Use a normal rela for
* exported symbols and a dynrela otherwise.
*/
if (lookup_is_exported_symbol(table, rela->sym->name))
continue;
if (lookup_global_symbol(table, rela->sym->name,
&result))
ERROR("lookup_global_symbol failed for %s, needed for %s\n",
rela->sym->name,
sec2->base->name);
} else {
/*
* Symbol is global, could be an object-level
* global not defined in this file or exported
* by another object.
*/
if (!strcmp(objname, "vmlinux") &&
lookup_is_exported_symbol(table, rela->sym->name))
continue;
/*
* Try to find global symbol. If none is
* found, assume it is exported and no dynrela
* is needed. If the symbol is not exported,
* the final patch module will fail to load.
* We have a patch to a module which references
* a global symbol. First try to find it in
* the module being patched.
*/
if (lookup_global_symbol(table, rela->sym->name,
&result))
&result))
/*
* Symbol is not a kernel object
* global. Assume symbol is exported by
* vmlinux or another kernel module.
* Not there, assume it's exported by
* another object.
*/
continue;
exported = true;
}
log_debug("lookup for %s @ 0x%016lx len %lu\n",
rela->sym->name, result.value, result.size);
/* dest filed in by rela entry below */
if (!strcmp(objname, "vmlinux"))
if (vmlinux)
dynrelas[index].src = result.value;
else
/* for modules, src is discovered at runtime */
dynrelas[index].src = 0;
dynrelas[index].addend = rela->addend;
dynrelas[index].type = rela->type;
dynrelas[index].exported = exported;
/* add rela to fill in dest field */
ALLOC_LINK(dynrela, &relasec->relas);