1 | /* $NetBSD: db_sym.c,v 1.64 2012/02/10 02:14:04 christos Exp $ */ |
2 | |
3 | /* |
4 | * Mach Operating System |
5 | * Copyright (c) 1991,1990 Carnegie Mellon University |
6 | * All Rights Reserved. |
7 | * |
8 | * Permission to use, copy, modify and distribute this software and its |
9 | * documentation is hereby granted, provided that both the copyright |
10 | * notice and this permission notice appear in all copies of the |
11 | * software, derivative works or modified versions, and any portions |
12 | * thereof, and that both notices appear in supporting documentation. |
13 | * |
14 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" |
15 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR |
16 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. |
17 | * |
18 | * Carnegie Mellon requests users of this software to return to |
19 | * |
20 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU |
21 | * School of Computer Science |
22 | * Carnegie Mellon University |
23 | * Pittsburgh PA 15213-3890 |
24 | * |
25 | * any improvements or extensions that they make and grant Carnegie the |
26 | * rights to redistribute these changes. |
27 | */ |
28 | |
29 | #include <sys/cdefs.h> |
30 | __KERNEL_RCSID(0, "$NetBSD: db_sym.c,v 1.64 2012/02/10 02:14:04 christos Exp $" ); |
31 | |
32 | #ifdef _KERNEL_OPT |
33 | #include "opt_ddbparam.h" |
34 | #endif |
35 | |
36 | #include <sys/param.h> |
37 | #include <sys/proc.h> |
38 | #include <sys/systm.h> |
39 | #include <sys/ksyms.h> |
40 | |
41 | #include <ddb/ddb.h> |
42 | |
43 | static void db_symsplit(char *, char **, char **); |
44 | |
45 | |
46 | #ifndef _KERNEL |
47 | #define TBLNAME "netbsd" |
48 | |
49 | #define use_ksyms 0 |
50 | |
51 | const db_symformat_t *db_symformat; |
52 | static db_forall_func_t db_sift; |
53 | extern db_symformat_t db_symformat_aout; |
54 | extern db_symformat_t db_symformat_elf; |
55 | #endif |
56 | |
57 | |
58 | /* |
59 | * Initialize the kernel debugger by initializing the master symbol |
60 | * table. Note that if initializing the master symbol table fails, |
61 | * no other symbol tables can be loaded. |
62 | */ |
63 | void |
64 | ddb_init(int symsize, void *vss, void *vse) |
65 | { |
66 | #ifdef _KERNEL |
67 | ksyms_addsyms_elf(symsize, vss, vse); /* Will complain if necessary */ |
68 | #else /* _KERNEL */ |
69 | db_symformat = &db_symformat_elf; |
70 | if ((*db_symformat->sym_init)(symsize, vss, vse, TBLNAME) != true) |
71 | printf("sym_init failed" ); |
72 | #endif /* _KERNEL */ |
73 | } |
74 | |
75 | bool |
76 | db_eqname(const char *src, const char *dst, int c) |
77 | { |
78 | |
79 | if (!strcmp(src, dst)) |
80 | return (true); |
81 | if (src[0] == c) |
82 | return (!strcmp(src+1,dst)); |
83 | return (false); |
84 | } |
85 | |
86 | bool |
87 | db_value_of_name(const char *name, db_expr_t *valuep) |
88 | { |
89 | char symbol[128]; |
90 | char *mod, *sym; |
91 | #ifdef _KERNEL |
92 | unsigned long uval; |
93 | long val; |
94 | #endif |
95 | |
96 | #ifndef _KERNEL |
97 | if (!use_ksyms) { |
98 | db_sym_t ssym; |
99 | |
100 | /* |
101 | * Cannot load symtabs in a.out kernels, so the ':' |
102 | * style of selecting modules is irrelevant. |
103 | */ |
104 | ssym = (*db_symformat->sym_lookup)(NULL, name); |
105 | if (ssym == DB_SYM_NULL) |
106 | return (false); |
107 | db_symbol_values(ssym, &name, valuep); |
108 | return (true); |
109 | } |
110 | #endif |
111 | |
112 | (void)strlcpy(symbol, name, sizeof(symbol)); |
113 | db_symsplit(symbol, &mod, &sym); |
114 | #ifdef _KERNEL |
115 | if (ksyms_getval_unlocked(mod, sym, &uval, KSYMS_EXTERN) == 0) { |
116 | val = (long) uval; |
117 | *valuep = (db_expr_t)val; |
118 | return true; |
119 | } |
120 | if (ksyms_getval_unlocked(mod, sym, &uval, KSYMS_ANY) == 0) { |
121 | val = (long) uval; |
122 | *valuep = (db_expr_t)val; |
123 | return true; |
124 | } |
125 | #endif |
126 | return false; |
127 | } |
128 | |
129 | #ifndef _KERNEL |
130 | /* Private structure for passing args to db_sift() from db_sifting(). */ |
131 | struct db_sift_args { |
132 | char *symstr; |
133 | int mode; |
134 | }; |
135 | |
136 | /* |
137 | * Does the work of db_sifting(), called once for each |
138 | * symbol via db_forall(), prints out symbols matching |
139 | * criteria. |
140 | */ |
141 | static void |
142 | db_sift(db_symtab_t *stab, db_sym_t sym, char *name, |
143 | char *suffix, int prefix, void *arg) |
144 | { |
145 | char c, sc; |
146 | char *find, *p; |
147 | size_t len; |
148 | struct db_sift_args *dsa; |
149 | |
150 | dsa = (struct db_sift_args*)arg; |
151 | |
152 | find = dsa->symstr; /* String we're looking for. */ |
153 | p = name; /* String we're searching within. */ |
154 | |
155 | /* Matching algorithm cribbed from strstr(), which is not |
156 | in the kernel. */ |
157 | if ((c = *find++) != 0) { |
158 | len = strlen(find); |
159 | do { |
160 | do { |
161 | if ((sc = *p++) == 0) |
162 | return; |
163 | } while (sc != c); |
164 | } while (strncmp(p, find, len) != 0); |
165 | } |
166 | if (dsa->mode=='F') /* ala ls -F */ |
167 | db_printf("%s%s " , name, suffix); |
168 | else |
169 | db_printf("%s " , name); |
170 | } |
171 | #endif |
172 | |
173 | /* |
174 | * "Sift" for a partial symbol. |
175 | * Named for the Sun OpenPROM command ("sifting"). |
176 | * If the symbol has a qualifier (e.g., ux:vm_map), |
177 | * then only the specified symbol table will be searched; |
178 | * otherwise, all symbol tables will be searched.. |
179 | * |
180 | * "mode" is how-to-display, set from modifiers. |
181 | */ |
182 | void |
183 | db_sifting(char *symstr, int mode) |
184 | { |
185 | #ifdef _KERNEL |
186 | char *mod, *sym; |
187 | #endif |
188 | |
189 | #ifndef _KERNEL |
190 | struct db_sift_args dsa; |
191 | |
192 | if (!use_ksyms) { |
193 | dsa.symstr = symstr; |
194 | dsa.mode = mode; |
195 | (*db_symformat->sym_forall)(NULL, db_sift, &dsa); |
196 | db_printf("\n" ); |
197 | return; |
198 | } |
199 | #endif |
200 | |
201 | #ifdef _KERNEL |
202 | db_symsplit(symstr, &mod, &sym); |
203 | if (ksyms_sift(mod, sym, mode) == ENODEV) |
204 | db_error("invalid symbol table name" ); |
205 | #endif |
206 | } |
207 | |
208 | /* |
209 | * Find the closest symbol to val, and return its name |
210 | * and the difference between val and the symbol found. |
211 | */ |
212 | db_sym_t |
213 | db_search_symbol(db_addr_t val, db_strategy_t strategy, db_expr_t *offp) |
214 | { |
215 | unsigned int diff; |
216 | db_sym_t ret = DB_SYM_NULL; |
217 | #ifdef _KERNEL |
218 | unsigned long naddr; |
219 | const char *mod; |
220 | const char *sym; |
221 | #endif |
222 | |
223 | #ifndef _KERNEL |
224 | if (!use_ksyms) { |
225 | db_expr_t newdiff; |
226 | db_sym_t ssym; |
227 | |
228 | newdiff = diff = ~0; |
229 | ssym = (*db_symformat->sym_search) |
230 | (NULL, val, strategy, &newdiff); |
231 | if ((unsigned int) newdiff < diff) { |
232 | diff = newdiff; |
233 | ret = ssym; |
234 | } |
235 | *offp = diff; |
236 | return ret; |
237 | } |
238 | #endif |
239 | |
240 | #ifdef _KERNEL |
241 | if (ksyms_getname(&mod, &sym, (vaddr_t)val, strategy) == 0) { |
242 | (void)ksyms_getval_unlocked(mod, sym, &naddr, KSYMS_ANY); |
243 | diff = val - (db_addr_t)naddr; |
244 | ret = (db_sym_t)naddr; |
245 | } else |
246 | #endif |
247 | diff = 0; |
248 | *offp = diff; |
249 | return ret; |
250 | } |
251 | |
252 | /* |
253 | * Return name and value of a symbol |
254 | */ |
255 | void |
256 | db_symbol_values(db_sym_t sym, const char **namep, db_expr_t *valuep) |
257 | { |
258 | #ifdef _KERNEL |
259 | const char *mod; |
260 | #endif |
261 | |
262 | if (sym == DB_SYM_NULL) { |
263 | *namep = 0; |
264 | return; |
265 | } |
266 | |
267 | #ifndef _KERNEL |
268 | if (!use_ksyms) { |
269 | db_expr_t value; |
270 | |
271 | (*db_symformat->sym_value)(NULL, sym, namep, &value); |
272 | if (valuep) |
273 | *valuep = value; |
274 | return; |
275 | } |
276 | #endif |
277 | |
278 | #ifdef _KERNEL |
279 | if (ksyms_getname(&mod, namep, (vaddr_t)sym, |
280 | KSYMS_ANY|KSYMS_EXACT) == 0) { |
281 | if (valuep) |
282 | *valuep = sym; |
283 | } else |
284 | #endif |
285 | *namep = NULL; |
286 | } |
287 | |
288 | |
289 | /* |
290 | * Print a the closest symbol to value |
291 | * |
292 | * After matching the symbol according to the given strategy |
293 | * we print it in the name+offset format, provided the symbol's |
294 | * value is close enough (eg smaller than db_maxoff). |
295 | * We also attempt to print [filename:linenum] when applicable |
296 | * (eg for procedure names). |
297 | * |
298 | * If we could not find a reasonable name+offset representation, |
299 | * then we just print the value in hex. Small values might get |
300 | * bogus symbol associations, e.g. 3 might get some absolute |
301 | * value like _INCLUDE_VERSION or something, therefore we do |
302 | * not accept symbols whose value is zero (and use plain hex). |
303 | */ |
304 | unsigned int db_maxoff = 0x100000; |
305 | |
306 | void |
307 | db_symstr(char *buf, size_t buflen, db_expr_t off, db_strategy_t strategy) |
308 | { |
309 | const char *name; |
310 | #ifdef _KERNEL |
311 | const char *mod; |
312 | unsigned long val; |
313 | #endif |
314 | |
315 | #ifndef _KERNEL |
316 | if (!use_ksyms) { |
317 | db_expr_t d; |
318 | char *filename; |
319 | db_expr_t value; |
320 | int linenum; |
321 | db_sym_t cursym; |
322 | |
323 | cursym = db_search_symbol(off, strategy, &d); |
324 | db_symbol_values(cursym, &name, &value); |
325 | if (name != NULL && ((unsigned int)d < db_maxoff) && |
326 | value != 0) { |
327 | strlcpy(buf, name, buflen); |
328 | if (d) { |
329 | strlcat(buf, "+" , buflen); |
330 | db_format_radix(buf + strlen(buf), 24, d, true); |
331 | } |
332 | if (strategy == DB_STGY_PROC) { |
333 | if ((*db_symformat->sym_line_at_pc) |
334 | (NULL, cursym, &filename, &linenum, off)) { |
335 | size_t len = strlen(buf); |
336 | snprintf(buf + len, buflen - len, |
337 | " [%s:%d]" , filename, linenum); |
338 | } |
339 | } |
340 | return; |
341 | } |
342 | strlcpy(buf, db_num_to_str(off), buflen); |
343 | return; |
344 | } |
345 | #endif |
346 | #ifdef _KERNEL |
347 | if (ksyms_getname(&mod, &name, (vaddr_t)off, |
348 | strategy|KSYMS_CLOSEST) == 0) { |
349 | (void)ksyms_getval_unlocked(mod, name, &val, KSYMS_ANY); |
350 | if (((off - val) < db_maxoff) && val) { |
351 | snprintf(buf, buflen, "%s:%s" , mod, name); |
352 | if (off - val) { |
353 | strlcat(buf, "+" , buflen); |
354 | db_format_radix(buf+strlen(buf), |
355 | 24, off - val, true); |
356 | } |
357 | #ifdef notyet |
358 | if (strategy & KSYMS_PROC) { |
359 | if (ksyms_fmaddr(off, &filename, &linenum) == 0) |
360 | snprintf(buf + strlen(buf), |
361 | buflen - strlen(buf), |
362 | " [%s:%d]" , filename, linenum); |
363 | } |
364 | #endif |
365 | return; |
366 | } |
367 | } |
368 | strlcpy(buf, db_num_to_str(off), buflen); |
369 | #endif |
370 | } |
371 | |
372 | void |
373 | db_printsym(db_expr_t off, db_strategy_t strategy, |
374 | void (*pr)(const char *, ...)) |
375 | { |
376 | const char *name; |
377 | #ifdef _KERNEL |
378 | const char *mod; |
379 | unsigned long uval; |
380 | long val; |
381 | #endif |
382 | #ifdef notyet |
383 | char *filename; |
384 | int linenum; |
385 | #endif |
386 | |
387 | #ifndef _KERNEL |
388 | if (!use_ksyms) { |
389 | db_expr_t d; |
390 | char *filename; |
391 | db_expr_t value; |
392 | int linenum; |
393 | db_sym_t cursym; |
394 | |
395 | cursym = db_search_symbol(off, strategy, &d); |
396 | db_symbol_values(cursym, &name, &value); |
397 | if (name != NULL && ((unsigned int)d < db_maxoff) && |
398 | value != 0) { |
399 | (*pr)("%s" , name); |
400 | if (d) { |
401 | char tbuf[24]; |
402 | |
403 | db_format_radix(tbuf, 24, d, true); |
404 | (*pr)("+%s" , tbuf); |
405 | } |
406 | if (strategy == DB_STGY_PROC) { |
407 | if ((*db_symformat->sym_line_at_pc) |
408 | (NULL, cursym, &filename, &linenum, off)) |
409 | (*pr)(" [%s:%d]" , filename, linenum); |
410 | } |
411 | return; |
412 | } |
413 | (*pr)("%s" , db_num_to_str(off)); |
414 | return; |
415 | } |
416 | #endif |
417 | #ifdef _KERNEL |
418 | if (ksyms_getname(&mod, &name, (vaddr_t)off, |
419 | strategy|KSYMS_CLOSEST) == 0) { |
420 | (void)ksyms_getval_unlocked(mod, name, &uval, KSYMS_ANY); |
421 | val = (long) uval; |
422 | if (((off - val) < db_maxoff) && val) { |
423 | (*pr)("%s:%s" , mod, name); |
424 | if (off - val) { |
425 | char tbuf[24]; |
426 | |
427 | db_format_radix(tbuf, 24, off - val, true); |
428 | (*pr)("+%s" , tbuf); |
429 | } |
430 | #ifdef notyet |
431 | if (strategy & KSYMS_PROC) { |
432 | if (ksyms_fmaddr(off, &filename, &linenum) == 0) |
433 | (*pr)(" [%s:%d]" , filename, linenum); |
434 | } |
435 | #endif |
436 | return; |
437 | } |
438 | } |
439 | #endif |
440 | (*pr)("%s" , db_num_to_str(off)); |
441 | return; |
442 | } |
443 | |
444 | /* |
445 | * Splits a string in the form "mod:sym" to two strings. |
446 | */ |
447 | static void |
448 | db_symsplit(char *str, char **mod, char **sym) |
449 | { |
450 | char *cp; |
451 | |
452 | if ((cp = strchr(str, ':')) != NULL) { |
453 | *cp++ = '\0'; |
454 | *mod = str; |
455 | *sym = cp; |
456 | } else { |
457 | *mod = NULL; |
458 | *sym = str; |
459 | } |
460 | } |
461 | |
462 | bool |
463 | db_sym_numargs(db_sym_t cursym, int *nargp, char **argnamep) |
464 | { |
465 | #ifndef _KERNEL |
466 | if (!use_ksyms) |
467 | return ((*db_symformat->sym_numargs)(NULL, cursym, nargp, |
468 | argnamep)); |
469 | #endif |
470 | return (false); |
471 | } |
472 | |
473 | |