db_sym.c revision 1.60 1 /* $NetBSD: db_sym.c,v 1.60 2011/04/11 04:22:32 mrg 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 (at) 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.60 2011/04/11 04:22:32 mrg 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 static int use_ksyms = true;
50 const db_symformat_t *db_symformat;
51 static db_forall_func_t db_sift;
52 extern db_symformat_t db_symformat_aout;
53 extern db_symformat_t db_symformat_elf;
54 #endif
55
56
57 /*
58 * Initialize the kernel debugger by initializing the master symbol
59 * table. Note that if initializing the master symbol table fails,
60 * no other symbol tables can be loaded.
61 */
62 void
63 ddb_init(int symsize, void *vss, void *vse)
64 {
65 #ifdef _KERNEL
66 ksyms_addsyms_elf(symsize, vss, vse); /* Will complain if necessary */
67 #else /* _KERNEL */
68 db_symformat = &db_symformat_elf;
69 if ((*db_symformat->sym_init)(symsize, vss, vse, TBLNAME) == true) {
70 use_ksyms = false;
71 return;
72 }
73 #endif /* _KERNEL */
74 }
75
76 bool
77 db_eqname(const char *src, const char *dst, int c)
78 {
79
80 if (!strcmp(src, dst))
81 return (true);
82 if (src[0] == c)
83 return (!strcmp(src+1,dst));
84 return (false);
85 }
86
87 bool
88 db_value_of_name(const char *name, db_expr_t *valuep)
89 {
90 char symbol[128];
91 char *mod, *sym;
92 #ifdef _KERNEL
93 unsigned long uval;
94 long val;
95 #endif
96
97 #ifndef _KERNEL
98 if (!use_ksyms) {
99 db_sym_t ssym;
100
101 /*
102 * Cannot load symtabs in a.out kernels, so the ':'
103 * style of selecting modules is irrelevant.
104 */
105 ssym = (*db_symformat->sym_lookup)(NULL, name);
106 if (ssym == DB_SYM_NULL)
107 return (false);
108 db_symbol_values(ssym, &name, valuep);
109 return (true);
110 }
111 #endif
112
113 (void)strlcpy(symbol, name, sizeof(symbol));
114 db_symsplit(symbol, &mod, &sym);
115 #ifdef _KERNEL
116 if (ksyms_getval_unlocked(mod, sym, &uval, KSYMS_EXTERN) == 0) {
117 val = (long) uval;
118 *valuep = (db_expr_t)val;
119 return true;
120 }
121 if (ksyms_getval_unlocked(mod, sym, &uval, KSYMS_ANY) == 0) {
122 val = (long) uval;
123 *valuep = (db_expr_t)val;
124 return true;
125 }
126 #endif
127 return false;
128 }
129
130 #ifndef _KERNEL
131 /* Private structure for passing args to db_sift() from db_sifting(). */
132 struct db_sift_args {
133 char *symstr;
134 int mode;
135 };
136
137 /*
138 * Does the work of db_sifting(), called once for each
139 * symbol via db_forall(), prints out symbols matching
140 * criteria.
141 */
142 static void
143 db_sift(db_symtab_t *stab, db_sym_t sym, char *name,
144 char *suffix, int prefix, void *arg)
145 {
146 char c, sc;
147 char *find, *p;
148 size_t len;
149 struct db_sift_args *dsa;
150
151 dsa = (struct db_sift_args*)arg;
152
153 find = dsa->symstr; /* String we're looking for. */
154 p = name; /* String we're searching within. */
155
156 /* Matching algorithm cribbed from strstr(), which is not
157 in the kernel. */
158 if ((c = *find++) != 0) {
159 len = strlen(find);
160 do {
161 do {
162 if ((sc = *p++) == 0)
163 return;
164 } while (sc != c);
165 } while (strncmp(p, find, len) != 0);
166 }
167 if (dsa->mode=='F') /* ala ls -F */
168 db_printf("%s%s ", name, suffix);
169 else
170 db_printf("%s ", name);
171 }
172 #endif
173
174 /*
175 * "Sift" for a partial symbol.
176 * Named for the Sun OpenPROM command ("sifting").
177 * If the symbol has a qualifier (e.g., ux:vm_map),
178 * then only the specified symbol table will be searched;
179 * otherwise, all symbol tables will be searched..
180 *
181 * "mode" is how-to-display, set from modifiers.
182 */
183 void
184 db_sifting(char *symstr, int mode)
185 {
186 #ifdef _KERNEL
187 char *mod, *sym;
188 #endif
189
190 #ifndef _KERNEL
191 struct db_sift_args dsa;
192
193 if (!use_ksyms) {
194 dsa.symstr = symstr;
195 dsa.mode = mode;
196 (*db_symformat->sym_forall)(NULL, db_sift, &dsa);
197 db_printf("\n");
198 return;
199 }
200 #endif
201
202 #ifdef _KERNEL
203 db_symsplit(symstr, &mod, &sym);
204 if (ksyms_sift(mod, sym, mode) == ENODEV)
205 db_error("invalid symbol table name");
206 #endif
207 }
208
209 /*
210 * Find the closest symbol to val, and return its name
211 * and the difference between val and the symbol found.
212 */
213 db_sym_t
214 db_search_symbol(db_addr_t val, db_strategy_t strategy, db_expr_t *offp)
215 {
216 unsigned int diff;
217 db_sym_t ret = DB_SYM_NULL;
218 #ifdef _KERNEL
219 unsigned long naddr;
220 const char *mod;
221 const char *sym;
222 #endif
223
224 #ifndef _KERNEL
225 if (!use_ksyms) {
226 db_expr_t newdiff;
227 db_sym_t ssym;
228
229 newdiff = diff = ~0;
230 ssym = (*db_symformat->sym_search)
231 (NULL, val, strategy, &newdiff);
232 if ((unsigned int) newdiff < diff) {
233 diff = newdiff;
234 ret = ssym;
235 }
236 *offp = diff;
237 return ret;
238 }
239 #endif
240
241 #ifdef _KERNEL
242 if (ksyms_getname(&mod, &sym, (vaddr_t)val, strategy) == 0) {
243 (void)ksyms_getval_unlocked(mod, sym, &naddr, KSYMS_ANY);
244 diff = val - (db_addr_t)naddr;
245 ret = (db_sym_t)naddr;
246 } else
247 #endif
248 diff = 0;
249 *offp = diff;
250 return ret;
251 }
252
253 /*
254 * Return name and value of a symbol
255 */
256 void
257 db_symbol_values(db_sym_t sym, const char **namep, db_expr_t *valuep)
258 {
259 #ifdef _KERNEL
260 const char *mod;
261 #endif
262
263 if (sym == DB_SYM_NULL) {
264 *namep = 0;
265 return;
266 }
267
268 #ifndef _KERNEL
269 if (!use_ksyms) {
270 db_expr_t value;
271
272 (*db_symformat->sym_value)(NULL, sym, namep, &value);
273 if (valuep)
274 *valuep = value;
275 return;
276 }
277 #endif
278
279 #ifdef _KERNEL
280 if (ksyms_getname(&mod, namep, (vaddr_t)sym,
281 KSYMS_ANY|KSYMS_EXACT) == 0) {
282 if (valuep)
283 *valuep = sym;
284 } else
285 #endif
286 *namep = NULL;
287 }
288
289
290 /*
291 * Print a the closest symbol to value
292 *
293 * After matching the symbol according to the given strategy
294 * we print it in the name+offset format, provided the symbol's
295 * value is close enough (eg smaller than db_maxoff).
296 * We also attempt to print [filename:linenum] when applicable
297 * (eg for procedure names).
298 *
299 * If we could not find a reasonable name+offset representation,
300 * then we just print the value in hex. Small values might get
301 * bogus symbol associations, e.g. 3 might get some absolute
302 * value like _INCLUDE_VERSION or something, therefore we do
303 * not accept symbols whose value is zero (and use plain hex).
304 * Also, avoid printing as "end+0x????" which is useless.
305 * The variable db_lastsym is used instead of "end" in case we
306 * add support for symbols in loadable driver modules.
307 */
308 extern char end[];
309 unsigned long db_lastsym = (unsigned long)end;
310 unsigned int db_maxoff = 0x100000;
311
312 void
313 db_symstr(char *buf, size_t buflen, db_expr_t off, db_strategy_t strategy)
314 {
315 const char *name;
316 #ifdef _KERNEL
317 const char *mod;
318 unsigned long val;
319 #endif
320
321 #ifndef _KERNEL
322 if (!use_ksyms) {
323 db_expr_t d;
324 char *filename;
325 db_expr_t value;
326 int linenum;
327 db_sym_t cursym;
328
329 if ((unsigned long) off <= db_lastsym) {
330 cursym = db_search_symbol(off, strategy, &d);
331 db_symbol_values(cursym, &name, &value);
332 if (name != NULL &&
333 ((unsigned int) d < db_maxoff) &&
334 value != 0) {
335 strlcpy(buf, name, buflen);
336 if (d) {
337 strlcat(buf, "+", buflen);
338 db_format_radix(buf+strlen(buf),
339 24, d, true);
340 }
341 if (strategy == DB_STGY_PROC) {
342 if ((*db_symformat->sym_line_at_pc)
343 (NULL, cursym, &filename,
344 &linenum, off))
345 snprintf(buf + strlen(buf),
346 buflen - strlen(buf),
347 " [%s:%d]",
348 filename, linenum);
349 }
350 return;
351 }
352 }
353 strlcpy(buf, db_num_to_str(off), buflen);
354 return;
355 }
356 #endif
357 #ifdef _KERNEL
358 if (ksyms_getname(&mod, &name, (vaddr_t)off,
359 strategy|KSYMS_CLOSEST) == 0) {
360 (void)ksyms_getval_unlocked(mod, name, &val, KSYMS_ANY);
361 if (((off - val) < db_maxoff) && val) {
362 snprintf(buf, buflen, "%s:%s", mod, name);
363 if (off - val) {
364 strlcat(buf, "+", buflen);
365 db_format_radix(buf+strlen(buf),
366 24, off - val, true);
367 }
368 #ifdef notyet
369 if (strategy & KSYMS_PROC) {
370 if (ksyms_fmaddr(off, &filename, &linenum) == 0)
371 snprintf(buf + strlen(buf),
372 buflen - strlen(buf),
373 " [%s:%d]", filename, linenum);
374 }
375 #endif
376 return;
377 }
378 }
379 strlcpy(buf, db_num_to_str(off), buflen);
380 #endif
381 }
382
383 void
384 db_printsym(db_expr_t off, db_strategy_t strategy,
385 void (*pr)(const char *, ...))
386 {
387 const char *name;
388 #ifdef _KERNEL
389 const char *mod;
390 unsigned long uval;
391 long val;
392 #endif
393 #ifdef notyet
394 char *filename;
395 int linenum;
396 #endif
397
398 #ifndef _KERNEL
399 if (!use_ksyms) {
400 db_expr_t d;
401 char *filename;
402 db_expr_t value;
403 int linenum;
404 db_sym_t cursym;
405
406 if ((unsigned long) off <= db_lastsym) {
407 cursym = db_search_symbol(off, strategy, &d);
408 db_symbol_values(cursym, &name, &value);
409 if (name != NULL &&
410 ((unsigned int) d < db_maxoff) &&
411 value != 0) {
412 (*pr)("%s", name);
413 if (d) {
414 char tbuf[24];
415
416 db_format_radix(tbuf, 24, d, true);
417 (*pr)("+%s", tbuf);
418 }
419 if (strategy == DB_STGY_PROC) {
420 if ((*db_symformat->sym_line_at_pc)
421 (NULL, cursym, &filename,
422 &linenum, off))
423 (*pr)(" [%s:%d]",
424 filename, linenum);
425 }
426 return;
427 }
428 }
429 (*pr)(db_num_to_str(off));
430 return;
431 }
432 #endif
433 #ifdef _KERNEL
434 if (ksyms_getname(&mod, &name, (vaddr_t)off,
435 strategy|KSYMS_CLOSEST) == 0) {
436 (void)ksyms_getval_unlocked(mod, name, &uval, KSYMS_ANY);
437 val = (long) uval;
438 if (((off - val) < db_maxoff) && val) {
439 (*pr)("%s:%s", mod, name);
440 if (off - val) {
441 char tbuf[24];
442
443 db_format_radix(tbuf, 24, off - val, true);
444 (*pr)("+%s", tbuf);
445 }
446 #ifdef notyet
447 if (strategy & KSYMS_PROC) {
448 if (ksyms_fmaddr(off, &filename, &linenum) == 0)
449 (*pr)(" [%s:%d]", filename, linenum);
450 }
451 #endif
452 return;
453 }
454 }
455 #endif
456 (*pr)(db_num_to_str(off));
457 return;
458 }
459
460 /*
461 * Splits a string in the form "mod:sym" to two strings.
462 */
463 static void
464 db_symsplit(char *str, char **mod, char **sym)
465 {
466 char *cp;
467
468 if ((cp = strchr(str, ':')) != NULL) {
469 *cp++ = '\0';
470 *mod = str;
471 *sym = cp;
472 } else {
473 *mod = NULL;
474 *sym = str;
475 }
476 }
477
478 bool
479 db_sym_numargs(db_sym_t cursym, int *nargp, char **argnamep)
480 {
481 #ifndef _KERNEL
482 if (!use_ksyms)
483 return ((*db_symformat->sym_numargs)(NULL, cursym, nargp,
484 argnamep));
485 #endif
486 return (false);
487 }
488
489