linux/arch/riscv/kernel/module.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *
   4 *  Copyright (C) 2017 Zihao Yu
   5 */
   6
   7#include <linux/elf.h>
   8#include <linux/err.h>
   9#include <linux/errno.h>
  10#include <linux/moduleloader.h>
  11#include <linux/vmalloc.h>
  12#include <linux/sizes.h>
  13#include <linux/pgtable.h>
  14#include <asm/sections.h>
  15
  16static int apply_r_riscv_32_rela(struct module *me, u32 *location, Elf_Addr v)
  17{
  18        if (v != (u32)v) {
  19                pr_err("%s: value %016llx out of range for 32-bit field\n",
  20                       me->name, (long long)v);
  21                return -EINVAL;
  22        }
  23        *location = v;
  24        return 0;
  25}
  26
  27static int apply_r_riscv_64_rela(struct module *me, u32 *location, Elf_Addr v)
  28{
  29        *(u64 *)location = v;
  30        return 0;
  31}
  32
  33static int apply_r_riscv_branch_rela(struct module *me, u32 *location,
  34                                     Elf_Addr v)
  35{
  36        ptrdiff_t offset = (void *)v - (void *)location;
  37        u32 imm12 = (offset & 0x1000) << (31 - 12);
  38        u32 imm11 = (offset & 0x800) >> (11 - 7);
  39        u32 imm10_5 = (offset & 0x7e0) << (30 - 10);
  40        u32 imm4_1 = (offset & 0x1e) << (11 - 4);
  41
  42        *location = (*location & 0x1fff07f) | imm12 | imm11 | imm10_5 | imm4_1;
  43        return 0;
  44}
  45
  46static int apply_r_riscv_jal_rela(struct module *me, u32 *location,
  47                                  Elf_Addr v)
  48{
  49        ptrdiff_t offset = (void *)v - (void *)location;
  50        u32 imm20 = (offset & 0x100000) << (31 - 20);
  51        u32 imm19_12 = (offset & 0xff000);
  52        u32 imm11 = (offset & 0x800) << (20 - 11);
  53        u32 imm10_1 = (offset & 0x7fe) << (30 - 10);
  54
  55        *location = (*location & 0xfff) | imm20 | imm19_12 | imm11 | imm10_1;
  56        return 0;
  57}
  58
  59static int apply_r_riscv_rcv_branch_rela(struct module *me, u32 *location,
  60                                         Elf_Addr v)
  61{
  62        ptrdiff_t offset = (void *)v - (void *)location;
  63        u16 imm8 = (offset & 0x100) << (12 - 8);
  64        u16 imm7_6 = (offset & 0xc0) >> (6 - 5);
  65        u16 imm5 = (offset & 0x20) >> (5 - 2);
  66        u16 imm4_3 = (offset & 0x18) << (12 - 5);
  67        u16 imm2_1 = (offset & 0x6) << (12 - 10);
  68
  69        *(u16 *)location = (*(u16 *)location & 0xe383) |
  70                    imm8 | imm7_6 | imm5 | imm4_3 | imm2_1;
  71        return 0;
  72}
  73
  74static int apply_r_riscv_rvc_jump_rela(struct module *me, u32 *location,
  75                                       Elf_Addr v)
  76{
  77        ptrdiff_t offset = (void *)v - (void *)location;
  78        u16 imm11 = (offset & 0x800) << (12 - 11);
  79        u16 imm10 = (offset & 0x400) >> (10 - 8);
  80        u16 imm9_8 = (offset & 0x300) << (12 - 11);
  81        u16 imm7 = (offset & 0x80) >> (7 - 6);
  82        u16 imm6 = (offset & 0x40) << (12 - 11);
  83        u16 imm5 = (offset & 0x20) >> (5 - 2);
  84        u16 imm4 = (offset & 0x10) << (12 - 5);
  85        u16 imm3_1 = (offset & 0xe) << (12 - 10);
  86
  87        *(u16 *)location = (*(u16 *)location & 0xe003) |
  88                    imm11 | imm10 | imm9_8 | imm7 | imm6 | imm5 | imm4 | imm3_1;
  89        return 0;
  90}
  91
  92static int apply_r_riscv_pcrel_hi20_rela(struct module *me, u32 *location,
  93                                         Elf_Addr v)
  94{
  95        ptrdiff_t offset = (void *)v - (void *)location;
  96        s32 hi20;
  97
  98        if (offset != (s32)offset) {
  99                pr_err(
 100                  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
 101                  me->name, (long long)v, location);
 102                return -EINVAL;
 103        }
 104
 105        hi20 = (offset + 0x800) & 0xfffff000;
 106        *location = (*location & 0xfff) | hi20;
 107        return 0;
 108}
 109
 110static int apply_r_riscv_pcrel_lo12_i_rela(struct module *me, u32 *location,
 111                                           Elf_Addr v)
 112{
 113        /*
 114         * v is the lo12 value to fill. It is calculated before calling this
 115         * handler.
 116         */
 117        *location = (*location & 0xfffff) | ((v & 0xfff) << 20);
 118        return 0;
 119}
 120
 121static int apply_r_riscv_pcrel_lo12_s_rela(struct module *me, u32 *location,
 122                                           Elf_Addr v)
 123{
 124        /*
 125         * v is the lo12 value to fill. It is calculated before calling this
 126         * handler.
 127         */
 128        u32 imm11_5 = (v & 0xfe0) << (31 - 11);
 129        u32 imm4_0 = (v & 0x1f) << (11 - 4);
 130
 131        *location = (*location & 0x1fff07f) | imm11_5 | imm4_0;
 132        return 0;
 133}
 134
 135static int apply_r_riscv_hi20_rela(struct module *me, u32 *location,
 136                                   Elf_Addr v)
 137{
 138        s32 hi20;
 139
 140        if (IS_ENABLED(CONFIG_CMODEL_MEDLOW)) {
 141                pr_err(
 142                  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
 143                  me->name, (long long)v, location);
 144                return -EINVAL;
 145        }
 146
 147        hi20 = ((s32)v + 0x800) & 0xfffff000;
 148        *location = (*location & 0xfff) | hi20;
 149        return 0;
 150}
 151
 152static int apply_r_riscv_lo12_i_rela(struct module *me, u32 *location,
 153                                     Elf_Addr v)
 154{
 155        /* Skip medlow checking because of filtering by HI20 already */
 156        s32 hi20 = ((s32)v + 0x800) & 0xfffff000;
 157        s32 lo12 = ((s32)v - hi20);
 158        *location = (*location & 0xfffff) | ((lo12 & 0xfff) << 20);
 159        return 0;
 160}
 161
 162static int apply_r_riscv_lo12_s_rela(struct module *me, u32 *location,
 163                                     Elf_Addr v)
 164{
 165        /* Skip medlow checking because of filtering by HI20 already */
 166        s32 hi20 = ((s32)v + 0x800) & 0xfffff000;
 167        s32 lo12 = ((s32)v - hi20);
 168        u32 imm11_5 = (lo12 & 0xfe0) << (31 - 11);
 169        u32 imm4_0 = (lo12 & 0x1f) << (11 - 4);
 170        *location = (*location & 0x1fff07f) | imm11_5 | imm4_0;
 171        return 0;
 172}
 173
 174static int apply_r_riscv_got_hi20_rela(struct module *me, u32 *location,
 175                                       Elf_Addr v)
 176{
 177        ptrdiff_t offset = (void *)v - (void *)location;
 178        s32 hi20;
 179
 180        /* Always emit the got entry */
 181        if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {
 182                offset = module_emit_got_entry(me, v);
 183                offset = (void *)offset - (void *)location;
 184        } else {
 185                pr_err(
 186                  "%s: can not generate the GOT entry for symbol = %016llx from PC = %p\n",
 187                  me->name, (long long)v, location);
 188                return -EINVAL;
 189        }
 190
 191        hi20 = (offset + 0x800) & 0xfffff000;
 192        *location = (*location & 0xfff) | hi20;
 193        return 0;
 194}
 195
 196static int apply_r_riscv_call_plt_rela(struct module *me, u32 *location,
 197                                       Elf_Addr v)
 198{
 199        ptrdiff_t offset = (void *)v - (void *)location;
 200        s32 fill_v = offset;
 201        u32 hi20, lo12;
 202
 203        if (offset != fill_v) {
 204                /* Only emit the plt entry if offset over 32-bit range */
 205                if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {
 206                        offset = module_emit_plt_entry(me, v);
 207                        offset = (void *)offset - (void *)location;
 208                } else {
 209                        pr_err(
 210                          "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
 211                          me->name, (long long)v, location);
 212                        return -EINVAL;
 213                }
 214        }
 215
 216        hi20 = (offset + 0x800) & 0xfffff000;
 217        lo12 = (offset - hi20) & 0xfff;
 218        *location = (*location & 0xfff) | hi20;
 219        *(location + 1) = (*(location + 1) & 0xfffff) | (lo12 << 20);
 220        return 0;
 221}
 222
 223static int apply_r_riscv_call_rela(struct module *me, u32 *location,
 224                                   Elf_Addr v)
 225{
 226        ptrdiff_t offset = (void *)v - (void *)location;
 227        s32 fill_v = offset;
 228        u32 hi20, lo12;
 229
 230        if (offset != fill_v) {
 231                pr_err(
 232                  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
 233                  me->name, (long long)v, location);
 234                return -EINVAL;
 235        }
 236
 237        hi20 = (offset + 0x800) & 0xfffff000;
 238        lo12 = (offset - hi20) & 0xfff;
 239        *location = (*location & 0xfff) | hi20;
 240        *(location + 1) = (*(location + 1) & 0xfffff) | (lo12 << 20);
 241        return 0;
 242}
 243
 244static int apply_r_riscv_relax_rela(struct module *me, u32 *location,
 245                                    Elf_Addr v)
 246{
 247        return 0;
 248}
 249
 250static int apply_r_riscv_align_rela(struct module *me, u32 *location,
 251                                    Elf_Addr v)
 252{
 253        pr_err(
 254          "%s: The unexpected relocation type 'R_RISCV_ALIGN' from PC = %p\n",
 255          me->name, location);
 256        return -EINVAL;
 257}
 258
 259static int apply_r_riscv_add32_rela(struct module *me, u32 *location,
 260                                    Elf_Addr v)
 261{
 262        *(u32 *)location += (u32)v;
 263        return 0;
 264}
 265
 266static int apply_r_riscv_add64_rela(struct module *me, u32 *location,
 267                                    Elf_Addr v)
 268{
 269        *(u64 *)location += (u64)v;
 270        return 0;
 271}
 272
 273static int apply_r_riscv_sub32_rela(struct module *me, u32 *location,
 274                                    Elf_Addr v)
 275{
 276        *(u32 *)location -= (u32)v;
 277        return 0;
 278}
 279
 280static int apply_r_riscv_sub64_rela(struct module *me, u32 *location,
 281                                    Elf_Addr v)
 282{
 283        *(u64 *)location -= (u64)v;
 284        return 0;
 285}
 286
 287static int (*reloc_handlers_rela[]) (struct module *me, u32 *location,
 288                                Elf_Addr v) = {
 289        [R_RISCV_32]                    = apply_r_riscv_32_rela,
 290        [R_RISCV_64]                    = apply_r_riscv_64_rela,
 291        [R_RISCV_BRANCH]                = apply_r_riscv_branch_rela,
 292        [R_RISCV_JAL]                   = apply_r_riscv_jal_rela,
 293        [R_RISCV_RVC_BRANCH]            = apply_r_riscv_rcv_branch_rela,
 294        [R_RISCV_RVC_JUMP]              = apply_r_riscv_rvc_jump_rela,
 295        [R_RISCV_PCREL_HI20]            = apply_r_riscv_pcrel_hi20_rela,
 296        [R_RISCV_PCREL_LO12_I]          = apply_r_riscv_pcrel_lo12_i_rela,
 297        [R_RISCV_PCREL_LO12_S]          = apply_r_riscv_pcrel_lo12_s_rela,
 298        [R_RISCV_HI20]                  = apply_r_riscv_hi20_rela,
 299        [R_RISCV_LO12_I]                = apply_r_riscv_lo12_i_rela,
 300        [R_RISCV_LO12_S]                = apply_r_riscv_lo12_s_rela,
 301        [R_RISCV_GOT_HI20]              = apply_r_riscv_got_hi20_rela,
 302        [R_RISCV_CALL_PLT]              = apply_r_riscv_call_plt_rela,
 303        [R_RISCV_CALL]                  = apply_r_riscv_call_rela,
 304        [R_RISCV_RELAX]                 = apply_r_riscv_relax_rela,
 305        [R_RISCV_ALIGN]                 = apply_r_riscv_align_rela,
 306        [R_RISCV_ADD32]                 = apply_r_riscv_add32_rela,
 307        [R_RISCV_ADD64]                 = apply_r_riscv_add64_rela,
 308        [R_RISCV_SUB32]                 = apply_r_riscv_sub32_rela,
 309        [R_RISCV_SUB64]                 = apply_r_riscv_sub64_rela,
 310};
 311
 312int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
 313                       unsigned int symindex, unsigned int relsec,
 314                       struct module *me)
 315{
 316        Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr;
 317        int (*handler)(struct module *me, u32 *location, Elf_Addr v);
 318        Elf_Sym *sym;
 319        u32 *location;
 320        unsigned int i, type;
 321        Elf_Addr v;
 322        int res;
 323
 324        pr_debug("Applying relocate section %u to %u\n", relsec,
 325               sechdrs[relsec].sh_info);
 326
 327        for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
 328                /* This is where to make the change */
 329                location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
 330                        + rel[i].r_offset;
 331                /* This is the symbol it is referring to */
 332                sym = (Elf_Sym *)sechdrs[symindex].sh_addr
 333                        + ELF_RISCV_R_SYM(rel[i].r_info);
 334                if (IS_ERR_VALUE(sym->st_value)) {
 335                        /* Ignore unresolved weak symbol */
 336                        if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
 337                                continue;
 338                        pr_warn("%s: Unknown symbol %s\n",
 339                                me->name, strtab + sym->st_name);
 340                        return -ENOENT;
 341                }
 342
 343                type = ELF_RISCV_R_TYPE(rel[i].r_info);
 344
 345                if (type < ARRAY_SIZE(reloc_handlers_rela))
 346                        handler = reloc_handlers_rela[type];
 347                else
 348                        handler = NULL;
 349
 350                if (!handler) {
 351                        pr_err("%s: Unknown relocation type %u\n",
 352                               me->name, type);
 353                        return -EINVAL;
 354                }
 355
 356                v = sym->st_value + rel[i].r_addend;
 357
 358                if (type == R_RISCV_PCREL_LO12_I || type == R_RISCV_PCREL_LO12_S) {
 359                        unsigned int j;
 360
 361                        for (j = 0; j < sechdrs[relsec].sh_size / sizeof(*rel); j++) {
 362                                unsigned long hi20_loc =
 363                                        sechdrs[sechdrs[relsec].sh_info].sh_addr
 364                                        + rel[j].r_offset;
 365                                u32 hi20_type = ELF_RISCV_R_TYPE(rel[j].r_info);
 366
 367                                /* Find the corresponding HI20 relocation entry */
 368                                if (hi20_loc == sym->st_value
 369                                    && (hi20_type == R_RISCV_PCREL_HI20
 370                                        || hi20_type == R_RISCV_GOT_HI20)) {
 371                                        s32 hi20, lo12;
 372                                        Elf_Sym *hi20_sym =
 373                                                (Elf_Sym *)sechdrs[symindex].sh_addr
 374                                                + ELF_RISCV_R_SYM(rel[j].r_info);
 375                                        unsigned long hi20_sym_val =
 376                                                hi20_sym->st_value
 377                                                + rel[j].r_addend;
 378
 379                                        /* Calculate lo12 */
 380                                        size_t offset = hi20_sym_val - hi20_loc;
 381                                        if (IS_ENABLED(CONFIG_MODULE_SECTIONS)
 382                                            && hi20_type == R_RISCV_GOT_HI20) {
 383                                                offset = module_emit_got_entry(
 384                                                         me, hi20_sym_val);
 385                                                offset = offset - hi20_loc;
 386                                        }
 387                                        hi20 = (offset + 0x800) & 0xfffff000;
 388                                        lo12 = offset - hi20;
 389                                        v = lo12;
 390
 391                                        break;
 392                                }
 393                        }
 394                        if (j == sechdrs[relsec].sh_size / sizeof(*rel)) {
 395                                pr_err(
 396                                  "%s: Can not find HI20 relocation information\n",
 397                                  me->name);
 398                                return -EINVAL;
 399                        }
 400                }
 401
 402                res = handler(me, location, v);
 403                if (res)
 404                        return res;
 405        }
 406
 407        return 0;
 408}
 409
 410#if defined(CONFIG_MMU) && defined(CONFIG_64BIT)
 411void *module_alloc(unsigned long size)
 412{
 413        return __vmalloc_node_range(size, 1, MODULES_VADDR,
 414                                    MODULES_END, GFP_KERNEL,
 415                                    PAGE_KERNEL, 0, NUMA_NO_NODE,
 416                                    __builtin_return_address(0));
 417}
 418#endif
 419