Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : /* Kernel module help for x86.
3 : : Copyright (C) 2001 Rusty Russell.
4 : :
5 : : */
6 : :
7 : : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8 : :
9 : : #include <linux/moduleloader.h>
10 : : #include <linux/elf.h>
11 : : #include <linux/vmalloc.h>
12 : : #include <linux/fs.h>
13 : : #include <linux/string.h>
14 : : #include <linux/kernel.h>
15 : : #include <linux/kasan.h>
16 : : #include <linux/bug.h>
17 : : #include <linux/mm.h>
18 : : #include <linux/gfp.h>
19 : : #include <linux/jump_label.h>
20 : : #include <linux/random.h>
21 : :
22 : : #include <asm/text-patching.h>
23 : : #include <asm/page.h>
24 : : #include <asm/pgtable.h>
25 : : #include <asm/setup.h>
26 : : #include <asm/unwind.h>
27 : :
28 : : #if 0
29 : : #define DEBUGP(fmt, ...) \
30 : : printk(KERN_DEBUG fmt, ##__VA_ARGS__)
31 : : #else
32 : : #define DEBUGP(fmt, ...) \
33 : : do { \
34 : : if (0) \
35 : : printk(KERN_DEBUG fmt, ##__VA_ARGS__); \
36 : : } while (0)
37 : : #endif
38 : :
39 : : #ifdef CONFIG_RANDOMIZE_BASE
40 : : static unsigned long module_load_offset;
41 : :
42 : : /* Mutex protects the module_load_offset. */
43 : : static DEFINE_MUTEX(module_kaslr_mutex);
44 : :
45 : : static unsigned long int get_module_load_offset(void)
46 : : {
47 : : if (kaslr_enabled()) {
48 : : mutex_lock(&module_kaslr_mutex);
49 : : /*
50 : : * Calculate the module_load_offset the first time this
51 : : * code is called. Once calculated it stays the same until
52 : : * reboot.
53 : : */
54 : : if (module_load_offset == 0)
55 : : module_load_offset =
56 : : (get_random_int() % 1024 + 1) * PAGE_SIZE;
57 : : mutex_unlock(&module_kaslr_mutex);
58 : : }
59 : : return module_load_offset;
60 : : }
61 : : #else
62 : 240 : static unsigned long int get_module_load_offset(void)
63 : : {
64 : 240 : return 0;
65 : : }
66 : : #endif
67 : :
68 : 240 : void *module_alloc(unsigned long size)
69 : : {
70 : 240 : void *p;
71 : :
72 [ + - ]: 240 : if (PAGE_ALIGN(size) > MODULES_LEN)
73 : : return NULL;
74 : :
75 : 480 : p = __vmalloc_node_range(size, MODULE_ALIGN,
76 : : MODULES_VADDR + get_module_load_offset(),
77 : : MODULES_END, GFP_KERNEL,
78 : 240 : PAGE_KERNEL, 0, NUMA_NO_NODE,
79 : 240 : __builtin_return_address(0));
80 [ + - - + ]: 240 : if (p && (kasan_module_alloc(p, size) < 0)) {
81 : 0 : vfree(p);
82 : 0 : return NULL;
83 : : }
84 : :
85 : : return p;
86 : : }
87 : :
88 : : #ifdef CONFIG_X86_32
89 : : int apply_relocate(Elf32_Shdr *sechdrs,
90 : : const char *strtab,
91 : : unsigned int symindex,
92 : : unsigned int relsec,
93 : : struct module *me)
94 : : {
95 : : unsigned int i;
96 : : Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr;
97 : : Elf32_Sym *sym;
98 : : uint32_t *location;
99 : :
100 : : DEBUGP("Applying relocate section %u to %u\n",
101 : : relsec, sechdrs[relsec].sh_info);
102 : : for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
103 : : /* This is where to make the change */
104 : : location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
105 : : + rel[i].r_offset;
106 : : /* This is the symbol it is referring to. Note that all
107 : : undefined symbols have been resolved. */
108 : : sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
109 : : + ELF32_R_SYM(rel[i].r_info);
110 : :
111 : : switch (ELF32_R_TYPE(rel[i].r_info)) {
112 : : case R_386_32:
113 : : /* We add the value into the location given */
114 : : *location += sym->st_value;
115 : : break;
116 : : case R_386_PC32:
117 : : /* Add the value, subtract its position */
118 : : *location += sym->st_value - (uint32_t)location;
119 : : break;
120 : : default:
121 : : pr_err("%s: Unknown relocation: %u\n",
122 : : me->name, ELF32_R_TYPE(rel[i].r_info));
123 : : return -ENOEXEC;
124 : : }
125 : : }
126 : : return 0;
127 : : }
128 : : #else /*X86_64*/
129 : 1500 : int apply_relocate_add(Elf64_Shdr *sechdrs,
130 : : const char *strtab,
131 : : unsigned int symindex,
132 : : unsigned int relsec,
133 : : struct module *me)
134 : : {
135 : 1500 : unsigned int i;
136 : 1500 : Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;
137 : 1500 : Elf64_Sym *sym;
138 : 1500 : void *loc;
139 : 1500 : u64 val;
140 : :
141 : 1500 : DEBUGP("Applying relocate section %u to %u\n",
142 : : relsec, sechdrs[relsec].sh_info);
143 [ + + ]: 2418060 : for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
144 : : /* This is where to make the change */
145 : 2416560 : loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
146 : 2416560 : + rel[i].r_offset;
147 : :
148 : : /* This is the symbol it is referring to. Note that all
149 : : undefined symbols have been resolved. */
150 : 2416560 : sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
151 : 2416560 : + ELF64_R_SYM(rel[i].r_info);
152 : :
153 : 2416560 : DEBUGP("type %d st_value %Lx r_addend %Lx loc %Lx\n",
154 : : (int)ELF64_R_TYPE(rel[i].r_info),
155 : : sym->st_value, rel[i].r_addend, (u64)loc);
156 : :
157 : 2416560 : val = sym->st_value + rel[i].r_addend;
158 : :
159 [ + - + + : 2416560 : switch (ELF64_R_TYPE(rel[i].r_info)) {
- - - ]
160 : : case R_X86_64_NONE:
161 : : break;
162 : 621150 : case R_X86_64_64:
163 [ - + ]: 621150 : if (*(u64 *)loc != 0)
164 : 0 : goto invalid_relocation;
165 : 621150 : *(u64 *)loc = val;
166 : 621150 : break;
167 : 0 : case R_X86_64_32:
168 [ # # ]: 0 : if (*(u32 *)loc != 0)
169 : 0 : goto invalid_relocation;
170 : 0 : *(u32 *)loc = val;
171 [ # # ]: 0 : if (val != *(u32 *)loc)
172 : 0 : goto overflow;
173 : : break;
174 : 104220 : case R_X86_64_32S:
175 [ - + ]: 104220 : if (*(s32 *)loc != 0)
176 : 0 : goto invalid_relocation;
177 : 104220 : *(s32 *)loc = val;
178 [ - + ]: 104220 : if ((s64)val != *(s32 *)loc)
179 : 0 : goto overflow;
180 : : break;
181 : 1691190 : case R_X86_64_PC32:
182 : : case R_X86_64_PLT32:
183 [ - + ]: 1691190 : if (*(u32 *)loc != 0)
184 : 0 : goto invalid_relocation;
185 : 1691190 : val -= (u64)loc;
186 : 1691190 : *(u32 *)loc = val;
187 : : #if 0
188 : : if ((s64)val != *(s32 *)loc)
189 : : goto overflow;
190 : : #endif
191 : 1691190 : break;
192 : 0 : case R_X86_64_PC64:
193 [ # # ]: 0 : if (*(u64 *)loc != 0)
194 : 0 : goto invalid_relocation;
195 : 0 : val -= (u64)loc;
196 : 0 : *(u64 *)loc = val;
197 : 0 : break;
198 : 0 : default:
199 : 0 : pr_err("%s: Unknown rela relocation: %llu\n",
200 : : me->name, ELF64_R_TYPE(rel[i].r_info));
201 : 0 : return -ENOEXEC;
202 : : }
203 : : }
204 : : return 0;
205 : :
206 : 0 : invalid_relocation:
207 : 0 : pr_err("x86/modules: Skipping invalid relocation target, existing value is nonzero for type %d, loc %p, val %Lx\n",
208 : : (int)ELF64_R_TYPE(rel[i].r_info), loc, val);
209 : 0 : return -ENOEXEC;
210 : :
211 : 0 : overflow:
212 : 0 : pr_err("overflow in relocation type %d val %Lx\n",
213 : : (int)ELF64_R_TYPE(rel[i].r_info), val);
214 : 0 : pr_err("`%s' likely not compiled with -mcmodel=kernel\n",
215 : : me->name);
216 : 0 : return -ENOEXEC;
217 : : }
218 : : #endif
219 : :
220 : 120 : int module_finalize(const Elf_Ehdr *hdr,
221 : : const Elf_Shdr *sechdrs,
222 : : struct module *me)
223 : : {
224 : 120 : const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL,
225 : : *para = NULL, *orc = NULL, *orc_ip = NULL;
226 : 120 : char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
227 : :
228 [ + + ]: 6570 : for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
229 [ + + ]: 6450 : if (!strcmp(".text", secstrings + s->sh_name))
230 : 120 : text = s;
231 [ + + ]: 6450 : if (!strcmp(".altinstructions", secstrings + s->sh_name))
232 : 30 : alt = s;
233 [ + + ]: 6450 : if (!strcmp(".smp_locks", secstrings + s->sh_name))
234 : 90 : locks = s;
235 [ - + ]: 6450 : if (!strcmp(".parainstructions", secstrings + s->sh_name))
236 : 0 : para = s;
237 [ + + ]: 6450 : if (!strcmp(".orc_unwind", secstrings + s->sh_name))
238 : 120 : orc = s;
239 [ + + ]: 6450 : if (!strcmp(".orc_unwind_ip", secstrings + s->sh_name))
240 : 120 : orc_ip = s;
241 : : }
242 : :
243 [ + + ]: 120 : if (alt) {
244 : : /* patch .altinstructions */
245 : 30 : void *aseg = (void *)alt->sh_addr;
246 : 30 : apply_alternatives(aseg, aseg + alt->sh_size);
247 : : }
248 [ + + ]: 120 : if (locks && text) {
249 : 90 : void *lseg = (void *)locks->sh_addr;
250 : 90 : void *tseg = (void *)text->sh_addr;
251 : 90 : alternatives_smp_module_add(me, me->name,
252 : 90 : lseg, lseg + locks->sh_size,
253 : 90 : tseg, tseg + text->sh_size);
254 : : }
255 : :
256 [ - + ]: 120 : if (para) {
257 : 0 : void *pseg = (void *)para->sh_addr;
258 : 0 : apply_paravirt(pseg, pseg + para->sh_size);
259 : : }
260 : :
261 : : /* make jump label nops */
262 : 120 : jump_label_apply_nops(me);
263 : :
264 [ + - ]: 120 : if (orc && orc_ip)
265 : 120 : unwind_module_init(me, (void *)orc_ip->sh_addr, orc_ip->sh_size,
266 : 120 : (void *)orc->sh_addr, orc->sh_size);
267 : :
268 : 120 : return 0;
269 : : }
270 : :
271 : 0 : void module_arch_cleanup(struct module *mod)
272 : : {
273 : 0 : alternatives_smp_module_del(mod);
274 : 0 : }
|