Don't look up .rodata symbols in the kernel.
[ksplice.git] / kmodsrc / modcommon.c
blob798f80f54a5040732301714f8961a61bb643b94b
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 <linux/kallsyms.h>
19 #include <linux/stop_machine.h>
21 #ifdef CONFIG_KALLSYMS
22 static const int CONFIG_KALLSYMS_VAL = 1;
23 extern unsigned long kallsyms_addresses[], kallsyms_num_syms;
24 extern u8 kallsyms_names[];
25 #else
26 static const int CONFIG_KALLSYMS_VAL = 0;
27 #endif
29 /* defined by ksplice-create */
30 extern struct ksplice_reloc ksplice_init_relocs, ksplice_relocs;
32 static int safe = 0, helper = 0;
33 int debug;
34 module_param(debug, int, 0600);
36 int process_ksplice_relocs(int caller_is_helper)
38 struct ksplice_reloc *r;
39 helper = caller_is_helper;
40 for (r = &ksplice_init_relocs; r->sym_name != NULL; r++) {
41 if (process_reloc(r) != 0)
42 return -1;
44 safe = 1;
45 for (r = &ksplice_relocs; r->sym_name != NULL; r++) {
46 if (process_reloc(r) != 0)
47 return -1;
49 return 0;
52 int process_reloc(struct ksplice_reloc *r)
54 int i;
55 long sym_addr;
56 struct reloc_addrmap *map;
58 #define blank_addr (r->blank_sect_addr+r->blank_offset)
60 LIST_HEAD(vals);
61 if (CONFIG_KALLSYMS_VAL || !safe) {
62 for (i = 0; i < r->num_sym_addrs; i++) {
63 int adjustment = (long)printk - map_printk;
64 if (adjustment & 0xfffff) {
65 print_abort("System.map does not match kernel");
66 return -1;
68 add_candidate_val(&vals, r->sym_addrs[i] + adjustment);
72 if (*(int *)blank_addr == 0x77777777) {
73 r->flags |= SAFE;
75 if (!(r->flags & SAFE)) {
76 if (debug >= 4) {
77 printk
78 ("ksplice%s: reloc: skipped %s:%08lx (altinstr)\n",
79 (helper ? "_h" : ""), r->sym_name,
80 r->blank_offset);
82 release_vals(&vals);
83 return 0;
86 compute_address(r->sym_name, &vals);
87 if (!singular(&vals)) {
88 release_vals(&vals);
89 if (!(helper && safe)) {
90 failed_to_find(r->sym_name);
91 return -1;
94 if (debug >= 4) {
95 printk("ksplice: reloc: deferred %s:%08lx to run-pre\n",
96 r->sym_name, r->blank_offset);
99 map = kmalloc(sizeof(*map), GFP_KERNEL);
100 map->addr = blank_addr;
101 map->nameval = find_nameval(r->sym_name, 1);
102 map->addend = r->addend;
103 map->flags = r->flags;
104 list_add(&map->list, &reloc_addrmaps);
105 return 0;
107 sym_addr = list_entry(vals.next, struct candidate_val, list)->val;
108 release_vals(&vals);
110 if (debug >= 4) {
111 printk("ksplice%s: reloc: %s:%08lx ",
112 (helper ? "_h" : ""), r->sym_name, r->blank_offset);
113 printk("(S=%08lx A=%08lx ", sym_addr, r->addend);
116 if ((r->flags & PCREL) && (helper && safe)) {
117 map = kmalloc(sizeof(*map), GFP_KERNEL);
118 map->addr = blank_addr;
119 map->nameval = find_nameval("ksplice_zero", 1);
120 map->nameval->val = 0;
121 map->nameval->status = VAL;
122 map->addend = sym_addr + r->addend;
123 map->flags = r->flags;
124 list_add(&map->list, &reloc_addrmaps);
126 } else if ((r->flags & PCREL) && !(helper && safe)) {
127 *(int *)blank_addr =
128 sym_addr + r->addend - (unsigned long)blank_addr;
129 } else {
130 *(int *)blank_addr = sym_addr + r->addend;
132 if (debug >= 4)
133 printk("aft=%08x)\n", *(int *)blank_addr);
134 return 0;
137 void compute_address(char *sym_name, struct list_head *vals)
139 int i, have_added_val = 0;
140 const char *prefix[] = { ".text.", ".bss.", ".data.", NULL };
142 if (!safe)
143 return;
145 if (!(helper && safe)) {
146 struct reloc_nameval *nv = find_nameval(sym_name, 0);
147 if (nv != NULL && nv->status != NOVAL) {
148 if (!have_added_val)
149 release_vals(vals);
150 have_added_val = 1;
151 add_candidate_val(vals, nv->val);
153 if (debug >= 1) {
154 printk("ksplice: using detected sym %s=%08lx\n",
155 sym_name, nv->val);
159 if (have_added_val)
160 return;
162 if (starts_with(sym_name, ".rodata"))
163 return;
165 #ifdef CONFIG_KALLSYMS
166 kernel_lookup(sym_name, vals);
167 other_module_lookup(sym_name, vals);
168 #endif
170 for (i = 0; prefix[i] != NULL; i++) {
171 if (starts_with(sym_name, prefix[i])) {
172 compute_address(sym_name + strlen(prefix[i]), vals);
177 #ifdef CONFIG_KALLSYMS
178 /* Modified version of Linux's kallsyms_lookup_name */
179 void kernel_lookup(const char *name_wlabel, struct list_head *vals)
181 char namebuf[KSYM_NAME_LEN + 1];
182 unsigned long i;
183 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
184 unsigned long off;
185 #endif
187 const char *name = dup_wolabel(name_wlabel);
189 /* kallsyms compression was added by 5648d78927ca65e74aadc88a2b1d6431e55e78ec
190 * 2.6.10 was the first release after this commit
192 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
193 for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
194 off = ksplice_kallsyms_expand_symbol(off, namebuf);
196 if (strcmp(namebuf, name) == 0) {
197 add_candidate_val(vals, kallsyms_addresses[i]);
200 #else
201 char *knames;
203 for (i = 0, knames = kallsyms_names; i < kallsyms_num_syms; i++) {
204 unsigned prefix = *knames++;
206 strlcpy(namebuf + prefix, knames, KSYM_NAME_LEN - prefix);
208 if (strcmp(namebuf, name) == 0) {
209 add_candidate_val(vals, kallsyms_addresses[i]);
212 knames += strlen(knames) + 1;
214 #endif
216 kfree(name);
219 /* kallsyms compression was added by 5648d78927ca65e74aadc88a2b1d6431e55e78ec
220 * 2.6.10 was the first release after this commit
222 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
223 extern u8 kallsyms_token_table[];
224 extern u16 kallsyms_token_index[];
225 /* Modified version of Linux's kallsyms_expand_symbol */
226 long ksplice_kallsyms_expand_symbol(unsigned long off, char *result)
228 long len, skipped_first = 0;
229 const u8 *tptr, *data;
231 data = &kallsyms_names[off];
232 len = *data;
233 data++;
235 off += len + 1;
237 while (len) {
238 tptr = &kallsyms_token_table[kallsyms_token_index[*data]];
239 data++;
240 len--;
242 while (*tptr) {
243 if (skipped_first) {
244 *result = *tptr;
245 result++;
246 } else
247 skipped_first = 1;
248 tptr++;
252 *result = '\0';
254 return off;
256 #endif /* LINUX_VERSION_CODE */
258 void this_module_lookup(const char *name, struct list_head *vals)
260 ksplice_mod_find_sym(THIS_MODULE, name, vals);
261 if (list_empty(vals) && starts_with(name, ".text.")) {
262 ksplice_mod_find_sym(THIS_MODULE, name + strlen(".text."),
263 vals);
267 void other_module_lookup(const char *name_wlabel, struct list_head *vals)
269 struct module *m;
270 const char *name = dup_wolabel(name_wlabel);
272 list_for_each_entry(m, &(THIS_MODULE->list), list) {
273 if (!starts_with(m->name, ksplice_name)
274 && !ends_with(m->name, "_helper")) {
275 ksplice_mod_find_sym(m, name, vals);
279 kfree(name);
282 /* Modified version of Linux's mod_find_symname */
283 void
284 ksplice_mod_find_sym(struct module *m, const char *name, struct list_head *vals)
286 int i;
287 if (strlen(m->name) <= 1)
288 return;
290 for (i = 0; i < m->num_symtab; i++) {
291 const char *cursym_name = m->strtab + m->symtab[i].st_name;
292 if (strncmp(cursym_name, name, strlen(name)) != 0)
293 continue;
295 cursym_name = dup_wolabel(cursym_name);
296 if (strcmp(cursym_name, name) == 0 &&
297 m->symtab[i].st_value != 0) {
299 add_candidate_val(vals, m->symtab[i].st_value);
301 kfree(cursym_name);
304 #endif /* CONFIG_KALLSYMS */
306 void add_candidate_val(struct list_head *vals, long val)
308 struct candidate_val *tmp, *new;
310 list_for_each_entry(tmp, vals, list) {
311 if (tmp->val == val)
312 return;
314 new = kmalloc(sizeof(*new), GFP_KERNEL);
315 new->val = val;
316 list_add(&new->list, vals);
319 void release_vals(struct list_head *vals)
321 clear_list(vals, struct candidate_val, list);
324 struct reloc_nameval *find_nameval(char *name, int create)
326 struct list_head *pos;
327 struct reloc_nameval *nv, *new;
328 char *newname;
329 list_for_each(pos, &reloc_namevals) {
330 nv = list_entry(pos, struct reloc_nameval, list);
331 newname = nv->name;
332 if (starts_with(newname, ".text.")) {
333 newname += 6;
335 if (strcmp(newname, name) == 0) {
336 return nv;
339 if (!create)
340 return NULL;
342 new = kmalloc(sizeof(*new), GFP_KERNEL);
343 new->name = name;
344 new->val = 0;
345 new->status = NOVAL;
346 list_add(&new->list, &reloc_namevals);
347 return new;
350 struct reloc_addrmap *find_addrmap(long addr)
352 struct list_head *pos;
353 struct reloc_addrmap *map;
354 list_for_each(pos, &reloc_addrmaps) {
355 map = list_entry(pos, struct reloc_addrmap, list);
356 if (addr >= map->addr && addr <= map->addr + 3) {
357 return map;
360 return NULL;
363 void set_temp_myst_relocs(int status_val)
365 struct list_head *pos;
366 struct reloc_nameval *nv;
367 list_for_each(pos, &reloc_namevals) {
368 nv = list_entry(pos, struct reloc_nameval, list);
369 if (nv->status == TEMP) {
370 nv->status = status_val;