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