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_hi20_rela(struct module *me, u32 *location, 96 Elf_Addr v) 97 { 98 s32 hi20; 99 100 if (IS_ENABLED(CMODEL_MEDLOW)) { 101 pr_err( 102 "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 103 me->name, v, location); 104 return -EINVAL; 105 } 106 107 hi20 = ((s32)v + 0x800) & 0xfffff000; 108 *location = (*location & 0xfff) | hi20; 109 return 0; 110 } 111 112 static int apply_r_riscv_lo12_i_rela(struct module *me, u32 *location, 113 Elf_Addr v) 114 { 115 /* Skip medlow checking because of filtering by HI20 already */ 116 s32 hi20 = ((s32)v + 0x800) & 0xfffff000; 117 s32 lo12 = ((s32)v - hi20); 118 *location = (*location & 0xfffff) | ((lo12 & 0xfff) << 20); 119 return 0; 120 } 121 122 static int apply_r_riscv_lo12_s_rela(struct module *me, u32 *location, 123 Elf_Addr v) 124 { 125 /* Skip medlow checking because of filtering by HI20 already */ 126 s32 hi20 = ((s32)v + 0x800) & 0xfffff000; 127 s32 lo12 = ((s32)v - hi20); 128 u32 imm11_5 = (lo12 & 0xfe0) << (31 - 11); 129 u32 imm4_0 = (lo12 & 0x1f) << (11 - 4); 130 *location = (*location & 0x1fff07f) | imm11_5 | imm4_0; 131 return 0; 132 } 133 134 static int apply_r_riscv_got_hi20_rela(struct module *me, u32 *location, 135 Elf_Addr v) 136 { 137 s64 offset = (void *)v - (void *)location; 138 s32 hi20; 139 140 /* Always emit the got entry */ 141 if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) { 142 offset = module_emit_got_entry(me, v); 143 offset = (void *)offset - (void *)location; 144 } else { 145 pr_err( 146 "%s: can not generate the GOT entry for symbol = %016llx from PC = %p\n", 147 me->name, v, location); 148 return -EINVAL; 149 } 150 151 hi20 = (offset + 0x800) & 0xfffff000; 152 *location = (*location & 0xfff) | hi20; 153 return 0; 154 } 155 156 static int apply_r_riscv_call_plt_rela(struct module *me, u32 *location, 157 Elf_Addr v) 158 { 159 s64 offset = (void *)v - (void *)location; 160 s32 fill_v = offset; 161 u32 hi20, lo12; 162 163 if (offset != fill_v) { 164 /* Only emit the plt entry if offset over 32-bit range */ 165 if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) { 166 offset = module_emit_plt_entry(me, v); 167 offset = (void *)offset - (void *)location; 168 } else { 169 pr_err( 170 "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 171 me->name, v, location); 172 return -EINVAL; 173 } 174 } 175 176 hi20 = (offset + 0x800) & 0xfffff000; 177 lo12 = (offset - hi20) & 0xfff; 178 *location = (*location & 0xfff) | hi20; 179 *(location + 1) = (*(location + 1) & 0xfffff) | (lo12 << 20); 180 return 0; 181 } 182 183 static int apply_r_riscv_call_rela(struct module *me, u32 *location, 184 Elf_Addr v) 185 { 186 s64 offset = (void *)v - (void *)location; 187 s32 fill_v = offset; 188 u32 hi20, lo12; 189 190 if (offset != fill_v) { 191 pr_err( 192 "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", 193 me->name, v, location); 194 return -EINVAL; 195 } 196 197 hi20 = (offset + 0x800) & 0xfffff000; 198 lo12 = (offset - hi20) & 0xfff; 199 *location = (*location & 0xfff) | hi20; 200 *(location + 1) = (*(location + 1) & 0xfffff) | (lo12 << 20); 201 return 0; 202 } 203 204 static int apply_r_riscv_relax_rela(struct module *me, u32 *location, 205 Elf_Addr v) 206 { 207 return 0; 208 } 209 210 static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, 211 Elf_Addr v) = { 212 [R_RISCV_64] = apply_r_riscv_64_rela, 213 [R_RISCV_BRANCH] = apply_r_riscv_branch_rela, 214 [R_RISCV_JAL] = apply_r_riscv_jal_rela, 215 [R_RISCV_PCREL_HI20] = apply_r_riscv_pcrel_hi20_rela, 216 [R_RISCV_PCREL_LO12_I] = apply_r_riscv_pcrel_lo12_i_rela, 217 [R_RISCV_PCREL_LO12_S] = apply_r_riscv_pcrel_lo12_s_rela, 218 [R_RISCV_HI20] = apply_r_riscv_hi20_rela, 219 [R_RISCV_LO12_I] = apply_r_riscv_lo12_i_rela, 220 [R_RISCV_LO12_S] = apply_r_riscv_lo12_s_rela, 221 [R_RISCV_GOT_HI20] = apply_r_riscv_got_hi20_rela, 222 [R_RISCV_CALL_PLT] = apply_r_riscv_call_plt_rela, 223 [R_RISCV_CALL] = apply_r_riscv_call_rela, 224 [R_RISCV_RELAX] = apply_r_riscv_relax_rela, 225 }; 226 227 int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, 228 unsigned int symindex, unsigned int relsec, 229 struct module *me) 230 { 231 Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr; 232 int (*handler)(struct module *me, u32 *location, Elf_Addr v); 233 Elf_Sym *sym; 234 u32 *location; 235 unsigned int i, type; 236 Elf_Addr v; 237 int res; 238 239 pr_debug("Applying relocate section %u to %u\n", relsec, 240 sechdrs[relsec].sh_info); 241 242 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 243 /* This is where to make the change */ 244 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 245 + rel[i].r_offset; 246 /* This is the symbol it is referring to */ 247 sym = (Elf_Sym *)sechdrs[symindex].sh_addr 248 + ELF_RISCV_R_SYM(rel[i].r_info); 249 if (IS_ERR_VALUE(sym->st_value)) { 250 /* Ignore unresolved weak symbol */ 251 if (ELF_ST_BIND(sym->st_info) == STB_WEAK) 252 continue; 253 pr_warning("%s: Unknown symbol %s\n", 254 me->name, strtab + sym->st_name); 255 return -ENOENT; 256 } 257 258 type = ELF_RISCV_R_TYPE(rel[i].r_info); 259 260 if (type < ARRAY_SIZE(reloc_handlers_rela)) 261 handler = reloc_handlers_rela[type]; 262 else 263 handler = NULL; 264 265 if (!handler) { 266 pr_err("%s: Unknown relocation type %u\n", 267 me->name, type); 268 return -EINVAL; 269 } 270 271 v = sym->st_value + rel[i].r_addend; 272 273 if (type == R_RISCV_PCREL_LO12_I || type == R_RISCV_PCREL_LO12_S) { 274 unsigned int j; 275 276 for (j = 0; j < sechdrs[relsec].sh_size / sizeof(*rel); j++) { 277 u64 hi20_loc = 278 sechdrs[sechdrs[relsec].sh_info].sh_addr 279 + rel[j].r_offset; 280 u32 hi20_type = ELF_RISCV_R_TYPE(rel[j].r_info); 281 282 /* Find the corresponding HI20 relocation entry */ 283 if (hi20_loc == sym->st_value 284 && (hi20_type == R_RISCV_PCREL_HI20 285 || hi20_type == R_RISCV_GOT_HI20)) { 286 s32 hi20, lo12; 287 Elf_Sym *hi20_sym = 288 (Elf_Sym *)sechdrs[symindex].sh_addr 289 + ELF_RISCV_R_SYM(rel[j].r_info); 290 u64 hi20_sym_val = 291 hi20_sym->st_value 292 + rel[j].r_addend; 293 294 /* Calculate lo12 */ 295 u64 offset = hi20_sym_val - hi20_loc; 296 if (IS_ENABLED(CONFIG_MODULE_SECTIONS) 297 && hi20_type == R_RISCV_GOT_HI20) { 298 offset = module_emit_got_entry( 299 me, hi20_sym_val); 300 offset = offset - hi20_loc; 301 } 302 hi20 = (offset + 0x800) & 0xfffff000; 303 lo12 = offset - hi20; 304 v = lo12; 305 306 break; 307 } 308 } 309 if (j == sechdrs[relsec].sh_size / sizeof(*rel)) { 310 pr_err( 311 "%s: Can not find HI20 relocation information\n", 312 me->name); 313 return -EINVAL; 314 } 315 } 316 317 res = handler(me, location, v); 318 if (res) 319 return res; 320 } 321 322 return 0; 323 } 324