Bug 886216 - Add missing breakpad patches. r=ted
authorMike Hommey <mh+mozilla@glandium.org>
Wed, 31 Jul 2013 14:03:21 +0900
changeset 140702 f5ddc199b1a67f14aaeab345f74d7fb80d21c0e5
parent 140701 9fed7a1253b147e9489fb823dbdfbc7cbb606d66
child 140703 b213e7cbee4ceaa2933e3255de97e3bb76e79e6a
push idunknown
push userunknown
push dateunknown
reviewersted
bugs886216
milestone25.0a1
Bug 886216 - Add missing breakpad patches. r=ted
toolkit/crashreporter/breakpad-patches/12-bug863475.patch
toolkit/crashreporter/breakpad-patches/12-sht-arm-exidx-define.patch
toolkit/crashreporter/breakpad-patches/13-bug836829.patch
toolkit/crashreporter/breakpad-patches/14-bug883126.patch
toolkit/crashreporter/breakpad-patches/15-bug859745.patch
toolkit/crashreporter/breakpad-patches/16-sht-arm-exidx-define.patch
toolkit/crashreporter/google-breakpad/Makefile.am
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-patches/12-bug863475.patch
@@ -0,0 +1,1305 @@
+# HG changeset patch
+# User Julian Seward <jseward@acm.org>
+# Date 1371190160 -7200
+#      Fri Jun 14 08:09:20 2013 +0200
+# Node ID e74de3db7dd27ffda8f4772f892cfb52c5c35649
+# Parent  4dcd4220c31068e116d88a58e5b396fbb01719dd
+Bug 863475 - integrate ARM EXIDX unwind parsing into Breakpad.  r=glandium,ted
+
+diff --git a/Makefile.am b/Makefile.am
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -428,16 +428,18 @@ src_tools_linux_dump_syms_dump_syms_SOUR
+ 	src/common/dwarf_line_to_module.cc \
+ 	src/common/language.cc \
+ 	src/common/module.cc \
+ 	src/common/stabs_reader.cc \
+ 	src/common/stabs_to_module.cc \
+ 	src/common/dwarf/bytereader.cc \
+ 	src/common/dwarf/dwarf2diehandler.cc \
+ 	src/common/dwarf/dwarf2reader.cc \
++        src/common/arm_ex_reader.cc \
++        src/common/arm_ex_to_module.cc \
+ 	src/common/linux/dump_symbols.cc \
+ 	src/common/linux/elf_symbols_to_module.cc \
+ 	src/common/linux/elfutils.cc \
+ 	src/common/linux/file_id.cc \
+ 	src/common/linux/linux_libc_support.cc \
+ 	src/common/linux/memory_mapped_file.cc \
+ 	src/common/linux/safe_readlink.cc \
+ 	src/tools/linux/dump_syms/dump_syms.cc
+@@ -1010,16 +1012,20 @@ EXTRA_DIST = \
+ 	src/client/windows/handler/exception_handler.vcproj \
+ 	src/client/windows/sender/crash_report_sender.cc \
+ 	src/client/windows/sender/crash_report_sender.h \
+ 	src/client/windows/sender/crash_report_sender.vcproj \
+ 	src/common/convert_UTF.c \
+ 	src/common/convert_UTF.h \
+ 	src/common/linux/dump_symbols.cc \
+ 	src/common/linux/dump_symbols.h \
++        src/common/arm_ex_reader.cc \
++        src/common/arm_ex_reader.h \
++        src/common/arm_ex_to_module.cc \
++        src/common/arm_ex_to_module.h \
+ 	src/common/linux/elf_symbols_to_module.cc \
+ 	src/common/linux/elf_symbols_to_module.h \
+ 	src/common/linux/elfutils.cc \
+ 	src/common/linux/elfutils.h \
+ 	src/common/linux/file_id.cc \
+ 	src/common/linux/file_id.h \
+ 	src/common/linux/guid_creator.cc \
+ 	src/common/linux/guid_creator.h \
+diff --git a/src/common/arm_ex_reader.cc b/src/common/arm_ex_reader.cc
+new file mode 100644
+--- /dev/null
++++ b/src/common/arm_ex_reader.cc
+@@ -0,0 +1,502 @@
++
++/* libunwind - a platform-independent unwind library
++   Copyright 2011 Linaro Limited
++
++This file is part of libunwind.
++
++Permission is hereby granted, free of charge, to any person obtaining
++a copy of this software and associated documentation files (the
++"Software"), to deal in the Software without restriction, including
++without limitation the rights to use, copy, modify, merge, publish,
++distribute, sublicense, and/or sell copies of the Software, and to
++permit persons to whom the Software is furnished to do so, subject to
++the following conditions:
++
++The above copyright notice and this permission notice shall be
++included in all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
++
++// Copyright (c) 2010 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.
++
++
++// Derived from libunwind, with extensive modifications.
++
++
++#include "common/arm_ex_reader.h"
++#include "common/logging.h"
++
++#include <assert.h>
++
++// This file, in conjunction with arm_ex_to_module.cc, translates
++// EXIDX unwind information into the same format that Breakpad uses
++// for CFI information.  Hence Breakpad's CFI unwinding abilities
++// also become usable for EXIDX.
++//
++// See: "Exception Handling ABI for the ARM Architecture", ARM IHI 0038A
++// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
++
++// EXIDX data is presented in two parts:
++//
++// * an index table.  This contains two words per routine,
++//   the first of which identifies the routine, and the second
++//   of which is a reference to the unwind bytecode.  If the
++//   bytecode is very compact -- 3 bytes or less -- it can be
++//   stored directly in the second word.
++//
++// * an area containing the unwind bytecodes.
++
++// General flow is: ExceptionTableInfo::Start iterates over all
++// of the index table entries (pairs).  For each entry, it:
++//
++// * calls ExceptionTableInfo::ExtabEntryExtract to copy the bytecode
++//   out into an intermediate buffer.
++
++// * uses ExceptionTableInfo::ExtabEntryDecode to parse the intermediate
++//   buffer.  Each bytecode instruction is bundled into a
++//   arm_ex_to_module::extab_data structure, and handed to ..
++//
++// * .. ARMExToModule::ImproveStackFrame, which in turn hands it to
++//   ARMExToModule::TranslateCmd, and that generates the pseudo-CFI
++//   records that Breakpad stores.
++
++#define ARM_EXIDX_CANT_UNWIND 0x00000001
++#define ARM_EXIDX_COMPACT     0x80000000
++#define ARM_EXTBL_OP_FINISH   0xb0
++#define ARM_EXIDX_TABLE_LIMIT (255*4)
++
++namespace arm_ex_reader {
++
++using arm_ex_to_module::ARM_EXIDX_CMD_FINISH;
++using arm_ex_to_module::ARM_EXIDX_CMD_SUB_FROM_VSP;
++using arm_ex_to_module::ARM_EXIDX_CMD_ADD_TO_VSP;
++using arm_ex_to_module::ARM_EXIDX_CMD_REG_POP;
++using arm_ex_to_module::ARM_EXIDX_CMD_REG_TO_SP;
++using arm_ex_to_module::ARM_EXIDX_CMD_VFP_POP;
++using arm_ex_to_module::ARM_EXIDX_CMD_WREG_POP;
++using arm_ex_to_module::ARM_EXIDX_CMD_WCGR_POP;
++using arm_ex_to_module::ARM_EXIDX_CMD_RESERVED;
++using arm_ex_to_module::ARM_EXIDX_CMD_REFUSED;
++using arm_ex_to_module::exidx_entry;
++using arm_ex_to_module::ARM_EXIDX_VFP_SHIFT_16;
++using arm_ex_to_module::ARM_EXIDX_VFP_FSTMD;
++using google_breakpad::MemoryRange;
++
++
++static void* Prel31ToAddr(const void* addr) 
++{
++  uint32_t offset32 = *reinterpret_cast<const uint32_t*>(addr);
++  // sign extend offset32[30:0] to 64 bits -- copy bit 30 to positions
++  // 63:31 inclusive.
++  uint64_t offset64 = offset32;
++  if (offset64 & (1ULL << 30))
++    offset64 |= 0xFFFFFFFF80000000ULL;
++  else
++    offset64 &= 0x000000007FFFFFFFULL;
++  return ((char*)addr) + (uintptr_t)offset64;
++}
++
++
++// Extract unwind bytecode for the function denoted by |entry| into |buf|,
++// and return the number of bytes of |buf| written, along with a code
++// indicating the outcome.
++
++ExceptionTableInfo::ExExtractResult
++ExceptionTableInfo::ExtabEntryExtract(const struct exidx_entry* entry,
++                                      uint8_t* buf, size_t buf_size,
++                                      /*OUT*/size_t* buf_used)
++{
++  MemoryRange mr_out(buf, buf_size);
++
++  *buf_used = 0;
++
++# define PUT_BUF_U8(_byte) \
++  do { if (!mr_out.Covers(*buf_used, 1)) return ExOutBufOverflow; \
++       buf[(*buf_used)++] = (_byte); } while (0)
++
++# define GET_EX_U32(_lval, _addr, _sec_mr) \
++  do { if (!(_sec_mr).Covers(reinterpret_cast<const uint8_t*>(_addr) \
++                             - (_sec_mr).data(), 4)) \
++         return ExInBufOverflow; \
++       (_lval) = *(reinterpret_cast<const uint32_t*>(_addr)); } while (0)
++
++# define GET_EXIDX_U32(_lval, _addr) \
++            GET_EX_U32(_lval, _addr, mr_exidx_)
++# define GET_EXTAB_U32(_lval, _addr) \
++            GET_EX_U32(_lval, _addr, mr_extab_)
++
++  uint32_t data;
++  GET_EXIDX_U32(data, &entry->data);
++
++  // A function can be marked CANT_UNWIND if (eg) it is known to be
++  // at the bottom of the stack.
++  if (data == ARM_EXIDX_CANT_UNWIND)
++    return ExCantUnwind;
++
++  uint32_t  pers;          // personality number
++  uint32_t  extra;         // number of extra data words required
++  uint32_t  extra_allowed; // number of extra data words allowed
++  uint32_t* extbl_data;    // the handler entry, if not inlined
++
++  if (data & ARM_EXIDX_COMPACT) {
++    // The handler table entry has been inlined into the index table entry.
++    // In this case it can only be an ARM-defined compact model, since
++    // bit 31 is 1.  Only personalities 0, 1 and 2 are defined for the
++    // ARM compact model, but 1 and 2 are "Long format" and may require
++    // extra data words.  Hence the allowable personalities here are:
++    //   personality 0, in which case 'extra' has no meaning
++    //   personality 1, with zero extra words
++    //   personality 2, with zero extra words
++    extbl_data = NULL;
++    pers  = (data >> 24) & 0x0F;
++    extra = (data >> 16) & 0xFF;
++    extra_allowed = 0;
++  }
++  else {
++    // The index table entry is a pointer to the handler entry.  Note
++    // that Prel31ToAddr will read the given address, but we already
++    // range-checked above.
++    extbl_data = reinterpret_cast<uint32_t*>(Prel31ToAddr(&entry->data));
++    GET_EXTAB_U32(data, extbl_data);
++    if (!(data & ARM_EXIDX_COMPACT)) {
++      // This denotes a "generic model" handler.  That will involve
++      // executing arbitary machine code, which is something we
++      // can't represent here; hence reject it.
++      return ExCantRepresent;
++    }
++    // So we have a compact model representation.  Again, 3 possible
++    // personalities, but this time up to 255 allowable extra words.
++    pers  = (data >> 24) & 0x0F;
++    extra = (data >> 16) & 0xFF;
++    extra_allowed = 255;
++    extbl_data++;
++  }
++
++  // Now look at the the handler table entry.  The first word is
++  // |data| and subsequent words start at |*extbl_data|.  The number
++  // of extra words to use is |extra|, provided that the personality
++  // allows extra words.  Even if it does, none may be available --
++  // extra_allowed is the maximum number of extra words allowed. */
++  if (pers == 0) {
++    // "Su16" in the documentation -- 3 unwinding insn bytes
++    // |extra| has no meaning here; instead that byte is an unwind-info byte
++    PUT_BUF_U8(data >> 16);
++    PUT_BUF_U8(data >> 8);
++    PUT_BUF_U8(data);
++  }
++  else if ((pers == 1 || pers == 2) && extra <= extra_allowed) {
++    // "Lu16" or "Lu32" respectively -- 2 unwinding insn bytes,
++    // and up to 255 extra words.
++    PUT_BUF_U8(data >> 8);
++    PUT_BUF_U8(data);
++    for (uint32_t j = 0; j < extra; j++) {
++      GET_EXTAB_U32(data, extbl_data);
++      extbl_data++;
++      PUT_BUF_U8(data >> 24);
++      PUT_BUF_U8(data >> 16);
++      PUT_BUF_U8(data >> 8);
++      PUT_BUF_U8(data >> 0);
++    }
++  }
++  else {
++    // The entry is invalid.
++    return ExInvalid;
++  }
++
++  // Make sure the entry is terminated with "FINISH"
++  if (*buf_used > 0 && buf[(*buf_used) - 1] != ARM_EXTBL_OP_FINISH)
++    PUT_BUF_U8(ARM_EXTBL_OP_FINISH);
++
++  return ExSuccess;
++
++# undef GET_EXTAB_U32
++# undef GET_EXIDX_U32
++# undef GET_U32
++# undef PUT_BUF_U8
++}
++
++
++// Take the unwind information extracted by ExtabEntryExtract
++// and parse it into frame-unwind instructions.  These are as
++// specified in "Table 4, ARM-defined frame-unwinding instructions"
++// in the specification document detailed in comments at the top
++// of this file.
++//
++// This reads from |buf[0, +data_size)|.  It checks for overruns of
++// the input buffer and returns a negative value if that happens, or
++// for any other failure cases.  It returns zero in case of success.
++int ExceptionTableInfo::ExtabEntryDecode(const uint8_t* buf, size_t buf_size)
++{
++  if (buf == NULL || buf_size == 0)
++    return -1;
++
++  MemoryRange mr_in(buf, buf_size);
++  const uint8_t* buf_initially = buf;
++
++# define GET_BUF_U8(_lval) \
++  do { if (!mr_in.Covers(buf - buf_initially, 1)) return -1; \
++       (_lval) = *(buf++); } while (0)
++
++  const uint8_t* end = buf + buf_size;
++
++  while (buf < end) {
++    struct arm_ex_to_module::extab_data edata;
++    memset(&edata, 0, sizeof(edata));
++
++    uint8_t op;
++    GET_BUF_U8(op);
++    if ((op & 0xc0) == 0x00) {
++      // vsp = vsp + (xxxxxx << 2) + 4
++      edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
++      edata.data = (((int)op & 0x3f) << 2) + 4;
++    }
++    else if ((op & 0xc0) == 0x40) {
++      // vsp = vsp - (xxxxxx << 2) - 4
++      edata.cmd = ARM_EXIDX_CMD_SUB_FROM_VSP;
++      edata.data = (((int)op & 0x3f) << 2) + 4;
++    }
++    else if ((op & 0xf0) == 0x80) {
++      uint8_t op2;
++      GET_BUF_U8(op2);
++      if (op == 0x80 && op2 == 0x00) {
++        // Refuse to unwind
++        edata.cmd = ARM_EXIDX_CMD_REFUSED;
++      } else {
++        // Pop up to 12 integer registers under masks {r15-r12},{r11-r4}
++        edata.cmd = ARM_EXIDX_CMD_REG_POP;
++        edata.data = ((op & 0xf) << 8) | op2;
++        edata.data = edata.data << 4;
++      }
++    }
++    else if ((op & 0xf0) == 0x90) {
++      if (op == 0x9d || op == 0x9f) {
++        // 9d: Reserved as prefix for ARM register to register moves
++        // 9f: Reserved as perfix for Intel Wireless MMX reg to reg moves
++        edata.cmd = ARM_EXIDX_CMD_RESERVED;
++      } else {
++        // Set vsp = r[nnnn]
++        edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
++        edata.data = op & 0x0f;
++      }
++    }
++    else if ((op & 0xf0) == 0xa0) {
++      // Pop r4 to r[4+nnn],          or
++      // Pop r4 to r[4+nnn] and r14   or
++      unsigned end = (op & 0x07);
++      edata.data = (1 << (end + 1)) - 1;
++      edata.data = edata.data << 4;
++      if (op & 0x08) edata.data |= 1 << 14;
++      edata.cmd = ARM_EXIDX_CMD_REG_POP;
++    }
++    else if (op == ARM_EXTBL_OP_FINISH) {
++      // Finish
++      edata.cmd = ARM_EXIDX_CMD_FINISH;
++      buf = end;
++    }
++    else if (op == 0xb1) {
++      uint8_t op2;
++      GET_BUF_U8(op2);
++      if (op2 == 0 || (op2 & 0xf0)) {
++        // Spare
++        edata.cmd = ARM_EXIDX_CMD_RESERVED;
++      } else {
++        // Pop integer registers under mask {r3,r2,r1,r0}
++        edata.cmd = ARM_EXIDX_CMD_REG_POP;
++        edata.data = op2 & 0x0f;
++      }
++    }
++    else if (op == 0xb2) {
++      // vsp = vsp + 0x204 + (uleb128 << 2)
++      uint64_t offset = 0;
++      uint8_t byte, shift = 0;
++      do {
++        GET_BUF_U8(byte);
++        offset |= (byte & 0x7f) << shift;
++        shift += 7;
++      } while ((byte & 0x80) && buf < end);
++      edata.data = offset * 4 + 0x204;
++      edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
++    }
++    else if (op == 0xb3 || op == 0xc8 || op == 0xc9) {
++      // b3: Pop VFP regs D[ssss]    to D[ssss+cccc],    FSTMFDX-ishly
++      // c8: Pop VFP regs D[16+ssss] to D[16+ssss+cccc], FSTMFDD-ishly
++      // c9: Pop VFP regs D[ssss]    to D[ssss+cccc],    FSTMFDD-ishly
++      edata.cmd = ARM_EXIDX_CMD_VFP_POP;
++      GET_BUF_U8(edata.data);
++      if (op == 0xc8) edata.data |= ARM_EXIDX_VFP_SHIFT_16;
++      if (op != 0xb3) edata.data |= ARM_EXIDX_VFP_FSTMD;
++    }
++    else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0) {
++      // b8: Pop VFP regs D[8] to D[8+nnn], FSTMFDX-ishly
++      // d0: Pop VFP regs D[8] to D[8+nnn], FSTMFDD-ishly
++      edata.cmd = ARM_EXIDX_CMD_VFP_POP;
++      edata.data = 0x80 | (op & 0x07);
++      if ((op & 0xf8) == 0xd0) edata.data |= ARM_EXIDX_VFP_FSTMD;
++    }
++    else if (op >= 0xc0 && op <= 0xc5) {
++      // Intel Wireless MMX pop wR[10]-wr[10+nnn], nnn != 6,7
++      edata.cmd = ARM_EXIDX_CMD_WREG_POP;
++      edata.data = 0xa0 | (op & 0x07);
++    }
++    else if (op == 0xc6) {
++      // Intel Wireless MMX pop wR[ssss] to wR[ssss+cccc]
++      edata.cmd = ARM_EXIDX_CMD_WREG_POP;
++      GET_BUF_U8(edata.data);
++    }
++    else if (op == 0xc7) {
++      uint8_t op2;
++      GET_BUF_U8(op2);
++      if (op2 == 0 || (op2 & 0xf0)) {
++        // Spare
++        edata.cmd = ARM_EXIDX_CMD_RESERVED;
++      } else {
++        // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}
++        edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
++        edata.data = op2 & 0x0f;
++      }
++    }
++    else {
++      // Spare
++      edata.cmd = ARM_EXIDX_CMD_RESERVED;
++    }
++
++    int ret = handler_->ImproveStackFrame(&edata);
++    if (ret < 0) return ret;
++  }
++  return 0;
++
++# undef GET_BUF_U8
++}
++
++void ExceptionTableInfo::Start()
++{
++  const struct exidx_entry* start
++    = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data());
++  const struct exidx_entry* end
++    = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data()
++                                                  + mr_exidx_.length());
++
++  // Iterate over each of the EXIDX entries (pairs of 32-bit words).
++  // These occupy the entire .exidx section.
++  for (const struct exidx_entry* entry = start; entry < end; ++entry) {
++
++    // Figure out the code address range that this table entry is
++    // associated with.
++    uint32_t addr = (reinterpret_cast<char*>(Prel31ToAddr(&entry->addr))
++                     - mapping_addr_ + loading_addr_) & 0x7fffffff;
++    uint32_t next_addr;
++    if (entry < end - 1)
++      next_addr = (reinterpret_cast<char*>(Prel31ToAddr(&((entry + 1)->addr)))
++                   - mapping_addr_ + loading_addr_) & 0x7fffffff;
++    else {
++      // This is the last EXIDX entry in the sequence, so we don't
++      // have an address for the start of the next function, to limit
++      // this one.  Instead use the address of the last byte of the
++      // text section associated with this .exidx section, that we
++      // have been given.  So as to avoid junking up the CFI unwind
++      // tables with absurdly large address ranges in the case where
++      // text_last_svma_ is wrong, only use the value if it is nonzero
++      // and within one page of |addr|.  Otherwise assume a length of 1.
++      //
++      // In some cases, gcc has been observed to finish the exidx
++      // section with an entry of length 1 marked CANT_UNWIND,
++      // presumably exactly for the purpose of giving a definite
++      // length for the last real entry, without having to look at
++      // text segment boundaries.
++      bool plausible = false;
++      next_addr = addr + 1;
++      if (text_last_svma_ != 0) {
++        uint32_t maybe_next_addr = text_last_svma_ + 1;
++        if (maybe_next_addr > addr && maybe_next_addr - addr <= 4096) {
++          next_addr = maybe_next_addr;
++          plausible = true;
++        }
++      }
++      if (!plausible)
++        BPLOG(INFO) << "ExceptionTableInfo: implausible EXIDX last entry size "
++                    << (int32_t)(text_last_svma_ - addr)
++                    << "; using 1 instead.";
++    }
++
++    // Extract the unwind info into |buf|.  This might fail for
++    // various reasons.  It involves reading both the .exidx and
++    // .extab sections.  All accesses to those sections are
++    // bounds-checked.
++    uint8_t buf[ARM_EXIDX_TABLE_LIMIT];
++    size_t buf_used = 0;
++    ExExtractResult res = ExtabEntryExtract(entry, buf, sizeof(buf), &buf_used);
++    if (res != ExSuccess) {
++      // Couldn't extract the unwind info, for some reason.  Move on.
++      switch (res) {
++        case ExInBufOverflow:
++          BPLOG(INFO) << "ExtabEntryExtract: .exidx/.extab section overrun";
++          break;
++        case ExOutBufOverflow:
++          BPLOG(INFO) << "ExtabEntryExtract: bytecode buffer overflow";
++          break;
++        case ExCantUnwind:
++          BPLOG(INFO) << "ExtabEntryExtract: function is marked CANT_UNWIND";
++          break;
++        case ExCantRepresent:
++          BPLOG(INFO) << "ExtabEntryExtract: bytecode can't be represented";
++          break;
++        case ExInvalid:
++          BPLOG(INFO) << "ExtabEntryExtract: index table entry is invalid";
++          break;
++        default:
++          BPLOG(INFO) << "ExtabEntryExtract: unknown error: " << (int)res;
++          break;
++      }
++      continue;
++    }
++
++    // Finally, work through the unwind instructions in |buf| and
++    // create CFI entries that Breakpad can use.  This can also fail.
++    // First, add a new stack frame entry, into which ExtabEntryDecode
++    // will write the CFI entries.
++    handler_->AddStackFrame(addr, next_addr - addr);
++    int ret = ExtabEntryDecode(buf, buf_used);
++    if (ret < 0) {
++      handler_->DeleteStackFrame();
++      BPLOG(INFO) << "ExtabEntryDecode: failed with error code: " << ret;
++      continue;
++    }
++    handler_->SubmitStackFrame();
++
++  } /* iterating over .exidx */
++}
++
++} // arm_ex_reader
+diff --git a/src/common/arm_ex_reader.h b/src/common/arm_ex_reader.h
+new file mode 100644
+--- /dev/null
++++ b/src/common/arm_ex_reader.h
+@@ -0,0 +1,115 @@
++
++/* libunwind - a platform-independent unwind library
++   Copyright 2011 Linaro Limited
++
++This file is part of libunwind.
++
++Permission is hereby granted, free of charge, to any person obtaining
++a copy of this software and associated documentation files (the
++"Software"), to deal in the Software without restriction, including
++without limitation the rights to use, copy, modify, merge, publish,
++distribute, sublicense, and/or sell copies of the Software, and to
++permit persons to whom the Software is furnished to do so, subject to
++the following conditions:
++
++The above copyright notice and this permission notice shall be
++included in all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
++
++// Copyright (c) 2010 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.
++
++
++// Derived from libunwind, with extensive modifications.
++
++#ifndef COMMON_ARM_EX_READER_H__
++#define COMMON_ARM_EX_READER_H__
++
++#include "common/arm_ex_to_module.h"
++#include "common/memory_range.h"
++
++namespace arm_ex_reader {
++
++// This class is a reader for ARM unwind information
++// from .ARM.exidx and .ARM.extab sections.
++class ExceptionTableInfo {
++ public:
++  ExceptionTableInfo(const char* exidx, size_t exidx_size,
++                     const char* extab, size_t extab_size,
++                     uint32_t text_last_svma,
++                     arm_ex_to_module::ARMExToModule* handler,
++                     const char* mapping_addr,
++                     uint32_t loading_addr)
++      : mr_exidx_(google_breakpad::MemoryRange(exidx, exidx_size)),
++        mr_extab_(google_breakpad::MemoryRange(extab, extab_size)),
++        text_last_svma_(text_last_svma),
++        handler_(handler), mapping_addr_(mapping_addr),
++        loading_addr_(loading_addr) { }
++
++  ~ExceptionTableInfo() { }
++
++  // Parses the entries in .ARM.exidx and possibly
++  // in .ARM.extab tables, reports what we find to
++  // arm_ex_to_module::ARMExToModule.
++  void Start();
++
++ private:
++  google_breakpad::MemoryRange mr_exidx_;
++  google_breakpad::MemoryRange mr_extab_;
++  uint32_t text_last_svma_;
++  arm_ex_to_module::ARMExToModule* handler_;
++  const char* mapping_addr_;
++  uint32_t loading_addr_;
++
++  enum ExExtractResult {
++    ExSuccess,        // success
++    ExInBufOverflow,  // out-of-range while reading .exidx
++    ExOutBufOverflow, // output buffer is too small
++    ExCantUnwind,     // this function is marked CANT_UNWIND
++    ExCantRepresent,  // entry valid, but we can't represent it
++    ExInvalid         // entry is invalid
++  };
++  ExExtractResult
++    ExtabEntryExtract(const struct arm_ex_to_module::exidx_entry* entry,
++                      uint8_t* buf, size_t buf_size,
++                      /*OUT*/size_t* buf_used);
++
++  int ExtabEntryDecode(const uint8_t* buf, size_t buf_size);
++};
++
++} // namespace arm_ex_reader
++
++#endif // COMMON_ARM_EX_READER_H__
+diff --git a/src/common/arm_ex_to_module.cc b/src/common/arm_ex_to_module.cc
+new file mode 100644
+--- /dev/null
++++ b/src/common/arm_ex_to_module.cc
+@@ -0,0 +1,206 @@
++
++/* libunwind - a platform-independent unwind library
++   Copyright 2011 Linaro Limited
++
++This file is part of libunwind.
++
++Permission is hereby granted, free of charge, to any person obtaining
++a copy of this software and associated documentation files (the
++"Software"), to deal in the Software without restriction, including
++without limitation the rights to use, copy, modify, merge, publish,
++distribute, sublicense, and/or sell copies of the Software, and to
++permit persons to whom the Software is furnished to do so, subject to
++the following conditions:
++
++The above copyright notice and this permission notice shall be
++included in all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
++
++// Copyright (c) 2010 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.
++
++
++// Derived from libunwind, with extensive modifications.
++
++#include "common/unique_string.h"
++#include "common/arm_ex_to_module.h"
++
++#include <stdio.h>
++#include <assert.h>
++
++// For big-picture comments on how the EXIDX reader works, 
++// see arm_ex_reader.cc.
++
++#define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f)
++#define ARM_EXBUF_COUNT(x) ((x) & 0x0f)
++#define ARM_EXBUF_END(x)   (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))
++
++using google_breakpad::ustr__pc;
++using google_breakpad::ustr__lr;
++using google_breakpad::ustr__sp;
++using google_breakpad::Module;
++using google_breakpad::ToUniqueString;
++using google_breakpad::UniqueString;
++
++namespace arm_ex_to_module {
++
++// Translate command from extab_data to command for Module.
++int ARMExToModule::TranslateCmd(const struct extab_data* edata,
++                                Module::StackFrameEntry* entry, string& vsp) {
++  int ret = 0;
++  switch (edata->cmd) {
++    case ARM_EXIDX_CMD_FINISH:
++      /* Copy LR to PC if there isn't currently a rule for PC in force. */
++      if (entry->initial_rules.find(ustr__pc())
++          == entry->initial_rules.end()) {
++        if (entry->initial_rules.find(ustr__lr())
++            == entry->initial_rules.end()) {
++          entry->initial_rules[ustr__pc()] = Module::Expr("lr");
++        } else {
++          entry->initial_rules[ustr__pc()] = entry->initial_rules[ustr__lr()];
++        }
++      }
++      break;
++    case ARM_EXIDX_CMD_SUB_FROM_VSP:
++      {
++        char c[16];
++        sprintf(c, " %d -", edata->data);
++        vsp += c;
++      }
++      break;
++    case ARM_EXIDX_CMD_ADD_TO_VSP:
++      {
++        char c[16];
++        sprintf(c, " %d +", edata->data);
++        vsp += c;
++      }
++      break;
++    case ARM_EXIDX_CMD_REG_POP:
++      for (unsigned int i = 0; i < 16; i++) {
++        if (edata->data & (1 << i)) {
++          entry->initial_rules[ToUniqueString(regnames[i])]
++            = Module::Expr(vsp + " ^");
++          vsp += " 4 +";
++        }
++      }
++      /* Set cfa in case the SP got popped. */
++      if (edata->data & (1 << 13)) {
++        Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()];
++        // It must be a postfix expression (we don't generate anything
++        // else here), so return -1 to fail out if it isn't.
++        if (!vsp_expr.isExprPostfix()) {
++          ret = -1;
++          break;
++        };
++        vsp = vsp_expr.getExprPostfix();
++      }
++      break;
++    case ARM_EXIDX_CMD_REG_TO_SP: {
++      assert (edata->data < 16);
++      const char* const regname = regnames[edata->data];
++      const UniqueString* regname_us = ToUniqueString(regname);
++      if (entry->initial_rules.find(regname_us) == entry->initial_rules.end()) {
++        entry->initial_rules[ustr__sp()] = Module::Expr(regname);
++      } else {
++        entry->initial_rules[ustr__sp()] = entry->initial_rules[regname_us];
++      }
++      Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()];
++      if (!vsp_expr.isExprPostfix()) {
++        ret = -1;
++        break;
++      };
++      vsp = vsp_expr.getExprPostfix();
++      break;
++    }
++    case ARM_EXIDX_CMD_VFP_POP:
++      /* Don't recover VFP registers, but be sure to adjust the stack
++         pointer. */
++      for (unsigned int i = ARM_EXBUF_START(edata->data);
++           i <= ARM_EXBUF_END(edata->data); i++) {
++        vsp += " 8 +";
++      }
++      if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) {
++        vsp += " 4 +";
++      }
++      break;
++    case ARM_EXIDX_CMD_WREG_POP:
++      for (unsigned int i = ARM_EXBUF_START(edata->data);
++           i <= ARM_EXBUF_END(edata->data); i++) {
++        vsp += " 8 +";
++      }
++      break;
++    case ARM_EXIDX_CMD_WCGR_POP:
++      // Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4"
++      for (unsigned int i = 0; i < 4; i++) {
++        if (edata->data & (1 << i)) {
++          vsp += " 4 +";
++        }
++      }
++      break;
++    case ARM_EXIDX_CMD_REFUSED:
++    case ARM_EXIDX_CMD_RESERVED:
++      ret = -1;
++      break;
++  }
++  return ret;
++}
++
++void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) {
++  stack_frame_entry_ = new Module::StackFrameEntry;
++  stack_frame_entry_->address = addr;
++  stack_frame_entry_->size = size;
++  stack_frame_entry_->initial_rules[ToUniqueString(kCFA)] = Module::Expr("sp");
++  vsp_ = "sp";
++}
++
++int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) {
++  return TranslateCmd(edata, stack_frame_entry_, vsp_) ;
++}
++
++void ARMExToModule::DeleteStackFrame() {
++  delete stack_frame_entry_;
++}
++
++void ARMExToModule::SubmitStackFrame() {
++  // return address always winds up in pc
++  stack_frame_entry_->initial_rules[ToUniqueString(kRA)]
++    = stack_frame_entry_->initial_rules[ustr__pc()];
++  // the final value of vsp is the new value of sp
++  stack_frame_entry_->initial_rules[ustr__sp()] = vsp_;
++  module_->AddStackFrameEntry(stack_frame_entry_);
++}
++
++} // namespace arm_ex_to_module
+diff --git a/src/common/arm_ex_to_module.h b/src/common/arm_ex_to_module.h
+new file mode 100644
+--- /dev/null
++++ b/src/common/arm_ex_to_module.h
+@@ -0,0 +1,129 @@
++
++/* libunwind - a platform-independent unwind library
++   Copyright 2011 Linaro Limited
++
++This file is part of libunwind.
++
++Permission is hereby granted, free of charge, to any person obtaining
++a copy of this software and associated documentation files (the
++"Software"), to deal in the Software without restriction, including
++without limitation the rights to use, copy, modify, merge, publish,
++distribute, sublicense, and/or sell copies of the Software, and to
++permit persons to whom the Software is furnished to do so, subject to
++the following conditions:
++
++The above copyright notice and this permission notice shall be
++included in all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
++
++// Copyright (c) 2010 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.
++
++
++// Derived from libunwind, with extensive modifications.
++
++#ifndef COMMON_ARM_EX_TO_MODULE__
++#define COMMON_ARM_EX_TO_MODULE__
++
++#include "common/module.h"
++
++#include <string.h>
++
++namespace arm_ex_to_module {
++
++using google_breakpad::Module;
++
++typedef enum extab_cmd {
++  ARM_EXIDX_CMD_FINISH,
++  ARM_EXIDX_CMD_SUB_FROM_VSP,
++  ARM_EXIDX_CMD_ADD_TO_VSP,
++  ARM_EXIDX_CMD_REG_POP,
++  ARM_EXIDX_CMD_REG_TO_SP,
++  ARM_EXIDX_CMD_VFP_POP,
++  ARM_EXIDX_CMD_WREG_POP,
++  ARM_EXIDX_CMD_WCGR_POP,
++  ARM_EXIDX_CMD_RESERVED,
++  ARM_EXIDX_CMD_REFUSED,
++} extab_cmd_t;
++
++struct exidx_entry {
++  uint32_t addr;
++  uint32_t data;
++};
++
++struct extab_data {
++  extab_cmd_t cmd;
++  uint32_t data;
++};
++
++enum extab_cmd_flags {
++  ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
++  ARM_EXIDX_VFP_FSTMD = 1 << 17, // distinguishes FSTMxxD from FSTMxxX
++};
++
++const string kRA = ".ra";
++const string kCFA = ".cfa";
++
++static const char* const regnames[] = {
++ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
++ "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
++ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
++ "fps", "cpsr"
++};
++
++// Receives information from arm_ex_reader::ExceptionTableInfo
++// and adds it to the Module object
++class ARMExToModule {
++ public:
++  ARMExToModule(Module* module)
++      : module_(module) { }
++  ~ARMExToModule() { }
++  void AddStackFrame(uintptr_t addr, size_t size);
++  int ImproveStackFrame(const struct extab_data* edata);
++  void DeleteStackFrame();
++  void SubmitStackFrame();
++ private:
++  Module* module_;
++  Module::StackFrameEntry* stack_frame_entry_;
++  string vsp_;
++  int TranslateCmd(const struct extab_data* edata,
++                   Module::StackFrameEntry* entry,
++                   string& vsp);
++};
++
++} // namespace arm_ex_to_module
++
++#endif // COMMON_ARM_EX_TO_MODULE__
+diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc
+--- a/src/common/linux/dump_symbols.cc
++++ b/src/common/linux/dump_symbols.cc
+@@ -47,16 +47,17 @@
+ #include <unistd.h>
+ 
+ #include <iostream>
+ #include <set>
+ #include <string>
+ #include <utility>
+ #include <vector>
+ 
++#include "common/arm_ex_reader.h"
+ #include "common/dwarf/bytereader-inl.h"
+ #include "common/dwarf/dwarf2diehandler.h"
+ #include "common/dwarf_cfi_to_module.h"
+ #include "common/dwarf_cu_to_module.h"
+ #include "common/dwarf_line_to_module.h"
+ #include "common/linux/elfutils.h"
+ #include "common/linux/elfutils-inl.h"
+ #include "common/linux/elf_symbols_to_module.h"
+@@ -65,16 +66,20 @@
+ #include "common/scoped_ptr.h"
+ #ifndef NO_STABS_SUPPORT
+ #include "common/stabs_reader.h"
+ #include "common/stabs_to_module.h"
+ #endif
+ #include "common/using_std_string.h"
+ #include "common/logging.h"
+ 
++#if defined(__ANDROID__) && !defined(SHT_ARM_EXIDX)
++# define SHT_ARM_EXIDX (SHT_LOPROC + 1)
++#endif
++
+ // This namespace contains helper functions.
+ namespace {
+ 
+ using google_breakpad::DwarfCFIToModule;
+ using google_breakpad::DwarfCUToModule;
+ using google_breakpad::DwarfLineToModule;
+ using google_breakpad::ElfClass;
+ using google_breakpad::ElfClass32;
+@@ -340,16 +345,62 @@ bool LoadDwarfCFI(const string& dwarf_fi
+                                                        section_name);
+   dwarf2reader::CallFrameInfo parser(cfi, cfi_size,
+                                      &byte_reader, &handler, &dwarf_reporter,
+                                      eh_frame);
+   parser.Start();
+   return true;
+ }
+ 
++template<typename ElfClass>
++bool LoadARMexidx(const typename ElfClass::Ehdr* elf_header,
++                  const typename ElfClass::Shdr* exidx_section,
++                  const typename ElfClass::Shdr* extab_section,
++                  uint32_t loading_addr,
++                  Module* module) {
++  // To do this properly we need to know:
++  // * the bounds of the .ARM.exidx section in the mapped image
++  // * the bounds of the .ARM.extab section in the mapped image
++  // * the vma of the last byte in the text section associated with the .exidx
++  // The first two are easy.  The third is a bit tricky.  If we can't
++  // figure out what it is, just pass in zero.
++  const char *exidx_img
++    = GetOffset<ElfClass, char>(elf_header, exidx_section->sh_offset);
++  size_t exidx_size = exidx_section->sh_size;
++  const char *extab_img
++    = GetOffset<ElfClass, char>(elf_header, extab_section->sh_offset);
++  size_t extab_size = extab_section->sh_size;
++
++  // The sh_link field of the exidx section gives the section number
++  // for the associated text section.
++  uint32_t exidx_text_last_svma = 0;
++  int exidx_text_sno = exidx_section->sh_link;
++  typedef typename ElfClass::Shdr Shdr;
++  // |sections| points to the section header table
++  const Shdr* sections
++    = GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
++  const int num_sections = elf_header->e_shnum;
++  if (exidx_text_sno >= 0 && exidx_text_sno < num_sections) {
++    const Shdr* exidx_text_shdr = &sections[exidx_text_sno];
++    if (exidx_text_shdr->sh_size > 0) {
++      exidx_text_last_svma
++        = exidx_text_shdr->sh_addr + exidx_text_shdr->sh_size - 1;
++    }
++  }
++
++  arm_ex_to_module::ARMExToModule handler(module);
++  arm_ex_reader::ExceptionTableInfo
++    parser(exidx_img, exidx_size, extab_img, extab_size, exidx_text_last_svma,
++           &handler,
++           reinterpret_cast<const char*>(elf_header),
++           loading_addr);
++  parser.Start();
++  return true;
++}
++
+ bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper,
+              void** elf_header) {
+   int obj_fd = open(obj_file.c_str(), O_RDONLY);
+   if (obj_fd < 0) {
+     fprintf(stderr, "Failed to open ELF file '%s': %s\n",
+             obj_file.c_str(), strerror(errno));
+     return false;
+   }
+@@ -629,16 +680,39 @@ bool LoadSymbols(const string& obj_file,
+                                  eh_frame_section, true,
+                                  got_section, text_section, big_endian, module);
+       found_usable_info = found_usable_info || result;
+       if (result)
+         BPLOG(INFO) << "LoadSymbols:   read CFI from .eh_frame";
+     }
+   }
+ 
++  // ARM has special unwind tables that can be used.
++  const Shdr* arm_exidx_section =
++      FindElfSectionByName<ElfClass>(".ARM.exidx", SHT_ARM_EXIDX,
++                                     sections, names, names_end,
++                                     elf_header->e_shnum);
++  const Shdr* arm_extab_section =
++      FindElfSectionByName<ElfClass>(".ARM.extab", SHT_PROGBITS,
++                                     sections, names, names_end,
++                                     elf_header->e_shnum);
++  // Only load information from this section if there isn't a .debug_info
++  // section.
++  if (!found_debug_info_section
++      && arm_exidx_section && arm_extab_section && symbol_data != NO_CFI) {
++    info->LoadedSection(".ARM.exidx");
++    info->LoadedSection(".ARM.extab");
++    bool result = LoadARMexidx<ElfClass>(elf_header,
++                                         arm_exidx_section, arm_extab_section,
++                                         loading_addr, module);
++    found_usable_info = found_usable_info || result;
++    if (result)
++      BPLOG(INFO) << "LoadSymbols:   read EXIDX from .ARM.{exidx,extab}";
++  }
++
+   if (!found_debug_info_section && symbol_data != ONLY_CFI) {
+     fprintf(stderr, "%s: file contains no debugging information"
+             " (no \".stab\" or \".debug_info\" sections)\n",
+             obj_file.c_str());
+ 
+     // Failed, but maybe there's a .gnu_debuglink section?
+     if (read_gnu_debug_link) {
+       const Shdr* gnu_debuglink_section
+diff --git a/src/common/module.cc b/src/common/module.cc
+--- a/src/common/module.cc
++++ b/src/common/module.cc
+@@ -253,17 +253,17 @@ void Module::AssignSourceIds() {
+ 
+ bool Module::ReportError() {
+   fprintf(stderr, "error writing symbol file: %s\n",
+           strerror(errno));
+   return false;
+ }
+ 
+ std::ostream& operator<<(std::ostream& stream, const Module::Expr& expr) {
+-  assert(!expr.invalid());
++  assert(!expr.isExprInvalid());
+   switch (expr.how_) {
+     case Module::kExprSimple:
+       stream << FromUniqueString(expr.ident_) << " " << expr.offset_ << " +";
+       break;
+     case Module::kExprSimpleMem:
+       stream << FromUniqueString(expr.ident_) << " " << expr.offset_ << " + ^";
+       break;
+     case Module::kExprPostfix:
+diff --git a/src/common/module.h b/src/common/module.h
+--- a/src/common/module.h
++++ b/src/common/module.h
+@@ -160,17 +160,24 @@ class Module {
+     }
+     // Construct an invalid expression
+     Expr() {
+       postfix_ = "";
+       ident_ = NULL;
+       offset_ = 0;
+       how_ = kExprInvalid;
+     }
+-    bool invalid() const { return how_ == kExprInvalid; }
++    bool isExprInvalid() const { return how_ == kExprInvalid; }
++    bool isExprPostfix() const { return how_ == kExprPostfix; }
++
++    // Return the postfix expression string.  This is only
++    // meaningful on Exprs for which isExprPostfix returns true.
++    // In all other cases it returns an empty string.
++    string getExprPostfix() const { return postfix_; }
++
+     bool operator==(const Expr& other) const {
+       return how_ == other.how_ &&
+           ident_ == other.ident_ &&
+           offset_ == other.offset_ &&
+           postfix_ == other.postfix_;
+     }
+ 
+     // The identifier that gives the starting value for simple expressions.
+diff --git a/src/common/unique_string.h b/src/common/unique_string.h
+--- a/src/common/unique_string.h
++++ b/src/common/unique_string.h
+@@ -230,16 +230,37 @@ inline static const UniqueString* ustr__
+ 
+ // ".ra"
+ inline static const UniqueString* ustr__ZDra() {
+   static const UniqueString* us = NULL;
+   if (!us) us = ToUniqueString(".ra");
+   return us;
+ }
+ 
++// "pc"
++inline static const UniqueString* ustr__pc() {
++  static const UniqueString* us = NULL;
++  if (!us) us = ToUniqueString("pc");
++  return us;
++}
++
++// "lr"
++inline static const UniqueString* ustr__lr() {
++  static const UniqueString* us = NULL;
++  if (!us) us = ToUniqueString("lr");
++  return us;
++}
++
++// "sp"
++inline static const UniqueString* ustr__sp() {
++  static const UniqueString* us = NULL;
++  if (!us) us = ToUniqueString("sp");
++  return us;
++}
++
+ template <typename ValueType>
+ class UniqueStringMap
+ {
+  private:
+   static const int N_FIXED = 10;
+ 
+  public:
+   UniqueStringMap() : n_fixed_(0), n_sets_(0), n_gets_(0), n_clears_(0) {};
+diff --git a/src/processor/cfi_frame_info.cc b/src/processor/cfi_frame_info.cc
+--- a/src/processor/cfi_frame_info.cc
++++ b/src/processor/cfi_frame_info.cc
+@@ -49,17 +49,17 @@ namespace google_breakpad {
+ #endif
+ 
+ template<typename V>
+ bool CFIFrameInfo::FindCallerRegs(const RegisterValueMap<V> &registers,
+                                   const MemoryRegion &memory,
+                                   RegisterValueMap<V> *caller_registers) const {
+   // If there are not rules for both .ra and .cfa in effect at this address,
+   // don't use this CFI data for stack walking.
+-  if (cfa_rule_.invalid() || ra_rule_.invalid())
++  if (cfa_rule_.isExprInvalid() || ra_rule_.isExprInvalid())
+     return false;
+ 
+   RegisterValueMap<V> working;
+   PostfixEvaluator<V> evaluator(&working, &memory);
+ 
+   caller_registers->clear();
+ 
+   // First, compute the CFA.
+@@ -100,20 +100,20 @@ template bool CFIFrameInfo::FindCallerRe
+ template bool CFIFrameInfo::FindCallerRegs<uint64_t>(
+     const RegisterValueMap<uint64_t> &registers,
+     const MemoryRegion &memory,
+     RegisterValueMap<uint64_t> *caller_registers) const;
+ 
+ string CFIFrameInfo::Serialize() const {
+   std::ostringstream stream;
+ 
+-  if (!cfa_rule_.invalid()) {
++  if (!cfa_rule_.isExprInvalid()) {
+     stream << ".cfa: " << cfa_rule_;
+   }
+-  if (!ra_rule_.invalid()) {
++  if (!ra_rule_.isExprInvalid()) {
+     if (static_cast<std::streamoff>(stream.tellp()) != 0)
+       stream << " ";
+     stream << ".ra: " << ra_rule_;
+   }
+ 
+   // Visit the register rules in alphabetical order.  Because
+   // register_rules_ has the elements in some arbitrary order,
+   // get the names out into a vector, sort them, and visit in
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-patches/13-bug836829.patch
@@ -0,0 +1,38 @@
+# HG changeset patch
+# User Georg Fritzsche <georg.fritzsche@googlemail.com>
+# Date 1366630152 -7200
+#      Mon Apr 22 13:29:12 2013 +0200
+# Node ID 11f7a9321b7d5d85eddc2db16e58e6870a7c4e06
+# Parent  e74de3db7dd27ffda8f4772f892cfb52c5c35649
+Bug 836829 - Fix missing result check in Mac exception handler. r=ted
+
+diff --git a/src/client/mac/handler/exception_handler.cc b/src/client/mac/handler/exception_handler.cc
+--- a/src/client/mac/handler/exception_handler.cc
++++ b/src/client/mac/handler/exception_handler.cc
+@@ -276,19 +276,23 @@ bool ExceptionHandler::WriteMinidump(boo
+ 
+   use_minidump_write_mutex_ = true;
+   last_minidump_write_result_ = false;
+ 
+   // Lock the mutex.  Since we just created it, this will return immediately.
+   if (pthread_mutex_lock(&minidump_write_mutex_) == 0) {
+     // Send an empty message to the handle port so that a minidump will
+     // be written
+-    SendMessageToHandlerThread(write_exception_stream ?
+-                                   kWriteDumpWithExceptionMessage :
+-                                   kWriteDumpMessage);
++    bool result = SendMessageToHandlerThread(write_exception_stream ?
++                                             kWriteDumpWithExceptionMessage :
++                                             kWriteDumpMessage);
++    if (!result) {
++      pthread_mutex_unlock(&minidump_write_mutex_);
++      return false;
++    }
+ 
+     // Wait for the minidump writer to complete its writing.  It will unlock
+     // the mutex when completed
+     pthread_mutex_lock(&minidump_write_mutex_);
+   }
+ 
+   use_minidump_write_mutex_ = false;
+   UpdateNextID();
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-patches/14-bug883126.patch
@@ -0,0 +1,346 @@
+# HG changeset patch
+# User Julian Seward <jseward@acm.org>
+# Date 1372168568 -7200
+#      Tue Jun 25 15:56:08 2013 +0200
+# Node ID 6d06a09b3f5624dd833bd6f905bfd88e3fdec00a
+# Parent  11f7a9321b7d5d85eddc2db16e58e6870a7c4e06
+Bug 883126 - Improve performance of EXIDX unwinding in Breakpad.  r=ted
+
+diff --git a/src/common/arm_ex_to_module.cc b/src/common/arm_ex_to_module.cc
+--- a/src/common/arm_ex_to_module.cc
++++ b/src/common/arm_ex_to_module.cc
+@@ -66,141 +66,126 @@ WITH THE SOFTWARE OR THE USE OR OTHER DE
+ 
+ #define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f)
+ #define ARM_EXBUF_COUNT(x) ((x) & 0x0f)
+ #define ARM_EXBUF_END(x)   (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))
+ 
+ using google_breakpad::ustr__pc;
+ using google_breakpad::ustr__lr;
+ using google_breakpad::ustr__sp;
++using google_breakpad::ustr__ZDra;
++using google_breakpad::ustr__ZDcfa;
+ using google_breakpad::Module;
+ using google_breakpad::ToUniqueString;
+ using google_breakpad::UniqueString;
+ 
+ namespace arm_ex_to_module {
+ 
+ // Translate command from extab_data to command for Module.
+ int ARMExToModule::TranslateCmd(const struct extab_data* edata,
+-                                Module::StackFrameEntry* entry, string& vsp) {
++                                Module::StackFrameEntry* entry,
++                                Module::Expr& vsp) {
+   int ret = 0;
+   switch (edata->cmd) {
+     case ARM_EXIDX_CMD_FINISH:
+       /* Copy LR to PC if there isn't currently a rule for PC in force. */
+       if (entry->initial_rules.find(ustr__pc())
+           == entry->initial_rules.end()) {
+         if (entry->initial_rules.find(ustr__lr())
+             == entry->initial_rules.end()) {
+-          entry->initial_rules[ustr__pc()] = Module::Expr("lr");
++          entry->initial_rules[ustr__pc()] = Module::Expr(ustr__lr(),
++                                                          0, false); // "lr"
+         } else {
+           entry->initial_rules[ustr__pc()] = entry->initial_rules[ustr__lr()];
+         }
+       }
+       break;
+     case ARM_EXIDX_CMD_SUB_FROM_VSP:
+-      {
+-        char c[16];
+-        sprintf(c, " %d -", edata->data);
+-        vsp += c;
+-      }
++      vsp = vsp.add_delta(- static_cast<long>(edata->data));
+       break;
+     case ARM_EXIDX_CMD_ADD_TO_VSP:
+-      {
+-        char c[16];
+-        sprintf(c, " %d +", edata->data);
+-        vsp += c;
+-      }
++      vsp = vsp.add_delta(static_cast<long>(edata->data));
+       break;
+     case ARM_EXIDX_CMD_REG_POP:
+       for (unsigned int i = 0; i < 16; i++) {
+         if (edata->data & (1 << i)) {
+-          entry->initial_rules[ToUniqueString(regnames[i])]
+-            = Module::Expr(vsp + " ^");
+-          vsp += " 4 +";
++          entry->initial_rules[ToUniqueString(regnames[i])] = vsp.deref();
++          vsp = vsp.add_delta(4);
+         }
+       }
+       /* Set cfa in case the SP got popped. */
+       if (edata->data & (1 << 13)) {
+-        Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()];
+-        // It must be a postfix expression (we don't generate anything
+-        // else here), so return -1 to fail out if it isn't.
+-        if (!vsp_expr.isExprPostfix()) {
+-          ret = -1;
+-          break;
+-        };
+-        vsp = vsp_expr.getExprPostfix();
++        vsp = entry->initial_rules[ustr__sp()];
+       }
+       break;
+     case ARM_EXIDX_CMD_REG_TO_SP: {
+       assert (edata->data < 16);
+       const char* const regname = regnames[edata->data];
+       const UniqueString* regname_us = ToUniqueString(regname);
+       if (entry->initial_rules.find(regname_us) == entry->initial_rules.end()) {
+-        entry->initial_rules[ustr__sp()] = Module::Expr(regname);
++        entry->initial_rules[ustr__sp()] = Module::Expr(regname_us,
++                                                        0, false); // "regname"
+       } else {
+         entry->initial_rules[ustr__sp()] = entry->initial_rules[regname_us];
+       }
+-      Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()];
+-      if (!vsp_expr.isExprPostfix()) {
+-        ret = -1;
+-        break;
+-      };
+-      vsp = vsp_expr.getExprPostfix();
++      vsp = entry->initial_rules[ustr__sp()];
+       break;
+     }
+     case ARM_EXIDX_CMD_VFP_POP:
+       /* Don't recover VFP registers, but be sure to adjust the stack
+          pointer. */
+       for (unsigned int i = ARM_EXBUF_START(edata->data);
+            i <= ARM_EXBUF_END(edata->data); i++) {
+-        vsp += " 8 +";
++        vsp = vsp.add_delta(8);
+       }
+       if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) {
+-        vsp += " 4 +";
++        vsp = vsp.add_delta(4);
+       }
+       break;
+     case ARM_EXIDX_CMD_WREG_POP:
+       for (unsigned int i = ARM_EXBUF_START(edata->data);
+            i <= ARM_EXBUF_END(edata->data); i++) {
+-        vsp += " 8 +";
++        vsp = vsp.add_delta(8);
+       }
+       break;
+     case ARM_EXIDX_CMD_WCGR_POP:
+       // Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4"
+       for (unsigned int i = 0; i < 4; i++) {
+         if (edata->data & (1 << i)) {
+-          vsp += " 4 +";
++          vsp = vsp.add_delta(4);
+         }
+       }
+       break;
+     case ARM_EXIDX_CMD_REFUSED:
+     case ARM_EXIDX_CMD_RESERVED:
+       ret = -1;
+       break;
+   }
+   return ret;
+ }
+ 
+ void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) {
+   stack_frame_entry_ = new Module::StackFrameEntry;
+   stack_frame_entry_->address = addr;
+   stack_frame_entry_->size = size;
+-  stack_frame_entry_->initial_rules[ToUniqueString(kCFA)] = Module::Expr("sp");
+-  vsp_ = "sp";
++  Module::Expr sp_expr = Module::Expr(ustr__sp(), 0, false); // "sp"
++  stack_frame_entry_->initial_rules[ustr__ZDcfa()] = sp_expr; // ".cfa"
++  vsp_ = sp_expr;
+ }
+ 
+ int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) {
+   return TranslateCmd(edata, stack_frame_entry_, vsp_) ;
+ }
+ 
+ void ARMExToModule::DeleteStackFrame() {
+   delete stack_frame_entry_;
+ }
+ 
+ void ARMExToModule::SubmitStackFrame() {
+   // return address always winds up in pc
+-  stack_frame_entry_->initial_rules[ToUniqueString(kRA)]
++  stack_frame_entry_->initial_rules[ustr__ZDra()] // ".ra"
+     = stack_frame_entry_->initial_rules[ustr__pc()];
+   // the final value of vsp is the new value of sp
+   stack_frame_entry_->initial_rules[ustr__sp()] = vsp_;
+   module_->AddStackFrameEntry(stack_frame_entry_);
+ }
+ 
+ } // namespace arm_ex_to_module
+diff --git a/src/common/arm_ex_to_module.h b/src/common/arm_ex_to_module.h
+--- a/src/common/arm_ex_to_module.h
++++ b/src/common/arm_ex_to_module.h
+@@ -89,19 +89,16 @@ struct extab_data {
+   uint32_t data;
+ };
+ 
+ enum extab_cmd_flags {
+   ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
+   ARM_EXIDX_VFP_FSTMD = 1 << 17, // distinguishes FSTMxxD from FSTMxxX
+ };
+ 
+-const string kRA = ".ra";
+-const string kCFA = ".cfa";
+-
+ static const char* const regnames[] = {
+  "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+  "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
+  "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+  "fps", "cpsr"
+ };
+ 
+ // Receives information from arm_ex_reader::ExceptionTableInfo
+@@ -113,17 +110,17 @@ class ARMExToModule {
+   ~ARMExToModule() { }
+   void AddStackFrame(uintptr_t addr, size_t size);
+   int ImproveStackFrame(const struct extab_data* edata);
+   void DeleteStackFrame();
+   void SubmitStackFrame();
+  private:
+   Module* module_;
+   Module::StackFrameEntry* stack_frame_entry_;
+-  string vsp_;
++  Module::Expr vsp_;
+   int TranslateCmd(const struct extab_data* edata,
+                    Module::StackFrameEntry* entry,
+-                   string& vsp);
++                   Module::Expr& vsp);
+ };
+ 
+ } // namespace arm_ex_to_module
+ 
+ #endif // COMMON_ARM_EX_TO_MODULE__
+diff --git a/src/common/module.h b/src/common/module.h
+--- a/src/common/module.h
++++ b/src/common/module.h
+@@ -39,16 +39,20 @@
+ #define COMMON_LINUX_MODULE_H__
+ 
+ #include <iostream>
+ #include <map>
+ #include <set>
+ #include <string>
+ #include <vector>
+ 
++#include <assert.h>
++#include <stdlib.h>
++#include <stdio.h>
++
+ #include "common/symbol_data.h"
+ #include "common/using_std_string.h"
+ #include "common/unique_string.h"
+ #include "google_breakpad/common/breakpad_types.h"
+ 
+ namespace google_breakpad {
+ 
+ using std::set;
+@@ -161,30 +165,98 @@ class Module {
+     // Construct an invalid expression
+     Expr() {
+       postfix_ = "";
+       ident_ = NULL;
+       offset_ = 0;
+       how_ = kExprInvalid;
+     }
+     bool isExprInvalid() const { return how_ == kExprInvalid; }
+-    bool isExprPostfix() const { return how_ == kExprPostfix; }
+ 
+-    // Return the postfix expression string.  This is only
+-    // meaningful on Exprs for which isExprPostfix returns true.
+-    // In all other cases it returns an empty string.
+-    string getExprPostfix() const { return postfix_; }
++    // Return the postfix expression string, either directly,
++    // if this is a postfix expression, or by synthesising it
++    // for a simple expression.
++    string getExprPostfix() const {
++      switch (how_) {
++        case kExprPostfix:
++          return postfix_;
++        case kExprSimple:
++        case kExprSimpleMem: {
++          char buf[40];
++          sprintf(buf, " %ld %c%s", labs(offset_), offset_ < 0 ? '-' : '+',
++                                    how_ == kExprSimple ? "" : " ^");
++          return string(FromUniqueString(ident_)) + string(buf);
++        }
++        case kExprInvalid:
++        default:
++          assert(0 && "getExprPostfix: invalid Module::Expr type");
++          return "Expr::genExprPostfix: kExprInvalid";
++      }
++    }
+ 
+     bool operator==(const Expr& other) const {
+       return how_ == other.how_ &&
+           ident_ == other.ident_ &&
+           offset_ == other.offset_ &&
+           postfix_ == other.postfix_;
+     }
+ 
++    // Returns an Expr which evaluates to |this| + |delta|
++    Expr add_delta(long delta) {
++      if (delta == 0) {
++        return *this;
++      }
++      // If it's a simple form expression of the form "identifier + offset",
++      // simply add |delta| on to |offset|.  In the other two possible
++      // cases:
++      //    *(identifier + offset)
++      //    completely arbitrary postfix expression string
++      // the only option is to "downgrade" it to a postfix expression and add
++      // "+/- delta" at the end of the string, since the result can't be
++      // represented in the simple form.
++      switch (how_) {
++        case kExprSimpleMem:
++        case kExprPostfix: {
++          char buf[40];
++          sprintf(buf, " %ld %c", labs(delta), delta < 0 ? '-' : '+');
++          return Expr(getExprPostfix() + string(buf));
++        }
++        case kExprSimple:
++          return Expr(ident_, offset_ + delta, false);
++        case kExprInvalid:
++        default:
++          assert(0 && "add_delta: invalid Module::Expr type");
++          // Invalid inputs produce an invalid result
++          return Expr();
++      }
++    }
++
++    // Returns an Expr which evaluates to *|this|
++    Expr deref() {
++      // In the simplest case, a kExprSimple can be changed into a
++      // kExprSimpleMem.  In all other cases it has to be dumped as a
++      // postfix string, and " ^" added at the end.
++      switch (how_) {
++        case kExprSimple: {
++          Expr t = *this;
++          t.how_ = kExprSimpleMem;
++          return t;
++        }
++        case kExprSimpleMem:
++        case kExprPostfix: {
++          return Expr(getExprPostfix() + " ^");
++        }
++        case kExprInvalid:
++        default:
++          assert(0 && "deref: invalid Module::Expr type");
++          // Invalid inputs produce an invalid result
++          return Expr();
++      }
++    }
++
+     // The identifier that gives the starting value for simple expressions.
+     const UniqueString* ident_;
+     // The offset to add for simple expressions.
+     long    offset_;
+     // The Postfix expression string to evaluate for non-simple expressions.
+     string  postfix_;
+     // The operation expressed by this expression.
+     ExprHow how_;
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-patches/15-bug859745.patch
@@ -0,0 +1,96 @@
+# HG changeset patch
+# User Julian Seward <jseward@acm.org>
+# Date 1366643454 -7200
+#      Mon Apr 22 17:10:54 2013 +0200
+# Node ID 3e64f12d9dab619c90bee02ed071bcda0100844e
+# Parent  6d06a09b3f5624dd833bd6f905bfd88e3fdec00a
+Bug 859745 - Install sane unwinding limit for SPS/breakpad.  r=ted
+
+diff --git a/src/google_breakpad/processor/stackwalker.h b/src/google_breakpad/processor/stackwalker.h
+--- a/src/google_breakpad/processor/stackwalker.h
++++ b/src/google_breakpad/processor/stackwalker.h
+@@ -83,17 +83,20 @@ class Stackwalker {
+   // argument.  If no suitable concrete subclass exists, returns NULL.
+   static Stackwalker* StackwalkerForCPU(
+      const SystemInfo* system_info,
+      MinidumpContext* context,
+      MemoryRegion* memory,
+      const CodeModules* modules,
+      StackFrameSymbolizer* resolver_helper);
+ 
+-  static void set_max_frames(uint32_t max_frames) { max_frames_ = max_frames; }
++  static void set_max_frames(uint32_t max_frames) {
++    max_frames_ = max_frames;
++    max_frames_set_ = true;
++  }
+   static uint32_t max_frames() { return max_frames_; }
+ 
+  protected:
+   // system_info identifies the operating system, NULL or empty if unknown.
+   // memory identifies a MemoryRegion that provides the stack memory
+   // for the stack to walk.  modules, if non-NULL, is a CodeModules
+   // object that is used to look up which code module each stack frame is
+   // associated with.  frame_symbolizer is a StackFrameSymbolizer object that
+@@ -191,14 +194,19 @@ class Stackwalker {
+   // the end of the stack has been reached).  GetCallerFrame allocates a new
+   // StackFrame (or StackFrame subclass), ownership of which is taken by
+   // the caller.
+   virtual StackFrame* GetCallerFrame(const CallStack* stack) = 0;
+ 
+   // The maximum number of frames Stackwalker will walk through.
+   // This defaults to 1024 to prevent infinite loops.
+   static uint32_t max_frames_;
++
++  // Keep track of whether max_frames_ has been set by the user, since
++  // it affects whether or not an error message is printed in the case
++  // where an unwind got stopped by the limit.
++  static bool max_frames_set_;
+ };
+ 
+ }  // namespace google_breakpad
+ 
+ 
+ #endif  // GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__
+diff --git a/src/processor/stackwalker.cc b/src/processor/stackwalker.cc
+--- a/src/processor/stackwalker.cc
++++ b/src/processor/stackwalker.cc
+@@ -52,16 +52,17 @@
+ #include "processor/stackwalker_x86.h"
+ #include "processor/stackwalker_amd64.h"
+ #include "processor/stackwalker_arm.h"
+ 
+ namespace google_breakpad {
+ 
+ const int Stackwalker::kRASearchWords = 30;
+ uint32_t Stackwalker::max_frames_ = 1024;
++bool Stackwalker::max_frames_set_ = false;
+ 
+ Stackwalker::Stackwalker(const SystemInfo* system_info,
+                          MemoryRegion* memory,
+                          const CodeModules* modules,
+                          StackFrameSymbolizer* frame_symbolizer)
+     : system_info_(system_info),
+       memory_(memory),
+       modules_(modules),
+@@ -120,17 +121,20 @@ bool Stackwalker::Walk(CallStack* stack,
+         modules_without_symbols->push_back(frame->module);
+       }
+     }
+ 
+     // Add the frame to the call stack.  Relinquish the ownership claim
+     // over the frame, because the stack now owns it.
+     stack->frames_.push_back(frame.release());
+     if (stack->frames_.size() > max_frames_) {
+-      BPLOG(ERROR) << "The stack is over " << max_frames_ << " frames.";
++      // Only emit an error message in the case where the limit that we
++      // reached is the default limit, not set by the user.
++      if (!max_frames_set_)
++        BPLOG(ERROR) << "The stack is over " << max_frames_ << " frames.";
+       break;
+     }
+ 
+     // Get the next frame and take ownership.
+     frame.reset(GetCallerFrame(stack));
+   }
+ 
+   return true;
rename from toolkit/crashreporter/breakpad-patches/12-sht-arm-exidx-define.patch
rename to toolkit/crashreporter/breakpad-patches/16-sht-arm-exidx-define.patch
--- a/toolkit/crashreporter/google-breakpad/Makefile.am
+++ b/toolkit/crashreporter/google-breakpad/Makefile.am
@@ -428,18 +428,16 @@ src_tools_linux_dump_syms_dump_syms_SOUR
 	src/common/dwarf_line_to_module.cc \
 	src/common/language.cc \
 	src/common/module.cc \
 	src/common/stabs_reader.cc \
 	src/common/stabs_to_module.cc \
 	src/common/dwarf/bytereader.cc \
 	src/common/dwarf/dwarf2diehandler.cc \
 	src/common/dwarf/dwarf2reader.cc \
-        src/common/arm_ex_reader.cc \
-        src/common/arm_ex_to_module.cc \
 	src/common/linux/dump_symbols.cc \
 	src/common/linux/elf_symbols_to_module.cc \
 	src/common/linux/elfutils.cc \
 	src/common/linux/file_id.cc \
 	src/common/linux/linux_libc_support.cc \
 	src/common/linux/memory_mapped_file.cc \
 	src/common/linux/safe_readlink.cc \
 	src/tools/linux/dump_syms/dump_syms.cc
@@ -1012,20 +1010,16 @@ EXTRA_DIST = \
 	src/client/windows/handler/exception_handler.vcproj \
 	src/client/windows/sender/crash_report_sender.cc \
 	src/client/windows/sender/crash_report_sender.h \
 	src/client/windows/sender/crash_report_sender.vcproj \
 	src/common/convert_UTF.c \
 	src/common/convert_UTF.h \
 	src/common/linux/dump_symbols.cc \
 	src/common/linux/dump_symbols.h \
-        src/common/arm_ex_reader.cc \
-        src/common/arm_ex_reader.h \
-        src/common/arm_ex_to_module.cc \
-        src/common/arm_ex_to_module.h \
 	src/common/linux/elf_symbols_to_module.cc \
 	src/common/linux/elf_symbols_to_module.h \
 	src/common/linux/elfutils.cc \
 	src/common/linux/elfutils.h \
 	src/common/linux/file_id.cc \
 	src/common/linux/file_id.h \
 	src/common/linux/guid_creator.cc \
 	src/common/linux/guid_creator.h \