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