| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 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 | 3 | * |
|---|
| 12 | 4 | * Copyright (C) 2017 Zihao Yu |
|---|
| 13 | 5 | */ |
|---|
| .. | .. |
|---|
| 18 | 10 | #include <linux/moduleloader.h> |
|---|
| 19 | 11 | #include <linux/vmalloc.h> |
|---|
| 20 | 12 | #include <linux/sizes.h> |
|---|
| 21 | | -#include <asm/pgtable.h> |
|---|
| 13 | +#include <linux/pgtable.h> |
|---|
| 22 | 14 | #include <asm/sections.h> |
|---|
| 15 | + |
|---|
| 16 | +/* |
|---|
| 17 | + * The auipc+jalr instruction pair can reach any PC-relative offset |
|---|
| 18 | + * in the range [-2^31 - 2^11, 2^31 - 2^11) |
|---|
| 19 | + */ |
|---|
| 20 | +static bool riscv_insn_valid_32bit_offset(ptrdiff_t val) |
|---|
| 21 | +{ |
|---|
| 22 | +#ifdef CONFIG_32BIT |
|---|
| 23 | + return true; |
|---|
| 24 | +#else |
|---|
| 25 | + return (-(1L << 31) - (1L << 11)) <= val && val < ((1L << 31) - (1L << 11)); |
|---|
| 26 | +#endif |
|---|
| 27 | +} |
|---|
| 23 | 28 | |
|---|
| 24 | 29 | static int apply_r_riscv_32_rela(struct module *me, u32 *location, Elf_Addr v) |
|---|
| 25 | 30 | { |
|---|
| .. | .. |
|---|
| 103 | 108 | ptrdiff_t offset = (void *)v - (void *)location; |
|---|
| 104 | 109 | s32 hi20; |
|---|
| 105 | 110 | |
|---|
| 106 | | - if (offset != (s32)offset) { |
|---|
| 111 | + if (!riscv_insn_valid_32bit_offset(offset)) { |
|---|
| 107 | 112 | pr_err( |
|---|
| 108 | 113 | "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", |
|---|
| 109 | 114 | me->name, (long long)v, location); |
|---|
| .. | .. |
|---|
| 145 | 150 | { |
|---|
| 146 | 151 | s32 hi20; |
|---|
| 147 | 152 | |
|---|
| 148 | | - if (IS_ENABLED(CMODEL_MEDLOW)) { |
|---|
| 153 | + if (IS_ENABLED(CONFIG_CMODEL_MEDLOW)) { |
|---|
| 149 | 154 | pr_err( |
|---|
| 150 | 155 | "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", |
|---|
| 151 | 156 | me->name, (long long)v, location); |
|---|
| .. | .. |
|---|
| 205 | 210 | Elf_Addr v) |
|---|
| 206 | 211 | { |
|---|
| 207 | 212 | ptrdiff_t offset = (void *)v - (void *)location; |
|---|
| 208 | | - s32 fill_v = offset; |
|---|
| 209 | 213 | u32 hi20, lo12; |
|---|
| 210 | 214 | |
|---|
| 211 | | - if (offset != fill_v) { |
|---|
| 215 | + if (!riscv_insn_valid_32bit_offset(offset)) { |
|---|
| 212 | 216 | /* Only emit the plt entry if offset over 32-bit range */ |
|---|
| 213 | 217 | if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) { |
|---|
| 214 | 218 | offset = module_emit_plt_entry(me, v); |
|---|
| .. | .. |
|---|
| 232 | 236 | Elf_Addr v) |
|---|
| 233 | 237 | { |
|---|
| 234 | 238 | ptrdiff_t offset = (void *)v - (void *)location; |
|---|
| 235 | | - s32 fill_v = offset; |
|---|
| 236 | 239 | u32 hi20, lo12; |
|---|
| 237 | 240 | |
|---|
| 238 | | - if (offset != fill_v) { |
|---|
| 241 | + if (!riscv_insn_valid_32bit_offset(offset)) { |
|---|
| 239 | 242 | pr_err( |
|---|
| 240 | 243 | "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", |
|---|
| 241 | 244 | me->name, (long long)v, location); |
|---|
| .. | .. |
|---|
| 271 | 274 | return 0; |
|---|
| 272 | 275 | } |
|---|
| 273 | 276 | |
|---|
| 277 | +static int apply_r_riscv_add64_rela(struct module *me, u32 *location, |
|---|
| 278 | + Elf_Addr v) |
|---|
| 279 | +{ |
|---|
| 280 | + *(u64 *)location += (u64)v; |
|---|
| 281 | + return 0; |
|---|
| 282 | +} |
|---|
| 283 | + |
|---|
| 274 | 284 | static int apply_r_riscv_sub32_rela(struct module *me, u32 *location, |
|---|
| 275 | 285 | Elf_Addr v) |
|---|
| 276 | 286 | { |
|---|
| 277 | 287 | *(u32 *)location -= (u32)v; |
|---|
| 288 | + return 0; |
|---|
| 289 | +} |
|---|
| 290 | + |
|---|
| 291 | +static int apply_r_riscv_sub64_rela(struct module *me, u32 *location, |
|---|
| 292 | + Elf_Addr v) |
|---|
| 293 | +{ |
|---|
| 294 | + *(u64 *)location -= (u64)v; |
|---|
| 278 | 295 | return 0; |
|---|
| 279 | 296 | } |
|---|
| 280 | 297 | |
|---|
| .. | .. |
|---|
| 298 | 315 | [R_RISCV_RELAX] = apply_r_riscv_relax_rela, |
|---|
| 299 | 316 | [R_RISCV_ALIGN] = apply_r_riscv_align_rela, |
|---|
| 300 | 317 | [R_RISCV_ADD32] = apply_r_riscv_add32_rela, |
|---|
| 318 | + [R_RISCV_ADD64] = apply_r_riscv_add64_rela, |
|---|
| 301 | 319 | [R_RISCV_SUB32] = apply_r_riscv_sub32_rela, |
|---|
| 320 | + [R_RISCV_SUB64] = apply_r_riscv_sub64_rela, |
|---|
| 302 | 321 | }; |
|---|
| 303 | 322 | |
|---|
| 304 | 323 | int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, |
|---|
| .. | .. |
|---|
| 327 | 346 | /* Ignore unresolved weak symbol */ |
|---|
| 328 | 347 | if (ELF_ST_BIND(sym->st_info) == STB_WEAK) |
|---|
| 329 | 348 | continue; |
|---|
| 330 | | - pr_warning("%s: Unknown symbol %s\n", |
|---|
| 331 | | - me->name, strtab + sym->st_name); |
|---|
| 349 | + pr_warn("%s: Unknown symbol %s\n", |
|---|
| 350 | + me->name, strtab + sym->st_name); |
|---|
| 332 | 351 | return -ENOENT; |
|---|
| 333 | 352 | } |
|---|
| 334 | 353 | |
|---|