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