/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* * This is an implementation of stack unwinding according to a subset * of the ARM Exception Handling ABI, as described in: * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf * * This handles only the ARM-defined "personality routines" (chapter * 9), and don't track the value of FP registers, because profiling * needs only chain of PC/SP values. * * Because the exception handling info may not be accurate for all * possible places where an async signal could occur (e.g., in a * prologue or epilogue), this bounds-checks all stack accesses. * * This file uses "struct" for structures in the exception tables and * "class" otherwise. We should avoid violating the C++11 * standard-layout rules in the former.
*/
class EHState { // Note that any core register can be used as a "frame pointer" to // influence the unwinding process, so this must track all of them.
uint32_t mRegs[16];
while (count < aNumFrames) {
uint32_t pc = state[R_PC], sp = state[R_SP];
// ARM instructions are always aligned to 2 or 4 bytes. // The last bit of the pc / lr indicates ARM or Thumb mode. // We're only interested in the instruction address, so we mask off that // bit.
constexpr uint32_t instrAddrMask = ~1;
uint32_t instrAddress = pc & instrAddrMask;
if (!space) break; // TODO: cache these lookups. Binary-searching libxul is // expensive (possibly more expensive than doing the actual // unwind), and even a small cache should help. const EHTable* table = space->lookup(pc); if (!table) break; const EHEntry* entry = table->lookup(pc); if (!entry) break; if (!state.unwind(entry, stackBase)) break;
}
return count;
}
class EHInterp { public: // Note that stackLimit is exclusive and stackBase is inclusive // (i.e, stackLimit < SP <= stackBase), following the convention // set by the AAPCS spec.
EHInterp(EHState& aState, const EHEntry* aEntry, uint32_t aStackLimit,
uint32_t aStackBase)
: mState(aState),
mStackLimit(aStackLimit),
mStackBase(aStackBase),
mNextWord(0),
mWordsLeft(0),
mFailed(false) { const PRel31& exidx = aEntry->exidx;
uint32_t firstWord;
private: // TODO: GCC has been observed not CSEing repeated reads of // mState[R_SP] with writes to mFailed between them, suggesting that // it hasn't determined that they can't alias and is thus missing // optimization opportunities. So, we may want to flatten EHState // into this class; this may also make the code simpler.
EHState& mState;
uint32_t mStackLimit;
uint32_t mStackBase; const uint32_t* mNextWord;
uint32_t mWord;
uint8_t mWordsLeft;
uint8_t mBytesLeft; bool mFailed;
// 10100nnn: Pop r4-r[4+nnn] // 10101nnn: Pop r4-r[4+nnn], r14 if ((insn & M_POPN) == I_POPN) {
uint8_t n = (insn & 0x07) + 1; bool lr = insn & 0x08;
uint32_t* ptr = ptrSP();
vSP() += (n + (lr ? 1 : 0)) * 4;
checkStackBase(); for (uint8_t r = 4; r < 4 + n; ++r) mState[r] = *ptr++; if (lr) mState[R_LR] = *ptr++; continue;
}
// 1011000: Finish if (insn == I_FINISH) { if (mState[R_PC] == 0) {
mState[R_PC] = mState[R_LR]; // Non-standard change (bug 916106): Prevent the caller from // re-using LR. Since the caller is by definition not a leaf // routine, it will have to restore LR from somewhere to // return to its own caller, so we can safely zero it here. // This makes a difference only if an error in unwinding // (e.g., caused by starting from within a prologue/epilogue) // causes us to load a pointer to a leaf routine as LR; if we // don't do something, we'll go into an infinite loop of // "returning" to that same function.
mState[R_LR] = 0;
} returntrue;
}
// 11001000 sssscccc: Pop VFP regs D[16+ssss]-D[16+ssss+cccc] (as FLDMFDD) // 11001001 sssscccc: Pop VFP regs D[ssss]-D[ssss+cccc] (as FLDMFDD) if ((insn & M_POPFDD) == I_POPFDD) {
uint8_t n = (next() & 0x0f) + 1; // Note: if the 16+ssss+cccc > 31, the encoding is reserved. // As the space is currently unused, we don't try to check.
vSP() += 8 * n;
checkStackBase(); continue;
}
// 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2) if (insn == I_ADDSPBIG) {
uint32_t acc = 0;
uint8_t shift = 0;
uint8_t byte; do { if (shift >= 32) returnfalse;
byte = next();
acc |= (byte & 0x7f) << shift;
shift += 7;
} while (byte & 0x80);
uint32_t offset = 0x204 + (acc << 2); // The calculations above could have overflowed. // But the one we care about is this: if (vSP() + offset < vSP()) mFailed = true;
vSP() += offset; // ...so that this is the only other check needed:
checkStackBase(); continue;
}
// 1000iiii iiiiiiii (i not all 0): Pop under masks {r15-r12}, {r11-r4} if ((insn & M_POPMASK) == I_POPMASK) {
popRange(4, 15, ((insn & 0x0f) << 8) | next()); continue;
}
// 1011001 0000iiii (i not all 0): Pop under mask {r3-r0} if (insn == I_POPLO) {
popRange(0, 3, next() & 0x0f); continue;
}
// 10110011 sssscccc: Pop VFP regs D[ssss]-D[ssss+cccc] (as FLDMFDX) if (insn == I_POPFDX) {
uint8_t n = (next() & 0x0f) + 1;
vSP() += 8 * n + 4;
checkStackBase(); continue;
}
// 10111nnn: Pop VFP regs D[8]-D[8+nnn] (as FLDMFDX) if ((insn & M_POPFDX8) == I_POPFDX8) {
uint8_t n = (insn & 0x07) + 1;
vSP() += 8 * n + 4;
checkStackBase(); continue;
}
// Async signal safe; can fail if Update() hasn't returned yet. const EHAddrSpace* EHAddrSpace::Get() { return sCurrent; }
// Collect unwinding information from loaded objects. Calls after the // first have no effect. Async signal unsafe. void EHAddrSpace::Update() { const EHAddrSpace* space = sCurrent; if (space) return;
SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
std::vector<EHTable> tables;
for (size_t i = 0; i < info.GetSize(); ++i) { const SharedLibrary& lib = info.GetEntry(i); // FIXME: This isn't correct if the start address isn't p_offset 0, because // the start address will not point at the file header. But this is worked // around by magic number checks in the EHTable constructor.
EHTable tab(reinterpret_cast<constvoid*>(lib.GetStart()),
lib.GetEnd() - lib.GetStart(), lib.GetDebugPath()); if (tab.isValid()) tables.push_back(tab);
}
space = new EHAddrSpace(tables);
if (!sCurrent.compareExchange(nullptr, space)) { delete space;
space = sCurrent;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.