Add error propagation into Unwinder/Elf objects.

The backtrace offline code uses these error codes to diagnose errors.
In addtion, I've had cases where seeing these errors would help diagnose
failures.

This also allows us to add a few features to indicate why an unwind
terminated (such as max frames exceeded).

Bug: 65682279

Test: Updated unit tests pass.
Change-Id: If82b5092698e8a194016d670efff1320f9b44d50
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 6b0646f..65638ae 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -57,6 +57,7 @@
   uint32_t data;
   if (!elf_memory_->Read32(entry_offset + 4, &data)) {
     status_ = ARM_STATUS_READ_FAILED;
+    status_address_ = entry_offset + 4;
     return false;
   }
   if (data == 1) {
@@ -97,6 +98,7 @@
   uint32_t addr = (entry_offset + 4) + signed_data;
   if (!elf_memory_->Read32(addr, &data)) {
     status_ = ARM_STATUS_READ_FAILED;
+    status_address_ = addr;
     return false;
   }
 
@@ -128,6 +130,7 @@
     addr += 4;
     if (!elf_memory_->Read32(addr, &data)) {
       status_ = ARM_STATUS_READ_FAILED;
+      status_address_ = addr;
       return false;
     }
     num_table_words = (data >> 24) & 0xff;
@@ -145,6 +148,7 @@
   for (size_t i = 0; i < num_table_words; i++) {
     if (!elf_memory_->Read32(addr, &data)) {
       status_ = ARM_STATUS_READ_FAILED;
+      status_address_ = addr;
       return false;
     }
     data_.push_back((data >> 24) & 0xff);
@@ -216,6 +220,7 @@
     if (registers & (1 << reg)) {
       if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
         status_ = ARM_STATUS_READ_FAILED;
+        status_address_ = cfa_;
         return false;
       }
       cfa_ += 4;
@@ -284,6 +289,7 @@
   for (size_t i = 4; i <= 4 + (byte & 0x7); i++) {
     if (!process_memory_->Read32(cfa_, &(*regs_)[i])) {
       status_ = ARM_STATUS_READ_FAILED;
+      status_address_ = cfa_;
       return false;
     }
     cfa_ += 4;
@@ -291,6 +297,7 @@
   if (byte & 0x8) {
     if (!process_memory_->Read32(cfa_, &(*regs_)[ARM_REG_R14])) {
       status_ = ARM_STATUS_READ_FAILED;
+      status_address_ = cfa_;
       return false;
     }
     cfa_ += 4;
@@ -357,6 +364,7 @@
     if (byte & (1 << reg)) {
       if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
         status_ = ARM_STATUS_READ_FAILED;
+        status_address_ = cfa_;
         return false;
       }
       cfa_ += 4;
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
index f4361d4..96756a0 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -61,6 +61,7 @@
   std::deque<uint8_t>* data() { return &data_; }
 
   ArmStatus status() { return status_; }
+  uint64_t status_address() { return status_address_; }
 
   RegsArm* regs() { return regs_; }
 
@@ -97,6 +98,7 @@
   uint32_t cfa_ = 0;
   std::deque<uint8_t> data_;
   ArmStatus status_ = ARM_STATUS_NONE;
+  uint64_t status_address_ = 0;
 
   Memory* elf_memory_;
   Memory* process_memory_;
diff --git a/libunwindstack/Check.h b/libunwindstack/Check.h
index 2d216d7..9643d76 100644
--- a/libunwindstack/Check.h
+++ b/libunwindstack/Check.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_ERROR_H
-#define _LIBUNWINDSTACK_ERROR_H
+#ifndef _LIBUNWINDSTACK_CHECK_H
+#define _LIBUNWINDSTACK_CHECK_H
 
 #include <stdlib.h>
 
@@ -31,4 +31,4 @@
 
 }  // namespace unwindstack
 
-#endif  // _LIBUNWINDSTACK_ERROR_H
+#endif  // _LIBUNWINDSTACK_CHECK_H
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index b1d55ba..4fc95c7 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -23,12 +23,12 @@
 
 #include <android-base/stringprintf.h>
 
+#include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfLocation.h>
 #include <unwindstack/Log.h>
 
 #include "DwarfCfa.h"
 #include "DwarfEncoding.h"
-#include "DwarfError.h"
 #include "DwarfOp.h"
 
 namespace unwindstack {
@@ -44,7 +44,8 @@
       (*loc_regs)[entry.first] = entry.second;
     }
   }
-  last_error_ = DWARF_ERROR_NONE;
+  last_error_.code = DWARF_ERROR_NONE;
+  last_error_.address = 0;
 
   memory_->set_cur_offset(start_offset);
   uint64_t cfa_offset;
@@ -54,7 +55,8 @@
     // Read the cfa information.
     uint8_t cfa_value;
     if (!memory_->ReadBytes(&cfa_value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_->cur_offset();
       return false;
     }
     uint8_t cfa_low = cfa_value & 0x3f;
@@ -66,7 +68,8 @@
       case 2: {
         uint64_t offset;
         if (!memory_->ReadULEB128(&offset)) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_->cur_offset();
           return false;
         }
         SignedType signed_offset =
@@ -78,7 +81,7 @@
       case 3: {
         if (cie_loc_regs_ == nullptr) {
           log(0, "restore while processing cie");
-          last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+          last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
           return false;
         }
 
@@ -93,7 +96,7 @@
       case 0: {
         const auto handle_func = DwarfCfa<AddressType>::kCallbackTable[cfa_low];
         if (handle_func == nullptr) {
-          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
           return false;
         }
 
@@ -102,7 +105,8 @@
           if (cfa->operands[i] == DW_EH_PE_block) {
             uint64_t block_length;
             if (!memory_->ReadULEB128(&block_length)) {
-              last_error_ = DWARF_ERROR_MEMORY_INVALID;
+              last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+              last_error_.address = memory_->cur_offset();
               return false;
             }
             operands_.push_back(block_length);
@@ -111,7 +115,8 @@
           }
           uint64_t value;
           if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
-            last_error_ = DWARF_ERROR_MEMORY_INVALID;
+            last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+            last_error_.address = memory_->cur_offset();
             return false;
           }
           operands_.push_back(value);
@@ -334,7 +339,7 @@
   AddressType reg = operands_[0];
   if (cie_loc_regs_ == nullptr) {
     log(0, "restore while processing cie");
-    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
   auto reg_entry = cie_loc_regs_->find(reg);
@@ -396,7 +401,7 @@
   auto cfa_location = loc_regs->find(CFA_REG);
   if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
     log(0, "Attempt to set new register, but cfa is not already set to a register.");
-    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
 
@@ -410,7 +415,7 @@
   auto cfa_location = loc_regs->find(CFA_REG);
   if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
     log(0, "Attempt to set offset, but cfa is not set to a register.");
-    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
   cfa_location->second.values[1] = operands_[0];
@@ -454,7 +459,7 @@
   auto cfa_location = loc_regs->find(CFA_REG);
   if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
     log(0, "Attempt to set offset, but cfa is not set to a register.");
-    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
   SignedType offset = static_cast<SignedType>(operands_[0]) * fde_->cie->data_alignment_factor;
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index 62b9b7a..16c66e2 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -24,12 +24,11 @@
 #include <type_traits>
 #include <vector>
 
+#include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfLocation.h>
 #include <unwindstack/DwarfMemory.h>
 #include <unwindstack/DwarfStructs.h>
 
-#include "DwarfError.h"
-
 namespace unwindstack {
 
 // DWARF Standard home: http://dwarfstd.org/
@@ -75,7 +74,9 @@
   bool Log(uint32_t indent, uint64_t pc, uint64_t load_bias, uint64_t start_offset,
            uint64_t end_offset);
 
-  DwarfError last_error() { return last_error_; }
+  const DwarfErrorData& last_error() { return last_error_; }
+  DwarfErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
 
   AddressType cur_pc() { return cur_pc_; }
 
@@ -89,7 +90,7 @@
   bool LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op, uint64_t* cur_pc);
 
  private:
-  DwarfError last_error_;
+  DwarfErrorData last_error_;
   DwarfMemory* memory_;
   const DwarfFde* fde_;
 
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index e0f1eed..a131abe 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -16,12 +16,12 @@
 
 #include <stdint.h>
 
+#include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfStructs.h>
 #include <unwindstack/Memory.h>
 
 #include "Check.h"
 #include "DwarfEhFrameWithHdr.h"
-#include "DwarfError.h"
 
 namespace unwindstack {
 
@@ -36,14 +36,15 @@
 
   // Read the first four bytes all at once.
   if (!memory_.ReadBytes(data, 4)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
   version_ = data[0];
   if (version_ != 1) {
     // Unknown version.
-    last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+    last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
     return false;
   }
 
@@ -54,18 +55,20 @@
 
   memory_.set_pc_offset(memory_.cur_offset());
   if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
   memory_.set_pc_offset(memory_.cur_offset());
   if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
   if (fde_count_ == 0) {
-    last_error_ = DWARF_ERROR_NO_FDES;
+    last_error_.code = DWARF_ERROR_NO_FDES;
     return false;
   }
 
@@ -101,7 +104,8 @@
   uint64_t value;
   if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
       !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     fde_info_.erase(index);
     return nullptr;
   }
@@ -147,7 +151,9 @@
 template <typename AddressType>
 bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
   CHECK(fde_count_ != 0);
-  last_error_ = DWARF_ERROR_NONE;
+  last_error_.code = DWARF_ERROR_NONE;
+  last_error_.address = 0;
+
   // We can do a binary search if the pc is in the range of the elements
   // that have already been cached.
   if (!fde_info_.empty()) {
@@ -176,14 +182,16 @@
     memory_.set_pc_offset(memory_.cur_offset());
     uint64_t value;
     if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
 
     FdeInfo* info = &fde_info_[current];
     if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
       fde_info_.erase(current);
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     info->pc = value + 4;
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
index 3b3d340..dcf04e6 100644
--- a/libunwindstack/DwarfOp.cpp
+++ b/libunwindstack/DwarfOp.cpp
@@ -22,12 +22,12 @@
 
 #include <android-base/stringprintf.h>
 
+#include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfMemory.h>
 #include <unwindstack/Log.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
 
-#include "DwarfError.h"
 #include "DwarfOp.h"
 
 namespace unwindstack {
@@ -48,7 +48,7 @@
     // To protect against a branch that creates an infinite loop,
     // terminate if the number of iterations gets too high.
     if (iterations++ == 1000) {
-      last_error_ = DWARF_ERROR_TOO_MANY_ITERATIONS;
+      last_error_.code = DWARF_ERROR_TOO_MANY_ITERATIONS;
       return false;
     }
   }
@@ -57,28 +57,29 @@
 
 template <typename AddressType>
 bool DwarfOp<AddressType>::Decode(uint8_t dwarf_version) {
-  last_error_ = DWARF_ERROR_NONE;
+  last_error_.code = DWARF_ERROR_NONE;
   if (!memory_->ReadBytes(&cur_op_, 1)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_->cur_offset();
     return false;
   }
 
   const auto* op = &kCallbackTable[cur_op_];
   const auto handle_func = op->handle_func;
   if (handle_func == nullptr) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
 
   // Check for an unsupported opcode.
   if (dwarf_version < op->supported_version) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
 
   // Make sure that the required number of stack elements is available.
   if (stack_.size() < op->num_required_stack_values) {
-    last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+    last_error_.code = DWARF_ERROR_STACK_INDEX_NOT_VALID;
     return false;
   }
 
@@ -86,7 +87,8 @@
   for (size_t i = 0; i < op->num_operands; i++) {
     uint64_t value;
     if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_->cur_offset();
       return false;
     }
     operands_.push_back(value);
@@ -142,7 +144,8 @@
   AddressType addr = StackPop();
   AddressType value;
   if (!regular_memory()->ReadFully(addr, &value, sizeof(value))) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = addr;
     return false;
   }
   stack_.push_front(value);
@@ -153,14 +156,15 @@
 bool DwarfOp<AddressType>::op_deref_size() {
   AddressType bytes_to_read = OperandAt(0);
   if (bytes_to_read > sizeof(AddressType) || bytes_to_read == 0) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
   // Read the address and dereference it.
   AddressType addr = StackPop();
   AddressType value = 0;
   if (!regular_memory()->ReadFully(addr, &value, bytes_to_read)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = addr;
     return false;
   }
   stack_.push_front(value);
@@ -198,7 +202,7 @@
 bool DwarfOp<AddressType>::op_pick() {
   AddressType index = OperandAt(0);
   if (index > StackSize()) {
-    last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+    last_error_.code = DWARF_ERROR_STACK_INDEX_NOT_VALID;
     return false;
   }
   stack_.push_front(StackAt(index));
@@ -243,7 +247,7 @@
 bool DwarfOp<AddressType>::op_div() {
   AddressType top = StackPop();
   if (top == 0) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
   SignedType signed_divisor = static_cast<SignedType>(top);
@@ -263,7 +267,7 @@
 bool DwarfOp<AddressType>::op_mod() {
   AddressType top = StackPop();
   if (top == 0) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
   stack_[0] %= top;
@@ -431,7 +435,7 @@
 bool DwarfOp<AddressType>::op_breg() {
   uint16_t reg = cur_op() - 0x70;
   if (reg >= regs_->total_regs()) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
   stack_.push_front((*regs_)[reg] + OperandAt(0));
@@ -442,7 +446,7 @@
 bool DwarfOp<AddressType>::op_bregx() {
   AddressType reg = OperandAt(0);
   if (reg >= regs_->total_regs()) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
   stack_.push_front((*regs_)[reg] + OperandAt(1));
@@ -456,7 +460,7 @@
 
 template <typename AddressType>
 bool DwarfOp<AddressType>::op_not_implemented() {
-  last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+  last_error_.code = DWARF_ERROR_NOT_IMPLEMENTED;
   return false;
 }
 
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index c29bf35..40b7b23 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -24,8 +24,9 @@
 #include <type_traits>
 #include <vector>
 
+#include <unwindstack/DwarfError.h>
+
 #include "DwarfEncoding.h"
-#include "DwarfError.h"
 
 namespace unwindstack {
 
@@ -72,7 +73,9 @@
 
   void set_regs(RegsImpl<AddressType>* regs) { regs_ = regs; }
 
-  DwarfError last_error() { return last_error_; }
+  const DwarfErrorData& last_error() { return last_error_; }
+  DwarfErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
 
   bool is_register() { return is_register_; }
 
@@ -96,7 +99,7 @@
 
   RegsImpl<AddressType>* regs_;
   bool is_register_ = false;
-  DwarfError last_error_ = DWARF_ERROR_NONE;
+  DwarfErrorData last_error_{DWARF_ERROR_NONE, 0};
   uint8_t cur_op_;
   std::vector<AddressType> operands_;
   std::deque<AddressType> stack_;
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 04a1fae..4e94f88 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -16,6 +16,7 @@
 
 #include <stdint.h>
 
+#include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfLocation.h>
 #include <unwindstack/DwarfMemory.h>
 #include <unwindstack/DwarfSection.h>
@@ -26,7 +27,6 @@
 
 #include "DwarfCfa.h"
 #include "DwarfEncoding.h"
-#include "DwarfError.h"
 #include "DwarfOp.h"
 
 #include "DwarfDebugFrame.h"
@@ -36,7 +36,7 @@
 
 constexpr uint64_t DEX_PC_REG = 0x20444558;
 
-DwarfSection::DwarfSection(Memory* memory) : memory_(memory), last_error_(DWARF_ERROR_NONE) {}
+DwarfSection::DwarfSection(Memory* memory) : memory_(memory) {}
 
 const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
   uint64_t fde_offset;
@@ -52,15 +52,15 @@
   if (pc < fde->pc_end) {
     return fde;
   }
-  last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+  last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
   return nullptr;
 }
 
 bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
-  last_error_ = DWARF_ERROR_NONE;
+  last_error_.code = DWARF_ERROR_NONE;
   const DwarfFde* fde = GetFdeFromPc(pc);
   if (fde == nullptr || fde->cie == nullptr) {
-    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
 
@@ -87,12 +87,12 @@
     return false;
   }
   if (op.StackSize() == 0) {
-    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
   // We don't support an expression that evaluates to a register number.
   if (op.is_register()) {
-    last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+    last_error_.code = DWARF_ERROR_NOT_IMPLEMENTED;
     return false;
   }
   *value = op.StackAt(0);
@@ -119,7 +119,8 @@
   switch (loc->type) {
     case DWARF_LOCATION_OFFSET:
       if (!regular_memory->ReadFully(eval_info->cfa + loc->values[0], reg_ptr, sizeof(AddressType))) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.address = eval_info->cfa + loc->values[0];
         return false;
       }
       break;
@@ -129,7 +130,7 @@
     case DWARF_LOCATION_REGISTER: {
       uint32_t cur_reg = loc->values[0];
       if (cur_reg >= eval_info->cur_regs->total_regs()) {
-        last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+        last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
         return false;
       }
       AddressType* cur_reg_ptr = &(*eval_info->cur_regs)[cur_reg];
@@ -158,7 +159,8 @@
       }
       if (loc->type == DWARF_LOCATION_EXPRESSION) {
         if (!regular_memory->ReadFully(value, reg_ptr, sizeof(AddressType))) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = value;
           return false;
         }
       } else {
@@ -183,14 +185,14 @@
                                          bool* finished) {
   RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
   if (cie->return_address_register >= cur_regs->total_regs()) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
 
   // Get the cfa value;
   auto cfa_entry = loc_regs.find(CFA_REG);
   if (cfa_entry == loc_regs.end()) {
-    last_error_ = DWARF_ERROR_CFA_NOT_DEFINED;
+    last_error_.code = DWARF_ERROR_CFA_NOT_DEFINED;
     return false;
   }
 
@@ -206,7 +208,7 @@
   switch (loc->type) {
     case DWARF_LOCATION_REGISTER:
       if (loc->values[0] >= cur_regs->total_regs()) {
-        last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+        last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
         return false;
       }
       // If the stack pointer register is the CFA, and the stack
@@ -227,7 +229,8 @@
       }
       if (loc->type == DWARF_LOCATION_EXPRESSION) {
         if (!regular_memory->ReadFully(value, &eval_info.cfa, sizeof(AddressType))) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = value;
           return false;
         }
       } else {
@@ -236,7 +239,7 @@
       break;
     }
     default:
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
   }
 
@@ -305,7 +308,8 @@
 bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
   uint32_t length32;
   if (!memory_.ReadBytes(&length32, sizeof(length32))) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
   // Set the default for the lsda encoding.
@@ -315,7 +319,8 @@
     // 64 bit Cie
     uint64_t length64;
     if (!memory_.ReadBytes(&length64, sizeof(length64))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
 
@@ -324,12 +329,13 @@
 
     uint64_t cie_id;
     if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     if (cie_id != cie64_value_) {
       // This is not a Cie, something has gone horribly wrong.
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
     }
   } else {
@@ -339,24 +345,26 @@
 
     uint32_t cie_id;
     if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     if (cie_id != cie32_value_) {
       // This is not a Cie, something has gone horribly wrong.
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
     }
   }
 
   if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
   if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
     // Unrecognized version.
-    last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+    last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
     return false;
   }
 
@@ -364,7 +372,8 @@
   char aug_value;
   do {
     if (!memory_.ReadBytes(&aug_value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     cie->augmentation_string.push_back(aug_value);
@@ -376,20 +385,23 @@
 
     // Segment Size
     if (!memory_.ReadBytes(&cie->segment_size, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
   }
 
   // Code Alignment Factor
   if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
   // Data Alignment Factor
   if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
@@ -397,12 +409,14 @@
     // Return Address is a single byte.
     uint8_t return_address_register;
     if (!memory_.ReadBytes(&return_address_register, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     cie->return_address_register = return_address_register;
   } else if (!memory_.ReadULEB128(&cie->return_address_register)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
@@ -413,7 +427,8 @@
 
   uint64_t aug_length;
   if (!memory_.ReadULEB128(&aug_length)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
   cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
@@ -422,24 +437,28 @@
     switch (cie->augmentation_string[i]) {
       case 'L':
         if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
           return false;
         }
         break;
       case 'P': {
         uint8_t encoding;
         if (!memory_.ReadBytes(&encoding, 1)) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
           return false;
         }
         if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
           return false;
         }
       } break;
       case 'R':
         if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
           return false;
         }
         break;
@@ -467,7 +486,8 @@
 bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
   uint32_t length32;
   if (!memory_.ReadBytes(&length32, sizeof(length32))) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
@@ -475,19 +495,21 @@
     // 64 bit Fde.
     uint64_t length64;
     if (!memory_.ReadBytes(&length64, sizeof(length64))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     fde->cfa_instructions_end = memory_.cur_offset() + length64;
 
     uint64_t value64;
     if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     if (value64 == cie64_value_) {
       // This is a Cie, this means something has gone wrong.
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
     }
 
@@ -500,12 +522,13 @@
 
     uint32_t value32;
     if (!memory_.ReadBytes(&value32, sizeof(value32))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     if (value32 == cie32_value_) {
       // This is a Cie, this means something has gone wrong.
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
     }
 
@@ -528,13 +551,15 @@
   memory_.set_cur_offset(cur_offset);
 
   if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
   fde->pc_start = AdjustPcFromFde(fde->pc_start);
 
   if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_end)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
   fde->pc_end += fde->pc_start;
@@ -542,13 +567,15 @@
     // Augmentation Size
     uint64_t aug_length;
     if (!memory_.ReadULEB128(&aug_length)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     uint64_t cur_offset = memory_.cur_offset();
 
     if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
 
@@ -619,7 +646,8 @@
 bool DwarfSectionImpl<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
   uint8_t version;
   if (!memory_.ReadBytes(&version, 1)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
   // Read the augmentation string.
@@ -628,7 +656,8 @@
   bool get_encoding = false;
   do {
     if (!memory_.ReadBytes(&aug_value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     if (aug_value == 'R') {
@@ -643,7 +672,8 @@
 
     // Read the segment size.
     if (!memory_.ReadBytes(segment_size, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
   } else {
@@ -659,7 +689,8 @@
   uint8_t value;
   do {
     if (!memory_.ReadBytes(&value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
   } while (value & 0x80);
@@ -667,7 +698,8 @@
   // Skip data alignment factor
   do {
     if (!memory_.ReadBytes(&value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
   } while (value & 0x80);
@@ -679,7 +711,8 @@
     // Skip return address register.
     do {
       if (!memory_.ReadBytes(&value, 1)) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.address = memory_.cur_offset();
         return false;
       }
     } while (value & 0x80);
@@ -688,7 +721,8 @@
   // Skip the augmentation length.
   do {
     if (!memory_.ReadBytes(&value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
   } while (value & 0x80);
@@ -696,7 +730,8 @@
   for (size_t i = 1; i < aug_string.size(); i++) {
     if (aug_string[i] == 'R') {
       if (!memory_.ReadBytes(encoding, 1)) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.address = memory_.cur_offset();
         return false;
       }
       // Got the encoding, that's all we are looking for.
@@ -706,12 +741,14 @@
     } else if (aug_string[i] == 'P') {
       uint8_t encoding;
       if (!memory_.ReadBytes(&encoding, 1)) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.address = memory_.cur_offset();
         return false;
       }
       uint64_t value;
       if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.address = memory_.cur_offset();
         return false;
       }
     }
@@ -730,14 +767,16 @@
 
   uint64_t start;
   if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
   start = AdjustPcFromFde(start);
 
   uint64_t length;
   if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
   if (length != 0) {
@@ -764,7 +803,8 @@
     // Figure out the entry length and type.
     uint32_t value32;
     if (!memory_.ReadBytes(&value32, sizeof(value32))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
 
@@ -772,14 +812,16 @@
     if (value32 == static_cast<uint32_t>(-1)) {
       uint64_t value64;
       if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.address = memory_.cur_offset();
         return false;
       }
       next_entry_offset = memory_.cur_offset() + value64;
 
       // Read the Cie Id of a Cie or the pointer of the Fde.
       if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.address = memory_.cur_offset();
         return false;
       }
 
@@ -794,7 +836,7 @@
         uint64_t last_cie_offset = GetCieOffsetFromFde64(value64);
         if (last_cie_offset != cie_offset) {
           // This means that this Fde is not following the Cie.
-          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
           return false;
         }
 
@@ -808,7 +850,8 @@
 
       // Read the Cie Id of a Cie or the pointer of the Fde.
       if (!memory_.ReadBytes(&value32, sizeof(value32))) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.address = memory_.cur_offset();
         return false;
       }
 
@@ -823,7 +866,7 @@
         uint64_t last_cie_offset = GetCieOffsetFromFde32(value32);
         if (last_cie_offset != cie_offset) {
           // This means that this Fde is not following the Cie.
-          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
           return false;
         }
 
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 220e549..f120da2 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -134,6 +134,26 @@
   return true;
 }
 
+void Elf::GetLastError(ErrorData* data) {
+  if (valid_) {
+    *data = interface_->last_error();
+  }
+}
+
+ErrorCode Elf::GetLastErrorCode() {
+  if (valid_) {
+    return interface_->LastErrorCode();
+  }
+  return ERROR_NONE;
+}
+
+uint64_t Elf::GetLastErrorAddress() {
+  if (valid_) {
+    return interface_->LastErrorAddress();
+  }
+  return 0;
+}
+
 // The relative pc is always relative to the start of the map from which it comes.
 bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
                Memory* process_memory, bool* finished) {
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 17cc16a..e413081 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -24,6 +24,7 @@
 #include <Xz.h>
 #include <XzCrc64.h>
 
+#include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfSection.h>
 #include <unwindstack/ElfInterface.h>
 #include <unwindstack/Log.h>
@@ -160,6 +161,8 @@
 bool ElfInterface::ReadAllHeaders(uint64_t* load_bias) {
   EhdrType ehdr;
   if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) {
+    last_error_.code = ERROR_MEMORY_INVALID;
+    last_error_.address = 0;
     return false;
   }
 
@@ -201,6 +204,9 @@
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
     PhdrType phdr;
     if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
+      last_error_.code = ERROR_MEMORY_INVALID;
+      last_error_.address =
+          offset + reinterpret_cast<uintptr_t>(&phdr.p_type) - reinterpret_cast<uintptr_t>(&phdr);
       return false;
     }
 
@@ -213,6 +219,9 @@
     {
       // Get the flags first, if this isn't an executable header, ignore it.
       if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_flags) -
+                              reinterpret_cast<uintptr_t>(&phdr);
         return false;
       }
       if ((phdr.p_flags & PF_X) == 0) {
@@ -220,12 +229,21 @@
       }
 
       if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_vaddr) -
+                              reinterpret_cast<uintptr_t>(&phdr);
         return false;
       }
       if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
+                              reinterpret_cast<uintptr_t>(&phdr);
         return false;
       }
       if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
+                              reinterpret_cast<uintptr_t>(&phdr);
         return false;
       }
       pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
@@ -238,11 +256,17 @@
 
     case PT_GNU_EH_FRAME:
       if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
+                              reinterpret_cast<uintptr_t>(&phdr);
         return false;
       }
       // This is really the pointer to the .eh_frame_hdr section.
       eh_frame_hdr_offset_ = phdr.p_offset;
       if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
+                              reinterpret_cast<uintptr_t>(&phdr);
         return false;
       }
       eh_frame_hdr_size_ = phdr.p_memsz;
@@ -250,14 +274,23 @@
 
     case PT_DYNAMIC:
       if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
+                              reinterpret_cast<uintptr_t>(&phdr);
         return false;
       }
       dynamic_offset_ = phdr.p_offset;
       if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_vaddr) -
+                              reinterpret_cast<uintptr_t>(&phdr);
         return false;
       }
       dynamic_vaddr_ = phdr.p_vaddr;
       if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
+                              reinterpret_cast<uintptr_t>(&phdr);
         return false;
       }
       dynamic_size_ = phdr.p_memsz;
@@ -290,31 +323,47 @@
   offset += ehdr.e_shentsize;
   for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
     if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
+      last_error_.code = ERROR_MEMORY_INVALID;
+      last_error_.address =
+          offset + reinterpret_cast<uintptr_t>(&shdr.sh_type) - reinterpret_cast<uintptr_t>(&shdr);
       return false;
     }
 
     if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
       if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = offset;
         return false;
       }
       // Need to go get the information about the section that contains
       // the string terminated names.
       ShdrType str_shdr;
       if (shdr.sh_link >= ehdr.e_shnum) {
+        last_error_.code = ERROR_UNWIND_INFO;
         return false;
       }
       uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
       if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_type, sizeof(str_shdr.sh_type))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = str_offset + reinterpret_cast<uintptr_t>(&str_shdr.sh_type) -
+                              reinterpret_cast<uintptr_t>(&str_shdr);
         return false;
       }
       if (str_shdr.sh_type != SHT_STRTAB) {
+        last_error_.code = ERROR_UNWIND_INFO;
         return false;
       }
       if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_offset,
                               sizeof(str_shdr.sh_offset))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = str_offset + reinterpret_cast<uintptr_t>(&str_shdr.sh_offset) -
+                              reinterpret_cast<uintptr_t>(&str_shdr);
         return false;
       }
       if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_size, sizeof(str_shdr.sh_size))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = str_offset + reinterpret_cast<uintptr_t>(&str_shdr.sh_size) -
+                              reinterpret_cast<uintptr_t>(&str_shdr);
         return false;
       }
       symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
@@ -322,6 +371,9 @@
     } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) {
       // Look for the .debug_frame and .gnu_debugdata.
       if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = offset + reinterpret_cast<uintptr_t>(&shdr.sh_name) -
+                              reinterpret_cast<uintptr_t>(&shdr);
         return false;
       }
       if (shdr.sh_name < sec_size) {
@@ -377,6 +429,8 @@
   uint64_t max_offset = offset + dynamic_size_;
   for (uint64_t offset = dynamic_offset_; offset < max_offset; offset += sizeof(DynType)) {
     if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
+      last_error_.code = ERROR_MEMORY_INVALID;
+      last_error_.address = offset;
       return false;
     }
 
@@ -434,8 +488,12 @@
 
 bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
                         bool* finished) {
+  last_error_.code = ERROR_NONE;
+  last_error_.address = 0;
+
   // Adjust the load bias to get the real relative pc.
   if (pc < load_bias) {
+    last_error_.code = ERROR_UNWIND_INFO;
     return false;
   }
   uint64_t adjusted_pc = pc - load_bias;
@@ -458,6 +516,46 @@
       gnu_debugdata_interface_->Step(pc, 0, regs, process_memory, finished)) {
     return true;
   }
+
+  // Set the error code based on the first error encountered.
+  DwarfSection* section = nullptr;
+  if (debug_frame_ != nullptr) {
+    section = debug_frame_.get();
+  } else if (eh_frame_ != nullptr) {
+    section = eh_frame_.get();
+  } else if (gnu_debugdata_interface_ != nullptr) {
+    last_error_ = gnu_debugdata_interface_->last_error();
+    return false;
+  } else {
+    return false;
+  }
+
+  // Convert the DWARF ERROR to an external error.
+  DwarfErrorCode code = section->LastErrorCode();
+  switch (code) {
+    case DWARF_ERROR_NONE:
+      last_error_.code = ERROR_NONE;
+      break;
+
+    case DWARF_ERROR_MEMORY_INVALID:
+      last_error_.code = ERROR_MEMORY_INVALID;
+      last_error_.address = section->LastErrorAddress();
+      break;
+
+    case DWARF_ERROR_ILLEGAL_VALUE:
+    case DWARF_ERROR_ILLEGAL_STATE:
+    case DWARF_ERROR_STACK_INDEX_NOT_VALID:
+    case DWARF_ERROR_TOO_MANY_ITERATIONS:
+    case DWARF_ERROR_CFA_NOT_DEFINED:
+    case DWARF_ERROR_NO_FDES:
+      last_error_.code = ERROR_UNWIND_INFO;
+      break;
+
+    case DWARF_ERROR_NOT_IMPLEMENTED:
+    case DWARF_ERROR_UNSUPPORTED_VERSION:
+      last_error_.code = ERROR_UNSUPPORTED;
+      break;
+  }
   return false;
 }
 
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index 5d99bd7..616d1b1 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -28,6 +28,7 @@
 
 bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
   if (start_offset_ == 0 || total_entries_ == 0) {
+    last_error_.code = ERROR_UNWIND_INFO;
     return false;
   }
 
@@ -56,12 +57,15 @@
     *entry_offset = start_offset_ + (last - 1) * 8;
     return true;
   }
+  last_error_.code = ERROR_UNWIND_INFO;
   return false;
 }
 
 bool ElfInterfaceArm::GetPrel31Addr(uint32_t offset, uint32_t* addr) {
   uint32_t data;
   if (!memory_->Read32(offset, &data)) {
+    last_error_.code = ERROR_MEMORY_INVALID;
+    last_error_.address = offset;
     return false;
   }
 
@@ -106,6 +110,7 @@
                                 bool* finished) {
   // Adjust the load bias to get the real relative pc.
   if (pc < load_bias) {
+    last_error_.code = ERROR_UNWIND_INFO;
     return false;
   }
   pc -= load_bias;
@@ -139,6 +144,30 @@
     *finished = true;
     return true;
   }
+
+  if (!return_value) {
+    switch (arm.status()) {
+      case ARM_STATUS_NONE:
+      case ARM_STATUS_NO_UNWIND:
+      case ARM_STATUS_FINISH:
+        last_error_.code = ERROR_NONE;
+        break;
+
+      case ARM_STATUS_RESERVED:
+      case ARM_STATUS_SPARE:
+      case ARM_STATUS_TRUNCATED:
+      case ARM_STATUS_MALFORMED:
+      case ARM_STATUS_INVALID_ALIGNMENT:
+      case ARM_STATUS_INVALID_PERSONALITY:
+        last_error_.code = ERROR_UNWIND_INFO;
+        break;
+
+      case ARM_STATUS_READ_FAILED:
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = arm.status_address();
+        break;
+    }
+  }
   return return_value;
 }
 
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index d711772..f70ed7b 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -78,6 +78,8 @@
 void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
                       const std::vector<std::string>* map_suffixes_to_ignore) {
   frames_.clear();
+  last_error_.code = ERROR_NONE;
+  last_error_.address = 0;
 
   bool return_address_attempt = false;
   bool adjust_pc = false;
@@ -95,6 +97,7 @@
       rel_pc = regs_->pc();
       adjusted_rel_pc = rel_pc;
       adjusted_pc = rel_pc;
+      last_error_.code = ERROR_INVALID_MAP;
     } else {
       if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
         break;
@@ -155,6 +158,7 @@
           bool finished;
           stepped = elf->Step(rel_pc, adjusted_pc, map_info->elf_offset, regs_,
                               process_memory_.get(), &finished);
+          elf->GetLastError(&last_error_);
           if (stepped && finished) {
             break;
           }
@@ -180,10 +184,14 @@
       }
     } else {
       return_address_attempt = false;
+      if (max_frames_ == frames_.size()) {
+        last_error_.code = ERROR_MAX_FRAMES_EXCEEDED;
+      }
     }
 
     // If the pc and sp didn't change, then consider everything stopped.
     if (cur_pc == regs_->pc() && cur_sp == regs_->sp()) {
+      last_error_.code = ERROR_REPEATED_FRAME;
       break;
     }
   }
