1/*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * Copyright (c) 1989, 1993
25 * The Regents of the University of California.  All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 *    notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 *    notice, this list of conditions and the following disclaimer in the
34 *    documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 *    must display the following acknowledgement:
37 *      This product includes software developed by the University of
38 *      California, Berkeley and its contributors.
39 * 4. Neither the name of the University nor the names of its contributors
40 *    may be used to endorse or promote products derived from this software
41 *    without specific prior written permission.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 * SUCH DAMAGE.
54 */
55
56
57/*
58 * This file was copied from libc/gen/nlist.c from Darwin's source code
59 * The version of nlist used as a base is from 10.5.2, libc-498
60 * http://www.opensource.apple.com/darwinsource/10.5.2/Libc-498/gen/nlist.c
61 *
62 * The full tarball is at:
63 * http://www.opensource.apple.com/darwinsource/tarballs/apsl/Libc-498.tar.gz
64 *
65 * I've modified it to be compatible with 64-bit images.
66*/
67
68#include "breakpad_nlist_64.h"
69
70#include <CoreFoundation/CoreFoundation.h>
71#include <fcntl.h>
72#include <mach-o/nlist.h>
73#include <mach-o/loader.h>
74#include <mach-o/fat.h>
75#include <mach/mach.h>
76#include <stdio.h>
77#include <stdlib.h>
78#include <sys/types.h>
79#include <sys/uio.h>
80#include <TargetConditionals.h>
81#include <unistd.h>
82
83/* Stuff lifted from <a.out.h> and <sys/exec.h> since they are gone */
84/*
85 * Header prepended to each a.out file.
86 */
87struct exec {
88  unsigned short  a_machtype;     /* machine type */
89  unsigned short  a_magic;        /* magic number */
90  unsigned long a_text;         /* size of text segment */
91  unsigned long a_data;         /* size of initialized data */
92  unsigned long a_bss;          /* size of uninitialized data */
93  unsigned long a_syms;         /* size of symbol table */
94  unsigned long a_entry;        /* entry point */
95  unsigned long a_trsize;       /* size of text relocation */
96  unsigned long a_drsize;       /* size of data relocation */
97};
98
99#define OMAGIC  0407            /* old impure format */
100#define NMAGIC  0410            /* read-only text */
101#define ZMAGIC  0413            /* demand load format */
102
103#define N_BADMAG(x)                                                     \
104  (((x).a_magic)!=OMAGIC && ((x).a_magic)!=NMAGIC && ((x).a_magic)!=ZMAGIC)
105#define N_TXTOFF(x)                                     \
106  ((x).a_magic==ZMAGIC ? 0 : sizeof (struct exec))
107#define N_SYMOFF(x)                                                     \
108  (N_TXTOFF(x) + (x).a_text+(x).a_data + (x).a_trsize+(x).a_drsize)
109
110// Traits structs for specializing function templates to handle
111// 32-bit/64-bit Mach-O files.
112template<typename T>
113struct MachBits {};
114
115typedef struct nlist nlist32;
116typedef struct nlist_64 nlist64;
117
118template<>
119struct MachBits<nlist32> {
120  typedef mach_header mach_header_type;
121  typedef uint32_t word_type;
122  static const uint32_t magic = MH_MAGIC;
123};
124
125template<>
126struct MachBits<nlist64> {
127  typedef mach_header_64 mach_header_type;
128  typedef uint64_t word_type;
129  static const uint32_t magic = MH_MAGIC_64;
130};
131
132template<typename nlist_type>
133int
134__breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
135                   cpu_type_t cpu_type);
136
137/*
138 * nlist - retreive attributes from name list (string table version)
139 */
140
141template <typename nlist_type>
142int breakpad_nlist_common(const char *name,
143                          nlist_type *list,
144                          const char **symbolNames,
145                          cpu_type_t cpu_type) {
146  int fd = open(name, O_RDONLY, 0);
147  if (fd < 0)
148    return -1;
149  int n = __breakpad_fdnlist(fd, list, symbolNames, cpu_type);
150  close(fd);
151  return n;
152}
153
154int breakpad_nlist(const char *name,
155                   struct nlist *list,
156                   const char **symbolNames,
157                   cpu_type_t cpu_type) {
158  return breakpad_nlist_common(name, list, symbolNames, cpu_type);
159}
160
161int breakpad_nlist(const char *name,
162                   struct nlist_64 *list,
163                   const char **symbolNames,
164                   cpu_type_t cpu_type) {
165  return breakpad_nlist_common(name, list, symbolNames, cpu_type);
166}
167
168/* Note: __fdnlist() is called from kvm_nlist in libkvm's kvm.c */
169
170template<typename nlist_type>
171int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
172                       cpu_type_t cpu_type) {
173  typedef typename MachBits<nlist_type>::mach_header_type mach_header_type;
174  typedef typename MachBits<nlist_type>::word_type word_type;
175
176  const uint32_t magic = MachBits<nlist_type>::magic;
177
178  int maxlen = 500;
179  int nreq = 0;
180  for (nlist_type* q = list;
181       symbolNames[q-list] && symbolNames[q-list][0];
182       q++, nreq++) {
183
184    q->n_type = 0;
185    q->n_value = 0;
186    q->n_desc = 0;
187    q->n_sect = 0;
188    q->n_un.n_strx = 0;
189  }
190
191  struct exec buf;
192  if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) ||
193      (N_BADMAG(buf) && *((uint32_t *)&buf) != magic &&
194        CFSwapInt32BigToHost(*((uint32_t *)&buf)) != FAT_MAGIC &&
195       /* The following is the big-endian ppc64 check */
196       (*((uint32_t*)&buf)) != FAT_MAGIC)) {
197    return -1;
198  }
199
200  /* Deal with fat file if necessary */
201  unsigned arch_offset = 0;
202  if (CFSwapInt32BigToHost(*((uint32_t *)&buf)) == FAT_MAGIC ||
203      /* The following is the big-endian ppc64 check */
204      *((unsigned int *)&buf) == FAT_MAGIC) {
205    /* Read in the fat header */
206    struct fat_header fh;
207    if (lseek(fd, 0, SEEK_SET) == -1) {
208      return -1;
209    }
210    if (read(fd, (char *)&fh, sizeof(fh)) != sizeof(fh)) {
211      return -1;
212    }
213
214    /* Convert fat_narchs to host byte order */
215    fh.nfat_arch = CFSwapInt32BigToHost(fh.nfat_arch);
216
217    /* Read in the fat archs */
218    struct fat_arch *fat_archs =
219        (struct fat_arch *)malloc(fh.nfat_arch * sizeof(struct fat_arch));
220    if (fat_archs == NULL) {
221      return -1;
222    }
223    if (read(fd, (char *)fat_archs,
224             sizeof(struct fat_arch) * fh.nfat_arch) !=
225        (ssize_t)(sizeof(struct fat_arch) * fh.nfat_arch)) {
226      free(fat_archs);
227      return -1;
228    }
229
230    /*
231     * Convert archs to host byte ordering (a constraint of
232     * cpusubtype_getbestarch()
233     */
234    for (unsigned i = 0; i < fh.nfat_arch; i++) {
235      fat_archs[i].cputype =
236        CFSwapInt32BigToHost(fat_archs[i].cputype);
237      fat_archs[i].cpusubtype =
238        CFSwapInt32BigToHost(fat_archs[i].cpusubtype);
239      fat_archs[i].offset =
240        CFSwapInt32BigToHost(fat_archs[i].offset);
241      fat_archs[i].size =
242        CFSwapInt32BigToHost(fat_archs[i].size);
243      fat_archs[i].align =
244        CFSwapInt32BigToHost(fat_archs[i].align);
245    }
246
247    struct fat_arch *fap = NULL;
248    for (unsigned i = 0; i < fh.nfat_arch; i++) {
249      if (fat_archs[i].cputype == cpu_type) {
250        fap = &fat_archs[i];
251        break;
252      }
253    }
254
255    if (!fap) {
256      free(fat_archs);
257      return -1;
258    }
259    arch_offset = fap->offset;
260    free(fat_archs);
261
262    /* Read in the beginning of the architecture-specific file */
263    if (lseek(fd, arch_offset, SEEK_SET) == -1) {
264      return -1;
265    }
266    if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf)) {
267      return -1;
268    }
269  }
270
271  off_t sa;  /* symbol address */
272  off_t ss;  /* start of strings */
273  register_t n;
274  if (*((unsigned int *)&buf) == magic) {
275    if (lseek(fd, arch_offset, SEEK_SET) == -1) {
276      return -1;
277    }
278    mach_header_type mh;
279    if (read(fd, (char *)&mh, sizeof(mh)) != sizeof(mh)) {
280      return -1;
281    }
282
283    struct load_command *load_commands =
284        (struct load_command *)malloc(mh.sizeofcmds);
285    if (load_commands == NULL) {
286      return -1;
287    }
288    if (read(fd, (char *)load_commands, mh.sizeofcmds) !=
289        (ssize_t)mh.sizeofcmds) {
290      free(load_commands);
291      return -1;
292    }
293    struct symtab_command *stp = NULL;
294    struct load_command *lcp = load_commands;
295    // iterate through all load commands, looking for
296    // LC_SYMTAB load command
297    for (uint32_t i = 0; i < mh.ncmds; i++) {
298      if (lcp->cmdsize % sizeof(word_type) != 0 ||
299          lcp->cmdsize <= 0 ||
300          (char *)lcp + lcp->cmdsize >
301          (char *)load_commands + mh.sizeofcmds) {
302        free(load_commands);
303        return -1;
304      }
305      if (lcp->cmd == LC_SYMTAB) {
306        if (lcp->cmdsize !=
307            sizeof(struct symtab_command)) {
308          free(load_commands);
309          return -1;
310        }
311        stp = (struct symtab_command *)lcp;
312        break;
313      }
314      lcp = (struct load_command *)
315        ((char *)lcp + lcp->cmdsize);
316    }
317    if (stp == NULL) {
318      free(load_commands);
319      return -1;
320    }
321    // sa points to the beginning of the symbol table
322    sa = stp->symoff + arch_offset;
323    // ss points to the beginning of the string table
324    ss = stp->stroff + arch_offset;
325    // n is the number of bytes in the symbol table
326    // each symbol table entry is an nlist structure
327    n = stp->nsyms * sizeof(nlist_type);
328    free(load_commands);
329  } else {
330    sa = N_SYMOFF(buf) + arch_offset;
331    ss = sa + buf.a_syms + arch_offset;
332    n = buf.a_syms;
333  }
334
335  if (lseek(fd, sa, SEEK_SET) == -1) {
336    return -1;
337  }
338
339  // the algorithm here is to read the nlist entries in m-sized
340  // chunks into q.  q is then iterated over. for each entry in q,
341  // use the string table index(q->n_un.n_strx) to read the symbol
342  // name, then scan the nlist entries passed in by the user(via p),
343  // and look for a match
344  while (n) {
345    nlist_type space[BUFSIZ/sizeof (nlist_type)];
346    register_t m = sizeof (space);
347
348    if (n < m)
349      m = n;
350    if (read(fd, (char *)space, m) != m)
351      break;
352    n -= m;
353    off_t savpos = lseek(fd, 0, SEEK_CUR);
354    if (savpos == -1) {
355      return -1;
356    }
357    for (nlist_type* q = space; (m -= sizeof(nlist_type)) >= 0; q++) {
358      char nambuf[BUFSIZ];
359
360      if (q->n_un.n_strx == 0 || q->n_type & N_STAB)
361        continue;
362
363      // seek to the location in the binary where the symbol
364      // name is stored & read it into memory
365      if (lseek(fd, ss+q->n_un.n_strx, SEEK_SET) == -1) {
366        return -1;
367      }
368      if (read(fd, nambuf, maxlen+1) == -1) {
369        return -1;
370      }
371      const char *s2 = nambuf;
372      for (nlist_type *p = list;
373           symbolNames[p-list] && symbolNames[p-list][0];
374           p++) {
375        // get the symbol name the user has passed in that
376        // corresponds to the nlist entry that we're looking at
377        const char *s1 = symbolNames[p - list];
378        while (*s1) {
379          if (*s1++ != *s2++)
380            goto cont;
381        }
382        if (*s2)
383          goto cont;
384
385        p->n_value = q->n_value;
386        p->n_type = q->n_type;
387        p->n_desc = q->n_desc;
388        p->n_sect = q->n_sect;
389        p->n_un.n_strx = q->n_un.n_strx;
390        if (--nreq == 0)
391          return nreq;
392
393        break;
394      cont:           ;
395      }
396    }
397    if (lseek(fd, savpos, SEEK_SET) == -1) {
398      return -1;
399    }
400  }
401  return nreq;
402}
403