tools/profiler/EHABIStackWalk.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tools/profiler/EHABIStackWalk.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,642 @@
     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 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 + * This is an implementation of stack unwinding according to a subset
    1.12 + * of the ARM Exception Handling ABI, as described in:
    1.13 + *   http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
    1.14 + *
    1.15 + * This handles only the ARM-defined "personality routines" (chapter
    1.16 + * 9), and don't track the value of FP registers, because profiling
    1.17 + * needs only chain of PC/SP values.
    1.18 + *
    1.19 + * Because the exception handling info may not be accurate for all
    1.20 + * possible places where an async signal could occur (e.g., in a
    1.21 + * prologue or epilogue), this bounds-checks all stack accesses.
    1.22 + *
    1.23 + * This file uses "struct" for structures in the exception tables and
    1.24 + * "class" otherwise.  We should avoid violating the C++11
    1.25 + * standard-layout rules in the former.
    1.26 + */
    1.27 +
    1.28 +#include "EHABIStackWalk.h"
    1.29 +
    1.30 +#include "shared-libraries.h"
    1.31 +#include "platform.h"
    1.32 +
    1.33 +#include "mozilla/Atomics.h"
    1.34 +#include "mozilla/Attributes.h"
    1.35 +#include "mozilla/DebugOnly.h"
    1.36 +#include "mozilla/Endian.h"
    1.37 +
    1.38 +#include <algorithm>
    1.39 +#include <elf.h>
    1.40 +#include <stdint.h>
    1.41 +#include <vector>
    1.42 +#include <string>
    1.43 +
    1.44 +#ifndef PT_ARM_EXIDX
    1.45 +#define PT_ARM_EXIDX 0x70000001
    1.46 +#endif
    1.47 +
    1.48 +
    1.49 +namespace mozilla {
    1.50 +
    1.51 +struct EHEntry;
    1.52 +
    1.53 +class EHState {
    1.54 +  // Note that any core register can be used as a "frame pointer" to
    1.55 +  // influence the unwinding process, so this must track all of them.
    1.56 +  uint32_t mRegs[16];
    1.57 +public:
    1.58 +  bool unwind(const EHEntry *aEntry, const void *stackBase);
    1.59 +  uint32_t &operator[](int i) { return mRegs[i]; }
    1.60 +  const uint32_t &operator[](int i) const { return mRegs[i]; }
    1.61 +  EHState(const mcontext_t &);
    1.62 +};
    1.63 +
    1.64 +enum {
    1.65 +  R_SP = 13,
    1.66 +  R_LR = 14,
    1.67 +  R_PC = 15
    1.68 +};
    1.69 +
    1.70 +class EHEntryHandle {
    1.71 +  const EHEntry *mValue;
    1.72 +public:
    1.73 +  EHEntryHandle(const EHEntry *aEntry) : mValue(aEntry) { }
    1.74 +  const EHEntry *value() const { return mValue; }
    1.75 +};
    1.76 +
    1.77 +class EHTable {
    1.78 +  uint32_t mStartPC;
    1.79 +  uint32_t mEndPC;
    1.80 +  uint32_t mLoadOffset;
    1.81 +  // In principle we should be able to binary-search the index section in
    1.82 +  // place, but the ICS toolchain's linker is noncompliant and produces
    1.83 +  // indices that aren't entirely sorted (e.g., libc).  So we have this:
    1.84 +  std::vector<EHEntryHandle> mEntries;
    1.85 +  std::string mName;
    1.86 +public:
    1.87 +  EHTable(const void *aELF, size_t aSize, const std::string &aName);
    1.88 +  const EHEntry *lookup(uint32_t aPC) const;
    1.89 +  bool isValid() const { return mEntries.size() > 0; }
    1.90 +  const std::string &name() const { return mName; }
    1.91 +  uint32_t startPC() const { return mStartPC; }
    1.92 +  uint32_t endPC() const { return mEndPC; }
    1.93 +  uint32_t loadOffset() const { return mLoadOffset; }
    1.94 +};
    1.95 +
    1.96 +class EHAddrSpace {
    1.97 +  std::vector<uint32_t> mStarts;
    1.98 +  std::vector<EHTable> mTables;
    1.99 +  static mozilla::Atomic<const EHAddrSpace*> sCurrent;
   1.100 +public:
   1.101 +  explicit EHAddrSpace(const std::vector<EHTable>& aTables);
   1.102 +  const EHTable *lookup(uint32_t aPC) const;
   1.103 +  static void Update();
   1.104 +  static const EHAddrSpace *Get();
   1.105 +};
   1.106 +
   1.107 +
   1.108 +void EHABIStackWalkInit()
   1.109 +{
   1.110 +  EHAddrSpace::Update();
   1.111 +}
   1.112 +
   1.113 +size_t EHABIStackWalk(const mcontext_t &aContext, void *stackBase,
   1.114 +                      void **aSPs, void **aPCs, const size_t aNumFrames)
   1.115 +{
   1.116 +  const EHAddrSpace *space = EHAddrSpace::Get();
   1.117 +  EHState state(aContext);
   1.118 +  size_t count = 0;
   1.119 +
   1.120 +  while (count < aNumFrames) {
   1.121 +    uint32_t pc = state[R_PC], sp = state[R_SP];
   1.122 +    aPCs[count] = reinterpret_cast<void *>(pc);
   1.123 +    aSPs[count] = reinterpret_cast<void *>(sp);
   1.124 +    count++;
   1.125 +
   1.126 +    if (!space)
   1.127 +      break;
   1.128 +    // TODO: cache these lookups.  Binary-searching libxul is
   1.129 +    // expensive (possibly more expensive than doing the actual
   1.130 +    // unwind), and even a small cache should help.
   1.131 +    const EHTable *table = space->lookup(pc);
   1.132 +    if (!table)
   1.133 +      break;
   1.134 +    const EHEntry *entry = table->lookup(pc);
   1.135 +    if (!entry)
   1.136 +      break;
   1.137 +    if (!state.unwind(entry, stackBase))
   1.138 +      break;
   1.139 +  }
   1.140 +  
   1.141 +  return count;
   1.142 +}
   1.143 +
   1.144 +
   1.145 +struct PRel31 {
   1.146 +  uint32_t mBits;
   1.147 +  bool topBit() const { return mBits & 0x80000000; }
   1.148 +  uint32_t value() const { return mBits & 0x7fffffff; }
   1.149 +  int32_t offset() const { return (static_cast<int32_t>(mBits) << 1) >> 1; }
   1.150 +  const void *compute() const {
   1.151 +    return reinterpret_cast<const char *>(this) + offset();
   1.152 +  }
   1.153 +private:
   1.154 +  PRel31(const PRel31 &copied) MOZ_DELETE;
   1.155 +  PRel31() MOZ_DELETE;
   1.156 +};
   1.157 +
   1.158 +struct EHEntry {
   1.159 +  PRel31 startPC;
   1.160 +  PRel31 exidx;
   1.161 +private:
   1.162 +  EHEntry(const EHEntry &copied) MOZ_DELETE;
   1.163 +  EHEntry() MOZ_DELETE;
   1.164 +};
   1.165 +
   1.166 +
   1.167 +class EHInterp {
   1.168 +public:
   1.169 +  // Note that stackLimit is exclusive and stackBase is inclusive
   1.170 +  // (i.e, stackLimit < SP <= stackBase), following the convention
   1.171 +  // set by the AAPCS spec.
   1.172 +  EHInterp(EHState &aState, const EHEntry *aEntry,
   1.173 +           uint32_t aStackLimit, uint32_t aStackBase)
   1.174 +    : mState(aState),
   1.175 +      mStackLimit(aStackLimit),
   1.176 +      mStackBase(aStackBase),
   1.177 +      mNextWord(0),
   1.178 +      mWordsLeft(0),
   1.179 +      mFailed(false)
   1.180 +  {
   1.181 +    const PRel31 &exidx = aEntry->exidx;
   1.182 +    uint32_t firstWord;
   1.183 +
   1.184 +    if (exidx.mBits == 1) {  // EXIDX_CANTUNWIND
   1.185 +      mFailed = true;
   1.186 +      return;
   1.187 +    }
   1.188 +    if (exidx.topBit()) {
   1.189 +      firstWord = exidx.mBits;
   1.190 +    } else {
   1.191 +      mNextWord = reinterpret_cast<const uint32_t *>(exidx.compute());
   1.192 +      firstWord = *mNextWord++;
   1.193 +    }
   1.194 +
   1.195 +    switch (firstWord >> 24) {
   1.196 +    case 0x80: // short
   1.197 +      mWord = firstWord << 8;
   1.198 +      mBytesLeft = 3;
   1.199 +      break;
   1.200 +    case 0x81: case 0x82: // long; catch descriptor size ignored
   1.201 +      mWord = firstWord << 16;
   1.202 +      mBytesLeft = 2;
   1.203 +      mWordsLeft = (firstWord >> 16) & 0xff;
   1.204 +      break;
   1.205 +    default:
   1.206 +      // unknown personality
   1.207 +      mFailed = true;
   1.208 +    }
   1.209 +  }
   1.210 +
   1.211 +  bool unwind();
   1.212 +
   1.213 +private:
   1.214 +  // TODO: GCC has been observed not CSEing repeated reads of
   1.215 +  // mState[R_SP] with writes to mFailed between them, suggesting that
   1.216 +  // it hasn't determined that they can't alias and is thus missing
   1.217 +  // optimization opportunities.  So, we may want to flatten EHState
   1.218 +  // into this class; this may also make the code simpler.
   1.219 +  EHState &mState;
   1.220 +  uint32_t mStackLimit;
   1.221 +  uint32_t mStackBase;
   1.222 +  const uint32_t *mNextWord;
   1.223 +  uint32_t mWord;
   1.224 +  uint8_t mWordsLeft;
   1.225 +  uint8_t mBytesLeft;
   1.226 +  bool mFailed;
   1.227 +
   1.228 +  enum {
   1.229 +    I_ADDSP    = 0x00, // 0sxxxxxx (subtract if s)
   1.230 +    M_ADDSP    = 0x80,
   1.231 +    I_POPMASK  = 0x80, // 1000iiii iiiiiiii (if any i set)
   1.232 +    M_POPMASK  = 0xf0,
   1.233 +    I_MOVSP    = 0x90, // 1001nnnn
   1.234 +    M_MOVSP    = 0xf0,
   1.235 +    I_POPN     = 0xa0, // 1010lnnn
   1.236 +    M_POPN     = 0xf0,
   1.237 +    I_FINISH   = 0xb0, // 10110000
   1.238 +    I_POPLO    = 0xb1, // 10110001 0000iiii (if any i set)
   1.239 +    I_ADDSPBIG = 0xb2, // 10110010 uleb128
   1.240 +    I_POPFDX   = 0xb3, // 10110011 sssscccc
   1.241 +    I_POPFDX8  = 0xb8, // 10111nnn
   1.242 +    M_POPFDX8  = 0xf8,
   1.243 +    // "Intel Wireless MMX" extensions omitted.
   1.244 +    I_POPFDD   = 0xc8, // 1100100h sssscccc
   1.245 +    M_POPFDD   = 0xfe,
   1.246 +    I_POPFDD8  = 0xd0, // 11010nnn
   1.247 +    M_POPFDD8  = 0xf8
   1.248 +  };
   1.249 +
   1.250 +  uint8_t next() {
   1.251 +    if (mBytesLeft == 0) {
   1.252 +      if (mWordsLeft == 0) {
   1.253 +        return I_FINISH;
   1.254 +      }
   1.255 +      mWordsLeft--;
   1.256 +      mWord = *mNextWord++;
   1.257 +      mBytesLeft = 4;
   1.258 +    }
   1.259 +    mBytesLeft--;
   1.260 +    mWord = (mWord << 8) | (mWord >> 24); // rotate
   1.261 +    return mWord;
   1.262 +  }
   1.263 +
   1.264 +  uint32_t &vSP() { return mState[R_SP]; }
   1.265 +  uint32_t *ptrSP() { return reinterpret_cast<uint32_t *>(vSP()); }
   1.266 +
   1.267 +  void checkStackBase() { if (vSP() > mStackBase) mFailed = true; }
   1.268 +  void checkStackLimit() { if (vSP() <= mStackLimit) mFailed = true; }
   1.269 +  void checkStackAlign() { if ((vSP() & 3) != 0) mFailed = true; }
   1.270 +  void checkStack() {
   1.271 +    checkStackBase();
   1.272 +    checkStackLimit();
   1.273 +    checkStackAlign();
   1.274 +  }
   1.275 +
   1.276 +  void popRange(uint8_t first, uint8_t last, uint16_t mask) {
   1.277 +    bool hasSP = false;
   1.278 +    uint32_t tmpSP;
   1.279 +    if (mask == 0)
   1.280 +      mFailed = true;
   1.281 +    for (uint8_t r = first; r <= last; ++r) {
   1.282 +      if (mask & 1) {
   1.283 +        if (r == R_SP) {
   1.284 +          hasSP = true;
   1.285 +          tmpSP = *ptrSP();
   1.286 +        } else
   1.287 +          mState[r] = *ptrSP();
   1.288 +        vSP() += 4;
   1.289 +        checkStackBase();
   1.290 +        if (mFailed)
   1.291 +          return;
   1.292 +      }
   1.293 +      mask >>= 1;
   1.294 +    }
   1.295 +    if (hasSP) {
   1.296 +      vSP() = tmpSP;
   1.297 +      checkStack();
   1.298 +    }
   1.299 +  }
   1.300 +};
   1.301 +
   1.302 +
   1.303 +bool EHState::unwind(const EHEntry *aEntry, const void *stackBasePtr) {
   1.304 +  // The unwinding program cannot set SP to less than the initial value.
   1.305 +  uint32_t stackLimit = mRegs[R_SP] - 4;
   1.306 +  uint32_t stackBase = reinterpret_cast<uint32_t>(stackBasePtr);
   1.307 +  EHInterp interp(*this, aEntry, stackLimit, stackBase);
   1.308 +  return interp.unwind();
   1.309 +}
   1.310 +
   1.311 +bool EHInterp::unwind() {
   1.312 +  mState[R_PC] = 0;
   1.313 +  checkStack();
   1.314 +  while (!mFailed) {
   1.315 +    uint8_t insn = next();
   1.316 +#if DEBUG_EHABI_UNWIND
   1.317 +    LOGF("unwind insn = %02x", (unsigned)insn);
   1.318 +#endif
   1.319 +    // Try to put the common cases first.
   1.320 +
   1.321 +    // 00xxxxxx: vsp = vsp + (xxxxxx << 2) + 4
   1.322 +    // 01xxxxxx: vsp = vsp - (xxxxxx << 2) - 4
   1.323 +    if ((insn & M_ADDSP) == I_ADDSP) {
   1.324 +      uint32_t offset = ((insn & 0x3f) << 2) + 4;
   1.325 +      if (insn & 0x40) {
   1.326 +        vSP() -= offset;
   1.327 +        checkStackLimit();
   1.328 +      } else {
   1.329 +        vSP() += offset;
   1.330 +        checkStackBase();
   1.331 +      }
   1.332 +      continue;
   1.333 +    }
   1.334 +
   1.335 +    // 10100nnn: Pop r4-r[4+nnn]
   1.336 +    // 10101nnn: Pop r4-r[4+nnn], r14
   1.337 +    if ((insn & M_POPN) == I_POPN) {
   1.338 +      uint8_t n = (insn & 0x07) + 1;
   1.339 +      bool lr = insn & 0x08;
   1.340 +      uint32_t *ptr = ptrSP();
   1.341 +      vSP() += (n + (lr ? 1 : 0)) * 4;
   1.342 +      checkStackBase();
   1.343 +      for (uint8_t r = 4; r < 4 + n; ++r)
   1.344 +        mState[r] = *ptr++;
   1.345 +      if (lr)
   1.346 +        mState[R_LR] = *ptr++;
   1.347 +      continue;
   1.348 +    }
   1.349 +
   1.350 +    // 1011000: Finish
   1.351 +    if (insn == I_FINISH) {
   1.352 +      if (mState[R_PC] == 0) {
   1.353 +        mState[R_PC] = mState[R_LR];
   1.354 +        // Non-standard change (bug 916106): Prevent the caller from
   1.355 +        // re-using LR.  Since the caller is by definition not a leaf
   1.356 +        // routine, it will have to restore LR from somewhere to
   1.357 +        // return to its own caller, so we can safely zero it here.
   1.358 +        // This makes a difference only if an error in unwinding
   1.359 +        // (e.g., caused by starting from within a prologue/epilogue)
   1.360 +        // causes us to load a pointer to a leaf routine as LR; if we
   1.361 +        // don't do something, we'll go into an infinite loop of
   1.362 +        // "returning" to that same function.
   1.363 +        mState[R_LR] = 0;
   1.364 +      }
   1.365 +      return true;
   1.366 +    }
   1.367 +
   1.368 +    // 1001nnnn: Set vsp = r[nnnn]
   1.369 +    if ((insn & M_MOVSP) == I_MOVSP) {
   1.370 +      vSP() = mState[insn & 0x0f];
   1.371 +      checkStack();
   1.372 +      continue;
   1.373 +    }
   1.374 +
   1.375 +    // 11001000 sssscccc: Pop VFP regs D[16+ssss]-D[16+ssss+cccc] (as FLDMFDD)
   1.376 +    // 11001001 sssscccc: Pop VFP regs D[ssss]-D[ssss+cccc] (as FLDMFDD)
   1.377 +    if ((insn & M_POPFDD) == I_POPFDD) {
   1.378 +      uint8_t n = (next() & 0x0f) + 1;
   1.379 +      // Note: if the 16+ssss+cccc > 31, the encoding is reserved.
   1.380 +      // As the space is currently unused, we don't try to check.
   1.381 +      vSP() += 8 * n;
   1.382 +      checkStackBase();
   1.383 +      continue;
   1.384 +    }
   1.385 +
   1.386 +    // 11010nnn: Pop VFP regs D[8]-D[8+nnn] (as FLDMFDD)
   1.387 +    if ((insn & M_POPFDD8) == I_POPFDD8) {
   1.388 +      uint8_t n = (insn & 0x07) + 1;
   1.389 +      vSP() += 8 * n;
   1.390 +      checkStackBase();
   1.391 +      continue;
   1.392 +    }
   1.393 +
   1.394 +    // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
   1.395 +    if (insn == I_ADDSPBIG) {
   1.396 +      uint32_t acc = 0;
   1.397 +      uint8_t shift = 0;
   1.398 +      uint8_t byte;
   1.399 +      do {
   1.400 +        if (shift >= 32)
   1.401 +          return false;
   1.402 +        byte = next();
   1.403 +        acc |= (byte & 0x7f) << shift;
   1.404 +        shift += 7;
   1.405 +      } while (byte & 0x80);
   1.406 +      uint32_t offset = 0x204 + (acc << 2);
   1.407 +      // The calculations above could have overflowed.
   1.408 +      // But the one we care about is this:
   1.409 +      if (vSP() + offset < vSP())
   1.410 +        mFailed = true;
   1.411 +      vSP() += offset;
   1.412 +      // ...so that this is the only other check needed:
   1.413 +      checkStackBase();
   1.414 +      continue;
   1.415 +    }
   1.416 +
   1.417 +    // 1000iiii iiiiiiii (i not all 0): Pop under masks {r15-r12}, {r11-r4}
   1.418 +    if ((insn & M_POPMASK) == I_POPMASK) {
   1.419 +      popRange(4, 15, ((insn & 0x0f) << 8) | next());
   1.420 +      continue;
   1.421 +    }
   1.422 +
   1.423 +    // 1011001 0000iiii (i not all 0): Pop under mask {r3-r0}
   1.424 +    if (insn == I_POPLO) {
   1.425 +      popRange(0, 3, next() & 0x0f);
   1.426 +      continue;
   1.427 +    }
   1.428 +
   1.429 +    // 10110011 sssscccc: Pop VFP regs D[ssss]-D[ssss+cccc] (as FLDMFDX)
   1.430 +    if (insn == I_POPFDX) {
   1.431 +      uint8_t n = (next() & 0x0f) + 1;
   1.432 +      vSP() += 8 * n + 4;
   1.433 +      checkStackBase();
   1.434 +      continue;
   1.435 +    }
   1.436 +
   1.437 +    // 10111nnn: Pop VFP regs D[8]-D[8+nnn] (as FLDMFDX)
   1.438 +    if ((insn & M_POPFDX8) == I_POPFDX8) {
   1.439 +      uint8_t n = (insn & 0x07) + 1;
   1.440 +      vSP() += 8 * n + 4;
   1.441 +      checkStackBase();
   1.442 +      continue;
   1.443 +    }
   1.444 +
   1.445 +    // unhandled instruction
   1.446 +#ifdef DEBUG_EHABI_UNWIND
   1.447 +    LOGF("Unhandled EHABI instruction 0x%02x", insn);
   1.448 +#endif
   1.449 +    mFailed = true;
   1.450 +  }
   1.451 +  return false;
   1.452 +}
   1.453 +
   1.454 +
   1.455 +bool operator<(const EHTable &lhs, const EHTable &rhs) {
   1.456 +  return lhs.startPC() < rhs.endPC();
   1.457 +}
   1.458 +
   1.459 +// Async signal unsafe.
   1.460 +EHAddrSpace::EHAddrSpace(const std::vector<EHTable>& aTables)
   1.461 +  : mTables(aTables)
   1.462 +{
   1.463 +  std::sort(mTables.begin(), mTables.end());
   1.464 +  DebugOnly<uint32_t> lastEnd = 0;
   1.465 +  for (std::vector<EHTable>::iterator i = mTables.begin();
   1.466 +       i != mTables.end(); ++i) {
   1.467 +    MOZ_ASSERT(i->startPC() >= lastEnd);
   1.468 +    mStarts.push_back(i->startPC());
   1.469 +    lastEnd = i->endPC();
   1.470 +  }
   1.471 +}
   1.472 +
   1.473 +const EHTable *EHAddrSpace::lookup(uint32_t aPC) const {
   1.474 +  ptrdiff_t i = (std::upper_bound(mStarts.begin(), mStarts.end(), aPC)
   1.475 +                 - mStarts.begin()) - 1;
   1.476 +
   1.477 +  if (i < 0 || aPC >= mTables[i].endPC())
   1.478 +    return 0;
   1.479 +  return &mTables[i];
   1.480 +}
   1.481 +
   1.482 +
   1.483 +bool operator<(const EHEntryHandle &lhs, const EHEntryHandle &rhs) {
   1.484 +  return lhs.value()->startPC.compute() < rhs.value()->startPC.compute();
   1.485 +}
   1.486 +
   1.487 +const EHEntry *EHTable::lookup(uint32_t aPC) const {
   1.488 +  MOZ_ASSERT(aPC >= mStartPC);
   1.489 +  if (aPC >= mEndPC)
   1.490 +    return nullptr;
   1.491 +
   1.492 +  std::vector<EHEntryHandle>::const_iterator begin = mEntries.begin();
   1.493 +  std::vector<EHEntryHandle>::const_iterator end = mEntries.end();
   1.494 +  MOZ_ASSERT(begin < end);
   1.495 +  if (aPC < reinterpret_cast<uint32_t>(begin->value()->startPC.compute()))
   1.496 +    return nullptr;
   1.497 +
   1.498 +  while (end - begin > 1) {
   1.499 +    std::vector<EHEntryHandle>::const_iterator mid = begin + (end - begin) / 2;
   1.500 +    if (aPC < reinterpret_cast<uint32_t>(mid->value()->startPC.compute()))
   1.501 +      end = mid;
   1.502 +    else
   1.503 +      begin = mid;
   1.504 +  }
   1.505 +  return begin->value();
   1.506 +}
   1.507 +
   1.508 +
   1.509 +#if MOZ_LITTLE_ENDIAN
   1.510 +static const unsigned char hostEndian = ELFDATA2LSB;
   1.511 +#elif MOZ_BIG_ENDIAN
   1.512 +static const unsigned char hostEndian = ELFDATA2MSB;
   1.513 +#else
   1.514 +#error "No endian?"
   1.515 +#endif
   1.516 +
   1.517 +// Async signal unsafe.  (Note use of std::vector::reserve.)
   1.518 +EHTable::EHTable(const void *aELF, size_t aSize, const std::string &aName)
   1.519 +  : mStartPC(~0), // largest uint32_t
   1.520 +    mEndPC(0),
   1.521 +    mName(aName)
   1.522 +{
   1.523 +  const uint32_t base = reinterpret_cast<uint32_t>(aELF);
   1.524 +
   1.525 +  if (aSize < sizeof(Elf32_Ehdr))
   1.526 +    return;
   1.527 +
   1.528 +  const Elf32_Ehdr &file = *(reinterpret_cast<Elf32_Ehdr *>(base));
   1.529 +  if (memcmp(&file.e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0 ||
   1.530 +      file.e_ident[EI_CLASS] != ELFCLASS32 ||
   1.531 +      file.e_ident[EI_DATA] != hostEndian ||
   1.532 +      file.e_ident[EI_VERSION] != EV_CURRENT ||
   1.533 +      file.e_ident[EI_OSABI] != ELFOSABI_SYSV ||
   1.534 +#ifdef EI_ABIVERSION
   1.535 +      file.e_ident[EI_ABIVERSION] != 0 ||
   1.536 +#endif
   1.537 +      file.e_machine != EM_ARM ||
   1.538 +      file.e_version != EV_CURRENT)
   1.539 +    // e_flags?
   1.540 +    return;
   1.541 +
   1.542 +  MOZ_ASSERT(file.e_phoff + file.e_phnum * file.e_phentsize <= aSize);
   1.543 +  const Elf32_Phdr *exidxHdr = 0, *zeroHdr = 0;
   1.544 +  for (unsigned i = 0; i < file.e_phnum; ++i) {
   1.545 +    const Elf32_Phdr &phdr =
   1.546 +      *(reinterpret_cast<Elf32_Phdr *>(base + file.e_phoff
   1.547 +                                       + i * file.e_phentsize));
   1.548 +    if (phdr.p_type == PT_ARM_EXIDX) {
   1.549 +      exidxHdr = &phdr;
   1.550 +    } else if (phdr.p_type == PT_LOAD) {
   1.551 +      if (phdr.p_offset == 0) {
   1.552 +        zeroHdr = &phdr;
   1.553 +      }
   1.554 +      if (phdr.p_flags & PF_X) {
   1.555 +        mStartPC = std::min(mStartPC, phdr.p_vaddr);
   1.556 +        mEndPC = std::max(mEndPC, phdr.p_vaddr + phdr.p_memsz);
   1.557 +      }
   1.558 +    }
   1.559 +  }
   1.560 +  if (!exidxHdr)
   1.561 +    return;
   1.562 +  if (!zeroHdr)
   1.563 +    return;
   1.564 +  mLoadOffset = base - zeroHdr->p_vaddr;
   1.565 +  mStartPC += mLoadOffset;
   1.566 +  mEndPC += mLoadOffset;
   1.567 +
   1.568 +  // Create a sorted index of the index to work around linker bugs.
   1.569 +  const EHEntry *startTable =
   1.570 +    reinterpret_cast<const EHEntry *>(mLoadOffset + exidxHdr->p_vaddr);
   1.571 +  const EHEntry *endTable =
   1.572 +    reinterpret_cast<const EHEntry *>(mLoadOffset + exidxHdr->p_vaddr
   1.573 +                                    + exidxHdr->p_memsz);
   1.574 +  mEntries.reserve(endTable - startTable);
   1.575 +  for (const EHEntry *i = startTable; i < endTable; ++i)
   1.576 +    mEntries.push_back(i);
   1.577 +  std::sort(mEntries.begin(), mEntries.end());
   1.578 +}
   1.579 +
   1.580 +
   1.581 +mozilla::Atomic<const EHAddrSpace*> EHAddrSpace::sCurrent(nullptr);
   1.582 +
   1.583 +// Async signal safe; can fail if Update() hasn't returned yet.
   1.584 +const EHAddrSpace *EHAddrSpace::Get() {
   1.585 +  return sCurrent;
   1.586 +}
   1.587 +
   1.588 +// Collect unwinding information from loaded objects.  Calls after the
   1.589 +// first have no effect.  Async signal unsafe.
   1.590 +void EHAddrSpace::Update() {
   1.591 +  const EHAddrSpace *space = sCurrent;
   1.592 +  if (space)
   1.593 +    return;
   1.594 +
   1.595 +  SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
   1.596 +  std::vector<EHTable> tables;
   1.597 +
   1.598 +  for (size_t i = 0; i < info.GetSize(); ++i) {
   1.599 +    const SharedLibrary &lib = info.GetEntry(i);
   1.600 +    if (lib.GetOffset() != 0)
   1.601 +      // TODO: if it has a name, and we haven't seen a mapping of
   1.602 +      // offset 0 for that file, try opening it and reading the
   1.603 +      // headers instead.  The only thing I've seen so far that's
   1.604 +      // linked so as to need that treatment is the dynamic linker
   1.605 +      // itself.
   1.606 +      continue;
   1.607 +    EHTable tab(reinterpret_cast<const void *>(lib.GetStart()),
   1.608 +              lib.GetEnd() - lib.GetStart(), lib.GetName());
   1.609 +    if (tab.isValid())
   1.610 +      tables.push_back(tab);
   1.611 +  }
   1.612 +  space = new EHAddrSpace(tables);
   1.613 +
   1.614 +  if (!sCurrent.compareExchange(nullptr, space)) {
   1.615 +    delete space;
   1.616 +    space = sCurrent;
   1.617 +  }
   1.618 +}
   1.619 +
   1.620 +
   1.621 +EHState::EHState(const mcontext_t &context) {
   1.622 +#ifdef linux
   1.623 +  mRegs[0] = context.arm_r0;
   1.624 +  mRegs[1] = context.arm_r1;
   1.625 +  mRegs[2] = context.arm_r2;
   1.626 +  mRegs[3] = context.arm_r3;
   1.627 +  mRegs[4] = context.arm_r4;
   1.628 +  mRegs[5] = context.arm_r5;
   1.629 +  mRegs[6] = context.arm_r6;
   1.630 +  mRegs[7] = context.arm_r7;
   1.631 +  mRegs[8] = context.arm_r8;
   1.632 +  mRegs[9] = context.arm_r9;
   1.633 +  mRegs[10] = context.arm_r10;
   1.634 +  mRegs[11] = context.arm_fp;
   1.635 +  mRegs[12] = context.arm_ip;
   1.636 +  mRegs[13] = context.arm_sp;
   1.637 +  mRegs[14] = context.arm_lr;
   1.638 +  mRegs[15] = context.arm_pc;
   1.639 +#else
   1.640 +# error "Unhandled OS for ARM EHABI unwinding"
   1.641 +#endif
   1.642 +}
   1.643 +
   1.644 +} // namespace mozilla
   1.645 +

mercurial