Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libc / gen / nlist.c
1 /*
2  * Copyright (c) 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  *      $FreeBSD: src/lib/libc/gen/nlist.c,v 1.12.2.1 2001/07/11 23:59:09 obrien Exp $
34  */
35
36 #if defined(LIBC_SCCS) && !defined(lint)
37 static char sccsid[] = "@(#)nlist.c     8.1 (Berkeley) 6/4/93";
38 #endif /* LIBC_SCCS and not lint */
39
40 #include <sys/param.h>
41 #include <sys/mman.h>
42 #include <sys/stat.h>
43 #include <sys/file.h>
44
45 #include <errno.h>
46 #include <a.out.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <unistd.h>
50
51 #define _NLIST_DO_AOUT
52 #define _NLIST_DO_ELF
53
54 #ifdef _NLIST_DO_ELF
55 #include <machine/elf.h>
56 #include <elf-hints.h>
57 #endif
58
59 int __fdnlist           __P((int, struct nlist *));
60 int __aout_fdnlist      __P((int, struct nlist *));
61 int __elf_fdnlist       __P((int, struct nlist *));
62
63 int
64 nlist(name, list)
65         const char *name;
66         struct nlist *list;
67 {
68         int fd, n;
69
70         fd = _open(name, O_RDONLY, 0);
71         if (fd < 0)
72                 return (-1);
73         n = __fdnlist(fd, list);
74         (void)_close(fd);
75         return (n);
76 }
77
78 static struct nlist_handlers {
79         int     (*fn) __P((int fd, struct nlist *list));
80 } nlist_fn[] = {
81 #ifdef _NLIST_DO_AOUT
82         { __aout_fdnlist },
83 #endif
84 #ifdef _NLIST_DO_ELF
85         { __elf_fdnlist },
86 #endif
87 };
88
89 int
90 __fdnlist(fd, list)
91         register int fd;
92         register struct nlist *list;
93 {
94         int n = -1, i;
95
96         for (i = 0; i < sizeof(nlist_fn) / sizeof(nlist_fn[0]); i++) {
97                 n = (nlist_fn[i].fn)(fd, list);
98                 if (n != -1)
99                         break;
100         }
101         return (n);
102 }
103
104 #define ISLAST(p)       (p->n_un.n_name == 0 || p->n_un.n_name[0] == 0)
105
106 #ifdef _NLIST_DO_AOUT
107 int
108 __aout_fdnlist(fd, list)
109         register int fd;
110         register struct nlist *list;
111 {
112         register struct nlist *p, *symtab;
113         register caddr_t strtab, a_out_mmap;
114         register off_t stroff, symoff;
115         register u_long symsize;
116         register int nent;
117         struct exec * exec;
118         struct stat st;
119
120         /* check that file is at least as large as struct exec! */
121         if ((fstat(fd, &st) < 0) || (st.st_size < sizeof(struct exec)))
122                 return (-1);
123
124         /* Check for files too large to mmap. */
125         if (st.st_size > SIZE_T_MAX) {
126                 errno = EFBIG;
127                 return (-1);
128         }
129
130         /*
131          * Map the whole a.out file into our address space.
132          * We then find the string table withing this area.
133          * We do not just mmap the string table, as it probably
134          * does not start at a page boundary - we save ourselves a
135          * lot of nastiness by mmapping the whole file.
136          *
137          * This gives us an easy way to randomly access all the strings,
138          * without making the memory allocation permanent as with
139          * malloc/free (i.e., munmap will return it to the system).
140          */
141         a_out_mmap = mmap(NULL, (size_t)st.st_size, PROT_READ, MAP_PRIVATE, fd, (off_t)0);
142         if (a_out_mmap == MAP_FAILED)
143                 return (-1);
144
145         exec = (struct exec *)a_out_mmap;
146         if (N_BADMAG(*exec)) {
147                 munmap(a_out_mmap, (size_t)st.st_size);
148                 return (-1);
149         }
150
151         symoff = N_SYMOFF(*exec);
152         symsize = exec->a_syms;
153         stroff = symoff + symsize;
154
155         /* find the string table in our mmapped area */
156         strtab = a_out_mmap + stroff;
157         symtab = (struct nlist *)(a_out_mmap + symoff);
158
159         /*
160          * clean out any left-over information for all valid entries.
161          * Type and value defined to be 0 if not found; historical
162          * versions cleared other and desc as well.  Also figure out
163          * the largest string length so don't read any more of the
164          * string table than we have to.
165          *
166          * XXX clearing anything other than n_type and n_value violates
167          * the semantics given in the man page.
168          */
169         nent = 0;
170         for (p = list; !ISLAST(p); ++p) {
171                 p->n_type = 0;
172                 p->n_other = 0;
173                 p->n_desc = 0;
174                 p->n_value = 0;
175                 ++nent;
176         }
177
178         while (symsize > 0) {
179                 register int soff;
180
181                 symsize-= sizeof(struct nlist);
182                 soff = symtab->n_un.n_strx;
183
184
185                 if (soff != 0 && (symtab->n_type & N_STAB) == 0)
186                         for (p = list; !ISLAST(p); p++)
187                                 if (!strcmp(&strtab[soff], p->n_un.n_name)) {
188                                         p->n_value = symtab->n_value;
189                                         p->n_type = symtab->n_type;
190                                         p->n_desc = symtab->n_desc;
191                                         p->n_other = symtab->n_other;
192                                         if (--nent <= 0)
193                                                 break;
194                                 }
195                 symtab++;
196         }
197         munmap(a_out_mmap, (size_t)st.st_size);
198         return (nent);
199 }
200 #endif
201
202 #ifdef _NLIST_DO_ELF
203 static void elf_sym_to_nlist __P((struct nlist *, Elf_Sym *, Elf_Shdr *, int));
204
205 /*
206  * __elf_is_okay__ - Determine if ehdr really
207  * is ELF and valid for the target platform.
208  *
209  * WARNING:  This is NOT a ELF ABI function and
210  * as such it's use should be restricted.
211  */
212 int
213 __elf_is_okay__(ehdr)
214         register Elf_Ehdr *ehdr;
215 {
216         register int retval = 0;
217         /*
218          * We need to check magic, class size, endianess,
219          * and version before we look at the rest of the
220          * Elf_Ehdr structure.  These few elements are
221          * represented in a machine independant fashion.
222          */
223         if (IS_ELF(*ehdr) &&
224             ehdr->e_ident[EI_CLASS] == ELF_TARG_CLASS &&
225             ehdr->e_ident[EI_DATA] == ELF_TARG_DATA &&
226             ehdr->e_ident[EI_VERSION] == ELF_TARG_VER) {
227
228                 /* Now check the machine dependant header */
229                 if (ehdr->e_machine == ELF_TARG_MACH &&
230                     ehdr->e_version == ELF_TARG_VER)
231                         retval = 1;
232         }
233         return retval;
234 }
235
236 int
237 __elf_fdnlist(fd, list)
238         register int fd;
239         register struct nlist *list;
240 {
241         register struct nlist *p;
242         register Elf_Off symoff = 0, symstroff = 0;
243         register Elf_Word symsize = 0, symstrsize = 0;
244         register Elf_Sword cc, i;
245         int nent = -1;
246         int errsave;
247         Elf_Sym sbuf[1024];
248         Elf_Sym *s;
249         Elf_Ehdr ehdr;
250         char *strtab = NULL;
251         Elf_Shdr *shdr = NULL;
252         Elf_Shdr *sh;
253         Elf_Word shdr_size;
254         void *base;
255         struct stat st;
256
257         /* Make sure obj is OK */
258         if (lseek(fd, (off_t)0, SEEK_SET) == -1 ||
259             _read(fd, &ehdr, sizeof(Elf_Ehdr)) != sizeof(Elf_Ehdr) ||
260             !__elf_is_okay__(&ehdr) ||
261             fstat(fd, &st) < 0)
262                 return (-1);
263
264         /* calculate section header table size */
265         shdr_size = ehdr.e_shentsize * ehdr.e_shnum;
266
267         /* Make sure it's not too big to mmap */
268         if (shdr_size > SIZE_T_MAX) {
269                 errno = EFBIG;
270                 return (-1);
271         }
272
273         /* mmap section header table */
274         base = mmap(NULL, (size_t)shdr_size, PROT_READ, 0, fd,
275             (off_t)ehdr.e_shoff);
276         if (base == MAP_FAILED)
277                 return (-1);
278         shdr = (Elf_Shdr *)base;
279
280         /*
281          * Find the symbol table entry and it's corresponding
282          * string table entry.  Version 1.1 of the ABI states
283          * that there is only one symbol table but that this
284          * could change in the future.
285          */
286         for (i = 0; i < ehdr.e_shnum; i++) {
287                 if (shdr[i].sh_type == SHT_SYMTAB) {
288                         symoff = shdr[i].sh_offset;
289                         symsize = shdr[i].sh_size;
290                         symstroff = shdr[shdr[i].sh_link].sh_offset;
291                         symstrsize = shdr[shdr[i].sh_link].sh_size;
292                         break;
293                 }
294         }
295
296         /* Check for files too large to mmap. */
297         if (symstrsize > SIZE_T_MAX) {
298                 errno = EFBIG;
299                 goto done;
300         }
301         /*
302          * Map string table into our address space.  This gives us
303          * an easy way to randomly access all the strings, without
304          * making the memory allocation permanent as with malloc/free
305          * (i.e., munmap will return it to the system).
306          */
307         base = mmap(NULL, (size_t)symstrsize, PROT_READ, 0, fd,
308             (off_t)symstroff);
309         if (base == MAP_FAILED)
310                 goto done;
311         strtab = (char *)base;
312
313         /*
314          * clean out any left-over information for all valid entries.
315          * Type and value defined to be 0 if not found; historical
316          * versions cleared other and desc as well.  Also figure out
317          * the largest string length so don't read any more of the
318          * string table than we have to.
319          *
320          * XXX clearing anything other than n_type and n_value violates
321          * the semantics given in the man page.
322          */
323         nent = 0;
324         for (p = list; !ISLAST(p); ++p) {
325                 p->n_type = 0;
326                 p->n_other = 0;
327                 p->n_desc = 0;
328                 p->n_value = 0;
329                 ++nent;
330         }
331
332         /* Don't process any further if object is stripped. */
333         if (symoff == 0)
334                 goto done;
335                 
336         if (lseek(fd, (off_t) symoff, SEEK_SET) == -1) {
337                 nent = -1;
338                 goto done;
339         }
340
341         while (symsize > 0 && nent > 0) {
342                 cc = MIN(symsize, sizeof(sbuf));
343                 if (_read(fd, sbuf, cc) != cc)
344                         break;
345                 symsize -= cc;
346                 for (s = sbuf; cc > 0 && nent > 0; ++s, cc -= sizeof(*s)) {
347                         char *name;
348                         struct nlist *p;
349
350                         name = strtab + s->st_name;
351                         if (name[0] == '\0')
352                                 continue;
353                         for (p = list; !ISLAST(p); p++) {
354                                 if ((p->n_un.n_name[0] == '_' &&
355                                     strcmp(name, p->n_un.n_name+1) == 0)
356                                     || strcmp(name, p->n_un.n_name) == 0) {
357                                         elf_sym_to_nlist(p, s, shdr,
358                                             ehdr.e_shnum);
359                                         if (--nent <= 0)
360                                                 break;
361                                 }
362                         }
363                 }
364         }
365   done:
366         errsave = errno;
367         if (strtab != NULL)
368                 munmap(strtab, symstrsize);
369         if (shdr != NULL)
370                 munmap(shdr, shdr_size);
371         errno = errsave;
372         return (nent);
373 }
374
375 /*
376  * Convert an Elf_Sym into an nlist structure.  This fills in only the
377  * n_value and n_type members.
378  */
379 static void
380 elf_sym_to_nlist(nl, s, shdr, shnum)
381         struct nlist *nl;
382         Elf_Sym *s;
383         Elf_Shdr *shdr;
384         int shnum;
385 {
386         nl->n_value = s->st_value;
387
388         switch (s->st_shndx) {
389         case SHN_UNDEF:
390         case SHN_COMMON:
391                 nl->n_type = N_UNDF;
392                 break;
393         case SHN_ABS:
394                 nl->n_type = ELF_ST_TYPE(s->st_info) == STT_FILE ?
395                     N_FN : N_ABS;
396                 break;
397         default:
398                 if (s->st_shndx >= shnum)
399                         nl->n_type = N_UNDF;
400                 else {
401                         Elf_Shdr *sh = shdr + s->st_shndx;
402
403                         nl->n_type = sh->sh_type == SHT_PROGBITS ?
404                             (sh->sh_flags & SHF_WRITE ? N_DATA : N_TEXT) :
405                             (sh->sh_type == SHT_NOBITS ? N_BSS : N_UNDF);
406                 }
407                 break;
408         }
409
410         if (ELF_ST_BIND(s->st_info) == STB_GLOBAL ||
411             ELF_ST_BIND(s->st_info) == STB_WEAK)
412                 nl->n_type |= N_EXT;
413 }
414 #endif /* _NLIST_DO_ELF */