diff --git a/libunwindstack/include/unwindstack/DwarfError.h b/libunwindstack/include/unwindstack/DwarfError.h
new file mode 100644
index 0000000..763e2cb
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfError.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_ERROR_H
+#define _LIBUNWINDSTACK_DWARF_ERROR_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum DwarfErrorCode : uint8_t {
+  DWARF_ERROR_NONE,
+  DWARF_ERROR_MEMORY_INVALID,
+  DWARF_ERROR_ILLEGAL_VALUE,
+  DWARF_ERROR_ILLEGAL_STATE,
+  DWARF_ERROR_STACK_INDEX_NOT_VALID,
+  DWARF_ERROR_NOT_IMPLEMENTED,
+  DWARF_ERROR_TOO_MANY_ITERATIONS,
+  DWARF_ERROR_CFA_NOT_DEFINED,
+  DWARF_ERROR_UNSUPPORTED_VERSION,
+  DWARF_ERROR_NO_FDES,
+};
+
+struct DwarfErrorData {
+  DwarfErrorCode code;
+  uint64_t address;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index e0004aa..03f40d6 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -22,6 +22,7 @@
 #include <iterator>
 #include <unordered_map>
 
+#include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfLocation.h>
 #include <unwindstack/DwarfMemory.h>
 #include <unwindstack/DwarfStructs.h>
@@ -29,7 +30,6 @@
 namespace unwindstack {
 
 // Forward declarations.
-enum DwarfError : uint8_t;
 class Memory;
 class Regs;
 
@@ -72,7 +72,8 @@
   iterator begin() { return iterator(this, 0); }
   iterator end() { return iterator(this, fde_count_); }
 
-  DwarfError last_error() { return last_error_; }
+  DwarfErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
 
   virtual bool Init(uint64_t offset, uint64_t size) = 0;
 
@@ -100,7 +101,7 @@
 
  protected:
   DwarfMemory memory_;
-  DwarfError last_error_;
+  DwarfErrorData last_error_{DWARF_ERROR_NONE, 0};
 
   uint32_t cie32_value_ = 0;
   uint64_t cie64_value_ = 0;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 5f34391..e562d6d 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -72,6 +72,10 @@
 
   bool IsValidPc(uint64_t pc);
 
+  void GetLastError(ErrorData* data);
+  ErrorCode GetLastErrorCode();
+  uint64_t GetLastErrorAddress();
+
   bool valid() { return valid_; }
 
   uint32_t machine_type() { return machine_type_; }
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index faa61ee..ea9ec9d 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -26,6 +26,7 @@
 #include <vector>
 
 #include <unwindstack/DwarfSection.h>
+#include <unwindstack/Error.h>
 
 namespace unwindstack {
 
@@ -90,6 +91,10 @@
   DwarfSection* eh_frame() { return eh_frame_.get(); }
   DwarfSection* debug_frame() { return debug_frame_.get(); }
 
+  const ErrorData& last_error() { return last_error_; }
+  ErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
+
   template <typename EhdrType, typename PhdrType>
   static uint64_t GetLoadBias(Memory* memory);
 
@@ -144,6 +149,8 @@
   uint8_t soname_type_ = SONAME_UNKNOWN;
   std::string soname_;
 
+  ErrorData last_error_{ERROR_NONE, 0};
+
   std::unique_ptr<DwarfSection> eh_frame_;
   std::unique_ptr<DwarfSection> debug_frame_;
   // The Elf object owns the gnu_debugdata interface object.
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
new file mode 100644
index 0000000..6ed0e0f
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_ERROR_H
+#define _LIBUNWINDSTACK_ERROR_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum ErrorCode : uint8_t {
+  ERROR_NONE,                 // No error.
+  ERROR_MEMORY_INVALID,       // Memory read failed.
+  ERROR_UNWIND_INFO,          // Unable to use unwind information to unwind.
+  ERROR_UNSUPPORTED,          // Encountered unsupported feature.
+  ERROR_INVALID_MAP,          // Unwind in an invalid map.
+  ERROR_MAX_FRAMES_EXCEEDED,  // The number of frames exceed the total allowed.
+  ERROR_REPEATED_FRAME,       // The last frame has the same pc/sp as the next.
+};
+
+struct ErrorData {
+  ErrorCode code;
+  uint64_t address;  // Only valid when code is ERROR_MEMORY_INVALID.
+                     // Indicates the failing address.
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_ERROR_H
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 32858ae..a7b57e6 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -24,6 +24,7 @@
 #include <string>
 #include <vector>
 
+#include <unwindstack/Error.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
@@ -74,6 +75,9 @@
 
   void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
 
+  ErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
+
  private:
   void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t adjusted_pc);
 
@@ -83,6 +87,7 @@
   std::vector<FrameData> frames_;
   std::shared_ptr<Memory> process_memory_;
   JitDebug* jit_debug_ = nullptr;
+  ErrorData last_error_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index caad131..8d0f0e5 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -257,22 +257,27 @@
 TEST_F(ArmExidxExtractTest, read_failures) {
   ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+  EXPECT_EQ(0x5004U, exidx_->status_address());
 
   elf_memory_.SetData32(0x5000, 0x100);
   ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+  EXPECT_EQ(0x5004U, exidx_->status_address());
 
   elf_memory_.SetData32(0x5004, 0x100);
   ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+  EXPECT_EQ(0x5104U, exidx_->status_address());
 
   elf_memory_.SetData32(0x5104, 0x1);
   ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+  EXPECT_EQ(0x5108U, exidx_->status_address());
 
   elf_memory_.SetData32(0x5108, 0x01010203);
   ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+  EXPECT_EQ(0x510cU, exidx_->status_address());
 }
 
 TEST_F(ArmExidxExtractTest, malformed) {
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 73a67ac..68dc30c 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -21,13 +21,13 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfLocation.h>
 #include <unwindstack/DwarfMemory.h>
 #include <unwindstack/DwarfStructs.h>
 #include <unwindstack/Log.h>
 
 #include "DwarfCfa.h"
-#include "DwarfError.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
@@ -78,7 +78,7 @@
     dwarf_loc_regs_t loc_regs;
 
     ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
-    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->last_error());
+    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->LastErrorCode());
     ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
 
     ASSERT_EQ("", GetFakeLogPrint());
@@ -198,7 +198,7 @@
   dwarf_loc_regs_t loc_regs;
 
   ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
   ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
   ASSERT_EQ(0U, loc_regs.size());
 
@@ -227,7 +227,7 @@
   dwarf_loc_regs_t loc_regs;
 
   ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4000, 0x4002, &loc_regs));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
   ASSERT_EQ(0x4002U, this->dmem_->cur_offset());
   ASSERT_EQ(0U, loc_regs.size());
 
