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::ustr__ZDra; michael@0: using google_breakpad::ustr__ZDcfa; 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, michael@0: Module::Expr& 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(ustr__lr(), michael@0: 0, false); // "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: vsp = vsp.add_delta(- static_cast(edata->data)); michael@0: break; michael@0: case ARM_EXIDX_CMD_ADD_TO_VSP: michael@0: vsp = vsp.add_delta(static_cast(edata->data)); 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])] = vsp.deref(); michael@0: vsp = vsp.add_delta(4); michael@0: } michael@0: } michael@0: /* Set cfa in case the SP got popped. */ michael@0: if (edata->data & (1 << 13)) { michael@0: vsp = entry->initial_rules[ustr__sp()]; 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_us, michael@0: 0, false); // "regname" michael@0: } else { michael@0: entry->initial_rules[ustr__sp()] = entry->initial_rules[regname_us]; michael@0: } michael@0: vsp = entry->initial_rules[ustr__sp()]; 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 = vsp.add_delta(8); michael@0: } michael@0: if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) { michael@0: vsp = vsp.add_delta(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 = vsp.add_delta(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 = vsp.add_delta(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: Module::Expr sp_expr = Module::Expr(ustr__sp(), 0, false); // "sp" michael@0: stack_frame_entry_->initial_rules[ustr__ZDcfa()] = sp_expr; // ".cfa" michael@0: vsp_ = sp_expr; 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[ustr__ZDra()] // ".ra" 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