michael@0: # HG changeset patch michael@0: # User Julian Seward michael@0: # Date 1371190160 -7200 michael@0: # Fri Jun 14 08:09:20 2013 +0200 michael@0: # Node ID e74de3db7dd27ffda8f4772f892cfb52c5c35649 michael@0: # Parent 4dcd4220c31068e116d88a58e5b396fbb01719dd michael@0: Bug 863475 - integrate ARM EXIDX unwind parsing into Breakpad. r=glandium,ted michael@0: michael@0: diff --git a/Makefile.am b/Makefile.am michael@0: --- a/Makefile.am michael@0: +++ b/Makefile.am michael@0: @@ -428,16 +428,18 @@ src_tools_linux_dump_syms_dump_syms_SOUR michael@0: src/common/dwarf_line_to_module.cc \ michael@0: src/common/language.cc \ michael@0: src/common/module.cc \ michael@0: src/common/stabs_reader.cc \ michael@0: src/common/stabs_to_module.cc \ michael@0: src/common/dwarf/bytereader.cc \ michael@0: src/common/dwarf/dwarf2diehandler.cc \ michael@0: src/common/dwarf/dwarf2reader.cc \ michael@0: + src/common/arm_ex_reader.cc \ michael@0: + src/common/arm_ex_to_module.cc \ michael@0: src/common/linux/dump_symbols.cc \ michael@0: src/common/linux/elf_symbols_to_module.cc \ michael@0: src/common/linux/elfutils.cc \ michael@0: src/common/linux/file_id.cc \ michael@0: src/common/linux/linux_libc_support.cc \ michael@0: src/common/linux/memory_mapped_file.cc \ michael@0: src/common/linux/safe_readlink.cc \ michael@0: src/tools/linux/dump_syms/dump_syms.cc michael@0: @@ -1010,16 +1012,20 @@ EXTRA_DIST = \ michael@0: src/client/windows/handler/exception_handler.vcproj \ michael@0: src/client/windows/sender/crash_report_sender.cc \ michael@0: src/client/windows/sender/crash_report_sender.h \ michael@0: src/client/windows/sender/crash_report_sender.vcproj \ michael@0: src/common/convert_UTF.c \ michael@0: src/common/convert_UTF.h \ michael@0: src/common/linux/dump_symbols.cc \ michael@0: src/common/linux/dump_symbols.h \ michael@0: + src/common/arm_ex_reader.cc \ michael@0: + src/common/arm_ex_reader.h \ michael@0: + src/common/arm_ex_to_module.cc \ michael@0: + src/common/arm_ex_to_module.h \ michael@0: src/common/linux/elf_symbols_to_module.cc \ michael@0: src/common/linux/elf_symbols_to_module.h \ michael@0: src/common/linux/elfutils.cc \ michael@0: src/common/linux/elfutils.h \ michael@0: src/common/linux/file_id.cc \ michael@0: src/common/linux/file_id.h \ michael@0: src/common/linux/guid_creator.cc \ michael@0: src/common/linux/guid_creator.h \ michael@0: diff --git a/src/common/arm_ex_reader.cc b/src/common/arm_ex_reader.cc michael@0: new file mode 100644 michael@0: --- /dev/null michael@0: +++ b/src/common/arm_ex_reader.cc michael@0: @@ -0,0 +1,502 @@ michael@0: + michael@0: +/* libunwind - a platform-independent unwind library michael@0: + Copyright 2011 Linaro Limited michael@0: + michael@0: +This file is part of libunwind. michael@0: + michael@0: +Permission is hereby granted, free of charge, to any person obtaining michael@0: +a copy of this software and associated documentation files (the michael@0: +"Software"), to deal in the Software without restriction, including michael@0: +without limitation the rights to use, copy, modify, merge, publish, michael@0: +distribute, sublicense, and/or sell copies of the Software, and to michael@0: +permit persons to whom the Software is furnished to do so, subject to michael@0: +the following conditions: michael@0: + michael@0: +The above copyright notice and this permission notice shall be michael@0: +included in all copies or substantial portions of the Software. michael@0: + michael@0: +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, michael@0: +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF michael@0: +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND michael@0: +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE michael@0: +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION michael@0: +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION michael@0: +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ michael@0: + michael@0: +// Copyright (c) 2010 Google Inc. michael@0: +// All rights reserved. michael@0: +// michael@0: +// Redistribution and use in source and binary forms, with or without michael@0: +// modification, are permitted provided that the following conditions are michael@0: +// met: michael@0: +// michael@0: +// * Redistributions of source code must retain the above copyright michael@0: +// notice, this list of conditions and the following disclaimer. michael@0: +// * Redistributions in binary form must reproduce the above michael@0: +// copyright notice, this list of conditions and the following disclaimer michael@0: +// in the documentation and/or other materials provided with the michael@0: +// distribution. michael@0: +// * Neither the name of Google Inc. nor the names of its michael@0: +// contributors may be used to endorse or promote products derived from michael@0: +// this software without specific prior written permission. michael@0: +// michael@0: +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: + michael@0: + michael@0: +// Derived from libunwind, with extensive modifications. michael@0: + michael@0: + michael@0: +#include "common/arm_ex_reader.h" michael@0: +#include "common/logging.h" michael@0: + michael@0: +#include michael@0: + michael@0: +// This file, in conjunction with arm_ex_to_module.cc, translates michael@0: +// EXIDX unwind information into the same format that Breakpad uses michael@0: +// for CFI information. Hence Breakpad's CFI unwinding abilities michael@0: +// also become usable for EXIDX. michael@0: +// michael@0: +// See: "Exception Handling ABI for the ARM Architecture", ARM IHI 0038A michael@0: +// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf michael@0: + michael@0: +// EXIDX data is presented in two parts: michael@0: +// michael@0: +// * an index table. This contains two words per routine, michael@0: +// the first of which identifies the routine, and the second michael@0: +// of which is a reference to the unwind bytecode. If the michael@0: +// bytecode is very compact -- 3 bytes or less -- it can be michael@0: +// stored directly in the second word. michael@0: +// michael@0: +// * an area containing the unwind bytecodes. michael@0: + michael@0: +// General flow is: ExceptionTableInfo::Start iterates over all michael@0: +// of the index table entries (pairs). For each entry, it: michael@0: +// michael@0: +// * calls ExceptionTableInfo::ExtabEntryExtract to copy the bytecode michael@0: +// out into an intermediate buffer. michael@0: + michael@0: +// * uses ExceptionTableInfo::ExtabEntryDecode to parse the intermediate michael@0: +// buffer. Each bytecode instruction is bundled into a michael@0: +// arm_ex_to_module::extab_data structure, and handed to .. michael@0: +// michael@0: +// * .. ARMExToModule::ImproveStackFrame, which in turn hands it to michael@0: +// ARMExToModule::TranslateCmd, and that generates the pseudo-CFI michael@0: +// records that Breakpad stores. michael@0: + michael@0: +#define ARM_EXIDX_CANT_UNWIND 0x00000001 michael@0: +#define ARM_EXIDX_COMPACT 0x80000000 michael@0: +#define ARM_EXTBL_OP_FINISH 0xb0 michael@0: +#define ARM_EXIDX_TABLE_LIMIT (255*4) michael@0: + michael@0: +namespace arm_ex_reader { michael@0: + michael@0: +using arm_ex_to_module::ARM_EXIDX_CMD_FINISH; michael@0: +using arm_ex_to_module::ARM_EXIDX_CMD_SUB_FROM_VSP; michael@0: +using arm_ex_to_module::ARM_EXIDX_CMD_ADD_TO_VSP; michael@0: +using arm_ex_to_module::ARM_EXIDX_CMD_REG_POP; michael@0: +using arm_ex_to_module::ARM_EXIDX_CMD_REG_TO_SP; michael@0: +using arm_ex_to_module::ARM_EXIDX_CMD_VFP_POP; michael@0: +using arm_ex_to_module::ARM_EXIDX_CMD_WREG_POP; michael@0: +using arm_ex_to_module::ARM_EXIDX_CMD_WCGR_POP; michael@0: +using arm_ex_to_module::ARM_EXIDX_CMD_RESERVED; michael@0: +using arm_ex_to_module::ARM_EXIDX_CMD_REFUSED; michael@0: +using arm_ex_to_module::exidx_entry; michael@0: +using arm_ex_to_module::ARM_EXIDX_VFP_SHIFT_16; michael@0: +using arm_ex_to_module::ARM_EXIDX_VFP_FSTMD; michael@0: +using google_breakpad::MemoryRange; michael@0: + michael@0: + michael@0: +static void* Prel31ToAddr(const void* addr) michael@0: +{ michael@0: + uint32_t offset32 = *reinterpret_cast(addr); michael@0: + // sign extend offset32[30:0] to 64 bits -- copy bit 30 to positions michael@0: + // 63:31 inclusive. michael@0: + uint64_t offset64 = offset32; michael@0: + if (offset64 & (1ULL << 30)) michael@0: + offset64 |= 0xFFFFFFFF80000000ULL; michael@0: + else michael@0: + offset64 &= 0x000000007FFFFFFFULL; michael@0: + return ((char*)addr) + (uintptr_t)offset64; michael@0: +} michael@0: + michael@0: + michael@0: +// Extract unwind bytecode for the function denoted by |entry| into |buf|, michael@0: +// and return the number of bytes of |buf| written, along with a code michael@0: +// indicating the outcome. michael@0: + michael@0: +ExceptionTableInfo::ExExtractResult michael@0: +ExceptionTableInfo::ExtabEntryExtract(const struct exidx_entry* entry, michael@0: + uint8_t* buf, size_t buf_size, michael@0: + /*OUT*/size_t* buf_used) michael@0: +{ michael@0: + MemoryRange mr_out(buf, buf_size); michael@0: + michael@0: + *buf_used = 0; michael@0: + michael@0: +# define PUT_BUF_U8(_byte) \ michael@0: + do { if (!mr_out.Covers(*buf_used, 1)) return ExOutBufOverflow; \ michael@0: + buf[(*buf_used)++] = (_byte); } while (0) michael@0: + michael@0: +# define GET_EX_U32(_lval, _addr, _sec_mr) \ michael@0: + do { if (!(_sec_mr).Covers(reinterpret_cast(_addr) \ michael@0: + - (_sec_mr).data(), 4)) \ michael@0: + return ExInBufOverflow; \ michael@0: + (_lval) = *(reinterpret_cast(_addr)); } while (0) michael@0: + michael@0: +# define GET_EXIDX_U32(_lval, _addr) \ michael@0: + GET_EX_U32(_lval, _addr, mr_exidx_) michael@0: +# define GET_EXTAB_U32(_lval, _addr) \ michael@0: + GET_EX_U32(_lval, _addr, mr_extab_) michael@0: + michael@0: + uint32_t data; michael@0: + GET_EXIDX_U32(data, &entry->data); michael@0: + michael@0: + // A function can be marked CANT_UNWIND if (eg) it is known to be michael@0: + // at the bottom of the stack. michael@0: + if (data == ARM_EXIDX_CANT_UNWIND) michael@0: + return ExCantUnwind; michael@0: + michael@0: + uint32_t pers; // personality number michael@0: + uint32_t extra; // number of extra data words required michael@0: + uint32_t extra_allowed; // number of extra data words allowed michael@0: + uint32_t* extbl_data; // the handler entry, if not inlined michael@0: + michael@0: + if (data & ARM_EXIDX_COMPACT) { michael@0: + // The handler table entry has been inlined into the index table entry. michael@0: + // In this case it can only be an ARM-defined compact model, since michael@0: + // bit 31 is 1. Only personalities 0, 1 and 2 are defined for the michael@0: + // ARM compact model, but 1 and 2 are "Long format" and may require michael@0: + // extra data words. Hence the allowable personalities here are: michael@0: + // personality 0, in which case 'extra' has no meaning michael@0: + // personality 1, with zero extra words michael@0: + // personality 2, with zero extra words michael@0: + extbl_data = NULL; michael@0: + pers = (data >> 24) & 0x0F; michael@0: + extra = (data >> 16) & 0xFF; michael@0: + extra_allowed = 0; michael@0: + } michael@0: + else { michael@0: + // The index table entry is a pointer to the handler entry. Note michael@0: + // that Prel31ToAddr will read the given address, but we already michael@0: + // range-checked above. michael@0: + extbl_data = reinterpret_cast(Prel31ToAddr(&entry->data)); michael@0: + GET_EXTAB_U32(data, extbl_data); michael@0: + if (!(data & ARM_EXIDX_COMPACT)) { michael@0: + // This denotes a "generic model" handler. That will involve michael@0: + // executing arbitary machine code, which is something we michael@0: + // can't represent here; hence reject it. michael@0: + return ExCantRepresent; michael@0: + } michael@0: + // So we have a compact model representation. Again, 3 possible michael@0: + // personalities, but this time up to 255 allowable extra words. michael@0: + pers = (data >> 24) & 0x0F; michael@0: + extra = (data >> 16) & 0xFF; michael@0: + extra_allowed = 255; michael@0: + extbl_data++; michael@0: + } michael@0: + michael@0: + // Now look at the the handler table entry. The first word is michael@0: + // |data| and subsequent words start at |*extbl_data|. The number michael@0: + // of extra words to use is |extra|, provided that the personality michael@0: + // allows extra words. Even if it does, none may be available -- michael@0: + // extra_allowed is the maximum number of extra words allowed. */ michael@0: + if (pers == 0) { michael@0: + // "Su16" in the documentation -- 3 unwinding insn bytes michael@0: + // |extra| has no meaning here; instead that byte is an unwind-info byte michael@0: + PUT_BUF_U8(data >> 16); michael@0: + PUT_BUF_U8(data >> 8); michael@0: + PUT_BUF_U8(data); michael@0: + } michael@0: + else if ((pers == 1 || pers == 2) && extra <= extra_allowed) { michael@0: + // "Lu16" or "Lu32" respectively -- 2 unwinding insn bytes, michael@0: + // and up to 255 extra words. michael@0: + PUT_BUF_U8(data >> 8); michael@0: + PUT_BUF_U8(data); michael@0: + for (uint32_t j = 0; j < extra; j++) { michael@0: + GET_EXTAB_U32(data, extbl_data); michael@0: + extbl_data++; michael@0: + PUT_BUF_U8(data >> 24); michael@0: + PUT_BUF_U8(data >> 16); michael@0: + PUT_BUF_U8(data >> 8); michael@0: + PUT_BUF_U8(data >> 0); michael@0: + } michael@0: + } michael@0: + else { michael@0: + // The entry is invalid. michael@0: + return ExInvalid; michael@0: + } michael@0: + michael@0: + // Make sure the entry is terminated with "FINISH" michael@0: + if (*buf_used > 0 && buf[(*buf_used) - 1] != ARM_EXTBL_OP_FINISH) michael@0: + PUT_BUF_U8(ARM_EXTBL_OP_FINISH); michael@0: + michael@0: + return ExSuccess; michael@0: + michael@0: +# undef GET_EXTAB_U32 michael@0: +# undef GET_EXIDX_U32 michael@0: +# undef GET_U32 michael@0: +# undef PUT_BUF_U8 michael@0: +} michael@0: + michael@0: + michael@0: +// Take the unwind information extracted by ExtabEntryExtract michael@0: +// and parse it into frame-unwind instructions. These are as michael@0: +// specified in "Table 4, ARM-defined frame-unwinding instructions" michael@0: +// in the specification document detailed in comments at the top michael@0: +// of this file. michael@0: +// michael@0: +// This reads from |buf[0, +data_size)|. It checks for overruns of michael@0: +// the input buffer and returns a negative value if that happens, or michael@0: +// for any other failure cases. It returns zero in case of success. michael@0: +int ExceptionTableInfo::ExtabEntryDecode(const uint8_t* buf, size_t buf_size) michael@0: +{ michael@0: + if (buf == NULL || buf_size == 0) michael@0: + return -1; michael@0: + michael@0: + MemoryRange mr_in(buf, buf_size); michael@0: + const uint8_t* buf_initially = buf; michael@0: + michael@0: +# define GET_BUF_U8(_lval) \ michael@0: + do { if (!mr_in.Covers(buf - buf_initially, 1)) return -1; \ michael@0: + (_lval) = *(buf++); } while (0) michael@0: + michael@0: + const uint8_t* end = buf + buf_size; michael@0: + michael@0: + while (buf < end) { michael@0: + struct arm_ex_to_module::extab_data edata; michael@0: + memset(&edata, 0, sizeof(edata)); michael@0: + michael@0: + uint8_t op; michael@0: + GET_BUF_U8(op); michael@0: + if ((op & 0xc0) == 0x00) { michael@0: + // vsp = vsp + (xxxxxx << 2) + 4 michael@0: + edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP; michael@0: + edata.data = (((int)op & 0x3f) << 2) + 4; michael@0: + } michael@0: + else if ((op & 0xc0) == 0x40) { michael@0: + // vsp = vsp - (xxxxxx << 2) - 4 michael@0: + edata.cmd = ARM_EXIDX_CMD_SUB_FROM_VSP; michael@0: + edata.data = (((int)op & 0x3f) << 2) + 4; michael@0: + } michael@0: + else if ((op & 0xf0) == 0x80) { michael@0: + uint8_t op2; michael@0: + GET_BUF_U8(op2); michael@0: + if (op == 0x80 && op2 == 0x00) { michael@0: + // Refuse to unwind michael@0: + edata.cmd = ARM_EXIDX_CMD_REFUSED; michael@0: + } else { michael@0: + // Pop up to 12 integer registers under masks {r15-r12},{r11-r4} michael@0: + edata.cmd = ARM_EXIDX_CMD_REG_POP; michael@0: + edata.data = ((op & 0xf) << 8) | op2; michael@0: + edata.data = edata.data << 4; michael@0: + } michael@0: + } michael@0: + else if ((op & 0xf0) == 0x90) { michael@0: + if (op == 0x9d || op == 0x9f) { michael@0: + // 9d: Reserved as prefix for ARM register to register moves michael@0: + // 9f: Reserved as perfix for Intel Wireless MMX reg to reg moves michael@0: + edata.cmd = ARM_EXIDX_CMD_RESERVED; michael@0: + } else { michael@0: + // Set vsp = r[nnnn] michael@0: + edata.cmd = ARM_EXIDX_CMD_REG_TO_SP; michael@0: + edata.data = op & 0x0f; michael@0: + } michael@0: + } michael@0: + else if ((op & 0xf0) == 0xa0) { michael@0: + // Pop r4 to r[4+nnn], or michael@0: + // Pop r4 to r[4+nnn] and r14 or michael@0: + unsigned end = (op & 0x07); michael@0: + edata.data = (1 << (end + 1)) - 1; michael@0: + edata.data = edata.data << 4; michael@0: + if (op & 0x08) edata.data |= 1 << 14; michael@0: + edata.cmd = ARM_EXIDX_CMD_REG_POP; michael@0: + } michael@0: + else if (op == ARM_EXTBL_OP_FINISH) { michael@0: + // Finish michael@0: + edata.cmd = ARM_EXIDX_CMD_FINISH; michael@0: + buf = end; michael@0: + } michael@0: + else if (op == 0xb1) { michael@0: + uint8_t op2; michael@0: + GET_BUF_U8(op2); michael@0: + if (op2 == 0 || (op2 & 0xf0)) { michael@0: + // Spare michael@0: + edata.cmd = ARM_EXIDX_CMD_RESERVED; michael@0: + } else { michael@0: + // Pop integer registers under mask {r3,r2,r1,r0} michael@0: + edata.cmd = ARM_EXIDX_CMD_REG_POP; michael@0: + edata.data = op2 & 0x0f; michael@0: + } michael@0: + } michael@0: + else if (op == 0xb2) { michael@0: + // vsp = vsp + 0x204 + (uleb128 << 2) michael@0: + uint64_t offset = 0; michael@0: + uint8_t byte, shift = 0; michael@0: + do { michael@0: + GET_BUF_U8(byte); michael@0: + offset |= (byte & 0x7f) << shift; michael@0: + shift += 7; michael@0: + } while ((byte & 0x80) && buf < end); michael@0: + edata.data = offset * 4 + 0x204; michael@0: + edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP; michael@0: + } michael@0: + else if (op == 0xb3 || op == 0xc8 || op == 0xc9) { michael@0: + // b3: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDX-ishly michael@0: + // c8: Pop VFP regs D[16+ssss] to D[16+ssss+cccc], FSTMFDD-ishly michael@0: + // c9: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDD-ishly michael@0: + edata.cmd = ARM_EXIDX_CMD_VFP_POP; michael@0: + GET_BUF_U8(edata.data); michael@0: + if (op == 0xc8) edata.data |= ARM_EXIDX_VFP_SHIFT_16; michael@0: + if (op != 0xb3) edata.data |= ARM_EXIDX_VFP_FSTMD; michael@0: + } michael@0: + else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0) { michael@0: + // b8: Pop VFP regs D[8] to D[8+nnn], FSTMFDX-ishly michael@0: + // d0: Pop VFP regs D[8] to D[8+nnn], FSTMFDD-ishly michael@0: + edata.cmd = ARM_EXIDX_CMD_VFP_POP; michael@0: + edata.data = 0x80 | (op & 0x07); michael@0: + if ((op & 0xf8) == 0xd0) edata.data |= ARM_EXIDX_VFP_FSTMD; michael@0: + } michael@0: + else if (op >= 0xc0 && op <= 0xc5) { michael@0: + // Intel Wireless MMX pop wR[10]-wr[10+nnn], nnn != 6,7 michael@0: + edata.cmd = ARM_EXIDX_CMD_WREG_POP; michael@0: + edata.data = 0xa0 | (op & 0x07); michael@0: + } michael@0: + else if (op == 0xc6) { michael@0: + // Intel Wireless MMX pop wR[ssss] to wR[ssss+cccc] michael@0: + edata.cmd = ARM_EXIDX_CMD_WREG_POP; michael@0: + GET_BUF_U8(edata.data); michael@0: + } michael@0: + else if (op == 0xc7) { michael@0: + uint8_t op2; michael@0: + GET_BUF_U8(op2); michael@0: + if (op2 == 0 || (op2 & 0xf0)) { michael@0: + // Spare michael@0: + edata.cmd = ARM_EXIDX_CMD_RESERVED; michael@0: + } else { michael@0: + // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0} michael@0: + edata.cmd = ARM_EXIDX_CMD_WCGR_POP; michael@0: + edata.data = op2 & 0x0f; michael@0: + } michael@0: + } michael@0: + else { michael@0: + // Spare michael@0: + edata.cmd = ARM_EXIDX_CMD_RESERVED; michael@0: + } michael@0: + michael@0: + int ret = handler_->ImproveStackFrame(&edata); michael@0: + if (ret < 0) return ret; michael@0: + } michael@0: + return 0; michael@0: + michael@0: +# undef GET_BUF_U8 michael@0: +} michael@0: + michael@0: +void ExceptionTableInfo::Start() michael@0: +{ michael@0: + const struct exidx_entry* start michael@0: + = reinterpret_cast(mr_exidx_.data()); michael@0: + const struct exidx_entry* end michael@0: + = reinterpret_cast(mr_exidx_.data() michael@0: + + mr_exidx_.length()); michael@0: + michael@0: + // Iterate over each of the EXIDX entries (pairs of 32-bit words). michael@0: + // These occupy the entire .exidx section. michael@0: + for (const struct exidx_entry* entry = start; entry < end; ++entry) { michael@0: + michael@0: + // Figure out the code address range that this table entry is michael@0: + // associated with. michael@0: + uint32_t addr = (reinterpret_cast(Prel31ToAddr(&entry->addr)) michael@0: + - mapping_addr_ + loading_addr_) & 0x7fffffff; michael@0: + uint32_t next_addr; michael@0: + if (entry < end - 1) michael@0: + next_addr = (reinterpret_cast(Prel31ToAddr(&((entry + 1)->addr))) michael@0: + - mapping_addr_ + loading_addr_) & 0x7fffffff; michael@0: + else { michael@0: + // This is the last EXIDX entry in the sequence, so we don't michael@0: + // have an address for the start of the next function, to limit michael@0: + // this one. Instead use the address of the last byte of the michael@0: + // text section associated with this .exidx section, that we michael@0: + // have been given. So as to avoid junking up the CFI unwind michael@0: + // tables with absurdly large address ranges in the case where michael@0: + // text_last_svma_ is wrong, only use the value if it is nonzero michael@0: + // and within one page of |addr|. Otherwise assume a length of 1. michael@0: + // michael@0: + // In some cases, gcc has been observed to finish the exidx michael@0: + // section with an entry of length 1 marked CANT_UNWIND, michael@0: + // presumably exactly for the purpose of giving a definite michael@0: + // length for the last real entry, without having to look at michael@0: + // text segment boundaries. michael@0: + bool plausible = false; michael@0: + next_addr = addr + 1; michael@0: + if (text_last_svma_ != 0) { michael@0: + uint32_t maybe_next_addr = text_last_svma_ + 1; michael@0: + if (maybe_next_addr > addr && maybe_next_addr - addr <= 4096) { michael@0: + next_addr = maybe_next_addr; michael@0: + plausible = true; michael@0: + } michael@0: + } michael@0: + if (!plausible) michael@0: + BPLOG(INFO) << "ExceptionTableInfo: implausible EXIDX last entry size " michael@0: + << (int32_t)(text_last_svma_ - addr) michael@0: + << "; using 1 instead."; michael@0: + } michael@0: + michael@0: + // Extract the unwind info into |buf|. This might fail for michael@0: + // various reasons. It involves reading both the .exidx and michael@0: + // .extab sections. All accesses to those sections are michael@0: + // bounds-checked. michael@0: + uint8_t buf[ARM_EXIDX_TABLE_LIMIT]; michael@0: + size_t buf_used = 0; michael@0: + ExExtractResult res = ExtabEntryExtract(entry, buf, sizeof(buf), &buf_used); michael@0: + if (res != ExSuccess) { michael@0: + // Couldn't extract the unwind info, for some reason. Move on. michael@0: + switch (res) { michael@0: + case ExInBufOverflow: michael@0: + BPLOG(INFO) << "ExtabEntryExtract: .exidx/.extab section overrun"; michael@0: + break; michael@0: + case ExOutBufOverflow: michael@0: + BPLOG(INFO) << "ExtabEntryExtract: bytecode buffer overflow"; michael@0: + break; michael@0: + case ExCantUnwind: michael@0: + BPLOG(INFO) << "ExtabEntryExtract: function is marked CANT_UNWIND"; michael@0: + break; michael@0: + case ExCantRepresent: michael@0: + BPLOG(INFO) << "ExtabEntryExtract: bytecode can't be represented"; michael@0: + break; michael@0: + case ExInvalid: michael@0: + BPLOG(INFO) << "ExtabEntryExtract: index table entry is invalid"; michael@0: + break; michael@0: + default: michael@0: + BPLOG(INFO) << "ExtabEntryExtract: unknown error: " << (int)res; michael@0: + break; michael@0: + } michael@0: + continue; michael@0: + } michael@0: + michael@0: + // Finally, work through the unwind instructions in |buf| and michael@0: + // create CFI entries that Breakpad can use. This can also fail. michael@0: + // First, add a new stack frame entry, into which ExtabEntryDecode michael@0: + // will write the CFI entries. michael@0: + handler_->AddStackFrame(addr, next_addr - addr); michael@0: + int ret = ExtabEntryDecode(buf, buf_used); michael@0: + if (ret < 0) { michael@0: + handler_->DeleteStackFrame(); michael@0: + BPLOG(INFO) << "ExtabEntryDecode: failed with error code: " << ret; michael@0: + continue; michael@0: + } michael@0: + handler_->SubmitStackFrame(); michael@0: + michael@0: + } /* iterating over .exidx */ michael@0: +} michael@0: + michael@0: +} // arm_ex_reader michael@0: diff --git a/src/common/arm_ex_reader.h b/src/common/arm_ex_reader.h michael@0: new file mode 100644 michael@0: --- /dev/null michael@0: +++ b/src/common/arm_ex_reader.h michael@0: @@ -0,0 +1,115 @@ michael@0: + michael@0: +/* libunwind - a platform-independent unwind library michael@0: + Copyright 2011 Linaro Limited michael@0: + michael@0: +This file is part of libunwind. michael@0: + michael@0: +Permission is hereby granted, free of charge, to any person obtaining michael@0: +a copy of this software and associated documentation files (the michael@0: +"Software"), to deal in the Software without restriction, including michael@0: +without limitation the rights to use, copy, modify, merge, publish, michael@0: +distribute, sublicense, and/or sell copies of the Software, and to michael@0: +permit persons to whom the Software is furnished to do so, subject to michael@0: +the following conditions: michael@0: + michael@0: +The above copyright notice and this permission notice shall be michael@0: +included in all copies or substantial portions of the Software. michael@0: + michael@0: +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, michael@0: +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF michael@0: +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND michael@0: +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE michael@0: +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION michael@0: +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION michael@0: +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ michael@0: + michael@0: +// Copyright (c) 2010 Google Inc. michael@0: +// All rights reserved. michael@0: +// michael@0: +// Redistribution and use in source and binary forms, with or without michael@0: +// modification, are permitted provided that the following conditions are michael@0: +// met: michael@0: +// michael@0: +// * Redistributions of source code must retain the above copyright michael@0: +// notice, this list of conditions and the following disclaimer. michael@0: +// * Redistributions in binary form must reproduce the above michael@0: +// copyright notice, this list of conditions and the following disclaimer michael@0: +// in the documentation and/or other materials provided with the michael@0: +// distribution. michael@0: +// * Neither the name of Google Inc. nor the names of its michael@0: +// contributors may be used to endorse or promote products derived from michael@0: +// this software without specific prior written permission. michael@0: +// michael@0: +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: + michael@0: + michael@0: +// Derived from libunwind, with extensive modifications. michael@0: + michael@0: +#ifndef COMMON_ARM_EX_READER_H__ michael@0: +#define COMMON_ARM_EX_READER_H__ michael@0: + michael@0: +#include "common/arm_ex_to_module.h" michael@0: +#include "common/memory_range.h" michael@0: + michael@0: +namespace arm_ex_reader { michael@0: + michael@0: +// This class is a reader for ARM unwind information michael@0: +// from .ARM.exidx and .ARM.extab sections. michael@0: +class ExceptionTableInfo { michael@0: + public: michael@0: + ExceptionTableInfo(const char* exidx, size_t exidx_size, michael@0: + const char* extab, size_t extab_size, michael@0: + uint32_t text_last_svma, michael@0: + arm_ex_to_module::ARMExToModule* handler, michael@0: + const char* mapping_addr, michael@0: + uint32_t loading_addr) michael@0: + : mr_exidx_(google_breakpad::MemoryRange(exidx, exidx_size)), michael@0: + mr_extab_(google_breakpad::MemoryRange(extab, extab_size)), michael@0: + text_last_svma_(text_last_svma), michael@0: + handler_(handler), mapping_addr_(mapping_addr), michael@0: + loading_addr_(loading_addr) { } michael@0: + michael@0: + ~ExceptionTableInfo() { } michael@0: + michael@0: + // Parses the entries in .ARM.exidx and possibly michael@0: + // in .ARM.extab tables, reports what we find to michael@0: + // arm_ex_to_module::ARMExToModule. michael@0: + void Start(); michael@0: + michael@0: + private: michael@0: + google_breakpad::MemoryRange mr_exidx_; michael@0: + google_breakpad::MemoryRange mr_extab_; michael@0: + uint32_t text_last_svma_; michael@0: + arm_ex_to_module::ARMExToModule* handler_; michael@0: + const char* mapping_addr_; michael@0: + uint32_t loading_addr_; michael@0: + michael@0: + enum ExExtractResult { michael@0: + ExSuccess, // success michael@0: + ExInBufOverflow, // out-of-range while reading .exidx michael@0: + ExOutBufOverflow, // output buffer is too small michael@0: + ExCantUnwind, // this function is marked CANT_UNWIND michael@0: + ExCantRepresent, // entry valid, but we can't represent it michael@0: + ExInvalid // entry is invalid michael@0: + }; michael@0: + ExExtractResult michael@0: + ExtabEntryExtract(const struct arm_ex_to_module::exidx_entry* entry, michael@0: + uint8_t* buf, size_t buf_size, michael@0: + /*OUT*/size_t* buf_used); michael@0: + michael@0: + int ExtabEntryDecode(const uint8_t* buf, size_t buf_size); michael@0: +}; michael@0: + michael@0: +} // namespace arm_ex_reader michael@0: + michael@0: +#endif // COMMON_ARM_EX_READER_H__ michael@0: diff --git a/src/common/arm_ex_to_module.cc b/src/common/arm_ex_to_module.cc michael@0: new file mode 100644 michael@0: --- /dev/null michael@0: +++ b/src/common/arm_ex_to_module.cc michael@0: @@ -0,0 +1,206 @@ michael@0: + michael@0: +/* libunwind - a platform-independent unwind library michael@0: + Copyright 2011 Linaro Limited michael@0: + michael@0: +This file is part of libunwind. michael@0: + michael@0: +Permission is hereby granted, free of charge, to any person obtaining michael@0: +a copy of this software and associated documentation files (the michael@0: +"Software"), to deal in the Software without restriction, including michael@0: +without limitation the rights to use, copy, modify, merge, publish, michael@0: +distribute, sublicense, and/or sell copies of the Software, and to michael@0: +permit persons to whom the Software is furnished to do so, subject to michael@0: +the following conditions: michael@0: + michael@0: +The above copyright notice and this permission notice shall be michael@0: +included in all copies or substantial portions of the Software. michael@0: + michael@0: +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, michael@0: +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF michael@0: +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND michael@0: +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE michael@0: +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION michael@0: +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION michael@0: +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ michael@0: + michael@0: +// Copyright (c) 2010 Google Inc. michael@0: +// All rights reserved. michael@0: +// michael@0: +// Redistribution and use in source and binary forms, with or without michael@0: +// modification, are permitted provided that the following conditions are michael@0: +// met: michael@0: +// michael@0: +// * Redistributions of source code must retain the above copyright michael@0: +// notice, this list of conditions and the following disclaimer. michael@0: +// * Redistributions in binary form must reproduce the above michael@0: +// copyright notice, this list of conditions and the following disclaimer michael@0: +// in the documentation and/or other materials provided with the michael@0: +// distribution. michael@0: +// * Neither the name of Google Inc. nor the names of its michael@0: +// contributors may be used to endorse or promote products derived from michael@0: +// this software without specific prior written permission. michael@0: +// michael@0: +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: + michael@0: + michael@0: +// Derived from libunwind, with extensive modifications. michael@0: + michael@0: +#include "common/unique_string.h" michael@0: +#include "common/arm_ex_to_module.h" michael@0: + michael@0: +#include michael@0: +#include michael@0: + michael@0: +// For big-picture comments on how the EXIDX reader works, michael@0: +// see arm_ex_reader.cc. michael@0: + michael@0: +#define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f) michael@0: +#define ARM_EXBUF_COUNT(x) ((x) & 0x0f) michael@0: +#define ARM_EXBUF_END(x) (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x)) michael@0: + michael@0: +using google_breakpad::ustr__pc; michael@0: +using google_breakpad::ustr__lr; michael@0: +using google_breakpad::ustr__sp; michael@0: +using google_breakpad::Module; michael@0: +using google_breakpad::ToUniqueString; michael@0: +using google_breakpad::UniqueString; michael@0: + michael@0: +namespace arm_ex_to_module { michael@0: + michael@0: +// Translate command from extab_data to command for Module. michael@0: +int ARMExToModule::TranslateCmd(const struct extab_data* edata, michael@0: + Module::StackFrameEntry* entry, string& vsp) { michael@0: + int ret = 0; michael@0: + switch (edata->cmd) { michael@0: + case ARM_EXIDX_CMD_FINISH: michael@0: + /* Copy LR to PC if there isn't currently a rule for PC in force. */ michael@0: + if (entry->initial_rules.find(ustr__pc()) michael@0: + == entry->initial_rules.end()) { michael@0: + if (entry->initial_rules.find(ustr__lr()) michael@0: + == entry->initial_rules.end()) { michael@0: + entry->initial_rules[ustr__pc()] = Module::Expr("lr"); michael@0: + } else { michael@0: + entry->initial_rules[ustr__pc()] = entry->initial_rules[ustr__lr()]; michael@0: + } michael@0: + } michael@0: + break; michael@0: + case ARM_EXIDX_CMD_SUB_FROM_VSP: michael@0: + { michael@0: + char c[16]; michael@0: + sprintf(c, " %d -", edata->data); michael@0: + vsp += c; michael@0: + } michael@0: + break; michael@0: + case ARM_EXIDX_CMD_ADD_TO_VSP: michael@0: + { michael@0: + char c[16]; michael@0: + sprintf(c, " %d +", edata->data); michael@0: + vsp += c; michael@0: + } michael@0: + break; michael@0: + case ARM_EXIDX_CMD_REG_POP: michael@0: + for (unsigned int i = 0; i < 16; i++) { michael@0: + if (edata->data & (1 << i)) { michael@0: + entry->initial_rules[ToUniqueString(regnames[i])] michael@0: + = Module::Expr(vsp + " ^"); michael@0: + vsp += " 4 +"; michael@0: + } michael@0: + } michael@0: + /* Set cfa in case the SP got popped. */ michael@0: + if (edata->data & (1 << 13)) { michael@0: + Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()]; michael@0: + // It must be a postfix expression (we don't generate anything michael@0: + // else here), so return -1 to fail out if it isn't. michael@0: + if (!vsp_expr.isExprPostfix()) { michael@0: + ret = -1; michael@0: + break; michael@0: + }; michael@0: + vsp = vsp_expr.getExprPostfix(); michael@0: + } michael@0: + break; michael@0: + case ARM_EXIDX_CMD_REG_TO_SP: { michael@0: + assert (edata->data < 16); michael@0: + const char* const regname = regnames[edata->data]; michael@0: + const UniqueString* regname_us = ToUniqueString(regname); michael@0: + if (entry->initial_rules.find(regname_us) == entry->initial_rules.end()) { michael@0: + entry->initial_rules[ustr__sp()] = Module::Expr(regname); michael@0: + } else { michael@0: + entry->initial_rules[ustr__sp()] = entry->initial_rules[regname_us]; michael@0: + } michael@0: + Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()]; michael@0: + if (!vsp_expr.isExprPostfix()) { michael@0: + ret = -1; michael@0: + break; michael@0: + }; michael@0: + vsp = vsp_expr.getExprPostfix(); michael@0: + break; michael@0: + } michael@0: + case ARM_EXIDX_CMD_VFP_POP: michael@0: + /* Don't recover VFP registers, but be sure to adjust the stack michael@0: + pointer. */ michael@0: + for (unsigned int i = ARM_EXBUF_START(edata->data); michael@0: + i <= ARM_EXBUF_END(edata->data); i++) { michael@0: + vsp += " 8 +"; michael@0: + } michael@0: + if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) { michael@0: + vsp += " 4 +"; michael@0: + } michael@0: + break; michael@0: + case ARM_EXIDX_CMD_WREG_POP: michael@0: + for (unsigned int i = ARM_EXBUF_START(edata->data); michael@0: + i <= ARM_EXBUF_END(edata->data); i++) { michael@0: + vsp += " 8 +"; michael@0: + } michael@0: + break; michael@0: + case ARM_EXIDX_CMD_WCGR_POP: michael@0: + // Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4" michael@0: + for (unsigned int i = 0; i < 4; i++) { michael@0: + if (edata->data & (1 << i)) { michael@0: + vsp += " 4 +"; michael@0: + } michael@0: + } michael@0: + break; michael@0: + case ARM_EXIDX_CMD_REFUSED: michael@0: + case ARM_EXIDX_CMD_RESERVED: michael@0: + ret = -1; michael@0: + break; michael@0: + } michael@0: + return ret; michael@0: +} michael@0: + michael@0: +void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) { michael@0: + stack_frame_entry_ = new Module::StackFrameEntry; michael@0: + stack_frame_entry_->address = addr; michael@0: + stack_frame_entry_->size = size; michael@0: + stack_frame_entry_->initial_rules[ToUniqueString(kCFA)] = Module::Expr("sp"); michael@0: + vsp_ = "sp"; michael@0: +} michael@0: + michael@0: +int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) { michael@0: + return TranslateCmd(edata, stack_frame_entry_, vsp_) ; michael@0: +} michael@0: + michael@0: +void ARMExToModule::DeleteStackFrame() { michael@0: + delete stack_frame_entry_; michael@0: +} michael@0: + michael@0: +void ARMExToModule::SubmitStackFrame() { michael@0: + // return address always winds up in pc michael@0: + stack_frame_entry_->initial_rules[ToUniqueString(kRA)] michael@0: + = stack_frame_entry_->initial_rules[ustr__pc()]; michael@0: + // the final value of vsp is the new value of sp michael@0: + stack_frame_entry_->initial_rules[ustr__sp()] = vsp_; michael@0: + module_->AddStackFrameEntry(stack_frame_entry_); michael@0: +} michael@0: + michael@0: +} // namespace arm_ex_to_module michael@0: diff --git a/src/common/arm_ex_to_module.h b/src/common/arm_ex_to_module.h michael@0: new file mode 100644 michael@0: --- /dev/null michael@0: +++ b/src/common/arm_ex_to_module.h michael@0: @@ -0,0 +1,129 @@ michael@0: + michael@0: +/* libunwind - a platform-independent unwind library michael@0: + Copyright 2011 Linaro Limited michael@0: + michael@0: +This file is part of libunwind. michael@0: + michael@0: +Permission is hereby granted, free of charge, to any person obtaining michael@0: +a copy of this software and associated documentation files (the michael@0: +"Software"), to deal in the Software without restriction, including michael@0: +without limitation the rights to use, copy, modify, merge, publish, michael@0: +distribute, sublicense, and/or sell copies of the Software, and to michael@0: +permit persons to whom the Software is furnished to do so, subject to michael@0: +the following conditions: michael@0: + michael@0: +The above copyright notice and this permission notice shall be michael@0: +included in all copies or substantial portions of the Software. michael@0: + michael@0: +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, michael@0: +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF michael@0: +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND michael@0: +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE michael@0: +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION michael@0: +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION michael@0: +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ michael@0: + michael@0: +// Copyright (c) 2010 Google Inc. michael@0: +// All rights reserved. michael@0: +// michael@0: +// Redistribution and use in source and binary forms, with or without michael@0: +// modification, are permitted provided that the following conditions are michael@0: +// met: michael@0: +// michael@0: +// * Redistributions of source code must retain the above copyright michael@0: +// notice, this list of conditions and the following disclaimer. michael@0: +// * Redistributions in binary form must reproduce the above michael@0: +// copyright notice, this list of conditions and the following disclaimer michael@0: +// in the documentation and/or other materials provided with the michael@0: +// distribution. michael@0: +// * Neither the name of Google Inc. nor the names of its michael@0: +// contributors may be used to endorse or promote products derived from michael@0: +// this software without specific prior written permission. michael@0: +// michael@0: +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: + michael@0: + michael@0: +// Derived from libunwind, with extensive modifications. michael@0: + michael@0: +#ifndef COMMON_ARM_EX_TO_MODULE__ michael@0: +#define COMMON_ARM_EX_TO_MODULE__ michael@0: + michael@0: +#include "common/module.h" michael@0: + michael@0: +#include michael@0: + michael@0: +namespace arm_ex_to_module { michael@0: + michael@0: +using google_breakpad::Module; michael@0: + michael@0: +typedef enum extab_cmd { michael@0: + ARM_EXIDX_CMD_FINISH, michael@0: + ARM_EXIDX_CMD_SUB_FROM_VSP, michael@0: + ARM_EXIDX_CMD_ADD_TO_VSP, michael@0: + ARM_EXIDX_CMD_REG_POP, michael@0: + ARM_EXIDX_CMD_REG_TO_SP, michael@0: + ARM_EXIDX_CMD_VFP_POP, michael@0: + ARM_EXIDX_CMD_WREG_POP, michael@0: + ARM_EXIDX_CMD_WCGR_POP, michael@0: + ARM_EXIDX_CMD_RESERVED, michael@0: + ARM_EXIDX_CMD_REFUSED, michael@0: +} extab_cmd_t; michael@0: + michael@0: +struct exidx_entry { michael@0: + uint32_t addr; michael@0: + uint32_t data; michael@0: +}; michael@0: + michael@0: +struct extab_data { michael@0: + extab_cmd_t cmd; michael@0: + uint32_t data; michael@0: +}; michael@0: + michael@0: +enum extab_cmd_flags { michael@0: + ARM_EXIDX_VFP_SHIFT_16 = 1 << 16, michael@0: + ARM_EXIDX_VFP_FSTMD = 1 << 17, // distinguishes FSTMxxD from FSTMxxX michael@0: +}; michael@0: + michael@0: +const string kRA = ".ra"; michael@0: +const string kCFA = ".cfa"; michael@0: + michael@0: +static const char* const regnames[] = { michael@0: + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", michael@0: + "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", michael@0: + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", michael@0: + "fps", "cpsr" michael@0: +}; michael@0: + michael@0: +// Receives information from arm_ex_reader::ExceptionTableInfo michael@0: +// and adds it to the Module object michael@0: +class ARMExToModule { michael@0: + public: michael@0: + ARMExToModule(Module* module) michael@0: + : module_(module) { } michael@0: + ~ARMExToModule() { } michael@0: + void AddStackFrame(uintptr_t addr, size_t size); michael@0: + int ImproveStackFrame(const struct extab_data* edata); michael@0: + void DeleteStackFrame(); michael@0: + void SubmitStackFrame(); michael@0: + private: michael@0: + Module* module_; michael@0: + Module::StackFrameEntry* stack_frame_entry_; michael@0: + string vsp_; michael@0: + int TranslateCmd(const struct extab_data* edata, michael@0: + Module::StackFrameEntry* entry, michael@0: + string& vsp); michael@0: +}; michael@0: + michael@0: +} // namespace arm_ex_to_module michael@0: + michael@0: +#endif // COMMON_ARM_EX_TO_MODULE__ michael@0: diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc michael@0: --- a/src/common/linux/dump_symbols.cc michael@0: +++ b/src/common/linux/dump_symbols.cc michael@0: @@ -47,16 +47,17 @@ michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: +#include "common/arm_ex_reader.h" michael@0: #include "common/dwarf/bytereader-inl.h" michael@0: #include "common/dwarf/dwarf2diehandler.h" michael@0: #include "common/dwarf_cfi_to_module.h" michael@0: #include "common/dwarf_cu_to_module.h" michael@0: #include "common/dwarf_line_to_module.h" michael@0: #include "common/linux/elfutils.h" michael@0: #include "common/linux/elfutils-inl.h" michael@0: #include "common/linux/elf_symbols_to_module.h" michael@0: @@ -65,16 +66,20 @@ michael@0: #include "common/scoped_ptr.h" michael@0: #ifndef NO_STABS_SUPPORT michael@0: #include "common/stabs_reader.h" michael@0: #include "common/stabs_to_module.h" michael@0: #endif michael@0: #include "common/using_std_string.h" michael@0: #include "common/logging.h" michael@0: michael@0: +#if defined(__ANDROID__) && !defined(SHT_ARM_EXIDX) michael@0: +# define SHT_ARM_EXIDX (SHT_LOPROC + 1) michael@0: +#endif michael@0: + michael@0: // This namespace contains helper functions. michael@0: namespace { michael@0: michael@0: using google_breakpad::DwarfCFIToModule; michael@0: using google_breakpad::DwarfCUToModule; michael@0: using google_breakpad::DwarfLineToModule; michael@0: using google_breakpad::ElfClass; michael@0: using google_breakpad::ElfClass32; michael@0: @@ -340,16 +345,62 @@ bool LoadDwarfCFI(const string& dwarf_fi michael@0: section_name); michael@0: dwarf2reader::CallFrameInfo parser(cfi, cfi_size, michael@0: &byte_reader, &handler, &dwarf_reporter, michael@0: eh_frame); michael@0: parser.Start(); michael@0: return true; michael@0: } michael@0: michael@0: +template michael@0: +bool LoadARMexidx(const typename ElfClass::Ehdr* elf_header, michael@0: + const typename ElfClass::Shdr* exidx_section, michael@0: + const typename ElfClass::Shdr* extab_section, michael@0: + uint32_t loading_addr, michael@0: + Module* module) { michael@0: + // To do this properly we need to know: michael@0: + // * the bounds of the .ARM.exidx section in the mapped image michael@0: + // * the bounds of the .ARM.extab section in the mapped image michael@0: + // * the vma of the last byte in the text section associated with the .exidx michael@0: + // The first two are easy. The third is a bit tricky. If we can't michael@0: + // figure out what it is, just pass in zero. michael@0: + const char *exidx_img michael@0: + = GetOffset(elf_header, exidx_section->sh_offset); michael@0: + size_t exidx_size = exidx_section->sh_size; michael@0: + const char *extab_img michael@0: + = GetOffset(elf_header, extab_section->sh_offset); michael@0: + size_t extab_size = extab_section->sh_size; michael@0: + michael@0: + // The sh_link field of the exidx section gives the section number michael@0: + // for the associated text section. michael@0: + uint32_t exidx_text_last_svma = 0; michael@0: + int exidx_text_sno = exidx_section->sh_link; michael@0: + typedef typename ElfClass::Shdr Shdr; michael@0: + // |sections| points to the section header table michael@0: + const Shdr* sections michael@0: + = GetOffset(elf_header, elf_header->e_shoff); michael@0: + const int num_sections = elf_header->e_shnum; michael@0: + if (exidx_text_sno >= 0 && exidx_text_sno < num_sections) { michael@0: + const Shdr* exidx_text_shdr = §ions[exidx_text_sno]; michael@0: + if (exidx_text_shdr->sh_size > 0) { michael@0: + exidx_text_last_svma michael@0: + = exidx_text_shdr->sh_addr + exidx_text_shdr->sh_size - 1; michael@0: + } michael@0: + } michael@0: + michael@0: + arm_ex_to_module::ARMExToModule handler(module); michael@0: + arm_ex_reader::ExceptionTableInfo michael@0: + parser(exidx_img, exidx_size, extab_img, extab_size, exidx_text_last_svma, michael@0: + &handler, michael@0: + reinterpret_cast(elf_header), michael@0: + loading_addr); michael@0: + parser.Start(); michael@0: + return true; michael@0: +} michael@0: + michael@0: bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper, michael@0: void** elf_header) { michael@0: int obj_fd = open(obj_file.c_str(), O_RDONLY); michael@0: if (obj_fd < 0) { michael@0: fprintf(stderr, "Failed to open ELF file '%s': %s\n", michael@0: obj_file.c_str(), strerror(errno)); michael@0: return false; michael@0: } michael@0: @@ -629,16 +680,39 @@ bool LoadSymbols(const string& obj_file, michael@0: eh_frame_section, true, michael@0: got_section, text_section, big_endian, module); michael@0: found_usable_info = found_usable_info || result; michael@0: if (result) michael@0: BPLOG(INFO) << "LoadSymbols: read CFI from .eh_frame"; michael@0: } michael@0: } michael@0: michael@0: + // ARM has special unwind tables that can be used. michael@0: + const Shdr* arm_exidx_section = michael@0: + FindElfSectionByName(".ARM.exidx", SHT_ARM_EXIDX, michael@0: + sections, names, names_end, michael@0: + elf_header->e_shnum); michael@0: + const Shdr* arm_extab_section = michael@0: + FindElfSectionByName(".ARM.extab", SHT_PROGBITS, michael@0: + sections, names, names_end, michael@0: + elf_header->e_shnum); michael@0: + // Only load information from this section if there isn't a .debug_info michael@0: + // section. michael@0: + if (!found_debug_info_section michael@0: + && arm_exidx_section && arm_extab_section && symbol_data != NO_CFI) { michael@0: + info->LoadedSection(".ARM.exidx"); michael@0: + info->LoadedSection(".ARM.extab"); michael@0: + bool result = LoadARMexidx(elf_header, michael@0: + arm_exidx_section, arm_extab_section, michael@0: + loading_addr, module); michael@0: + found_usable_info = found_usable_info || result; michael@0: + if (result) michael@0: + BPLOG(INFO) << "LoadSymbols: read EXIDX from .ARM.{exidx,extab}"; michael@0: + } michael@0: + michael@0: if (!found_debug_info_section && symbol_data != ONLY_CFI) { michael@0: fprintf(stderr, "%s: file contains no debugging information" michael@0: " (no \".stab\" or \".debug_info\" sections)\n", michael@0: obj_file.c_str()); michael@0: michael@0: // Failed, but maybe there's a .gnu_debuglink section? michael@0: if (read_gnu_debug_link) { michael@0: const Shdr* gnu_debuglink_section michael@0: diff --git a/src/common/module.cc b/src/common/module.cc michael@0: --- a/src/common/module.cc michael@0: +++ b/src/common/module.cc michael@0: @@ -253,17 +253,17 @@ void Module::AssignSourceIds() { michael@0: michael@0: bool Module::ReportError() { michael@0: fprintf(stderr, "error writing symbol file: %s\n", michael@0: strerror(errno)); michael@0: return false; michael@0: } michael@0: michael@0: std::ostream& operator<<(std::ostream& stream, const Module::Expr& expr) { michael@0: - assert(!expr.invalid()); michael@0: + assert(!expr.isExprInvalid()); michael@0: switch (expr.how_) { michael@0: case Module::kExprSimple: michael@0: stream << FromUniqueString(expr.ident_) << " " << expr.offset_ << " +"; michael@0: break; michael@0: case Module::kExprSimpleMem: michael@0: stream << FromUniqueString(expr.ident_) << " " << expr.offset_ << " + ^"; michael@0: break; michael@0: case Module::kExprPostfix: michael@0: diff --git a/src/common/module.h b/src/common/module.h michael@0: --- a/src/common/module.h michael@0: +++ b/src/common/module.h michael@0: @@ -160,17 +160,24 @@ class Module { michael@0: } michael@0: // Construct an invalid expression michael@0: Expr() { michael@0: postfix_ = ""; michael@0: ident_ = NULL; michael@0: offset_ = 0; michael@0: how_ = kExprInvalid; michael@0: } michael@0: - bool invalid() const { return how_ == kExprInvalid; } michael@0: + bool isExprInvalid() const { return how_ == kExprInvalid; } michael@0: + bool isExprPostfix() const { return how_ == kExprPostfix; } michael@0: + michael@0: + // Return the postfix expression string. This is only michael@0: + // meaningful on Exprs for which isExprPostfix returns true. michael@0: + // In all other cases it returns an empty string. michael@0: + string getExprPostfix() const { return postfix_; } michael@0: + michael@0: bool operator==(const Expr& other) const { michael@0: return how_ == other.how_ && michael@0: ident_ == other.ident_ && michael@0: offset_ == other.offset_ && michael@0: postfix_ == other.postfix_; michael@0: } michael@0: michael@0: // The identifier that gives the starting value for simple expressions. michael@0: diff --git a/src/common/unique_string.h b/src/common/unique_string.h michael@0: --- a/src/common/unique_string.h michael@0: +++ b/src/common/unique_string.h michael@0: @@ -230,16 +230,37 @@ inline static const UniqueString* ustr__ michael@0: michael@0: // ".ra" michael@0: inline static const UniqueString* ustr__ZDra() { michael@0: static const UniqueString* us = NULL; michael@0: if (!us) us = ToUniqueString(".ra"); michael@0: return us; michael@0: } michael@0: michael@0: +// "pc" michael@0: +inline static const UniqueString* ustr__pc() { michael@0: + static const UniqueString* us = NULL; michael@0: + if (!us) us = ToUniqueString("pc"); michael@0: + return us; michael@0: +} michael@0: + michael@0: +// "lr" michael@0: +inline static const UniqueString* ustr__lr() { michael@0: + static const UniqueString* us = NULL; michael@0: + if (!us) us = ToUniqueString("lr"); michael@0: + return us; michael@0: +} michael@0: + michael@0: +// "sp" michael@0: +inline static const UniqueString* ustr__sp() { michael@0: + static const UniqueString* us = NULL; michael@0: + if (!us) us = ToUniqueString("sp"); michael@0: + return us; michael@0: +} michael@0: + michael@0: template michael@0: class UniqueStringMap michael@0: { michael@0: private: michael@0: static const int N_FIXED = 10; michael@0: michael@0: public: michael@0: UniqueStringMap() : n_fixed_(0), n_sets_(0), n_gets_(0), n_clears_(0) {}; michael@0: diff --git a/src/processor/cfi_frame_info.cc b/src/processor/cfi_frame_info.cc michael@0: --- a/src/processor/cfi_frame_info.cc michael@0: +++ b/src/processor/cfi_frame_info.cc michael@0: @@ -49,17 +49,17 @@ namespace google_breakpad { michael@0: #endif michael@0: michael@0: template michael@0: bool CFIFrameInfo::FindCallerRegs(const RegisterValueMap ®isters, michael@0: const MemoryRegion &memory, michael@0: RegisterValueMap *caller_registers) const { michael@0: // If there are not rules for both .ra and .cfa in effect at this address, michael@0: // don't use this CFI data for stack walking. michael@0: - if (cfa_rule_.invalid() || ra_rule_.invalid()) michael@0: + if (cfa_rule_.isExprInvalid() || ra_rule_.isExprInvalid()) michael@0: return false; michael@0: michael@0: RegisterValueMap working; michael@0: PostfixEvaluator evaluator(&working, &memory); michael@0: michael@0: caller_registers->clear(); michael@0: michael@0: // First, compute the CFA. michael@0: @@ -100,20 +100,20 @@ template bool CFIFrameInfo::FindCallerRe michael@0: template bool CFIFrameInfo::FindCallerRegs( michael@0: const RegisterValueMap ®isters, michael@0: const MemoryRegion &memory, michael@0: RegisterValueMap *caller_registers) const; michael@0: michael@0: string CFIFrameInfo::Serialize() const { michael@0: std::ostringstream stream; michael@0: michael@0: - if (!cfa_rule_.invalid()) { michael@0: + if (!cfa_rule_.isExprInvalid()) { michael@0: stream << ".cfa: " << cfa_rule_; michael@0: } michael@0: - if (!ra_rule_.invalid()) { michael@0: + if (!ra_rule_.isExprInvalid()) { michael@0: if (static_cast(stream.tellp()) != 0) michael@0: stream << " "; michael@0: stream << ".ra: " << ra_rule_; michael@0: } michael@0: michael@0: // Visit the register rules in alphabetical order. Because michael@0: // register_rules_ has the elements in some arbitrary order, michael@0: // get the names out into a vector, sort them, and visit in