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