mirror of
https://github.com/dynup/kpatch
synced 2025-02-23 13:26:55 +00:00
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:
parent
731de44ecf
commit
f5de932b8d
@ -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);
|
||||
|
@ -53,7 +53,7 @@ struct kpatch_dynrela {
|
||||
unsigned long type;
|
||||
const char *name;
|
||||
int addend;
|
||||
int exported;
|
||||
int external;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ struct kpatch_patch_dynrela {
|
||||
unsigned long type;
|
||||
char *name;
|
||||
char *objname;
|
||||
int exported;
|
||||
int external;
|
||||
int addend;
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
35
test/integration/module-call-external.patch
Normal file
35
test/integration/module-call-external.patch
Normal 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);
|
Loading…
Reference in New Issue
Block a user