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 }