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 if (starts_with(sym_name
, ".rodata"))
165 #ifdef CONFIG_KALLSYMS
166 kernel_lookup(sym_name
, vals
);
167 other_module_lookup(sym_name
, vals
);
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];
183 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
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
]);
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;
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
];
238 tptr
= &kallsyms_token_table
[kallsyms_token_index
[*data
]];
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."),
267 void other_module_lookup(const char *name_wlabel
, struct list_head
*vals
)
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
);
282 /* Modified version of Linux's mod_find_symname */
284 ksplice_mod_find_sym(struct module
*m
, const char *name
, struct list_head
*vals
)
287 if (strlen(m
->name
) <= 1)
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)
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
);
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
) {
314 new = kmalloc(sizeof(*new), GFP_KERNEL
);
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;
329 list_for_each(pos
, &reloc_namevals
) {
330 nv
= list_entry(pos
, struct reloc_nameval
, list
);
332 if (starts_with(newname
, ".text.")) {
335 if (strcmp(newname
, name
) == 0) {
342 new = kmalloc(sizeof(*new), GFP_KERNEL
);
346 list_add(&new->list
, &reloc_namevals
);
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) {
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
;