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