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