Allow out-of-process minidump generation to work on processes of a different CPU architecture

R=mark at http://breakpad.appspot.com/241001

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@746 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
ted.mielczarek@gmail.com 2010-12-15 21:55:56 +00:00
parent 0d9bd40775
commit 0344a368de
10 changed files with 1262 additions and 653 deletions

View file

@ -54,7 +54,7 @@
*/
/* nealsid:
/*
* 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
@ -62,24 +62,22 @@
* 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. However,
* 32-bit compatibility has not been retained.
* I've modified it to be compatible with 64-bit images.
*/
#ifdef __LP64__
#include "breakpad_nlist_64.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 <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include "breakpad_nlist_64.h"
#include <TargetConditionals.h>
#include <stdio.h>
#include <mach/mach.h>
#include <unistd.h>
/* Stuff lifted from <a.out.h> and <sys/exec.h> since they are gone */
/*
@ -108,44 +106,77 @@ 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_64(int fd, breakpad_nlist *list, const char **symbolNames);
__breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
cpu_type_t cpu_type);
/*
* nlist - retreive attributes from name list (string table version)
*/
int
breakpad_nlist_64(const char *name,
breakpad_nlist *list,
const char **symbolNames) {
int fd, n;
fd = open(name, O_RDONLY, 0);
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);
n = __breakpad_fdnlist_64(fd, list, symbolNames);
(void)close(fd);
return (n);
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 */
int
__breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
register breakpad_nlist *p, *q;
breakpad_nlist space[BUFSIZ/sizeof (breakpad_nlist)];
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 register char *s1, *s2;
register register_t n, m;
int maxlen, nreq;
off_t sa; /* symbol address */
off_t ss; /* start of strings */
struct exec buf;
unsigned arch_offset = 0;
const uint32_t magic = MachBits<nlist_type>::magic;
maxlen = 500;
for (q = list, nreq = 0;
int maxlen = 500;
int nreq = 0;
for (nlist_type* q = list;
symbolNames[q-list] && symbolNames[q-list][0];
q++, nreq++) {
@ -156,61 +187,61 @@ __breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
q->n_un.n_strx = 0;
}
struct exec buf;
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) ||
(N_BADMAG(buf) && *((long *)&buf) != MH_MAGIC &&
(N_BADMAG(buf) && *((long *)&buf) != magic &&
NXSwapBigLongToHost(*((long *)&buf)) != FAT_MAGIC) &&
/* nealsid: The following is the big-endian ppc64 check */
/* The following is the big-endian ppc64 check */
(*((long*)&buf)) != FAT_MAGIC) {
return (-1);
return -1;
}
/* Deal with fat file if necessary */
unsigned arch_offset = 0;
if (NXSwapBigLongToHost(*((long *)&buf)) == FAT_MAGIC ||
/* nealsid: The following is the big-endian ppc64 check */
/* The following is the big-endian ppc64 check */
*((unsigned int *)&buf) == FAT_MAGIC) {
/* Get host info */
host_t host = mach_host_self();
unsigned i = HOST_BASIC_INFO_COUNT;
struct host_basic_info hbi;
struct fat_header fh;
struct fat_arch *fat_archs, *fap;
unsigned i;
host_t host;
/* Get our host info */
host = mach_host_self();
i = HOST_BASIC_INFO_COUNT;
kern_return_t kr;
if ((kr=host_info(host, HOST_BASIC_INFO,
(host_info_t)(&hbi), &i)) != KERN_SUCCESS) {
return (-1);
if ((kr = host_info(host, HOST_BASIC_INFO,
(host_info_t)(&hbi), &i)) != KERN_SUCCESS) {
return -1;
}
mach_port_deallocate(mach_task_self(), host);
/* Read in the fat header */
lseek(fd, 0, SEEK_SET);
struct fat_header fh;
if (lseek(fd, 0, SEEK_SET) == -1) {
return -1;
}
if (read(fd, (char *)&fh, sizeof(fh)) != sizeof(fh)) {
return (-1);
return -1;
}
/* Convert fat_narchs to host byte order */
fh.nfat_arch = NXSwapBigIntToHost(fh.nfat_arch);
/* Read in the fat archs */
fat_archs = (struct fat_arch *)malloc(fh.nfat_arch *
sizeof(struct fat_arch));
struct fat_arch *fat_archs =
(struct fat_arch *)malloc(fh.nfat_arch * sizeof(struct fat_arch));
if (fat_archs == NULL) {
return (-1);
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);
return -1;
}
/*
* Convert archs to host byte ordering (a constraint of
* cpusubtype_getbestarch()
*/
for (i = 0; i < fh.nfat_arch; i++) {
for (unsigned i = 0; i < fh.nfat_arch; i++) {
fat_archs[i].cputype =
NXSwapBigIntToHost(fat_archs[i].cputype);
fat_archs[i].cpusubtype =
@ -223,159 +254,159 @@ __breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
NXSwapBigIntToHost(fat_archs[i].align);
}
fap = NULL;
for (i = 0; i < fh.nfat_arch; i++) {
/* nealsid: Although the original Apple code uses host_info */
/* to retrieve the CPU type, the host_info will still return */
/* CPU_TYPE_X86 even if running as an x86_64 binary. Given that */
/* this code isn't necessary on i386, I've decided to hardcode */
/* looking for a 64-bit binary */
#if TARGET_CPU_X86_64
if (fat_archs[i].cputype == CPU_TYPE_X86_64) {
#elif TARGET_CPU_PPC64
if (fat_archs[i].cputype == CPU_TYPE_POWERPC64) {
#else
#error undefined cpu!
{
#endif
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 */
lseek(fd, arch_offset, SEEK_SET);
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf)) {
return (-1);
}
}
if (*((unsigned int *)&buf) == MH_MAGIC_64) {
struct mach_header_64 mh;
struct load_command *load_commands, *lcp;
struct symtab_command *stp;
long i;
lseek(fd, arch_offset, SEEK_SET);
if (read(fd, (char *)&mh, sizeof(mh)) != sizeof(mh)) {
return (-1);
}
load_commands = (struct load_command *)malloc(mh.sizeofcmds);
if (load_commands == NULL) {
return (-1);
}
if (read(fd, (char *)load_commands, mh.sizeofcmds) !=
mh.sizeofcmds) {
free(load_commands);
return (-1);
}
stp = NULL;
lcp = load_commands;
// nealsid:iterate through all load commands, looking for
// LC_SYMTAB load command
for (i = 0; i < mh.ncmds; i++) {
if (lcp->cmdsize % sizeof(long) != 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(breakpad_nlist);
free(load_commands);
}
else {
sa = N_SYMOFF(buf) + arch_offset;
ss = sa + buf.a_syms + arch_offset;
n = buf.a_syms;
}
lseek(fd, sa, SEEK_SET);
// 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) {
long savpos;
m = sizeof (space);
if (n < m)
m = n;
if (read(fd, (char *)space, m) != m)
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;
n -= m;
savpos = lseek(fd, 0, SEEK_CUR);
for (q = space; (m -= sizeof(breakpad_nlist)) >= 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
lseek(fd, ss+q->n_un.n_strx, SEEK_SET);
read(fd, nambuf, maxlen+1);
s2 = nambuf;
for (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
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: ;
}
}
lseek(fd, savpos, SEEK_SET);
}
return (nreq);
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;
}
}
#endif /* __LP64__ */
off_t sa; /* symbol address */
off_t ss; /* start of strings */
register 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) !=
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 (long 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 register_t m = sizeof (space);
if (n < m)
m = n;
if (read(fd, (char *)space, m) != m)
break;
n -= m;
long 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;
}