@@ -594,7 +594,7 @@
   // This fails because the cfa is not defined as a register.
   ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
   ASSERT_EQ(0U, loc_regs.size());
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
 
   ASSERT_EQ("4 unwind Attempt to set new register, but cfa is not already set to a register.\n",
             GetFakeLogPrint());
@@ -637,7 +637,7 @@
   // This fails because the cfa is not defined as a register.
   ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
   ASSERT_EQ(0U, loc_regs.size());
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
 
   ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
             GetFakeLogPrint());
@@ -679,7 +679,7 @@
 
   // This fails because the cfa is not defined as a register.
   ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
 
   ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
             GetFakeLogPrint());
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 243198e..c28a41e 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -19,9 +19,10 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfError.h>
+
 #include "DwarfDebugFrame.h"
 #include "DwarfEncoding.h"
-#include "DwarfError.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
@@ -142,7 +143,7 @@
   this->memory_.SetData32(0x510c, 0x200);
 
   ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, Init32_do_not_fail_on_bad_next_entry) {
@@ -267,7 +268,7 @@
   this->memory_.SetData64(0x511c, 0x200);
 
   ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, Init64_do_not_fail_on_bad_next_entry) {
@@ -404,11 +405,11 @@
   this->debug_frame_->TestSetFdeCount(0);
   uint64_t fde_offset;
   ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
 
   this->debug_frame_->TestSetFdeCount(9);
   ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   // Odd number of elements.
   for (size_t i = 0; i < 9; i++) {
     TypeParam pc = 0x1000 * (i + 1);
@@ -422,7 +423,7 @@
     EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
     ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
         << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   }
 
   // Even number of elements.
@@ -444,7 +445,7 @@
     EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
     ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
         << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
   }
 }
 
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 3a629f8..a73db65 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -19,9 +19,10 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfError.h>
+
 #include "DwarfEhFrame.h"
 #include "DwarfEncoding.h"
