| /* |
| * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. |
| * |
| * @APPLE_LICENSE_HEADER_START@ |
| * |
| * This file contains Original Code and/or Modifications of Original Code |
| * as defined in and that are subject to the Apple Public Source License |
| * Version 2.0 (the 'License'). You may not use this file except in |
| * compliance with the License. Please obtain a copy of the License at |
| * http://www.opensource.apple.com/apsl/ and read it before using this |
| * file. |
| * |
| * The Original Code and all software distributed under the License are |
| * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| * Please see the License for the specific language governing rights and |
| * limitations under the License. |
| * |
| * @APPLE_LICENSE_HEADER_END@ |
| */ |
| /* |
| * Copyright (c) 1989, 1993 |
| * The Regents of the University of California. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. All advertising materials mentioning features or use of this software |
| * must display the following acknowledgement: |
| * This product includes software developed by the University of |
| * California, Berkeley and its contributors. |
| * 4. Neither the name of the University nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| |
| /* |
| * This file was copied from libc/gen/nlist.c from Darwin's source code |
| * The version of nlist used as a base is from 10.5.2, libc-498 |
| * http://www.opensource.apple.com/darwinsource/10.5.2/Libc-498/gen/nlist.c |
| * |
| * The full tarball is at: |
| * http://www.opensource.apple.com/darwinsource/tarballs/apsl/Libc-498.tar.gz |
| * |
| * I've modified it to be compatible with 64-bit images. |
| */ |
| |
| #include "breakpad_nlist_64.h" |
| |
| #include <CoreFoundation/CoreFoundation.h> |
| #include <fcntl.h> |
| #include <mach-o/nlist.h> |
| #include <mach-o/loader.h> |
| #include <mach-o/fat.h> |
| #include <mach/mach.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/uio.h> |
| #include <TargetConditionals.h> |
| #include <unistd.h> |
| |
| /* Stuff lifted from <a.out.h> and <sys/exec.h> since they are gone */ |
| /* |
| * Header prepended to each a.out file. |
| */ |
| struct exec { |
| unsigned short a_machtype; /* machine type */ |
| unsigned short a_magic; /* magic number */ |
| unsigned long a_text; /* size of text segment */ |
| unsigned long a_data; /* size of initialized data */ |
| unsigned long a_bss; /* size of uninitialized data */ |
| unsigned long a_syms; /* size of symbol table */ |
| unsigned long a_entry; /* entry point */ |
| unsigned long a_trsize; /* size of text relocation */ |
| unsigned long a_drsize; /* size of data relocation */ |
| }; |
| |
| #define OMAGIC 0407 /* old impure format */ |
| #define NMAGIC 0410 /* read-only text */ |
| #define ZMAGIC 0413 /* demand load format */ |
| |
| #define N_BADMAG(x) \ |
| (((x).a_magic)!=OMAGIC && ((x).a_magic)!=NMAGIC && ((x).a_magic)!=ZMAGIC) |
| #define N_TXTOFF(x) \ |
| ((x).a_magic==ZMAGIC ? 0 : sizeof (struct exec)) |
| #define N_SYMOFF(x) \ |
| (N_TXTOFF(x) + (x).a_text+(x).a_data + (x).a_trsize+(x).a_drsize) |
| |
| // Traits structs for specializing function templates to handle |
| // 32-bit/64-bit Mach-O files. |
| template<typename T> |
| struct MachBits {}; |
| |
| typedef struct nlist nlist32; |
| typedef struct nlist_64 nlist64; |
| |
| template<> |
| struct MachBits<nlist32> { |
| typedef mach_header mach_header_type; |
| typedef uint32_t word_type; |
| static const uint32_t magic = MH_MAGIC; |
| }; |
| |
| template<> |
| struct MachBits<nlist64> { |
| typedef mach_header_64 mach_header_type; |
| typedef uint64_t word_type; |
| static const uint32_t magic = MH_MAGIC_64; |
| }; |
| |
| template<typename nlist_type> |
| int |
| __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames, |
| cpu_type_t cpu_type); |
| |
| /* |
| * nlist - retreive attributes from name list (string table version) |
| */ |
| |
| template <typename nlist_type> |
| int breakpad_nlist_common(const char *name, |
| nlist_type *list, |
| const char **symbolNames, |
| cpu_type_t cpu_type) { |
| int fd = open(name, O_RDONLY, 0); |
| if (fd < 0) |
| return -1; |
| int n = __breakpad_fdnlist(fd, list, symbolNames, cpu_type); |
| close(fd); |
| return n; |
| } |
| |
| int breakpad_nlist(const char *name, |
| struct nlist *list, |
| const char **symbolNames, |
| cpu_type_t cpu_type) { |
| return breakpad_nlist_common(name, list, symbolNames, cpu_type); |
| } |
| |
| int breakpad_nlist(const char *name, |
| struct nlist_64 *list, |
| const char **symbolNames, |
| cpu_type_t cpu_type) { |
| return breakpad_nlist_common(name, list, symbolNames, cpu_type); |
| } |
| |
| /* Note: __fdnlist() is called from kvm_nlist in libkvm's kvm.c */ |
| |
| template<typename nlist_type> |
| int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames, |
| cpu_type_t cpu_type) { |
| typedef typename MachBits<nlist_type>::mach_header_type mach_header_type; |
| typedef typename MachBits<nlist_type>::word_type word_type; |
| |
| const uint32_t magic = MachBits<nlist_type>::magic; |
| |
| int maxlen = 500; |
| int nreq = 0; |
| for (nlist_type* q = list; |
| symbolNames[q-list] && symbolNames[q-list][0]; |
| q++, nreq++) { |
| |
| q->n_type = 0; |
| q->n_value = 0; |
| q->n_desc = 0; |
| q->n_sect = 0; |
| q->n_un.n_strx = 0; |
| } |
| |
| struct exec buf; |
| if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) || |
| (N_BADMAG(buf) && *((uint32_t *)&buf) != magic && |
| CFSwapInt32BigToHost(*((uint32_t *)&buf)) != FAT_MAGIC && |
| /* The following is the big-endian ppc64 check */ |
| (*((uint32_t*)&buf)) != FAT_MAGIC)) { |
| return -1; |
| } |
| |
| /* Deal with fat file if necessary */ |
| unsigned arch_offset = 0; |
| if (CFSwapInt32BigToHost(*((uint32_t *)&buf)) == FAT_MAGIC || |
| /* The following is the big-endian ppc64 check */ |
| *((unsigned int *)&buf) == FAT_MAGIC) { |
| /* Read in the fat header */ |
| struct fat_header fh; |
| if (lseek(fd, 0, SEEK_SET) == -1) { |
| return -1; |
| } |
| if (read(fd, (char *)&fh, sizeof(fh)) != sizeof(fh)) { |
| return -1; |
| } |
| |
| /* Convert fat_narchs to host byte order */ |
| fh.nfat_arch = CFSwapInt32BigToHost(fh.nfat_arch); |
| |
| /* Read in the fat archs */ |
| struct fat_arch *fat_archs = |
| (struct fat_arch *)malloc(fh.nfat_arch * sizeof(struct fat_arch)); |
| if (fat_archs == NULL) { |
| return -1; |
| } |
| if (read(fd, (char *)fat_archs, |
| sizeof(struct fat_arch) * fh.nfat_arch) != |
| (ssize_t)(sizeof(struct fat_arch) * fh.nfat_arch)) { |
| free(fat_archs); |
| return -1; |
| } |
| |
| /* |
| * Convert archs to host byte ordering (a constraint of |
| * cpusubtype_getbestarch() |
| */ |
| for (unsigned i = 0; i < fh.nfat_arch; i++) { |
| fat_archs[i].cputype = |
| CFSwapInt32BigToHost(fat_archs[i].cputype); |
| fat_archs[i].cpusubtype = |
| CFSwapInt32BigToHost(fat_archs[i].cpusubtype); |
| fat_archs[i].offset = |
| CFSwapInt32BigToHost(fat_archs[i].offset); |
| fat_archs[i].size = |
| CFSwapInt32BigToHost(fat_archs[i].size); |
| fat_archs[i].align = |
| CFSwapInt32BigToHost(fat_archs[i].align); |
| } |
| |
| struct fat_arch *fap = NULL; |
| for (unsigned i = 0; i < fh.nfat_arch; i++) { |
| if (fat_archs[i].cputype == cpu_type) { |
| fap = &fat_archs[i]; |
| break; |
| } |
| } |
| |
| if (!fap) { |
| free(fat_archs); |
| return -1; |
| } |
| arch_offset = fap->offset; |
| free(fat_archs); |
| |
| /* Read in the beginning of the architecture-specific file */ |
| if (lseek(fd, arch_offset, SEEK_SET) == -1) { |
| return -1; |
| } |
| if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf)) { |
| return -1; |
| } |
| } |
| |
| off_t sa; /* symbol address */ |
| off_t ss; /* start of strings */ |
| register_t n; |
| if (*((unsigned int *)&buf) == magic) { |
| if (lseek(fd, arch_offset, SEEK_SET) == -1) { |
| return -1; |
| } |
| mach_header_type mh; |
| if (read(fd, (char *)&mh, sizeof(mh)) != sizeof(mh)) { |
| return -1; |
| } |
| |
| struct load_command *load_commands = |
| (struct load_command *)malloc(mh.sizeofcmds); |
| if (load_commands == NULL) { |
| return -1; |
| } |
| if (read(fd, (char *)load_commands, mh.sizeofcmds) != |
| (ssize_t)mh.sizeofcmds) { |
| free(load_commands); |
| return -1; |
| } |
| struct symtab_command *stp = NULL; |
| struct load_command *lcp = load_commands; |
| // iterate through all load commands, looking for |
| // LC_SYMTAB load command |
| for (uint32_t i = 0; i < mh.ncmds; i++) { |
| if (lcp->cmdsize % sizeof(word_type) != 0 || |
| lcp->cmdsize <= 0 || |
| (char *)lcp + lcp->cmdsize > |
| (char *)load_commands + mh.sizeofcmds) { |
| free(load_commands); |
| return -1; |
| } |
| if (lcp->cmd == LC_SYMTAB) { |
| if (lcp->cmdsize != |
| sizeof(struct symtab_command)) { |
| free(load_commands); |
| return -1; |
| } |
| stp = (struct symtab_command *)lcp; |
| break; |
| } |
| lcp = (struct load_command *) |
| ((char *)lcp + lcp->cmdsize); |
| } |
| if (stp == NULL) { |
| free(load_commands); |
| return -1; |
| } |
| // sa points to the beginning of the symbol table |
| sa = stp->symoff + arch_offset; |
| // ss points to the beginning of the string table |
| ss = stp->stroff + arch_offset; |
| // n is the number of bytes in the symbol table |
| // each symbol table entry is an nlist structure |
| n = stp->nsyms * sizeof(nlist_type); |
| free(load_commands); |
| } else { |
| sa = N_SYMOFF(buf) + arch_offset; |
| ss = sa + buf.a_syms + arch_offset; |
| n = buf.a_syms; |
| } |
| |
| if (lseek(fd, sa, SEEK_SET) == -1) { |
| return -1; |
| } |
| |
| // the algorithm here is to read the nlist entries in m-sized |
| // chunks into q. q is then iterated over. for each entry in q, |
| // use the string table index(q->n_un.n_strx) to read the symbol |
| // name, then scan the nlist entries passed in by the user(via p), |
| // and look for a match |
| while (n) { |
| nlist_type space[BUFSIZ/sizeof (nlist_type)]; |
| register_t m = sizeof (space); |
| |
| if (n < m) |
| m = n; |
| if (read(fd, (char *)space, m) != m) |
| break; |
| n -= m; |
| off_t savpos = lseek(fd, 0, SEEK_CUR); |
| if (savpos == -1) { |
| return -1; |
| } |
| for (nlist_type* q = space; (m -= sizeof(nlist_type)) >= 0; q++) { |
| char nambuf[BUFSIZ]; |
| |
| if (q->n_un.n_strx == 0 || q->n_type & N_STAB) |
| continue; |
| |
| // seek to the location in the binary where the symbol |
| // name is stored & read it into memory |
| if (lseek(fd, ss+q->n_un.n_strx, SEEK_SET) == -1) { |
| return -1; |
| } |
| if (read(fd, nambuf, maxlen+1) == -1) { |
| return -1; |
| } |
| const char *s2 = nambuf; |
| for (nlist_type *p = list; |
| symbolNames[p-list] && symbolNames[p-list][0]; |
| p++) { |
| // get the symbol name the user has passed in that |
| // corresponds to the nlist entry that we're looking at |
| const char *s1 = symbolNames[p - list]; |
| while (*s1) { |
| if (*s1++ != *s2++) |
| goto cont; |
| } |
| if (*s2) |
| goto cont; |
| |
| p->n_value = q->n_value; |
| p->n_type = q->n_type; |
| p->n_desc = q->n_desc; |
| p->n_sect = q->n_sect; |
| p->n_un.n_strx = q->n_un.n_strx; |
| if (--nreq == 0) |
| return nreq; |
| |
| break; |
| cont: ; |
| } |
| } |
| if (lseek(fd, savpos, SEEK_SET) == -1) { |
| return -1; |
| } |
| } |
| return nreq; |
| } |