1fcdfe9d2SHuacai Chen // SPDX-License-Identifier: GPL-2.0+ 2fcdfe9d2SHuacai Chen /* 3fcdfe9d2SHuacai Chen * Author: Hanlu Li <lihanlu@loongson.cn> 4fcdfe9d2SHuacai Chen * Huacai Chen <chenhuacai@loongson.cn> 5fcdfe9d2SHuacai Chen * 6fcdfe9d2SHuacai Chen * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 7fcdfe9d2SHuacai Chen */ 8fcdfe9d2SHuacai Chen 9fcdfe9d2SHuacai Chen #define pr_fmt(fmt) "kmod: " fmt 10fcdfe9d2SHuacai Chen 11fcdfe9d2SHuacai Chen #include <linux/moduleloader.h> 12fcdfe9d2SHuacai Chen #include <linux/elf.h> 13fcdfe9d2SHuacai Chen #include <linux/mm.h> 14d4b6f156SHuacai Chen #include <linux/numa.h> 15fcdfe9d2SHuacai Chen #include <linux/vmalloc.h> 16fcdfe9d2SHuacai Chen #include <linux/slab.h> 17fcdfe9d2SHuacai Chen #include <linux/fs.h> 18fcdfe9d2SHuacai Chen #include <linux/string.h> 19fcdfe9d2SHuacai Chen #include <linux/kernel.h> 20*19e5eb15SHuacai Chen #include <asm/alternative.h> 21fcdfe9d2SHuacai Chen 22fcdfe9d2SHuacai Chen static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top) 23fcdfe9d2SHuacai Chen { 24fcdfe9d2SHuacai Chen if (*rela_stack_top >= RELA_STACK_DEPTH) 25fcdfe9d2SHuacai Chen return -ENOEXEC; 26fcdfe9d2SHuacai Chen 27fcdfe9d2SHuacai Chen rela_stack[(*rela_stack_top)++] = stack_value; 28fcdfe9d2SHuacai Chen pr_debug("%s stack_value = 0x%llx\n", __func__, stack_value); 29fcdfe9d2SHuacai Chen 30fcdfe9d2SHuacai Chen return 0; 31fcdfe9d2SHuacai Chen } 32fcdfe9d2SHuacai Chen 33fcdfe9d2SHuacai Chen static int rela_stack_pop(s64 *stack_value, s64 *rela_stack, size_t *rela_stack_top) 34fcdfe9d2SHuacai Chen { 35fcdfe9d2SHuacai Chen if (*rela_stack_top == 0) 36fcdfe9d2SHuacai Chen return -ENOEXEC; 37fcdfe9d2SHuacai Chen 38fcdfe9d2SHuacai Chen *stack_value = rela_stack[--(*rela_stack_top)]; 39fcdfe9d2SHuacai Chen pr_debug("%s stack_value = 0x%llx\n", __func__, *stack_value); 40fcdfe9d2SHuacai Chen 41fcdfe9d2SHuacai Chen return 0; 42fcdfe9d2SHuacai Chen } 43fcdfe9d2SHuacai Chen 44fcdfe9d2SHuacai Chen static int apply_r_larch_none(struct module *mod, u32 *location, Elf_Addr v, 45fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 46fcdfe9d2SHuacai Chen { 47fcdfe9d2SHuacai Chen return 0; 48fcdfe9d2SHuacai Chen } 49fcdfe9d2SHuacai Chen 50fcdfe9d2SHuacai Chen static int apply_r_larch_error(struct module *me, u32 *location, Elf_Addr v, 51fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 52fcdfe9d2SHuacai Chen { 53fcdfe9d2SHuacai Chen pr_err("%s: Unsupport relocation type %u, please add its support.\n", me->name, type); 54fcdfe9d2SHuacai Chen return -EINVAL; 55fcdfe9d2SHuacai Chen } 56fcdfe9d2SHuacai Chen 57fcdfe9d2SHuacai Chen static int apply_r_larch_32(struct module *mod, u32 *location, Elf_Addr v, 58fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 59fcdfe9d2SHuacai Chen { 60fcdfe9d2SHuacai Chen *location = v; 61fcdfe9d2SHuacai Chen return 0; 62fcdfe9d2SHuacai Chen } 63fcdfe9d2SHuacai Chen 64fcdfe9d2SHuacai Chen static int apply_r_larch_64(struct module *mod, u32 *location, Elf_Addr v, 65fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 66fcdfe9d2SHuacai Chen { 67fcdfe9d2SHuacai Chen *(Elf_Addr *)location = v; 68fcdfe9d2SHuacai Chen return 0; 69fcdfe9d2SHuacai Chen } 70fcdfe9d2SHuacai Chen 71fcdfe9d2SHuacai Chen static int apply_r_larch_sop_push_pcrel(struct module *mod, u32 *location, Elf_Addr v, 72fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 73fcdfe9d2SHuacai Chen { 74fcdfe9d2SHuacai Chen return rela_stack_push(v - (u64)location, rela_stack, rela_stack_top); 75fcdfe9d2SHuacai Chen } 76fcdfe9d2SHuacai Chen 77fcdfe9d2SHuacai Chen static int apply_r_larch_sop_push_absolute(struct module *mod, u32 *location, Elf_Addr v, 78fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 79fcdfe9d2SHuacai Chen { 80fcdfe9d2SHuacai Chen return rela_stack_push(v, rela_stack, rela_stack_top); 81fcdfe9d2SHuacai Chen } 82fcdfe9d2SHuacai Chen 83fcdfe9d2SHuacai Chen static int apply_r_larch_sop_push_dup(struct module *mod, u32 *location, Elf_Addr v, 84fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 85fcdfe9d2SHuacai Chen { 86fcdfe9d2SHuacai Chen int err = 0; 87fcdfe9d2SHuacai Chen s64 opr1; 88fcdfe9d2SHuacai Chen 89fcdfe9d2SHuacai Chen err = rela_stack_pop(&opr1, rela_stack, rela_stack_top); 90fcdfe9d2SHuacai Chen if (err) 91fcdfe9d2SHuacai Chen return err; 92fcdfe9d2SHuacai Chen err = rela_stack_push(opr1, rela_stack, rela_stack_top); 93fcdfe9d2SHuacai Chen if (err) 94fcdfe9d2SHuacai Chen return err; 95fcdfe9d2SHuacai Chen err = rela_stack_push(opr1, rela_stack, rela_stack_top); 96fcdfe9d2SHuacai Chen if (err) 97fcdfe9d2SHuacai Chen return err; 98fcdfe9d2SHuacai Chen 99fcdfe9d2SHuacai Chen return 0; 100fcdfe9d2SHuacai Chen } 101fcdfe9d2SHuacai Chen 102fcdfe9d2SHuacai Chen static int apply_r_larch_sop_push_plt_pcrel(struct module *mod, u32 *location, Elf_Addr v, 103fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 104fcdfe9d2SHuacai Chen { 105fcdfe9d2SHuacai Chen ptrdiff_t offset = (void *)v - (void *)location; 106fcdfe9d2SHuacai Chen 107fcdfe9d2SHuacai Chen if (offset >= SZ_128M) 108fcdfe9d2SHuacai Chen v = module_emit_plt_entry(mod, v); 109fcdfe9d2SHuacai Chen 110fcdfe9d2SHuacai Chen if (offset < -SZ_128M) 111fcdfe9d2SHuacai Chen v = module_emit_plt_entry(mod, v); 112fcdfe9d2SHuacai Chen 113fcdfe9d2SHuacai Chen return apply_r_larch_sop_push_pcrel(mod, location, v, rela_stack, rela_stack_top, type); 114fcdfe9d2SHuacai Chen } 115fcdfe9d2SHuacai Chen 116fcdfe9d2SHuacai Chen static int apply_r_larch_sop(struct module *mod, u32 *location, Elf_Addr v, 117fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 118fcdfe9d2SHuacai Chen { 119fcdfe9d2SHuacai Chen int err = 0; 120fcdfe9d2SHuacai Chen s64 opr1, opr2, opr3; 121fcdfe9d2SHuacai Chen 122fcdfe9d2SHuacai Chen if (type == R_LARCH_SOP_IF_ELSE) { 123fcdfe9d2SHuacai Chen err = rela_stack_pop(&opr3, rela_stack, rela_stack_top); 124fcdfe9d2SHuacai Chen if (err) 125fcdfe9d2SHuacai Chen return err; 126fcdfe9d2SHuacai Chen } 127fcdfe9d2SHuacai Chen 128fcdfe9d2SHuacai Chen err = rela_stack_pop(&opr2, rela_stack, rela_stack_top); 129fcdfe9d2SHuacai Chen if (err) 130fcdfe9d2SHuacai Chen return err; 131fcdfe9d2SHuacai Chen err = rela_stack_pop(&opr1, rela_stack, rela_stack_top); 132fcdfe9d2SHuacai Chen if (err) 133fcdfe9d2SHuacai Chen return err; 134fcdfe9d2SHuacai Chen 135fcdfe9d2SHuacai Chen switch (type) { 136fcdfe9d2SHuacai Chen case R_LARCH_SOP_AND: 137fcdfe9d2SHuacai Chen err = rela_stack_push(opr1 & opr2, rela_stack, rela_stack_top); 138fcdfe9d2SHuacai Chen break; 139fcdfe9d2SHuacai Chen case R_LARCH_SOP_ADD: 140fcdfe9d2SHuacai Chen err = rela_stack_push(opr1 + opr2, rela_stack, rela_stack_top); 141fcdfe9d2SHuacai Chen break; 142fcdfe9d2SHuacai Chen case R_LARCH_SOP_SUB: 143fcdfe9d2SHuacai Chen err = rela_stack_push(opr1 - opr2, rela_stack, rela_stack_top); 144fcdfe9d2SHuacai Chen break; 145fcdfe9d2SHuacai Chen case R_LARCH_SOP_SL: 146fcdfe9d2SHuacai Chen err = rela_stack_push(opr1 << opr2, rela_stack, rela_stack_top); 147fcdfe9d2SHuacai Chen break; 148fcdfe9d2SHuacai Chen case R_LARCH_SOP_SR: 149fcdfe9d2SHuacai Chen err = rela_stack_push(opr1 >> opr2, rela_stack, rela_stack_top); 150fcdfe9d2SHuacai Chen break; 151fcdfe9d2SHuacai Chen case R_LARCH_SOP_IF_ELSE: 152fcdfe9d2SHuacai Chen err = rela_stack_push(opr1 ? opr2 : opr3, rela_stack, rela_stack_top); 153fcdfe9d2SHuacai Chen break; 154fcdfe9d2SHuacai Chen default: 155fcdfe9d2SHuacai Chen pr_err("%s: Unsupport relocation type %u\n", mod->name, type); 156fcdfe9d2SHuacai Chen return -EINVAL; 157fcdfe9d2SHuacai Chen } 158fcdfe9d2SHuacai Chen 159fcdfe9d2SHuacai Chen return err; 160fcdfe9d2SHuacai Chen } 161fcdfe9d2SHuacai Chen 162fcdfe9d2SHuacai Chen static int apply_r_larch_sop_imm_field(struct module *mod, u32 *location, Elf_Addr v, 163fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 164fcdfe9d2SHuacai Chen { 165fcdfe9d2SHuacai Chen int err = 0; 166fcdfe9d2SHuacai Chen s64 opr1; 167fcdfe9d2SHuacai Chen union loongarch_instruction *insn = (union loongarch_instruction *)location; 168fcdfe9d2SHuacai Chen 169fcdfe9d2SHuacai Chen err = rela_stack_pop(&opr1, rela_stack, rela_stack_top); 170fcdfe9d2SHuacai Chen if (err) 171fcdfe9d2SHuacai Chen return err; 172fcdfe9d2SHuacai Chen 173fcdfe9d2SHuacai Chen switch (type) { 174fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_U_10_12: 175fcdfe9d2SHuacai Chen if (!unsigned_imm_check(opr1, 12)) 176fcdfe9d2SHuacai Chen goto overflow; 177fcdfe9d2SHuacai Chen 178fcdfe9d2SHuacai Chen /* (*(uint32_t *) PC) [21 ... 10] = opr [11 ... 0] */ 179fcdfe9d2SHuacai Chen insn->reg2i12_format.immediate = opr1 & 0xfff; 180fcdfe9d2SHuacai Chen return 0; 181fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_S_10_12: 182fcdfe9d2SHuacai Chen if (!signed_imm_check(opr1, 12)) 183fcdfe9d2SHuacai Chen goto overflow; 184fcdfe9d2SHuacai Chen 185fcdfe9d2SHuacai Chen insn->reg2i12_format.immediate = opr1 & 0xfff; 186fcdfe9d2SHuacai Chen return 0; 187fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_S_10_16: 188fcdfe9d2SHuacai Chen if (!signed_imm_check(opr1, 16)) 189fcdfe9d2SHuacai Chen goto overflow; 190fcdfe9d2SHuacai Chen 191fcdfe9d2SHuacai Chen insn->reg2i16_format.immediate = opr1 & 0xffff; 192fcdfe9d2SHuacai Chen return 0; 193fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_S_10_16_S2: 194fcdfe9d2SHuacai Chen if (opr1 % 4) 195fcdfe9d2SHuacai Chen goto unaligned; 196fcdfe9d2SHuacai Chen 197fcdfe9d2SHuacai Chen if (!signed_imm_check(opr1, 18)) 198fcdfe9d2SHuacai Chen goto overflow; 199fcdfe9d2SHuacai Chen 200fcdfe9d2SHuacai Chen insn->reg2i16_format.immediate = (opr1 >> 2) & 0xffff; 201fcdfe9d2SHuacai Chen return 0; 202fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_S_5_20: 203fcdfe9d2SHuacai Chen if (!signed_imm_check(opr1, 20)) 204fcdfe9d2SHuacai Chen goto overflow; 205fcdfe9d2SHuacai Chen 206fcdfe9d2SHuacai Chen insn->reg1i20_format.immediate = (opr1) & 0xfffff; 207fcdfe9d2SHuacai Chen return 0; 208fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_S_0_5_10_16_S2: 209fcdfe9d2SHuacai Chen if (opr1 % 4) 210fcdfe9d2SHuacai Chen goto unaligned; 211fcdfe9d2SHuacai Chen 212fcdfe9d2SHuacai Chen if (!signed_imm_check(opr1, 23)) 213fcdfe9d2SHuacai Chen goto overflow; 214fcdfe9d2SHuacai Chen 215fcdfe9d2SHuacai Chen opr1 >>= 2; 216fcdfe9d2SHuacai Chen insn->reg1i21_format.immediate_l = opr1 & 0xffff; 217fcdfe9d2SHuacai Chen insn->reg1i21_format.immediate_h = (opr1 >> 16) & 0x1f; 218fcdfe9d2SHuacai Chen return 0; 219fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_S_0_10_10_16_S2: 220fcdfe9d2SHuacai Chen if (opr1 % 4) 221fcdfe9d2SHuacai Chen goto unaligned; 222fcdfe9d2SHuacai Chen 223fcdfe9d2SHuacai Chen if (!signed_imm_check(opr1, 28)) 224fcdfe9d2SHuacai Chen goto overflow; 225fcdfe9d2SHuacai Chen 226fcdfe9d2SHuacai Chen opr1 >>= 2; 227fcdfe9d2SHuacai Chen insn->reg0i26_format.immediate_l = opr1 & 0xffff; 228fcdfe9d2SHuacai Chen insn->reg0i26_format.immediate_h = (opr1 >> 16) & 0x3ff; 229fcdfe9d2SHuacai Chen return 0; 230fcdfe9d2SHuacai Chen case R_LARCH_SOP_POP_32_U: 231fcdfe9d2SHuacai Chen if (!unsigned_imm_check(opr1, 32)) 232fcdfe9d2SHuacai Chen goto overflow; 233fcdfe9d2SHuacai Chen 234fcdfe9d2SHuacai Chen /* (*(uint32_t *) PC) = opr */ 235fcdfe9d2SHuacai Chen *location = (u32)opr1; 236fcdfe9d2SHuacai Chen return 0; 237fcdfe9d2SHuacai Chen default: 238fcdfe9d2SHuacai Chen pr_err("%s: Unsupport relocation type %u\n", mod->name, type); 239fcdfe9d2SHuacai Chen return -EINVAL; 240fcdfe9d2SHuacai Chen } 241fcdfe9d2SHuacai Chen 242fcdfe9d2SHuacai Chen overflow: 243fcdfe9d2SHuacai Chen pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s (%u) relocation\n", 244fcdfe9d2SHuacai Chen mod->name, opr1, __func__, type); 245fcdfe9d2SHuacai Chen return -ENOEXEC; 246fcdfe9d2SHuacai Chen 247fcdfe9d2SHuacai Chen unaligned: 248fcdfe9d2SHuacai Chen pr_err("module %s: opr1 = 0x%llx unaligned! dangerous %s (%u) relocation\n", 249fcdfe9d2SHuacai Chen mod->name, opr1, __func__, type); 250fcdfe9d2SHuacai Chen return -ENOEXEC; 251fcdfe9d2SHuacai Chen } 252fcdfe9d2SHuacai Chen 253fcdfe9d2SHuacai Chen static int apply_r_larch_add_sub(struct module *mod, u32 *location, Elf_Addr v, 254fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 255fcdfe9d2SHuacai Chen { 256fcdfe9d2SHuacai Chen switch (type) { 257fcdfe9d2SHuacai Chen case R_LARCH_ADD32: 258fcdfe9d2SHuacai Chen *(s32 *)location += v; 259fcdfe9d2SHuacai Chen return 0; 260fcdfe9d2SHuacai Chen case R_LARCH_ADD64: 261fcdfe9d2SHuacai Chen *(s64 *)location += v; 262fcdfe9d2SHuacai Chen return 0; 263fcdfe9d2SHuacai Chen case R_LARCH_SUB32: 264fcdfe9d2SHuacai Chen *(s32 *)location -= v; 265fcdfe9d2SHuacai Chen return 0; 266fcdfe9d2SHuacai Chen case R_LARCH_SUB64: 267fcdfe9d2SHuacai Chen *(s64 *)location -= v; 268fcdfe9d2SHuacai Chen return 0; 269fcdfe9d2SHuacai Chen default: 270fcdfe9d2SHuacai Chen pr_err("%s: Unsupport relocation type %u\n", mod->name, type); 271fcdfe9d2SHuacai Chen return -EINVAL; 272fcdfe9d2SHuacai Chen } 273fcdfe9d2SHuacai Chen } 274fcdfe9d2SHuacai Chen 2759bd1e380SXi Ruoyao static int apply_r_larch_b26(struct module *mod, u32 *location, Elf_Addr v, 2769bd1e380SXi Ruoyao s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 2779bd1e380SXi Ruoyao { 2789bd1e380SXi Ruoyao ptrdiff_t offset = (void *)v - (void *)location; 2799bd1e380SXi Ruoyao union loongarch_instruction *insn = (union loongarch_instruction *)location; 2809bd1e380SXi Ruoyao 2819bd1e380SXi Ruoyao if (offset >= SZ_128M) 2829bd1e380SXi Ruoyao v = module_emit_plt_entry(mod, v); 2839bd1e380SXi Ruoyao 2849bd1e380SXi Ruoyao if (offset < -SZ_128M) 2859bd1e380SXi Ruoyao v = module_emit_plt_entry(mod, v); 2869bd1e380SXi Ruoyao 2879bd1e380SXi Ruoyao offset = (void *)v - (void *)location; 2889bd1e380SXi Ruoyao 2899bd1e380SXi Ruoyao if (offset & 3) { 2909bd1e380SXi Ruoyao pr_err("module %s: jump offset = 0x%llx unaligned! dangerous R_LARCH_B26 (%u) relocation\n", 2919bd1e380SXi Ruoyao mod->name, (long long)offset, type); 2929bd1e380SXi Ruoyao return -ENOEXEC; 2939bd1e380SXi Ruoyao } 2949bd1e380SXi Ruoyao 2959bd1e380SXi Ruoyao if (!signed_imm_check(offset, 28)) { 2969bd1e380SXi Ruoyao pr_err("module %s: jump offset = 0x%llx overflow! dangerous R_LARCH_B26 (%u) relocation\n", 2979bd1e380SXi Ruoyao mod->name, (long long)offset, type); 2989bd1e380SXi Ruoyao return -ENOEXEC; 2999bd1e380SXi Ruoyao } 3009bd1e380SXi Ruoyao 3019bd1e380SXi Ruoyao offset >>= 2; 3029bd1e380SXi Ruoyao insn->reg0i26_format.immediate_l = offset & 0xffff; 3039bd1e380SXi Ruoyao insn->reg0i26_format.immediate_h = (offset >> 16) & 0x3ff; 3049bd1e380SXi Ruoyao 3059bd1e380SXi Ruoyao return 0; 3069bd1e380SXi Ruoyao } 3079bd1e380SXi Ruoyao 3089bd1e380SXi Ruoyao static int apply_r_larch_pcala(struct module *mod, u32 *location, Elf_Addr v, 3099bd1e380SXi Ruoyao s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 3109bd1e380SXi Ruoyao { 3119bd1e380SXi Ruoyao union loongarch_instruction *insn = (union loongarch_instruction *)location; 3129bd1e380SXi Ruoyao /* Use s32 for a sign-extension deliberately. */ 3139bd1e380SXi Ruoyao s32 offset_hi20 = (void *)((v + 0x800) & ~0xfff) - 3149bd1e380SXi Ruoyao (void *)((Elf_Addr)location & ~0xfff); 3159bd1e380SXi Ruoyao Elf_Addr anchor = (((Elf_Addr)location) & ~0xfff) + offset_hi20; 3169bd1e380SXi Ruoyao ptrdiff_t offset_rem = (void *)v - (void *)anchor; 3179bd1e380SXi Ruoyao 3189bd1e380SXi Ruoyao switch (type) { 3199bd1e380SXi Ruoyao case R_LARCH_PCALA_LO12: 3209bd1e380SXi Ruoyao insn->reg2i12_format.immediate = v & 0xfff; 3219bd1e380SXi Ruoyao break; 3229bd1e380SXi Ruoyao case R_LARCH_PCALA_HI20: 3239bd1e380SXi Ruoyao v = offset_hi20 >> 12; 3249bd1e380SXi Ruoyao insn->reg1i20_format.immediate = v & 0xfffff; 3259bd1e380SXi Ruoyao break; 3269bd1e380SXi Ruoyao case R_LARCH_PCALA64_LO20: 3279bd1e380SXi Ruoyao v = offset_rem >> 32; 3289bd1e380SXi Ruoyao insn->reg1i20_format.immediate = v & 0xfffff; 3299bd1e380SXi Ruoyao break; 3309bd1e380SXi Ruoyao case R_LARCH_PCALA64_HI12: 3319bd1e380SXi Ruoyao v = offset_rem >> 52; 3329bd1e380SXi Ruoyao insn->reg2i12_format.immediate = v & 0xfff; 3339bd1e380SXi Ruoyao break; 3349bd1e380SXi Ruoyao default: 3359bd1e380SXi Ruoyao pr_err("%s: Unsupport relocation type %u\n", mod->name, type); 3369bd1e380SXi Ruoyao return -EINVAL; 3379bd1e380SXi Ruoyao } 3389bd1e380SXi Ruoyao 3399bd1e380SXi Ruoyao return 0; 3409bd1e380SXi Ruoyao } 3419bd1e380SXi Ruoyao 34259b3d4a9SXi Ruoyao static int apply_r_larch_got_pc(struct module *mod, u32 *location, Elf_Addr v, 34359b3d4a9SXi Ruoyao s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 34459b3d4a9SXi Ruoyao { 34559b3d4a9SXi Ruoyao Elf_Addr got = module_emit_got_entry(mod, v); 34659b3d4a9SXi Ruoyao 34759b3d4a9SXi Ruoyao if (!got) 34859b3d4a9SXi Ruoyao return -EINVAL; 34959b3d4a9SXi Ruoyao 35059b3d4a9SXi Ruoyao switch (type) { 35159b3d4a9SXi Ruoyao case R_LARCH_GOT_PC_LO12: 35259b3d4a9SXi Ruoyao type = R_LARCH_PCALA_LO12; 35359b3d4a9SXi Ruoyao break; 35459b3d4a9SXi Ruoyao case R_LARCH_GOT_PC_HI20: 35559b3d4a9SXi Ruoyao type = R_LARCH_PCALA_HI20; 35659b3d4a9SXi Ruoyao break; 35759b3d4a9SXi Ruoyao default: 35859b3d4a9SXi Ruoyao pr_err("%s: Unsupport relocation type %u\n", mod->name, type); 35959b3d4a9SXi Ruoyao return -EINVAL; 36059b3d4a9SXi Ruoyao } 36159b3d4a9SXi Ruoyao 36259b3d4a9SXi Ruoyao return apply_r_larch_pcala(mod, location, got, rela_stack, rela_stack_top, type); 36359b3d4a9SXi Ruoyao } 36459b3d4a9SXi Ruoyao 365fcdfe9d2SHuacai Chen /* 366fcdfe9d2SHuacai Chen * reloc_handlers_rela() - Apply a particular relocation to a module 367fcdfe9d2SHuacai Chen * @mod: the module to apply the reloc to 368fcdfe9d2SHuacai Chen * @location: the address at which the reloc is to be applied 369fcdfe9d2SHuacai Chen * @v: the value of the reloc, with addend for RELA-style 370fcdfe9d2SHuacai Chen * @rela_stack: the stack used for store relocation info, LOCAL to THIS module 371fcdfe9d2SHuacai Chen * @rela_stac_top: where the stack operation(pop/push) applies to 372fcdfe9d2SHuacai Chen * 373fcdfe9d2SHuacai Chen * Return: 0 upon success, else -ERRNO 374fcdfe9d2SHuacai Chen */ 375fcdfe9d2SHuacai Chen typedef int (*reloc_rela_handler)(struct module *mod, u32 *location, Elf_Addr v, 376fcdfe9d2SHuacai Chen s64 *rela_stack, size_t *rela_stack_top, unsigned int type); 377fcdfe9d2SHuacai Chen 378fcdfe9d2SHuacai Chen /* The handlers for known reloc types */ 379fcdfe9d2SHuacai Chen static reloc_rela_handler reloc_rela_handlers[] = { 3800a75e5d1SXi Ruoyao [R_LARCH_NONE ... R_LARCH_RELAX] = apply_r_larch_error, 381fcdfe9d2SHuacai Chen 382fcdfe9d2SHuacai Chen [R_LARCH_NONE] = apply_r_larch_none, 383fcdfe9d2SHuacai Chen [R_LARCH_32] = apply_r_larch_32, 384fcdfe9d2SHuacai Chen [R_LARCH_64] = apply_r_larch_64, 385fcdfe9d2SHuacai Chen [R_LARCH_MARK_LA] = apply_r_larch_none, 386fcdfe9d2SHuacai Chen [R_LARCH_MARK_PCREL] = apply_r_larch_none, 387fcdfe9d2SHuacai Chen [R_LARCH_SOP_PUSH_PCREL] = apply_r_larch_sop_push_pcrel, 388fcdfe9d2SHuacai Chen [R_LARCH_SOP_PUSH_ABSOLUTE] = apply_r_larch_sop_push_absolute, 389fcdfe9d2SHuacai Chen [R_LARCH_SOP_PUSH_DUP] = apply_r_larch_sop_push_dup, 390fcdfe9d2SHuacai Chen [R_LARCH_SOP_PUSH_PLT_PCREL] = apply_r_larch_sop_push_plt_pcrel, 391fcdfe9d2SHuacai Chen [R_LARCH_SOP_SUB ... R_LARCH_SOP_IF_ELSE] = apply_r_larch_sop, 392fcdfe9d2SHuacai Chen [R_LARCH_SOP_POP_32_S_10_5 ... R_LARCH_SOP_POP_32_U] = apply_r_larch_sop_imm_field, 393fcdfe9d2SHuacai Chen [R_LARCH_ADD32 ... R_LARCH_SUB64] = apply_r_larch_add_sub, 3949bd1e380SXi Ruoyao [R_LARCH_B26] = apply_r_larch_b26, 3959bd1e380SXi Ruoyao [R_LARCH_PCALA_HI20...R_LARCH_PCALA64_HI12] = apply_r_larch_pcala, 39659b3d4a9SXi Ruoyao [R_LARCH_GOT_PC_HI20...R_LARCH_GOT_PC_LO12] = apply_r_larch_got_pc, 397fcdfe9d2SHuacai Chen }; 398fcdfe9d2SHuacai Chen 399fcdfe9d2SHuacai Chen int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, 400fcdfe9d2SHuacai Chen unsigned int symindex, unsigned int relsec, 401fcdfe9d2SHuacai Chen struct module *mod) 402fcdfe9d2SHuacai Chen { 403fcdfe9d2SHuacai Chen int i, err; 404fcdfe9d2SHuacai Chen unsigned int type; 405fcdfe9d2SHuacai Chen s64 rela_stack[RELA_STACK_DEPTH]; 406fcdfe9d2SHuacai Chen size_t rela_stack_top = 0; 407fcdfe9d2SHuacai Chen reloc_rela_handler handler; 408fcdfe9d2SHuacai Chen void *location; 409fcdfe9d2SHuacai Chen Elf_Addr v; 410fcdfe9d2SHuacai Chen Elf_Sym *sym; 411fcdfe9d2SHuacai Chen Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr; 412fcdfe9d2SHuacai Chen 413fcdfe9d2SHuacai Chen pr_debug("%s: Applying relocate section %u to %u\n", __func__, relsec, 414fcdfe9d2SHuacai Chen sechdrs[relsec].sh_info); 415fcdfe9d2SHuacai Chen 416fcdfe9d2SHuacai Chen rela_stack_top = 0; 417fcdfe9d2SHuacai Chen for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 418fcdfe9d2SHuacai Chen /* This is where to make the change */ 419fcdfe9d2SHuacai Chen location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + rel[i].r_offset; 420fcdfe9d2SHuacai Chen /* This is the symbol it is referring to */ 421fcdfe9d2SHuacai Chen sym = (Elf_Sym *)sechdrs[symindex].sh_addr + ELF_R_SYM(rel[i].r_info); 422fcdfe9d2SHuacai Chen if (IS_ERR_VALUE(sym->st_value)) { 423fcdfe9d2SHuacai Chen /* Ignore unresolved weak symbol */ 424fcdfe9d2SHuacai Chen if (ELF_ST_BIND(sym->st_info) == STB_WEAK) 425fcdfe9d2SHuacai Chen continue; 426fcdfe9d2SHuacai Chen pr_warn("%s: Unknown symbol %s\n", mod->name, strtab + sym->st_name); 427fcdfe9d2SHuacai Chen return -ENOENT; 428fcdfe9d2SHuacai Chen } 429fcdfe9d2SHuacai Chen 430fcdfe9d2SHuacai Chen type = ELF_R_TYPE(rel[i].r_info); 431fcdfe9d2SHuacai Chen 432fcdfe9d2SHuacai Chen if (type < ARRAY_SIZE(reloc_rela_handlers)) 433fcdfe9d2SHuacai Chen handler = reloc_rela_handlers[type]; 434fcdfe9d2SHuacai Chen else 435fcdfe9d2SHuacai Chen handler = NULL; 436fcdfe9d2SHuacai Chen 437fcdfe9d2SHuacai Chen if (!handler) { 438fcdfe9d2SHuacai Chen pr_err("%s: Unknown relocation type %u\n", mod->name, type); 439fcdfe9d2SHuacai Chen return -EINVAL; 440fcdfe9d2SHuacai Chen } 441fcdfe9d2SHuacai Chen 442fcdfe9d2SHuacai Chen pr_debug("type %d st_value %llx r_addend %llx loc %llx\n", 443fcdfe9d2SHuacai Chen (int)ELF_R_TYPE(rel[i].r_info), 444fcdfe9d2SHuacai Chen sym->st_value, rel[i].r_addend, (u64)location); 445fcdfe9d2SHuacai Chen 446fcdfe9d2SHuacai Chen v = sym->st_value + rel[i].r_addend; 447fcdfe9d2SHuacai Chen err = handler(mod, location, v, rela_stack, &rela_stack_top, type); 448fcdfe9d2SHuacai Chen if (err) 449fcdfe9d2SHuacai Chen return err; 450fcdfe9d2SHuacai Chen } 451fcdfe9d2SHuacai Chen 452fcdfe9d2SHuacai Chen return 0; 453fcdfe9d2SHuacai Chen } 454fcdfe9d2SHuacai Chen 455fcdfe9d2SHuacai Chen void *module_alloc(unsigned long size) 456fcdfe9d2SHuacai Chen { 457fcdfe9d2SHuacai Chen return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, 458fcdfe9d2SHuacai Chen GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, __builtin_return_address(0)); 459fcdfe9d2SHuacai Chen } 460*19e5eb15SHuacai Chen 461*19e5eb15SHuacai Chen int module_finalize(const Elf_Ehdr *hdr, 462*19e5eb15SHuacai Chen const Elf_Shdr *sechdrs, struct module *mod) 463*19e5eb15SHuacai Chen { 464*19e5eb15SHuacai Chen const Elf_Shdr *s, *se; 465*19e5eb15SHuacai Chen const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 466*19e5eb15SHuacai Chen 467*19e5eb15SHuacai Chen for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) { 468*19e5eb15SHuacai Chen if (!strcmp(".altinstructions", secstrs + s->sh_name)) 469*19e5eb15SHuacai Chen apply_alternatives((void *)s->sh_addr, (void *)s->sh_addr + s->sh_size); 470*19e5eb15SHuacai Chen } 471*19e5eb15SHuacai Chen 472*19e5eb15SHuacai Chen return 0; 473*19e5eb15SHuacai Chen } 474