-#include "DwarfError.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
@@ -142,7 +143,7 @@
   this->memory_.SetData32(0x510c, 0x200);
 
   ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfEhFrameTest, Init64) {
@@ -228,7 +229,7 @@
   this->memory_.SetData64(0x511c, 0x200);
 
   ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfEhFrameTest, Init_version1) {
@@ -320,11 +321,11 @@
   this->eh_frame_->TestSetFdeCount(0);
   uint64_t fde_offset;
   ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
 
   this->eh_frame_->TestSetFdeCount(9);
   ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
   // Odd number of elements.
   for (size_t i = 0; i < 9; i++) {
     TypeParam pc = 0x1000 * (i + 1);
@@ -337,7 +338,7 @@
     EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
     ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
         << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
   }
 
   // Even number of elements.
@@ -358,7 +359,7 @@
     EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
     ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
         << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
   }
 }
 
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index ef2fb32..a2ae5eb 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -19,9 +19,10 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfError.h>
+
 #include "DwarfEhFrameWithHdr.h"
 #include "DwarfEncoding.h"
-#include "DwarfError.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
@@ -97,25 +98,29 @@
   // Verify a zero fde count fails to init.
   this->memory_.SetData32(0x1006, 0);
   ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
-  ASSERT_EQ(DWARF_ERROR_NO_FDES, this->eh_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_NO_FDES, this->eh_frame_->LastErrorCode());
 
   // Verify an unexpected version will cause a fail.
   this->memory_.SetData32(0x1006, 126);
   this->memory_.SetData8(0x1000, 0);
   ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
