xref: /openbsd-src/usr.sbin/kvm_mkdb/nlist.c (revision df69c215c7c66baf660f3f65414fd34796c96152)
1*df69c215Sderaadt /*	$OpenBSD: nlist.c,v 1.53 2019/06/28 13:32:48 deraadt Exp $	*/
22d682701Smillert 
3df930be7Sderaadt /*-
4df930be7Sderaadt  * Copyright (c) 1990, 1993
5df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
6df930be7Sderaadt  *
7df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
8df930be7Sderaadt  * modification, are permitted provided that the following conditions
9df930be7Sderaadt  * are met:
10df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
11df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
12df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
13df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
14df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
1529295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
16df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
17df930be7Sderaadt  *    without specific prior written permission.
18df930be7Sderaadt  *
19df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29df930be7Sderaadt  * SUCH DAMAGE.
30df930be7Sderaadt  */
31df930be7Sderaadt 
32b9fc9a72Sderaadt #include <sys/types.h>
33f4147939Sguenther #include <sys/mman.h>
34f4147939Sguenther #include <sys/sysctl.h>
35df930be7Sderaadt 
36df930be7Sderaadt #include <db.h>
37f4147939Sguenther #include <elf.h>
38df930be7Sderaadt #include <err.h>
39df930be7Sderaadt #include <errno.h>
40df930be7Sderaadt #include <fcntl.h>
41df930be7Sderaadt #include <kvm.h>
42df930be7Sderaadt #include <limits.h>
43b7fcd3d4Spefo #include <paths.h>
44df930be7Sderaadt #include <stdio.h>
45df930be7Sderaadt #include <stdlib.h>
46df930be7Sderaadt #include <string.h>
47df930be7Sderaadt #include <unistd.h>
48df930be7Sderaadt 
49df930be7Sderaadt #include "extern.h"
50df930be7Sderaadt 
51df930be7Sderaadt typedef struct nlist NLIST;
52df930be7Sderaadt #define	_strx	n_un.n_strx
53df930be7Sderaadt #define	_name	n_un.n_name
54df930be7Sderaadt 
55df930be7Sderaadt static char *kfile;
56c59ab75fSmillert static char *fmterr;
57df930be7Sderaadt 
58973e73afSderaadt int	__elf_knlist(int fd, DB *db, int ksyms);
59973e73afSderaadt 
60bc468043Spefo int
__elf_knlist(int fd,DB * db,int ksyms)6100da5b9dSderaadt __elf_knlist(int fd, DB *db, int ksyms)
62bc468043Spefo {
6324945c80Sderaadt 	caddr_t strtab = NULL;
640ac0d02eSmpech 	off_t symstroff, symoff;
65dcd2bb36Smickey 	u_long symsize, symstrsize;
660ac0d02eSmpech 	u_long kernvma, kernoffs;
67c23bb812Sderaadt 	int i, error = 0;
68c23bb812Sderaadt 	Elf32_Word j;
69200f9bbbSart 	Elf_Sym sbuf;
70dcd2bb36Smickey 	char buf[1024];
71200f9bbbSart 	Elf_Ehdr eh;
72200f9bbbSart 	Elf_Shdr *sh = NULL;
73bc468043Spefo 	DBT data, key;
74bc468043Spefo 	NLIST nbuf;
75bc468043Spefo 	FILE *fp;
76e1c41ebcSart 	int usemalloc = 0;
77bc468043Spefo 
7823af6843Sderaadt 	if ((fp = fdopen(fd, "r")) == NULL)
79da2f7a99Smillert 		err(1, "%s", kfile);
80bc468043Spefo 
81bc468043Spefo 	if (fseek(fp, (off_t)0, SEEK_SET) == -1 ||
82bc468043Spefo 	    fread(&eh, sizeof(eh), 1, fp) != 1 ||
834253757bSjsg 	    !IS_ELF(eh)) {
844253757bSjsg 		fclose(fp);
8532131dd8Smillert 		return (1);
864253757bSjsg 	}
87bc468043Spefo 
8835de856eSderaadt 	sh = calloc(sizeof(Elf_Shdr), eh.e_shnum);
89c59ab75fSmillert 	if (sh == NULL)
9082ff3630Sguenther 		errx(1, "cannot allocate %zu bytes for symbol header",
91200f9bbbSart 		    sizeof(Elf_Shdr) * eh.e_shnum);
92bc468043Spefo 
93*df69c215Sderaadt 	if (fseek(fp, eh.e_shoff, SEEK_SET) == -1) {
9432131dd8Smillert 		fmterr = "no exec header";
9524945c80Sderaadt 		error = -1;
9624945c80Sderaadt 		goto done;
9732131dd8Smillert 	}
98bc468043Spefo 
99200f9bbbSart 	if (fread(sh, sizeof(Elf_Shdr) * eh.e_shnum, 1, fp) != 1) {
10032131dd8Smillert 		fmterr = "no exec header";
10124945c80Sderaadt 		error = -1;
10224945c80Sderaadt 		goto done;
10332131dd8Smillert 	}
104bc468043Spefo 
105106d1525Smiod 	symstrsize = symsize = 0;
106106d1525Smiod 	kernvma = (u_long)-1;	/* 0 is a valid value (at least on hp300) */
107bc468043Spefo 	for (i = 0; i < eh.e_shnum; i++) {
108dcd2bb36Smickey 		if (sh[i].sh_type == SHT_STRTAB) {
109dcd2bb36Smickey 			for (j = 0; j < eh.e_shnum; j++)
110dcd2bb36Smickey 				if (sh[j].sh_type == SHT_SYMTAB &&
111dcd2bb36Smickey 				    sh[j].sh_link == (unsigned)i) {
112bc468043Spefo 					symstroff = sh[i].sh_offset;
113bc468043Spefo 					symstrsize = sh[i].sh_size;
114bc468043Spefo 			}
115dcd2bb36Smickey 		} else if (sh[i].sh_type == SHT_SYMTAB) {
116bc468043Spefo 			symoff = sh[i].sh_offset;
117bc468043Spefo 			symsize = sh[i].sh_size;
118dcd2bb36Smickey 		} else if (sh[i].sh_type == SHT_PROGBITS &&
119dcd2bb36Smickey 		    (sh[i].sh_flags & SHF_EXECINSTR)) {
120bc468043Spefo 			kernvma = sh[i].sh_addr;
121bc468043Spefo 			kernoffs = sh[i].sh_offset;
122bc468043Spefo 		}
123bc468043Spefo 	}
124bc468043Spefo 
125106d1525Smiod 	if (symstrsize == 0 || symsize == 0 || kernvma == (u_long)-1) {
12632131dd8Smillert 		fmterr = "corrupt file";
12724945c80Sderaadt 		error = -1;
12824945c80Sderaadt 		goto done;
129bc468043Spefo 	}
130dcd2bb36Smickey 
131bc468043Spefo 	/*
132bc468043Spefo 	 * Map string table into our address space.  This gives us
133bc468043Spefo 	 * an easy way to randomly access all the strings, without
134bc468043Spefo 	 * making the memory allocation permanent as with malloc/free
135bc468043Spefo 	 * (i.e., munmap will return it to the system).
136e1c41ebcSart 	 *
137e1c41ebcSart 	 * XXX - we really want to check if this is a regular file.
138e1c41ebcSart 	 *	 then we probably want a MAP_PRIVATE here.
139bc468043Spefo 	 */
140c13e57aaSderaadt 	strtab = mmap(NULL, (size_t)symstrsize, PROT_READ,
141e1c41ebcSart 	    MAP_SHARED|MAP_FILE, fileno(fp), symstroff);
142e1c41ebcSart 	if (strtab == MAP_FAILED) {
143e1c41ebcSart 		usemalloc = 1;
144e1c41ebcSart 		if ((strtab = malloc(symstrsize)) == NULL) {
145e1c41ebcSart 			fmterr = "out of memory";
14624945c80Sderaadt 			error = -1;
14724945c80Sderaadt 			goto done;
148e1c41ebcSart 		}
149e1c41ebcSart 		if (fseek(fp, symstroff, SEEK_SET) == -1) {
15032131dd8Smillert 			fmterr = "corrupt file";
15124945c80Sderaadt 			error = -1;
15224945c80Sderaadt 			goto done;
15332131dd8Smillert 		}
154e1c41ebcSart 		if (fread(strtab, symstrsize, 1, fp) != 1) {
155e1c41ebcSart 			fmterr = "corrupt file";
15624945c80Sderaadt 			error = -1;
15724945c80Sderaadt 			goto done;
158e1c41ebcSart 		}
159e1c41ebcSart 	}
160bc468043Spefo 
16132131dd8Smillert 	if (fseek(fp, symoff, SEEK_SET) == -1) {
16232131dd8Smillert 		fmterr = "corrupt file";
16324945c80Sderaadt 		error = -1;
16424945c80Sderaadt 		goto done;
16532131dd8Smillert 	}
166bc468043Spefo 
167bc468043Spefo 	data.data = (u_char *)&nbuf;
168bc468043Spefo 	data.size = sizeof(NLIST);
169bc468043Spefo 
170bc468043Spefo 	/* Read each symbol and enter it into the database. */
171bc468043Spefo 	while (symsize > 0) {
172200f9bbbSart 		symsize -= sizeof(Elf_Sym);
173bc468043Spefo 		if (fread((char *)&sbuf, sizeof(sbuf), 1, fp) != 1) {
174bc468043Spefo 			if (feof(fp))
17532131dd8Smillert 				fmterr = "corrupted symbol table";
17632131dd8Smillert 			else
177b638aa94Smillert 				warn("%s", kfile);
17824945c80Sderaadt 			error = -1;
17924945c80Sderaadt 			goto done;
180bc468043Spefo 		}
181bc468043Spefo 		if (!sbuf.st_name)
182bc468043Spefo 			continue;
183bc468043Spefo 
184bc468043Spefo 		nbuf.n_value = sbuf.st_value;
185bc468043Spefo 
186bc468043Spefo 		/* XXX type conversion is pretty rude... */
187200f9bbbSart 		switch(ELF_ST_TYPE(sbuf.st_info)) {
188bc468043Spefo 		case STT_NOTYPE:
18989290fbfSmickey 			switch (sbuf.st_shndx) {
19089290fbfSmickey 			case SHN_UNDEF:
191bc468043Spefo 				nbuf.n_type = N_UNDF;
192bc468043Spefo 				break;
19389290fbfSmickey 			case SHN_ABS:
19489290fbfSmickey 				nbuf.n_type = N_ABS;
19589290fbfSmickey 				break;
19689290fbfSmickey 			case SHN_COMMON:
19789290fbfSmickey 				nbuf.n_type = N_COMM;
19889290fbfSmickey 				break;
19989290fbfSmickey 			default:
20089290fbfSmickey 				nbuf.n_type = N_COMM | N_EXT;
20189290fbfSmickey 				break;
20289290fbfSmickey 			}
20389290fbfSmickey 			break;
204bc468043Spefo 		case STT_FUNC:
205bc468043Spefo 			nbuf.n_type = N_TEXT;
206bc468043Spefo 			break;
207bc468043Spefo 		case STT_OBJECT:
208bc468043Spefo 			nbuf.n_type = N_DATA;
209bc468043Spefo 			break;
21089290fbfSmickey 		case STT_FILE:
21189290fbfSmickey 			nbuf.n_type = N_FN;
21289290fbfSmickey 			break;
213bc468043Spefo 		}
214200f9bbbSart 		if (ELF_ST_BIND(sbuf.st_info) == STB_LOCAL)
215bc468043Spefo 			nbuf.n_type = N_EXT;
216bc468043Spefo 
217bc468043Spefo 		*buf = '_';
2187f8fa835Sderaadt 		strlcpy(buf + 1, strtab + sbuf.st_name, sizeof buf - 1);
219bc468043Spefo 		key.data = (u_char *)buf;
220bc468043Spefo 		key.size = strlen((char *)key.data);
221bc468043Spefo 		if (db->put(db, &key, &data, 0))
222bc468043Spefo 			err(1, "record enter");
223bc468043Spefo 
224bc468043Spefo 		if (strcmp((char *)key.data, VRS_SYM) == 0) {
225b7fcd3d4Spefo 			long cur_off;
226b7fcd3d4Spefo 			if (!ksyms) {
227bc468043Spefo 				/*
228b7fcd3d4Spefo 				 * Calculate offset to the version string in
229b7fcd3d4Spefo 				 * the file. kernvma is where the kernel is
230b7fcd3d4Spefo 				 * really loaded; kernoffs is where in the
231b7fcd3d4Spefo 				 * file it starts.
232bc468043Spefo 				 */
233b7fcd3d4Spefo 				long voff;
234bc468043Spefo 				voff = nbuf.n_value - kernvma + kernoffs;
235bc468043Spefo 				cur_off = ftell(fp);
23632131dd8Smillert 				if (fseek(fp, voff, SEEK_SET) == -1) {
23732131dd8Smillert 					fmterr = "corrupted string table";
23824945c80Sderaadt 					error = -1;
23924945c80Sderaadt 					goto done;
24032131dd8Smillert 				}
241bc468043Spefo 
242bc468043Spefo 				/*
243b7fcd3d4Spefo 				 * Read version string up to, and including
244b7fcd3d4Spefo 				 * newline. This code assumes that a newline
245b7fcd3d4Spefo 				 * terminates the version line.
246bc468043Spefo 				 */
24732131dd8Smillert 				if (fgets(buf, sizeof(buf), fp) == NULL) {
24832131dd8Smillert 					fmterr = "corrupted string table";
24924945c80Sderaadt 					error = -1;
25024945c80Sderaadt 					goto done;
25132131dd8Smillert 				}
252b7fcd3d4Spefo 			} else {
253b7fcd3d4Spefo 				/*
254b7fcd3d4Spefo 				 * This is /dev/ksyms or a look alike.
255b7fcd3d4Spefo 				 * Use sysctl() to get version since we
256b7fcd3d4Spefo 				 * don't have real text or data.
257b7fcd3d4Spefo 				 */
258b7fcd3d4Spefo 				int mib[2];
259d3be0374Sderaadt 				size_t len;
260b7fcd3d4Spefo 				char *p;
261b7fcd3d4Spefo 
262b7fcd3d4Spefo 				mib[0] = CTL_KERN;
263b7fcd3d4Spefo 				mib[1] = KERN_VERSION;
264b7fcd3d4Spefo 				len = sizeof(buf);
265b7fcd3d4Spefo 				if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
266b7fcd3d4Spefo 					err(1, "sysctl can't find kernel "
267b7fcd3d4Spefo 					    "version string");
268b7fcd3d4Spefo 				}
269b7fcd3d4Spefo 				if ((p = strchr(buf, '\n')) != NULL)
270b7fcd3d4Spefo 					*(p+1) = '\0';
271b7fcd3d4Spefo 			}
272bc468043Spefo 
273bc468043Spefo 			key.data = (u_char *)VRS_KEY;
274bc468043Spefo 			key.size = sizeof(VRS_KEY) - 1;
275bc468043Spefo 			data.data = (u_char *)buf;
276bc468043Spefo 			data.size = strlen(buf);
277bc468043Spefo 			if (db->put(db, &key, &data, 0))
278bc468043Spefo 				err(1, "record enter");
279bc468043Spefo 
280bc468043Spefo 			/* Restore to original values. */
281bc468043Spefo 			data.data = (u_char *)&nbuf;
282bc468043Spefo 			data.size = sizeof(NLIST);
283b7fcd3d4Spefo 			if (!ksyms && fseek(fp, cur_off, SEEK_SET) == -1) {
28432131dd8Smillert 				fmterr = "corrupted string table";
28524945c80Sderaadt 				error = -1;
28624945c80Sderaadt 				goto done;
28732131dd8Smillert 			}
288bc468043Spefo 		}
289bc468043Spefo 	}
29024945c80Sderaadt done:
29124945c80Sderaadt 	if (strtab) {
292e1c41ebcSart 		if (usemalloc)
293e1c41ebcSart 			free(strtab);
294e1c41ebcSart 		else
295bc468043Spefo 			munmap(strtab, symstrsize);
29624945c80Sderaadt 	}
297bc468043Spefo 	(void)fclose(fp);
29824945c80Sderaadt 	free(sh);
29924945c80Sderaadt 	return (error);
300bc468043Spefo }
301bc468043Spefo 
302feed01ecSmillert int
create_knlist(char * name,int fd,DB * db)30300da5b9dSderaadt create_knlist(char *name, int fd, DB *db)
304bc468043Spefo {
305973e73afSderaadt 	int error, ksyms;
306b7fcd3d4Spefo 
307b7fcd3d4Spefo 	if (strcmp(name, _PATH_KSYMS) == 0) {
308b7fcd3d4Spefo 		ksyms = 1;
309b7fcd3d4Spefo 	} else {
310b7fcd3d4Spefo 		ksyms = 0;
311b7fcd3d4Spefo 	}
312bc468043Spefo 
313c59ab75fSmillert 	fmterr = NULL;
314da2f7a99Smillert 	kfile = name;
31532131dd8Smillert 	/* rval of 1 means wrong executable type */
316973e73afSderaadt 	error = __elf_knlist(fd, db, ksyms);
317973e73afSderaadt 
318c59ab75fSmillert 	if (fmterr != NULL)
3195ad04d35Sguenther 		warnc(EFTYPE, "%s: %s", kfile, fmterr);
320feed01ecSmillert 
321feed01ecSmillert 	return(error);
322bc468043Spefo }
323