xref: /linux/kernel/module/livepatch.c (revision 6486a57f)
11be9473eSAaron Tomlin // SPDX-License-Identifier: GPL-2.0-or-later
21be9473eSAaron Tomlin /*
31be9473eSAaron Tomlin  * Module livepatch support
41be9473eSAaron Tomlin  *
51be9473eSAaron Tomlin  * Copyright (C) 2016 Jessica Yu <jeyu@redhat.com>
61be9473eSAaron Tomlin  */
71be9473eSAaron Tomlin 
81be9473eSAaron Tomlin #include <linux/module.h>
91be9473eSAaron Tomlin #include <linux/string.h>
101be9473eSAaron Tomlin #include <linux/slab.h>
111be9473eSAaron Tomlin #include "internal.h"
121be9473eSAaron Tomlin 
131be9473eSAaron Tomlin /*
14*6486a57fSAlexey Dobriyan  * Persist ELF information about a module. Copy the ELF header,
151be9473eSAaron Tomlin  * section header table, section string table, and symtab section
161be9473eSAaron Tomlin  * index from info to mod->klp_info.
171be9473eSAaron Tomlin  */
copy_module_elf(struct module * mod,struct load_info * info)181be9473eSAaron Tomlin int copy_module_elf(struct module *mod, struct load_info *info)
191be9473eSAaron Tomlin {
201be9473eSAaron Tomlin 	unsigned int size, symndx;
211be9473eSAaron Tomlin 	int ret;
221be9473eSAaron Tomlin 
231be9473eSAaron Tomlin 	size = sizeof(*mod->klp_info);
241be9473eSAaron Tomlin 	mod->klp_info = kmalloc(size, GFP_KERNEL);
251be9473eSAaron Tomlin 	if (!mod->klp_info)
261be9473eSAaron Tomlin 		return -ENOMEM;
271be9473eSAaron Tomlin 
28*6486a57fSAlexey Dobriyan 	/* ELF header */
291be9473eSAaron Tomlin 	size = sizeof(mod->klp_info->hdr);
301be9473eSAaron Tomlin 	memcpy(&mod->klp_info->hdr, info->hdr, size);
311be9473eSAaron Tomlin 
32*6486a57fSAlexey Dobriyan 	/* ELF section header table */
331be9473eSAaron Tomlin 	size = sizeof(*info->sechdrs) * info->hdr->e_shnum;
341be9473eSAaron Tomlin 	mod->klp_info->sechdrs = kmemdup(info->sechdrs, size, GFP_KERNEL);
351be9473eSAaron Tomlin 	if (!mod->klp_info->sechdrs) {
361be9473eSAaron Tomlin 		ret = -ENOMEM;
371be9473eSAaron Tomlin 		goto free_info;
381be9473eSAaron Tomlin 	}
391be9473eSAaron Tomlin 
40*6486a57fSAlexey Dobriyan 	/* ELF section name string table */
411be9473eSAaron Tomlin 	size = info->sechdrs[info->hdr->e_shstrndx].sh_size;
421be9473eSAaron Tomlin 	mod->klp_info->secstrings = kmemdup(info->secstrings, size, GFP_KERNEL);
431be9473eSAaron Tomlin 	if (!mod->klp_info->secstrings) {
441be9473eSAaron Tomlin 		ret = -ENOMEM;
451be9473eSAaron Tomlin 		goto free_sechdrs;
461be9473eSAaron Tomlin 	}
471be9473eSAaron Tomlin 
48*6486a57fSAlexey Dobriyan 	/* ELF symbol section index */
491be9473eSAaron Tomlin 	symndx = info->index.sym;
501be9473eSAaron Tomlin 	mod->klp_info->symndx = symndx;
511be9473eSAaron Tomlin 
521be9473eSAaron Tomlin 	/*
531be9473eSAaron Tomlin 	 * For livepatch modules, core_kallsyms.symtab is a complete
541be9473eSAaron Tomlin 	 * copy of the original symbol table. Adjust sh_addr to point
551be9473eSAaron Tomlin 	 * to core_kallsyms.symtab since the copy of the symtab in module
561be9473eSAaron Tomlin 	 * init memory is freed at the end of do_init_module().
571be9473eSAaron Tomlin 	 */
581be9473eSAaron Tomlin 	mod->klp_info->sechdrs[symndx].sh_addr = (unsigned long)mod->core_kallsyms.symtab;
591be9473eSAaron Tomlin 
601be9473eSAaron Tomlin 	return 0;
611be9473eSAaron Tomlin 
621be9473eSAaron Tomlin free_sechdrs:
631be9473eSAaron Tomlin 	kfree(mod->klp_info->sechdrs);
641be9473eSAaron Tomlin free_info:
651be9473eSAaron Tomlin 	kfree(mod->klp_info);
661be9473eSAaron Tomlin 	return ret;
671be9473eSAaron Tomlin }
681be9473eSAaron Tomlin 
free_module_elf(struct module * mod)691be9473eSAaron Tomlin void free_module_elf(struct module *mod)
701be9473eSAaron Tomlin {
711be9473eSAaron Tomlin 	kfree(mod->klp_info->sechdrs);
721be9473eSAaron Tomlin 	kfree(mod->klp_info->secstrings);
731be9473eSAaron Tomlin 	kfree(mod->klp_info);
741be9473eSAaron Tomlin }
75