/ src / client / mac / handler / breakpad_nlist_64.cc
breakpad_nlist_64.cc
  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  #ifdef HAVE_CONFIG_H
 69  #include <config.h>  // Must come first
 70  #endif
 71  
 72  #include "breakpad_nlist_64.h"
 73  
 74  #include <CoreFoundation/CoreFoundation.h>
 75  #include <fcntl.h>
 76  #include <mach-o/nlist.h>
 77  #include <mach-o/loader.h>
 78  #include <mach-o/fat.h>
 79  #include <mach/mach.h>
 80  #include <stdio.h>
 81  #include <stdlib.h>
 82  #include <sys/types.h>
 83  #include <sys/uio.h>
 84  #include <TargetConditionals.h>
 85  #include <unistd.h>
 86  
 87  /* Stuff lifted from <a.out.h> and <sys/exec.h> since they are gone */
 88  /*
 89   * Header prepended to each a.out file.
 90   */
 91  struct exec {
 92    unsigned short  a_machtype;     /* machine type */
 93    unsigned short  a_magic;        /* magic number */
 94    unsigned long a_text;         /* size of text segment */
 95    unsigned long a_data;         /* size of initialized data */
 96    unsigned long a_bss;          /* size of uninitialized data */
 97    unsigned long a_syms;         /* size of symbol table */
 98    unsigned long a_entry;        /* entry point */
 99    unsigned long a_trsize;       /* size of text relocation */
100    unsigned long a_drsize;       /* size of data relocation */
101  };
102  
103  #define OMAGIC  0407            /* old impure format */
104  #define NMAGIC  0410            /* read-only text */
105  #define ZMAGIC  0413            /* demand load format */
106  
107  #define N_BADMAG(x)                                                     \
108    (((x).a_magic)!=OMAGIC && ((x).a_magic)!=NMAGIC && ((x).a_magic)!=ZMAGIC)
109  #define N_TXTOFF(x)                                     \
110    ((x).a_magic==ZMAGIC ? 0 : sizeof (struct exec))
111  #define N_SYMOFF(x)                                                     \
112    (N_TXTOFF(x) + (x).a_text+(x).a_data + (x).a_trsize+(x).a_drsize)
113  
114  // Traits structs for specializing function templates to handle
115  // 32-bit/64-bit Mach-O files.
116  template<typename T>
117  struct MachBits {};
118  
119  typedef struct nlist nlist32;
120  typedef struct nlist_64 nlist64;
121  
122  template<>
123  struct MachBits<nlist32> {
124    typedef mach_header mach_header_type;
125    typedef uint32_t word_type;
126    static const uint32_t magic = MH_MAGIC;
127  };
128  
129  template<>
130  struct MachBits<nlist64> {
131    typedef mach_header_64 mach_header_type;
132    typedef uint64_t word_type;
133    static const uint32_t magic = MH_MAGIC_64;
134  };
135  
136  template<typename nlist_type>
137  int
138  __breakpad_fdnlist(int fd, nlist_type* list, const char** symbolNames,
139                     cpu_type_t cpu_type);
140  
141  /*
142   * nlist - retreive attributes from name list (string table version)
143   */
144  
145  template <typename nlist_type>
146  int breakpad_nlist_common(const char* name,
147                            nlist_type* list,
148                            const char** symbolNames,
149                            cpu_type_t cpu_type) {
150    int fd = open(name, O_RDONLY, 0);
151    if (fd < 0)
152      return -1;
153    int n = __breakpad_fdnlist(fd, list, symbolNames, cpu_type);
154    close(fd);
155    return n;
156  }
157  
158  int breakpad_nlist(const char* name,
159                     struct nlist* list,
160                     const char** symbolNames,
161                     cpu_type_t cpu_type) {
162    return breakpad_nlist_common(name, list, symbolNames, cpu_type);
163  }
164  
165  int breakpad_nlist(const char* name,
166                     struct nlist_64* list,
167                     const char** symbolNames,
168                     cpu_type_t cpu_type) {
169    return breakpad_nlist_common(name, list, symbolNames, cpu_type);
170  }
171  
172  /* Note: __fdnlist() is called from kvm_nlist in libkvm's kvm.c */
173  
174  template<typename nlist_type>
175  int __breakpad_fdnlist(int fd, nlist_type* list, const char** symbolNames,
176                         cpu_type_t cpu_type) {
177    typedef typename MachBits<nlist_type>::mach_header_type mach_header_type;
178    typedef typename MachBits<nlist_type>::word_type word_type;
179  
180    const uint32_t magic = MachBits<nlist_type>::magic;
181  
182    int maxlen = 500;
183    int nreq = 0;
184    for (nlist_type* q = list;
185         symbolNames[q-list] && symbolNames[q-list][0];
186         q++, nreq++) {
187  
188      q->n_type = 0;
189      q->n_value = 0;
190      q->n_desc = 0;
191      q->n_sect = 0;
192      q->n_un.n_strx = 0;
193    }
194  
195    struct exec buf;
196    if (read(fd, (char*)&buf, sizeof(buf)) != sizeof(buf) ||
197        (N_BADMAG(buf) && *((uint32_t*)&buf) != magic &&
198          CFSwapInt32BigToHost(*((uint32_t*)&buf)) != FAT_MAGIC &&
199         /* The following is the big-endian ppc64 check */
200         (*((uint32_t*)&buf)) != FAT_MAGIC)) {
201      return -1;
202    }
203  
204    /* Deal with fat file if necessary */
205    unsigned arch_offset = 0;
206    if (CFSwapInt32BigToHost(*((uint32_t*)&buf)) == FAT_MAGIC ||
207        /* The following is the big-endian ppc64 check */
208        *((unsigned int*)&buf) == FAT_MAGIC) {
209      /* Read in the fat header */
210      struct fat_header fh;
211      if (lseek(fd, 0, SEEK_SET) == -1) {
212        return -1;
213      }
214      if (read(fd, (char*)&fh, sizeof(fh)) != sizeof(fh)) {
215        return -1;
216      }
217  
218      /* Convert fat_narchs to host byte order */
219      fh.nfat_arch = CFSwapInt32BigToHost(fh.nfat_arch);
220  
221      /* Read in the fat archs */
222      struct fat_arch* fat_archs =
223          (struct fat_arch*)malloc(fh.nfat_arch * sizeof(struct fat_arch));
224      if (fat_archs == NULL) {
225        return -1;
226      }
227      if (read(fd, (char*)fat_archs,
228               sizeof(struct fat_arch) * fh.nfat_arch) !=
229          (ssize_t)(sizeof(struct fat_arch) * fh.nfat_arch)) {
230        free(fat_archs);
231        return -1;
232      }
233  
234      /*
235       * Convert archs to host byte ordering (a constraint of
236       * cpusubtype_getbestarch()
237       */
238      for (unsigned i = 0; i < fh.nfat_arch; i++) {
239        fat_archs[i].cputype =
240          CFSwapInt32BigToHost(fat_archs[i].cputype);
241        fat_archs[i].cpusubtype =
242          CFSwapInt32BigToHost(fat_archs[i].cpusubtype);
243        fat_archs[i].offset =
244          CFSwapInt32BigToHost(fat_archs[i].offset);
245        fat_archs[i].size =
246          CFSwapInt32BigToHost(fat_archs[i].size);
247        fat_archs[i].align =
248          CFSwapInt32BigToHost(fat_archs[i].align);
249      }
250  
251      struct fat_arch* fap = NULL;
252      for (unsigned i = 0; i < fh.nfat_arch; i++) {
253        if (fat_archs[i].cputype == cpu_type) {
254          fap = &fat_archs[i];
255          break;
256        }
257      }
258  
259      if (!fap) {
260        free(fat_archs);
261        return -1;
262      }
263      arch_offset = fap->offset;
264      free(fat_archs);
265  
266      /* Read in the beginning of the architecture-specific file */
267      if (lseek(fd, arch_offset, SEEK_SET) == -1) {
268        return -1;
269      }
270      if (read(fd, (char*)&buf, sizeof(buf)) != sizeof(buf)) {
271        return -1;
272      }
273    }
274  
275    off_t sa;  /* symbol address */
276    off_t ss;  /* start of strings */
277    register_t n;
278    if (*((unsigned int*)&buf) == magic) {
279      if (lseek(fd, arch_offset, SEEK_SET) == -1) {
280        return -1;
281      }
282      mach_header_type mh;
283      if (read(fd, (char*)&mh, sizeof(mh)) != sizeof(mh)) {
284        return -1;
285      }
286  
287      struct load_command* load_commands =
288          (struct load_command*)malloc(mh.sizeofcmds);
289      if (load_commands == NULL) {
290        return -1;
291      }
292      if (read(fd, (char*)load_commands, mh.sizeofcmds) !=
293          (ssize_t)mh.sizeofcmds) {
294        free(load_commands);
295        return -1;
296      }
297      struct symtab_command* stp = NULL;
298      struct load_command* lcp = load_commands;
299      // iterate through all load commands, looking for
300      // LC_SYMTAB load command
301      for (uint32_t i = 0; i < mh.ncmds; i++) {
302        if (lcp->cmdsize % sizeof(word_type) != 0 ||
303            lcp->cmdsize <= 0 ||
304            (char*)lcp + lcp->cmdsize > (char*)load_commands + mh.sizeofcmds) {
305          free(load_commands);
306          return -1;
307        }
308        if (lcp->cmd == LC_SYMTAB) {
309          if (lcp->cmdsize != sizeof(struct symtab_command)) {
310            free(load_commands);
311            return -1;
312          }
313          stp = (struct symtab_command*)lcp;
314          break;
315        }
316        lcp = (struct load_command*)((char*)lcp + lcp->cmdsize);
317      }
318      if (stp == NULL) {
319        free(load_commands);
320        return -1;
321      }
322      // sa points to the beginning of the symbol table
323      sa = stp->symoff + arch_offset;
324      // ss points to the beginning of the string table
325      ss = stp->stroff + arch_offset;
326      // n is the number of bytes in the symbol table
327      // each symbol table entry is an nlist structure
328      n = stp->nsyms * sizeof(nlist_type);
329      free(load_commands);
330    } else {
331      sa = N_SYMOFF(buf) + arch_offset;
332      ss = sa + buf.a_syms + arch_offset;
333      n = buf.a_syms;
334    }
335  
336    if (lseek(fd, sa, SEEK_SET) == -1) {
337      return -1;
338    }
339  
340    // the algorithm here is to read the nlist entries in m-sized
341    // chunks into q.  q is then iterated over. for each entry in q,
342    // use the string table index(q->n_un.n_strx) to read the symbol 
343    // name, then scan the nlist entries passed in by the user(via p),
344    // and look for a match
345    while (n) {
346      nlist_type space[BUFSIZ/sizeof (nlist_type)];
347      register_t m = sizeof (space);
348  
349      if (n < m)
350        m = n;
351      if (read(fd, (char*)space, m) != m)
352        break;
353      n -= m;
354      off_t savpos = lseek(fd, 0, SEEK_CUR);
355      if (savpos == -1) {
356        return -1;
357      }
358      for (nlist_type* q = space; (m -= sizeof(nlist_type)) >= 0; q++) {
359        char nambuf[BUFSIZ];
360  
361        if (q->n_un.n_strx == 0 || q->n_type & N_STAB)
362          continue;
363  
364        // seek to the location in the binary where the symbol
365        // name is stored & read it into memory
366        if (lseek(fd, ss+q->n_un.n_strx, SEEK_SET) == -1) {
367          return -1;
368        }
369        if (read(fd, nambuf, maxlen+1) == -1) {
370          return -1;
371        }
372        const char* s2 = nambuf;
373        for (nlist_type* p = list; 
374             symbolNames[p-list] && symbolNames[p-list][0];
375             p++) {
376          // get the symbol name the user has passed in that 
377          // corresponds to the nlist entry that we're looking at
378          const char* s1 = symbolNames[p - list];
379          while (*s1) {
380            if (*s1++ != *s2++)
381              goto cont;
382          }
383          if (*s2)
384            goto cont;
385  
386          p->n_value = q->n_value;
387          p->n_type = q->n_type;
388          p->n_desc = q->n_desc;
389          p->n_sect = q->n_sect;
390          p->n_un.n_strx = q->n_un.n_strx;
391          if (--nreq == 0)
392            return nreq;
393  
394          break;
395        cont:           ;
396        }
397      }
398      if (lseek(fd, savpos, SEEK_SET) == -1) {
399        return -1;
400      }
401    }
402    return nreq;
403  }