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
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
[];
26 static const int CONFIG_KALLSYMS_VAL
= 0;
29 /* defined by ksplice-create */
30 extern struct ksplice_reloc ksplice_init_relocs
, ksplice_relocs
;
32 static int safe
= 0, helper
= 0;
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)
45 for (r
= &ksplice_relocs
; r
->sym_name
!= NULL
; r
++) {
46 if (process_reloc(r
) != 0)
52 int process_reloc(struct ksplice_reloc
*r
)
56 struct reloc_addrmap
*map
;
58 #define blank_addr (r->blank_sect_addr+r->blank_offset)
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");
68 add_candidate_val(&vals
, r
->sym_addrs
[i
] + adjustment
);
72 if (*(int *)blank_addr
== 0x77777777) {
75 if (!(r
->flags
& SAFE
)) {
78 ("ksplice%s: reloc: skipped %s:%08lx (altinstr)\n",
79 (helper
? "_h" : ""), r
->sym_name
,
86 compute_address(r
->sym_name
, &vals
);
87 if (!singular(&vals
)) {
89 if (!(helper
&& safe
)) {
90 failed_to_find(r
->sym_name
);
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
);
107 sym_addr
= list_entry(vals
.next
, struct candidate_val
, list
)->val
;
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
)) {
128 sym_addr
+ r
->addend
- (unsigned long)blank_addr
;
130 *(int *)blank_addr
= sym_addr
+ r
->addend
;
133 printk("aft=%08x)\n", *(int *)blank_addr
);
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
};
145 if (!(helper
&& safe
)) {
146 struct reloc_nameval
*nv
= find_nameval(sym_name
, 0);
147 if (nv
!= NULL
&& nv
->status
!= NOVAL
) {
151 add_candidate_val(vals
, nv
->val
);
154 printk("ksplice: using detected sym %s=%08lx\n",
162 #ifdef CONFIG_KALLSYMS
163 kernel_lookup(sym_name
, vals
);
164 other_module_lookup(sym_name
, vals
);
167 for (i
= 0; prefix
[i
] != NULL
; i
++) {
168 if (starts_with(sym_name
, prefix
[i
])) {
169 compute_address(sym_name
+ strlen(prefix
[i
]), vals
);
174 #ifdef CONFIG_KALLSYMS
175 /* Modified version of Linux's kallsyms_lookup_name */
176 void kernel_lookup(const char *name_wlabel
, struct list_head
*vals
)
178 char namebuf
[KSYM_NAME_LEN
+ 1];
180 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
184 const char *name
= dup_wolabel(name_wlabel
);
186 /* kallsyms compression was added by 5648d78927ca65e74aadc88a2b1d6431e55e78ec
187 * 2.6.10 was the first release after this commit
189 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
190 for (i
= 0, off
= 0; i
< kallsyms_num_syms
; i
++) {
191 off
= ksplice_kallsyms_expand_symbol(off
, namebuf
);
193 if (strcmp(namebuf
, name
) == 0) {
194 add_candidate_val(vals
, kallsyms_addresses
[i
]);
200 for (i
= 0, knames
= kallsyms_names
; i
< kallsyms_num_syms
; i
++) {
201 unsigned prefix
= *knames
++;
203 strlcpy(namebuf
+ prefix
, knames
, KSYM_NAME_LEN
- prefix
);
205 if (strcmp(namebuf
, name
) == 0) {
206 add_candidate_val(vals
, kallsyms_addresses
[i
]);
209 knames
+= strlen(knames
) + 1;
216 /* kallsyms compression was added by 5648d78927ca65e74aadc88a2b1d6431e55e78ec
217 * 2.6.10 was the first release after this commit
219 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
220 extern u8 kallsyms_token_table
[];
221 extern u16 kallsyms_token_index
[];
222 /* Modified version of Linux's kallsyms_expand_symbol */
223 long ksplice_kallsyms_expand_symbol(unsigned long off
, char *result
)
225 long len
, skipped_first
= 0;
226 const u8
*tptr
, *data
;
228 data
= &kallsyms_names
[off
];
235 tptr
= &kallsyms_token_table
[kallsyms_token_index
[*data
]];
253 #endif /* LINUX_VERSION_CODE */
255 void this_module_lookup(const char *name
, struct list_head
*vals
)
257 ksplice_mod_find_sym(THIS_MODULE
, name
, vals
);
258 if (list_empty(vals
) && starts_with(name
, ".text.")) {
259 ksplice_mod_find_sym(THIS_MODULE
, name
+ strlen(".text."),
264 void other_module_lookup(const char *name_wlabel
, struct list_head
*vals
)
267 const char *name
= dup_wolabel(name_wlabel
);
269 list_for_each_entry(m
, &(THIS_MODULE
->list
), list
) {
270 if (!starts_with(m
->name
, ksplice_name
)
271 && !ends_with(m
->name
, "_helper")) {
272 ksplice_mod_find_sym(m
, name
, vals
);
279 /* Modified version of Linux's mod_find_symname */
281 ksplice_mod_find_sym(struct module
*m
, const char *name
, struct list_head
*vals
)
284 if (strlen(m
->name
) <= 1)
287 for (i
= 0; i
< m
->num_symtab
; i
++) {
288 const char *cursym_name
= m
->strtab
+ m
->symtab
[i
].st_name
;
289 if (strncmp(cursym_name
, name
, strlen(name
)) != 0)
292 cursym_name
= dup_wolabel(cursym_name
);
293 if (strcmp(cursym_name
, name
) == 0 &&
294 m
->symtab
[i
].st_value
!= 0) {
296 add_candidate_val(vals
, m
->symtab
[i
].st_value
);
301 #endif /* CONFIG_KALLSYMS */
303 void add_candidate_val(struct list_head
*vals
, long val
)
305 struct candidate_val
*tmp
, *new;
307 list_for_each_entry(tmp
, vals
, list
) {
311 new = kmalloc(sizeof(*new), GFP_KERNEL
);
313 list_add(&new->list
, vals
);
316 void release_vals(struct list_head
*vals
)
318 clear_list(vals
, struct candidate_val
, list
);
321 struct reloc_nameval
*find_nameval(char *name
, int create
)
323 struct list_head
*pos
;
324 struct reloc_nameval
*nv
, *new;
326 list_for_each(pos
, &reloc_namevals
) {
327 nv
= list_entry(pos
, struct reloc_nameval
, list
);
329 if (starts_with(newname
, ".text.")) {
332 if (strcmp(newname
, name
) == 0) {
339 new = kmalloc(sizeof(*new), GFP_KERNEL
);
343 list_add(&new->list
, &reloc_namevals
);
347 struct reloc_addrmap
*find_addrmap(long addr
)
349 struct list_head
*pos
;
350 struct reloc_addrmap
*map
;
351 list_for_each(pos
, &reloc_addrmaps
) {
352 map
= list_entry(pos
, struct reloc_addrmap
, list
);
353 if (addr
>= map
->addr
&& addr
<= map
->addr
+ 3) {
360 void set_temp_myst_relocs(int status_val
)
362 struct list_head
*pos
;
363 struct reloc_nameval
*nv
;
364 list_for_each(pos
, &reloc_namevals
) {
365 nv
= list_entry(pos
, struct reloc_nameval
, list
);
366 if (nv
->status
== TEMP
) {
367 nv
->status
= status_val
;