xref: /openbsd-src/usr.sbin/kvm_mkdb/nlist.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: nlist.c,v 1.37 2007/09/02 15:19:38 deraadt Exp $	*/
2 
3 /*-
4  * Copyright (c) 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #ifndef lint
33 #if 0
34 static char sccsid[] = "from: @(#)nlist.c	8.1 (Berkeley) 6/6/93";
35 #else
36 static const char rcsid[] = "$OpenBSD: nlist.c,v 1.37 2007/09/02 15:19:38 deraadt Exp $";
37 #endif
38 #endif /* not lint */
39 
40 #include <sys/param.h>
41 
42 #include <a.out.h>
43 #include <db.h>
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <kvm.h>
48 #include <limits.h>
49 #include <paths.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 
55 #include "extern.h"
56 
57 #include <sys/mman.h>
58 #include <sys/stat.h>
59 #include <sys/file.h>
60 #include <sys/sysctl.h>
61 
62 #ifdef _NLIST_DO_ELF
63 #include <elf_abi.h>
64 #endif
65 
66 #ifdef _NLIST_DO_ECOFF
67 #include <sys/exec_ecoff.h>
68 #endif
69 
70 typedef struct nlist NLIST;
71 #define	_strx	n_un.n_strx
72 #define	_name	n_un.n_name
73 
74 static char *kfile;
75 static char *fmterr;
76 
77 #if defined(_NLIST_DO_AOUT)
78 
79 static u_long get_kerntext(char *kfn, u_int magic);
80 
81 int
82 __aout_knlist(int fd, DB *db, int ksyms)
83 {
84 	int nsyms;
85 	struct exec ebuf;
86 	FILE *fp;
87 	NLIST nbuf;
88 	DBT data, key;
89 	int nr, strsize;
90 	size_t len;
91 	u_long kerntextoff;
92 	size_t snamesize = 0;
93 	char *strtab, buf[1024], *sname, *p;
94 
95 	/* Read in exec structure. */
96 	nr = read(fd, &ebuf, sizeof(struct exec));
97 	if (nr != sizeof(struct exec)) {
98 		fmterr = "no exec header";
99 		return (1);
100 	}
101 
102 	/* Check magic number and symbol count. */
103 	if (N_BADMAG(ebuf)) {
104 		fmterr = "bad magic number";
105 		return (1);
106 	}
107 
108 	/* Must have a symbol table. */
109 	if (!ebuf.a_syms) {
110 		fmterr = "stripped";
111 		return (-1);
112 	}
113 
114 	/* Seek to string table. */
115 	if (lseek(fd, N_STROFF(ebuf), SEEK_SET) == -1) {
116 		fmterr = "corrupted string table";
117 		return (-1);
118 	}
119 
120 	/* Read in the size of the string table. */
121 	nr = read(fd, (char *)&strsize, sizeof(strsize));
122 	if (nr != sizeof(strsize)) {
123 		fmterr = "no symbol table";
124 		return (-1);
125 	}
126 
127 	/* Read in the string table. */
128 	strsize -= sizeof(strsize);
129 	if (!(strtab = malloc(strsize)))
130 		errx(1, "cannot allocate %d bytes for string table", strsize);
131 	if ((nr = read(fd, strtab, strsize)) != strsize) {
132 		fmterr = "corrupted symbol table";
133 		return (-1);
134 	}
135 
136 	/* Seek to symbol table. */
137 	if (!(fp = fdopen(fd, "r")))
138 		err(1, "%s", kfile);
139 	if (fseek(fp, N_SYMOFF(ebuf), SEEK_SET) == -1) {
140 		warn("fseek %s", kfile);
141 		return (-1);
142 	}
143 
144 	data.data = (u_char *)&nbuf;
145 	data.size = sizeof(NLIST);
146 
147 	kerntextoff = get_kerntext(kfile, N_GETMAGIC(ebuf));
148 
149 	/* Read each symbol and enter it into the database. */
150 	nsyms = ebuf.a_syms / sizeof(struct nlist);
151 	sname = NULL;
152 	while (nsyms--) {
153 		if (fread((char *)&nbuf, sizeof (NLIST), 1, fp) != 1) {
154 			if (feof(fp))
155 				fmterr = "corrupted symbol table";
156 			else
157 				warn("%s", kfile);
158 			return (-1);
159 		}
160 		if (!nbuf._strx || (nbuf.n_type & N_STAB))
161 			continue;
162 
163 		/* If the symbol does not start with '_', add one */
164 		p = strtab + nbuf._strx - sizeof(int);
165 		if (*p != '_') {
166 			len = strlen(p) + 1;
167 			if (len >= snamesize) {
168 				char *newsname;
169 				int newsnamesize = len + 1024;
170 
171 				newsname = realloc(sname, newsnamesize);
172 				if (newsname == NULL) {
173 					if (sname)
174 						free(sname);
175 					sname = NULL;
176 				} else {
177 					sname = newsname;
178 					snamesize = newsnamesize;
179 				}
180 			}
181 			if (sname == NULL)
182 				errx(1, "cannot allocate memory");
183 			*sname = '_';
184 			strlcpy(sname + 1, p, snamesize  - 1);
185 			key.data = (u_char *)sname;
186 			key.size = len;
187 		} else {
188 			key.data = (u_char *)p;
189 			key.size = strlen((char *)key.data);
190 		}
191 		if (db->put(db, &key, &data, 0))
192 			err(1, "record enter");
193 
194 		if (strcmp((char *)key.data, VRS_SYM) == 0) {
195 			long cur_off;
196 
197 			if (!ksyms) {
198 				/*
199 				 * Calculate offset relative to a normal
200 				 * (non-kernel) a.out.  Kerntextoff is where the
201 				 * kernel is really loaded; N_TXTADDR is where
202 				 * a normal file is loaded.  From there, locate
203 				 * file offset in text or data.
204 				 */
205 				long voff;
206 
207 				voff = nbuf.n_value-kerntextoff+N_TXTADDR(ebuf);
208 				if ((nbuf.n_type & N_TYPE) == N_TEXT)
209 					voff += N_TXTOFF(ebuf)-N_TXTADDR(ebuf);
210 				else
211 					voff += N_DATOFF(ebuf)-N_DATADDR(ebuf);
212 				cur_off = ftell(fp);
213 				if (fseek(fp, voff, SEEK_SET) == -1) {
214 					fmterr = "corrupted string table";
215 					return (-1);
216 				}
217 
218 				/*
219 				 * Read version string up to, and including
220 				 * newline.  This code assumes that a newline
221 				 * terminates the version line.
222 				 */
223 				if (fgets(buf, sizeof(buf), fp) == NULL) {
224 					fmterr = "corrupted string table";
225 					return (-1);
226 				}
227 			} else {
228 				/*
229 				 * This is /dev/ksyms or a look alike.
230 				 * Use sysctl() to get version since we
231 				 * don't have real text or data.
232 				 */
233 				int mib[2];
234 
235 				mib[0] = CTL_KERN;
236 				mib[1] = KERN_VERSION;
237 				len = sizeof(buf);
238 				if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
239 					err(1, "sysctl can't find kernel "
240 					    "version string");
241 				}
242 				if ((p = strchr(buf, '\n')) != NULL)
243 					*(p+1) = '\0';
244 			}
245 			key.data = (u_char *)VRS_KEY;
246 			key.size = sizeof(VRS_KEY) - 1;
247 			data.data = (u_char *)buf;
248 			data.size = strlen(buf);
249 			if (db->put(db, &key, &data, 0))
250 				err(1, "record enter");
251 
252 			/* Restore to original values. */
253 			data.data = (u_char *)&nbuf;
254 			data.size = sizeof(NLIST);
255 			if (!ksyms && fseek(fp, cur_off, SEEK_SET) == -1) {
256 				fmterr = "corrupted string table";
257 				return (-1);
258 			}
259 		}
260 	}
261 	(void)fclose(fp);
262 	return (0);
263 }
264 
265 /*
266  * XXX: Using this value from machine/param.h introduces a
267  * XXX: machine dependency on this program, so /usr can not
268  * XXX: be shared between (i.e.) several m68k machines.
269  * Instead of compiling in KERNTEXTOFF or KERNBASE, try to
270  * determine the text start address from a standard symbol.
271  * For backward compatibility, use the old compiled-in way
272  * when the standard symbol name is not found.
273  */
274 #ifndef KERNTEXTOFF
275 #define KERNTEXTOFF KERNBASE
276 #endif
277 
278 static u_long
279 get_kerntext(char *name, u_int magic)
280 {
281 	NLIST nl[2];
282 
283 	bzero((caddr_t)nl, sizeof(nl));
284 	nl[0]._name = "_kernel_text";
285 
286 	if (nlist(name, nl) != 0)
287 		return (KERNTEXTOFF);
288 
289 	return (nl[0].n_value);
290 }
291 
292 #endif /* _NLIST_DO_AOUT */
293 
294 #ifdef _NLIST_DO_ELF
295 int
296 __elf_knlist(int fd, DB *db, int ksyms)
297 {
298 	caddr_t strtab;
299 	off_t symstroff, symoff;
300 	u_long symsize, symstrsize;
301 	u_long kernvma, kernoffs;
302 	int i, j;
303 	Elf_Sym sbuf;
304 	char buf[1024];
305 	Elf_Ehdr eh;
306 	Elf_Shdr *sh = NULL;
307 	DBT data, key;
308 	NLIST nbuf;
309 	FILE *fp;
310 	int usemalloc = 0;
311 
312 	if ((fp = fdopen(fd, "r")) == NULL)
313 		err(1, "%s", kfile);
314 
315 	if (fseek(fp, (off_t)0, SEEK_SET) == -1 ||
316 	    fread(&eh, sizeof(eh), 1, fp) != 1 ||
317 	    !IS_ELF(eh))
318 		return (1);
319 
320 	sh = (Elf_Shdr *)calloc(sizeof(Elf_Shdr), eh.e_shnum);
321 	if (sh == NULL)
322 		errx(1, "cannot allocate %d bytes for symbol header",
323 		    sizeof(Elf_Shdr) * eh.e_shnum);
324 
325 	if (fseek (fp, eh.e_shoff, SEEK_SET) < 0) {
326 		fmterr = "no exec header";
327 		return (-1);
328 	}
329 
330 	if (fread(sh, sizeof(Elf_Shdr) * eh.e_shnum, 1, fp) != 1) {
331 		fmterr = "no exec header";
332 		return (-1);
333 	}
334 
335 
336 	symstrsize = symsize = kernvma = 0;
337 	for (i = 0; i < eh.e_shnum; i++) {
338 		if (sh[i].sh_type == SHT_STRTAB) {
339 			for (j = 0; j < eh.e_shnum; j++)
340 				if (sh[j].sh_type == SHT_SYMTAB &&
341 				    sh[j].sh_link == (unsigned)i) {
342 					symstroff = sh[i].sh_offset;
343 					symstrsize = sh[i].sh_size;
344 			}
345 		} else if (sh[i].sh_type == SHT_SYMTAB) {
346 			symoff = sh[i].sh_offset;
347 			symsize = sh[i].sh_size;
348 		} else if (sh[i].sh_type == SHT_PROGBITS &&
349 		    (sh[i].sh_flags & SHF_EXECINSTR)) {
350 			kernvma = sh[i].sh_addr;
351 			kernoffs = sh[i].sh_offset;
352 		}
353 	}
354 
355 	if (!symstrsize || !symsize || !kernvma) {
356 		fmterr = "corrupt file";
357 		return (-1);
358 	}
359 
360 	/*
361 	 * Map string table into our address space.  This gives us
362 	 * an easy way to randomly access all the strings, without
363 	 * making the memory allocation permanent as with malloc/free
364 	 * (i.e., munmap will return it to the system).
365 	 *
366 	 * XXX - we really want to check if this is a regular file.
367 	 *	 then we probably want a MAP_PRIVATE here.
368 	 */
369 	strtab = mmap(NULL, (size_t)symstrsize, PROT_READ,
370 	    MAP_SHARED|MAP_FILE, fileno(fp), symstroff);
371 	if (strtab == MAP_FAILED) {
372 		usemalloc = 1;
373 		if ((strtab = malloc(symstrsize)) == NULL) {
374 			fmterr = "out of memory";
375 			return (-1);
376 		}
377 		if (fseek(fp, symstroff, SEEK_SET) == -1) {
378 			free(strtab);
379 			fmterr = "corrupt file";
380 			return (-1);
381 		}
382 		if (fread(strtab, symstrsize, 1, fp) != 1) {
383 			free(strtab);
384 			fmterr = "corrupt file";
385 			return (-1);
386 		}
387 	}
388 
389 	if (fseek(fp, symoff, SEEK_SET) == -1) {
390 		fmterr = "corrupt file";
391 		return (-1);
392 	}
393 
394 	data.data = (u_char *)&nbuf;
395 	data.size = sizeof(NLIST);
396 
397 	/* Read each symbol and enter it into the database. */
398 	while (symsize > 0) {
399 		symsize -= sizeof(Elf_Sym);
400 		if (fread((char *)&sbuf, sizeof(sbuf), 1, fp) != 1) {
401 			if (feof(fp))
402 				fmterr = "corrupted symbol table";
403 			else
404 				warn("%s", kfile);
405 			return (-1);
406 		}
407 		if (!sbuf.st_name)
408 			continue;
409 
410 		nbuf.n_value = sbuf.st_value;
411 
412 		/* XXX type conversion is pretty rude... */
413 		switch(ELF_ST_TYPE(sbuf.st_info)) {
414 		case STT_NOTYPE:
415 			switch (sbuf.st_shndx) {
416 			case SHN_UNDEF:
417 				nbuf.n_type = N_UNDF;
418 				break;
419 			case SHN_ABS:
420 				nbuf.n_type = N_ABS;
421 				break;
422 			case SHN_COMMON:
423 				nbuf.n_type = N_COMM;
424 				break;
425 			default:
426 				nbuf.n_type = N_COMM | N_EXT;
427 				break;
428 			}
429 			break;
430 		case STT_FUNC:
431 			nbuf.n_type = N_TEXT;
432 			break;
433 		case STT_OBJECT:
434 			nbuf.n_type = N_DATA;
435 			break;
436 		case STT_FILE:
437 			nbuf.n_type = N_FN;
438 			break;
439 		}
440 		if (ELF_ST_BIND(sbuf.st_info) == STB_LOCAL)
441 			nbuf.n_type = N_EXT;
442 
443 		*buf = '_';
444 		strlcpy(buf + 1, strtab + sbuf.st_name, sizeof buf - 1);
445 		key.data = (u_char *)buf;
446 		key.size = strlen((char *)key.data);
447 		if (db->put(db, &key, &data, 0))
448 			err(1, "record enter");
449 
450 		if (strcmp((char *)key.data, VRS_SYM) == 0) {
451 			long cur_off;
452 			if (!ksyms) {
453 				/*
454 				 * Calculate offset to the version string in
455 				 * the file. kernvma is where the kernel is
456 				 * really loaded; kernoffs is where in the
457 				 * file it starts.
458 				 */
459 				long voff;
460 				voff = nbuf.n_value - kernvma + kernoffs;
461 				cur_off = ftell(fp);
462 				if (fseek(fp, voff, SEEK_SET) == -1) {
463 					fmterr = "corrupted string table";
464 					return (-1);
465 				}
466 
467 				/*
468 				 * Read version string up to, and including
469 				 * newline. This code assumes that a newline
470 				 * terminates the version line.
471 				 */
472 				if (fgets(buf, sizeof(buf), fp) == NULL) {
473 					fmterr = "corrupted string table";
474 					return (-1);
475 				}
476 			} else {
477 				/*
478 				 * This is /dev/ksyms or a look alike.
479 				 * Use sysctl() to get version since we
480 				 * don't have real text or data.
481 				 */
482 				int mib[2];
483 				size_t len;
484 				char *p;
485 
486 				mib[0] = CTL_KERN;
487 				mib[1] = KERN_VERSION;
488 				len = sizeof(buf);
489 				if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
490 					err(1, "sysctl can't find kernel "
491 					    "version string");
492 				}
493 				if ((p = strchr(buf, '\n')) != NULL)
494 					*(p+1) = '\0';
495 			}
496 
497 			key.data = (u_char *)VRS_KEY;
498 			key.size = sizeof(VRS_KEY) - 1;
499 			data.data = (u_char *)buf;
500 			data.size = strlen(buf);
501 			if (db->put(db, &key, &data, 0))
502 				err(1, "record enter");
503 
504 			/* Restore to original values. */
505 			data.data = (u_char *)&nbuf;
506 			data.size = sizeof(NLIST);
507 			if (!ksyms && fseek(fp, cur_off, SEEK_SET) == -1) {
508 				fmterr = "corrupted string table";
509 				return (-1);
510 			}
511 		}
512 	}
513 	if (usemalloc)
514 		free(strtab);
515 	else
516 		munmap(strtab, symstrsize);
517 	(void)fclose(fp);
518 	return (0);
519 }
520 #endif /* _NLIST_DO_ELF */
521 
522 #ifdef _NLIST_DO_ECOFF
523 
524 #define check(off, size)	((off < 0) || (off + size > mappedsize))
525 #define BAD			do { rv = -1; goto out; } while (0)
526 #define BADUNMAP		do { rv = -1; goto unmap; } while (0)
527 #define ECOFF_INTXT(p, e)	((p) > (e)->a.text_start && \
528 				 (p) < (e)->a.text_start + (e)->a.tsize)
529 #define ECOFF_INDAT(p, e)	((p) > (e)->a.data_start && \
530 				 (p) < (e)->a.data_start + (e)->a.dsize)
531 
532 int
533 __ecoff_knlist(int fd, DB *db, int ksyms)
534 {
535 	struct ecoff_exechdr *exechdrp;
536 	struct ecoff_symhdr *symhdrp;
537 	struct ecoff_extsym *esyms;
538 	struct stat st;
539 	char *mappedfile, *cp;
540 	size_t mappedsize;
541 	u_long symhdroff, extstroff, off;
542 	u_int symhdrsize;
543 	int rv = 0;
544 	long i, nesyms;
545 	DBT data, key;
546 	NLIST nbuf;
547 	char *sname = NULL;
548 	size_t len, snamesize = 0;
549 
550 	if (fstat(fd, &st) < 0)
551 		err(1, "can't stat %s", kfile);
552 	if (st.st_size > SIZE_T_MAX) {
553 		fmterr = "file too large";
554 		BAD;
555 	}
556 
557 	mappedsize = st.st_size;
558 	mappedfile = mmap(NULL, mappedsize, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
559 	if (mappedfile == MAP_FAILED) {
560 		fmterr = "unable to mmap";
561 		BAD;
562 	}
563 
564 	if (check(0, sizeof *exechdrp))
565 		BADUNMAP;
566 	exechdrp = (struct ecoff_exechdr *)&mappedfile[0];
567 
568 	if (ECOFF_BADMAG(exechdrp))
569 		BADUNMAP;
570 
571 	symhdroff = exechdrp->f.f_symptr;
572 	symhdrsize = exechdrp->f.f_nsyms;
573 	if (symhdrsize == 0) {
574 		fmterr = "stripped";
575 		return (-1);
576 	}
577 
578 	if (check(symhdroff, sizeof *symhdrp) ||
579 	    sizeof *symhdrp != symhdrsize)
580 		BADUNMAP;
581 	symhdrp = (struct ecoff_symhdr *)&mappedfile[symhdroff];
582 
583 	nesyms = symhdrp->esymMax;
584 	if (check(symhdrp->cbExtOffset, nesyms * sizeof *esyms))
585 		BADUNMAP;
586 	esyms = (struct ecoff_extsym *)&mappedfile[symhdrp->cbExtOffset];
587 	extstroff = symhdrp->cbSsExtOffset;
588 
589 	data.data = (u_char *)&nbuf;
590 	data.size = sizeof(NLIST);
591 
592 	for (sname = NULL, i = 0; i < nesyms; i++) {
593 		/* Need to prepend a '_' */
594 		len = strlen(&mappedfile[extstroff + esyms[i].es_strindex]) + 1;
595 		if (len >= snamesize) {
596 			char *newsname;
597 			int newsnamesize = len + 1024;
598 
599 			newsname = realloc(sname, newsnamesize);
600 			if (newsname == NULL) {
601 				if (sname)
602 					free(sname);
603 				sname = NULL;
604 			} else {
605 				sname = newsname;
606 				snamesize = newsnamesize;
607 			}
608 		}
609 		if (sname == NULL)
610 			errx(1, "cannot allocate memory");
611 		*sname = '_';
612 		strlcpy(sname+1, &mappedfile[extstroff + esyms[i].es_strindex],
613 		    snamesize - 1);
614 
615 		/* Fill in NLIST */
616 		bzero(&nbuf, sizeof(nbuf));
617 		nbuf.n_value = esyms[i].es_value;
618 		nbuf.n_type = N_EXT;		/* XXX */
619 
620 		/* Store entry in db */
621 		key.data = (u_char *)sname;
622 		key.size = strlen(sname);
623 		if (db->put(db, &key, &data, 0))
624 			err(1, "record enter");
625 
626 		if (strcmp(sname, VRS_SYM) == 0) {
627 			key.data = (u_char *)VRS_KEY;
628 			key.size = sizeof(VRS_KEY) - 1;
629 
630 			/* Version string may be in either text or data segs */
631 			if (ECOFF_INTXT(nbuf.n_value, exechdrp))
632 				off = nbuf.n_value - exechdrp->a.text_start +
633 				    ECOFF_TXTOFF(exechdrp);
634 			else if (ECOFF_INDAT(nbuf.n_value, exechdrp))
635 				off = nbuf.n_value - exechdrp->a.data_start +
636 				    ECOFF_DATOFF(exechdrp);
637 			else
638 				err(1, "unable to find version string");
639 
640 			/* Version string should end in newline but... */
641 			data.data = &mappedfile[off];
642 			if ((cp = strchr(data.data, '\n')) != NULL)
643 				data.size = cp - (char *)data.data;
644 			else
645 				data.size = strlen((char *)data.data);
646 
647 			if (db->put(db, &key, &data, 0))
648 				err(1, "record enter");
649 
650 			/* Restore to original values */
651 			data.data = (u_char *)&nbuf;
652 			data.size = sizeof(nbuf);
653 		}
654 	}
655 
656 unmap:
657 	munmap(mappedfile, mappedsize);
658 out:
659 	return (rv);
660 }
661 #endif /* _NLIST_DO_ECOFF */
662 
663 static struct knlist_handlers {
664 	int	(*fn)(int fd, DB *db, int ksyms);
665 } nlist_fn[] = {
666 #ifdef _NLIST_DO_AOUT
667 	{ __aout_knlist },
668 #endif
669 #ifdef _NLIST_DO_ELF
670 	{ __elf_knlist },
671 #endif
672 #ifdef _NLIST_DO_ECOFF
673 	{ __ecoff_knlist },
674 #endif
675 };
676 
677 int
678 create_knlist(char *name, int fd, DB *db)
679 {
680 	int i, error, ksyms;
681 
682 	if (strcmp(name, _PATH_KSYMS) == 0) {
683 		ksyms = 1;
684 	} else {
685 		ksyms = 0;
686 	}
687 
688 	for (i = 0; i < sizeof(nlist_fn)/sizeof(nlist_fn[0]); i++) {
689 		fmterr = NULL;
690 		kfile = name;
691 		/* rval of 1 means wrong executable type */
692 		if ((error = (nlist_fn[i].fn)(fd, db, ksyms)) != 1)
693 			break;
694 	}
695 	if (fmterr != NULL)
696 		warnx("%s: %s: %s", kfile, fmterr, strerror(EFTYPE));
697 
698 	return(error);
699 }
700