1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/profiler/LulExidx.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,690 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 + 1.7 +/* libunwind - a platform-independent unwind library 1.8 + Copyright 2011 Linaro Limited 1.9 + 1.10 +This file is part of libunwind. 1.11 + 1.12 +Permission is hereby granted, free of charge, to any person obtaining 1.13 +a copy of this software and associated documentation files (the 1.14 +"Software"), to deal in the Software without restriction, including 1.15 +without limitation the rights to use, copy, modify, merge, publish, 1.16 +distribute, sublicense, and/or sell copies of the Software, and to 1.17 +permit persons to whom the Software is furnished to do so, subject to 1.18 +the following conditions: 1.19 + 1.20 +The above copyright notice and this permission notice shall be 1.21 +included in all copies or substantial portions of the Software. 1.22 + 1.23 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1.24 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1.25 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 1.26 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 1.27 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 1.28 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1.29 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 1.30 + 1.31 + 1.32 +// Copyright (c) 2010 Google Inc. 1.33 +// All rights reserved. 1.34 +// 1.35 +// Redistribution and use in source and binary forms, with or without 1.36 +// modification, are permitted provided that the following conditions are 1.37 +// met: 1.38 +// 1.39 +// * Redistributions of source code must retain the above copyright 1.40 +// notice, this list of conditions and the following disclaimer. 1.41 +// * Redistributions in binary form must reproduce the above 1.42 +// copyright notice, this list of conditions and the following disclaimer 1.43 +// in the documentation and/or other materials provided with the 1.44 +// distribution. 1.45 +// * Neither the name of Google Inc. nor the names of its 1.46 +// contributors may be used to endorse or promote products derived from 1.47 +// this software without specific prior written permission. 1.48 +// 1.49 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.50 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.51 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.52 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.53 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.54 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.55 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.56 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.57 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.58 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.59 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.60 + 1.61 + 1.62 +// Derived from libunwind, with extensive modifications. 1.63 + 1.64 +// This file translates EXIDX unwind information into the same format 1.65 +// that LUL uses for CFI information. Hence LUL's CFI unwinding 1.66 +// abilities also become usable for EXIDX. 1.67 +// 1.68 +// See: "Exception Handling ABI for the ARM Architecture", ARM IHI 0038A 1.69 +// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf 1.70 + 1.71 +// EXIDX data is presented in two parts: 1.72 +// 1.73 +// * an index table. This contains two words per routine, 1.74 +// the first of which identifies the routine, and the second 1.75 +// of which is a reference to the unwind bytecode. If the 1.76 +// bytecode is very compact -- 3 bytes or less -- it can be 1.77 +// stored directly in the second word. 1.78 +// 1.79 +// * an area containing the unwind bytecodes. 1.80 +// 1.81 +// General flow is: ExceptionTableInfo::Start iterates over all 1.82 +// of the index table entries (pairs). For each entry, it: 1.83 +// 1.84 +// * calls ExceptionTableInfo::ExtabEntryExtract to copy the bytecode 1.85 +// out into an intermediate buffer. 1.86 + 1.87 +// * uses ExceptionTableInfo::ExtabEntryDecode to parse the intermediate 1.88 +// buffer. Each bytecode instruction is bundled into a 1.89 +// arm_ex_to_module::extab_data structure, and handed to .. 1.90 +// 1.91 +// * .. ARMExToModule::ImproveStackFrame, which in turn hands it to 1.92 +// ARMExToModule::TranslateCmd, and that generates the pseudo-CFI 1.93 +// records that Breakpad stores. 1.94 + 1.95 +// This file is derived from the following files in 1.96 +// toolkit/crashreporter/google-breakpad: 1.97 +// src/common/arm_ex_to_module.cc 1.98 +// src/common/arm_ex_reader.cc 1.99 + 1.100 +#include "mozilla/Assertions.h" 1.101 +#include "mozilla/NullPtr.h" 1.102 + 1.103 +#include "LulExidxExt.h" 1.104 + 1.105 + 1.106 +#define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f) 1.107 +#define ARM_EXBUF_COUNT(x) ((x) & 0x0f) 1.108 +#define ARM_EXBUF_END(x) (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x)) 1.109 + 1.110 +namespace lul { 1.111 + 1.112 +// Translate command from extab_data to command for Module. 1.113 +int ARMExToModule::TranslateCmd(const struct extab_data* edata, 1.114 + LExpr& vsp) { 1.115 + int ret = 0; 1.116 + switch (edata->cmd) { 1.117 + case ARM_EXIDX_CMD_FINISH: 1.118 + /* Copy LR to PC if there isn't currently a rule for PC in force. */ 1.119 + if (curr_rules_.mR15expr.mHow == LExpr::UNKNOWN) { 1.120 + if (curr_rules_.mR14expr.mHow == LExpr::UNKNOWN) { 1.121 + curr_rules_.mR15expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R14, 0); 1.122 + } else { 1.123 + curr_rules_.mR15expr = curr_rules_.mR14expr; 1.124 + } 1.125 + } 1.126 + break; 1.127 + case ARM_EXIDX_CMD_SUB_FROM_VSP: 1.128 + vsp = vsp.add_delta(- static_cast<long>(edata->data)); 1.129 + break; 1.130 + case ARM_EXIDX_CMD_ADD_TO_VSP: 1.131 + vsp = vsp.add_delta(static_cast<long>(edata->data)); 1.132 + break; 1.133 + case ARM_EXIDX_CMD_REG_POP: 1.134 + for (unsigned int i = 0; i < 16; i++) { 1.135 + if (edata->data & (1 << i)) { 1.136 + // See if we're summarising for int register |i|. If so, 1.137 + // describe how to pull it off the stack. The cast of |i| is 1.138 + // a bit of a kludge but works because DW_REG_ARM_Rn has the 1.139 + // value |n|, for 0 <= |n| <= 15 -- that is, for the ARM 1.140 + // general-purpose registers. 1.141 + LExpr* regI_exprP = curr_rules_.ExprForRegno((DW_REG_NUMBER)i); 1.142 + if (regI_exprP) { 1.143 + *regI_exprP = vsp.deref(); 1.144 + } 1.145 + vsp = vsp.add_delta(4); 1.146 + } 1.147 + } 1.148 + /* Set cfa in case the SP got popped. */ 1.149 + if (edata->data & (1 << 13)) { 1.150 + vsp = curr_rules_.mR13expr; 1.151 + } 1.152 + break; 1.153 + case ARM_EXIDX_CMD_REG_TO_SP: { 1.154 + MOZ_ASSERT (edata->data < 16); 1.155 + int reg_no = edata->data; 1.156 + // Same comment as above, re the casting of |reg_no|, applies. 1.157 + LExpr* reg_exprP = curr_rules_.ExprForRegno((DW_REG_NUMBER)reg_no); 1.158 + if (reg_exprP) { 1.159 + if (reg_exprP->mHow == LExpr::UNKNOWN) { 1.160 + curr_rules_.mR13expr = LExpr(LExpr::NODEREF, reg_no, 0); 1.161 + } else { 1.162 + curr_rules_.mR13expr = *reg_exprP; 1.163 + } 1.164 + vsp = curr_rules_.mR13expr; 1.165 + } 1.166 + break; 1.167 + } 1.168 + case ARM_EXIDX_CMD_VFP_POP: 1.169 + /* Don't recover VFP registers, but be sure to adjust the stack 1.170 + pointer. */ 1.171 + for (unsigned int i = ARM_EXBUF_START(edata->data); 1.172 + i <= ARM_EXBUF_END(edata->data); i++) { 1.173 + vsp = vsp.add_delta(8); 1.174 + } 1.175 + if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) { 1.176 + vsp = vsp.add_delta(4); 1.177 + } 1.178 + break; 1.179 + case ARM_EXIDX_CMD_WREG_POP: 1.180 + for (unsigned int i = ARM_EXBUF_START(edata->data); 1.181 + i <= ARM_EXBUF_END(edata->data); i++) { 1.182 + vsp = vsp.add_delta(8); 1.183 + } 1.184 + break; 1.185 + case ARM_EXIDX_CMD_WCGR_POP: 1.186 + // Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4" 1.187 + for (unsigned int i = 0; i < 4; i++) { 1.188 + if (edata->data & (1 << i)) { 1.189 + vsp = vsp.add_delta(4); 1.190 + } 1.191 + } 1.192 + break; 1.193 + case ARM_EXIDX_CMD_REFUSED: 1.194 + case ARM_EXIDX_CMD_RESERVED: 1.195 + ret = -1; 1.196 + break; 1.197 + } 1.198 + return ret; 1.199 +} 1.200 + 1.201 +void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) { 1.202 + // Here we are effectively reinitialising the EXIDX summariser for a 1.203 + // new code address range. smap_ stays unchanged. All other fields 1.204 + // are reinitialised. 1.205 + vsp_ = LExpr(LExpr::NODEREF, DW_REG_ARM_R13, 0); 1.206 + (void) new (&curr_rules_) RuleSet(); 1.207 + curr_rules_.mAddr = (uintptr_t)addr; 1.208 + curr_rules_.mLen = (uintptr_t)size; 1.209 + if (0) { 1.210 + char buf[100]; 1.211 + sprintf(buf, " AddStackFrame %llx .. %llx", 1.212 + (uint64_t)addr, (uint64_t)(addr + size - 1)); 1.213 + log_(buf); 1.214 + } 1.215 +} 1.216 + 1.217 +int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) { 1.218 + return TranslateCmd(edata, vsp_) ; 1.219 +} 1.220 + 1.221 +void ARMExToModule::DeleteStackFrame() { 1.222 +} 1.223 + 1.224 +void ARMExToModule::SubmitStackFrame() { 1.225 + // JRS: I'm really not sure what this means, or if it is necessary 1.226 + // return address always winds up in pc 1.227 + //stack_frame_entry_->initial_rules[ustr__ZDra()] // ".ra" 1.228 + // = stack_frame_entry_->initial_rules[ustr__pc()]; 1.229 + // maybe don't need to do anything here? 1.230 + 1.231 + // the final value of vsp is the new value of sp 1.232 + curr_rules_.mR13expr = vsp_; 1.233 + 1.234 + // Finally, add the completed RuleSet to the SecMap 1.235 + if (curr_rules_.mLen > 0) { 1.236 + 1.237 + // Futz with the rules for r4 .. r11 in the same way as happens 1.238 + // with the CFI summariser: 1.239 + /* Mark callee-saved registers (r4 .. r11) as unchanged, if there is 1.240 + no other information about them. FIXME: do this just once, at 1.241 + the point where the ruleset is committed. */ 1.242 + if (curr_rules_.mR7expr.mHow == LExpr::UNKNOWN) { 1.243 + curr_rules_.mR7expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R7, 0); 1.244 + } 1.245 + if (curr_rules_.mR11expr.mHow == LExpr::UNKNOWN) { 1.246 + curr_rules_.mR11expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R11, 0); 1.247 + } 1.248 + if (curr_rules_.mR12expr.mHow == LExpr::UNKNOWN) { 1.249 + curr_rules_.mR12expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R12, 0); 1.250 + } 1.251 + if (curr_rules_.mR14expr.mHow == LExpr::UNKNOWN) { 1.252 + curr_rules_.mR14expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R14, 0); 1.253 + } 1.254 + 1.255 + // And add them 1.256 + smap_->AddRuleSet(&curr_rules_); 1.257 + 1.258 + if (0) { 1.259 + curr_rules_.Print(log_); 1.260 + } 1.261 + if (0) { 1.262 + char buf[100]; 1.263 + sprintf(buf, " SubmitStackFrame %llx .. %llx", 1.264 + (uint64_t)curr_rules_.mAddr, 1.265 + (uint64_t)(curr_rules_.mAddr + curr_rules_.mLen - 1)); 1.266 + log_(buf); 1.267 + } 1.268 + } 1.269 +} 1.270 + 1.271 + 1.272 +#define ARM_EXIDX_CANT_UNWIND 0x00000001 1.273 +#define ARM_EXIDX_COMPACT 0x80000000 1.274 +#define ARM_EXTBL_OP_FINISH 0xb0 1.275 +#define ARM_EXIDX_TABLE_LIMIT (255*4) 1.276 + 1.277 +using lul::ARM_EXIDX_CMD_FINISH; 1.278 +using lul::ARM_EXIDX_CMD_SUB_FROM_VSP; 1.279 +using lul::ARM_EXIDX_CMD_ADD_TO_VSP; 1.280 +using lul::ARM_EXIDX_CMD_REG_POP; 1.281 +using lul::ARM_EXIDX_CMD_REG_TO_SP; 1.282 +using lul::ARM_EXIDX_CMD_VFP_POP; 1.283 +using lul::ARM_EXIDX_CMD_WREG_POP; 1.284 +using lul::ARM_EXIDX_CMD_WCGR_POP; 1.285 +using lul::ARM_EXIDX_CMD_RESERVED; 1.286 +using lul::ARM_EXIDX_CMD_REFUSED; 1.287 +using lul::exidx_entry; 1.288 +using lul::ARM_EXIDX_VFP_SHIFT_16; 1.289 +using lul::ARM_EXIDX_VFP_FSTMD; 1.290 +using lul::MemoryRange; 1.291 + 1.292 + 1.293 +static void* Prel31ToAddr(const void* addr) 1.294 +{ 1.295 + uint32_t offset32 = *reinterpret_cast<const uint32_t*>(addr); 1.296 + // sign extend offset32[30:0] to 64 bits -- copy bit 30 to positions 1.297 + // 63:31 inclusive. 1.298 + uint64_t offset64 = offset32; 1.299 + if (offset64 & (1ULL << 30)) 1.300 + offset64 |= 0xFFFFFFFF80000000ULL; 1.301 + else 1.302 + offset64 &= 0x000000007FFFFFFFULL; 1.303 + return ((char*)addr) + (uintptr_t)offset64; 1.304 +} 1.305 + 1.306 + 1.307 +// Extract unwind bytecode for the function denoted by |entry| into |buf|, 1.308 +// and return the number of bytes of |buf| written, along with a code 1.309 +// indicating the outcome. 1.310 + 1.311 +ExceptionTableInfo::ExExtractResult 1.312 +ExceptionTableInfo::ExtabEntryExtract(const struct exidx_entry* entry, 1.313 + uint8_t* buf, size_t buf_size, 1.314 + /*OUT*/size_t* buf_used) 1.315 +{ 1.316 + MemoryRange mr_out(buf, buf_size); 1.317 + 1.318 + *buf_used = 0; 1.319 + 1.320 +# define PUT_BUF_U8(_byte) \ 1.321 + do { if (!mr_out.Covers(*buf_used, 1)) return ExOutBufOverflow; \ 1.322 + buf[(*buf_used)++] = (_byte); } while (0) 1.323 + 1.324 +# define GET_EX_U32(_lval, _addr, _sec_mr) \ 1.325 + do { if (!(_sec_mr).Covers(reinterpret_cast<const uint8_t*>(_addr) \ 1.326 + - (_sec_mr).data(), 4)) \ 1.327 + return ExInBufOverflow; \ 1.328 + (_lval) = *(reinterpret_cast<const uint32_t*>(_addr)); } while (0) 1.329 + 1.330 +# define GET_EXIDX_U32(_lval, _addr) \ 1.331 + GET_EX_U32(_lval, _addr, mr_exidx_) 1.332 +# define GET_EXTAB_U32(_lval, _addr) \ 1.333 + GET_EX_U32(_lval, _addr, mr_extab_) 1.334 + 1.335 + uint32_t data; 1.336 + GET_EXIDX_U32(data, &entry->data); 1.337 + 1.338 + // A function can be marked CANT_UNWIND if (eg) it is known to be 1.339 + // at the bottom of the stack. 1.340 + if (data == ARM_EXIDX_CANT_UNWIND) 1.341 + return ExCantUnwind; 1.342 + 1.343 + uint32_t pers; // personality number 1.344 + uint32_t extra; // number of extra data words required 1.345 + uint32_t extra_allowed; // number of extra data words allowed 1.346 + uint32_t* extbl_data; // the handler entry, if not inlined 1.347 + 1.348 + if (data & ARM_EXIDX_COMPACT) { 1.349 + // The handler table entry has been inlined into the index table entry. 1.350 + // In this case it can only be an ARM-defined compact model, since 1.351 + // bit 31 is 1. Only personalities 0, 1 and 2 are defined for the 1.352 + // ARM compact model, but 1 and 2 are "Long format" and may require 1.353 + // extra data words. Hence the allowable personalities here are: 1.354 + // personality 0, in which case 'extra' has no meaning 1.355 + // personality 1, with zero extra words 1.356 + // personality 2, with zero extra words 1.357 + extbl_data = nullptr; 1.358 + pers = (data >> 24) & 0x0F; 1.359 + extra = (data >> 16) & 0xFF; 1.360 + extra_allowed = 0; 1.361 + } 1.362 + else { 1.363 + // The index table entry is a pointer to the handler entry. Note 1.364 + // that Prel31ToAddr will read the given address, but we already 1.365 + // range-checked above. 1.366 + extbl_data = reinterpret_cast<uint32_t*>(Prel31ToAddr(&entry->data)); 1.367 + GET_EXTAB_U32(data, extbl_data); 1.368 + if (!(data & ARM_EXIDX_COMPACT)) { 1.369 + // This denotes a "generic model" handler. That will involve 1.370 + // executing arbitary machine code, which is something we 1.371 + // can't represent here; hence reject it. 1.372 + return ExCantRepresent; 1.373 + } 1.374 + // So we have a compact model representation. Again, 3 possible 1.375 + // personalities, but this time up to 255 allowable extra words. 1.376 + pers = (data >> 24) & 0x0F; 1.377 + extra = (data >> 16) & 0xFF; 1.378 + extra_allowed = 255; 1.379 + extbl_data++; 1.380 + } 1.381 + 1.382 + // Now look at the the handler table entry. The first word is 1.383 + // |data| and subsequent words start at |*extbl_data|. The number 1.384 + // of extra words to use is |extra|, provided that the personality 1.385 + // allows extra words. Even if it does, none may be available -- 1.386 + // extra_allowed is the maximum number of extra words allowed. */ 1.387 + if (pers == 0) { 1.388 + // "Su16" in the documentation -- 3 unwinding insn bytes 1.389 + // |extra| has no meaning here; instead that byte is an unwind-info byte 1.390 + PUT_BUF_U8(data >> 16); 1.391 + PUT_BUF_U8(data >> 8); 1.392 + PUT_BUF_U8(data); 1.393 + } 1.394 + else if ((pers == 1 || pers == 2) && extra <= extra_allowed) { 1.395 + // "Lu16" or "Lu32" respectively -- 2 unwinding insn bytes, 1.396 + // and up to 255 extra words. 1.397 + PUT_BUF_U8(data >> 8); 1.398 + PUT_BUF_U8(data); 1.399 + for (uint32_t j = 0; j < extra; j++) { 1.400 + GET_EXTAB_U32(data, extbl_data); 1.401 + extbl_data++; 1.402 + PUT_BUF_U8(data >> 24); 1.403 + PUT_BUF_U8(data >> 16); 1.404 + PUT_BUF_U8(data >> 8); 1.405 + PUT_BUF_U8(data >> 0); 1.406 + } 1.407 + } 1.408 + else { 1.409 + // The entry is invalid. 1.410 + return ExInvalid; 1.411 + } 1.412 + 1.413 + // Make sure the entry is terminated with "FINISH" 1.414 + if (*buf_used > 0 && buf[(*buf_used) - 1] != ARM_EXTBL_OP_FINISH) 1.415 + PUT_BUF_U8(ARM_EXTBL_OP_FINISH); 1.416 + 1.417 + return ExSuccess; 1.418 + 1.419 +# undef GET_EXTAB_U32 1.420 +# undef GET_EXIDX_U32 1.421 +# undef GET_U32 1.422 +# undef PUT_BUF_U8 1.423 +} 1.424 + 1.425 + 1.426 +// Take the unwind information extracted by ExtabEntryExtract 1.427 +// and parse it into frame-unwind instructions. These are as 1.428 +// specified in "Table 4, ARM-defined frame-unwinding instructions" 1.429 +// in the specification document detailed in comments at the top 1.430 +// of this file. 1.431 +// 1.432 +// This reads from |buf[0, +data_size)|. It checks for overruns of 1.433 +// the input buffer and returns a negative value if that happens, or 1.434 +// for any other failure cases. It returns zero in case of success. 1.435 +int ExceptionTableInfo::ExtabEntryDecode(const uint8_t* buf, size_t buf_size) 1.436 +{ 1.437 + if (buf == nullptr || buf_size == 0) 1.438 + return -1; 1.439 + 1.440 + MemoryRange mr_in(buf, buf_size); 1.441 + const uint8_t* buf_initially = buf; 1.442 + 1.443 +# define GET_BUF_U8(_lval) \ 1.444 + do { if (!mr_in.Covers(buf - buf_initially, 1)) return -1; \ 1.445 + (_lval) = *(buf++); } while (0) 1.446 + 1.447 + const uint8_t* end = buf + buf_size; 1.448 + 1.449 + while (buf < end) { 1.450 + struct lul::extab_data edata; 1.451 + memset(&edata, 0, sizeof(edata)); 1.452 + 1.453 + uint8_t op; 1.454 + GET_BUF_U8(op); 1.455 + if ((op & 0xc0) == 0x00) { 1.456 + // vsp = vsp + (xxxxxx << 2) + 4 1.457 + edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP; 1.458 + edata.data = (((int)op & 0x3f) << 2) + 4; 1.459 + } 1.460 + else if ((op & 0xc0) == 0x40) { 1.461 + // vsp = vsp - (xxxxxx << 2) - 4 1.462 + edata.cmd = ARM_EXIDX_CMD_SUB_FROM_VSP; 1.463 + edata.data = (((int)op & 0x3f) << 2) + 4; 1.464 + } 1.465 + else if ((op & 0xf0) == 0x80) { 1.466 + uint8_t op2; 1.467 + GET_BUF_U8(op2); 1.468 + if (op == 0x80 && op2 == 0x00) { 1.469 + // Refuse to unwind 1.470 + edata.cmd = ARM_EXIDX_CMD_REFUSED; 1.471 + } else { 1.472 + // Pop up to 12 integer registers under masks {r15-r12},{r11-r4} 1.473 + edata.cmd = ARM_EXIDX_CMD_REG_POP; 1.474 + edata.data = ((op & 0xf) << 8) | op2; 1.475 + edata.data = edata.data << 4; 1.476 + } 1.477 + } 1.478 + else if ((op & 0xf0) == 0x90) { 1.479 + if (op == 0x9d || op == 0x9f) { 1.480 + // 9d: Reserved as prefix for ARM register to register moves 1.481 + // 9f: Reserved as perfix for Intel Wireless MMX reg to reg moves 1.482 + edata.cmd = ARM_EXIDX_CMD_RESERVED; 1.483 + } else { 1.484 + // Set vsp = r[nnnn] 1.485 + edata.cmd = ARM_EXIDX_CMD_REG_TO_SP; 1.486 + edata.data = op & 0x0f; 1.487 + } 1.488 + } 1.489 + else if ((op & 0xf0) == 0xa0) { 1.490 + // Pop r4 to r[4+nnn], or 1.491 + // Pop r4 to r[4+nnn] and r14 or 1.492 + unsigned end = (op & 0x07); 1.493 + edata.data = (1 << (end + 1)) - 1; 1.494 + edata.data = edata.data << 4; 1.495 + if (op & 0x08) edata.data |= 1 << 14; 1.496 + edata.cmd = ARM_EXIDX_CMD_REG_POP; 1.497 + } 1.498 + else if (op == ARM_EXTBL_OP_FINISH) { 1.499 + // Finish 1.500 + edata.cmd = ARM_EXIDX_CMD_FINISH; 1.501 + buf = end; 1.502 + } 1.503 + else if (op == 0xb1) { 1.504 + uint8_t op2; 1.505 + GET_BUF_U8(op2); 1.506 + if (op2 == 0 || (op2 & 0xf0)) { 1.507 + // Spare 1.508 + edata.cmd = ARM_EXIDX_CMD_RESERVED; 1.509 + } else { 1.510 + // Pop integer registers under mask {r3,r2,r1,r0} 1.511 + edata.cmd = ARM_EXIDX_CMD_REG_POP; 1.512 + edata.data = op2 & 0x0f; 1.513 + } 1.514 + } 1.515 + else if (op == 0xb2) { 1.516 + // vsp = vsp + 0x204 + (uleb128 << 2) 1.517 + uint64_t offset = 0; 1.518 + uint8_t byte, shift = 0; 1.519 + do { 1.520 + GET_BUF_U8(byte); 1.521 + offset |= (byte & 0x7f) << shift; 1.522 + shift += 7; 1.523 + } while ((byte & 0x80) && buf < end); 1.524 + edata.data = offset * 4 + 0x204; 1.525 + edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP; 1.526 + } 1.527 + else if (op == 0xb3 || op == 0xc8 || op == 0xc9) { 1.528 + // b3: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDX-ishly 1.529 + // c8: Pop VFP regs D[16+ssss] to D[16+ssss+cccc], FSTMFDD-ishly 1.530 + // c9: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDD-ishly 1.531 + edata.cmd = ARM_EXIDX_CMD_VFP_POP; 1.532 + GET_BUF_U8(edata.data); 1.533 + if (op == 0xc8) edata.data |= ARM_EXIDX_VFP_SHIFT_16; 1.534 + if (op != 0xb3) edata.data |= ARM_EXIDX_VFP_FSTMD; 1.535 + } 1.536 + else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0) { 1.537 + // b8: Pop VFP regs D[8] to D[8+nnn], FSTMFDX-ishly 1.538 + // d0: Pop VFP regs D[8] to D[8+nnn], FSTMFDD-ishly 1.539 + edata.cmd = ARM_EXIDX_CMD_VFP_POP; 1.540 + edata.data = 0x80 | (op & 0x07); 1.541 + if ((op & 0xf8) == 0xd0) edata.data |= ARM_EXIDX_VFP_FSTMD; 1.542 + } 1.543 + else if (op >= 0xc0 && op <= 0xc5) { 1.544 + // Intel Wireless MMX pop wR[10]-wr[10+nnn], nnn != 6,7 1.545 + edata.cmd = ARM_EXIDX_CMD_WREG_POP; 1.546 + edata.data = 0xa0 | (op & 0x07); 1.547 + } 1.548 + else if (op == 0xc6) { 1.549 + // Intel Wireless MMX pop wR[ssss] to wR[ssss+cccc] 1.550 + edata.cmd = ARM_EXIDX_CMD_WREG_POP; 1.551 + GET_BUF_U8(edata.data); 1.552 + } 1.553 + else if (op == 0xc7) { 1.554 + uint8_t op2; 1.555 + GET_BUF_U8(op2); 1.556 + if (op2 == 0 || (op2 & 0xf0)) { 1.557 + // Spare 1.558 + edata.cmd = ARM_EXIDX_CMD_RESERVED; 1.559 + } else { 1.560 + // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0} 1.561 + edata.cmd = ARM_EXIDX_CMD_WCGR_POP; 1.562 + edata.data = op2 & 0x0f; 1.563 + } 1.564 + } 1.565 + else { 1.566 + // Spare 1.567 + edata.cmd = ARM_EXIDX_CMD_RESERVED; 1.568 + } 1.569 + 1.570 + int ret = handler_->ImproveStackFrame(&edata); 1.571 + if (ret < 0) return ret; 1.572 + } 1.573 + return 0; 1.574 + 1.575 +# undef GET_BUF_U8 1.576 +} 1.577 + 1.578 +void ExceptionTableInfo::Start() 1.579 +{ 1.580 + const struct exidx_entry* start 1.581 + = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data()); 1.582 + const struct exidx_entry* end 1.583 + = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data() 1.584 + + mr_exidx_.length()); 1.585 + 1.586 + // Iterate over each of the EXIDX entries (pairs of 32-bit words). 1.587 + // These occupy the entire .exidx section. 1.588 + for (const struct exidx_entry* entry = start; entry < end; ++entry) { 1.589 + 1.590 + // Figure out the code address range that this table entry is 1.591 + // associated with. 1.592 + // 1.593 + // I don't claim to understand the biasing here. It appears that 1.594 + // (Prel31ToAddr(&entry->addr)) 1.595 + // - mapping_addr_ + loading_addr_) & 0x7fffffff 1.596 + // produces a SVMA. Adding the text_bias_ gives plausible AVMAs. 1.597 + uint32_t svma = (reinterpret_cast<char*>(Prel31ToAddr(&entry->addr)) 1.598 + - mapping_addr_ + loading_addr_) & 0x7fffffff; 1.599 + uint32_t next_svma; 1.600 + if (entry < end - 1) { 1.601 + next_svma = (reinterpret_cast<char*>(Prel31ToAddr(&((entry + 1)->addr))) 1.602 + - mapping_addr_ + loading_addr_) & 0x7fffffff; 1.603 + } else { 1.604 + // This is the last EXIDX entry in the sequence, so we don't 1.605 + // have an address for the start of the next function, to limit 1.606 + // this one. Instead use the address of the last byte of the 1.607 + // text section associated with this .exidx section, that we 1.608 + // have been given. So as to avoid junking up the CFI unwind 1.609 + // tables with absurdly large address ranges in the case where 1.610 + // text_last_svma_ is wrong, only use the value if it is nonzero 1.611 + // and within one page of |svma|. Otherwise assume a length of 1. 1.612 + // 1.613 + // In some cases, gcc has been observed to finish the exidx 1.614 + // section with an entry of length 1 marked CANT_UNWIND, 1.615 + // presumably exactly for the purpose of giving a definite 1.616 + // length for the last real entry, without having to look at 1.617 + // text segment boundaries. 1.618 + bool plausible = false; 1.619 + next_svma = svma + 1; 1.620 + if (text_last_svma_ != 0) { 1.621 + uint32_t maybe_next_svma = text_last_svma_ + 1; 1.622 + if (maybe_next_svma > svma && maybe_next_svma - svma <= 4096) { 1.623 + next_svma = maybe_next_svma; 1.624 + plausible = true; 1.625 + } 1.626 + } 1.627 + if (!plausible) { 1.628 + char buf[100]; 1.629 + snprintf(buf, sizeof(buf), 1.630 + "ExceptionTableInfo: implausible EXIDX last entry size %d" 1.631 + "; using 1 instead.", (int32_t)(text_last_svma_ - svma)); 1.632 + buf[sizeof(buf)-1] = 0; 1.633 + log_(buf); 1.634 + } 1.635 + } 1.636 + 1.637 + // Extract the unwind info into |buf|. This might fail for 1.638 + // various reasons. It involves reading both the .exidx and 1.639 + // .extab sections. All accesses to those sections are 1.640 + // bounds-checked. 1.641 + uint8_t buf[ARM_EXIDX_TABLE_LIMIT]; 1.642 + size_t buf_used = 0; 1.643 + ExExtractResult res = ExtabEntryExtract(entry, buf, sizeof(buf), &buf_used); 1.644 + if (res != ExSuccess) { 1.645 + // Couldn't extract the unwind info, for some reason. Move on. 1.646 + switch (res) { 1.647 + case ExInBufOverflow: 1.648 + log_("ExtabEntryExtract: .exidx/.extab section overrun"); 1.649 + break; 1.650 + case ExOutBufOverflow: 1.651 + log_("ExtabEntryExtract: bytecode buffer overflow"); 1.652 + break; 1.653 + case ExCantUnwind: 1.654 + log_("ExtabEntryExtract: function is marked CANT_UNWIND"); 1.655 + break; 1.656 + case ExCantRepresent: 1.657 + log_("ExtabEntryExtract: bytecode can't be represented"); 1.658 + break; 1.659 + case ExInvalid: 1.660 + log_("ExtabEntryExtract: index table entry is invalid"); 1.661 + break; 1.662 + default: { 1.663 + char buf[100]; 1.664 + snprintf(buf, sizeof(buf), 1.665 + "ExtabEntryExtract: unknown error: %d", (int)res); 1.666 + buf[sizeof(buf)-1] = 0; 1.667 + log_(buf); 1.668 + break; 1.669 + } 1.670 + } 1.671 + continue; 1.672 + } 1.673 + 1.674 + // Finally, work through the unwind instructions in |buf| and 1.675 + // create CFI entries that Breakpad can use. This can also fail. 1.676 + // First, add a new stack frame entry, into which ExtabEntryDecode 1.677 + // will write the CFI entries. 1.678 + handler_->AddStackFrame(svma + text_bias_, next_svma - svma); 1.679 + int ret = ExtabEntryDecode(buf, buf_used); 1.680 + if (ret < 0) { 1.681 + handler_->DeleteStackFrame(); 1.682 + char buf[100]; 1.683 + snprintf(buf, sizeof(buf), 1.684 + "ExtabEntryDecode: failed with error code: %d", ret); 1.685 + buf[sizeof(buf)-1] = 0; 1.686 + log_(buf); 1.687 + continue; 1.688 + } 1.689 + handler_->SubmitStackFrame(); 1.690 + } /* iterating over .exidx */ 1.691 +} 1.692 + 1.693 +} // namespace lul