Clean up run-pre matching loop structure.
[ksplice.git] / kmodsrc / helper.c
blob0167deb9388c134deae7f4ef7659f0d25b57d965
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 init_module(void)
34 printk("ksplice_h: Preparing and checking %s\n", ksplice_name);
36 if (ksplice_do_helper() != 0)
37 return -1;
39 if (ksplice_do_primary() != 0)
40 return -1;
42 return 0;
45 void cleanup_module(void)
47 clear_list(&reloc_namevals, struct reloc_nameval, list);
48 clear_list(&reloc_addrmaps, struct reloc_addrmap, list);
49 if (ksplice_state == KSPLICE_PREPARING)
50 clear_list(&safety_records, struct safety_record, list);
53 int ksplice_do_helper(void)
55 struct ksplice_size *s;
56 int i, record_count = 0, ret;
57 char *finished;
58 int numfinished, oldfinished = 0;
59 int restart_count = 0, stage = 1;
61 if (process_ksplice_relocs(1) != 0)
62 return -1;
64 for (s = &ksplice_sizes; s->name != NULL; s++) {
65 record_count++;
68 /* old kernels do not have kcalloc */
69 finished = ksplice_kcalloc(record_count);
71 start:
72 for (s = &ksplice_sizes, i = 0; s->name != NULL; s++, i++) {
73 if (s->size == 0)
74 finished[i] = 1;
75 if (finished[i])
76 continue;
78 ret = search_for_match(s, &stage);
79 if (ret < 0) {
80 kfree(finished);
81 return ret;
82 } else if (ret == 0) {
83 finished[i] = 1;
87 numfinished = 0;
88 for (i = 0; i < record_count; i++) {
89 if (finished[i])
90 numfinished++;
92 if (numfinished == record_count) {
93 kfree(finished);
94 return 0;
97 if (oldfinished == numfinished) {
98 if (stage < 3) {
99 stage++;
100 goto start;
102 print_abort("run-pre: could not match some sections");
103 kfree(finished);
104 return -1;
106 oldfinished = numfinished;
108 if (restart_count < 20) {
109 restart_count++;
110 goto start;
112 print_abort("run-pre: restart limit exceeded");
113 kfree(finished);
114 return -1;
117 /* old kernels do not have kcalloc */
118 void *ksplice_kcalloc(int size)
120 char *mem = kmalloc(size, GFP_KERNEL);
121 int i;
122 for (i = 0; i < size; i++) {
123 mem[i] = 0;
125 return mem;
128 int search_for_match(struct ksplice_size *s, int *stage)
130 int i, saved_debug;
131 long run_addr;
132 LIST_HEAD(vals);
133 struct candidate_val *v;
135 for (i = 0; i < s->num_sym_addrs; i++) {
136 add_candidate_val(&vals, s->sym_addrs[i]);
139 compute_address(s->name, &vals);
140 if (*stage <= 1 && !singular(&vals)) {
141 release_vals(&vals);
142 return 1;
145 if (debug >= 3) {
146 printk("ksplice_h: run-pre: starting sect search for %s\n",
147 s->name);
150 list_for_each_entry(v, &vals, list) {
151 run_addr = v->val;
153 yield();
154 if (try_addr(s, run_addr, s->thismod_addr, !singular(&vals))) {
155 release_vals(&vals);
156 return 0;
159 release_vals(&vals);
161 if (*stage <= 2)
162 return 1;
164 saved_debug = debug;
165 debug = 0;
166 brute_search_all_mods(s);
167 debug = saved_debug;
168 return 1;
172 try_addr(struct ksplice_size *s, long run_addr, long pre_addr,
173 int create_nameval)
175 struct safety_record *tmp;
177 if (run_pre_cmp(run_addr, pre_addr, s->size, 0) != 0) {
178 set_temp_myst_relocs(NOVAL);
179 if (debug >= 1) {
180 printk("ksplice_h: run-pre: sect %s does not match ",
181 s->name);
182 printk("(r_a=%08lx p_a=%08lx s=%ld)\n",
183 run_addr, pre_addr, s->size);
184 printk("ksplice_h: run-pre: ");
185 run_pre_cmp(run_addr, pre_addr, s->size, 1);
186 printk("\n");
188 } else {
189 set_temp_myst_relocs(VAL);
191 if (debug >= 3) {
192 printk("ksplice_h: run-pre: found sect %s=%08lx\n",
193 s->name, run_addr);
196 tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
197 tmp->addr = run_addr;
198 tmp->size = s->size;
199 tmp->care = 0;
200 list_add(&tmp->list, &safety_records);
202 if (create_nameval) {
203 struct reloc_nameval *nv = find_nameval(s->name, 1);
204 nv->val = run_addr;
205 nv->status = VAL;
208 return 1;
210 return 0;
213 int run_pre_cmp(long run_addr, long pre_addr, int size, int rerun)
215 int run_o = 0, pre_o = 0, lenient = 0, prev_c3 = 0, recent_5b = 0;
216 unsigned char run, pre;
217 struct reloc_addrmap *map;
219 if (size == 0)
220 return 1;
222 while (run_o < size && pre_o < size) {
223 if (lenient > 0)
224 lenient--;
225 if (prev_c3 > 0)
226 prev_c3--;
227 if (recent_5b > 0)
228 recent_5b--;
230 if (!virtual_address_mapped(run_addr + run_o))
231 return 1;
233 if ((map = find_addrmap(pre_addr + pre_o)) != NULL) {
234 if (handle_myst_reloc
235 (pre_addr, &pre_o, run_addr, &run_o, map,
236 rerun) == 1)
237 return 1;
238 continue;
241 if (match_nop(run_addr, &run_o) || match_nop(pre_addr, &pre_o))
242 continue;
244 run = *(unsigned char *)(run_addr + run_o);
245 pre = *(unsigned char *)(pre_addr + pre_o);
247 if (rerun)
248 printk("%02x/%02x ", run, pre);
250 if (run == pre) {
251 if (pre == 0xc3)
252 prev_c3 = 1 + 1;
253 if (pre == 0x5b)
254 recent_5b = 10 + 1;
255 if (jumplen[pre])
256 lenient = max(jumplen[pre] + 1, lenient);
257 pre_o++, run_o++;
258 continue;
261 if (prev_c3 && recent_5b)
262 return 0;
263 if (jumplen[run] && jumplen[pre]) {
264 run_o += 1 + jumplen[run];
265 pre_o += 1 + jumplen[pre];
266 continue;
268 if (lenient) {
269 pre_o++, run_o++;
270 continue;
272 if (rerun) {
273 printk("[p_o=%08x] ! %02x/%02x %02x/%02x",
274 pre_o,
275 *(unsigned char *)(run_addr + run_o + 1),
276 *(unsigned char *)(pre_addr + pre_o + 1),
277 *(unsigned char *)(run_addr + run_o + 2),
278 *(unsigned char *)(pre_addr + pre_o + 2));
280 return 1;
282 return 0;
286 handle_myst_reloc(long pre_addr, int *pre_o, long run_addr,
287 int *run_o, struct reloc_addrmap *map, int rerun)
289 int expected;
290 int offset = (int)(pre_addr + *pre_o - map->addr);
291 long run_reloc;
292 long run_reloc_addr;
293 run_reloc_addr = run_addr + *run_o - offset;
294 if (map->size == 4) {
295 run_reloc = *(int *)run_reloc_addr;
296 } else if (map->size == 8) {
297 run_reloc = *(long long *)run_reloc_addr;
298 } else {
299 BUG();
302 if (debug >= 3 && !rerun) {
303 printk("ksplice_h: run-pre: reloc at r_a=%08lx p_o=%08x: ",
304 run_addr, *pre_o);
305 printk("%s=%08lx (A=%08lx *r=%08lx)\n",
306 map->nameval->name, map->nameval->val,
307 map->addend, run_reloc);
310 if (!starts_with(map->nameval->name, ".rodata.str")) {
311 expected = run_reloc - map->addend;
312 if ((int)run_reloc == 0x77777777)
313 return 1;
314 if (map->flags & PCREL)
315 expected += run_reloc_addr;
316 if (map->nameval->status == NOVAL) {
317 map->nameval->val = expected;
318 map->nameval->status = TEMP;
319 } else if (map->nameval->val != expected) {
320 if (rerun)
321 return 1;
322 printk("ksplice_h: pre-run reloc: Expected %s=%08x!\n",
323 map->nameval->name, expected);
324 return 1;
328 *pre_o += map->size - offset;
329 *run_o += map->size - offset;
330 return 0;
333 /* TODO: The recommended way to pad 64bit code is to use NOPs preceded by
334 maximally four 0x66 prefixes. */
335 int match_nop(long addr, int *o)
337 int i, j;
338 for (i = NUM_NOPS - 1; i >= 0; i--) {
339 for (j = 0; j < i + 1; j++) {
340 if (!virtual_address_mapped(addr + *o + j))
341 break;
342 if (*(unsigned char *)(addr + *o + j) != nops[i][j])
343 break;
345 if (j == i + 1) {
346 *o += j;
347 return 1;
351 return 0;
354 void brute_search_all_mods(struct ksplice_size *s)
356 struct module *m;
357 list_for_each_entry(m, &(THIS_MODULE->list), list) {
358 if (!starts_with(m->name, ksplice_name)
359 && !ends_with(m->name, "_helper")) {
360 if (brute_search(s, m->module_core, m->core_size) == 0)
361 return;
362 if (brute_search(s, m->module_init, m->init_size) == 0)
363 return;