-  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
   this->memory_.SetData8(0x1000, 2);
   ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
-  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_expect_cache_fail) {
   this->eh_frame_->TestSetTableEntrySize(0x10);
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+
   ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->LastErrorCode());
+  EXPECT_EQ(0x1000U, this->eh_frame_->LastErrorAddress());
   ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->LastErrorCode());
+  EXPECT_EQ(0x1000U, this->eh_frame_->LastErrorAddress());
 }
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_pcrel) {
@@ -184,7 +189,7 @@
   uint64_t fde_offset;
   EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
   // Not an error, just not found.
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
   // Even number of elements.
   for (size_t i = 0; i < 10; i++) {
     TypeParam pc = 0x1000 * (i + 1);
@@ -280,7 +285,7 @@
 
   uint64_t fde_offset;
   ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_fail_fde_count) {
@@ -288,7 +293,7 @@
 
   uint64_t fde_offset;
   ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_binary_search) {
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
index 234d1c9..3f09dd8 100644
--- a/libunwindstack/tests/DwarfOpLogTest.cpp
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -21,11 +21,11 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfMemory.h>
 #include <unwindstack/Log.h>
 #include <unwindstack/Regs.h>
 
-#include "DwarfError.h"
 #include "DwarfOp.h"
 
 #include "MemoryFake.h"
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 2d5007b..036226d 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -21,10 +21,10 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfMemory.h>
 #include <unwindstack/Log.h>
 
-#include "DwarfError.h"
 #include "DwarfOp.h"
 
 #include "MemoryFake.h"
@@ -53,13 +53,14 @@
 TYPED_TEST_P(DwarfOpTest, decode) {
   // Memory error.
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
+  EXPECT_EQ(0U, this->op_->LastErrorAddress());
 
   // No error.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
   this->mem_->set_cur_offset(0);
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->LastErrorCode());
   ASSERT_EQ(0x96U, this->op_->cur_op());
   ASSERT_EQ(1U, this->mem_->cur_offset());
 }
@@ -67,7 +68,8 @@
 TYPED_TEST_P(DwarfOpTest, eval) {
   // Memory error.
   ASSERT_FALSE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
+  EXPECT_EQ(0U, this->op_->LastErrorAddress());
 
   // Register set.
   // Do this first, to verify that subsequent calls reset the value.
@@ -84,7 +86,7 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_TRUE(this->op_->Eval(0, 8, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->LastErrorCode());
   ASSERT_FALSE(this->op_->is_register());
   ASSERT_EQ(8U, this->mem_->cur_offset());
   ASSERT_EQ(4U, this->op_->StackSize());
@@ -96,7 +98,7 @@
   // Infinite loop.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x2f, 0xfd, 0xff});
   ASSERT_FALSE(this->op_->Eval(0, 4, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->LastErrorCode());
   ASSERT_FALSE(this->op_->is_register());
   ASSERT_EQ(0U, this->op_->StackSize());
 }
@@ -111,7 +113,7 @@
 
   for (size_t i = 0; i < opcode_buffer.size(); i++) {
     ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
     ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
   }
 }
@@ -122,7 +124,7 @@
 
   for (size_t i = 0; i < opcode_buffer.size(); i++) {
     ASSERT_FALSE(this->op_->Decode(2));
-    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
     ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
   }
 }
@@ -133,7 +135,7 @@
 
   for (size_t i = 0; i < opcode_buffer.size(); i++) {
     ASSERT_FALSE(this->op_->Decode(3));
-    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
     ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
   }
 }
