1 /* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License as published by 4 * the Free Software Foundation; either version 2 of the License, or 5 * (at your option) any later version. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 * 12 * Copyright (C) 2017 Zihao Yu 13 */ 14 15 #include <linux/elf.h> 16 #include <linux/err.h> 17 #include <linux/errno.h> 18 #include <linux/moduleloader.h> 19 20 static int apply_r_riscv_64_rela(struct module *me, u32 *location, Elf_Addr v) 21 { 22 *(u64 *)location = v; 23 return 0; 24 } 25 26 static int apply_r_riscv_branch_rela(struct module *me, u32 *location, 27 Elf_Addr v) 28 { 29 s64 offset = (void *)v - (void *)location; 30 u32 imm12 = (offset & 0x1000) << (31 - 12); 31 u32 imm11 = (offset & 0x800) >> (11 - 7); 32 u32 imm10_5 = (offset & 0x7e0) << (30 - 10); 33 u32 imm4_1 = (offset & 0x1e) << (11 - 4); 34 35 *location = (*location & 0x1fff07f) | imm12 | imm11 | imm10_5 | imm4_1; 36 return 0; 37 } 38 39 static int apply_r_riscv_jal_rela(struct module *me, u32 *location, 40 Elf_Addr v) 41 { 42 s64 offset = (void *)v - (void *)location; 43 u32 imm20 = (offset & 0x100000) << (31 - 20); 44 u32 imm19_12 = (offset & 0xff000); 45 u32 imm11 = (offset & 0x800) << (20 - 11); 46 u32 imm10_1 = (offset & 0x7fe) << (30 - 10); 47 48 *location = (*location & 0xfff) | imm20 | imm19_12 | imm11 | imm10_1; 49 return 0; 50 } 51 52 static int apply_r_riscv_pcrel_hi20_rela(struct module *me, u32 *location, 53 Elf_Addr v) 54 { 55 s64 offset = (void *)v - (void *)location; 56 s32 hi20; 57 58 if (offset != (s32)offset) { 59 pr_err( 60 "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 61 me->name, v, location); 62 return -EINVAL; 63 } 64 65 hi20 = (offset + 0x800) & 0xfffff000; 66 *location = (*location & 0xfff) | hi20; 67 return 0; 68 } 69 70 static int apply_r_riscv_pcrel_lo12_i_rela(struct module *me, u32 *location, 71 Elf_Addr v) 72 { 73 /* 74 * v is the lo12 value to fill. It is calculated before calling this 75 * handler. 76 */ 77 *location = (*location & 0xfffff) | ((v & 0xfff) << 20); 78 return 0; 79 } 80 81 static int apply_r_riscv_pcrel_lo12_s_rela(struct module *me, u32 *location, 82 Elf_Addr v) 83 { 84 /* 85 * v is the lo12 value to fill. It is calculated before calling this 86 * handler. 87 */ 88 u32 imm11_5 = (v & 0xfe0) << (31 - 11); 89 u32 imm4_0 = (v & 0x1f) << (11 - 4); 90 91 *location = (*location & 0x1fff07f) | imm11_5 | imm4_0; 92 return 0; 93 } 94 95 static int apply_r_riscv_call_plt_rela(struct module *me, u32 *location, 96 Elf_Addr v) 97 { 98 s64 offset = (void *)v - (void *)location; 99 s32 fill_v = offset; 100 u32 hi20, lo12; 101 102 if (offset != fill_v) { 103 pr_err( 104 "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 105 me->name, v, location); 106 return -EINVAL; 107 } 108 109 hi20 = (offset + 0x800) & 0xfffff000; 110 lo12 = (offset - hi20) & 0xfff; 111 *location = (*location & 0xfff) | hi20; 112 *(location + 1) = (*(location + 1) & 0xfffff) | (lo12 << 20); 113 return 0; 114 } 115 116 static int apply_r_riscv_relax_rela(struct module *me, u32 *location, 117 Elf_Addr v) 118 { 119 return 0; 120 } 121 122 static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, 123 Elf_Addr v) = { 124 [R_RISCV_64] = apply_r_riscv_64_rela, 125 [R_RISCV_BRANCH] = apply_r_riscv_branch_rela, 126 [R_RISCV_JAL] = apply_r_riscv_jal_rela, 127 [R_RISCV_PCREL_HI20] = apply_r_riscv_pcrel_hi20_rela, 128 [R_RISCV_PCREL_LO12_I] = apply_r_riscv_pcrel_lo12_i_rela, 129 [R_RISCV_PCREL_LO12_S] = apply_r_riscv_pcrel_lo12_s_rela, 130 [R_RISCV_CALL_PLT] = apply_r_riscv_call_plt_rela, 131 [R_RISCV_RELAX] = apply_r_riscv_relax_rela, 132 }; 133 134 int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, 135 unsigned int symindex, unsigned int relsec, 136 struct module *me) 137 { 138 Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr; 139 int (*handler)(struct module *me, u32 *location, Elf_Addr v); 140 Elf_Sym *sym; 141 u32 *location; 142 unsigned int i, type; 143 Elf_Addr v; 144 int res; 145 146 pr_debug("Applying relocate section %u to %u\n", relsec, 147 sechdrs[relsec].sh_info); 148 149 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 150 /* This is where to make the change */ 151 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 152 + rel[i].r_offset; 153 /* This is the symbol it is referring to */ 154 sym = (Elf_Sym *)sechdrs[symindex].sh_addr 155 + ELF_RISCV_R_SYM(rel[i].r_info); 156 if (IS_ERR_VALUE(sym->st_value)) { 157 /* Ignore unresolved weak symbol */ 158 if (ELF_ST_BIND(sym->st_info) == STB_WEAK) 159 continue; 160 pr_warning("%s: Unknown symbol %s\n", 161 me->name, strtab + sym->st_name); 162 return -ENOENT; 163 } 164 165 type = ELF_RISCV_R_TYPE(rel[i].r_info); 166 167 if (type < ARRAY_SIZE(reloc_handlers_rela)) 168 handler = reloc_handlers_rela[type]; 169 else 170 handler = NULL; 171 172 if (!handler) { 173 pr_err("%s: Unknown relocation type %u\n", 174 me->name, type); 175 return -EINVAL; 176 } 177 178 v = sym->st_value + rel[i].r_addend; 179 180 if (type == R_RISCV_PCREL_LO12_I || type == R_RISCV_PCREL_LO12_S) { 181 unsigned int j; 182 183 for (j = 0; j < sechdrs[relsec].sh_size / sizeof(*rel); j++) { 184 u64 hi20_loc = 185 sechdrs[sechdrs[relsec].sh_info].sh_addr 186 + rel[j].r_offset; 187 /* Find the corresponding HI20 PC-relative relocation entry */ 188 if (hi20_loc == sym->st_value) { 189 Elf_Sym *hi20_sym = 190 (Elf_Sym *)sechdrs[symindex].sh_addr 191 + ELF_RISCV_R_SYM(rel[j].r_info); 192 u64 hi20_sym_val = 193 hi20_sym->st_value 194 + rel[j].r_addend; 195 /* Calculate lo12 */ 196 s64 offset = hi20_sym_val - hi20_loc; 197 s32 hi20 = (offset + 0x800) & 0xfffff000; 198 s32 lo12 = offset - hi20; 199 v = lo12; 200 break; 201 } 202 } 203 if (j == sechdrs[relsec].sh_size / sizeof(*rel)) { 204 pr_err( 205 "%s: Can not find HI20 PC-relative relocation information\n", 206 me->name); 207 return -EINVAL; 208 } 209 } 210 211 res = handler(me, location, v); 212 if (res) 213 return res; 214 } 215 216 return 0; 217 } 218