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 +