xref: /linux-6.15/arch/alpha/kernel/module.c (revision b6b17a8b)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*  Kernel module help for Alpha.
31da177e4SLinus Torvalds     Copyright (C) 2002 Richard Henderson.
41da177e4SLinus Torvalds 
51da177e4SLinus Torvalds */
61da177e4SLinus Torvalds #include <linux/moduleloader.h>
71da177e4SLinus Torvalds #include <linux/elf.h>
81da177e4SLinus Torvalds #include <linux/vmalloc.h>
91da177e4SLinus Torvalds #include <linux/fs.h>
101da177e4SLinus Torvalds #include <linux/string.h>
111da177e4SLinus Torvalds #include <linux/kernel.h>
121da177e4SLinus Torvalds #include <linux/slab.h>
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #if 0
151da177e4SLinus Torvalds #define DEBUGP printk
161da177e4SLinus Torvalds #else
171da177e4SLinus Torvalds #define DEBUGP(fmt...)
181da177e4SLinus Torvalds #endif
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds /* Allocate the GOT at the end of the core sections.  */
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds struct got_entry {
231da177e4SLinus Torvalds 	struct got_entry *next;
2469ac5964SChaskiel Grundman 	Elf64_Sxword r_addend;
251da177e4SLinus Torvalds 	int got_offset;
261da177e4SLinus Torvalds };
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds static inline void
process_reloc_for_got(Elf64_Rela * rela,struct got_entry * chains,Elf64_Xword * poffset)291da177e4SLinus Torvalds process_reloc_for_got(Elf64_Rela *rela,
301da177e4SLinus Torvalds 		      struct got_entry *chains, Elf64_Xword *poffset)
311da177e4SLinus Torvalds {
321da177e4SLinus Torvalds 	unsigned long r_sym = ELF64_R_SYM (rela->r_info);
331da177e4SLinus Torvalds 	unsigned long r_type = ELF64_R_TYPE (rela->r_info);
3469ac5964SChaskiel Grundman 	Elf64_Sxword r_addend = rela->r_addend;
351da177e4SLinus Torvalds 	struct got_entry *g;
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds 	if (r_type != R_ALPHA_LITERAL)
381da177e4SLinus Torvalds 		return;
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds 	for (g = chains + r_sym; g ; g = g->next)
4169ac5964SChaskiel Grundman 		if (g->r_addend == r_addend) {
421da177e4SLinus Torvalds 			if (g->got_offset == 0) {
431da177e4SLinus Torvalds 				g->got_offset = *poffset;
441da177e4SLinus Torvalds 				*poffset += 8;
451da177e4SLinus Torvalds 			}
461da177e4SLinus Torvalds 			goto found_entry;
471da177e4SLinus Torvalds 		}
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds 	g = kmalloc (sizeof (*g), GFP_KERNEL);
501da177e4SLinus Torvalds 	g->next = chains[r_sym].next;
5169ac5964SChaskiel Grundman 	g->r_addend = r_addend;
521da177e4SLinus Torvalds 	g->got_offset = *poffset;
531da177e4SLinus Torvalds 	*poffset += 8;
541da177e4SLinus Torvalds 	chains[r_sym].next = g;
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds  found_entry:
571da177e4SLinus Torvalds 	/* Trick: most of the ELF64_R_TYPE field is unused.  There are
581da177e4SLinus Torvalds 	   42 valid relocation types, and a 32-bit field.  Co-opt the
591da177e4SLinus Torvalds 	   bits above 256 to store the got offset for this reloc.  */
601da177e4SLinus Torvalds 	rela->r_info |= g->got_offset << 8;
611da177e4SLinus Torvalds }
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds int
module_frob_arch_sections(Elf64_Ehdr * hdr,Elf64_Shdr * sechdrs,char * secstrings,struct module * me)641da177e4SLinus Torvalds module_frob_arch_sections(Elf64_Ehdr *hdr, Elf64_Shdr *sechdrs,
651da177e4SLinus Torvalds 			  char *secstrings, struct module *me)
661da177e4SLinus Torvalds {
671da177e4SLinus Torvalds 	struct got_entry *chains;
681da177e4SLinus Torvalds 	Elf64_Rela *rela;
691da177e4SLinus Torvalds 	Elf64_Shdr *esechdrs, *symtab, *s, *got;
701da177e4SLinus Torvalds 	unsigned long nsyms, nrela, i;
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	esechdrs = sechdrs + hdr->e_shnum;
731da177e4SLinus Torvalds 	symtab = got = NULL;
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 	/* Find out how large the symbol table is.  Allocate one got_entry
761da177e4SLinus Torvalds 	   head per symbol.  Normally this will be enough, but not always.
771da177e4SLinus Torvalds 	   We'll chain different offsets for the symbol down each head.  */
781da177e4SLinus Torvalds 	for (s = sechdrs; s < esechdrs; ++s)
791da177e4SLinus Torvalds 		if (s->sh_type == SHT_SYMTAB)
801da177e4SLinus Torvalds 			symtab = s;
811da177e4SLinus Torvalds 		else if (!strcmp(".got", secstrings + s->sh_name)) {
821da177e4SLinus Torvalds 			got = s;
831da177e4SLinus Torvalds 			me->arch.gotsecindex = s - sechdrs;
841da177e4SLinus Torvalds 		}
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	if (!symtab) {
871da177e4SLinus Torvalds 		printk(KERN_ERR "module %s: no symbol table\n", me->name);
881da177e4SLinus Torvalds 		return -ENOEXEC;
891da177e4SLinus Torvalds 	}
901da177e4SLinus Torvalds 	if (!got) {
911da177e4SLinus Torvalds 		printk(KERN_ERR "module %s: no got section\n", me->name);
921da177e4SLinus Torvalds 		return -ENOEXEC;
931da177e4SLinus Torvalds 	}
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 	nsyms = symtab->sh_size / sizeof(Elf64_Sym);
96dd00cc48SYoann Padioleau 	chains = kcalloc(nsyms, sizeof(struct got_entry), GFP_KERNEL);
97b901d40cSJim Meyering 	if (!chains) {
98b901d40cSJim Meyering 		printk(KERN_ERR
99b901d40cSJim Meyering 		       "module %s: no memory for symbol chain buffer\n",
100b901d40cSJim Meyering 		       me->name);
101b901d40cSJim Meyering 		return -ENOMEM;
102b901d40cSJim Meyering 	}
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds 	got->sh_size = 0;
1051da177e4SLinus Torvalds 	got->sh_addralign = 8;
1061da177e4SLinus Torvalds 	got->sh_type = SHT_NOBITS;
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds 	/* Examine all LITERAL relocations to find out what GOT entries
1091da177e4SLinus Torvalds 	   are required.  This sizes the GOT section as well.  */
1101da177e4SLinus Torvalds 	for (s = sechdrs; s < esechdrs; ++s)
1111da177e4SLinus Torvalds 		if (s->sh_type == SHT_RELA) {
1121da177e4SLinus Torvalds 			nrela = s->sh_size / sizeof(Elf64_Rela);
1131da177e4SLinus Torvalds 			rela = (void *)hdr + s->sh_offset;
1141da177e4SLinus Torvalds 			for (i = 0; i < nrela; ++i)
1151da177e4SLinus Torvalds 				process_reloc_for_got(rela+i, chains,
1161da177e4SLinus Torvalds 						      &got->sh_size);
1171da177e4SLinus Torvalds 		}
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 	/* Free the memory we allocated.  */
1201da177e4SLinus Torvalds 	for (i = 0; i < nsyms; ++i) {
1211da177e4SLinus Torvalds 		struct got_entry *g, *n;
1221da177e4SLinus Torvalds 		for (g = chains[i].next; g ; g = n) {
1231da177e4SLinus Torvalds 			n = g->next;
1241da177e4SLinus Torvalds 			kfree(g);
1251da177e4SLinus Torvalds 		}
1261da177e4SLinus Torvalds 	}
1271da177e4SLinus Torvalds 	kfree(chains);
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	return 0;
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds int
apply_relocate_add(Elf64_Shdr * sechdrs,const char * strtab,unsigned int symindex,unsigned int relsec,struct module * me)1331da177e4SLinus Torvalds apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab,
1341da177e4SLinus Torvalds 		   unsigned int symindex, unsigned int relsec,
1351da177e4SLinus Torvalds 		   struct module *me)
1361da177e4SLinus Torvalds {
1371da177e4SLinus Torvalds 	Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr;
1381da177e4SLinus Torvalds 	unsigned long i, n = sechdrs[relsec].sh_size / sizeof(*rela);
1391da177e4SLinus Torvalds 	Elf64_Sym *symtab, *sym;
1401da177e4SLinus Torvalds 	void *base, *location;
1411da177e4SLinus Torvalds 	unsigned long got, gp;
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	DEBUGP("Applying relocate section %u to %u\n", relsec,
1441da177e4SLinus Torvalds 	       sechdrs[relsec].sh_info);
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds 	base = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr;
1471da177e4SLinus Torvalds 	symtab = (Elf64_Sym *)sechdrs[symindex].sh_addr;
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds 	got = sechdrs[me->arch.gotsecindex].sh_addr;
150*b6b17a8bSEdward Humes 	gp = got + 0x8000;
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	for (i = 0; i < n; i++) {
1531da177e4SLinus Torvalds 		unsigned long r_sym = ELF64_R_SYM (rela[i].r_info);
1541da177e4SLinus Torvalds 		unsigned long r_type = ELF64_R_TYPE (rela[i].r_info);
1551da177e4SLinus Torvalds 		unsigned long r_got_offset = r_type >> 8;
1561da177e4SLinus Torvalds 		unsigned long value, hi, lo;
1571da177e4SLinus Torvalds 		r_type &= 0xff;
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 		/* This is where to make the change.  */
1601da177e4SLinus Torvalds 		location = base + rela[i].r_offset;
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 		/* This is the symbol it is referring to.  Note that all
1631da177e4SLinus Torvalds 		   unresolved symbols have been resolved.  */
1641da177e4SLinus Torvalds 		sym = symtab + r_sym;
1651da177e4SLinus Torvalds 		value = sym->st_value + rela[i].r_addend;
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds 		switch (r_type) {
1681da177e4SLinus Torvalds 		case R_ALPHA_NONE:
1691da177e4SLinus Torvalds 			break;
1704f61e078SMichael Cree 		case R_ALPHA_REFLONG:
1714f61e078SMichael Cree 			*(u32 *)location = value;
1724f61e078SMichael Cree 			break;
1731da177e4SLinus Torvalds 		case R_ALPHA_REFQUAD:
1741da177e4SLinus Torvalds 			/* BUG() can produce misaligned relocations. */
1751da177e4SLinus Torvalds 			((u32 *)location)[0] = value;
1761da177e4SLinus Torvalds 			((u32 *)location)[1] = value >> 32;
1771da177e4SLinus Torvalds 			break;
1781da177e4SLinus Torvalds 		case R_ALPHA_GPREL32:
1791da177e4SLinus Torvalds 			value -= gp;
1801da177e4SLinus Torvalds 			if ((int)value != value)
1811da177e4SLinus Torvalds 				goto reloc_overflow;
1821da177e4SLinus Torvalds 			*(u32 *)location = value;
1831da177e4SLinus Torvalds 			break;
1841da177e4SLinus Torvalds 		case R_ALPHA_LITERAL:
1851da177e4SLinus Torvalds 			hi = got + r_got_offset;
1861da177e4SLinus Torvalds 			lo = hi - gp;
1871da177e4SLinus Torvalds 			if ((short)lo != lo)
1881da177e4SLinus Torvalds 				goto reloc_overflow;
1891da177e4SLinus Torvalds 			*(u16 *)location = lo;
1901da177e4SLinus Torvalds 			*(u64 *)hi = value;
1911da177e4SLinus Torvalds 			break;
1921da177e4SLinus Torvalds 		case R_ALPHA_LITUSE:
1931da177e4SLinus Torvalds 			break;
1941da177e4SLinus Torvalds 		case R_ALPHA_GPDISP:
1951da177e4SLinus Torvalds 			value = gp - (u64)location;
1961da177e4SLinus Torvalds 			lo = (short)value;
1971da177e4SLinus Torvalds 			hi = (int)(value - lo);
1981da177e4SLinus Torvalds 			if (hi + lo != value)
1991da177e4SLinus Torvalds 				goto reloc_overflow;
2001da177e4SLinus Torvalds 			*(u16 *)location = hi >> 16;
2011da177e4SLinus Torvalds 			*(u16 *)(location + rela[i].r_addend) = lo;
2021da177e4SLinus Torvalds 			break;
2031da177e4SLinus Torvalds 		case R_ALPHA_BRSGP:
2041da177e4SLinus Torvalds 			/* BRSGP is only allowed to bind to local symbols.
2051da177e4SLinus Torvalds 			   If the section is undef, this means that the
2061da177e4SLinus Torvalds 			   value was resolved from somewhere else.  */
2071da177e4SLinus Torvalds 			if (sym->st_shndx == SHN_UNDEF)
2081da177e4SLinus Torvalds 				goto reloc_overflow;
2091da177e4SLinus Torvalds 			if ((sym->st_other & STO_ALPHA_STD_GPLOAD) ==
2101da177e4SLinus Torvalds 			    STO_ALPHA_STD_GPLOAD)
2111da177e4SLinus Torvalds 				/* Omit the prologue. */
2121da177e4SLinus Torvalds 				value += 8;
213df561f66SGustavo A. R. Silva 			fallthrough;
2141da177e4SLinus Torvalds 		case R_ALPHA_BRADDR:
2151da177e4SLinus Torvalds 			value -= (u64)location + 4;
2161da177e4SLinus Torvalds 			if (value & 3)
2171da177e4SLinus Torvalds 				goto reloc_overflow;
2181da177e4SLinus Torvalds 			value = (long)value >> 2;
2191da177e4SLinus Torvalds 			if (value + (1<<21) >= 1<<22)
2201da177e4SLinus Torvalds 				goto reloc_overflow;
2211da177e4SLinus Torvalds 			value &= 0x1fffff;
2221da177e4SLinus Torvalds 			value |= *(u32 *)location & ~0x1fffff;
2231da177e4SLinus Torvalds 			*(u32 *)location = value;
2241da177e4SLinus Torvalds 			break;
2251da177e4SLinus Torvalds 		case R_ALPHA_HINT:
2261da177e4SLinus Torvalds 			break;
2271da177e4SLinus Torvalds 		case R_ALPHA_SREL32:
2281da177e4SLinus Torvalds 			value -= (u64)location;
2291da177e4SLinus Torvalds 			if ((int)value != value)
2301da177e4SLinus Torvalds 				goto reloc_overflow;
2311da177e4SLinus Torvalds 			*(u32 *)location = value;
2321da177e4SLinus Torvalds 			break;
2331da177e4SLinus Torvalds 		case R_ALPHA_SREL64:
2341da177e4SLinus Torvalds 			value -= (u64)location;
2351da177e4SLinus Torvalds 			*(u64 *)location = value;
2361da177e4SLinus Torvalds 			break;
2371da177e4SLinus Torvalds 		case R_ALPHA_GPRELHIGH:
2381da177e4SLinus Torvalds 			value = (long)(value - gp + 0x8000) >> 16;
2391da177e4SLinus Torvalds 			if ((short) value != value)
2401da177e4SLinus Torvalds 				goto reloc_overflow;
2411da177e4SLinus Torvalds 			*(u16 *)location = value;
2421da177e4SLinus Torvalds 			break;
2431da177e4SLinus Torvalds 		case R_ALPHA_GPRELLOW:
2441da177e4SLinus Torvalds 			value -= gp;
2451da177e4SLinus Torvalds 			*(u16 *)location = value;
2461da177e4SLinus Torvalds 			break;
2471da177e4SLinus Torvalds 		case R_ALPHA_GPREL16:
2481da177e4SLinus Torvalds 			value -= gp;
2491da177e4SLinus Torvalds 			if ((short) value != value)
2501da177e4SLinus Torvalds 				goto reloc_overflow;
2511da177e4SLinus Torvalds 			*(u16 *)location = value;
2521da177e4SLinus Torvalds 			break;
2531da177e4SLinus Torvalds 		default:
2541da177e4SLinus Torvalds 			printk(KERN_ERR "module %s: Unknown relocation: %lu\n",
2551da177e4SLinus Torvalds 			       me->name, r_type);
2561da177e4SLinus Torvalds 			return -ENOEXEC;
2571da177e4SLinus Torvalds 		reloc_overflow:
2581da177e4SLinus Torvalds 			if (ELF64_ST_TYPE (sym->st_info) == STT_SECTION)
2591da177e4SLinus Torvalds 			  printk(KERN_ERR
2601b75b05bSIvan Kokshaysky 			         "module %s: Relocation (type %lu) overflow vs section %d\n",
2611b75b05bSIvan Kokshaysky 			         me->name, r_type, sym->st_shndx);
2621da177e4SLinus Torvalds 			else
2631da177e4SLinus Torvalds 			  printk(KERN_ERR
2641b75b05bSIvan Kokshaysky 			         "module %s: Relocation (type %lu) overflow vs %s\n",
2651b75b05bSIvan Kokshaysky 			         me->name, r_type, strtab + sym->st_name);
2661da177e4SLinus Torvalds 			return -ENOEXEC;
2671da177e4SLinus Torvalds 		}
2681da177e4SLinus Torvalds 	}
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 	return 0;
2711da177e4SLinus Torvalds }
272