1 /* $OpenBSD: nlist.c,v 1.72 2022/12/27 17:10:06 jmc Exp $ */
2 /*
3 * Copyright (c) 1989, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/types.h>
32 #include <sys/mman.h>
33 #include <sys/stat.h>
34
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <a.out.h> /* pulls in nlist.h */
43 #include <elf.h>
44
45 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
46
47 int __fdnlist(int, struct nlist *);
48 PROTO_NORMAL(__fdnlist);
49
50 #define ISLAST(p) (p->n_un.n_name == 0 || p->n_un.n_name[0] == 0)
51
52 /*
53 * __elf_is_okay__ - Determine if ehdr really
54 * is ELF and valid for the target platform.
55 *
56 * WARNING: This is NOT a ELF ABI function and
57 * as such its use should be restricted.
58 */
59 static int
__elf_is_okay__(Elf_Ehdr * ehdr)60 __elf_is_okay__(Elf_Ehdr *ehdr)
61 {
62 int retval = 0;
63 /*
64 * We need to check magic, class size, endianness,
65 * and version before we look at the rest of the
66 * Elf_Ehdr structure. These few elements are
67 * represented in a machine independent fashion.
68 */
69 if (IS_ELF(*ehdr) &&
70 ehdr->e_ident[EI_CLASS] == ELF_TARG_CLASS &&
71 ehdr->e_ident[EI_DATA] == ELF_TARG_DATA &&
72 ehdr->e_ident[EI_VERSION] == ELF_TARG_VER) {
73
74 /* Now check the machine dependent header */
75 if (ehdr->e_machine == ELF_TARG_MACH &&
76 ehdr->e_version == ELF_TARG_VER)
77 retval = 1;
78 }
79
80 if (ehdr->e_shentsize != sizeof(Elf_Shdr))
81 return 0;
82
83 return retval;
84 }
85
86 int
__fdnlist(int fd,struct nlist * list)87 __fdnlist(int fd, struct nlist *list)
88 {
89 struct nlist *p;
90 caddr_t strtab;
91 Elf_Off symoff = 0, symstroff = 0;
92 Elf_Word symsize = 0, symstrsize = 0;
93 Elf_Sword nent, cc, i;
94 Elf_Sym sbuf[1024];
95 Elf_Sym *s;
96 Elf_Ehdr ehdr;
97 Elf_Shdr *shdr = NULL;
98 Elf_Word shdr_size;
99 struct stat st;
100 int usemalloc = 0;
101 size_t left, len;
102
103 /* Make sure obj is OK */
104 if (pread(fd, &ehdr, sizeof(Elf_Ehdr), 0) != sizeof(Elf_Ehdr) ||
105 !__elf_is_okay__(&ehdr) || fstat(fd, &st) == -1)
106 return (-1);
107
108 /* calculate section header table size */
109 shdr_size = ehdr.e_shentsize * ehdr.e_shnum;
110
111 /* Make sure it's not too big to mmap */
112 if (SIZE_MAX - ehdr.e_shoff < shdr_size ||
113 (S_ISREG(st.st_mode) && ehdr.e_shoff + shdr_size > st.st_size)) {
114 errno = EFBIG;
115 return (-1);
116 }
117
118 /* mmap section header table */
119 shdr = mmap(NULL, shdr_size, PROT_READ, MAP_SHARED|MAP_FILE, fd,
120 ehdr.e_shoff);
121 if (shdr == MAP_FAILED) {
122 usemalloc = 1;
123 if ((shdr = malloc(shdr_size)) == NULL)
124 return (-1);
125
126 if (pread(fd, shdr, shdr_size, ehdr.e_shoff) != shdr_size) {
127 free(shdr);
128 return (-1);
129 }
130 }
131
132 /*
133 * Find the symbol table entry and its corresponding
134 * string table entry. Version 1.1 of the ABI states
135 * that there is only one symbol table but that this
136 * could change in the future.
137 */
138 for (i = 0; i < ehdr.e_shnum; i++) {
139 if (shdr[i].sh_type == SHT_SYMTAB) {
140 if (shdr[i].sh_link >= ehdr.e_shnum)
141 continue;
142 symoff = shdr[i].sh_offset;
143 symsize = shdr[i].sh_size;
144 symstroff = shdr[shdr[i].sh_link].sh_offset;
145 symstrsize = shdr[shdr[i].sh_link].sh_size;
146 break;
147 }
148 }
149
150 /* Flush the section header table */
151 if (usemalloc)
152 free(shdr);
153 else
154 munmap((caddr_t)shdr, shdr_size);
155
156 /*
157 * clean out any left-over information for all valid entries.
158 * Type and value defined to be 0 if not found; historical
159 * versions cleared other and desc as well. Also figure out
160 * the largest string length so don't read any more of the
161 * string table than we have to.
162 *
163 * XXX clearing anything other than n_type and n_value violates
164 * the semantics given in the man page.
165 */
166 nent = 0;
167 for (p = list; !ISLAST(p); ++p) {
168 p->n_type = 0;
169 p->n_other = 0;
170 p->n_desc = 0;
171 p->n_value = 0;
172 ++nent;
173 }
174
175 /* Don't process any further if object is stripped. */
176 /* ELFism - dunno if stripped by looking at header */
177 if (symoff == 0)
178 return nent;
179
180 /* Check for files too large to mmap. */
181 if (SIZE_MAX - symstrsize < symstroff ||
182 (S_ISREG(st.st_mode) && symstrsize + symstroff > st.st_size)) {
183 errno = EFBIG;
184 return (-1);
185 }
186
187 /*
188 * Map string table into our address space. This gives us
189 * an easy way to randomly access all the strings, without
190 * making the memory allocation permanent as with malloc/free
191 * (i.e., munmap will return it to the system).
192 */
193 if (usemalloc) {
194 if ((strtab = malloc(symstrsize)) == NULL)
195 return (-1);
196 if (pread(fd, strtab, symstrsize, symstroff) != symstrsize) {
197 free(strtab);
198 return (-1);
199 }
200 } else {
201 strtab = mmap(NULL, symstrsize, PROT_READ, MAP_SHARED|MAP_FILE,
202 fd, symstroff);
203 if (strtab == MAP_FAILED)
204 return (-1);
205 }
206
207 while (symsize >= sizeof(Elf_Sym)) {
208 cc = MINIMUM(symsize, sizeof(sbuf));
209 if (pread(fd, sbuf, cc, symoff) != cc)
210 break;
211 symsize -= cc;
212 symoff += cc;
213 for (s = sbuf; cc > 0; ++s, cc -= sizeof(*s)) {
214 Elf_Word soff = s->st_name;
215
216 if (soff == 0 || soff >= symstrsize)
217 continue;
218 left = symstrsize - soff;
219
220 for (p = list; !ISLAST(p); p++) {
221 char *sym;
222
223 /*
224 * First we check for the symbol as it was
225 * provided by the user. If that fails
226 * and the first char is an '_', skip over
227 * the '_' and try again.
228 * XXX - What do we do when the user really
229 * wants '_foo' and there are symbols
230 * for both 'foo' and '_foo' in the
231 * table and 'foo' is first?
232 */
233 sym = p->n_un.n_name;
234 len = strlen(sym);
235
236 if ((len >= left ||
237 strcmp(&strtab[soff], sym) != 0) &&
238 (sym[0] != '_' || len - 1 >= left ||
239 strcmp(&strtab[soff], sym + 1) != 0))
240 continue;
241
242 p->n_value = s->st_value;
243
244 /* XXX - type conversion */
245 /* is pretty rude. */
246 switch(ELF_ST_TYPE(s->st_info)) {
247 case STT_NOTYPE:
248 switch (s->st_shndx) {
249 case SHN_UNDEF:
250 p->n_type = N_UNDF;
251 break;
252 case SHN_ABS:
253 p->n_type = N_ABS;
254 break;
255 case SHN_COMMON:
256 p->n_type = N_COMM;
257 break;
258 default:
259 p->n_type = N_COMM | N_EXT;
260 break;
261 }
262 break;
263 case STT_OBJECT:
264 p->n_type = N_DATA;
265 break;
266 case STT_FUNC:
267 p->n_type = N_TEXT;
268 break;
269 case STT_FILE:
270 p->n_type = N_FN;
271 break;
272 }
273 if (ELF_ST_BIND(s->st_info) == STB_LOCAL)
274 p->n_type = N_EXT;
275 p->n_desc = 0;
276 p->n_other = 0;
277 if (--nent <= 0)
278 break;
279 }
280 }
281 }
282 if (usemalloc)
283 free(strtab);
284 else
285 munmap(strtab, symstrsize);
286 return (nent);
287 }
288 DEF_STRONG(__fdnlist);
289
290 int
nlist(const char * name,struct nlist * list)291 nlist(const char *name, struct nlist *list)
292 {
293 int fd, n;
294
295 fd = open(name, O_RDONLY | O_CLOEXEC);
296 if (fd == -1)
297 return (-1);
298 n = __fdnlist(fd, list);
299 (void)close(fd);
300 return (n);
301 }
302