@@ -178,7 +180,7 @@
 
   while (this->mem_->cur_offset() < opcode_buffer.size()) {
     ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-    ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->last_error());
+    ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->LastErrorCode());
   }
 }
 
@@ -216,7 +218,7 @@
   this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
@@ -226,7 +228,8 @@
   ASSERT_EQ(value, this->op_->StackAt(0));
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
+  ASSERT_EQ(0x12345678U, this->op_->LastErrorAddress());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_deref_size) {
@@ -235,7 +238,7 @@
   this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   // Read all byte sizes up to the sizeof the type.
   for (size_t i = 1; i < sizeof(TypeParam); i++) {
@@ -252,17 +255,18 @@
   // Zero byte read.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, 0x00});
   ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 
   // Read too many bytes.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, sizeof(TypeParam) + 1});
   ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 
   // Force bad memory read.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x40, 0x94, 0x01});
   ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
+  EXPECT_EQ(0x4010U, this->op_->LastErrorAddress());
 }
 
 TYPED_TEST_P(DwarfOpTest, const_unsigned) {
@@ -529,7 +533,7 @@
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(0x12, this->op_->cur_op());
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
@@ -577,7 +581,7 @@
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(0x13, this->op_->cur_op());
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_over) {
@@ -612,7 +616,7 @@
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(0x14, this->op_->cur_op());
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_pick) {
@@ -654,7 +658,7 @@
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(0x15, this->op_->cur_op());
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_swap) {
@@ -686,7 +690,7 @@
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(0x16, this->op_->cur_op());
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_rot) {
@@ -703,19 +707,19 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(2U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(3U, this->op_->StackSize());
@@ -753,7 +757,7 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
@@ -805,13 +809,13 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   // Two positive values.
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
@@ -854,7 +858,7 @@
   ASSERT_EQ(5U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_div) {
@@ -871,13 +875,13 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(2U, this->op_->StackSize());
@@ -902,13 +906,13 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(2U, this->op_->StackSize());
@@ -935,13 +939,13 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(2U, this->op_->StackSize());
@@ -957,7 +961,7 @@
   ASSERT_EQ(3U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_mul) {
@@ -974,13 +978,13 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(2U, this->op_->StackSize());
@@ -1003,7 +1007,7 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
@@ -1034,7 +1038,7 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
@@ -1067,13 +1071,13 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(2U, this->op_->StackSize());
@@ -1098,13 +1102,13 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(2U, this->op_->StackSize());
@@ -1125,7 +1129,7 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
@@ -1150,13 +1154,13 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(2U, this->op_->StackSize());
@@ -1181,13 +1185,13 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(2U, this->op_->StackSize());
@@ -1216,13 +1220,13 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(2U, this->op_->StackSize());
@@ -1247,13 +1251,13 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(1U, this->op_->StackSize());
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
   ASSERT_EQ(2U, this->op_->StackSize());
@@ -1280,7 +1284,7 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   // Push on a non-zero value with a positive branch.
   ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
@@ -1342,12 +1346,12 @@
 
     ASSERT_FALSE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
     ASSERT_EQ(opcode, this->op_->cur_op());
-    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
     ASSERT_FALSE(this->op_->Eval(1, 4, DWARF_VERSION_MAX));
     ASSERT_EQ(opcode, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize());
-    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
   }
 }
 
@@ -1532,7 +1536,7 @@
 
   // Should fail since this references a non-existent register.
   ASSERT_FALSE(this->op_->Eval(2, 4, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_bregx) {
@@ -1560,7 +1564,7 @@
   ASSERT_EQ(0x90U, this->op_->StackAt(0));
 
   ASSERT_FALSE(this->op_->Eval(7, 12, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_nop) {
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index dfd2ce0..7e10935 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -19,10 +19,10 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfSection.h>
 
 #include "DwarfEncoding.h"
-#include "DwarfError.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
@@ -67,7 +67,7 @@
   }
   void TestClearCachedCieLocRegs() { this->cie_loc_regs_.clear(); }
 
-  void TestClearError() { this->last_error_ = DWARF_ERROR_NONE; }
+  void TestClearError() { this->last_error_.code = DWARF_ERROR_NONE; }
 };
 
 template <typename TypeParam>
@@ -102,7 +102,8 @@
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x5000U, this->section_->LastErrorAddress());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
@@ -118,7 +119,7 @@
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
@@ -172,7 +173,7 @@
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
-  EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
@@ -182,7 +183,7 @@
 
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
@@ -192,7 +193,7 @@
 
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
-  EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
@@ -203,25 +204,25 @@
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 
   this->section_->TestClearError();
   loc_regs.erase(CFA_REG);
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 
   this->section_->TestClearError();
   loc_regs.erase(CFA_REG);
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 
   this->section_->TestClearError();
   loc_regs.erase(CFA_REG);
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
@@ -341,7 +342,7 @@
   loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {10, 0}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
@@ -489,10 +490,12 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, GetCie_fail_should_not_cache) {
   ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
   this->section_->TestClearError();
   ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, GetCie_32_version_check) {
@@ -518,24 +521,24 @@
   EXPECT_EQ(4U, cie->code_alignment_factor);
   EXPECT_EQ(8, cie->data_alignment_factor);
   EXPECT_EQ(0x20U, cie->return_address_register);
-  EXPECT_EQ(DWARF_ERROR_NONE, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_NONE, this->section_->LastErrorCode());
 
   this->section_->TestClearCachedCieEntry();
   // Set version to 0, 2, 5 and verify we fail.
   this->memory_.SetData8(0x5008, 0x0);
   this->section_->TestClearError();
   ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->LastErrorCode());
 
   this->memory_.SetData8(0x5008, 0x2);
   this->section_->TestClearError();
   ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->LastErrorCode());
 
   this->memory_.SetData8(0x5008, 0x5);
   this->section_->TestClearError();
   ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, GetCie_negative_data_alignment_factor) {
@@ -681,10 +684,12 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
   ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
   this->section_->TestClearError();
   ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment) {
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index 099026c..e232986 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -87,6 +87,10 @@
     steps_.clear();
   }
 
+  void FakeSetErrorCode(ErrorCode code) { last_error_.code = code; }
+
+  void FakeSetErrorAddress(uint64_t address) { last_error_.address = address; }
+
  private:
   std::unordered_map<std::string, uint64_t> globals_;
 
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index e6763ab..31d6a63 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -303,6 +303,7 @@
   // FindEntry fails.
   bool finished;
   ASSERT_FALSE(interface.StepExidx(0x7000, 0, nullptr, nullptr, &finished));
+  EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // ExtractEntry should fail.
   interface.FakeSetStartOffset(0x1000);
@@ -316,14 +317,18 @@
   regs.set_sp(regs[ARM_REG_SP]);
   regs.set_pc(0x1234);
   ASSERT_FALSE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_MEMORY_INVALID, interface.LastErrorCode());
