xref: /linux-6.15/arch/powerpc/kernel/module_32.c (revision 3c752965)
1ed981856SPaul Mackerras /*  Kernel module help for PPC.
2ed981856SPaul Mackerras     Copyright (C) 2001 Rusty Russell.
3ed981856SPaul Mackerras 
4ed981856SPaul Mackerras     This program is free software; you can redistribute it and/or modify
5ed981856SPaul Mackerras     it under the terms of the GNU General Public License as published by
6ed981856SPaul Mackerras     the Free Software Foundation; either version 2 of the License, or
7ed981856SPaul Mackerras     (at your option) any later version.
8ed981856SPaul Mackerras 
9ed981856SPaul Mackerras     This program is distributed in the hope that it will be useful,
10ed981856SPaul Mackerras     but WITHOUT ANY WARRANTY; without even the implied warranty of
11ed981856SPaul Mackerras     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12ed981856SPaul Mackerras     GNU General Public License for more details.
13ed981856SPaul Mackerras 
14ed981856SPaul Mackerras     You should have received a copy of the GNU General Public License
15ed981856SPaul Mackerras     along with this program; if not, write to the Free Software
16ed981856SPaul Mackerras     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17ed981856SPaul Mackerras */
18ed981856SPaul Mackerras #include <linux/module.h>
19ed981856SPaul Mackerras #include <linux/moduleloader.h>
20ed981856SPaul Mackerras #include <linux/elf.h>
21ed981856SPaul Mackerras #include <linux/vmalloc.h>
22ed981856SPaul Mackerras #include <linux/fs.h>
23ed981856SPaul Mackerras #include <linux/string.h>
24ed981856SPaul Mackerras #include <linux/kernel.h>
257cc45e64SSteven Rostedt #include <linux/ftrace.h>
26ed981856SPaul Mackerras #include <linux/cache.h>
2773c9ceabSJeremy Fitzhardinge #include <linux/bug.h>
28eda09fbdSEmil Medve #include <linux/sort.h>
29ed981856SPaul Mackerras 
3021c4ff80SBenjamin Herrenschmidt #include "setup.h"
3121c4ff80SBenjamin Herrenschmidt 
32ed981856SPaul Mackerras #if 0
33ed981856SPaul Mackerras #define DEBUGP printk
34ed981856SPaul Mackerras #else
35ed981856SPaul Mackerras #define DEBUGP(fmt , ...)
36ed981856SPaul Mackerras #endif
37ed981856SPaul Mackerras 
38ed981856SPaul Mackerras /* Count how many different relocations (different symbol, different
39ed981856SPaul Mackerras    addend) */
40ed981856SPaul Mackerras static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num)
41ed981856SPaul Mackerras {
42eda09fbdSEmil Medve 	unsigned int i, r_info, r_addend, _count_relocs;
43ed981856SPaul Mackerras 
44eda09fbdSEmil Medve 	_count_relocs = 0;
45eda09fbdSEmil Medve 	r_info = 0;
46eda09fbdSEmil Medve 	r_addend = 0;
47eda09fbdSEmil Medve 	for (i = 0; i < num; i++)
48eda09fbdSEmil Medve 		/* Only count 24-bit relocs, others don't need stubs */
49eda09fbdSEmil Medve 		if (ELF32_R_TYPE(rela[i].r_info) == R_PPC_REL24 &&
50eda09fbdSEmil Medve 		    (r_info != ELF32_R_SYM(rela[i].r_info) ||
51eda09fbdSEmil Medve 		     r_addend != rela[i].r_addend)) {
52eda09fbdSEmil Medve 			_count_relocs++;
53eda09fbdSEmil Medve 			r_info = ELF32_R_SYM(rela[i].r_info);
54eda09fbdSEmil Medve 			r_addend = rela[i].r_addend;
55ed981856SPaul Mackerras 		}
56eda09fbdSEmil Medve 
577cc45e64SSteven Rostedt #ifdef CONFIG_DYNAMIC_FTRACE
587cc45e64SSteven Rostedt 	_count_relocs++;	/* add one for ftrace_caller */
597cc45e64SSteven Rostedt #endif
60eda09fbdSEmil Medve 	return _count_relocs;
61ed981856SPaul Mackerras }
62eda09fbdSEmil Medve 
63eda09fbdSEmil Medve static int relacmp(const void *_x, const void *_y)
64eda09fbdSEmil Medve {
65eda09fbdSEmil Medve 	const Elf32_Rela *x, *y;
66eda09fbdSEmil Medve 
67eda09fbdSEmil Medve 	y = (Elf32_Rela *)_x;
68eda09fbdSEmil Medve 	x = (Elf32_Rela *)_y;
69eda09fbdSEmil Medve 
70eda09fbdSEmil Medve 	/* Compare the entire r_info (as opposed to ELF32_R_SYM(r_info) only) to
71eda09fbdSEmil Medve 	 * make the comparison cheaper/faster. It won't affect the sorting or
72eda09fbdSEmil Medve 	 * the counting algorithms' performance
73eda09fbdSEmil Medve 	 */
74eda09fbdSEmil Medve 	if (x->r_info < y->r_info)
75eda09fbdSEmil Medve 		return -1;
76eda09fbdSEmil Medve 	else if (x->r_info > y->r_info)
77eda09fbdSEmil Medve 		return 1;
78eda09fbdSEmil Medve 	else if (x->r_addend < y->r_addend)
79eda09fbdSEmil Medve 		return -1;
80eda09fbdSEmil Medve 	else if (x->r_addend > y->r_addend)
81eda09fbdSEmil Medve 		return 1;
82eda09fbdSEmil Medve 	else
83eda09fbdSEmil Medve 		return 0;
84eda09fbdSEmil Medve }
85eda09fbdSEmil Medve 
86eda09fbdSEmil Medve static void relaswap(void *_x, void *_y, int size)
87eda09fbdSEmil Medve {
88eda09fbdSEmil Medve 	uint32_t *x, *y, tmp;
89eda09fbdSEmil Medve 	int i;
90eda09fbdSEmil Medve 
91eda09fbdSEmil Medve 	y = (uint32_t *)_x;
92eda09fbdSEmil Medve 	x = (uint32_t *)_y;
93eda09fbdSEmil Medve 
94eda09fbdSEmil Medve 	for (i = 0; i < sizeof(Elf32_Rela) / sizeof(uint32_t); i++) {
95eda09fbdSEmil Medve 		tmp = x[i];
96eda09fbdSEmil Medve 		x[i] = y[i];
97eda09fbdSEmil Medve 		y[i] = tmp;
98eda09fbdSEmil Medve 	}
99ed981856SPaul Mackerras }
100ed981856SPaul Mackerras 
101ed981856SPaul Mackerras /* Get the potential trampolines size required of the init and
102ed981856SPaul Mackerras    non-init sections */
103ed981856SPaul Mackerras static unsigned long get_plt_size(const Elf32_Ehdr *hdr,
104ed981856SPaul Mackerras 				  const Elf32_Shdr *sechdrs,
105ed981856SPaul Mackerras 				  const char *secstrings,
106ed981856SPaul Mackerras 				  int is_init)
107ed981856SPaul Mackerras {
108ed981856SPaul Mackerras 	unsigned long ret = 0;
109ed981856SPaul Mackerras 	unsigned i;
110ed981856SPaul Mackerras 
111ed981856SPaul Mackerras 	/* Everything marked ALLOC (this includes the exported
112ed981856SPaul Mackerras            symbols) */
113ed981856SPaul Mackerras 	for (i = 1; i < hdr->e_shnum; i++) {
114ed981856SPaul Mackerras 		/* If it's called *.init*, and we're not init, we're
115ed981856SPaul Mackerras                    not interested */
116ed981856SPaul Mackerras 		if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0)
117ed981856SPaul Mackerras 		    != is_init)
118ed981856SPaul Mackerras 			continue;
119ed981856SPaul Mackerras 
120ed981856SPaul Mackerras 		/* We don't want to look at debug sections. */
121ed981856SPaul Mackerras 		if (strstr(secstrings + sechdrs[i].sh_name, ".debug") != 0)
122ed981856SPaul Mackerras 			continue;
123ed981856SPaul Mackerras 
124ed981856SPaul Mackerras 		if (sechdrs[i].sh_type == SHT_RELA) {
125ed981856SPaul Mackerras 			DEBUGP("Found relocations in section %u\n", i);
126ed981856SPaul Mackerras 			DEBUGP("Ptr: %p.  Number: %u\n",
127ed981856SPaul Mackerras 			       (void *)hdr + sechdrs[i].sh_offset,
128ed981856SPaul Mackerras 			       sechdrs[i].sh_size / sizeof(Elf32_Rela));
129eda09fbdSEmil Medve 
130eda09fbdSEmil Medve 			/* Sort the relocation information based on a symbol and
131eda09fbdSEmil Medve 			 * addend key. This is a stable O(n*log n) complexity
132eda09fbdSEmil Medve 			 * alogrithm but it will reduce the complexity of
133eda09fbdSEmil Medve 			 * count_relocs() to linear complexity O(n)
134eda09fbdSEmil Medve 			 */
135eda09fbdSEmil Medve 			sort((void *)hdr + sechdrs[i].sh_offset,
136eda09fbdSEmil Medve 			     sechdrs[i].sh_size / sizeof(Elf32_Rela),
137eda09fbdSEmil Medve 			     sizeof(Elf32_Rela), relacmp, relaswap);
138eda09fbdSEmil Medve 
139ed981856SPaul Mackerras 			ret += count_relocs((void *)hdr
140ed981856SPaul Mackerras 					     + sechdrs[i].sh_offset,
141ed981856SPaul Mackerras 					     sechdrs[i].sh_size
142ed981856SPaul Mackerras 					     / sizeof(Elf32_Rela))
143ed981856SPaul Mackerras 				* sizeof(struct ppc_plt_entry);
144ed981856SPaul Mackerras 		}
145ed981856SPaul Mackerras 	}
146ed981856SPaul Mackerras 
147ed981856SPaul Mackerras 	return ret;
148ed981856SPaul Mackerras }
149ed981856SPaul Mackerras 
150ed981856SPaul Mackerras int module_frob_arch_sections(Elf32_Ehdr *hdr,
151ed981856SPaul Mackerras 			      Elf32_Shdr *sechdrs,
152ed981856SPaul Mackerras 			      char *secstrings,
153ed981856SPaul Mackerras 			      struct module *me)
154ed981856SPaul Mackerras {
155ed981856SPaul Mackerras 	unsigned int i;
156ed981856SPaul Mackerras 
157ed981856SPaul Mackerras 	/* Find .plt and .init.plt sections */
158ed981856SPaul Mackerras 	for (i = 0; i < hdr->e_shnum; i++) {
159ed981856SPaul Mackerras 		if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0)
160ed981856SPaul Mackerras 			me->arch.init_plt_section = i;
161ed981856SPaul Mackerras 		else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0)
162ed981856SPaul Mackerras 			me->arch.core_plt_section = i;
163ed981856SPaul Mackerras 	}
164ed981856SPaul Mackerras 	if (!me->arch.core_plt_section || !me->arch.init_plt_section) {
165ed981856SPaul Mackerras 		printk("Module doesn't contain .plt or .init.plt sections.\n");
166ed981856SPaul Mackerras 		return -ENOEXEC;
167ed981856SPaul Mackerras 	}
168ed981856SPaul Mackerras 
169ed981856SPaul Mackerras 	/* Override their sizes */
170ed981856SPaul Mackerras 	sechdrs[me->arch.core_plt_section].sh_size
171ed981856SPaul Mackerras 		= get_plt_size(hdr, sechdrs, secstrings, 0);
172ed981856SPaul Mackerras 	sechdrs[me->arch.init_plt_section].sh_size
173ed981856SPaul Mackerras 		= get_plt_size(hdr, sechdrs, secstrings, 1);
174ed981856SPaul Mackerras 	return 0;
175ed981856SPaul Mackerras }
176ed981856SPaul Mackerras 
177ed981856SPaul Mackerras static inline int entry_matches(struct ppc_plt_entry *entry, Elf32_Addr val)
178ed981856SPaul Mackerras {
179*3c752965SSteffen Rumler 	if (entry->jump[0] == 0x3d800000 + ((val + 0x8000) >> 16)
180*3c752965SSteffen Rumler 	    && entry->jump[1] == 0x398c0000 + (val & 0xffff))
181ed981856SPaul Mackerras 		return 1;
182ed981856SPaul Mackerras 	return 0;
183ed981856SPaul Mackerras }
184ed981856SPaul Mackerras 
185ed981856SPaul Mackerras /* Set up a trampoline in the PLT to bounce us to the distant function */
186ed981856SPaul Mackerras static uint32_t do_plt_call(void *location,
187ed981856SPaul Mackerras 			    Elf32_Addr val,
188ed981856SPaul Mackerras 			    Elf32_Shdr *sechdrs,
189ed981856SPaul Mackerras 			    struct module *mod)
190ed981856SPaul Mackerras {
191ed981856SPaul Mackerras 	struct ppc_plt_entry *entry;
192ed981856SPaul Mackerras 
193ed981856SPaul Mackerras 	DEBUGP("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location);
194ed981856SPaul Mackerras 	/* Init, or core PLT? */
195ed981856SPaul Mackerras 	if (location >= mod->module_core
196ed981856SPaul Mackerras 	    && location < mod->module_core + mod->core_size)
197ed981856SPaul Mackerras 		entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
198ed981856SPaul Mackerras 	else
199ed981856SPaul Mackerras 		entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
200ed981856SPaul Mackerras 
201ed981856SPaul Mackerras 	/* Find this entry, or if that fails, the next avail. entry */
202ed981856SPaul Mackerras 	while (entry->jump[0]) {
203ed981856SPaul Mackerras 		if (entry_matches(entry, val)) return (uint32_t)entry;
204ed981856SPaul Mackerras 		entry++;
205ed981856SPaul Mackerras 	}
206ed981856SPaul Mackerras 
207*3c752965SSteffen Rumler 	entry->jump[0] = 0x3d800000+((val+0x8000)>>16); /* lis r12,sym@ha */
208*3c752965SSteffen Rumler 	entry->jump[1] = 0x398c0000 + (val&0xffff);     /* addi r12,r12,sym@l*/
209*3c752965SSteffen Rumler 	entry->jump[2] = 0x7d8903a6;                    /* mtctr r12 */
210ed981856SPaul Mackerras 	entry->jump[3] = 0x4e800420;			/* bctr */
211ed981856SPaul Mackerras 
212ed981856SPaul Mackerras 	DEBUGP("Initialized plt for 0x%x at %p\n", val, entry);
213ed981856SPaul Mackerras 	return (uint32_t)entry;
214ed981856SPaul Mackerras }
215ed981856SPaul Mackerras 
216ed981856SPaul Mackerras int apply_relocate_add(Elf32_Shdr *sechdrs,
217ed981856SPaul Mackerras 		       const char *strtab,
218ed981856SPaul Mackerras 		       unsigned int symindex,
219ed981856SPaul Mackerras 		       unsigned int relsec,
220ed981856SPaul Mackerras 		       struct module *module)
221ed981856SPaul Mackerras {
222ed981856SPaul Mackerras 	unsigned int i;
223ed981856SPaul Mackerras 	Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr;
224ed981856SPaul Mackerras 	Elf32_Sym *sym;
225ed981856SPaul Mackerras 	uint32_t *location;
226ed981856SPaul Mackerras 	uint32_t value;
227ed981856SPaul Mackerras 
228ed981856SPaul Mackerras 	DEBUGP("Applying ADD relocate section %u to %u\n", relsec,
229ed981856SPaul Mackerras 	       sechdrs[relsec].sh_info);
230ed981856SPaul Mackerras 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
231ed981856SPaul Mackerras 		/* This is where to make the change */
232ed981856SPaul Mackerras 		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
233ed981856SPaul Mackerras 			+ rela[i].r_offset;
234ed981856SPaul Mackerras 		/* This is the symbol it is referring to.  Note that all
235ed981856SPaul Mackerras 		   undefined symbols have been resolved.  */
236ed981856SPaul Mackerras 		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
237ed981856SPaul Mackerras 			+ ELF32_R_SYM(rela[i].r_info);
238ed981856SPaul Mackerras 		/* `Everything is relative'. */
239ed981856SPaul Mackerras 		value = sym->st_value + rela[i].r_addend;
240ed981856SPaul Mackerras 
241ed981856SPaul Mackerras 		switch (ELF32_R_TYPE(rela[i].r_info)) {
242ed981856SPaul Mackerras 		case R_PPC_ADDR32:
243ed981856SPaul Mackerras 			/* Simply set it */
244ed981856SPaul Mackerras 			*(uint32_t *)location = value;
245ed981856SPaul Mackerras 			break;
246ed981856SPaul Mackerras 
247ed981856SPaul Mackerras 		case R_PPC_ADDR16_LO:
248ed981856SPaul Mackerras 			/* Low half of the symbol */
249ed981856SPaul Mackerras 			*(uint16_t *)location = value;
250ed981856SPaul Mackerras 			break;
251ed981856SPaul Mackerras 
2529a3d6458SSimon Vallet 		case R_PPC_ADDR16_HI:
2539a3d6458SSimon Vallet 			/* Higher half of the symbol */
2549a3d6458SSimon Vallet 			*(uint16_t *)location = (value >> 16);
2559a3d6458SSimon Vallet 			break;
2569a3d6458SSimon Vallet 
257ed981856SPaul Mackerras 		case R_PPC_ADDR16_HA:
258ed981856SPaul Mackerras 			/* Sign-adjusted lower 16 bits: PPC ELF ABI says:
259ed981856SPaul Mackerras 			   (((x >> 16) + ((x & 0x8000) ? 1 : 0))) & 0xFFFF.
260ed981856SPaul Mackerras 			   This is the same, only sane.
261ed981856SPaul Mackerras 			 */
262ed981856SPaul Mackerras 			*(uint16_t *)location = (value + 0x8000) >> 16;
263ed981856SPaul Mackerras 			break;
264ed981856SPaul Mackerras 
265ed981856SPaul Mackerras 		case R_PPC_REL24:
266ed981856SPaul Mackerras 			if ((int)(value - (uint32_t)location) < -0x02000000
267ed981856SPaul Mackerras 			    || (int)(value - (uint32_t)location) >= 0x02000000)
268ed981856SPaul Mackerras 				value = do_plt_call(location, value,
269ed981856SPaul Mackerras 						    sechdrs, module);
270ed981856SPaul Mackerras 
271ed981856SPaul Mackerras 			/* Only replace bits 2 through 26 */
272ed981856SPaul Mackerras 			DEBUGP("REL24 value = %08X. location = %08X\n",
273ed981856SPaul Mackerras 			       value, (uint32_t)location);
274ed981856SPaul Mackerras 			DEBUGP("Location before: %08X.\n",
275ed981856SPaul Mackerras 			       *(uint32_t *)location);
276ed981856SPaul Mackerras 			*(uint32_t *)location
277ed981856SPaul Mackerras 				= (*(uint32_t *)location & ~0x03fffffc)
278ed981856SPaul Mackerras 				| ((value - (uint32_t)location)
279ed981856SPaul Mackerras 				   & 0x03fffffc);
280ed981856SPaul Mackerras 			DEBUGP("Location after: %08X.\n",
281ed981856SPaul Mackerras 			       *(uint32_t *)location);
282ed981856SPaul Mackerras 			DEBUGP("ie. jump to %08X+%08X = %08X\n",
283ed981856SPaul Mackerras 			       *(uint32_t *)location & 0x03fffffc,
284ed981856SPaul Mackerras 			       (uint32_t)location,
285ed981856SPaul Mackerras 			       (*(uint32_t *)location & 0x03fffffc)
286ed981856SPaul Mackerras 			       + (uint32_t)location);
287ed981856SPaul Mackerras 			break;
288ed981856SPaul Mackerras 
289ed981856SPaul Mackerras 		case R_PPC_REL32:
290ed981856SPaul Mackerras 			/* 32-bit relative jump. */
291ed981856SPaul Mackerras 			*(uint32_t *)location = value - (uint32_t)location;
292ed981856SPaul Mackerras 			break;
293ed981856SPaul Mackerras 
294ed981856SPaul Mackerras 		default:
295ed981856SPaul Mackerras 			printk("%s: unknown ADD relocation: %u\n",
296ed981856SPaul Mackerras 			       module->name,
297ed981856SPaul Mackerras 			       ELF32_R_TYPE(rela[i].r_info));
298ed981856SPaul Mackerras 			return -ENOEXEC;
299ed981856SPaul Mackerras 		}
300ed981856SPaul Mackerras 	}
3017cc45e64SSteven Rostedt #ifdef CONFIG_DYNAMIC_FTRACE
3027cc45e64SSteven Rostedt 	module->arch.tramp =
3037cc45e64SSteven Rostedt 		do_plt_call(module->module_core,
3047cc45e64SSteven Rostedt 			    (unsigned long)ftrace_caller,
3057cc45e64SSteven Rostedt 			    sechdrs, module);
3067cc45e64SSteven Rostedt #endif
307ed981856SPaul Mackerras 	return 0;
308ed981856SPaul Mackerras }
309