michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * This is an implementation of stack unwinding according to a subset michael@0: * of the ARM Exception Handling ABI, as described in: michael@0: * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf michael@0: * michael@0: * This handles only the ARM-defined "personality routines" (chapter michael@0: * 9), and don't track the value of FP registers, because profiling michael@0: * needs only chain of PC/SP values. michael@0: * michael@0: * Because the exception handling info may not be accurate for all michael@0: * possible places where an async signal could occur (e.g., in a michael@0: * prologue or epilogue), this bounds-checks all stack accesses. michael@0: * michael@0: * This file uses "struct" for structures in the exception tables and michael@0: * "class" otherwise. We should avoid violating the C++11 michael@0: * standard-layout rules in the former. michael@0: */ michael@0: michael@0: #include "EHABIStackWalk.h" michael@0: michael@0: #include "shared-libraries.h" michael@0: #include "platform.h" michael@0: michael@0: #include "mozilla/Atomics.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/Endian.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #ifndef PT_ARM_EXIDX michael@0: #define PT_ARM_EXIDX 0x70000001 michael@0: #endif michael@0: michael@0: michael@0: namespace mozilla { michael@0: michael@0: struct EHEntry; michael@0: michael@0: class EHState { michael@0: // Note that any core register can be used as a "frame pointer" to michael@0: // influence the unwinding process, so this must track all of them. michael@0: uint32_t mRegs[16]; michael@0: public: michael@0: bool unwind(const EHEntry *aEntry, const void *stackBase); michael@0: uint32_t &operator[](int i) { return mRegs[i]; } michael@0: const uint32_t &operator[](int i) const { return mRegs[i]; } michael@0: EHState(const mcontext_t &); michael@0: }; michael@0: michael@0: enum { michael@0: R_SP = 13, michael@0: R_LR = 14, michael@0: R_PC = 15 michael@0: }; michael@0: michael@0: class EHEntryHandle { michael@0: const EHEntry *mValue; michael@0: public: michael@0: EHEntryHandle(const EHEntry *aEntry) : mValue(aEntry) { } michael@0: const EHEntry *value() const { return mValue; } michael@0: }; michael@0: michael@0: class EHTable { michael@0: uint32_t mStartPC; michael@0: uint32_t mEndPC; michael@0: uint32_t mLoadOffset; michael@0: // In principle we should be able to binary-search the index section in michael@0: // place, but the ICS toolchain's linker is noncompliant and produces michael@0: // indices that aren't entirely sorted (e.g., libc). So we have this: michael@0: std::vector mEntries; michael@0: std::string mName; michael@0: public: michael@0: EHTable(const void *aELF, size_t aSize, const std::string &aName); michael@0: const EHEntry *lookup(uint32_t aPC) const; michael@0: bool isValid() const { return mEntries.size() > 0; } michael@0: const std::string &name() const { return mName; } michael@0: uint32_t startPC() const { return mStartPC; } michael@0: uint32_t endPC() const { return mEndPC; } michael@0: uint32_t loadOffset() const { return mLoadOffset; } michael@0: }; michael@0: michael@0: class EHAddrSpace { michael@0: std::vector mStarts; michael@0: std::vector mTables; michael@0: static mozilla::Atomic sCurrent; michael@0: public: michael@0: explicit EHAddrSpace(const std::vector& aTables); michael@0: const EHTable *lookup(uint32_t aPC) const; michael@0: static void Update(); michael@0: static const EHAddrSpace *Get(); michael@0: }; michael@0: michael@0: michael@0: void EHABIStackWalkInit() michael@0: { michael@0: EHAddrSpace::Update(); michael@0: } michael@0: michael@0: size_t EHABIStackWalk(const mcontext_t &aContext, void *stackBase, michael@0: void **aSPs, void **aPCs, const size_t aNumFrames) michael@0: { michael@0: const EHAddrSpace *space = EHAddrSpace::Get(); michael@0: EHState state(aContext); michael@0: size_t count = 0; michael@0: michael@0: while (count < aNumFrames) { michael@0: uint32_t pc = state[R_PC], sp = state[R_SP]; michael@0: aPCs[count] = reinterpret_cast(pc); michael@0: aSPs[count] = reinterpret_cast(sp); michael@0: count++; michael@0: michael@0: if (!space) michael@0: break; michael@0: // TODO: cache these lookups. Binary-searching libxul is michael@0: // expensive (possibly more expensive than doing the actual michael@0: // unwind), and even a small cache should help. michael@0: const EHTable *table = space->lookup(pc); michael@0: if (!table) michael@0: break; michael@0: const EHEntry *entry = table->lookup(pc); michael@0: if (!entry) michael@0: break; michael@0: if (!state.unwind(entry, stackBase)) michael@0: break; michael@0: } michael@0: michael@0: return count; michael@0: } michael@0: michael@0: michael@0: struct PRel31 { michael@0: uint32_t mBits; michael@0: bool topBit() const { return mBits & 0x80000000; } michael@0: uint32_t value() const { return mBits & 0x7fffffff; } michael@0: int32_t offset() const { return (static_cast(mBits) << 1) >> 1; } michael@0: const void *compute() const { michael@0: return reinterpret_cast(this) + offset(); michael@0: } michael@0: private: michael@0: PRel31(const PRel31 &copied) MOZ_DELETE; michael@0: PRel31() MOZ_DELETE; michael@0: }; michael@0: michael@0: struct EHEntry { michael@0: PRel31 startPC; michael@0: PRel31 exidx; michael@0: private: michael@0: EHEntry(const EHEntry &copied) MOZ_DELETE; michael@0: EHEntry() MOZ_DELETE; michael@0: }; michael@0: michael@0: michael@0: class EHInterp { michael@0: public: michael@0: // Note that stackLimit is exclusive and stackBase is inclusive michael@0: // (i.e, stackLimit < SP <= stackBase), following the convention michael@0: // set by the AAPCS spec. michael@0: EHInterp(EHState &aState, const EHEntry *aEntry, michael@0: uint32_t aStackLimit, uint32_t aStackBase) michael@0: : mState(aState), michael@0: mStackLimit(aStackLimit), michael@0: mStackBase(aStackBase), michael@0: mNextWord(0), michael@0: mWordsLeft(0), michael@0: mFailed(false) michael@0: { michael@0: const PRel31 &exidx = aEntry->exidx; michael@0: uint32_t firstWord; michael@0: michael@0: if (exidx.mBits == 1) { // EXIDX_CANTUNWIND michael@0: mFailed = true; michael@0: return; michael@0: } michael@0: if (exidx.topBit()) { michael@0: firstWord = exidx.mBits; michael@0: } else { michael@0: mNextWord = reinterpret_cast(exidx.compute()); michael@0: firstWord = *mNextWord++; michael@0: } michael@0: michael@0: switch (firstWord >> 24) { michael@0: case 0x80: // short michael@0: mWord = firstWord << 8; michael@0: mBytesLeft = 3; michael@0: break; michael@0: case 0x81: case 0x82: // long; catch descriptor size ignored michael@0: mWord = firstWord << 16; michael@0: mBytesLeft = 2; michael@0: mWordsLeft = (firstWord >> 16) & 0xff; michael@0: break; michael@0: default: michael@0: // unknown personality michael@0: mFailed = true; michael@0: } michael@0: } michael@0: michael@0: bool unwind(); michael@0: michael@0: private: michael@0: // TODO: GCC has been observed not CSEing repeated reads of michael@0: // mState[R_SP] with writes to mFailed between them, suggesting that michael@0: // it hasn't determined that they can't alias and is thus missing michael@0: // optimization opportunities. So, we may want to flatten EHState michael@0: // into this class; this may also make the code simpler. michael@0: EHState &mState; michael@0: uint32_t mStackLimit; michael@0: uint32_t mStackBase; michael@0: const uint32_t *mNextWord; michael@0: uint32_t mWord; michael@0: uint8_t mWordsLeft; michael@0: uint8_t mBytesLeft; michael@0: bool mFailed; michael@0: michael@0: enum { michael@0: I_ADDSP = 0x00, // 0sxxxxxx (subtract if s) michael@0: M_ADDSP = 0x80, michael@0: I_POPMASK = 0x80, // 1000iiii iiiiiiii (if any i set) michael@0: M_POPMASK = 0xf0, michael@0: I_MOVSP = 0x90, // 1001nnnn michael@0: M_MOVSP = 0xf0, michael@0: I_POPN = 0xa0, // 1010lnnn michael@0: M_POPN = 0xf0, michael@0: I_FINISH = 0xb0, // 10110000 michael@0: I_POPLO = 0xb1, // 10110001 0000iiii (if any i set) michael@0: I_ADDSPBIG = 0xb2, // 10110010 uleb128 michael@0: I_POPFDX = 0xb3, // 10110011 sssscccc michael@0: I_POPFDX8 = 0xb8, // 10111nnn michael@0: M_POPFDX8 = 0xf8, michael@0: // "Intel Wireless MMX" extensions omitted. michael@0: I_POPFDD = 0xc8, // 1100100h sssscccc michael@0: M_POPFDD = 0xfe, michael@0: I_POPFDD8 = 0xd0, // 11010nnn michael@0: M_POPFDD8 = 0xf8 michael@0: }; michael@0: michael@0: uint8_t next() { michael@0: if (mBytesLeft == 0) { michael@0: if (mWordsLeft == 0) { michael@0: return I_FINISH; michael@0: } michael@0: mWordsLeft--; michael@0: mWord = *mNextWord++; michael@0: mBytesLeft = 4; michael@0: } michael@0: mBytesLeft--; michael@0: mWord = (mWord << 8) | (mWord >> 24); // rotate michael@0: return mWord; michael@0: } michael@0: michael@0: uint32_t &vSP() { return mState[R_SP]; } michael@0: uint32_t *ptrSP() { return reinterpret_cast(vSP()); } michael@0: michael@0: void checkStackBase() { if (vSP() > mStackBase) mFailed = true; } michael@0: void checkStackLimit() { if (vSP() <= mStackLimit) mFailed = true; } michael@0: void checkStackAlign() { if ((vSP() & 3) != 0) mFailed = true; } michael@0: void checkStack() { michael@0: checkStackBase(); michael@0: checkStackLimit(); michael@0: checkStackAlign(); michael@0: } michael@0: michael@0: void popRange(uint8_t first, uint8_t last, uint16_t mask) { michael@0: bool hasSP = false; michael@0: uint32_t tmpSP; michael@0: if (mask == 0) michael@0: mFailed = true; michael@0: for (uint8_t r = first; r <= last; ++r) { michael@0: if (mask & 1) { michael@0: if (r == R_SP) { michael@0: hasSP = true; michael@0: tmpSP = *ptrSP(); michael@0: } else michael@0: mState[r] = *ptrSP(); michael@0: vSP() += 4; michael@0: checkStackBase(); michael@0: if (mFailed) michael@0: return; michael@0: } michael@0: mask >>= 1; michael@0: } michael@0: if (hasSP) { michael@0: vSP() = tmpSP; michael@0: checkStack(); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: michael@0: bool EHState::unwind(const EHEntry *aEntry, const void *stackBasePtr) { michael@0: // The unwinding program cannot set SP to less than the initial value. michael@0: uint32_t stackLimit = mRegs[R_SP] - 4; michael@0: uint32_t stackBase = reinterpret_cast(stackBasePtr); michael@0: EHInterp interp(*this, aEntry, stackLimit, stackBase); michael@0: return interp.unwind(); michael@0: } michael@0: michael@0: bool EHInterp::unwind() { michael@0: mState[R_PC] = 0; michael@0: checkStack(); michael@0: while (!mFailed) { michael@0: uint8_t insn = next(); michael@0: #if DEBUG_EHABI_UNWIND michael@0: LOGF("unwind insn = %02x", (unsigned)insn); michael@0: #endif michael@0: // Try to put the common cases first. michael@0: michael@0: // 00xxxxxx: vsp = vsp + (xxxxxx << 2) + 4 michael@0: // 01xxxxxx: vsp = vsp - (xxxxxx << 2) - 4 michael@0: if ((insn & M_ADDSP) == I_ADDSP) { michael@0: uint32_t offset = ((insn & 0x3f) << 2) + 4; michael@0: if (insn & 0x40) { michael@0: vSP() -= offset; michael@0: checkStackLimit(); michael@0: } else { michael@0: vSP() += offset; michael@0: checkStackBase(); michael@0: } michael@0: continue; michael@0: } michael@0: michael@0: // 10100nnn: Pop r4-r[4+nnn] michael@0: // 10101nnn: Pop r4-r[4+nnn], r14 michael@0: if ((insn & M_POPN) == I_POPN) { michael@0: uint8_t n = (insn & 0x07) + 1; michael@0: bool lr = insn & 0x08; michael@0: uint32_t *ptr = ptrSP(); michael@0: vSP() += (n + (lr ? 1 : 0)) * 4; michael@0: checkStackBase(); michael@0: for (uint8_t r = 4; r < 4 + n; ++r) michael@0: mState[r] = *ptr++; michael@0: if (lr) michael@0: mState[R_LR] = *ptr++; michael@0: continue; michael@0: } michael@0: michael@0: // 1011000: Finish michael@0: if (insn == I_FINISH) { michael@0: if (mState[R_PC] == 0) { michael@0: mState[R_PC] = mState[R_LR]; michael@0: // Non-standard change (bug 916106): Prevent the caller from michael@0: // re-using LR. Since the caller is by definition not a leaf michael@0: // routine, it will have to restore LR from somewhere to michael@0: // return to its own caller, so we can safely zero it here. michael@0: // This makes a difference only if an error in unwinding michael@0: // (e.g., caused by starting from within a prologue/epilogue) michael@0: // causes us to load a pointer to a leaf routine as LR; if we michael@0: // don't do something, we'll go into an infinite loop of michael@0: // "returning" to that same function. michael@0: mState[R_LR] = 0; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // 1001nnnn: Set vsp = r[nnnn] michael@0: if ((insn & M_MOVSP) == I_MOVSP) { michael@0: vSP() = mState[insn & 0x0f]; michael@0: checkStack(); michael@0: continue; michael@0: } michael@0: michael@0: // 11001000 sssscccc: Pop VFP regs D[16+ssss]-D[16+ssss+cccc] (as FLDMFDD) michael@0: // 11001001 sssscccc: Pop VFP regs D[ssss]-D[ssss+cccc] (as FLDMFDD) michael@0: if ((insn & M_POPFDD) == I_POPFDD) { michael@0: uint8_t n = (next() & 0x0f) + 1; michael@0: // Note: if the 16+ssss+cccc > 31, the encoding is reserved. michael@0: // As the space is currently unused, we don't try to check. michael@0: vSP() += 8 * n; michael@0: checkStackBase(); michael@0: continue; michael@0: } michael@0: michael@0: // 11010nnn: Pop VFP regs D[8]-D[8+nnn] (as FLDMFDD) michael@0: if ((insn & M_POPFDD8) == I_POPFDD8) { michael@0: uint8_t n = (insn & 0x07) + 1; michael@0: vSP() += 8 * n; michael@0: checkStackBase(); michael@0: continue; michael@0: } michael@0: michael@0: // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2) michael@0: if (insn == I_ADDSPBIG) { michael@0: uint32_t acc = 0; michael@0: uint8_t shift = 0; michael@0: uint8_t byte; michael@0: do { michael@0: if (shift >= 32) michael@0: return false; michael@0: byte = next(); michael@0: acc |= (byte & 0x7f) << shift; michael@0: shift += 7; michael@0: } while (byte & 0x80); michael@0: uint32_t offset = 0x204 + (acc << 2); michael@0: // The calculations above could have overflowed. michael@0: // But the one we care about is this: michael@0: if (vSP() + offset < vSP()) michael@0: mFailed = true; michael@0: vSP() += offset; michael@0: // ...so that this is the only other check needed: michael@0: checkStackBase(); michael@0: continue; michael@0: } michael@0: michael@0: // 1000iiii iiiiiiii (i not all 0): Pop under masks {r15-r12}, {r11-r4} michael@0: if ((insn & M_POPMASK) == I_POPMASK) { michael@0: popRange(4, 15, ((insn & 0x0f) << 8) | next()); michael@0: continue; michael@0: } michael@0: michael@0: // 1011001 0000iiii (i not all 0): Pop under mask {r3-r0} michael@0: if (insn == I_POPLO) { michael@0: popRange(0, 3, next() & 0x0f); michael@0: continue; michael@0: } michael@0: michael@0: // 10110011 sssscccc: Pop VFP regs D[ssss]-D[ssss+cccc] (as FLDMFDX) michael@0: if (insn == I_POPFDX) { michael@0: uint8_t n = (next() & 0x0f) + 1; michael@0: vSP() += 8 * n + 4; michael@0: checkStackBase(); michael@0: continue; michael@0: } michael@0: michael@0: // 10111nnn: Pop VFP regs D[8]-D[8+nnn] (as FLDMFDX) michael@0: if ((insn & M_POPFDX8) == I_POPFDX8) { michael@0: uint8_t n = (insn & 0x07) + 1; michael@0: vSP() += 8 * n + 4; michael@0: checkStackBase(); michael@0: continue; michael@0: } michael@0: michael@0: // unhandled instruction michael@0: #ifdef DEBUG_EHABI_UNWIND michael@0: LOGF("Unhandled EHABI instruction 0x%02x", insn); michael@0: #endif michael@0: mFailed = true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: michael@0: bool operator<(const EHTable &lhs, const EHTable &rhs) { michael@0: return lhs.startPC() < rhs.endPC(); michael@0: } michael@0: michael@0: // Async signal unsafe. michael@0: EHAddrSpace::EHAddrSpace(const std::vector& aTables) michael@0: : mTables(aTables) michael@0: { michael@0: std::sort(mTables.begin(), mTables.end()); michael@0: DebugOnly lastEnd = 0; michael@0: for (std::vector::iterator i = mTables.begin(); michael@0: i != mTables.end(); ++i) { michael@0: MOZ_ASSERT(i->startPC() >= lastEnd); michael@0: mStarts.push_back(i->startPC()); michael@0: lastEnd = i->endPC(); michael@0: } michael@0: } michael@0: michael@0: const EHTable *EHAddrSpace::lookup(uint32_t aPC) const { michael@0: ptrdiff_t i = (std::upper_bound(mStarts.begin(), mStarts.end(), aPC) michael@0: - mStarts.begin()) - 1; michael@0: michael@0: if (i < 0 || aPC >= mTables[i].endPC()) michael@0: return 0; michael@0: return &mTables[i]; michael@0: } michael@0: michael@0: michael@0: bool operator<(const EHEntryHandle &lhs, const EHEntryHandle &rhs) { michael@0: return lhs.value()->startPC.compute() < rhs.value()->startPC.compute(); michael@0: } michael@0: michael@0: const EHEntry *EHTable::lookup(uint32_t aPC) const { michael@0: MOZ_ASSERT(aPC >= mStartPC); michael@0: if (aPC >= mEndPC) michael@0: return nullptr; michael@0: michael@0: std::vector::const_iterator begin = mEntries.begin(); michael@0: std::vector::const_iterator end = mEntries.end(); michael@0: MOZ_ASSERT(begin < end); michael@0: if (aPC < reinterpret_cast(begin->value()->startPC.compute())) michael@0: return nullptr; michael@0: michael@0: while (end - begin > 1) { michael@0: std::vector::const_iterator mid = begin + (end - begin) / 2; michael@0: if (aPC < reinterpret_cast(mid->value()->startPC.compute())) michael@0: end = mid; michael@0: else michael@0: begin = mid; michael@0: } michael@0: return begin->value(); michael@0: } michael@0: michael@0: michael@0: #if MOZ_LITTLE_ENDIAN michael@0: static const unsigned char hostEndian = ELFDATA2LSB; michael@0: #elif MOZ_BIG_ENDIAN michael@0: static const unsigned char hostEndian = ELFDATA2MSB; michael@0: #else michael@0: #error "No endian?" michael@0: #endif michael@0: michael@0: // Async signal unsafe. (Note use of std::vector::reserve.) michael@0: EHTable::EHTable(const void *aELF, size_t aSize, const std::string &aName) michael@0: : mStartPC(~0), // largest uint32_t michael@0: mEndPC(0), michael@0: mName(aName) michael@0: { michael@0: const uint32_t base = reinterpret_cast(aELF); michael@0: michael@0: if (aSize < sizeof(Elf32_Ehdr)) michael@0: return; michael@0: michael@0: const Elf32_Ehdr &file = *(reinterpret_cast(base)); michael@0: if (memcmp(&file.e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0 || michael@0: file.e_ident[EI_CLASS] != ELFCLASS32 || michael@0: file.e_ident[EI_DATA] != hostEndian || michael@0: file.e_ident[EI_VERSION] != EV_CURRENT || michael@0: file.e_ident[EI_OSABI] != ELFOSABI_SYSV || michael@0: #ifdef EI_ABIVERSION michael@0: file.e_ident[EI_ABIVERSION] != 0 || michael@0: #endif michael@0: file.e_machine != EM_ARM || michael@0: file.e_version != EV_CURRENT) michael@0: // e_flags? michael@0: return; michael@0: michael@0: MOZ_ASSERT(file.e_phoff + file.e_phnum * file.e_phentsize <= aSize); michael@0: const Elf32_Phdr *exidxHdr = 0, *zeroHdr = 0; michael@0: for (unsigned i = 0; i < file.e_phnum; ++i) { michael@0: const Elf32_Phdr &phdr = michael@0: *(reinterpret_cast(base + file.e_phoff michael@0: + i * file.e_phentsize)); michael@0: if (phdr.p_type == PT_ARM_EXIDX) { michael@0: exidxHdr = &phdr; michael@0: } else if (phdr.p_type == PT_LOAD) { michael@0: if (phdr.p_offset == 0) { michael@0: zeroHdr = &phdr; michael@0: } michael@0: if (phdr.p_flags & PF_X) { michael@0: mStartPC = std::min(mStartPC, phdr.p_vaddr); michael@0: mEndPC = std::max(mEndPC, phdr.p_vaddr + phdr.p_memsz); michael@0: } michael@0: } michael@0: } michael@0: if (!exidxHdr) michael@0: return; michael@0: if (!zeroHdr) michael@0: return; michael@0: mLoadOffset = base - zeroHdr->p_vaddr; michael@0: mStartPC += mLoadOffset; michael@0: mEndPC += mLoadOffset; michael@0: michael@0: // Create a sorted index of the index to work around linker bugs. michael@0: const EHEntry *startTable = michael@0: reinterpret_cast(mLoadOffset + exidxHdr->p_vaddr); michael@0: const EHEntry *endTable = michael@0: reinterpret_cast(mLoadOffset + exidxHdr->p_vaddr michael@0: + exidxHdr->p_memsz); michael@0: mEntries.reserve(endTable - startTable); michael@0: for (const EHEntry *i = startTable; i < endTable; ++i) michael@0: mEntries.push_back(i); michael@0: std::sort(mEntries.begin(), mEntries.end()); michael@0: } michael@0: michael@0: michael@0: mozilla::Atomic EHAddrSpace::sCurrent(nullptr); michael@0: michael@0: // Async signal safe; can fail if Update() hasn't returned yet. michael@0: const EHAddrSpace *EHAddrSpace::Get() { michael@0: return sCurrent; michael@0: } michael@0: michael@0: // Collect unwinding information from loaded objects. Calls after the michael@0: // first have no effect. Async signal unsafe. michael@0: void EHAddrSpace::Update() { michael@0: const EHAddrSpace *space = sCurrent; michael@0: if (space) michael@0: return; michael@0: michael@0: SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf(); michael@0: std::vector tables; michael@0: michael@0: for (size_t i = 0; i < info.GetSize(); ++i) { michael@0: const SharedLibrary &lib = info.GetEntry(i); michael@0: if (lib.GetOffset() != 0) michael@0: // TODO: if it has a name, and we haven't seen a mapping of michael@0: // offset 0 for that file, try opening it and reading the michael@0: // headers instead. The only thing I've seen so far that's michael@0: // linked so as to need that treatment is the dynamic linker michael@0: // itself. michael@0: continue; michael@0: EHTable tab(reinterpret_cast(lib.GetStart()), michael@0: lib.GetEnd() - lib.GetStart(), lib.GetName()); michael@0: if (tab.isValid()) michael@0: tables.push_back(tab); michael@0: } michael@0: space = new EHAddrSpace(tables); michael@0: michael@0: if (!sCurrent.compareExchange(nullptr, space)) { michael@0: delete space; michael@0: space = sCurrent; michael@0: } michael@0: } michael@0: michael@0: michael@0: EHState::EHState(const mcontext_t &context) { michael@0: #ifdef linux michael@0: mRegs[0] = context.arm_r0; michael@0: mRegs[1] = context.arm_r1; michael@0: mRegs[2] = context.arm_r2; michael@0: mRegs[3] = context.arm_r3; michael@0: mRegs[4] = context.arm_r4; michael@0: mRegs[5] = context.arm_r5; michael@0: mRegs[6] = context.arm_r6; michael@0: mRegs[7] = context.arm_r7; michael@0: mRegs[8] = context.arm_r8; michael@0: mRegs[9] = context.arm_r9; michael@0: mRegs[10] = context.arm_r10; michael@0: mRegs[11] = context.arm_fp; michael@0: mRegs[12] = context.arm_ip; michael@0: mRegs[13] = context.arm_sp; michael@0: mRegs[14] = context.arm_lr; michael@0: mRegs[15] = context.arm_pc; michael@0: #else michael@0: # error "Unhandled OS for ARM EHABI unwinding" michael@0: #endif michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: