1 /* $OpenBSD: nlist.c,v 1.53 2019/06/28 13:32:48 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 #include <sys/types.h>
33 #include <sys/mman.h>
34 #include <sys/sysctl.h>
35
36 #include <db.h>
37 #include <elf.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <kvm.h>
42 #include <limits.h>
43 #include <paths.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48
49 #include "extern.h"
50
51 typedef struct nlist NLIST;
52 #define _strx n_un.n_strx
53 #define _name n_un.n_name
54
55 static char *kfile;
56 static char *fmterr;
57
58 int __elf_knlist(int fd, DB *db, int ksyms);
59
60 int
__elf_knlist(int fd,DB * db,int ksyms)61 __elf_knlist(int fd, DB *db, int ksyms)
62 {
63 caddr_t strtab = NULL;
64 off_t symstroff, symoff;
65 u_long symsize, symstrsize;
66 u_long kernvma, kernoffs;
67 int i, error = 0;
68 Elf32_Word j;
69 Elf_Sym sbuf;
70 char buf[1024];
71 Elf_Ehdr eh;
72 Elf_Shdr *sh = NULL;
73 DBT data, key;
74 NLIST nbuf;
75 FILE *fp;
76 int usemalloc = 0;
77
78 if ((fp = fdopen(fd, "r")) == NULL)
79 err(1, "%s", kfile);
80
81 if (fseek(fp, (off_t)0, SEEK_SET) == -1 ||
82 fread(&eh, sizeof(eh), 1, fp) != 1 ||
83 !IS_ELF(eh)) {
84 fclose(fp);
85 return (1);
86 }
87
88 sh = calloc(sizeof(Elf_Shdr), eh.e_shnum);
89 if (sh == NULL)
90 errx(1, "cannot allocate %zu bytes for symbol header",
91 sizeof(Elf_Shdr) * eh.e_shnum);
92
93 if (fseek(fp, eh.e_shoff, SEEK_SET) == -1) {
94 fmterr = "no exec header";
95 error = -1;
96 goto done;
97 }
98
99 if (fread(sh, sizeof(Elf_Shdr) * eh.e_shnum, 1, fp) != 1) {
100 fmterr = "no exec header";
101 error = -1;
102 goto done;
103 }
104
105 symstrsize = symsize = 0;
106 kernvma = (u_long)-1; /* 0 is a valid value (at least on hp300) */
107 for (i = 0; i < eh.e_shnum; i++) {
108 if (sh[i].sh_type == SHT_STRTAB) {
109 for (j = 0; j < eh.e_shnum; j++)
110 if (sh[j].sh_type == SHT_SYMTAB &&
111 sh[j].sh_link == (unsigned)i) {
112 symstroff = sh[i].sh_offset;
113 symstrsize = sh[i].sh_size;
114 }
115 } else if (sh[i].sh_type == SHT_SYMTAB) {
116 symoff = sh[i].sh_offset;
117 symsize = sh[i].sh_size;
118 } else if (sh[i].sh_type == SHT_PROGBITS &&
119 (sh[i].sh_flags & SHF_EXECINSTR)) {
120 kernvma = sh[i].sh_addr;
121 kernoffs = sh[i].sh_offset;
122 }
123 }
124
125 if (symstrsize == 0 || symsize == 0 || kernvma == (u_long)-1) {
126 fmterr = "corrupt file";
127 error = -1;
128 goto done;
129 }
130
131 /*
132 * Map string table into our address space. This gives us
133 * an easy way to randomly access all the strings, without
134 * making the memory allocation permanent as with malloc/free
135 * (i.e., munmap will return it to the system).
136 *
137 * XXX - we really want to check if this is a regular file.
138 * then we probably want a MAP_PRIVATE here.
139 */
140 strtab = mmap(NULL, (size_t)symstrsize, PROT_READ,
141 MAP_SHARED|MAP_FILE, fileno(fp), symstroff);
142 if (strtab == MAP_FAILED) {
143 usemalloc = 1;
144 if ((strtab = malloc(symstrsize)) == NULL) {
145 fmterr = "out of memory";
146 error = -1;
147 goto done;
148 }
149 if (fseek(fp, symstroff, SEEK_SET) == -1) {
150 fmterr = "corrupt file";
151 error = -1;
152 goto done;
153 }
154 if (fread(strtab, symstrsize, 1, fp) != 1) {
155 fmterr = "corrupt file";
156 error = -1;
157 goto done;
158 }
159 }
160
161 if (fseek(fp, symoff, SEEK_SET) == -1) {
162 fmterr = "corrupt file";
163 error = -1;
164 goto done;
165 }
166
167 data.data = (u_char *)&nbuf;
168 data.size = sizeof(NLIST);
169
170 /* Read each symbol and enter it into the database. */
171 while (symsize > 0) {
172 symsize -= sizeof(Elf_Sym);
173 if (fread((char *)&sbuf, sizeof(sbuf), 1, fp) != 1) {
174 if (feof(fp))
175 fmterr = "corrupted symbol table";
176 else
177 warn("%s", kfile);
178 error = -1;
179 goto done;
180 }
181 if (!sbuf.st_name)
182 continue;
183
184 nbuf.n_value = sbuf.st_value;
185
186 /* XXX type conversion is pretty rude... */
187 switch(ELF_ST_TYPE(sbuf.st_info)) {
188 case STT_NOTYPE:
189 switch (sbuf.st_shndx) {
190 case SHN_UNDEF:
191 nbuf.n_type = N_UNDF;
192 break;
193 case SHN_ABS:
194 nbuf.n_type = N_ABS;
195 break;
196 case SHN_COMMON:
197 nbuf.n_type = N_COMM;
198 break;
199 default:
200 nbuf.n_type = N_COMM | N_EXT;
201 break;
202 }
203 break;
204 case STT_FUNC:
205 nbuf.n_type = N_TEXT;
206 break;
207 case STT_OBJECT:
208 nbuf.n_type = N_DATA;
209 break;
210 case STT_FILE:
211 nbuf.n_type = N_FN;
212 break;
213 }
214 if (ELF_ST_BIND(sbuf.st_info) == STB_LOCAL)
215 nbuf.n_type = N_EXT;
216
217 *buf = '_';
218 strlcpy(buf + 1, strtab + sbuf.st_name, sizeof buf - 1);
219 key.data = (u_char *)buf;
220 key.size = strlen((char *)key.data);
221 if (db->put(db, &key, &data, 0))
222 err(1, "record enter");
223
224 if (strcmp((char *)key.data, VRS_SYM) == 0) {
225 long cur_off;
226 if (!ksyms) {
227 /*
228 * Calculate offset to the version string in
229 * the file. kernvma is where the kernel is
230 * really loaded; kernoffs is where in the
231 * file it starts.
232 */
233 long voff;
234 voff = nbuf.n_value - kernvma + kernoffs;
235 cur_off = ftell(fp);
236 if (fseek(fp, voff, SEEK_SET) == -1) {
237 fmterr = "corrupted string table";
238 error = -1;
239 goto done;
240 }
241
242 /*
243 * Read version string up to, and including
244 * newline. This code assumes that a newline
245 * terminates the version line.
246 */
247 if (fgets(buf, sizeof(buf), fp) == NULL) {
248 fmterr = "corrupted string table";
249 error = -1;
250 goto done;
251 }
252 } else {
253 /*
254 * This is /dev/ksyms or a look alike.
255 * Use sysctl() to get version since we
256 * don't have real text or data.
257 */
258 int mib[2];
259 size_t len;
260 char *p;
261
262 mib[0] = CTL_KERN;
263 mib[1] = KERN_VERSION;
264 len = sizeof(buf);
265 if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
266 err(1, "sysctl can't find kernel "
267 "version string");
268 }
269 if ((p = strchr(buf, '\n')) != NULL)
270 *(p+1) = '\0';
271 }
272
273 key.data = (u_char *)VRS_KEY;
274 key.size = sizeof(VRS_KEY) - 1;
275 data.data = (u_char *)buf;
276 data.size = strlen(buf);
277 if (db->put(db, &key, &data, 0))
278 err(1, "record enter");
279
280 /* Restore to original values. */
281 data.data = (u_char *)&nbuf;
282 data.size = sizeof(NLIST);
283 if (!ksyms && fseek(fp, cur_off, SEEK_SET) == -1) {
284 fmterr = "corrupted string table";
285 error = -1;
286 goto done;
287 }
288 }
289 }
290 done:
291 if (strtab) {
292 if (usemalloc)
293 free(strtab);
294 else
295 munmap(strtab, symstrsize);
296 }
297 (void)fclose(fp);
298 free(sh);
299 return (error);
300 }
301
302 int
create_knlist(char * name,int fd,DB * db)303 create_knlist(char *name, int fd, DB *db)
304 {
305 int error, ksyms;
306
307 if (strcmp(name, _PATH_KSYMS) == 0) {
308 ksyms = 1;
309 } else {
310 ksyms = 0;
311 }
312
313 fmterr = NULL;
314 kfile = name;
315 /* rval of 1 means wrong executable type */
316 error = __elf_knlist(fd, db, ksyms);
317
318 if (fmterr != NULL)
319 warnc(EFTYPE, "%s: %s", kfile, fmterr);
320
321 return(error);
322 }
323