tools/profiler/LulExidx.cpp

changeset 0
6474c204b198
     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

mercurial