+  EXPECT_EQ(0x1004U, interface.LastErrorAddress());
 
   // Eval should fail.
   memory_.SetData32(0x1004, 0x81000000);
   ASSERT_FALSE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // Everything should pass.
   memory_.SetData32(0x1004, 0x80b0b0b0);
   ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
   ASSERT_FALSE(finished);
   ASSERT_EQ(0x1000U, regs.sp());
   ASSERT_EQ(0x1000U, regs[ARM_REG_SP]);
@@ -332,9 +337,11 @@
 
   // Load bias is non-zero.
   ASSERT_TRUE(interface.StepExidx(0x8000, 0x1000, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // Pc too small.
   ASSERT_FALSE(interface.StepExidx(0x8000, 0x9000, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 }
 
 TEST_F(ElfInterfaceArmTest, StepExidx_pc_set) {
@@ -356,6 +363,7 @@
   // Everything should pass.
   bool finished;
   ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_FALSE(finished);
   ASSERT_EQ(0x10004U, regs.sp());
   ASSERT_EQ(0x10004U, regs[ARM_REG_SP]);
@@ -379,6 +387,7 @@
 
   bool finished;
   ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0x10000U, regs.sp());
   ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
@@ -401,6 +410,7 @@
 
   bool finished;
   ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0x10000U, regs.sp());
   ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
@@ -427,6 +437,7 @@
 
   bool finished;
   ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0U, regs.pc());
 
@@ -439,6 +450,7 @@
   regs.set_pc(0x1234);
 
   ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0U, regs.pc());
 }
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 7e6a62a..eb85033 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -581,4 +581,30 @@
   EXPECT_TRUE(elf.IsValidPc(0x1500));
 }
 
