Reuse module loader code to write relocations, thereby eliminating the need for architecture specific code in livepatch. Namely, we reuse apply_relocate_add() in the module loader to write relocs instead of duplicating functionality in livepatch's klp_write_module_reloc(). To apply relocation sections, remaining SHN_LIVEPATCH symbols referenced by relocs are resolved and then apply_relocate_add() is called to apply those relocations. Signed-off-by: Jessica Yu <jeyu@xxxxxxxxxx> --- include/linux/livepatch.h | 11 ++++-- include/linux/module.h | 6 ++++ kernel/livepatch/core.c | 89 +++++++++++++++++++++++++++++------------------ 3 files changed, 70 insertions(+), 36 deletions(-) diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index 31db7a0..601e892 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -85,7 +85,7 @@ struct klp_reloc { /** * struct klp_object - kernel object structure for live patching * @name: module name (or NULL for vmlinux) - * @relocs: relocation entries to be applied at load time + * @reloc_secs: relocation sections to be applied at load time * @funcs: function entries for functions to be patched in the object * @kobj: kobject for sysfs resources * @mod: kernel module associated with the patched object @@ -95,7 +95,7 @@ struct klp_reloc { struct klp_object { /* external */ const char *name; - struct klp_reloc *relocs; + struct list_head reloc_secs; struct klp_func *funcs; /* internal */ @@ -129,6 +129,13 @@ struct klp_patch { #define klp_for_each_func(obj, func) \ for (func = obj->funcs; func->old_name; func++) +struct klp_reloc_sec { + unsigned int index; + char *name; + char *objname; + struct list_head list; +}; + int klp_register_patch(struct klp_patch *); int klp_unregister_patch(struct klp_patch *); int klp_enable_patch(struct klp_patch *); diff --git a/include/linux/module.h b/include/linux/module.h index c8680b1..3c34eb8 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -793,9 +793,15 @@ extern int module_sysfs_initialized; #ifdef CONFIG_DEBUG_SET_MODULE_RONX extern void set_all_modules_text_rw(void); extern void set_all_modules_text_ro(void); +extern void +set_page_attributes(void *start, void *end, + int (*set)(unsigned long start, int num_pages)); #else static inline void set_all_modules_text_rw(void) { } static inline void set_all_modules_text_ro(void) { } +static inline void +set_page_attributes(void *start, void *end, + int (*set)(unsigned long start, int num_pages)) { } #endif #ifdef CONFIG_GENERIC_BUG diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 087a8c7..26c419f 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -28,6 +28,8 @@ #include <linux/list.h> #include <linux/kallsyms.h> #include <linux/livepatch.h> +#include <linux/elf.h> +#include <asm/cacheflush.h> /** * struct klp_ops - structure for tracking registered ftrace ops structs @@ -281,46 +283,54 @@ static int klp_find_external_symbol(struct module *pmod, const char *name, } static int klp_write_object_relocations(struct module *pmod, - struct klp_object *obj) + struct klp_object *obj, + struct klp_patch *patch) { - int ret; - struct klp_reloc *reloc; + int relindex, num_relas; + int i, ret = 0; + unsigned long addr; + unsigned int bind; + char *symname; + struct klp_reloc_sec *reloc_sec; + struct load_info *info; + Elf_Rela *rela; + Elf_Sym *sym, *symtab; + Elf_Shdr *symsect; if (WARN_ON(!klp_is_object_loaded(obj))) return -EINVAL; - if (WARN_ON(!obj->relocs)) - return -EINVAL; - - for (reloc = obj->relocs; reloc->name; reloc++) { - if (!klp_is_module(obj)) { - ret = klp_verify_vmlinux_symbol(reloc->name, - reloc->val); - if (ret) - return ret; - } else { - /* module, reloc->val needs to be discovered */ - if (reloc->external) - ret = klp_find_external_symbol(pmod, - reloc->name, - &reloc->val); - else - ret = klp_find_object_symbol(obj->mod->name, - reloc->name, - &reloc->val); - if (ret) - return ret; - } - ret = klp_write_module_reloc(pmod, reloc->type, reloc->loc, - reloc->val + reloc->addend); - if (ret) { - pr_err("relocation failed for symbol '%s' at 0x%016lx (%d)\n", - reloc->name, reloc->val, ret); - return ret; + info = pmod->info; + symsect = info->sechdrs + info->index.sym; + symtab = (void *)info->hdr + symsect->sh_offset; + + /* For each __klp_rela section for this object */ + list_for_each_entry(reloc_sec, &obj->reloc_secs, list) { + relindex = reloc_sec->index; + num_relas = info->sechdrs[relindex].sh_size / sizeof(Elf_Rela); + rela = (Elf_Rela *) info->sechdrs[relindex].sh_addr; + + /* For each rela in this __klp_rela section */ + for (i = 0; i < num_relas; i++, rela++) { + sym = symtab + ELF_R_SYM(rela->r_info); + symname = info->strtab + sym->st_name; + bind = ELF_ST_BIND(sym->st_info); + + if (sym->st_shndx == SHN_LIVEPATCH) { + if (bind == STB_LIVEPATCH_EXT) + ret = klp_find_external_symbol(pmod, symname, &addr); + else + ret = klp_find_object_symbol(obj->name, symname, &addr); + if (ret) + return ret; + sym->st_value = addr; + } } + ret = apply_relocate_add(info->sechdrs, info->strtab, + info->index.sym, relindex, pmod); } - return 0; + return ret; } static void notrace klp_ftrace_handler(unsigned long ip, @@ -741,12 +751,23 @@ static int klp_init_object_loaded(struct klp_patch *patch, struct klp_object *obj) { struct klp_func *func; + struct module *pmod; int ret; - if (obj->relocs) { - ret = klp_write_object_relocations(patch->mod, obj); + pmod = patch->mod; + + if (!list_empty(&obj->reloc_secs)) { + set_page_attributes(pmod->module_core, + pmod->module_core + pmod->core_text_size, + set_memory_rw); + + ret = klp_write_object_relocations(pmod, obj, patch); if (ret) return ret; + + set_page_attributes(pmod->module_core, + pmod->module_core + pmod->core_text_size, + set_memory_ro); } klp_for_each_func(obj, func) { -- 2.4.3 -- To unsubscribe from this list: send the line "unsubscribe linux-api" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html