allow patched modules to call external functions

When patching a kernel module, if we can't find a needed dynrela symbol,
we currently assume it's exported.  However, it's also possible that
it's provided by another .o in the patch module.  Add support for that.

Fixes #445.
This commit is contained in:
Josh Poimboeuf 2014-10-03 21:47:35 -05:00
parent 731de44ecf
commit f5de932b8d
6 changed files with 68 additions and 21 deletions

View File

@ -534,16 +534,6 @@ static int kpatch_verify_symbol_match(const char *name, unsigned long addr)
return 0;
}
static unsigned long kpatch_find_exported_symbol(const char *name)
{
const struct kernel_symbol *sym;
preempt_disable();
sym = find_symbol(name, NULL, NULL, true, true);
preempt_enable();
return sym ? sym->value : 0;
}
static unsigned long kpatch_find_module_symbol(struct module *mod,
const char *name)
{
@ -564,6 +554,26 @@ static unsigned long kpatch_find_module_symbol(struct module *mod,
return kallsyms_lookup_name(buf);
}
/*
* External symbols are located outside the parent object (where the parent
* object is either vmlinux or the kmod being patched).
*/
static unsigned long kpatch_find_external_symbol(struct kpatch_module *kpmod,
const char *name)
{
const struct kernel_symbol *sym;
/* first, check if it's an exported symbol */
preempt_disable();
sym = find_symbol(name, NULL, NULL, true, true);
preempt_enable();
if (sym)
return sym->value;
/* otherwise check if it's in another .o within the patch module */
return kpatch_find_module_symbol(kpmod->mod, name);
}
static int kpatch_write_relocations(struct kpatch_module *kpmod,
struct kpatch_object *object)
{
@ -584,8 +594,9 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod,
} else {
/* module, dynrela->src needs to be discovered */
if (dynrela->exported)
src = kpatch_find_exported_symbol(dynrela->name);
if (dynrela->external)
src = kpatch_find_external_symbol(kpmod,
dynrela->name);
else
src = kpatch_find_module_symbol(object->mod,
dynrela->name);

View File

@ -53,7 +53,7 @@ struct kpatch_dynrela {
unsigned long type;
const char *name;
int addend;
int exported;
int external;
struct list_head list;
};

View File

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

View File

@ -37,7 +37,7 @@ struct kpatch_patch_dynrela {
unsigned long type;
char *name;
char *objname;
int exported;
int external;
int addend;
};

View File

@ -2213,7 +2213,7 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
struct symbol *strsym;
struct lookup_result result;
struct kpatch_patch_dynrela *dynrelas;
int vmlinux, exported;
int vmlinux, external;
vmlinux = !strcmp(objname, "vmlinux");
@ -2261,7 +2261,7 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
if (kpatch_is_core_module_symbol(rela->sym->name))
continue;
exported = 0;
external = 0;
if (rela->sym->bind == STB_LOCAL) {
/* An unchanged local symbol */
@ -2310,10 +2310,11 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
if (lookup_global_symbol(table, rela->sym->name,
&result))
/*
* Not there, assume it's exported by
* another object.
* Not there, assume it's either an
* exported symbol or provided by
* another .o in the patch module.
*/
exported = 1;
external = 1;
}
log_debug("lookup for %s @ 0x%016lx len %lu\n",
rela->sym->name, result.value, result.size);
@ -2326,7 +2327,7 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
dynrelas[index].src = 0;
dynrelas[index].addend = rela->addend;
dynrelas[index].type = rela->type;
dynrelas[index].exported = exported;
dynrelas[index].external = external;
/* add rela to fill in dest field */
ALLOC_LINK(dynrela, &relasec->relas);

View File

@ -0,0 +1,35 @@
Index: src/fs/nfsd/export.c
===================================================================
--- src.orig/fs/nfsd/export.c
+++ src/fs/nfsd/export.c
@@ -1241,6 +1241,8 @@ static void exp_flags(struct seq_file *m
}
}
+extern char *kpatch_string(void);
+
static int e_show(struct seq_file *m, void *p)
{
struct cache_head *cp = p;
@@ -1250,6 +1252,7 @@ static int e_show(struct seq_file *m, vo
if (p == SEQ_START_TOKEN) {
seq_puts(m, "# Version 1.1\n");
seq_puts(m, "# Path Client(Flags) # IPs\n");
+ seq_puts(m, kpatch_string());
return 0;
}
Index: src/net/netlink/af_netlink.c
===================================================================
--- src.orig/net/netlink/af_netlink.c
+++ src/net/netlink/af_netlink.c
@@ -3228,4 +3228,9 @@ panic:
panic("netlink_init: Cannot allocate nl_table\n");
}
+char *kpatch_string(void)
+{
+ return "# kpatch\n";
+}
+
core_initcall(netlink_proto_init);