xref: /linux-6.15/arch/arc/kernel/module.c (revision fa1c3ff9)
1 /*
2  * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8 
9 #include <linux/module.h>
10 #include <linux/moduleloader.h>
11 #include <linux/kernel.h>
12 #include <linux/elf.h>
13 #include <linux/vmalloc.h>
14 #include <linux/slab.h>
15 #include <linux/fs.h>
16 #include <linux/string.h>
17 
18 static inline void arc_write_me(unsigned short *addr, unsigned long value)
19 {
20 	*addr = (value & 0xffff0000) >> 16;
21 	*(addr + 1) = (value & 0xffff);
22 }
23 
24 int apply_relocate_add(Elf32_Shdr *sechdrs,
25 		       const char *strtab,
26 		       unsigned int symindex,	/* sec index for sym tbl */
27 		       unsigned int relsec,	/* sec index for relo sec */
28 		       struct module *module)
29 {
30 	int i, n;
31 	Elf32_Rela *rel_entry = (void *)sechdrs[relsec].sh_addr;
32 	Elf32_Sym *sym_entry, *sym_sec;
33 	Elf32_Addr relocation;
34 	Elf32_Addr location;
35 	Elf32_Addr sec_to_patch;
36 	int relo_type;
37 
38 	sec_to_patch = sechdrs[sechdrs[relsec].sh_info].sh_addr;
39 	sym_sec = (Elf32_Sym *) sechdrs[symindex].sh_addr;
40 	n = sechdrs[relsec].sh_size / sizeof(*rel_entry);
41 
42 	pr_debug("\n========== Module Sym reloc ===========================\n");
43 	pr_debug("Section to fixup %x\n", sec_to_patch);
44 	pr_debug("=========================================================\n");
45 	pr_debug("rela->r_off | rela->addend | sym->st_value | ADDR | VALUE\n");
46 	pr_debug("=========================================================\n");
47 
48 	/* Loop thru entries in relocation section */
49 	for (i = 0; i < n; i++) {
50 
51 		/* This is where to make the change */
52 		location = sec_to_patch + rel_entry[i].r_offset;
53 
54 		/* This is the symbol it is referring to.  Note that all
55 		   undefined symbols have been resolved.  */
56 		sym_entry = sym_sec + ELF32_R_SYM(rel_entry[i].r_info);
57 
58 		relocation = sym_entry->st_value + rel_entry[i].r_addend;
59 
60 		pr_debug("\t%x\t\t%x\t\t%x  %x %x [%s]\n",
61 			rel_entry[i].r_offset, rel_entry[i].r_addend,
62 			sym_entry->st_value, location, relocation,
63 			strtab + sym_entry->st_name);
64 
65 		/* This assumes modules are built with -mlong-calls
66 		 * so any branches/jumps are absolute 32 bit jmps
67 		 * global data access again is abs 32 bit.
68 		 * Both of these are handled by same relocation type
69 		 */
70 		relo_type = ELF32_R_TYPE(rel_entry[i].r_info);
71 
72 		if (likely(R_ARC_32_ME == relo_type))
73 			arc_write_me((unsigned short *)location, relocation);
74 		else if (R_ARC_32 == relo_type)
75 			*((Elf32_Addr *) location) = relocation;
76 		else
77 			goto relo_err;
78 
79 	}
80 	return 0;
81 
82 relo_err:
83 	pr_err("%s: unknown relocation: %u\n",
84 		module->name, ELF32_R_TYPE(rel_entry[i].r_info));
85 	return -ENOEXEC;
86 
87 }
88