.. | .. |
---|
| 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 | |
---|