Use <linux/list.h> API for reloc_addrmaps and reloc_namevals.
[ksplice.git] / kmodsrc / helper.c
blob9d1cf95fa8b3c20f1854973d94d2d9cf3c693f6f
1 /* Copyright (C) 2008 Jeffrey Brian Arnold <jbarnold@mit.edu>
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License, version 2.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
14 * 02110-1301, USA.
17 #include "modcommon.h"
18 #include "helper.h"
19 #include "jumps.h"
20 #include "nops.h"
21 #include <linux/kthread.h>
23 /* defined by modcommon.c */
24 extern int debug;
26 /* defined by ksplice-create */
27 extern struct ksplice_size ksplice_sizes;
29 #undef max
30 #define max(a, b) ((a) > (b) ? (a) : (b))
32 int
33 init_module(void)
35 printk("ksplice_h: Preparing and checking %s\n", ksplice_name);
37 if (ksplice_do_helper() != 0)
38 return -1;
40 if (ksplice_do_primary() != 0)
41 return -1;
43 return 0;
46 void
47 cleanup_module(void)
49 clear_list(&reloc_namevals, struct reloc_nameval, list);
50 clear_list(&reloc_addrmaps, struct reloc_addrmap, list);
52 /* These pointers should always be NULL when no helper module is loaded */
53 release_list((struct starts_with_next *) safety_records);
56 int
57 ksplice_do_helper(void)
59 struct ksplice_size *s;
60 int i, record_count = 0, ret;
61 char *finished;
62 int numfinished, oldfinished = 0;
63 int restart_count = 0, stage = 1;
65 if (process_ksplice_relocs(1) != 0)
66 return -1;
68 for (s = &ksplice_sizes; s->name != NULL; s++) {
69 record_count++;
72 /* old kernels do not have kcalloc */
73 finished = ksplice_kcalloc(record_count);
75 start:
76 for (s = &ksplice_sizes, i = 0; s->name != NULL; s++, i++) {
77 if (s->size == 0)
78 finished[i] = 1;
79 if (finished[i])
80 continue;
82 ret = search_for_match(s, &stage);
83 if (ret < 0) {
84 kfree(finished);
85 return ret;
86 } else if (ret == 0) {
87 finished[i] = 1;
91 numfinished = 0;
92 for (i = 0; i < record_count; i++) {
93 if (finished[i])
94 numfinished++;
96 if (numfinished == record_count) {
97 kfree(finished);
98 return 0;
101 if (oldfinished == numfinished) {
102 if (stage < 3) {
103 stage++;
104 goto start;
106 print_abort("run-pre: could not match some sections");
107 kfree(finished);
108 return -1;
110 oldfinished = numfinished;
112 if (restart_count < 20) {
113 restart_count++;
114 goto start;
116 print_abort("run-pre: restart limit exceeded");
117 kfree(finished);
118 return -1;
121 /* old kernels do not have kcalloc */
122 void *
123 ksplice_kcalloc(int size)
125 char *mem = kmalloc(size, GFP_KERNEL);
126 int i;
127 for (i = 0; i < size; i++) {
128 mem[i] = 0;
130 return mem;
134 search_for_match(struct ksplice_size *s, int *stage)
136 int i, saved_debug;
137 long run_addr;
138 LIST_HEAD(glob);
139 struct ansglob *g;
141 for (i = 0; i < s->num_sym_addrs; i++) {
142 add2glob(&glob, s->sym_addrs[i]);
145 compute_address(s->name, &glob);
146 if (*stage <= 1 && !singular(&glob)) {
147 release(&glob);
148 return 1;
151 if (debug >= 3) {
152 printk("ksplice_h: run-pre: starting sect search for %s\n",
153 s->name);
156 list_for_each_entry(g, &glob, list) {
157 run_addr = g->val;
159 yield();
160 if (try_addr(s, run_addr, s->thismod_addr, !singular(&glob))) {
161 release(&glob);
162 return 0;
165 release(&glob);
167 if (*stage <= 2)
168 return 1;
170 saved_debug = debug;
171 debug = 0;
172 brute_search_all_mods(s);
173 debug = saved_debug;
174 return 1;
178 try_addr(struct ksplice_size *s, long run_addr, long pre_addr,
179 int create_nameval)
181 struct safety_record *tmp;
183 if (run_pre_cmp(run_addr, pre_addr, s->size, 0) != 0) {
184 set_temp_myst_relocs(NOVAL);
185 if (debug >= 1) {
186 printk("ksplice_h: run-pre: sect %s does not match ",
187 s->name);
188 printk("(r_a=%08lx p_a=%08lx s=%ld)\n",
189 run_addr, pre_addr, s->size);
190 printk("ksplice_h: run-pre: ");
191 run_pre_cmp(run_addr, pre_addr, s->size, 1);
192 printk("\n");
194 } else {
195 set_temp_myst_relocs(VAL);
197 if (debug >= 3) {
198 printk("ksplice_h: run-pre: found sect %s=%08lx\n",
199 s->name, run_addr);
202 tmp = kmalloc(sizeof (*tmp), GFP_KERNEL);
203 tmp->addr = run_addr;
204 tmp->size = s->size;
205 tmp->next = safety_records;
206 tmp->care = 0;
207 safety_records = tmp;
209 if (create_nameval) {
210 struct reloc_nameval *nv = find_nameval(s->name, 1);
211 nv->val = run_addr;
212 nv->status = VAL;
215 return 1;
217 return 0;
221 run_pre_cmp(long run_addr, long pre_addr, int size, int rerun)
223 int run_o, pre_o, lenient = 0, prev_c3 = 0, recent_5b = 0;
224 unsigned char run, pre;
225 struct reloc_addrmap *map;
227 if (size == 0)
228 return 1;
230 for (run_o = 0, pre_o = 0; run_o < size && pre_o < size;
231 pre_o++, run_o++) {
232 if (lenient > 0)
233 lenient--;
234 if (prev_c3 > 0)
235 prev_c3--;
236 if (recent_5b > 0)
237 recent_5b--;
239 if (!virtual_address_mapped(run_addr + run_o))
240 return 1;
241 run = *(unsigned char *) (run_addr + run_o);
242 pre = *(unsigned char *) (pre_addr + pre_o);
244 if (rerun)
245 printk("%02x/%02x ", run, pre);
247 if (run == pre) {
248 if ((map = find_addrmap(pre_addr + pre_o)) != NULL) {
249 if (handle_myst_reloc
250 (pre_addr, &pre_o, run_addr, &run_o,
251 map, rerun) == 1)
252 return 1;
253 continue;
255 if (pre == 0xc3)
256 prev_c3 = 1 + 1;
257 if (pre == 0x5b)
258 recent_5b = 10 + 1;
259 if (jumplen[pre])
260 lenient = max(jumplen[pre] + 1, lenient);
261 if (match_nop(run_addr, &run_o, &pre_o) ||
262 match_nop(pre_addr, &pre_o, &run_o))
263 continue;
264 continue;
267 if ((map = find_addrmap(pre_addr + pre_o)) != NULL) {
268 if (handle_myst_reloc
269 (pre_addr, &pre_o, run_addr, &run_o, map,
270 rerun) == 1)
271 return 1;
272 continue;
274 if (prev_c3 && recent_5b)
275 return 0;
276 if (match_nop(run_addr, &run_o, &pre_o) ||
277 match_nop(pre_addr, &pre_o, &run_o))
278 continue;
279 if (jumplen[run] && jumplen[pre]) {
280 run_o += jumplen[run];
281 pre_o += jumplen[pre];
282 continue;
284 if (lenient)
285 continue;
286 if (rerun) {
287 printk("[p_o=%08x] ! %02x/%02x %02x/%02x",
288 pre_o,
289 *(unsigned char *) (run_addr + run_o + 1),
290 *(unsigned char *) (pre_addr + pre_o + 1),
291 *(unsigned char *) (run_addr + run_o + 2),
292 *(unsigned char *) (pre_addr + pre_o + 2));
294 return 1;
296 return 0;
300 handle_myst_reloc(long pre_addr, int *pre_o, long run_addr,
301 int *run_o, struct reloc_addrmap *map, int rerun)
303 int expected;
304 int offset = (int) (pre_addr + *pre_o - map->addr);
305 int run_reloc = *(int *) (run_addr + *run_o - offset);
307 if (debug >= 3 && !rerun) {
308 printk("ksplice_h: run-pre: reloc at r_a=%08lx p_o=%08x: ",
309 run_addr, *pre_o);
310 printk("%s=%08lx (A=%08lx *r=%08x)\n",
311 map->nameval->name, map->nameval->val,
312 map->addend, run_reloc);
315 if (!starts_with(map->nameval->name, ".rodata.str")) {
316 expected = run_reloc - map->addend;
317 if (run_reloc == 0x77777777)
318 return 1;
319 if (map->flags & PCREL)
320 expected += run_addr + *run_o - offset;
321 if (map->nameval->status == NOVAL) {
322 map->nameval->val = expected;
323 map->nameval->status = TEMP;
324 } else if (map->nameval->val != expected) {
325 if (rerun)
326 return 1;
327 printk("ksplice_h: pre-run reloc: Expected %s=%08x!\n",
328 map->nameval->name, expected);
329 return 1;
333 *pre_o += 4 - offset - 1;
334 *run_o += 4 - offset - 1;
335 return 0;
338 /* TODO: The recommended way to pad 64bit code is to use NOPs preceded by
339 maximally four 0x66 prefixes. */
341 match_nop(long addr, int *o, int *other_o)
343 int i, j;
344 for (i = NUM_NOPS - 1; i >= 0; i--) {
345 for (j = 0; j < i + 1; j++) {
346 if (!virtual_address_mapped(addr + *o + j))
347 break;
348 if (*(unsigned char *) (addr + *o + j) != nops[i][j])
349 break;
351 if (j == i + 1) {
352 *o += i;
353 (*other_o)--;
354 return 1;
358 return 0;
361 void
362 brute_search_all_mods(struct ksplice_size *s)
364 struct module *m;
365 list_for_each_entry(m, &(THIS_MODULE->list), list) {
366 if (!starts_with(m->name, ksplice_name)
367 && !ends_with(m->name, "_helper")) {
368 if (brute_search(s, m->module_core, m->core_size) == 0)
369 return;
370 if (brute_search(s, m->module_init, m->init_size) == 0)
371 return;