+TEST_F(ElfTest, error_code_not_valid) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(false);
+
+  ErrorData error{ERROR_MEMORY_INVALID, 0x100};
+  elf.GetLastError(&error);
+  EXPECT_EQ(ERROR_MEMORY_INVALID, error.code);
+  EXPECT_EQ(0x100U, error.address);
+}
+
+TEST_F(ElfTest, error_code_valid) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  ElfInterfaceFake* interface = new ElfInterfaceFake(memory_);
+  elf.FakeSetInterface(interface);
+  interface->FakeSetErrorCode(ERROR_MEMORY_INVALID);
+  interface->FakeSetErrorAddress(0x1000);
+
+  ErrorData error{ERROR_NONE, 0};
+  elf.GetLastError(&error);
+  EXPECT_EQ(ERROR_MEMORY_INVALID, error.code);
+  EXPECT_EQ(0x1000U, error.address);
+  EXPECT_EQ(ERROR_MEMORY_INVALID, elf.GetLastErrorCode());
+  EXPECT_EQ(0x1000U, elf.GetLastErrorAddress());
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index cd46807..bf2c1d8 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -129,6 +129,7 @@
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -184,6 +185,7 @@
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -218,6 +220,7 @@
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -248,6 +251,7 @@
 
   Unwinder unwinder(20, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
+  EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
 
   ASSERT_EQ(20U, unwinder.NumFrames());
 
@@ -288,6 +292,7 @@
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
   unwinder.Unwind(&skip_libs);
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -346,6 +351,7 @@
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
 
@@ -392,6 +398,7 @@
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 }
@@ -410,6 +417,7 @@
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 }
@@ -423,6 +431,7 @@
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
+  EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -457,6 +466,7 @@
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
   ASSERT_EQ(3U, unwinder.NumFrames());
 
@@ -517,6 +527,7 @@
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
@@ -552,6 +563,7 @@
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   std::vector<std::string> suffixes{"oat"};
   unwinder.Unwind(nullptr, &suffixes);
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
   // Make sure the elf was not initialized.
@@ -607,6 +619,7 @@
 
   Unwinder unwinder(64, &maps_, &regs_, process_memory_);
   unwinder.Unwind();
+  EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
 
   ASSERT_EQ(3U, unwinder.NumFrames());