xref: /kernel/linux/linux-5.10/arch/arc/kernel/module.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/moduleloader.h>
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/elf.h>
108c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/fs.h>
138c2ecf20Sopenharmony_ci#include <linux/string.h>
148c2ecf20Sopenharmony_ci#include <asm/unwind.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic inline void arc_write_me(unsigned short *addr, unsigned long value)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	*addr = (value & 0xffff0000) >> 16;
198c2ecf20Sopenharmony_ci	*(addr + 1) = (value & 0xffff);
208c2ecf20Sopenharmony_ci}
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/*
238c2ecf20Sopenharmony_ci * This gets called before relocation loop in generic loader
248c2ecf20Sopenharmony_ci * Make a note of the section index of unwinding section
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ciint module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
278c2ecf20Sopenharmony_ci			      char *secstr, struct module *mod)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci#ifdef CONFIG_ARC_DW2_UNWIND
308c2ecf20Sopenharmony_ci	mod->arch.unw_sec_idx = 0;
318c2ecf20Sopenharmony_ci	mod->arch.unw_info = NULL;
328c2ecf20Sopenharmony_ci#endif
338c2ecf20Sopenharmony_ci	mod->arch.secstr = secstr;
348c2ecf20Sopenharmony_ci	return 0;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_civoid module_arch_cleanup(struct module *mod)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci#ifdef CONFIG_ARC_DW2_UNWIND
408c2ecf20Sopenharmony_ci	if (mod->arch.unw_info)
418c2ecf20Sopenharmony_ci		unwind_remove_table(mod->arch.unw_info, 0);
428c2ecf20Sopenharmony_ci#endif
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciint apply_relocate_add(Elf32_Shdr *sechdrs,
468c2ecf20Sopenharmony_ci		       const char *strtab,
478c2ecf20Sopenharmony_ci		       unsigned int symindex,	/* sec index for sym tbl */
488c2ecf20Sopenharmony_ci		       unsigned int relsec,	/* sec index for relo sec */
498c2ecf20Sopenharmony_ci		       struct module *module)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	int i, n, relo_type;
528c2ecf20Sopenharmony_ci	Elf32_Rela *rel_entry = (void *)sechdrs[relsec].sh_addr;
538c2ecf20Sopenharmony_ci	Elf32_Sym *sym_entry, *sym_sec;
548c2ecf20Sopenharmony_ci	Elf32_Addr relocation, location, tgt_addr;
558c2ecf20Sopenharmony_ci	unsigned int tgtsec;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/*
588c2ecf20Sopenharmony_ci	 * @relsec has relocations e.g. .rela.init.text
598c2ecf20Sopenharmony_ci	 * @tgtsec is section to patch e.g. .init.text
608c2ecf20Sopenharmony_ci	 */
618c2ecf20Sopenharmony_ci	tgtsec = sechdrs[relsec].sh_info;
628c2ecf20Sopenharmony_ci	tgt_addr = sechdrs[tgtsec].sh_addr;
638c2ecf20Sopenharmony_ci	sym_sec = (Elf32_Sym *) sechdrs[symindex].sh_addr;
648c2ecf20Sopenharmony_ci	n = sechdrs[relsec].sh_size / sizeof(*rel_entry);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	pr_debug("\nSection to fixup %s @%x\n",
678c2ecf20Sopenharmony_ci		 module->arch.secstr + sechdrs[tgtsec].sh_name, tgt_addr);
688c2ecf20Sopenharmony_ci	pr_debug("=========================================================\n");
698c2ecf20Sopenharmony_ci	pr_debug("r_off\tr_add\tst_value ADDRESS  VALUE\n");
708c2ecf20Sopenharmony_ci	pr_debug("=========================================================\n");
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* Loop thru entries in relocation section */
738c2ecf20Sopenharmony_ci	for (i = 0; i < n; i++) {
748c2ecf20Sopenharmony_ci		const char *s;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci		/* This is where to make the change */
778c2ecf20Sopenharmony_ci		location = tgt_addr + rel_entry[i].r_offset;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		/* This is the symbol it is referring to.  Note that all
808c2ecf20Sopenharmony_ci		   undefined symbols have been resolved.  */
818c2ecf20Sopenharmony_ci		sym_entry = sym_sec + ELF32_R_SYM(rel_entry[i].r_info);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci		relocation = sym_entry->st_value + rel_entry[i].r_addend;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		if (sym_entry->st_name == 0 && ELF_ST_TYPE (sym_entry->st_info) == STT_SECTION) {
868c2ecf20Sopenharmony_ci			s = module->arch.secstr + sechdrs[sym_entry->st_shndx].sh_name;
878c2ecf20Sopenharmony_ci		} else {
888c2ecf20Sopenharmony_ci			s = strtab + sym_entry->st_name;
898c2ecf20Sopenharmony_ci		}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci		pr_debug("   %x\t%x\t%x %x %x [%s]\n",
928c2ecf20Sopenharmony_ci			 rel_entry[i].r_offset, rel_entry[i].r_addend,
938c2ecf20Sopenharmony_ci			 sym_entry->st_value, location, relocation, s);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		/* This assumes modules are built with -mlong-calls
968c2ecf20Sopenharmony_ci		 * so any branches/jumps are absolute 32 bit jmps
978c2ecf20Sopenharmony_ci		 * global data access again is abs 32 bit.
988c2ecf20Sopenharmony_ci		 * Both of these are handled by same relocation type
998c2ecf20Sopenharmony_ci		 */
1008c2ecf20Sopenharmony_ci		relo_type = ELF32_R_TYPE(rel_entry[i].r_info);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci		if (likely(R_ARC_32_ME == relo_type))	/* ME ( S + A ) */
1038c2ecf20Sopenharmony_ci			arc_write_me((unsigned short *)location, relocation);
1048c2ecf20Sopenharmony_ci		else if (R_ARC_32 == relo_type)		/* ( S + A ) */
1058c2ecf20Sopenharmony_ci			*((Elf32_Addr *) location) = relocation;
1068c2ecf20Sopenharmony_ci		else if (R_ARC_32_PCREL == relo_type)	/* ( S + A ) - PDATA ) */
1078c2ecf20Sopenharmony_ci			*((Elf32_Addr *) location) = relocation - location;
1088c2ecf20Sopenharmony_ci		else
1098c2ecf20Sopenharmony_ci			goto relo_err;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#ifdef CONFIG_ARC_DW2_UNWIND
1148c2ecf20Sopenharmony_ci	if (strcmp(module->arch.secstr+sechdrs[tgtsec].sh_name, ".eh_frame") == 0)
1158c2ecf20Sopenharmony_ci		module->arch.unw_sec_idx = tgtsec;
1168c2ecf20Sopenharmony_ci#endif
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return 0;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cirelo_err:
1218c2ecf20Sopenharmony_ci	pr_err("%s: unknown relocation: %u\n",
1228c2ecf20Sopenharmony_ci		module->name, ELF32_R_TYPE(rel_entry[i].r_info));
1238c2ecf20Sopenharmony_ci	return -ENOEXEC;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci/* Just before lift off: After sections have been relocated, we add the
1288c2ecf20Sopenharmony_ci * dwarf section to unwinder table pool
1298c2ecf20Sopenharmony_ci * This couldn't be done in module_frob_arch_sections() because
1308c2ecf20Sopenharmony_ci * relocations had not been applied by then
1318c2ecf20Sopenharmony_ci */
1328c2ecf20Sopenharmony_ciint module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
1338c2ecf20Sopenharmony_ci		    struct module *mod)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci#ifdef CONFIG_ARC_DW2_UNWIND
1368c2ecf20Sopenharmony_ci	void *unw;
1378c2ecf20Sopenharmony_ci	int unwsec = mod->arch.unw_sec_idx;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (unwsec) {
1408c2ecf20Sopenharmony_ci		unw = unwind_add_table(mod, (void *)sechdrs[unwsec].sh_addr,
1418c2ecf20Sopenharmony_ci				       sechdrs[unwsec].sh_size);
1428c2ecf20Sopenharmony_ci		mod->arch.unw_info = unw;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci#endif
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci}
147