mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2026-01-03 21:24:43 +01:00
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:
parent
0d9bd40775
commit
0344a368de
10 changed files with 1262 additions and 653 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue