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