toolkit/crashreporter/google-breakpad/src/common/arm_ex_reader.cc

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     2 /* libunwind - a platform-independent unwind library
     3    Copyright 2011 Linaro Limited
     5 This file is part of libunwind.
     7 Permission is hereby granted, free of charge, to any person obtaining
     8 a copy of this software and associated documentation files (the
     9 "Software"), to deal in the Software without restriction, including
    10 without limitation the rights to use, copy, modify, merge, publish,
    11 distribute, sublicense, and/or sell copies of the Software, and to
    12 permit persons to whom the Software is furnished to do so, subject to
    13 the following conditions:
    15 The above copyright notice and this permission notice shall be
    16 included in all copies or substantial portions of the Software.
    18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
    26 // Copyright (c) 2010 Google Inc.
    27 // All rights reserved.
    28 //
    29 // Redistribution and use in source and binary forms, with or without
    30 // modification, are permitted provided that the following conditions are
    31 // met:
    32 //
    33 //     * Redistributions of source code must retain the above copyright
    34 // notice, this list of conditions and the following disclaimer.
    35 //     * Redistributions in binary form must reproduce the above
    36 // copyright notice, this list of conditions and the following disclaimer
    37 // in the documentation and/or other materials provided with the
    38 // distribution.
    39 //     * Neither the name of Google Inc. nor the names of its
    40 // contributors may be used to endorse or promote products derived from
    41 // this software without specific prior written permission.
    42 //
    43 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    44 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    45 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    46 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    47 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    48 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    49 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    50 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    51 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    52 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    53 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    56 // Derived from libunwind, with extensive modifications.
    59 #include "common/arm_ex_reader.h"
    60 #include "common/logging.h"
    62 #include <assert.h>
    64 // This file, in conjunction with arm_ex_to_module.cc, translates
    65 // EXIDX unwind information into the same format that Breakpad uses
    66 // for CFI information.  Hence Breakpad's CFI unwinding abilities
    67 // also become usable for EXIDX.
    68 //
    69 // See: "Exception Handling ABI for the ARM Architecture", ARM IHI 0038A
    70 // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
    72 // EXIDX data is presented in two parts:
    73 //
    74 // * an index table.  This contains two words per routine,
    75 //   the first of which identifies the routine, and the second
    76 //   of which is a reference to the unwind bytecode.  If the
    77 //   bytecode is very compact -- 3 bytes or less -- it can be
    78 //   stored directly in the second word.
    79 //
    80 // * an area containing the unwind bytecodes.
    82 // General flow is: ExceptionTableInfo::Start iterates over all
    83 // of the index table entries (pairs).  For each entry, it:
    84 //
    85 // * calls ExceptionTableInfo::ExtabEntryExtract to copy the bytecode
    86 //   out into an intermediate buffer.
    88 // * uses ExceptionTableInfo::ExtabEntryDecode to parse the intermediate
    89 //   buffer.  Each bytecode instruction is bundled into a
    90 //   arm_ex_to_module::extab_data structure, and handed to ..
    91 //
    92 // * .. ARMExToModule::ImproveStackFrame, which in turn hands it to
    93 //   ARMExToModule::TranslateCmd, and that generates the pseudo-CFI
    94 //   records that Breakpad stores.
    96 #define ARM_EXIDX_CANT_UNWIND 0x00000001
    97 #define ARM_EXIDX_COMPACT     0x80000000
    98 #define ARM_EXTBL_OP_FINISH   0xb0
    99 #define ARM_EXIDX_TABLE_LIMIT (255*4)
   101 namespace arm_ex_reader {
   103 using arm_ex_to_module::ARM_EXIDX_CMD_FINISH;
   104 using arm_ex_to_module::ARM_EXIDX_CMD_SUB_FROM_VSP;
   105 using arm_ex_to_module::ARM_EXIDX_CMD_ADD_TO_VSP;
   106 using arm_ex_to_module::ARM_EXIDX_CMD_REG_POP;
   107 using arm_ex_to_module::ARM_EXIDX_CMD_REG_TO_SP;
   108 using arm_ex_to_module::ARM_EXIDX_CMD_VFP_POP;
   109 using arm_ex_to_module::ARM_EXIDX_CMD_WREG_POP;
   110 using arm_ex_to_module::ARM_EXIDX_CMD_WCGR_POP;
   111 using arm_ex_to_module::ARM_EXIDX_CMD_RESERVED;
   112 using arm_ex_to_module::ARM_EXIDX_CMD_REFUSED;
   113 using arm_ex_to_module::exidx_entry;
   114 using arm_ex_to_module::ARM_EXIDX_VFP_SHIFT_16;
   115 using arm_ex_to_module::ARM_EXIDX_VFP_FSTMD;
   116 using google_breakpad::MemoryRange;
   119 static void* Prel31ToAddr(const void* addr) 
   120 {
   121   uint32_t offset32 = *reinterpret_cast<const uint32_t*>(addr);
   122   // sign extend offset32[30:0] to 64 bits -- copy bit 30 to positions
   123   // 63:31 inclusive.
   124   uint64_t offset64 = offset32;
   125   if (offset64 & (1ULL << 30))
   126     offset64 |= 0xFFFFFFFF80000000ULL;
   127   else
   128     offset64 &= 0x000000007FFFFFFFULL;
   129   return ((char*)addr) + (uintptr_t)offset64;
   130 }
   133 // Extract unwind bytecode for the function denoted by |entry| into |buf|,
   134 // and return the number of bytes of |buf| written, along with a code
   135 // indicating the outcome.
   137 ExceptionTableInfo::ExExtractResult
   138 ExceptionTableInfo::ExtabEntryExtract(const struct exidx_entry* entry,
   139                                       uint8_t* buf, size_t buf_size,
   140                                       /*OUT*/size_t* buf_used)
   141 {
   142   MemoryRange mr_out(buf, buf_size);
   144   *buf_used = 0;
   146 # define PUT_BUF_U8(_byte) \
   147   do { if (!mr_out.Covers(*buf_used, 1)) return ExOutBufOverflow; \
   148        buf[(*buf_used)++] = (_byte); } while (0)
   150 # define GET_EX_U32(_lval, _addr, _sec_mr) \
   151   do { if (!(_sec_mr).Covers(reinterpret_cast<const uint8_t*>(_addr) \
   152                              - (_sec_mr).data(), 4)) \
   153          return ExInBufOverflow; \
   154        (_lval) = *(reinterpret_cast<const uint32_t*>(_addr)); } while (0)
   156 # define GET_EXIDX_U32(_lval, _addr) \
   157             GET_EX_U32(_lval, _addr, mr_exidx_)
   158 # define GET_EXTAB_U32(_lval, _addr) \
   159             GET_EX_U32(_lval, _addr, mr_extab_)
   161   uint32_t data;
   162   GET_EXIDX_U32(data, &entry->data);
   164   // A function can be marked CANT_UNWIND if (eg) it is known to be
   165   // at the bottom of the stack.
   166   if (data == ARM_EXIDX_CANT_UNWIND)
   167     return ExCantUnwind;
   169   uint32_t  pers;          // personality number
   170   uint32_t  extra;         // number of extra data words required
   171   uint32_t  extra_allowed; // number of extra data words allowed
   172   uint32_t* extbl_data;    // the handler entry, if not inlined
   174   if (data & ARM_EXIDX_COMPACT) {
   175     // The handler table entry has been inlined into the index table entry.
   176     // In this case it can only be an ARM-defined compact model, since
   177     // bit 31 is 1.  Only personalities 0, 1 and 2 are defined for the
   178     // ARM compact model, but 1 and 2 are "Long format" and may require
   179     // extra data words.  Hence the allowable personalities here are:
   180     //   personality 0, in which case 'extra' has no meaning
   181     //   personality 1, with zero extra words
   182     //   personality 2, with zero extra words
   183     extbl_data = NULL;
   184     pers  = (data >> 24) & 0x0F;
   185     extra = (data >> 16) & 0xFF;
   186     extra_allowed = 0;
   187   }
   188   else {
   189     // The index table entry is a pointer to the handler entry.  Note
   190     // that Prel31ToAddr will read the given address, but we already
   191     // range-checked above.
   192     extbl_data = reinterpret_cast<uint32_t*>(Prel31ToAddr(&entry->data));
   193     GET_EXTAB_U32(data, extbl_data);
   194     if (!(data & ARM_EXIDX_COMPACT)) {
   195       // This denotes a "generic model" handler.  That will involve
   196       // executing arbitary machine code, which is something we
   197       // can't represent here; hence reject it.
   198       return ExCantRepresent;
   199     }
   200     // So we have a compact model representation.  Again, 3 possible
   201     // personalities, but this time up to 255 allowable extra words.
   202     pers  = (data >> 24) & 0x0F;
   203     extra = (data >> 16) & 0xFF;
   204     extra_allowed = 255;
   205     extbl_data++;
   206   }
   208   // Now look at the the handler table entry.  The first word is
   209   // |data| and subsequent words start at |*extbl_data|.  The number
   210   // of extra words to use is |extra|, provided that the personality
   211   // allows extra words.  Even if it does, none may be available --
   212   // extra_allowed is the maximum number of extra words allowed. */
   213   if (pers == 0) {
   214     // "Su16" in the documentation -- 3 unwinding insn bytes
   215     // |extra| has no meaning here; instead that byte is an unwind-info byte
   216     PUT_BUF_U8(data >> 16);
   217     PUT_BUF_U8(data >> 8);
   218     PUT_BUF_U8(data);
   219   }
   220   else if ((pers == 1 || pers == 2) && extra <= extra_allowed) {
   221     // "Lu16" or "Lu32" respectively -- 2 unwinding insn bytes,
   222     // and up to 255 extra words.
   223     PUT_BUF_U8(data >> 8);
   224     PUT_BUF_U8(data);
   225     for (uint32_t j = 0; j < extra; j++) {
   226       GET_EXTAB_U32(data, extbl_data);
   227       extbl_data++;
   228       PUT_BUF_U8(data >> 24);
   229       PUT_BUF_U8(data >> 16);
   230       PUT_BUF_U8(data >> 8);
   231       PUT_BUF_U8(data >> 0);
   232     }
   233   }
   234   else {
   235     // The entry is invalid.
   236     return ExInvalid;
   237   }
   239   // Make sure the entry is terminated with "FINISH"
   240   if (*buf_used > 0 && buf[(*buf_used) - 1] != ARM_EXTBL_OP_FINISH)
   241     PUT_BUF_U8(ARM_EXTBL_OP_FINISH);
   243   return ExSuccess;
   245 # undef GET_EXTAB_U32
   246 # undef GET_EXIDX_U32
   247 # undef GET_U32
   248 # undef PUT_BUF_U8
   249 }
   252 // Take the unwind information extracted by ExtabEntryExtract
   253 // and parse it into frame-unwind instructions.  These are as
   254 // specified in "Table 4, ARM-defined frame-unwinding instructions"
   255 // in the specification document detailed in comments at the top
   256 // of this file.
   257 //
   258 // This reads from |buf[0, +data_size)|.  It checks for overruns of
   259 // the input buffer and returns a negative value if that happens, or
   260 // for any other failure cases.  It returns zero in case of success.
   261 int ExceptionTableInfo::ExtabEntryDecode(const uint8_t* buf, size_t buf_size)
   262 {
   263   if (buf == NULL || buf_size == 0)
   264     return -1;
   266   MemoryRange mr_in(buf, buf_size);
   267   const uint8_t* buf_initially = buf;
   269 # define GET_BUF_U8(_lval) \
   270   do { if (!mr_in.Covers(buf - buf_initially, 1)) return -1; \
   271        (_lval) = *(buf++); } while (0)
   273   const uint8_t* end = buf + buf_size;
   275   while (buf < end) {
   276     struct arm_ex_to_module::extab_data edata;
   277     memset(&edata, 0, sizeof(edata));
   279     uint8_t op;
   280     GET_BUF_U8(op);
   281     if ((op & 0xc0) == 0x00) {
   282       // vsp = vsp + (xxxxxx << 2) + 4
   283       edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
   284       edata.data = (((int)op & 0x3f) << 2) + 4;
   285     }
   286     else if ((op & 0xc0) == 0x40) {
   287       // vsp = vsp - (xxxxxx << 2) - 4
   288       edata.cmd = ARM_EXIDX_CMD_SUB_FROM_VSP;
   289       edata.data = (((int)op & 0x3f) << 2) + 4;
   290     }
   291     else if ((op & 0xf0) == 0x80) {
   292       uint8_t op2;
   293       GET_BUF_U8(op2);
   294       if (op == 0x80 && op2 == 0x00) {
   295         // Refuse to unwind
   296         edata.cmd = ARM_EXIDX_CMD_REFUSED;
   297       } else {
   298         // Pop up to 12 integer registers under masks {r15-r12},{r11-r4}
   299         edata.cmd = ARM_EXIDX_CMD_REG_POP;
   300         edata.data = ((op & 0xf) << 8) | op2;
   301         edata.data = edata.data << 4;
   302       }
   303     }
   304     else if ((op & 0xf0) == 0x90) {
   305       if (op == 0x9d || op == 0x9f) {
   306         // 9d: Reserved as prefix for ARM register to register moves
   307         // 9f: Reserved as perfix for Intel Wireless MMX reg to reg moves
   308         edata.cmd = ARM_EXIDX_CMD_RESERVED;
   309       } else {
   310         // Set vsp = r[nnnn]
   311         edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
   312         edata.data = op & 0x0f;
   313       }
   314     }
   315     else if ((op & 0xf0) == 0xa0) {
   316       // Pop r4 to r[4+nnn],          or
   317       // Pop r4 to r[4+nnn] and r14   or
   318       unsigned end = (op & 0x07);
   319       edata.data = (1 << (end + 1)) - 1;
   320       edata.data = edata.data << 4;
   321       if (op & 0x08) edata.data |= 1 << 14;
   322       edata.cmd = ARM_EXIDX_CMD_REG_POP;
   323     }
   324     else if (op == ARM_EXTBL_OP_FINISH) {
   325       // Finish
   326       edata.cmd = ARM_EXIDX_CMD_FINISH;
   327       buf = end;
   328     }
   329     else if (op == 0xb1) {
   330       uint8_t op2;
   331       GET_BUF_U8(op2);
   332       if (op2 == 0 || (op2 & 0xf0)) {
   333         // Spare
   334         edata.cmd = ARM_EXIDX_CMD_RESERVED;
   335       } else {
   336         // Pop integer registers under mask {r3,r2,r1,r0}
   337         edata.cmd = ARM_EXIDX_CMD_REG_POP;
   338         edata.data = op2 & 0x0f;
   339       }
   340     }
   341     else if (op == 0xb2) {
   342       // vsp = vsp + 0x204 + (uleb128 << 2)
   343       uint64_t offset = 0;
   344       uint8_t byte, shift = 0;
   345       do {
   346         GET_BUF_U8(byte);
   347         offset |= (byte & 0x7f) << shift;
   348         shift += 7;
   349       } while ((byte & 0x80) && buf < end);
   350       edata.data = offset * 4 + 0x204;
   351       edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
   352     }
   353     else if (op == 0xb3 || op == 0xc8 || op == 0xc9) {
   354       // b3: Pop VFP regs D[ssss]    to D[ssss+cccc],    FSTMFDX-ishly
   355       // c8: Pop VFP regs D[16+ssss] to D[16+ssss+cccc], FSTMFDD-ishly
   356       // c9: Pop VFP regs D[ssss]    to D[ssss+cccc],    FSTMFDD-ishly
   357       edata.cmd = ARM_EXIDX_CMD_VFP_POP;
   358       GET_BUF_U8(edata.data);
   359       if (op == 0xc8) edata.data |= ARM_EXIDX_VFP_SHIFT_16;
   360       if (op != 0xb3) edata.data |= ARM_EXIDX_VFP_FSTMD;
   361     }
   362     else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0) {
   363       // b8: Pop VFP regs D[8] to D[8+nnn], FSTMFDX-ishly
   364       // d0: Pop VFP regs D[8] to D[8+nnn], FSTMFDD-ishly
   365       edata.cmd = ARM_EXIDX_CMD_VFP_POP;
   366       edata.data = 0x80 | (op & 0x07);
   367       if ((op & 0xf8) == 0xd0) edata.data |= ARM_EXIDX_VFP_FSTMD;
   368     }
   369     else if (op >= 0xc0 && op <= 0xc5) {
   370       // Intel Wireless MMX pop wR[10]-wr[10+nnn], nnn != 6,7
   371       edata.cmd = ARM_EXIDX_CMD_WREG_POP;
   372       edata.data = 0xa0 | (op & 0x07);
   373     }
   374     else if (op == 0xc6) {
   375       // Intel Wireless MMX pop wR[ssss] to wR[ssss+cccc]
   376       edata.cmd = ARM_EXIDX_CMD_WREG_POP;
   377       GET_BUF_U8(edata.data);
   378     }
   379     else if (op == 0xc7) {
   380       uint8_t op2;
   381       GET_BUF_U8(op2);
   382       if (op2 == 0 || (op2 & 0xf0)) {
   383         // Spare
   384         edata.cmd = ARM_EXIDX_CMD_RESERVED;
   385       } else {
   386         // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}
   387         edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
   388         edata.data = op2 & 0x0f;
   389       }
   390     }
   391     else {
   392       // Spare
   393       edata.cmd = ARM_EXIDX_CMD_RESERVED;
   394     }
   396     int ret = handler_->ImproveStackFrame(&edata);
   397     if (ret < 0) return ret;
   398   }
   399   return 0;
   401 # undef GET_BUF_U8
   402 }
   404 void ExceptionTableInfo::Start()
   405 {
   406   const struct exidx_entry* start
   407     = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data());
   408   const struct exidx_entry* end
   409     = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data()
   410                                                   + mr_exidx_.length());
   412   // Iterate over each of the EXIDX entries (pairs of 32-bit words).
   413   // These occupy the entire .exidx section.
   414   for (const struct exidx_entry* entry = start; entry < end; ++entry) {
   416     // Figure out the code address range that this table entry is
   417     // associated with.
   418     uint32_t addr = (reinterpret_cast<char*>(Prel31ToAddr(&entry->addr))
   419                      - mapping_addr_ + loading_addr_) & 0x7fffffff;
   420     uint32_t next_addr;
   421     if (entry < end - 1)
   422       next_addr = (reinterpret_cast<char*>(Prel31ToAddr(&((entry + 1)->addr)))
   423                    - mapping_addr_ + loading_addr_) & 0x7fffffff;
   424     else {
   425       // This is the last EXIDX entry in the sequence, so we don't
   426       // have an address for the start of the next function, to limit
   427       // this one.  Instead use the address of the last byte of the
   428       // text section associated with this .exidx section, that we
   429       // have been given.  So as to avoid junking up the CFI unwind
   430       // tables with absurdly large address ranges in the case where
   431       // text_last_svma_ is wrong, only use the value if it is nonzero
   432       // and within one page of |addr|.  Otherwise assume a length of 1.
   433       //
   434       // In some cases, gcc has been observed to finish the exidx
   435       // section with an entry of length 1 marked CANT_UNWIND,
   436       // presumably exactly for the purpose of giving a definite
   437       // length for the last real entry, without having to look at
   438       // text segment boundaries.
   439       bool plausible = false;
   440       next_addr = addr + 1;
   441       if (text_last_svma_ != 0) {
   442         uint32_t maybe_next_addr = text_last_svma_ + 1;
   443         if (maybe_next_addr > addr && maybe_next_addr - addr <= 4096) {
   444           next_addr = maybe_next_addr;
   445           plausible = true;
   446         }
   447       }
   448       if (!plausible)
   449         BPLOG(INFO) << "ExceptionTableInfo: implausible EXIDX last entry size "
   450                     << (int32_t)(text_last_svma_ - addr)
   451                     << "; using 1 instead.";
   452     }
   454     // Extract the unwind info into |buf|.  This might fail for
   455     // various reasons.  It involves reading both the .exidx and
   456     // .extab sections.  All accesses to those sections are
   457     // bounds-checked.
   458     uint8_t buf[ARM_EXIDX_TABLE_LIMIT];
   459     size_t buf_used = 0;
   460     ExExtractResult res = ExtabEntryExtract(entry, buf, sizeof(buf), &buf_used);
   461     if (res != ExSuccess) {
   462       // Couldn't extract the unwind info, for some reason.  Move on.
   463       switch (res) {
   464         case ExInBufOverflow:
   465           BPLOG(INFO) << "ExtabEntryExtract: .exidx/.extab section overrun";
   466           break;
   467         case ExOutBufOverflow:
   468           BPLOG(INFO) << "ExtabEntryExtract: bytecode buffer overflow";
   469           break;
   470         case ExCantUnwind:
   471           BPLOG(INFO) << "ExtabEntryExtract: function is marked CANT_UNWIND";
   472           break;
   473         case ExCantRepresent:
   474           BPLOG(INFO) << "ExtabEntryExtract: bytecode can't be represented";
   475           break;
   476         case ExInvalid:
   477           BPLOG(INFO) << "ExtabEntryExtract: index table entry is invalid";
   478           break;
   479         default:
   480           BPLOG(INFO) << "ExtabEntryExtract: unknown error: " << (int)res;
   481           break;
   482       }
   483       continue;
   484     }
   486     // Finally, work through the unwind instructions in |buf| and
   487     // create CFI entries that Breakpad can use.  This can also fail.
   488     // First, add a new stack frame entry, into which ExtabEntryDecode
   489     // will write the CFI entries.
   490     handler_->AddStackFrame(addr, next_addr - addr);
   491     int ret = ExtabEntryDecode(buf, buf_used);
   492     if (ret < 0) {
   493       handler_->DeleteStackFrame();
   494       BPLOG(INFO) << "ExtabEntryDecode: failed with error code: " << ret;
   495       continue;
   496     }
   497     handler_->SubmitStackFrame();
   499   } /* iterating over .exidx */
   500 }
   502 } // arm_ex_reader

mercurial