mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-11-13 09:04:59 +00:00
We're working on building our Firefox Mac builds as a Linux cross-compile (https://bugzilla.mozilla.org/show_bug.cgi?id=921040) and we need symbol dumping to work. This change ports the Mac dump_syms tool to build and work on Linux. I've tested it and it produces identical output to running the tool on Mac. The bulk of the work here was converting src/common/mac/dump_syms.mm and src/tools/mac/dump_syms/dump_syms_tool.mm from ObjC++ to C++ and removing their use of Foundation classes in favor of standard C/C++. This won't compile out-of-the-box on Linux, it requires some Mac system headers that are not included in this patch. I have those tentatively in a separate patch to land in Gecko (http://hg.mozilla.org/users/tmielczarek_mozilla.com/mc/rev/5fb8da23c83c), but I wasn't sure if you'd be interested in having them in the Breakpad tree. We could almost certainly pare down the set of headers included there, I didn't spend too much time trying to minimize them (we primarily just need the Mach-O structs and a few associated bits). I just realized that this patch is missing updating the XCode project files (ugh). I'll fix that up in a bit. R=mark@chromium.org BUG=https://bugzilla.mozilla.org/show_bug.cgi?id=543111 Review URL: https://codereview.chromium.org/1340543002 .
269 lines
7.9 KiB
C++
269 lines
7.9 KiB
C++
// Copyright (c) 2006, Google Inc.
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * 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.
|
|
// * Neither the name of Google Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT
|
|
// OWNER 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.
|
|
|
|
// macho_walker.cc: Iterate over the load commands in a mach-o file
|
|
//
|
|
// See macho_walker.h for documentation
|
|
//
|
|
// Author: Dan Waylonis
|
|
|
|
#include <assert.h>
|
|
#include <fcntl.h>
|
|
#include <mach-o/arch.h>
|
|
#include <mach-o/fat.h>
|
|
#include <mach-o/loader.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "common/mac/byteswap.h"
|
|
#include "common/mac/macho_walker.h"
|
|
#include "common/mac/macho_utilities.h"
|
|
|
|
namespace MacFileUtilities {
|
|
|
|
MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback,
|
|
void *context)
|
|
: file_(-1),
|
|
memory_(NULL),
|
|
memory_size_(0),
|
|
callback_(callback),
|
|
callback_context_(context),
|
|
current_header_(NULL),
|
|
current_header_size_(0),
|
|
current_header_offset_(0) {
|
|
file_ = open(path, O_RDONLY);
|
|
}
|
|
|
|
MachoWalker::MachoWalker(void *memory, size_t size,
|
|
LoadCommandCallback callback, void *context)
|
|
: file_(-1),
|
|
memory_(memory),
|
|
memory_size_(size),
|
|
callback_(callback),
|
|
callback_context_(context),
|
|
current_header_(NULL),
|
|
current_header_size_(0),
|
|
current_header_offset_(0) {
|
|
}
|
|
|
|
MachoWalker::~MachoWalker() {
|
|
if (file_ != -1)
|
|
close(file_);
|
|
}
|
|
|
|
bool MachoWalker::WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
|
|
cpu_type_t valid_cpu_type = cpu_type;
|
|
cpu_subtype_t valid_cpu_subtype = cpu_subtype;
|
|
// if |cpu_type| is 0, use the native cpu type.
|
|
if (cpu_type == 0) {
|
|
const NXArchInfo *arch = NXGetLocalArchInfo();
|
|
assert(arch);
|
|
valid_cpu_type = arch->cputype;
|
|
valid_cpu_subtype = CPU_SUBTYPE_MULTIPLE;
|
|
}
|
|
off_t offset;
|
|
if (FindHeader(valid_cpu_type, valid_cpu_subtype, offset)) {
|
|
if (cpu_type & CPU_ARCH_ABI64)
|
|
return WalkHeader64AtOffset(offset);
|
|
|
|
return WalkHeaderAtOffset(offset);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MachoWalker::ReadBytes(void *buffer, size_t size, off_t offset) {
|
|
if (memory_) {
|
|
if (offset < 0)
|
|
return false;
|
|
bool result = true;
|
|
if (offset + size > memory_size_) {
|
|
if (static_cast<size_t>(offset) >= memory_size_)
|
|
return false;
|
|
size = memory_size_ - static_cast<size_t>(offset);
|
|
result = false;
|
|
}
|
|
memcpy(buffer, static_cast<char *>(memory_) + offset, size);
|
|
return result;
|
|
} else {
|
|
return pread(file_, buffer, size, offset) == (ssize_t)size;
|
|
}
|
|
}
|
|
|
|
bool MachoWalker::CurrentHeader(struct mach_header_64 *header, off_t *offset) {
|
|
if (current_header_) {
|
|
memcpy(header, current_header_, sizeof(mach_header_64));
|
|
*offset = current_header_offset_;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MachoWalker::FindHeader(cpu_type_t cpu_type,
|
|
cpu_subtype_t cpu_subtype,
|
|
off_t &offset) {
|
|
// Read the magic bytes that's common amongst all mach-o files
|
|
uint32_t magic;
|
|
if (!ReadBytes(&magic, sizeof(magic), 0))
|
|
return false;
|
|
|
|
offset = sizeof(magic);
|
|
|
|
// Figure out what type of file we've got
|
|
bool is_fat = false;
|
|
if (magic == FAT_MAGIC || magic == FAT_CIGAM) {
|
|
is_fat = true;
|
|
}
|
|
else if (magic != MH_MAGIC && magic != MH_CIGAM && magic != MH_MAGIC_64 &&
|
|
magic != MH_CIGAM_64) {
|
|
return false;
|
|
}
|
|
|
|
if (!is_fat) {
|
|
// If we don't have a fat header, check if the cpu type matches the single
|
|
// header
|
|
struct mach_header header;
|
|
if (!ReadBytes(&header, sizeof(header), 0))
|
|
return false;
|
|
|
|
if (magic == MH_CIGAM || magic == MH_CIGAM_64)
|
|
breakpad_swap_mach_header(&header);
|
|
|
|
if (cpu_type != header.cputype ||
|
|
(cpu_subtype != CPU_SUBTYPE_MULTIPLE &&
|
|
cpu_subtype != header.cpusubtype)) {
|
|
return false;
|
|
}
|
|
|
|
offset = 0;
|
|
return true;
|
|
} else {
|
|
// Read the fat header and find an appropriate architecture
|
|
offset = 0;
|
|
struct fat_header fat;
|
|
if (!ReadBytes(&fat, sizeof(fat), offset))
|
|
return false;
|
|
|
|
if (NXHostByteOrder() != NX_BigEndian)
|
|
breakpad_swap_fat_header(&fat);
|
|
|
|
offset += sizeof(fat);
|
|
|
|
// Search each architecture for the desired one
|
|
struct fat_arch arch;
|
|
for (uint32_t i = 0; i < fat.nfat_arch; ++i) {
|
|
if (!ReadBytes(&arch, sizeof(arch), offset))
|
|
return false;
|
|
|
|
if (NXHostByteOrder() != NX_BigEndian)
|
|
breakpad_swap_fat_arch(&arch, 1);
|
|
|
|
if (arch.cputype == cpu_type &&
|
|
(cpu_subtype == CPU_SUBTYPE_MULTIPLE ||
|
|
arch.cpusubtype == cpu_subtype)) {
|
|
offset = arch.offset;
|
|
return true;
|
|
}
|
|
|
|
offset += sizeof(arch);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MachoWalker::WalkHeaderAtOffset(off_t offset) {
|
|
struct mach_header header;
|
|
if (!ReadBytes(&header, sizeof(header), offset))
|
|
return false;
|
|
|
|
bool swap = (header.magic == MH_CIGAM);
|
|
if (swap)
|
|
breakpad_swap_mach_header(&header);
|
|
|
|
// Copy the data into the mach_header_64 structure. Since the 32-bit and
|
|
// 64-bit only differ in the last field (reserved), this is safe to do.
|
|
struct mach_header_64 header64;
|
|
memcpy((void *)&header64, (const void *)&header, sizeof(header));
|
|
header64.reserved = 0;
|
|
|
|
current_header_ = &header64;
|
|
current_header_size_ = sizeof(header); // 32-bit, not 64-bit
|
|
current_header_offset_ = offset;
|
|
offset += current_header_size_;
|
|
bool result = WalkHeaderCore(offset, header.ncmds, swap);
|
|
current_header_ = NULL;
|
|
current_header_size_ = 0;
|
|
current_header_offset_ = 0;
|
|
return result;
|
|
}
|
|
|
|
bool MachoWalker::WalkHeader64AtOffset(off_t offset) {
|
|
struct mach_header_64 header;
|
|
if (!ReadBytes(&header, sizeof(header), offset))
|
|
return false;
|
|
|
|
bool swap = (header.magic == MH_CIGAM_64);
|
|
if (swap)
|
|
breakpad_swap_mach_header_64(&header);
|
|
|
|
current_header_ = &header;
|
|
current_header_size_ = sizeof(header);
|
|
current_header_offset_ = offset;
|
|
offset += current_header_size_;
|
|
bool result = WalkHeaderCore(offset, header.ncmds, swap);
|
|
current_header_ = NULL;
|
|
current_header_size_ = 0;
|
|
current_header_offset_ = 0;
|
|
return result;
|
|
}
|
|
|
|
bool MachoWalker::WalkHeaderCore(off_t offset, uint32_t number_of_commands,
|
|
bool swap) {
|
|
for (uint32_t i = 0; i < number_of_commands; ++i) {
|
|
struct load_command cmd;
|
|
if (!ReadBytes(&cmd, sizeof(cmd), offset))
|
|
return false;
|
|
|
|
if (swap)
|
|
breakpad_swap_load_command(&cmd);
|
|
|
|
// Call the user callback
|
|
if (callback_ && !callback_(this, &cmd, offset, swap, callback_context_))
|
|
break;
|
|
|
|
offset += cmd.cmdsize;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace MacFileUtilities
|