tools/profiler/EHABIStackWalk.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     8  * This is an implementation of stack unwinding according to a subset
     9  * of the ARM Exception Handling ABI, as described in:
    10  *   http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
    11  *
    12  * This handles only the ARM-defined "personality routines" (chapter
    13  * 9), and don't track the value of FP registers, because profiling
    14  * needs only chain of PC/SP values.
    15  *
    16  * Because the exception handling info may not be accurate for all
    17  * possible places where an async signal could occur (e.g., in a
    18  * prologue or epilogue), this bounds-checks all stack accesses.
    19  *
    20  * This file uses "struct" for structures in the exception tables and
    21  * "class" otherwise.  We should avoid violating the C++11
    22  * standard-layout rules in the former.
    23  */
    25 #include "EHABIStackWalk.h"
    27 #include "shared-libraries.h"
    28 #include "platform.h"
    30 #include "mozilla/Atomics.h"
    31 #include "mozilla/Attributes.h"
    32 #include "mozilla/DebugOnly.h"
    33 #include "mozilla/Endian.h"
    35 #include <algorithm>
    36 #include <elf.h>
    37 #include <stdint.h>
    38 #include <vector>
    39 #include <string>
    41 #ifndef PT_ARM_EXIDX
    42 #define PT_ARM_EXIDX 0x70000001
    43 #endif
    46 namespace mozilla {
    48 struct EHEntry;
    50 class EHState {
    51   // Note that any core register can be used as a "frame pointer" to
    52   // influence the unwinding process, so this must track all of them.
    53   uint32_t mRegs[16];
    54 public:
    55   bool unwind(const EHEntry *aEntry, const void *stackBase);
    56   uint32_t &operator[](int i) { return mRegs[i]; }
    57   const uint32_t &operator[](int i) const { return mRegs[i]; }
    58   EHState(const mcontext_t &);
    59 };
    61 enum {
    62   R_SP = 13,
    63   R_LR = 14,
    64   R_PC = 15
    65 };
    67 class EHEntryHandle {
    68   const EHEntry *mValue;
    69 public:
    70   EHEntryHandle(const EHEntry *aEntry) : mValue(aEntry) { }
    71   const EHEntry *value() const { return mValue; }
    72 };
    74 class EHTable {
    75   uint32_t mStartPC;
    76   uint32_t mEndPC;
    77   uint32_t mLoadOffset;
    78   // In principle we should be able to binary-search the index section in
    79   // place, but the ICS toolchain's linker is noncompliant and produces
    80   // indices that aren't entirely sorted (e.g., libc).  So we have this:
    81   std::vector<EHEntryHandle> mEntries;
    82   std::string mName;
    83 public:
    84   EHTable(const void *aELF, size_t aSize, const std::string &aName);
    85   const EHEntry *lookup(uint32_t aPC) const;
    86   bool isValid() const { return mEntries.size() > 0; }
    87   const std::string &name() const { return mName; }
    88   uint32_t startPC() const { return mStartPC; }
    89   uint32_t endPC() const { return mEndPC; }
    90   uint32_t loadOffset() const { return mLoadOffset; }
    91 };
    93 class EHAddrSpace {
    94   std::vector<uint32_t> mStarts;
    95   std::vector<EHTable> mTables;
    96   static mozilla::Atomic<const EHAddrSpace*> sCurrent;
    97 public:
    98   explicit EHAddrSpace(const std::vector<EHTable>& aTables);
    99   const EHTable *lookup(uint32_t aPC) const;
   100   static void Update();
   101   static const EHAddrSpace *Get();
   102 };
   105 void EHABIStackWalkInit()
   106 {
   107   EHAddrSpace::Update();
   108 }
   110 size_t EHABIStackWalk(const mcontext_t &aContext, void *stackBase,
   111                       void **aSPs, void **aPCs, const size_t aNumFrames)
   112 {
   113   const EHAddrSpace *space = EHAddrSpace::Get();
   114   EHState state(aContext);
   115   size_t count = 0;
   117   while (count < aNumFrames) {
   118     uint32_t pc = state[R_PC], sp = state[R_SP];
   119     aPCs[count] = reinterpret_cast<void *>(pc);
   120     aSPs[count] = reinterpret_cast<void *>(sp);
   121     count++;
   123     if (!space)
   124       break;
   125     // TODO: cache these lookups.  Binary-searching libxul is
   126     // expensive (possibly more expensive than doing the actual
   127     // unwind), and even a small cache should help.
   128     const EHTable *table = space->lookup(pc);
   129     if (!table)
   130       break;
   131     const EHEntry *entry = table->lookup(pc);
   132     if (!entry)
   133       break;
   134     if (!state.unwind(entry, stackBase))
   135       break;
   136   }
   138   return count;
   139 }
   142 struct PRel31 {
   143   uint32_t mBits;
   144   bool topBit() const { return mBits & 0x80000000; }
   145   uint32_t value() const { return mBits & 0x7fffffff; }
   146   int32_t offset() const { return (static_cast<int32_t>(mBits) << 1) >> 1; }
   147   const void *compute() const {
   148     return reinterpret_cast<const char *>(this) + offset();
   149   }
   150 private:
   151   PRel31(const PRel31 &copied) MOZ_DELETE;
   152   PRel31() MOZ_DELETE;
   153 };
   155 struct EHEntry {
   156   PRel31 startPC;
   157   PRel31 exidx;
   158 private:
   159   EHEntry(const EHEntry &copied) MOZ_DELETE;
   160   EHEntry() MOZ_DELETE;
   161 };
   164 class EHInterp {
   165 public:
   166   // Note that stackLimit is exclusive and stackBase is inclusive
   167   // (i.e, stackLimit < SP <= stackBase), following the convention
   168   // set by the AAPCS spec.
   169   EHInterp(EHState &aState, const EHEntry *aEntry,
   170            uint32_t aStackLimit, uint32_t aStackBase)
   171     : mState(aState),
   172       mStackLimit(aStackLimit),
   173       mStackBase(aStackBase),
   174       mNextWord(0),
   175       mWordsLeft(0),
   176       mFailed(false)
   177   {
   178     const PRel31 &exidx = aEntry->exidx;
   179     uint32_t firstWord;
   181     if (exidx.mBits == 1) {  // EXIDX_CANTUNWIND
   182       mFailed = true;
   183       return;
   184     }
   185     if (exidx.topBit()) {
   186       firstWord = exidx.mBits;
   187     } else {
   188       mNextWord = reinterpret_cast<const uint32_t *>(exidx.compute());
   189       firstWord = *mNextWord++;
   190     }
   192     switch (firstWord >> 24) {
   193     case 0x80: // short
   194       mWord = firstWord << 8;
   195       mBytesLeft = 3;
   196       break;
   197     case 0x81: case 0x82: // long; catch descriptor size ignored
   198       mWord = firstWord << 16;
   199       mBytesLeft = 2;
   200       mWordsLeft = (firstWord >> 16) & 0xff;
   201       break;
   202     default:
   203       // unknown personality
   204       mFailed = true;
   205     }
   206   }
   208   bool unwind();
   210 private:
   211   // TODO: GCC has been observed not CSEing repeated reads of
   212   // mState[R_SP] with writes to mFailed between them, suggesting that
   213   // it hasn't determined that they can't alias and is thus missing
   214   // optimization opportunities.  So, we may want to flatten EHState
   215   // into this class; this may also make the code simpler.
   216   EHState &mState;
   217   uint32_t mStackLimit;
   218   uint32_t mStackBase;
   219   const uint32_t *mNextWord;
   220   uint32_t mWord;
   221   uint8_t mWordsLeft;
   222   uint8_t mBytesLeft;
   223   bool mFailed;
   225   enum {
   226     I_ADDSP    = 0x00, // 0sxxxxxx (subtract if s)
   227     M_ADDSP    = 0x80,
   228     I_POPMASK  = 0x80, // 1000iiii iiiiiiii (if any i set)
   229     M_POPMASK  = 0xf0,
   230     I_MOVSP    = 0x90, // 1001nnnn
   231     M_MOVSP    = 0xf0,
   232     I_POPN     = 0xa0, // 1010lnnn
   233     M_POPN     = 0xf0,
   234     I_FINISH   = 0xb0, // 10110000
   235     I_POPLO    = 0xb1, // 10110001 0000iiii (if any i set)
   236     I_ADDSPBIG = 0xb2, // 10110010 uleb128
   237     I_POPFDX   = 0xb3, // 10110011 sssscccc
   238     I_POPFDX8  = 0xb8, // 10111nnn
   239     M_POPFDX8  = 0xf8,
   240     // "Intel Wireless MMX" extensions omitted.
   241     I_POPFDD   = 0xc8, // 1100100h sssscccc
   242     M_POPFDD   = 0xfe,
   243     I_POPFDD8  = 0xd0, // 11010nnn
   244     M_POPFDD8  = 0xf8
   245   };
   247   uint8_t next() {
   248     if (mBytesLeft == 0) {
   249       if (mWordsLeft == 0) {
   250         return I_FINISH;
   251       }
   252       mWordsLeft--;
   253       mWord = *mNextWord++;
   254       mBytesLeft = 4;
   255     }
   256     mBytesLeft--;
   257     mWord = (mWord << 8) | (mWord >> 24); // rotate
   258     return mWord;
   259   }
   261   uint32_t &vSP() { return mState[R_SP]; }
   262   uint32_t *ptrSP() { return reinterpret_cast<uint32_t *>(vSP()); }
   264   void checkStackBase() { if (vSP() > mStackBase) mFailed = true; }
   265   void checkStackLimit() { if (vSP() <= mStackLimit) mFailed = true; }
   266   void checkStackAlign() { if ((vSP() & 3) != 0) mFailed = true; }
   267   void checkStack() {
   268     checkStackBase();
   269     checkStackLimit();
   270     checkStackAlign();
   271   }
   273   void popRange(uint8_t first, uint8_t last, uint16_t mask) {
   274     bool hasSP = false;
   275     uint32_t tmpSP;
   276     if (mask == 0)
   277       mFailed = true;
   278     for (uint8_t r = first; r <= last; ++r) {
   279       if (mask & 1) {
   280         if (r == R_SP) {
   281           hasSP = true;
   282           tmpSP = *ptrSP();
   283         } else
   284           mState[r] = *ptrSP();
   285         vSP() += 4;
   286         checkStackBase();
   287         if (mFailed)
   288           return;
   289       }
   290       mask >>= 1;
   291     }
   292     if (hasSP) {
   293       vSP() = tmpSP;
   294       checkStack();
   295     }
   296   }
   297 };
   300 bool EHState::unwind(const EHEntry *aEntry, const void *stackBasePtr) {
   301   // The unwinding program cannot set SP to less than the initial value.
   302   uint32_t stackLimit = mRegs[R_SP] - 4;
   303   uint32_t stackBase = reinterpret_cast<uint32_t>(stackBasePtr);
   304   EHInterp interp(*this, aEntry, stackLimit, stackBase);
   305   return interp.unwind();
   306 }
   308 bool EHInterp::unwind() {
   309   mState[R_PC] = 0;
   310   checkStack();
   311   while (!mFailed) {
   312     uint8_t insn = next();
   313 #if DEBUG_EHABI_UNWIND
   314     LOGF("unwind insn = %02x", (unsigned)insn);
   315 #endif
   316     // Try to put the common cases first.
   318     // 00xxxxxx: vsp = vsp + (xxxxxx << 2) + 4
   319     // 01xxxxxx: vsp = vsp - (xxxxxx << 2) - 4
   320     if ((insn & M_ADDSP) == I_ADDSP) {
   321       uint32_t offset = ((insn & 0x3f) << 2) + 4;
   322       if (insn & 0x40) {
   323         vSP() -= offset;
   324         checkStackLimit();
   325       } else {
   326         vSP() += offset;
   327         checkStackBase();
   328       }
   329       continue;
   330     }
   332     // 10100nnn: Pop r4-r[4+nnn]
   333     // 10101nnn: Pop r4-r[4+nnn], r14
   334     if ((insn & M_POPN) == I_POPN) {
   335       uint8_t n = (insn & 0x07) + 1;
   336       bool lr = insn & 0x08;
   337       uint32_t *ptr = ptrSP();
   338       vSP() += (n + (lr ? 1 : 0)) * 4;
   339       checkStackBase();
   340       for (uint8_t r = 4; r < 4 + n; ++r)
   341         mState[r] = *ptr++;
   342       if (lr)
   343         mState[R_LR] = *ptr++;
   344       continue;
   345     }
   347     // 1011000: Finish
   348     if (insn == I_FINISH) {
   349       if (mState[R_PC] == 0) {
   350         mState[R_PC] = mState[R_LR];
   351         // Non-standard change (bug 916106): Prevent the caller from
   352         // re-using LR.  Since the caller is by definition not a leaf
   353         // routine, it will have to restore LR from somewhere to
   354         // return to its own caller, so we can safely zero it here.
   355         // This makes a difference only if an error in unwinding
   356         // (e.g., caused by starting from within a prologue/epilogue)
   357         // causes us to load a pointer to a leaf routine as LR; if we
   358         // don't do something, we'll go into an infinite loop of
   359         // "returning" to that same function.
   360         mState[R_LR] = 0;
   361       }
   362       return true;
   363     }
   365     // 1001nnnn: Set vsp = r[nnnn]
   366     if ((insn & M_MOVSP) == I_MOVSP) {
   367       vSP() = mState[insn & 0x0f];
   368       checkStack();
   369       continue;
   370     }
   372     // 11001000 sssscccc: Pop VFP regs D[16+ssss]-D[16+ssss+cccc] (as FLDMFDD)
   373     // 11001001 sssscccc: Pop VFP regs D[ssss]-D[ssss+cccc] (as FLDMFDD)
   374     if ((insn & M_POPFDD) == I_POPFDD) {
   375       uint8_t n = (next() & 0x0f) + 1;
   376       // Note: if the 16+ssss+cccc > 31, the encoding is reserved.
   377       // As the space is currently unused, we don't try to check.
   378       vSP() += 8 * n;
   379       checkStackBase();
   380       continue;
   381     }
   383     // 11010nnn: Pop VFP regs D[8]-D[8+nnn] (as FLDMFDD)
   384     if ((insn & M_POPFDD8) == I_POPFDD8) {
   385       uint8_t n = (insn & 0x07) + 1;
   386       vSP() += 8 * n;
   387       checkStackBase();
   388       continue;
   389     }
   391     // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
   392     if (insn == I_ADDSPBIG) {
   393       uint32_t acc = 0;
   394       uint8_t shift = 0;
   395       uint8_t byte;
   396       do {
   397         if (shift >= 32)
   398           return false;
   399         byte = next();
   400         acc |= (byte & 0x7f) << shift;
   401         shift += 7;
   402       } while (byte & 0x80);
   403       uint32_t offset = 0x204 + (acc << 2);
   404       // The calculations above could have overflowed.
   405       // But the one we care about is this:
   406       if (vSP() + offset < vSP())
   407         mFailed = true;
   408       vSP() += offset;
   409       // ...so that this is the only other check needed:
   410       checkStackBase();
   411       continue;
   412     }
   414     // 1000iiii iiiiiiii (i not all 0): Pop under masks {r15-r12}, {r11-r4}
   415     if ((insn & M_POPMASK) == I_POPMASK) {
   416       popRange(4, 15, ((insn & 0x0f) << 8) | next());
   417       continue;
   418     }
   420     // 1011001 0000iiii (i not all 0): Pop under mask {r3-r0}
   421     if (insn == I_POPLO) {
   422       popRange(0, 3, next() & 0x0f);
   423       continue;
   424     }
   426     // 10110011 sssscccc: Pop VFP regs D[ssss]-D[ssss+cccc] (as FLDMFDX)
   427     if (insn == I_POPFDX) {
   428       uint8_t n = (next() & 0x0f) + 1;
   429       vSP() += 8 * n + 4;
   430       checkStackBase();
   431       continue;
   432     }
   434     // 10111nnn: Pop VFP regs D[8]-D[8+nnn] (as FLDMFDX)
   435     if ((insn & M_POPFDX8) == I_POPFDX8) {
   436       uint8_t n = (insn & 0x07) + 1;
   437       vSP() += 8 * n + 4;
   438       checkStackBase();
   439       continue;
   440     }
   442     // unhandled instruction
   443 #ifdef DEBUG_EHABI_UNWIND
   444     LOGF("Unhandled EHABI instruction 0x%02x", insn);
   445 #endif
   446     mFailed = true;
   447   }
   448   return false;
   449 }
   452 bool operator<(const EHTable &lhs, const EHTable &rhs) {
   453   return lhs.startPC() < rhs.endPC();
   454 }
   456 // Async signal unsafe.
   457 EHAddrSpace::EHAddrSpace(const std::vector<EHTable>& aTables)
   458   : mTables(aTables)
   459 {
   460   std::sort(mTables.begin(), mTables.end());
   461   DebugOnly<uint32_t> lastEnd = 0;
   462   for (std::vector<EHTable>::iterator i = mTables.begin();
   463        i != mTables.end(); ++i) {
   464     MOZ_ASSERT(i->startPC() >= lastEnd);
   465     mStarts.push_back(i->startPC());
   466     lastEnd = i->endPC();
   467   }
   468 }
   470 const EHTable *EHAddrSpace::lookup(uint32_t aPC) const {
   471   ptrdiff_t i = (std::upper_bound(mStarts.begin(), mStarts.end(), aPC)
   472                  - mStarts.begin()) - 1;
   474   if (i < 0 || aPC >= mTables[i].endPC())
   475     return 0;
   476   return &mTables[i];
   477 }
   480 bool operator<(const EHEntryHandle &lhs, const EHEntryHandle &rhs) {
   481   return lhs.value()->startPC.compute() < rhs.value()->startPC.compute();
   482 }
   484 const EHEntry *EHTable::lookup(uint32_t aPC) const {
   485   MOZ_ASSERT(aPC >= mStartPC);
   486   if (aPC >= mEndPC)
   487     return nullptr;
   489   std::vector<EHEntryHandle>::const_iterator begin = mEntries.begin();
   490   std::vector<EHEntryHandle>::const_iterator end = mEntries.end();
   491   MOZ_ASSERT(begin < end);
   492   if (aPC < reinterpret_cast<uint32_t>(begin->value()->startPC.compute()))
   493     return nullptr;
   495   while (end - begin > 1) {
   496     std::vector<EHEntryHandle>::const_iterator mid = begin + (end - begin) / 2;
   497     if (aPC < reinterpret_cast<uint32_t>(mid->value()->startPC.compute()))
   498       end = mid;
   499     else
   500       begin = mid;
   501   }
   502   return begin->value();
   503 }
   506 #if MOZ_LITTLE_ENDIAN
   507 static const unsigned char hostEndian = ELFDATA2LSB;
   508 #elif MOZ_BIG_ENDIAN
   509 static const unsigned char hostEndian = ELFDATA2MSB;
   510 #else
   511 #error "No endian?"
   512 #endif
   514 // Async signal unsafe.  (Note use of std::vector::reserve.)
   515 EHTable::EHTable(const void *aELF, size_t aSize, const std::string &aName)
   516   : mStartPC(~0), // largest uint32_t
   517     mEndPC(0),
   518     mName(aName)
   519 {
   520   const uint32_t base = reinterpret_cast<uint32_t>(aELF);
   522   if (aSize < sizeof(Elf32_Ehdr))
   523     return;
   525   const Elf32_Ehdr &file = *(reinterpret_cast<Elf32_Ehdr *>(base));
   526   if (memcmp(&file.e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0 ||
   527       file.e_ident[EI_CLASS] != ELFCLASS32 ||
   528       file.e_ident[EI_DATA] != hostEndian ||
   529       file.e_ident[EI_VERSION] != EV_CURRENT ||
   530       file.e_ident[EI_OSABI] != ELFOSABI_SYSV ||
   531 #ifdef EI_ABIVERSION
   532       file.e_ident[EI_ABIVERSION] != 0 ||
   533 #endif
   534       file.e_machine != EM_ARM ||
   535       file.e_version != EV_CURRENT)
   536     // e_flags?
   537     return;
   539   MOZ_ASSERT(file.e_phoff + file.e_phnum * file.e_phentsize <= aSize);
   540   const Elf32_Phdr *exidxHdr = 0, *zeroHdr = 0;
   541   for (unsigned i = 0; i < file.e_phnum; ++i) {
   542     const Elf32_Phdr &phdr =
   543       *(reinterpret_cast<Elf32_Phdr *>(base + file.e_phoff
   544                                        + i * file.e_phentsize));
   545     if (phdr.p_type == PT_ARM_EXIDX) {
   546       exidxHdr = &phdr;
   547     } else if (phdr.p_type == PT_LOAD) {
   548       if (phdr.p_offset == 0) {
   549         zeroHdr = &phdr;
   550       }
   551       if (phdr.p_flags & PF_X) {
   552         mStartPC = std::min(mStartPC, phdr.p_vaddr);
   553         mEndPC = std::max(mEndPC, phdr.p_vaddr + phdr.p_memsz);
   554       }
   555     }
   556   }
   557   if (!exidxHdr)
   558     return;
   559   if (!zeroHdr)
   560     return;
   561   mLoadOffset = base - zeroHdr->p_vaddr;
   562   mStartPC += mLoadOffset;
   563   mEndPC += mLoadOffset;
   565   // Create a sorted index of the index to work around linker bugs.
   566   const EHEntry *startTable =
   567     reinterpret_cast<const EHEntry *>(mLoadOffset + exidxHdr->p_vaddr);
   568   const EHEntry *endTable =
   569     reinterpret_cast<const EHEntry *>(mLoadOffset + exidxHdr->p_vaddr
   570                                     + exidxHdr->p_memsz);
   571   mEntries.reserve(endTable - startTable);
   572   for (const EHEntry *i = startTable; i < endTable; ++i)
   573     mEntries.push_back(i);
   574   std::sort(mEntries.begin(), mEntries.end());
   575 }
   578 mozilla::Atomic<const EHAddrSpace*> EHAddrSpace::sCurrent(nullptr);
   580 // Async signal safe; can fail if Update() hasn't returned yet.
   581 const EHAddrSpace *EHAddrSpace::Get() {
   582   return sCurrent;
   583 }
   585 // Collect unwinding information from loaded objects.  Calls after the
   586 // first have no effect.  Async signal unsafe.
   587 void EHAddrSpace::Update() {
   588   const EHAddrSpace *space = sCurrent;
   589   if (space)
   590     return;
   592   SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
   593   std::vector<EHTable> tables;
   595   for (size_t i = 0; i < info.GetSize(); ++i) {
   596     const SharedLibrary &lib = info.GetEntry(i);
   597     if (lib.GetOffset() != 0)
   598       // TODO: if it has a name, and we haven't seen a mapping of
   599       // offset 0 for that file, try opening it and reading the
   600       // headers instead.  The only thing I've seen so far that's
   601       // linked so as to need that treatment is the dynamic linker
   602       // itself.
   603       continue;
   604     EHTable tab(reinterpret_cast<const void *>(lib.GetStart()),
   605               lib.GetEnd() - lib.GetStart(), lib.GetName());
   606     if (tab.isValid())
   607       tables.push_back(tab);
   608   }
   609   space = new EHAddrSpace(tables);
   611   if (!sCurrent.compareExchange(nullptr, space)) {
   612     delete space;
   613     space = sCurrent;
   614   }
   615 }
   618 EHState::EHState(const mcontext_t &context) {
   619 #ifdef linux
   620   mRegs[0] = context.arm_r0;
   621   mRegs[1] = context.arm_r1;
   622   mRegs[2] = context.arm_r2;
   623   mRegs[3] = context.arm_r3;
   624   mRegs[4] = context.arm_r4;
   625   mRegs[5] = context.arm_r5;
   626   mRegs[6] = context.arm_r6;
   627   mRegs[7] = context.arm_r7;
   628   mRegs[8] = context.arm_r8;
   629   mRegs[9] = context.arm_r9;
   630   mRegs[10] = context.arm_r10;
   631   mRegs[11] = context.arm_fp;
   632   mRegs[12] = context.arm_ip;
   633   mRegs[13] = context.arm_sp;
   634   mRegs[14] = context.arm_lr;
   635   mRegs[15] = context.arm_pc;
   636 #else
   637 # error "Unhandled OS for ARM EHABI unwinding"
   638 #endif
   639 }
   641 } // namespace mozilla

mercurial