1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Kernel module help for SH. 3 4 SHcompact version by Kaz Kojima and Paul Mundt. 5 6 SHmedia bits: 7 8 Copyright 2004 SuperH (UK) Ltd 9 Author: Richard Curnow 10 11 Based on the sh version, and on code from the sh64-specific parts of 12 modutils, originally written by Richard Curnow and Ben Gaster. 13 */ 14 #include <linux/moduleloader.h> 15 #include <linux/elf.h> 16 #include <linux/vmalloc.h> 17 #include <linux/bug.h> 18 #include <linux/fs.h> 19 #include <linux/string.h> 20 #include <linux/kernel.h> 21 #include <asm/unaligned.h> 22 #include <asm/dwarf.h> 23 24 int apply_relocate_add(Elf32_Shdr *sechdrs, 25 const char *strtab, 26 unsigned int symindex, 27 unsigned int relsec, 28 struct module *me) 29 { 30 unsigned int i; 31 Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; 32 Elf32_Sym *sym; 33 Elf32_Addr relocation; 34 uint32_t *location; 35 uint32_t value; 36 37 pr_debug("Applying relocate section %u to %u\n", relsec, 38 sechdrs[relsec].sh_info); 39 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 40 /* This is where to make the change */ 41 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 42 + rel[i].r_offset; 43 /* This is the symbol it is referring to. Note that all 44 undefined symbols have been resolved. */ 45 sym = (Elf32_Sym *)sechdrs[symindex].sh_addr 46 + ELF32_R_SYM(rel[i].r_info); 47 relocation = sym->st_value + rel[i].r_addend; 48 49 #ifdef CONFIG_SUPERH64 50 /* For text addresses, bit2 of the st_other field indicates 51 * whether the symbol is SHmedia (1) or SHcompact (0). If 52 * SHmedia, the LSB of the symbol needs to be asserted 53 * for the CPU to be in SHmedia mode when it starts executing 54 * the branch target. */ 55 relocation |= !!(sym->st_other & 4); 56 #endif 57 58 switch (ELF32_R_TYPE(rel[i].r_info)) { 59 case R_SH_NONE: 60 break; 61 case R_SH_DIR32: 62 value = get_unaligned(location); 63 value += relocation; 64 put_unaligned(value, location); 65 break; 66 case R_SH_REL32: 67 relocation = (relocation - (Elf32_Addr) location); 68 value = get_unaligned(location); 69 value += relocation; 70 put_unaligned(value, location); 71 break; 72 case R_SH_IMM_LOW16: 73 *location = (*location & ~0x3fffc00) | 74 ((relocation & 0xffff) << 10); 75 break; 76 case R_SH_IMM_MEDLOW16: 77 *location = (*location & ~0x3fffc00) | 78 (((relocation >> 16) & 0xffff) << 10); 79 break; 80 case R_SH_IMM_LOW16_PCREL: 81 relocation -= (Elf32_Addr) location; 82 *location = (*location & ~0x3fffc00) | 83 ((relocation & 0xffff) << 10); 84 break; 85 case R_SH_IMM_MEDLOW16_PCREL: 86 relocation -= (Elf32_Addr) location; 87 *location = (*location & ~0x3fffc00) | 88 (((relocation >> 16) & 0xffff) << 10); 89 break; 90 default: 91 printk(KERN_ERR "module %s: Unknown relocation: %u\n", 92 me->name, ELF32_R_TYPE(rel[i].r_info)); 93 return -ENOEXEC; 94 } 95 } 96 return 0; 97 } 98 99 int module_finalize(const Elf_Ehdr *hdr, 100 const Elf_Shdr *sechdrs, 101 struct module *me) 102 { 103 int ret = 0; 104 105 ret |= module_dwarf_finalize(hdr, sechdrs, me); 106 107 return ret; 108 } 109 110 void module_arch_cleanup(struct module *mod) 111 { 112 module_dwarf_cleanup(mod); 113 } 114