~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm64/kernel/module.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * AArch64 loadable module support.
  4  *
  5  * Copyright (C) 2012 ARM Limited
  6  *
  7  * Author: Will Deacon <will.deacon@arm.com>
  8  */
  9 
 10 #define pr_fmt(fmt) "Modules: " fmt
 11 
 12 #include <linux/bitops.h>
 13 #include <linux/elf.h>
 14 #include <linux/ftrace.h>
 15 #include <linux/kasan.h>
 16 #include <linux/kernel.h>
 17 #include <linux/mm.h>
 18 #include <linux/moduleloader.h>
 19 #include <linux/random.h>
 20 #include <linux/scs.h>
 21 
 22 #include <asm/alternative.h>
 23 #include <asm/insn.h>
 24 #include <asm/scs.h>
 25 #include <asm/sections.h>
 26 
 27 enum aarch64_reloc_op {
 28         RELOC_OP_NONE,
 29         RELOC_OP_ABS,
 30         RELOC_OP_PREL,
 31         RELOC_OP_PAGE,
 32 };
 33 
 34 static u64 do_reloc(enum aarch64_reloc_op reloc_op, __le32 *place, u64 val)
 35 {
 36         switch (reloc_op) {
 37         case RELOC_OP_ABS:
 38                 return val;
 39         case RELOC_OP_PREL:
 40                 return val - (u64)place;
 41         case RELOC_OP_PAGE:
 42                 return (val & ~0xfff) - ((u64)place & ~0xfff);
 43         case RELOC_OP_NONE:
 44                 return 0;
 45         }
 46 
 47         pr_err("do_reloc: unknown relocation operation %d\n", reloc_op);
 48         return 0;
 49 }
 50 
 51 static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len)
 52 {
 53         s64 sval = do_reloc(op, place, val);
 54 
 55         /*
 56          * The ELF psABI for AArch64 documents the 16-bit and 32-bit place
 57          * relative and absolute relocations as having a range of [-2^15, 2^16)
 58          * or [-2^31, 2^32), respectively. However, in order to be able to
 59          * detect overflows reliably, we have to choose whether we interpret
 60          * such quantities as signed or as unsigned, and stick with it.
 61          * The way we organize our address space requires a signed
 62          * interpretation of 32-bit relative references, so let's use that
 63          * for all R_AARCH64_PRELxx relocations. This means our upper
 64          * bound for overflow detection should be Sxx_MAX rather than Uxx_MAX.
 65          */
 66 
 67         switch (len) {
 68         case 16:
 69                 *(s16 *)place = sval;
 70                 switch (op) {
 71                 case RELOC_OP_ABS:
 72                         if (sval < 0 || sval > U16_MAX)
 73                                 return -ERANGE;
 74                         break;
 75                 case RELOC_OP_PREL:
 76                         if (sval < S16_MIN || sval > S16_MAX)
 77                                 return -ERANGE;
 78                         break;
 79                 default:
 80                         pr_err("Invalid 16-bit data relocation (%d)\n", op);
 81                         return 0;
 82                 }
 83                 break;
 84         case 32:
 85                 *(s32 *)place = sval;
 86                 switch (op) {
 87                 case RELOC_OP_ABS:
 88                         if (sval < 0 || sval > U32_MAX)
 89                                 return -ERANGE;
 90                         break;
 91                 case RELOC_OP_PREL:
 92                         if (sval < S32_MIN || sval > S32_MAX)
 93                                 return -ERANGE;
 94                         break;
 95                 default:
 96                         pr_err("Invalid 32-bit data relocation (%d)\n", op);
 97                         return 0;
 98                 }
 99                 break;
100         case 64:
101                 *(s64 *)place = sval;
102                 break;
103         default:
104                 pr_err("Invalid length (%d) for data relocation\n", len);
105                 return 0;
106         }
107         return 0;
108 }
109 
110 enum aarch64_insn_movw_imm_type {
111         AARCH64_INSN_IMM_MOVNZ,
112         AARCH64_INSN_IMM_MOVKZ,
113 };
114 
115 static int reloc_insn_movw(enum aarch64_reloc_op op, __le32 *place, u64 val,
116                            int lsb, enum aarch64_insn_movw_imm_type imm_type)
117 {
118         u64 imm;
119         s64 sval;
120         u32 insn = le32_to_cpu(*place);
121 
122         sval = do_reloc(op, place, val);
123         imm = sval >> lsb;
124 
125         if (imm_type == AARCH64_INSN_IMM_MOVNZ) {
126                 /*
127                  * For signed MOVW relocations, we have to manipulate the
128                  * instruction encoding depending on whether or not the
129                  * immediate is less than zero.
130                  */
131                 insn &= ~(3 << 29);
132                 if (sval >= 0) {
133                         /* >=0: Set the instruction to MOVZ (opcode 10b). */
134                         insn |= 2 << 29;
135                 } else {
136                         /*
137                          * <0: Set the instruction to MOVN (opcode 00b).
138                          *     Since we've masked the opcode already, we
139                          *     don't need to do anything other than
140                          *     inverting the new immediate field.
141                          */
142                         imm = ~imm;
143                 }
144         }
145 
146         /* Update the instruction with the new encoding. */
147         insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_16, insn, imm);
148         *place = cpu_to_le32(insn);
149 
150         if (imm > U16_MAX)
151                 return -ERANGE;
152 
153         return 0;
154 }
155 
156 static int reloc_insn_imm(enum aarch64_reloc_op op, __le32 *place, u64 val,
157                           int lsb, int len, enum aarch64_insn_imm_type imm_type)
158 {
159         u64 imm, imm_mask;
160         s64 sval;
161         u32 insn = le32_to_cpu(*place);
162 
163         /* Calculate the relocation value. */
164         sval = do_reloc(op, place, val);
165         sval >>= lsb;
166 
167         /* Extract the value bits and shift them to bit 0. */
168         imm_mask = (BIT(lsb + len) - 1) >> lsb;
169         imm = sval & imm_mask;
170 
171         /* Update the instruction's immediate field. */
172         insn = aarch64_insn_encode_immediate(imm_type, insn, imm);
173         *place = cpu_to_le32(insn);
174 
175         /*
176          * Extract the upper value bits (including the sign bit) and
177          * shift them to bit 0.
178          */
179         sval = (s64)(sval & ~(imm_mask >> 1)) >> (len - 1);
180 
181         /*
182          * Overflow has occurred if the upper bits are not all equal to
183          * the sign bit of the value.
184          */
185         if ((u64)(sval + 1) >= 2)
186                 return -ERANGE;
187 
188         return 0;
189 }
190 
191 static int reloc_insn_adrp(struct module *mod, Elf64_Shdr *sechdrs,
192                            __le32 *place, u64 val)
193 {
194         u32 insn;
195 
196         if (!is_forbidden_offset_for_adrp(place))
197                 return reloc_insn_imm(RELOC_OP_PAGE, place, val, 12, 21,
198                                       AARCH64_INSN_IMM_ADR);
199 
200         /* patch ADRP to ADR if it is in range */
201         if (!reloc_insn_imm(RELOC_OP_PREL, place, val & ~0xfff, 0, 21,
202                             AARCH64_INSN_IMM_ADR)) {
203                 insn = le32_to_cpu(*place);
204                 insn &= ~BIT(31);
205         } else {
206                 /* out of range for ADR -> emit a veneer */
207                 val = module_emit_veneer_for_adrp(mod, sechdrs, place, val & ~0xfff);
208                 if (!val)
209                         return -ENOEXEC;
210                 insn = aarch64_insn_gen_branch_imm((u64)place, val,
211                                                    AARCH64_INSN_BRANCH_NOLINK);
212         }
213 
214         *place = cpu_to_le32(insn);
215         return 0;
216 }
217 
218 int apply_relocate_add(Elf64_Shdr *sechdrs,
219                        const char *strtab,
220                        unsigned int symindex,
221                        unsigned int relsec,
222                        struct module *me)
223 {
224         unsigned int i;
225         int ovf;
226         bool overflow_check;
227         Elf64_Sym *sym;
228         void *loc;
229         u64 val;
230         Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;
231 
232         for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
233                 /* loc corresponds to P in the AArch64 ELF document. */
234                 loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
235                         + rel[i].r_offset;
236 
237                 /* sym is the ELF symbol we're referring to. */
238                 sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
239                         + ELF64_R_SYM(rel[i].r_info);
240 
241                 /* val corresponds to (S + A) in the AArch64 ELF document. */
242                 val = sym->st_value + rel[i].r_addend;
243 
244                 /* Check for overflow by default. */
245                 overflow_check = true;
246 
247                 /* Perform the static relocation. */
248                 switch (ELF64_R_TYPE(rel[i].r_info)) {
249                 /* Null relocations. */
250                 case R_ARM_NONE:
251                 case R_AARCH64_NONE:
252                         ovf = 0;
253                         break;
254 
255                 /* Data relocations. */
256                 case R_AARCH64_ABS64:
257                         overflow_check = false;
258                         ovf = reloc_data(RELOC_OP_ABS, loc, val, 64);
259                         break;
260                 case R_AARCH64_ABS32:
261                         ovf = reloc_data(RELOC_OP_ABS, loc, val, 32);
262                         break;
263                 case R_AARCH64_ABS16:
264                         ovf = reloc_data(RELOC_OP_ABS, loc, val, 16);
265                         break;
266                 case R_AARCH64_PREL64:
267                         overflow_check = false;
268                         ovf = reloc_data(RELOC_OP_PREL, loc, val, 64);
269                         break;
270                 case R_AARCH64_PREL32:
271                         ovf = reloc_data(RELOC_OP_PREL, loc, val, 32);
272                         break;
273                 case R_AARCH64_PREL16:
274                         ovf = reloc_data(RELOC_OP_PREL, loc, val, 16);
275                         break;
276 
277                 /* MOVW instruction relocations. */
278                 case R_AARCH64_MOVW_UABS_G0_NC:
279                         overflow_check = false;
280                         fallthrough;
281                 case R_AARCH64_MOVW_UABS_G0:
282                         ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
283                                               AARCH64_INSN_IMM_MOVKZ);
284                         break;
285                 case R_AARCH64_MOVW_UABS_G1_NC:
286                         overflow_check = false;
287                         fallthrough;
288                 case R_AARCH64_MOVW_UABS_G1:
289                         ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
290                                               AARCH64_INSN_IMM_MOVKZ);
291                         break;
292                 case R_AARCH64_MOVW_UABS_G2_NC:
293                         overflow_check = false;
294                         fallthrough;
295                 case R_AARCH64_MOVW_UABS_G2:
296                         ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
297                                               AARCH64_INSN_IMM_MOVKZ);
298                         break;
299                 case R_AARCH64_MOVW_UABS_G3:
300                         /* We're using the top bits so we can't overflow. */
301                         overflow_check = false;
302                         ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48,
303                                               AARCH64_INSN_IMM_MOVKZ);
304                         break;
305                 case R_AARCH64_MOVW_SABS_G0:
306                         ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
307                                               AARCH64_INSN_IMM_MOVNZ);
308                         break;
309                 case R_AARCH64_MOVW_SABS_G1:
310                         ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
311                                               AARCH64_INSN_IMM_MOVNZ);
312                         break;
313                 case R_AARCH64_MOVW_SABS_G2:
314                         ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
315                                               AARCH64_INSN_IMM_MOVNZ);
316                         break;
317                 case R_AARCH64_MOVW_PREL_G0_NC:
318                         overflow_check = false;
319                         ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
320                                               AARCH64_INSN_IMM_MOVKZ);
321                         break;
322                 case R_AARCH64_MOVW_PREL_G0:
323                         ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
324                                               AARCH64_INSN_IMM_MOVNZ);
325                         break;
326                 case R_AARCH64_MOVW_PREL_G1_NC:
327                         overflow_check = false;
328                         ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
329                                               AARCH64_INSN_IMM_MOVKZ);
330                         break;
331                 case R_AARCH64_MOVW_PREL_G1:
332                         ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
333                                               AARCH64_INSN_IMM_MOVNZ);
334                         break;
335                 case R_AARCH64_MOVW_PREL_G2_NC:
336                         overflow_check = false;
337                         ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
338                                               AARCH64_INSN_IMM_MOVKZ);
339                         break;
340                 case R_AARCH64_MOVW_PREL_G2:
341                         ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
342                                               AARCH64_INSN_IMM_MOVNZ);
343                         break;
344                 case R_AARCH64_MOVW_PREL_G3:
345                         /* We're using the top bits so we can't overflow. */
346                         overflow_check = false;
347                         ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48,
348                                               AARCH64_INSN_IMM_MOVNZ);
349                         break;
350 
351                 /* Immediate instruction relocations. */
352                 case R_AARCH64_LD_PREL_LO19:
353                         ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
354                                              AARCH64_INSN_IMM_19);
355                         break;
356                 case R_AARCH64_ADR_PREL_LO21:
357                         ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21,
358                                              AARCH64_INSN_IMM_ADR);
359                         break;
360                 case R_AARCH64_ADR_PREL_PG_HI21_NC:
361                         overflow_check = false;
362                         fallthrough;
363                 case R_AARCH64_ADR_PREL_PG_HI21:
364                         ovf = reloc_insn_adrp(me, sechdrs, loc, val);
365                         if (ovf && ovf != -ERANGE)
366                                 return ovf;
367                         break;
368                 case R_AARCH64_ADD_ABS_LO12_NC:
369                 case R_AARCH64_LDST8_ABS_LO12_NC:
370                         overflow_check = false;
371                         ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12,
372                                              AARCH64_INSN_IMM_12);
373                         break;
374                 case R_AARCH64_LDST16_ABS_LO12_NC:
375                         overflow_check = false;
376                         ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11,
377                                              AARCH64_INSN_IMM_12);
378                         break;
379                 case R_AARCH64_LDST32_ABS_LO12_NC:
380                         overflow_check = false;
381                         ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10,
382                                              AARCH64_INSN_IMM_12);
383                         break;
384                 case R_AARCH64_LDST64_ABS_LO12_NC:
385                         overflow_check = false;
386                         ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9,
387                                              AARCH64_INSN_IMM_12);
388                         break;
389                 case R_AARCH64_LDST128_ABS_LO12_NC:
390                         overflow_check = false;
391                         ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8,
392                                              AARCH64_INSN_IMM_12);
393                         break;
394                 case R_AARCH64_TSTBR14:
395                         ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14,
396                                              AARCH64_INSN_IMM_14);
397                         break;
398                 case R_AARCH64_CONDBR19:
399                         ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
400                                              AARCH64_INSN_IMM_19);
401                         break;
402                 case R_AARCH64_JUMP26:
403                 case R_AARCH64_CALL26:
404                         ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26,
405                                              AARCH64_INSN_IMM_26);
406                         if (ovf == -ERANGE) {
407                                 val = module_emit_plt_entry(me, sechdrs, loc, &rel[i], sym);
408                                 if (!val)
409                                         return -ENOEXEC;
410                                 ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2,
411                                                      26, AARCH64_INSN_IMM_26);
412                         }
413                         break;
414 
415                 default:
416                         pr_err("module %s: unsupported RELA relocation: %llu\n",
417                                me->name, ELF64_R_TYPE(rel[i].r_info));
418                         return -ENOEXEC;
419                 }
420 
421                 if (overflow_check && ovf == -ERANGE)
422                         goto overflow;
423 
424         }
425 
426         return 0;
427 
428 overflow:
429         pr_err("module %s: overflow in relocation type %d val %Lx\n",
430                me->name, (int)ELF64_R_TYPE(rel[i].r_info), val);
431         return -ENOEXEC;
432 }
433 
434 static inline void __init_plt(struct plt_entry *plt, unsigned long addr)
435 {
436         *plt = get_plt_entry(addr, plt);
437 }
438 
439 static int module_init_ftrace_plt(const Elf_Ehdr *hdr,
440                                   const Elf_Shdr *sechdrs,
441                                   struct module *mod)
442 {
443 #if defined(CONFIG_DYNAMIC_FTRACE)
444         const Elf_Shdr *s;
445         struct plt_entry *plts;
446 
447         s = find_section(hdr, sechdrs, ".text.ftrace_trampoline");
448         if (!s)
449                 return -ENOEXEC;
450 
451         plts = (void *)s->sh_addr;
452 
453         __init_plt(&plts[FTRACE_PLT_IDX], FTRACE_ADDR);
454 
455         mod->arch.ftrace_trampolines = plts;
456 #endif
457         return 0;
458 }
459 
460 int module_finalize(const Elf_Ehdr *hdr,
461                     const Elf_Shdr *sechdrs,
462                     struct module *me)
463 {
464         const Elf_Shdr *s;
465         s = find_section(hdr, sechdrs, ".altinstructions");
466         if (s)
467                 apply_alternatives_module((void *)s->sh_addr, s->sh_size);
468 
469         if (scs_is_dynamic()) {
470                 s = find_section(hdr, sechdrs, ".init.eh_frame");
471                 if (s)
472                         __pi_scs_patch((void *)s->sh_addr, s->sh_size);
473         }
474 
475         return module_init_ftrace_plt(hdr, sechdrs, me);
476 }
477 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php