mirror of
https://github.com/dynup/kpatch
synced 2025-05-05 01:17:57 +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
kmod
kpatch-build
test/integration
@ -534,16 +534,6 @@ static int kpatch_verify_symbol_match(const char *name, unsigned long addr)
|
|||||||
return 0;
|
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,
|
static unsigned long kpatch_find_module_symbol(struct module *mod,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
@ -564,6 +554,26 @@ static unsigned long kpatch_find_module_symbol(struct module *mod,
|
|||||||
return kallsyms_lookup_name(buf);
|
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,
|
static int kpatch_write_relocations(struct kpatch_module *kpmod,
|
||||||
struct kpatch_object *object)
|
struct kpatch_object *object)
|
||||||
{
|
{
|
||||||
@ -584,8 +594,9 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod,
|
|||||||
} else {
|
} else {
|
||||||
/* module, dynrela->src needs to be discovered */
|
/* module, dynrela->src needs to be discovered */
|
||||||
|
|
||||||
if (dynrela->exported)
|
if (dynrela->external)
|
||||||
src = kpatch_find_exported_symbol(dynrela->name);
|
src = kpatch_find_external_symbol(kpmod,
|
||||||
|
dynrela->name);
|
||||||
else
|
else
|
||||||
src = kpatch_find_module_symbol(object->mod,
|
src = kpatch_find_module_symbol(object->mod,
|
||||||
dynrela->name);
|
dynrela->name);
|
||||||
|
@ -53,7 +53,7 @@ struct kpatch_dynrela {
|
|||||||
unsigned long type;
|
unsigned long type;
|
||||||
const char *name;
|
const char *name;
|
||||||
int addend;
|
int addend;
|
||||||
int exported;
|
int external;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ static int patch_make_dynrelas_list(struct list_head *objects)
|
|||||||
dynrela->src = p_dynrela->src;
|
dynrela->src = p_dynrela->src;
|
||||||
dynrela->type = p_dynrela->type;
|
dynrela->type = p_dynrela->type;
|
||||||
dynrela->name = p_dynrela->name;
|
dynrela->name = p_dynrela->name;
|
||||||
dynrela->exported = p_dynrela->exported;
|
dynrela->external = p_dynrela->external;
|
||||||
dynrela->addend = p_dynrela->addend;
|
dynrela->addend = p_dynrela->addend;
|
||||||
list_add_tail(&dynrela->list, &object->dynrelas);
|
list_add_tail(&dynrela->list, &object->dynrelas);
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ struct kpatch_patch_dynrela {
|
|||||||
unsigned long type;
|
unsigned long type;
|
||||||
char *name;
|
char *name;
|
||||||
char *objname;
|
char *objname;
|
||||||
int exported;
|
int external;
|
||||||
int addend;
|
int addend;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2213,7 +2213,7 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
|
|||||||
struct symbol *strsym;
|
struct symbol *strsym;
|
||||||
struct lookup_result result;
|
struct lookup_result result;
|
||||||
struct kpatch_patch_dynrela *dynrelas;
|
struct kpatch_patch_dynrela *dynrelas;
|
||||||
int vmlinux, exported;
|
int vmlinux, external;
|
||||||
|
|
||||||
vmlinux = !strcmp(objname, "vmlinux");
|
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))
|
if (kpatch_is_core_module_symbol(rela->sym->name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
exported = 0;
|
external = 0;
|
||||||
|
|
||||||
if (rela->sym->bind == STB_LOCAL) {
|
if (rela->sym->bind == STB_LOCAL) {
|
||||||
/* An unchanged local symbol */
|
/* 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,
|
if (lookup_global_symbol(table, rela->sym->name,
|
||||||
&result))
|
&result))
|
||||||
/*
|
/*
|
||||||
* Not there, assume it's exported by
|
* Not there, assume it's either an
|
||||||
* another object.
|
* 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",
|
log_debug("lookup for %s @ 0x%016lx len %lu\n",
|
||||||
rela->sym->name, result.value, result.size);
|
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].src = 0;
|
||||||
dynrelas[index].addend = rela->addend;
|
dynrelas[index].addend = rela->addend;
|
||||||
dynrelas[index].type = rela->type;
|
dynrelas[index].type = rela->type;
|
||||||
dynrelas[index].exported = exported;
|
dynrelas[index].external = external;
|
||||||
|
|
||||||
/* add rela to fill in dest field */
|
/* add rela to fill in dest field */
|
||||||
ALLOC_LINK(dynrela, &relasec->relas);
|
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