Merge "Add tracing tags to vibrator"
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index 0a8a85a..6f5396a 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -124,9 +124,6 @@
  ** for each JDWP process, we record its pid and its connected socket
  **/
 
-// PIDs are transmitted as 4 hex digits in ascii.
-static constexpr size_t PID_LEN = 4;
-
 static void jdwp_process_event(int socket, unsigned events, void* _proc);
 static void jdwp_process_list_updated(void);
 
@@ -174,7 +171,7 @@
         _jdwp_list.remove_if(pred);
     }
 
-    int pid = -1;
+    int32_t pid = -1;
     int socket = -1;
     fdevent* fde = nullptr;
 
@@ -221,17 +218,9 @@
 
     if (events & FDE_READ) {
         if (proc->pid < 0) {
-            /* read the PID as a 4-hexchar string */
-            char buf[PID_LEN + 1];
-            ssize_t rc = TEMP_FAILURE_RETRY(recv(socket, buf, PID_LEN, 0));
-            if (rc != PID_LEN) {
-                D("failed to read jdwp pid: %s", strerror(errno));
-                goto CloseProcess;
-            }
-            buf[PID_LEN] = '\0';
-
-            if (sscanf(buf, "%04x", &proc->pid) != 1) {
-                D("could not decode JDWP %p PID number: '%s'", proc, buf);
+            ssize_t rc = TEMP_FAILURE_RETRY(recv(socket, &proc->pid, sizeof(proc->pid), 0));
+            if (rc != sizeof(proc->pid)) {
+                D("failed to read jdwp pid: rc = %zd, errno = %s", rc, strerror(errno));
                 goto CloseProcess;
             }
 
@@ -453,7 +442,7 @@
  **/
 
 struct JdwpSocket : public asocket {
-    bool pass;
+    bool pass = false;
 };
 
 static void jdwp_socket_close(asocket* s) {
@@ -467,7 +456,7 @@
     }
 
     remove_socket(s);
-    free(s);
+    delete s;
 }
 
 static int jdwp_socket_enqueue(asocket* s, std::string) {
@@ -497,7 +486,7 @@
 }
 
 asocket* create_jdwp_service_socket(void) {
-    JdwpSocket* s = reinterpret_cast<JdwpSocket*>(calloc(sizeof(*s), 1));
+    JdwpSocket* s = new JdwpSocket();
 
     if (!s) {
         fatal("failed to allocate JdwpSocket");
diff --git a/adb/services.cpp b/adb/services.cpp
index 6dc71cf..fe74eb6 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -296,6 +296,7 @@
         void* arg = strdup(name + 7);
         if (arg == NULL) return -1;
         ret = create_service_thread("reboot", reboot_service, arg);
+        if (ret < 0) free(arg);
     } else if(!strncmp(name, "root:", 5)) {
         ret = create_service_thread("root", restart_root_service, nullptr);
     } else if(!strncmp(name, "unroot:", 7)) {
diff --git a/adb/socket.h b/adb/socket.h
index a1b52b3..2f09080 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -36,31 +36,31 @@
 struct asocket {
     /* the unique identifier for this asocket
      */
-    unsigned id;
+    unsigned id = 0;
 
     /* flag: set when the socket's peer has closed
      * but packets are still queued for delivery
      */
-    int closing;
+    int closing = 0;
 
     // flag: set when the socket failed to write, so the socket will not wait to
     // write packets and close directly.
-    bool has_write_error;
+    bool has_write_error = 0;
 
     /* flag: quit adbd when both ends close the
      * local service socket
      */
-    int exit_on_close;
+    int exit_on_close = 0;
 
     // the asocket we are connected to
-    asocket* peer;
+    asocket* peer = nullptr;
 
     /* For local asockets, the fde is used to bind
      * us to our fd event system.  For remote asockets
      * these fields are not used.
      */
-    fdevent fde;
-    int fd;
+    fdevent fde = {};
+    int fd = 0;
 
     // queue of data waiting to be written
     std::deque<Range> packet_queue;
@@ -73,27 +73,27 @@
      * peer->ready() when we once again are ready to
      * receive data.
      */
-    int (*enqueue)(asocket* s, std::string data);
+    int (*enqueue)(asocket* s, std::string data) = nullptr;
 
     /* ready is called by the peer when it is ready for
      * us to send data via enqueue again
      */
-    void (*ready)(asocket* s);
+    void (*ready)(asocket* s) = nullptr;
 
     /* shutdown is called by the peer before it goes away.
      * the socket should not do any further calls on its peer.
      * Always followed by a call to close. Optional, i.e. can be NULL.
      */
-    void (*shutdown)(asocket* s);
+    void (*shutdown)(asocket* s) = nullptr;
 
     /* close is called by the peer when it has gone away.
      * we are not allowed to make any further calls on the
      * peer once our close method is called.
      */
-    void (*close)(asocket* s);
+    void (*close)(asocket* s) = nullptr;
 
     /* A socket is bound to atransport */
-    atransport* transport;
+    atransport* transport = nullptr;
 
     size_t get_max_payload() const;
 };
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index e9c45b7..307cbfe 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -170,7 +170,7 @@
     fdevent_remove(&s->fde);
 
     remove_socket(s);
-    free(s);
+    delete s;
 
     if (exit_on_close) {
         D("local_socket_destroy: exiting");
@@ -347,10 +347,7 @@
 }
 
 asocket* create_local_socket(int fd) {
-    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
-    if (s == NULL) {
-        fatal("cannot allocate socket");
-    }
+    asocket* s = new asocket();
     s->fd = fd;
     s->enqueue = local_socket_enqueue;
     s->ready = local_socket_ready;
@@ -459,7 +456,7 @@
     D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d", s->id, s->fd,
       s->peer ? s->peer->fd : -1);
     D("RS(%d): closed", s->id);
-    free(s);
+    delete s;
 }
 
 // Create a remote socket to exchange packets with a remote service through transport
@@ -470,11 +467,7 @@
     if (id == 0) {
         fatal("invalid remote socket id (0)");
     }
-    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
-
-    if (s == NULL) {
-        fatal("cannot allocate socket");
-    }
+    asocket* s = new asocket();
     s->id = id;
     s->enqueue = remote_socket_enqueue;
     s->ready = remote_socket_ready;
@@ -811,13 +804,12 @@
         s->peer->close(s->peer);
         s->peer = 0;
     }
-    free(s);
+    delete s;
 }
 
 static asocket* create_smart_socket(void) {
     D("Creating smart socket");
-    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
-    if (s == NULL) fatal("cannot allocate socket");
+    asocket* s = new asocket();
     s->enqueue = smart_socket_enqueue;
     s->ready = smart_socket_ready;
     s->shutdown = NULL;
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 3b0669c..9ae1297 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -378,9 +378,9 @@
  */
 struct device_tracker {
     asocket socket;
-    bool update_needed;
-    bool long_output;
-    device_tracker* next;
+    bool update_needed = false;
+    bool long_output = false;
+    device_tracker* next = nullptr;
 };
 
 /* linked list of all device trackers */
@@ -411,7 +411,7 @@
         peer->close(peer);
     }
     device_tracker_remove(tracker);
-    free(tracker);
+    delete tracker;
 }
 
 static int device_tracker_enqueue(asocket* socket, std::string) {
@@ -446,7 +446,7 @@
 }
 
 asocket* create_device_tracker(bool long_output) {
-    device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
+    device_tracker* tracker = new device_tracker();
     if (tracker == nullptr) fatal("cannot allocate device tracker");
 
     D("device tracker %p created", tracker);
diff --git a/base/properties.cpp b/base/properties.cpp
index cde4d69..ca8e96f 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -23,6 +23,7 @@
 
 #include <algorithm>
 #include <chrono>
+#include <limits>
 #include <string>
 
 #include <android-base/parseint.h>
@@ -109,7 +110,7 @@
 static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
   auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
   auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
-  ts.tv_sec = s.count();
+  ts.tv_sec = std::min<std::chrono::seconds::rep>(s.count(), std::numeric_limits<time_t>::max());
   ts.tv_nsec = ns.count();
 }
 
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index e530774..3ddadbc 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -69,6 +69,9 @@
     ],
     init_rc: ["bootstat.rc"],
     product_variables: {
+        pdk: {
+            enabled: false,
+        },
         debuggable: {
             init_rc: ["bootstat-debug.rc"],
         },
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 1ec5451..d8ee899 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -943,13 +943,20 @@
     return;
   }
 
-  const std::string system_reason(BootReasonStrToReason(reason));
+  const std::string system_reason(GetProperty(system_reboot_reason_property));
   android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, reason.c_str(),
                              system_reason.c_str(), end_time.count(), total_duration.count(),
                              (int64_t)bootloader_duration_ms,
                              (int64_t)time_since_last_boot_sec * 1000);
 }
 
+void SetSystemBootReason() {
+  const std::string bootloader_boot_reason(GetProperty(bootloader_reboot_reason_property));
+  const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));
+  // Record the scrubbed system_boot_reason to the property
+  SetProperty(system_reboot_reason_property, system_boot_reason);
+}
+
 // Records several metrics related to the time it takes to boot the device,
 // including disambiguating boot time on encrypted or non-encrypted devices.
 void RecordBootComplete() {
@@ -1037,12 +1044,10 @@
   boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
 
   // Log the scrubbed system_boot_reason.
-  const std::string system_reason(BootReasonStrToReason(reason));
+  const std::string system_reason(GetProperty(system_reboot_reason_property));
   int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
   boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
 
-  // Record the scrubbed system_boot_reason to the property
-  SetProperty(system_reboot_reason_property, system_reason);
   if (reason == "") {
     SetProperty(bootloader_reboot_reason_property, system_reason);
   }
@@ -1108,20 +1113,22 @@
 
   int option_index = 0;
   static const char value_str[] = "value";
+  static const char system_boot_reason_str[] = "set_system_boot_reason";
   static const char boot_complete_str[] = "record_boot_complete";
   static const char boot_reason_str[] = "record_boot_reason";
   static const char factory_reset_str[] = "record_time_since_factory_reset";
   static const struct option long_options[] = {
       // clang-format off
-      { "help",            no_argument,       NULL,   'h' },
-      { "log",             no_argument,       NULL,   'l' },
-      { "print",           no_argument,       NULL,   'p' },
-      { "record",          required_argument, NULL,   'r' },
-      { value_str,         required_argument, NULL,   0 },
-      { boot_complete_str, no_argument,       NULL,   0 },
-      { boot_reason_str,   no_argument,       NULL,   0 },
-      { factory_reset_str, no_argument,       NULL,   0 },
-      { NULL,              0,                 NULL,   0 }
+      { "help",                 no_argument,       NULL,   'h' },
+      { "log",                  no_argument,       NULL,   'l' },
+      { "print",                no_argument,       NULL,   'p' },
+      { "record",               required_argument, NULL,   'r' },
+      { value_str,              required_argument, NULL,   0 },
+      { system_boot_reason_str, no_argument,       NULL,   0 },
+      { boot_complete_str,      no_argument,       NULL,   0 },
+      { boot_reason_str,        no_argument,       NULL,   0 },
+      { factory_reset_str,      no_argument,       NULL,   0 },
+      { NULL,                   0,                 NULL,   0 }
       // clang-format on
   };
 
@@ -1137,6 +1144,8 @@
           // |optarg| is an external variable set by getopt representing
           // the option argument.
           value = optarg;
+        } else if (option_name == system_boot_reason_str) {
+          SetSystemBootReason();
         } else if (option_name == boot_complete_str) {
           RecordBootComplete();
         } else if (option_name == boot_reason_str) {
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index f06a38f..1300a27 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -68,8 +68,9 @@
 
 # Record boot complete metrics.
 on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
+    # Converts bootloader boot reason to system boot reason
     # Record boot_complete and related stats (decryption, etc).
     # Record the boot reason.
     # Record time since factory reset.
     # Log all boot events.
-    exec_background - system log -- /system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
+    exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 0f049fd..7b04e71 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -68,7 +68,7 @@
 class Memory;
 }
 
-void dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const char* fmt, ...);
+void dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const std::string&);
 
 void read_with_default(const char* path, char* buf, size_t len, const char* default_value);
 
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index 7c8a0ea..be39582 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -201,7 +201,7 @@
   }
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, memory_mock_.get(), 0x12345678, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x12345678, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -221,7 +221,7 @@
   memory_mock_->SetReadData(buffer, sizeof(buffer));
   memory_mock_->SetPartialReadAmount(96);
 
-  dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -240,7 +240,7 @@
   }
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -253,7 +253,7 @@
 }
 
 TEST_F(DumpMemoryTest, memory_unreadable) {
-  dump_memory(&log_, memory_mock_.get(), 0xa2345678, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0xa2345678, "memory near pc");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -309,7 +309,7 @@
   }
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -329,7 +329,7 @@
   memory_mock_->SetReadData(buffer, sizeof(buffer));
   memory_mock_->SetPartialReadAmount(102);
 
-  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -354,7 +354,7 @@
   memory_mock_->SetReadData(buffer, sizeof(buffer));
   memory_mock_->SetPartialReadAmount(45);
 
-  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -380,7 +380,7 @@
   memset(buffer, 0, sizeof(buffer));
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, memory_mock_.get(), 0x1000, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x1000, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -434,7 +434,7 @@
   memset(buffer, 0, sizeof(buffer));
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, memory_mock_.get(), 0, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -452,13 +452,13 @@
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
 #if defined(__LP64__)
-  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1");
-  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1");
-  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL, "memory near r1");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 32, "memory near r1");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 216, "memory near r1");
 #else
-  dump_memory(&log_, memory_mock_.get(), 0xffff0000, "memory near %.2s:", "r1");
-  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1");
-  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000, "memory near r1");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 32, "memory near r1");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 220, "memory near r1");
 #endif
 
   std::string tombstone_contents;
@@ -477,9 +477,9 @@
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
 #if defined(__LP64__)
-  dump_memory(&log_, memory_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xfffffffffffffff0, "memory near r1");
 #else
-  dump_memory(&log_, memory_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xfffffff0, "memory near r1");
 #endif
 
   std::string tombstone_contents;
@@ -500,9 +500,9 @@
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
 #if defined(__LP64__)
-  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 224, "memory near r4");
 #else
-  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 224, "memory near r4");
 #endif
 
   std::string tombstone_contents;
@@ -562,7 +562,7 @@
 
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   uintptr_t addr = 0x10000020 + page_size - 120;
-  dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -621,7 +621,7 @@
 
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   uintptr_t addr = 0x10000020 + page_size - 192;
-  dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -679,7 +679,7 @@
   memory_mock_->SetPartialReadAmount(0);
 
   uintptr_t addr = 0x10000020;
-  dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -739,7 +739,7 @@
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   uintptr_t addr = 0x10000020 + page_size - 256;
 
-  dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 7d85602..140ef6d 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -65,6 +65,8 @@
 using unwindstack::Memory;
 using unwindstack::Regs;
 
+using namespace std::literals::string_literals;
+
 #define STACK_WORDS 16
 
 static void dump_header_info(log_t* log) {
@@ -148,8 +150,9 @@
 
     backtrace_map_t map;
     backtrace_map->FillIn(stack_data[i], &map);
-    if (BacktraceMap::IsValid(map) && !map.name.empty()) {
-      line += "  " + map.name;
+    std::string map_name{map.Name()};
+    if (BacktraceMap::IsValid(map) && !map_name.empty()) {
+      line += "  " + map_name;
       uint64_t offset = 0;
       std::string func_name = backtrace_map->GetFunctionName(stack_data[i], &offset);
       if (!func_name.empty()) {
@@ -382,9 +385,16 @@
   print_register_row(log, special_row);
 }
 
-void dump_memory_and_code(log_t* log, Memory* memory, Regs* regs) {
-  regs->IterateRegisters([log, memory](const char* name, uint64_t value) {
-    dump_memory(log, memory, value, "memory near %s:", name);
+void dump_memory_and_code(log_t* log, BacktraceMap* map, Memory* memory, Regs* regs) {
+  regs->IterateRegisters([log, map, memory](const char* reg_name, uint64_t reg_value) {
+    std::string label{"memory near "s + reg_name};
+    if (map) {
+      backtrace_map_t map_info;
+      map->FillIn(reg_value, &map_info);
+      std::string map_name{map_info.Name()};
+      if (!map_name.empty()) label += " (" + map_info.Name() + ")";
+    }
+    dump_memory(log, memory, reg_value, label);
   });
 }
 
@@ -423,7 +433,7 @@
   }
 
   if (primary_thread) {
-    dump_memory_and_code(log, process_memory, thread_info.registers.get());
+    dump_memory_and_code(log, map, process_memory, thread_info.registers.get());
     if (map) {
       uint64_t addr = 0;
       siginfo_t* si = thread_info.siginfo;
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 3ac98f5..d153865 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -124,13 +124,7 @@
 #define MEMORY_BYTES_TO_DUMP 256
 #define MEMORY_BYTES_PER_LINE 16
 
-void dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const char* fmt, ...) {
-  std::string log_msg;
-  va_list ap;
-  va_start(ap, fmt);
-  android::base::StringAppendV(&log_msg, fmt, ap);
-  va_end(ap);
-
+void dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const std::string& label) {
   // Align the address to sizeof(long) and start 32 bytes before the address.
   addr &= ~(sizeof(long) - 1);
   if (addr >= 4128) {
@@ -147,7 +141,7 @@
     return;
   }
 
-  _LOG(log, logtype::MEMORY, "\n%s\n", log_msg.c_str());
+  _LOG(log, logtype::MEMORY, "\n%s:\n", label.c_str());
 
   // Dump 256 bytes
   uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
diff --git a/debuggerd/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy
index b1f459d..254330d 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm.policy
@@ -26,11 +26,11 @@
 rt_tgsigqueueinfo: 1
 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
 madvise: 1
-mprotect: arg2 in PROT_READ|PROT_WRITE
+mprotect: arg2 in 0x1|0x2
 munmap: 1
 getuid32: 1
 fstat64: 1
-mmap2: arg2 in PROT_READ|PROT_WRITE
+mmap2: arg2 in 0x1|0x2
 geteuid32: 1
 getgid32: 1
 getegid32: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index e5e7afb..9b3ef09 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -25,11 +25,11 @@
 rt_tgsigqueueinfo: 1
 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
 madvise: 1
-mprotect: arg2 in PROT_READ|PROT_WRITE
+mprotect: arg2 in 0x1|0x2
 munmap: 1
 getuid: 1
 fstat: 1
-mmap: arg2 in PROT_READ|PROT_WRITE
+mmap: arg2 in 0x1|0x2
 geteuid: 1
 getgid: 1
 getegid: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index b78c94a..2ef31b0 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -35,6 +35,14 @@
 #define PR_SET_VMA 0x53564d41
 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA
 
+#if 0
+libminijail on vendor partitions older than P does not have constants from <sys/mman.h>.
+Define the values of PROT_READ and PROT_WRITE ourselves to maintain backwards compatibility.
+#else
+#define PROT_READ 0x1
+#define PROT_WRITE 0x2
+#endif
+
 madvise: 1
 mprotect: arg2 in PROT_READ|PROT_WRITE
 munmap: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy
index b1f459d..254330d 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86.policy
@@ -26,11 +26,11 @@
 rt_tgsigqueueinfo: 1
 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
 madvise: 1
-mprotect: arg2 in PROT_READ|PROT_WRITE
+mprotect: arg2 in 0x1|0x2
 munmap: 1
 getuid32: 1
 fstat64: 1
-mmap2: arg2 in PROT_READ|PROT_WRITE
+mmap2: arg2 in 0x1|0x2
 geteuid32: 1
 getgid32: 1
 getegid32: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86_64.policy b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
index e5e7afb..9b3ef09 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86_64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
@@ -25,11 +25,11 @@
 rt_tgsigqueueinfo: 1
 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
 madvise: 1
-mprotect: arg2 in PROT_READ|PROT_WRITE
+mprotect: arg2 in 0x1|0x2
 munmap: 1
 getuid: 1
 fstat: 1
-mmap: arg2 in PROT_READ|PROT_WRITE
+mmap: arg2 in 0x1|0x2
 geteuid: 1
 getgid: 1
 getegid: 1
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index 46a6f76..1787031 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -509,6 +509,29 @@
   ASSERT_EQ("_ZTH_N3oneE", demangler.Parse("_ZTH_N3oneE"));
 }
 
+TEST(DemangleTest, r_value_reference) {
+  Demangler demangler;
+  ASSERT_EQ(
+      "android::SurfaceComposerClient::Transaction::merge(android::SurfaceComposerClient::"
+      "Transaction&&)",
+      demangler.Parse("_ZN7android21SurfaceComposerClient11Transaction5mergeEOS1_"));
+}
+
+TEST(DemangleTest, initial_St) {
+  Demangler demangler;
+  EXPECT_EQ("std::state", demangler.Parse("_ZSt5state"));
+  EXPECT_EQ("std::_In::ward", demangler.Parse("_ZNSt3_In4wardE"));
+  EXPECT_EQ("std::__terminate(void (*)())", demangler.Parse("_ZSt11__terminatePFvvE"));
+}
+
+TEST(DemangleTest, cfi) {
+  Demangler demangler;
+  EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*)",
+            demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB"));
+  EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*) [clone .cfi]",
+            demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB.cfi"));
+}
+
 TEST(DemangleTest, demangle) {
   std::string str;
 
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index af2816c..7a3aa81 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -580,6 +580,10 @@
     }
     return name + 1;
 
+  case 'O':
+    cur_state_.suffixes.push_back("&&");
+    return name + 1;
+
   case 'K':
   case 'V': {
     const char* suffix;
@@ -701,6 +705,9 @@
         cur_state_.str.clear();
       }
       return name;
+    } else if (strcmp(name, ".cfi") == 0) {
+      function_suffix_ += " [clone .cfi]";
+      return name + 4;
     }
   }
   return nullptr;
@@ -816,6 +823,16 @@
     return name + 1;
   }
 
+  if (*name == 'S') {
+    name++;
+    if (*name == 't') {
+      function_name_ = "std::";
+      name++;
+    } else {
+      return nullptr;
+    }
+  }
+
   if (std::isdigit(*name)) {
     name = GetStringFromLength(name, &function_name_);
   } else if (*name == 'L' && std::isdigit(name[1])) {
diff --git a/gatekeeperd/OWNERS b/gatekeeperd/OWNERS
new file mode 100644
index 0000000..9c99c6e
--- /dev/null
+++ b/gatekeeperd/OWNERS
@@ -0,0 +1,2 @@
+swillden@google.com
+jdanis@google.com
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 5781765..cae1e8b 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -322,7 +322,7 @@
                 std::vector<uint8_t> auth_token_vector(*auth_token,
                                                        (*auth_token) + *auth_token_length);
                 int result = 0;
-                auto binder_result = service->addAuthToken(auth_token_vector, &result);
+                auto binder_result = service->addAuthToken(auth_token_vector, uid, &result);
                 if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) {
                     ALOGE("Failure sending auth token to KeyStore: %" PRId32, result);
                 }
diff --git a/init/Android.bp b/init/Android.bp
index 6c80ee6..1f2ad2e 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -93,6 +93,8 @@
     defaults: ["init_defaults"],
     srcs: [
         "action.cpp",
+        "action_manager.cpp",
+        "action_parser.cpp",
         "bootchart.cpp",
         "builtins.cpp",
         "capabilities.cpp",
diff --git a/init/action.cpp b/init/action.cpp
index ba03e66..11335ca 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -21,11 +21,9 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 
-#include "stable_properties.h"
 #include "util.h"
 
 using android::base::Join;
-using android::base::StartsWith;
 
 namespace android {
 namespace init {
@@ -70,8 +68,15 @@
     return Join(args_, ' ');
 }
 
-Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line)
-    : oneshot_(oneshot), subcontext_(subcontext), filename_(filename), line_(line) {}
+Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line,
+               const std::string& event_trigger,
+               const std::map<std::string, std::string>& property_triggers)
+    : property_triggers_(property_triggers),
+      event_trigger_(event_trigger),
+      oneshot_(oneshot),
+      subcontext_(subcontext),
+      filename_(filename),
+      line_(line) {}
 
 const KeywordFunctionMap* Action::function_map_ = nullptr;
 
@@ -135,85 +140,6 @@
     }
 }
 
-static bool IsActionableProperty(Subcontext* subcontext, const std::string& prop_name) {
-    static bool enabled =
-        android::base::GetBoolProperty("ro.actionable_compatible_property.enabled", false);
-
-    if (subcontext == nullptr || !enabled) {
-        return true;
-    }
-
-    if (kExportedActionableProperties.count(prop_name) == 1) {
-        return true;
-    }
-    for (const auto& prefix : kPartnerPrefixes) {
-        if (android::base::StartsWith(prop_name, prefix)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-Result<Success> Action::ParsePropertyTrigger(const std::string& trigger) {
-    const static std::string prop_str("property:");
-    std::string prop_name(trigger.substr(prop_str.length()));
-    size_t equal_pos = prop_name.find('=');
-    if (equal_pos == std::string::npos) {
-        return Error() << "property trigger found without matching '='";
-    }
-
-    std::string prop_value(prop_name.substr(equal_pos + 1));
-    prop_name.erase(equal_pos);
-
-    if (!IsActionableProperty(subcontext_, prop_name)) {
-        return Error() << "unexported property tigger found: " << prop_name;
-    }
-
-    if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
-        return Error() << "multiple property triggers found for same property";
-    }
-    return Success();
-}
-
-Result<Success> Action::InitTriggers(const std::vector<std::string>& args) {
-    const static std::string prop_str("property:");
-    for (std::size_t i = 0; i < args.size(); ++i) {
-        if (args[i].empty()) {
-            return Error() << "empty trigger is not valid";
-        }
-
-        if (i % 2) {
-            if (args[i] != "&&") {
-                return Error() << "&& is the only symbol allowed to concatenate actions";
-            } else {
-                continue;
-            }
-        }
-
-        if (!args[i].compare(0, prop_str.length(), prop_str)) {
-            if (auto result = ParsePropertyTrigger(args[i]); !result) {
-                return result;
-            }
-        } else {
-            if (!event_trigger_.empty()) {
-                return Error() << "multiple event triggers are not allowed";
-            }
-
-            event_trigger_ = args[i];
-        }
-    }
-
-    return Success();
-}
-
-Result<Success> Action::InitSingleTrigger(const std::string& trigger) {
-    std::vector<std::string> name_vector{trigger};
-    if (auto result = InitTriggers(name_vector); !result) {
-        return Error() << "InitTriggers() failed: " << result.error();
-    }
-    return Success();
-}
-
 // This function checks that all property triggers are satisfied, that is
 // for each (name, value) in property_triggers_, check that the current
 // value of the property 'name' == value.
@@ -281,142 +207,5 @@
     }
 }
 
-ActionManager::ActionManager() : current_command_(0) {
-}
-
-ActionManager& ActionManager::GetInstance() {
-    static ActionManager instance;
-    return instance;
-}
-
-void ActionManager::AddAction(std::unique_ptr<Action> action) {
-    actions_.emplace_back(std::move(action));
-}
-
-void ActionManager::QueueEventTrigger(const std::string& trigger) {
-    event_queue_.emplace(trigger);
-}
-
-void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
-    event_queue_.emplace(std::make_pair(name, value));
-}
-
-void ActionManager::QueueAllPropertyActions() {
-    QueuePropertyChange("", "");
-}
-
-void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
-    auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0);
-    std::vector<std::string> name_vector{name};
-
-    if (auto result = action->InitSingleTrigger(name); !result) {
-        LOG(ERROR) << "Cannot queue BuiltinAction for " << name << ": " << result.error();
-        return;
-    }
-
-    action->AddCommand(func, name_vector, 0);
-
-    event_queue_.emplace(action.get());
-    actions_.emplace_back(std::move(action));
-}
-
-void ActionManager::ExecuteOneCommand() {
-    // Loop through the event queue until we have an action to execute
-    while (current_executing_actions_.empty() && !event_queue_.empty()) {
-        for (const auto& action : actions_) {
-            if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
-                           event_queue_.front())) {
-                current_executing_actions_.emplace(action.get());
-            }
-        }
-        event_queue_.pop();
-    }
-
-    if (current_executing_actions_.empty()) {
-        return;
-    }
-
-    auto action = current_executing_actions_.front();
-
-    if (current_command_ == 0) {
-        std::string trigger_name = action->BuildTriggersString();
-        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
-                  << ":" << action->line() << ")";
-    }
-
-    action->ExecuteOneCommand(current_command_);
-
-    // If this was the last command in the current action, then remove
-    // the action from the executing list.
-    // If this action was oneshot, then also remove it from actions_.
-    ++current_command_;
-    if (current_command_ == action->NumCommands()) {
-        current_executing_actions_.pop();
-        current_command_ = 0;
-        if (action->oneshot()) {
-            auto eraser = [&action] (std::unique_ptr<Action>& a) {
-                return a.get() == action;
-            };
-            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
-        }
-    }
-}
-
-bool ActionManager::HasMoreCommands() const {
-    return !current_executing_actions_.empty() || !event_queue_.empty();
-}
-
-void ActionManager::DumpState() const {
-    for (const auto& a : actions_) {
-        a->DumpState();
-    }
-}
-
-void ActionManager::ClearQueue() {
-    // We are shutting down so don't claim the oneshot builtin actions back
-    current_executing_actions_ = {};
-    event_queue_ = {};
-    current_command_ = 0;
-}
-
-Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
-                                           const std::string& filename, int line) {
-    std::vector<std::string> triggers(args.begin() + 1, args.end());
-    if (triggers.size() < 1) {
-        return Error() << "Actions must have a trigger";
-    }
-
-    Subcontext* action_subcontext = nullptr;
-    if (subcontexts_) {
-        for (auto& subcontext : *subcontexts_) {
-            if (StartsWith(filename, subcontext.path_prefix())) {
-                action_subcontext = &subcontext;
-                break;
-            }
-        }
-    }
-
-    auto action = std::make_unique<Action>(false, action_subcontext, filename, line);
-
-    if (auto result = action->InitTriggers(triggers); !result) {
-        return Error() << "InitTriggers() failed: " << result.error();
-    }
-
-    action_ = std::move(action);
-    return Success();
-}
-
-Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
-    return action_ ? action_->AddCommand(std::move(args), line) : Success();
-}
-
-Result<Success> ActionParser::EndSection() {
-    if (action_ && action_->NumCommands() > 0) {
-        action_manager_->AddAction(std::move(action_));
-    }
-
-    return Success();
-}
-
 }  // namespace init
 }  // namespace android
diff --git a/init/action.h b/init/action.h
index 1bfc6c7..4f063cc 100644
--- a/init/action.h
+++ b/init/action.h
@@ -25,7 +25,6 @@
 
 #include "builtins.h"
 #include "keyword_map.h"
-#include "parser.h"
 #include "result.h"
 #include "subcontext.h"
 
@@ -58,12 +57,12 @@
 
 class Action {
   public:
-    Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line);
+    Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line,
+           const std::string& event_trigger,
+           const std::map<std::string, std::string>& property_triggers);
 
     Result<Success> AddCommand(const std::vector<std::string>& args, int line);
     void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
-    Result<Success> InitTriggers(const std::vector<std::string>& args);
-    Result<Success> InitSingleTrigger(const std::string& trigger);
     std::size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
@@ -84,7 +83,6 @@
     void ExecuteCommand(const Command& command) const;
     bool CheckPropertyTriggers(const std::string& name = "",
                                const std::string& value = "") const;
-    Result<Success> ParsePropertyTrigger(const std::string& trigger);
 
     std::map<std::string, std::string> property_triggers_;
     std::string event_trigger_;
@@ -96,48 +94,6 @@
     static const KeywordFunctionMap* function_map_;
 };
 
-class ActionManager {
-  public:
-    static ActionManager& GetInstance();
-
-    // Exposed for testing
-    ActionManager();
-
-    void AddAction(std::unique_ptr<Action> action);
-    void QueueEventTrigger(const std::string& trigger);
-    void QueuePropertyChange(const std::string& name, const std::string& value);
-    void QueueAllPropertyActions();
-    void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
-    void ExecuteOneCommand();
-    bool HasMoreCommands() const;
-    void DumpState() const;
-    void ClearQueue();
-
-  private:
-    ActionManager(ActionManager const&) = delete;
-    void operator=(ActionManager const&) = delete;
-
-    std::vector<std::unique_ptr<Action>> actions_;
-    std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
-    std::queue<const Action*> current_executing_actions_;
-    std::size_t current_command_;
-};
-
-class ActionParser : public SectionParser {
-  public:
-    ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
-        : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
-    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                 int line) override;
-    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
-    Result<Success> EndSection() override;
-
-  private:
-    ActionManager* action_manager_;
-    std::vector<Subcontext>* subcontexts_;
-    std::unique_ptr<Action> action_;
-};
-
 }  // namespace init
 }  // namespace android
 
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
new file mode 100644
index 0000000..22977bb
--- /dev/null
+++ b/init/action_manager.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#include "action_manager.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace init {
+
+ActionManager::ActionManager() : current_command_(0) {}
+
+ActionManager& ActionManager::GetInstance() {
+    static ActionManager instance;
+    return instance;
+}
+
+void ActionManager::AddAction(std::unique_ptr<Action> action) {
+    actions_.emplace_back(std::move(action));
+}
+
+void ActionManager::QueueEventTrigger(const std::string& trigger) {
+    event_queue_.emplace(trigger);
+}
+
+void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
+    event_queue_.emplace(std::make_pair(name, value));
+}
+
+void ActionManager::QueueAllPropertyActions() {
+    QueuePropertyChange("", "");
+}
+
+void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
+    auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
+                                           std::map<std::string, std::string>{});
+    std::vector<std::string> name_vector{name};
+
+    action->AddCommand(func, name_vector, 0);
+
+    event_queue_.emplace(action.get());
+    actions_.emplace_back(std::move(action));
+}
+
+void ActionManager::ExecuteOneCommand() {
+    // Loop through the event queue until we have an action to execute
+    while (current_executing_actions_.empty() && !event_queue_.empty()) {
+        for (const auto& action : actions_) {
+            if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
+                           event_queue_.front())) {
+                current_executing_actions_.emplace(action.get());
+            }
+        }
+        event_queue_.pop();
+    }
+
+    if (current_executing_actions_.empty()) {
+        return;
+    }
+
+    auto action = current_executing_actions_.front();
+
+    if (current_command_ == 0) {
+        std::string trigger_name = action->BuildTriggersString();
+        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
+                  << ":" << action->line() << ")";
+    }
+
+    action->ExecuteOneCommand(current_command_);
+
+    // If this was the last command in the current action, then remove
+    // the action from the executing list.
+    // If this action was oneshot, then also remove it from actions_.
+    ++current_command_;
+    if (current_command_ == action->NumCommands()) {
+        current_executing_actions_.pop();
+        current_command_ = 0;
+        if (action->oneshot()) {
+            auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
+            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
+        }
+    }
+}
+
+bool ActionManager::HasMoreCommands() const {
+    return !current_executing_actions_.empty() || !event_queue_.empty();
+}
+
+void ActionManager::DumpState() const {
+    for (const auto& a : actions_) {
+        a->DumpState();
+    }
+}
+
+void ActionManager::ClearQueue() {
+    // We are shutting down so don't claim the oneshot builtin actions back
+    current_executing_actions_ = {};
+    event_queue_ = {};
+    current_command_ = 0;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/action_manager.h b/init/action_manager.h
new file mode 100644
index 0000000..5f47a6d
--- /dev/null
+++ b/init/action_manager.h
@@ -0,0 +1,59 @@
+/*
+ * 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 _INIT_ACTION_MANAGER_H
+#define _INIT_ACTION_MANAGER_H
+
+#include <string>
+#include <vector>
+
+#include "action.h"
+#include "builtins.h"
+
+namespace android {
+namespace init {
+
+class ActionManager {
+  public:
+    static ActionManager& GetInstance();
+
+    // Exposed for testing
+    ActionManager();
+
+    void AddAction(std::unique_ptr<Action> action);
+    void QueueEventTrigger(const std::string& trigger);
+    void QueuePropertyChange(const std::string& name, const std::string& value);
+    void QueueAllPropertyActions();
+    void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
+    void ExecuteOneCommand();
+    bool HasMoreCommands() const;
+    void DumpState() const;
+    void ClearQueue();
+
+  private:
+    ActionManager(ActionManager const&) = delete;
+    void operator=(ActionManager const&) = delete;
+
+    std::vector<std::unique_ptr<Action>> actions_;
+    std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
+    std::queue<const Action*> current_executing_actions_;
+    std::size_t current_command_;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
new file mode 100644
index 0000000..8a4b518
--- /dev/null
+++ b/init/action_parser.cpp
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+#include "action_parser.h"
+
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+
+#include "stable_properties.h"
+
+using android::base::GetBoolProperty;
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+namespace {
+
+bool IsActionableProperty(Subcontext* subcontext, const std::string& prop_name) {
+    static bool enabled = GetBoolProperty("ro.actionable_compatible_property.enabled", false);
+
+    if (subcontext == nullptr || !enabled) {
+        return true;
+    }
+
+    if (kExportedActionableProperties.count(prop_name) == 1) {
+        return true;
+    }
+    for (const auto& prefix : kPartnerPrefixes) {
+        if (android::base::StartsWith(prop_name, prefix)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
+                                     std::map<std::string, std::string>* property_triggers) {
+    const static std::string prop_str("property:");
+    std::string prop_name(trigger.substr(prop_str.length()));
+    size_t equal_pos = prop_name.find('=');
+    if (equal_pos == std::string::npos) {
+        return Error() << "property trigger found without matching '='";
+    }
+
+    std::string prop_value(prop_name.substr(equal_pos + 1));
+    prop_name.erase(equal_pos);
+
+    if (!IsActionableProperty(subcontext, prop_name)) {
+        return Error() << "unexported property tigger found: " << prop_name;
+    }
+
+    if (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) {
+        return Error() << "multiple property triggers found for same property";
+    }
+    return Success();
+}
+
+Result<Success> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
+                              std::string* event_trigger,
+                              std::map<std::string, std::string>* property_triggers) {
+    const static std::string prop_str("property:");
+    for (std::size_t i = 0; i < args.size(); ++i) {
+        if (args[i].empty()) {
+            return Error() << "empty trigger is not valid";
+        }
+
+        if (i % 2) {
+            if (args[i] != "&&") {
+                return Error() << "&& is the only symbol allowed to concatenate actions";
+            } else {
+                continue;
+            }
+        }
+
+        if (!args[i].compare(0, prop_str.length(), prop_str)) {
+            if (auto result = ParsePropertyTrigger(args[i], subcontext, property_triggers);
+                !result) {
+                return result;
+            }
+        } else {
+            if (!event_trigger->empty()) {
+                return Error() << "multiple event triggers are not allowed";
+            }
+
+            *event_trigger = args[i];
+        }
+    }
+
+    return Success();
+}
+
+}  // namespace
+
+Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
+                                           const std::string& filename, int line) {
+    std::vector<std::string> triggers(args.begin() + 1, args.end());
+    if (triggers.size() < 1) {
+        return Error() << "Actions must have a trigger";
+    }
+
+    Subcontext* action_subcontext = nullptr;
+    if (subcontexts_) {
+        for (auto& subcontext : *subcontexts_) {
+            if (StartsWith(filename, subcontext.path_prefix())) {
+                action_subcontext = &subcontext;
+                break;
+            }
+        }
+    }
+
+    std::string event_trigger;
+    std::map<std::string, std::string> property_triggers;
+
+    if (auto result = ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);
+        !result) {
+        return Error() << "ParseTriggers() failed: " << result.error();
+    }
+
+    auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger,
+                                           property_triggers);
+
+    action_ = std::move(action);
+    return Success();
+}
+
+Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    return action_ ? action_->AddCommand(std::move(args), line) : Success();
+}
+
+Result<Success> ActionParser::EndSection() {
+    if (action_ && action_->NumCommands() > 0) {
+        action_manager_->AddAction(std::move(action_));
+    }
+
+    return Success();
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/action_parser.h b/init/action_parser.h
new file mode 100644
index 0000000..b7f7074
--- /dev/null
+++ b/init/action_parser.h
@@ -0,0 +1,49 @@
+/*
+ * 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 _INIT_ACTION_PARSER_H
+#define _INIT_ACTION_PARSER_H
+
+#include <string>
+#include <vector>
+
+#include "action.h"
+#include "action_manager.h"
+#include "parser.h"
+#include "subcontext.h"
+
+namespace android {
+namespace init {
+
+class ActionParser : public SectionParser {
+  public:
+    ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
+        : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
+    Result<Success> EndSection() override;
+
+  private:
+    ActionManager* action_manager_;
+    std::vector<Subcontext>* subcontexts_;
+    std::unique_ptr<Action> action_;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/builtins.cpp b/init/builtins.cpp
index be259c2..1040b47 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -58,7 +58,7 @@
 #include <selinux/selinux.h>
 #include <system/thread_defs.h>
 
-#include "action.h"
+#include "action_manager.h"
 #include "bootchart.h"
 #include "init.h"
 #include "parser.h"
@@ -285,11 +285,8 @@
 
     if (e4crypt_is_native()) {
         if (e4crypt_set_directory_policy(args[1].c_str())) {
-            const std::vector<std::string> options = {
-                "--prompt_and_wipe_data",
-                "--reason=set_policy_failed:"s + args[1]};
-            reboot_into_recovery(options);
-            return Success();
+            reboot_into_recovery(
+                {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
         }
     }
     return Success();
@@ -987,6 +984,24 @@
     return android::base::GetProperty("ro.crypto.type", "") == "file";
 }
 
+static Result<Success> ExecWithRebootOnFailure(const std::string& reboot_reason,
+                                               const std::vector<std::string>& args) {
+    auto service = Service::MakeTemporaryOneshotService(args);
+    if (!service) {
+        return Error() << "Could not create exec service";
+    }
+    service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
+        if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
+            reboot_into_recovery({"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+        }
+    });
+    if (auto result = service->ExecStart(); !result) {
+        return Error() << "Could not start exec service: " << result.error();
+    }
+    ServiceList::GetInstance().AddService(std::move(service));
+    return Success();
+}
+
 static Result<Success> do_installkey(const BuiltinArguments& args) {
     if (!is_file_crypto()) return Success();
 
@@ -994,15 +1009,13 @@
     if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
         return ErrnoError() << "Failed to create " << unencrypted_dir;
     }
-    std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
-                                          "enablefilecrypto"};
-    return do_exec({std::move(exec_args), args.context});
+    return ExecWithRebootOnFailure("enablefilecrypto_failed", {"exec", "/system/bin/vdc", "--wait",
+                                                               "cryptfs", "enablefilecrypto"});
 }
 
 static Result<Success> do_init_user0(const BuiltinArguments& args) {
-    std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
-                                          "init_user0"};
-    return do_exec({std::move(exec_args), args.context});
+    return ExecWithRebootOnFailure("init_user0_failed",
+                                   {"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"});
 }
 
 const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
diff --git a/init/init.cpp b/init/init.cpp
index bd09e4b..7e4eaa8 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -45,6 +45,7 @@
 #include <memory>
 #include <optional>
 
+#include "action_parser.h"
 #include "import_parser.h"
 #include "init_first_stage.h"
 #include "keychords.h"
diff --git a/init/init.h b/init/init.h
index ff7bdeb..ecce5d7 100644
--- a/init/init.h
+++ b/init/init.h
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include "action.h"
+#include "action_manager.h"
 #include "parser.h"
 #include "service.h"
 
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 268873c..0f9635f 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -21,6 +21,8 @@
 #include <gtest/gtest.h>
 
 #include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
 #include "builtins.h"
 #include "import_parser.h"
 #include "keyword_map.h"
diff --git a/init/reboot.cpp b/init/reboot.cpp
index a88a42d..242750a 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -50,6 +50,7 @@
 #include <private/android_filesystem_config.h>
 #include <selinux/selinux.h>
 
+#include "action_manager.h"
 #include "capabilities.h"
 #include "init.h"
 #include "property_service.h"
diff --git a/init/service.cpp b/init/service.cpp
index a4e33f7..35dd319 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -189,7 +189,8 @@
       capabilities_(capabilities),
       namespace_flags_(namespace_flags),
       seclabel_(seclabel),
-      onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0),
+      onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
+                 "onrestart", {}),
       keychord_id_(0),
       ioprio_class_(IoSchedClass_NONE),
       ioprio_pri_(0),
@@ -199,9 +200,7 @@
       soft_limit_in_bytes_(-1),
       limit_in_bytes_(-1),
       start_order_(0),
-      args_(args) {
-    onrestart_.InitSingleTrigger("onrestart");
-}
+      args_(args) {}
 
 void Service::NotifyStateChange(const std::string& new_state) const {
     if ((flags_ & SVC_TEMPORARY) != 0) {
@@ -299,7 +298,7 @@
     }
 }
 
-void Service::Reap() {
+void Service::Reap(const siginfo_t& siginfo) {
     if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
         KillProcessGroup(SIGKILL);
     }
@@ -308,6 +307,10 @@
     std::for_each(descriptors_.begin(), descriptors_.end(),
                   std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
 
+    for (const auto& f : reap_callbacks_) {
+        f(siginfo);
+    }
+
     if (flags_ & SVC_EXEC) UnSetExec();
 
     if (flags_ & SVC_TEMPORARY) return;
diff --git a/init/service.h b/init/service.h
index d46a413..bcf1943 100644
--- a/init/service.h
+++ b/init/service.h
@@ -17,6 +17,7 @@
 #ifndef _INIT_SERVICE_H
 #define _INIT_SERVICE_H
 
+#include <signal.h>
 #include <sys/resource.h>
 #include <sys/types.h>
 
@@ -81,7 +82,7 @@
     void Stop();
     void Terminate();
     void Restart();
-    void Reap();
+    void Reap(const siginfo_t& siginfo);
     void DumpState() const;
     void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
     bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
@@ -89,6 +90,9 @@
         is_exec_service_running_ = false;
         flags_ &= ~SVC_EXEC;
     }
+    void AddReapCallback(std::function<void(const siginfo_t& siginfo)> callback) {
+        reap_callbacks_.emplace_back(std::move(callback));
+    }
 
     static bool is_exec_service_running() { return is_exec_service_running_; }
 
@@ -210,6 +214,8 @@
     std::vector<std::pair<int, rlimit>> rlimits_;
 
     std::vector<std::string> args_;
+
+    std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
 };
 
 class ServiceList {
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 072a0fb..badacaf 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -84,16 +84,15 @@
         }
     }
 
-    auto status = siginfo.si_status;
-    if (WIFEXITED(status)) {
-        LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
-    } else if (WIFSIGNALED(status)) {
-        LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
+    if (siginfo.si_code == CLD_EXITED) {
+        LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;
+    } else {
+        LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
     }
 
     if (!service) return true;
 
-    service->Reap();
+    service->Reap(siginfo);
 
     if (service->flags() & SVC_TEMPORARY) {
         ServiceList::GetInstance().RemoveService(*service);
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 10a4e46..be56531 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -83,6 +83,9 @@
     target: {
         darwin: {
             enabled: true,
+            shared_libs: [
+               "libbase",
+             ],
         },
         linux: {
             srcs: libbacktrace_sources,
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index dec241c..6445a7c 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -85,14 +85,12 @@
 std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
   std::string map_name;
   if (BacktraceMap::IsValid(frame->map)) {
+    map_name = frame->map.Name();
     if (!frame->map.name.empty()) {
-      map_name = frame->map.name.c_str();
       if (map_name[0] == '[' && map_name[map_name.size() - 1] == ']') {
         map_name.resize(map_name.size() - 1);
         map_name += StringPrintf(":%" PRIPTR "]", frame->map.start);
       }
-    } else {
-      map_name = StringPrintf("<anonymous:%" PRIPTR ">", frame->map.start);
     }
   } else {
     map_name = "<unknown>";
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index c8a500c..bdae140 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -24,11 +24,21 @@
 
 #include <log/log.h>
 
-#include <backtrace/backtrace_constants.h>
+#include <android-base/stringprintf.h>
+#include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
+#include <backtrace/backtrace_constants.h>
 
 #include "thread_utils.h"
 
+using android::base::StringPrintf;
+
+std::string backtrace_map_t::Name() const {
+  if (!name.empty()) return name;
+  if (start == 0 && end == 0) return "";
+  return StringPrintf("<anonymous:%" PRIPTR ">", start);
+}
+
 BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) {
   if (pid_ < 0) {
     pid_ = getpid();
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index e19c413..7b26079 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -48,6 +48,9 @@
   uint64_t load_bias = 0;
   int flags = 0;
   std::string name;
+
+  // Returns `name` if non-empty, or `<anonymous:0x...>` otherwise.
+  std::string Name() const;
 };
 
 namespace unwindstack {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 984071c..006076a 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -204,8 +204,9 @@
 
     // Support wifi_hal_legacy administering a network interface.
     { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
-                                           CAP_MASK_LONG(CAP_NET_RAW),
-                                              "vendor/bin/hw/android.hardware.wifi@1.0-service" },
+                                           CAP_MASK_LONG(CAP_NET_RAW) |
+                                           CAP_MASK_LONG(CAP_SYS_MODULE),
+                                           "vendor/bin/hw/android.hardware.wifi@1.0-service" },
 
     // A non-privileged zygote that spawns isolated processes for web rendering.
     { 0750,  AID_ROOT,      AID_ROOT,      CAP_MASK_LONG(CAP_SETUID) |
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index f32330a..f95c6c5 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -70,7 +70,7 @@
     for (int i = 0; i < count; i++) {
         snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
         property_get(buf, value, "");
-        if (strcmp(value, cmdline) == 0) {
+        if (strcmp(value, "*") == 0 || strcmp(value, cmdline) == 0) {
             return true;
         }
     }
diff --git a/libion/ion_test.c b/libion/ion_test.c
index b7d5583..f3874ae 100644
--- a/libion/ion_test.c
+++ b/libion/ion_test.c
@@ -250,7 +250,7 @@
         case 'p':
             prot = 0;
             prot |= strstr(optarg, "MAP_PRIVATE") ? MAP_PRIVATE : 0;
-            prot |= strstr(optarg, "MAP_SHARED") ? MAP_PRIVATE : 0;
+            prot |= strstr(optarg, "MAP_SHARED") ? MAP_SHARED : 0;
             break;
         case 'f':
             alloc_flags = atol(optarg);
diff --git a/liblog/include_vndk/log/log_event_list.h b/liblog/include_vndk/log/log_event_list.h
new file mode 100644
index 0000000..cbd3091
--- /dev/null
+++ b/liblog/include_vndk/log/log_event_list.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2005-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.
+ */
+
+/* Special log_event_list.h file for VNDK linking modules */
+
+#ifndef _LIBS_LOG_EVENT_LIST_H
+#define _LIBS_LOG_EVENT_LIST_H
+
+#include <stdint.h>
+
+#include <log/log_id.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
+
+/*
+ * The opaque context used to manipulate lists of events.
+ */
+#ifndef __android_log_context_defined
+#define __android_log_context_defined
+typedef struct android_log_context_internal* android_log_context;
+#endif
+
+/*
+ * Creates a context associated with an event tag to write elements to
+ * the list of events.
+ */
+android_log_context create_android_logger(uint32_t tag);
+
+/* All lists must be braced by a begin and end call */
+/*
+ * NB: If the first level braces are missing when specifying multiple
+ *     elements, we will manufacturer a list to embrace it for your API
+ *     convenience. For a single element, it will remain solitary.
+ */
+int android_log_write_list_begin(android_log_context ctx);
+int android_log_write_list_end(android_log_context ctx);
+
+int android_log_write_int32(android_log_context ctx, int32_t value);
+int android_log_write_int64(android_log_context ctx, int64_t value);
+int android_log_write_string8(android_log_context ctx, const char* value);
+int android_log_write_string8_len(android_log_context ctx, const char* value,
+                                  size_t maxlen);
+int android_log_write_float32(android_log_context ctx, float value);
+
+/* Submit the composed list context to the specified logger id */
+/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
+int android_log_write_list(android_log_context ctx, log_id_t id);
+
+/* Finished with reader or writer context */
+int android_log_destroy(android_log_context* ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_EVENT_LIST_H */
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 3c4c1f1..9d21e56 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -35,6 +35,17 @@
     android_logger_get_statistics; # vndk
     __android_log_error_write; # vndk
     __android_log_is_loggable;
+    create_android_logger; #vndk
+    android_log_destroy; #vndk
+    android_log_write_list_begin; #vndk
+    android_log_write_list_end; #vndk
+    android_log_write_int32; #vndk
+    android_log_write_int64; #vndk
+    android_log_write_string8; #vndk
+    android_log_write_string8_len; #vndk
+    android_log_write_float32; #vndk
+    android_log_write_list; #vndk
+
 };
 
 LIBLOG_O {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 892fb48..a7bdd2e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -171,6 +171,7 @@
     shared_libs: [
         "libbase",
         "libunwindstack",
+        "libdexfile",
     ],
 
     // libdexfile will eventually properly export headers, for now include
@@ -248,6 +249,7 @@
         "tests/files/elf64.xz",
         "tests/files/offline/bad_eh_frame_hdr_arm64/*",
         "tests/files/offline/debug_frame_first_x86/*",
+        "tests/files/offline/eh_frame_hdr_begin_x86_64/*",
         "tests/files/offline/jit_debug_arm/*",
         "tests/files/offline/jit_debug_x86/*",
         "tests/files/offline/gnu_debugdata_arm/*",
@@ -306,6 +308,15 @@
     ],
 }
 
+cc_binary {
+    name: "unwind_reg_info",
+    defaults: ["libunwindstack_tools"],
+
+    srcs: [
+        "tools/unwind_reg_info.cpp",
+    ],
+}
+
 // Generates the elf data for use in the tests for .gnu_debugdata frames.
 // Once these files are generated, use the xz command to compress the data.
 cc_binary_host {
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index a131abe..9a49013 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -109,7 +109,7 @@
     fde_info_.erase(index);
     return nullptr;
   }
-  info->pc = value + 4;
+  info->pc = value;
   return info;
 }
 
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
index dcf04e6..5bc60b9 100644
--- a/libunwindstack/DwarfOp.cpp
+++ b/libunwindstack/DwarfOp.cpp
@@ -36,13 +36,45 @@
 constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
 
 template <typename AddressType>
-bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end, uint8_t dwarf_version) {
-  uint32_t iterations = 0;
+bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end) {
   is_register_ = false;
   stack_.clear();
   memory_->set_cur_offset(start);
+  dex_pc_set_ = false;
+
+  // Unroll the first Decode calls to be able to check for a special
+  // sequence of ops and values that indicate this is the dex pc.
+  // The pattern is:
+  //   OP_const4u (0x0c)  'D' 'E' 'X' '1'
+  //   OP_drop (0x13)
+  if (memory_->cur_offset() < end) {
+    if (!Decode()) {
+      return false;
+    }
+  } else {
+    return true;
+  }
+  bool check_for_drop;
+  if (cur_op_ == 0x0c && operands_.back() == 0x31584544) {
+    check_for_drop = true;
+  } else {
+    check_for_drop = false;
+  }
+  if (memory_->cur_offset() < end) {
+    if (!Decode()) {
+      return false;
+    }
+  } else {
+    return true;
+  }
+
+  if (check_for_drop && cur_op_ == 0x13) {
+    dex_pc_set_ = true;
+  }
+
+  uint32_t iterations = 2;
   while (memory_->cur_offset() < end) {
-    if (!Decode(dwarf_version)) {
+    if (!Decode()) {
       return false;
     }
     // To protect against a branch that creates an infinite loop,
@@ -56,7 +88,7 @@
 }
 
 template <typename AddressType>
-bool DwarfOp<AddressType>::Decode(uint8_t dwarf_version) {
+bool DwarfOp<AddressType>::Decode() {
   last_error_.code = DWARF_ERROR_NONE;
   if (!memory_->ReadBytes(&cur_op_, 1)) {
     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
@@ -71,12 +103,6 @@
     return false;
   }
 
-  // Check for an unsupported opcode.
-  if (dwarf_version < op->supported_version) {
-    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_.code = DWARF_ERROR_STACK_INDEX_NOT_VALID;
@@ -434,22 +460,22 @@
 template <typename AddressType>
 bool DwarfOp<AddressType>::op_breg() {
   uint16_t reg = cur_op() - 0x70;
-  if (reg >= regs_->total_regs()) {
+  if (reg >= regs_info_->Total()) {
     last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
-  stack_.push_front((*regs_)[reg] + OperandAt(0));
+  stack_.push_front(regs_info_->Get(reg) + OperandAt(0));
   return true;
 }
 
 template <typename AddressType>
 bool DwarfOp<AddressType>::op_bregx() {
   AddressType reg = OperandAt(0);
-  if (reg >= regs_->total_regs()) {
+  if (reg >= regs_info_->Total()) {
     last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
-  stack_.push_front((*regs_)[reg] + OperandAt(1));
+  stack_.push_front(regs_info_->Get(reg) + OperandAt(1));
   return true;
 }
 
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index 40b7b23..4c69b3d 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -27,16 +27,10 @@
 #include <unwindstack/DwarfError.h>
 
 #include "DwarfEncoding.h"
+#include "RegsInfo.h"
 
 namespace unwindstack {
 
-enum DwarfVersion : uint8_t {
-  DWARF_VERSION_2 = 2,
-  DWARF_VERSION_3 = 3,
-  DWARF_VERSION_4 = 4,
-  DWARF_VERSION_MAX = DWARF_VERSION_4,
-};
-
 // Forward declarations.
 class DwarfMemory;
 class Memory;
@@ -51,7 +45,6 @@
   struct OpCallback {
     const char* name;
     bool (DwarfOp::*handle_func)();
-    uint8_t supported_version;
     uint8_t num_required_stack_values;
     uint8_t num_operands;
     uint8_t operands[2];
@@ -62,21 +55,23 @@
       : memory_(memory), regular_memory_(regular_memory) {}
   virtual ~DwarfOp() = default;
 
-  bool Decode(uint8_t dwarf_version);
+  bool Decode();
 
-  bool Eval(uint64_t start, uint64_t end, uint8_t dwarf_version);
+  bool Eval(uint64_t start, uint64_t end);
 
   void GetLogInfo(uint64_t start, uint64_t end, std::vector<std::string>* lines);
 
   AddressType StackAt(size_t index) { return stack_[index]; }
   size_t StackSize() { return stack_.size(); }
 
-  void set_regs(RegsImpl<AddressType>* regs) { regs_ = regs; }
+  void set_regs_info(RegsInfo<AddressType>* regs_info) { regs_info_ = regs_info; }
 
   const DwarfErrorData& last_error() { return last_error_; }
   DwarfErrorCode LastErrorCode() { return last_error_.code; }
   uint64_t LastErrorAddress() { return last_error_.address; }
 
+  bool dex_pc_set() { return dex_pc_set_; }
+
   bool is_register() { return is_register_; }
 
   uint8_t cur_op() { return cur_op_; }
@@ -97,7 +92,8 @@
   DwarfMemory* memory_;
   Memory* regular_memory_;
 
-  RegsImpl<AddressType>* regs_;
+  RegsInfo<AddressType>* regs_info_;
+  bool dex_pc_set_ = false;
   bool is_register_ = false;
   DwarfErrorData last_error_{DWARF_ERROR_NONE, 0};
   uint8_t cur_op_;
@@ -148,35 +144,32 @@
   bool op_not_implemented();
 
   constexpr static OpCallback kCallbackTable[256] = {
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x00 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x01 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x02 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x00 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x01 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x02 illegal op
       {
           // 0x03 DW_OP_addr
           "DW_OP_addr",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_absptr},
       },
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x04 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x05 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x04 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x05 illegal op
       {
           // 0x06 DW_OP_deref
           "DW_OP_deref",
           &DwarfOp::op_deref,
-          DWARF_VERSION_2,
           1,
           0,
           {},
       },
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x07 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x07 illegal op
       {
           // 0x08 DW_OP_const1u
           "DW_OP_const1u",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata1},
@@ -185,7 +178,6 @@
           // 0x09 DW_OP_const1s
           "DW_OP_const1s",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata1},
@@ -194,7 +186,6 @@
           // 0x0a DW_OP_const2u
           "DW_OP_const2u",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata2},
@@ -203,7 +194,6 @@
           // 0x0b DW_OP_const2s
           "DW_OP_const2s",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata2},
@@ -212,7 +202,6 @@
           // 0x0c DW_OP_const4u
           "DW_OP_const4u",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata4},
@@ -221,7 +210,6 @@
           // 0x0d DW_OP_const4s
           "DW_OP_const4s",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata4},
@@ -230,7 +218,6 @@
           // 0x0e DW_OP_const8u
           "DW_OP_const8u",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata8},
@@ -239,7 +226,6 @@
           // 0x0f DW_OP_const8s
           "DW_OP_const8s",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata8},
@@ -248,7 +234,6 @@
           // 0x10 DW_OP_constu
           "DW_OP_constu",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_uleb128},
@@ -257,7 +242,6 @@
           // 0x11 DW_OP_consts
           "DW_OP_consts",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -266,7 +250,6 @@
           // 0x12 DW_OP_dup
           "DW_OP_dup",
           &DwarfOp::op_dup,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -275,7 +258,6 @@
           // 0x13 DW_OP_drop
           "DW_OP_drop",
           &DwarfOp::op_drop,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -284,7 +266,6 @@
           // 0x14 DW_OP_over
           "DW_OP_over",
           &DwarfOp::op_over,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -293,7 +274,6 @@
           // 0x15 DW_OP_pick
           "DW_OP_pick",
           &DwarfOp::op_pick,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata1},
@@ -302,7 +282,6 @@
           // 0x16 DW_OP_swap
           "DW_OP_swap",
           &DwarfOp::op_swap,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -311,7 +290,6 @@
           // 0x17 DW_OP_rot
           "DW_OP_rot",
           &DwarfOp::op_rot,
-          DWARF_VERSION_2,
           3,
           0,
           {},
@@ -320,7 +298,6 @@
           // 0x18 DW_OP_xderef
           "DW_OP_xderef",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -329,7 +306,6 @@
           // 0x19 DW_OP_abs
           "DW_OP_abs",
           &DwarfOp::op_abs,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -338,7 +314,6 @@
           // 0x1a DW_OP_and
           "DW_OP_and",
           &DwarfOp::op_and,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -347,7 +322,6 @@
           // 0x1b DW_OP_div
           "DW_OP_div",
           &DwarfOp::op_div,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -356,7 +330,6 @@
           // 0x1c DW_OP_minus
           "DW_OP_minus",
           &DwarfOp::op_minus,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -365,7 +338,6 @@
           // 0x1d DW_OP_mod
           "DW_OP_mod",
           &DwarfOp::op_mod,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -374,7 +346,6 @@
           // 0x1e DW_OP_mul
           "DW_OP_mul",
           &DwarfOp::op_mul,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -383,7 +354,6 @@
           // 0x1f DW_OP_neg
           "DW_OP_neg",
           &DwarfOp::op_neg,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -392,7 +362,6 @@
           // 0x20 DW_OP_not
           "DW_OP_not",
           &DwarfOp::op_not,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -401,7 +370,6 @@
           // 0x21 DW_OP_or
           "DW_OP_or",
           &DwarfOp::op_or,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -410,7 +378,6 @@
           // 0x22 DW_OP_plus
           "DW_OP_plus",
           &DwarfOp::op_plus,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -419,7 +386,6 @@
           // 0x23 DW_OP_plus_uconst
           "DW_OP_plus_uconst",
           &DwarfOp::op_plus_uconst,
-          DWARF_VERSION_2,
           1,
           1,
           {DW_EH_PE_uleb128},
@@ -428,7 +394,6 @@
           // 0x24 DW_OP_shl
           "DW_OP_shl",
           &DwarfOp::op_shl,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -437,7 +402,6 @@
           // 0x25 DW_OP_shr
           "DW_OP_shr",
           &DwarfOp::op_shr,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -446,7 +410,6 @@
           // 0x26 DW_OP_shra
           "DW_OP_shra",
           &DwarfOp::op_shra,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -455,7 +418,6 @@
           // 0x27 DW_OP_xor
           "DW_OP_xor",
           &DwarfOp::op_xor,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -464,7 +426,6 @@
           // 0x28 DW_OP_bra
           "DW_OP_bra",
           &DwarfOp::op_bra,
-          DWARF_VERSION_2,
           1,
           1,
           {DW_EH_PE_sdata2},
@@ -473,7 +434,6 @@
           // 0x29 DW_OP_eq
           "DW_OP_eq",
           &DwarfOp::op_eq,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -482,7 +442,6 @@
           // 0x2a DW_OP_ge
           "DW_OP_ge",
           &DwarfOp::op_ge,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -491,7 +450,6 @@
           // 0x2b DW_OP_gt
           "DW_OP_gt",
           &DwarfOp::op_gt,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -500,7 +458,6 @@
           // 0x2c DW_OP_le
           "DW_OP_le",
           &DwarfOp::op_le,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -509,7 +466,6 @@
           // 0x2d DW_OP_lt
           "DW_OP_lt",
           &DwarfOp::op_lt,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -518,7 +474,6 @@
           // 0x2e DW_OP_ne
           "DW_OP_ne",
           &DwarfOp::op_ne,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -527,7 +482,6 @@
           // 0x2f DW_OP_skip
           "DW_OP_skip",
           &DwarfOp::op_skip,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata2},
@@ -536,7 +490,6 @@
           // 0x30 DW_OP_lit0
           "DW_OP_lit0",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -545,7 +498,6 @@
           // 0x31 DW_OP_lit1
           "DW_OP_lit1",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -554,7 +506,6 @@
           // 0x32 DW_OP_lit2
           "DW_OP_lit2",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -563,7 +514,6 @@
           // 0x33 DW_OP_lit3
           "DW_OP_lit3",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -572,7 +522,6 @@
           // 0x34 DW_OP_lit4
           "DW_OP_lit4",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -581,7 +530,6 @@
           // 0x35 DW_OP_lit5
           "DW_OP_lit5",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -590,7 +538,6 @@
           // 0x36 DW_OP_lit6
           "DW_OP_lit6",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -599,7 +546,6 @@
           // 0x37 DW_OP_lit7
           "DW_OP_lit7",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -608,7 +554,6 @@
           // 0x38 DW_OP_lit8
           "DW_OP_lit8",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -617,7 +562,6 @@
           // 0x39 DW_OP_lit9
           "DW_OP_lit9",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -626,7 +570,6 @@
           // 0x3a DW_OP_lit10
           "DW_OP_lit10",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -635,7 +578,6 @@
           // 0x3b DW_OP_lit11
           "DW_OP_lit11",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -644,7 +586,6 @@
           // 0x3c DW_OP_lit12
           "DW_OP_lit12",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -653,7 +594,6 @@
           // 0x3d DW_OP_lit13
           "DW_OP_lit13",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -662,7 +602,6 @@
           // 0x3e DW_OP_lit14
           "DW_OP_lit14",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -671,7 +610,6 @@
           // 0x3f DW_OP_lit15
           "DW_OP_lit15",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -680,7 +618,6 @@
           // 0x40 DW_OP_lit16
           "DW_OP_lit16",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -689,7 +626,6 @@
           // 0x41 DW_OP_lit17
           "DW_OP_lit17",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -698,7 +634,6 @@
           // 0x42 DW_OP_lit18
           "DW_OP_lit18",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -707,7 +642,6 @@
           // 0x43 DW_OP_lit19
           "DW_OP_lit19",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -716,7 +650,6 @@
           // 0x44 DW_OP_lit20
           "DW_OP_lit20",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -725,7 +658,6 @@
           // 0x45 DW_OP_lit21
           "DW_OP_lit21",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -734,7 +666,6 @@
           // 0x46 DW_OP_lit22
           "DW_OP_lit22",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -743,7 +674,6 @@
           // 0x47 DW_OP_lit23
           "DW_OP_lit23",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -752,7 +682,6 @@
           // 0x48 DW_OP_lit24
           "DW_OP_lit24",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -761,7 +690,6 @@
           // 0x49 DW_OP_lit25
           "DW_OP_lit25",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -770,7 +698,6 @@
           // 0x4a DW_OP_lit26
           "DW_OP_lit26",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -779,7 +706,6 @@
           // 0x4b DW_OP_lit27
           "DW_OP_lit27",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -788,7 +714,6 @@
           // 0x4c DW_OP_lit28
           "DW_OP_lit28",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -797,7 +722,6 @@
           // 0x4d DW_OP_lit29
           "DW_OP_lit29",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -806,7 +730,6 @@
           // 0x4e DW_OP_lit30
           "DW_OP_lit30",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -815,7 +738,6 @@
           // 0x4f DW_OP_lit31
           "DW_OP_lit31",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -824,7 +746,6 @@
           // 0x50 DW_OP_reg0
           "DW_OP_reg0",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -833,7 +754,6 @@
           // 0x51 DW_OP_reg1
           "DW_OP_reg1",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -842,7 +762,6 @@
           // 0x52 DW_OP_reg2
           "DW_OP_reg2",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -851,7 +770,6 @@
           // 0x53 DW_OP_reg3
           "DW_OP_reg3",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -860,7 +778,6 @@
           // 0x54 DW_OP_reg4
           "DW_OP_reg4",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -869,7 +786,6 @@
           // 0x55 DW_OP_reg5
           "DW_OP_reg5",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -878,7 +794,6 @@
           // 0x56 DW_OP_reg6
           "DW_OP_reg6",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -887,7 +802,6 @@
           // 0x57 DW_OP_reg7
           "DW_OP_reg7",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -896,7 +810,6 @@
           // 0x58 DW_OP_reg8
           "DW_OP_reg8",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -905,7 +818,6 @@
           // 0x59 DW_OP_reg9
           "DW_OP_reg9",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -914,7 +826,6 @@
           // 0x5a DW_OP_reg10
           "DW_OP_reg10",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -923,7 +834,6 @@
           // 0x5b DW_OP_reg11
           "DW_OP_reg11",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -932,7 +842,6 @@
           // 0x5c DW_OP_reg12
           "DW_OP_reg12",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -941,7 +850,6 @@
           // 0x5d DW_OP_reg13
           "DW_OP_reg13",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -950,7 +858,6 @@
           // 0x5e DW_OP_reg14
           "DW_OP_reg14",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -959,7 +866,6 @@
           // 0x5f DW_OP_reg15
           "DW_OP_reg15",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -968,7 +874,6 @@
           // 0x60 DW_OP_reg16
           "DW_OP_reg16",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -977,7 +882,6 @@
           // 0x61 DW_OP_reg17
           "DW_OP_reg17",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -986,7 +890,6 @@
           // 0x62 DW_OP_reg18
           "DW_OP_reg18",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -995,7 +898,6 @@
           // 0x63 DW_OP_reg19
           "DW_OP_reg19",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1004,7 +906,6 @@
           // 0x64 DW_OP_reg20
           "DW_OP_reg20",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1013,7 +914,6 @@
           // 0x65 DW_OP_reg21
           "DW_OP_reg21",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1022,7 +922,6 @@
           // 0x66 DW_OP_reg22
           "DW_OP_reg22",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1031,7 +930,6 @@
           // 0x67 DW_OP_reg23
           "DW_OP_reg23",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1040,7 +938,6 @@
           // 0x68 DW_OP_reg24
           "DW_OP_reg24",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1049,7 +946,6 @@
           // 0x69 DW_OP_reg25
           "DW_OP_reg25",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1058,7 +954,6 @@
           // 0x6a DW_OP_reg26
           "DW_OP_reg26",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1067,7 +962,6 @@
           // 0x6b DW_OP_reg27
           "DW_OP_reg27",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1076,7 +970,6 @@
           // 0x6c DW_OP_reg28
           "DW_OP_reg28",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1085,7 +978,6 @@
           // 0x6d DW_OP_reg29
           "DW_OP_reg29",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1094,7 +986,6 @@
           // 0x6e DW_OP_reg30
           "DW_OP_reg30",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1103,7 +994,6 @@
           // 0x6f DW_OP_reg31
           "DW_OP_reg31",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1112,7 +1002,6 @@
           // 0x70 DW_OP_breg0
           "DW_OP_breg0",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1121,7 +1010,6 @@
           // 0x71 DW_OP_breg1
           "DW_OP_breg1",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1130,7 +1018,6 @@
           // 0x72 DW_OP_breg2
           "DW_OP_breg2",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1139,7 +1026,6 @@
           // 0x73 DW_OP_breg3
           "DW_OP_breg3",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1148,7 +1034,6 @@
           // 0x74 DW_OP_breg4
           "DW_OP_breg4",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1157,7 +1042,6 @@
           // 0x75 DW_OP_breg5
           "DW_OP_breg5",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1166,7 +1050,6 @@
           // 0x76 DW_OP_breg6
           "DW_OP_breg6",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1175,7 +1058,6 @@
           // 0x77 DW_OP_breg7
           "DW_OP_breg7",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1184,7 +1066,6 @@
           // 0x78 DW_OP_breg8
           "DW_OP_breg8",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1193,7 +1074,6 @@
           // 0x79 DW_OP_breg9
           "DW_OP_breg9",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1202,7 +1082,6 @@
           // 0x7a DW_OP_breg10
           "DW_OP_breg10",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1211,7 +1090,6 @@
           // 0x7b DW_OP_breg11
           "DW_OP_breg11",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1220,7 +1098,6 @@
           // 0x7c DW_OP_breg12
           "DW_OP_breg12",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1229,7 +1106,6 @@
           // 0x7d DW_OP_breg13
           "DW_OP_breg13",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1238,7 +1114,6 @@
           // 0x7e DW_OP_breg14
           "DW_OP_breg14",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1247,7 +1122,6 @@
           // 0x7f DW_OP_breg15
           "DW_OP_breg15",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1256,7 +1130,6 @@
           // 0x80 DW_OP_breg16
           "DW_OP_breg16",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1265,7 +1138,6 @@
           // 0x81 DW_OP_breg17
           "DW_OP_breg17",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1274,7 +1146,6 @@
           // 0x82 DW_OP_breg18
           "DW_OP_breg18",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1283,7 +1154,6 @@
           // 0x83 DW_OP_breg19
           "DW_OP_breg19",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1292,7 +1162,6 @@
           // 0x84 DW_OP_breg20
           "DW_OP_breg20",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1301,7 +1170,6 @@
           // 0x85 DW_OP_breg21
           "DW_OP_breg21",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1310,7 +1178,6 @@
           // 0x86 DW_OP_breg22
           "DW_OP_breg22",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1319,7 +1186,6 @@
           // 0x87 DW_OP_breg23
           "DW_OP_breg23",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1328,7 +1194,6 @@
           // 0x88 DW_OP_breg24
           "DW_OP_breg24",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1337,7 +1202,6 @@
           // 0x89 DW_OP_breg25
           "DW_OP_breg25",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1346,7 +1210,6 @@
           // 0x8a DW_OP_breg26
           "DW_OP_breg26",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1355,7 +1218,6 @@
           // 0x8b DW_OP_breg27
           "DW_OP_breg27",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1364,7 +1226,6 @@
           // 0x8c DW_OP_breg28
           "DW_OP_breg28",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1373,7 +1234,6 @@
           // 0x8d DW_OP_breg29
           "DW_OP_breg29",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1382,7 +1242,6 @@
           // 0x8e DW_OP_breg30
           "DW_OP_breg30",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1391,7 +1250,6 @@
           // 0x8f DW_OP_breg31
           "DW_OP_breg31",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1400,7 +1258,6 @@
           // 0x90 DW_OP_regx
           "DW_OP_regx",
           &DwarfOp::op_regx,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_uleb128},
@@ -1409,7 +1266,6 @@
           // 0x91 DW_OP_fbreg
           "DW_OP_fbreg",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1418,7 +1274,6 @@
           // 0x92 DW_OP_bregx
           "DW_OP_bregx",
           &DwarfOp::op_bregx,
-          DWARF_VERSION_2,
           0,
           2,
           {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
@@ -1427,7 +1282,6 @@
           // 0x93 DW_OP_piece
           "DW_OP_piece",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_uleb128},
@@ -1436,7 +1290,6 @@
           // 0x94 DW_OP_deref_size
           "DW_OP_deref_size",
           &DwarfOp::op_deref_size,
-          DWARF_VERSION_2,
           1,
           1,
           {DW_EH_PE_udata1},
@@ -1445,7 +1298,6 @@
           // 0x95 DW_OP_xderef_size
           "DW_OP_xderef_size",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata1},
@@ -1454,7 +1306,6 @@
           // 0x96 DW_OP_nop
           "DW_OP_nop",
           &DwarfOp::op_nop,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1463,7 +1314,6 @@
           // 0x97 DW_OP_push_object_address
           "DW_OP_push_object_address",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           0,
           {},
@@ -1472,7 +1322,6 @@
           // 0x98 DW_OP_call2
           "DW_OP_call2",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           1,
           {DW_EH_PE_udata2},
@@ -1481,7 +1330,6 @@
           // 0x99 DW_OP_call4
           "DW_OP_call4",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           1,
           {DW_EH_PE_udata4},
@@ -1490,7 +1338,6 @@
           // 0x9a DW_OP_call_ref
           "DW_OP_call_ref",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           0,  // Has a different sized operand (4 bytes or 8 bytes).
           {},
@@ -1499,7 +1346,6 @@
           // 0x9b DW_OP_form_tls_address
           "DW_OP_form_tls_address",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           0,
           {},
@@ -1508,7 +1354,6 @@
           // 0x9c DW_OP_call_frame_cfa
           "DW_OP_call_frame_cfa",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           0,
           {},
@@ -1517,7 +1362,6 @@
           // 0x9d DW_OP_bit_piece
           "DW_OP_bit_piece",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           2,
           {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
@@ -1526,7 +1370,6 @@
           // 0x9e DW_OP_implicit_value
           "DW_OP_implicit_value",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_4,
           0,
           1,
           {DW_EH_PE_uleb128},
@@ -1535,107 +1378,106 @@
           // 0x9f DW_OP_stack_value
           "DW_OP_stack_value",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_4,
           1,
           0,
           {},
       },
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xaa illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xab illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xac illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xad illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xae illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xaf illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xba illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbc illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbd illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbe illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbf illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xca illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcc illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcd illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xce illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcf illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xda illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdc illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdd illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xde illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdf illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe0 DW_OP_lo_user
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xea illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xeb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xec illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xed illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xee illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xef illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfa illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfc illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfd illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfe illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xff DW_OP_hi_user
+      {nullptr, nullptr, 0, 0, {}},  // 0xa0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xaa illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xab illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xac illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xad illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xae illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xaf illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xba illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbc illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbd illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbe illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbf illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xca illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xcb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xcc illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xcd illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xce illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xcf illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xda illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xdb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xdc illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xdd illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xde illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xdf illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe0 DW_OP_lo_user
+      {nullptr, nullptr, 0, 0, {}},  // 0xe1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xea illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xeb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xec illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xed illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xee illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xef illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfa illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfc illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfd illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfe illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xff DW_OP_hi_user
   };
 };
 
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 4e94f88..7649798 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -26,16 +26,14 @@
 #include <unwindstack/Regs.h>
 
 #include "DwarfCfa.h"
-#include "DwarfEncoding.h"
-#include "DwarfOp.h"
-
 #include "DwarfDebugFrame.h"
 #include "DwarfEhFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfOp.h"
+#include "RegsInfo.h"
 
 namespace unwindstack {
 
-constexpr uint64_t DEX_PC_REG = 0x20444558;
-
 DwarfSection::DwarfSection(Memory* memory) : memory_(memory) {}
 
 const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
@@ -75,14 +73,17 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uint8_t version,
-                                                   Memory* regular_memory, AddressType* value) {
+bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, Memory* regular_memory,
+                                                   AddressType* value,
+                                                   RegsInfo<AddressType>* regs_info,
+                                                   bool* is_dex_pc) {
   DwarfOp<AddressType> op(&memory_, regular_memory);
+  op.set_regs_info(regs_info);
 
   // Need to evaluate the op data.
-  uint64_t start = loc.values[1];
-  uint64_t end = start + loc.values[0];
-  if (!op.Eval(start, end, version)) {
+  uint64_t end = loc.values[1];
+  uint64_t start = end - loc.values[0];
+  if (!op.Eval(start, end)) {
     last_error_ = op.last_error();
     return false;
   }
@@ -96,6 +97,9 @@
     return false;
   }
   *value = op.StackAt(0);
+  if (is_dex_pc != nullptr && op.dex_pc_set()) {
+    *is_dex_pc = true;
+  }
   return true;
 }
 
@@ -103,12 +107,10 @@
 struct EvalInfo {
   const dwarf_loc_regs_t* loc_regs;
   const DwarfCie* cie;
-  RegsImpl<AddressType>* cur_regs;
   Memory* regular_memory;
   AddressType cfa;
   bool return_address_undefined = false;
-  uint64_t reg_map = 0;
-  AddressType reg_values[64];
+  RegsInfo<AddressType> regs_info;
 };
 
 template <typename AddressType>
@@ -129,32 +131,18 @@
       break;
     case DWARF_LOCATION_REGISTER: {
       uint32_t cur_reg = loc->values[0];
-      if (cur_reg >= eval_info->cur_regs->total_regs()) {
+      if (cur_reg >= eval_info->regs_info.Total()) {
         last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
         return false;
       }
-      AddressType* cur_reg_ptr = &(*eval_info->cur_regs)[cur_reg];
-      const auto& entry = eval_info->loc_regs->find(cur_reg);
-      if (entry != eval_info->loc_regs->end()) {
-        if (!(eval_info->reg_map & (1 << cur_reg))) {
-          eval_info->reg_map |= 1 << cur_reg;
-          eval_info->reg_values[cur_reg] = *cur_reg_ptr;
-          if (!EvalRegister(&entry->second, cur_reg, cur_reg_ptr, eval_info)) {
-            return false;
-          }
-        }
-
-        // Use the register value from before any evaluations.
-        *reg_ptr = eval_info->reg_values[cur_reg] + loc->values[1];
-      } else {
-        *reg_ptr = *cur_reg_ptr + loc->values[1];
-      }
+      *reg_ptr = eval_info->regs_info.Get(cur_reg) + loc->values[1];
       break;
     }
     case DWARF_LOCATION_EXPRESSION:
     case DWARF_LOCATION_VAL_EXPRESSION: {
       AddressType value;
-      if (!EvalExpression(*loc, eval_info->cie->version, regular_memory, &value)) {
+      bool is_dex_pc = false;
+      if (!EvalExpression(*loc, regular_memory, &value, &eval_info->regs_info, &is_dex_pc)) {
         return false;
       }
       if (loc->type == DWARF_LOCATION_EXPRESSION) {
@@ -165,6 +153,9 @@
         }
       } else {
         *reg_ptr = value;
+        if (is_dex_pc) {
+          eval_info->regs_info.regs->set_dex_pc(value);
+        }
       }
       break;
     }
@@ -201,8 +192,10 @@
 
   AddressType prev_cfa = regs->sp();
 
-  EvalInfo<AddressType> eval_info{
-      .loc_regs = &loc_regs, .cie = cie, .regular_memory = regular_memory, .cur_regs = cur_regs};
+  EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
+                                  .cie = cie,
+                                  .regular_memory = regular_memory,
+                                  .regs_info = RegsInfo<AddressType>(cur_regs)};
   const DwarfLocation* loc = &cfa_entry->second;
   // Only a few location types are valid for the cfa.
   switch (loc->type) {
@@ -224,7 +217,7 @@
     case DWARF_LOCATION_EXPRESSION:
     case DWARF_LOCATION_VAL_EXPRESSION: {
       AddressType value;
-      if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
+      if (!EvalExpression(*loc, regular_memory, &value, &eval_info.regs_info, nullptr)) {
         return false;
       }
       if (loc->type == DWARF_LOCATION_EXPRESSION) {
@@ -249,28 +242,15 @@
     if (reg == CFA_REG) continue;
 
     AddressType* reg_ptr;
-    AddressType dex_pc = 0;
-    if (reg == DEX_PC_REG) {
-      // Special register that indicates this is a dex pc.
-      dex_pc = 0;
-      reg_ptr = &dex_pc;
-    } else if (reg >= cur_regs->total_regs() || eval_info.reg_map & (1 << reg)) {
-      // Skip this unknown register, or a register that has already been
-      // processed.
+    if (reg >= cur_regs->total_regs()) {
+      // Skip this unknown register.
       continue;
-    } else {
-      reg_ptr = &(*cur_regs)[reg];
-      eval_info.reg_map |= 1 << reg;
-      eval_info.reg_values[reg] = *reg_ptr;
     }
 
+    reg_ptr = eval_info.regs_info.Save(reg);
     if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
       return false;
     }
-
-    if (reg == DEX_PC_REG) {
-      cur_regs->set_dex_pc(dex_pc);
-    }
   }
 
   // Find the return address location.
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index dbf772e..02f8a9a 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -20,6 +20,7 @@
 #include <memory>
 #include <mutex>
 #include <string>
+#include <utility>
 
 #define LOG_TAG "unwind"
 #include <log/log.h>
@@ -36,7 +37,7 @@
 namespace unwindstack {
 
 bool Elf::cache_enabled_;
-std::unordered_map<std::string, std::shared_ptr<Elf>>* Elf::cache_;
+std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* Elf::cache_;
 std::mutex* Elf::cache_lock_;
 
 bool Elf::Init(bool init_gnu_debugdata) {
@@ -308,7 +309,7 @@
 void Elf::SetCachingEnabled(bool enable) {
   if (!cache_enabled_ && enable) {
     cache_enabled_ = true;
-    cache_ = new std::unordered_map<std::string, std::shared_ptr<Elf>>;
+    cache_ = new std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>;
     cache_lock_ = new std::mutex;
   } else if (cache_enabled_ && !enable) {
     cache_enabled_ = false;
@@ -326,18 +327,54 @@
 }
 
 void Elf::CacheAdd(MapInfo* info) {
-  if (info->offset == 0) {
-    (*cache_)[info->name] = info->elf;
-  } else {
-    std::string name(info->name + ':' + std::to_string(info->offset));
-    (*cache_)[name] = info->elf;
+  // If elf_offset != 0, then cache both name:offset and name.
+  // The cached name is used to do lookups if multiple maps for the same
+  // named elf file exist.
+  // For example, if there are two maps boot.odex:1000 and boot.odex:2000
+  // where each reference the entire boot.odex, the cache will properly
+  // use the same cached elf object.
+
+  if (info->offset == 0 || info->elf_offset != 0) {
+    (*cache_)[info->name] = std::make_pair(info->elf, true);
+  }
+
+  if (info->offset != 0) {
+    // The second element in the pair indicates whether elf_offset should
+    // be set to offset when getting out of the cache.
+    (*cache_)[info->name + ':' + std::to_string(info->offset)] =
+        std::make_pair(info->elf, info->elf_offset != 0);
   }
 }
 
-bool Elf::CacheGet(const std::string& name, std::shared_ptr<Elf>* elf) {
+bool Elf::CacheAfterCreateMemory(MapInfo* info) {
+  if (info->name.empty() || info->offset == 0 || info->elf_offset == 0) {
+    return false;
+  }
+
+  auto entry = cache_->find(info->name);
+  if (entry == cache_->end()) {
+    return false;
+  }
+
+  // In this case, the whole file is the elf, and the name has already
+  // been cached. Add an entry at name:offset to get this directly out
+  // of the cache next time.
+  info->elf = entry->second.first;
+  (*cache_)[info->name + ':' + std::to_string(info->offset)] = std::make_pair(info->elf, true);
+  return true;
+}
+
+bool Elf::CacheGet(MapInfo* info) {
+  std::string name(info->name);
+  if (info->offset != 0) {
+    name += ':' + std::to_string(info->offset);
+  }
   auto entry = cache_->find(name);
   if (entry != cache_->end()) {
-    *elf = entry->second;
+    info->elf = entry->second.first;
+    if (entry->second.second) {
+      info->elf_offset = info->offset;
+    }
     return true;
   }
   return false;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index e413081..10afe33 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -19,6 +19,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 
 #include <7zCrc.h>
 #include <Xz.h>
@@ -322,19 +323,13 @@
   // Skip the first header, it's always going to be NULL.
   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))) {
+    if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
       last_error_.code = ERROR_MEMORY_INVALID;
-      last_error_.address =
-          offset + reinterpret_cast<uintptr_t>(&shdr.sh_type) - reinterpret_cast<uintptr_t>(&shdr);
+      last_error_.address = offset;
       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;
@@ -343,39 +338,19 @@
         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))) {
+      if (!memory_->Read(str_offset, &str_shdr, sizeof(str_shdr))) {
         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);
+        last_error_.address = str_offset;
         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,
                                      str_shdr.sh_offset, str_shdr.sh_size));
     } 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) {
         std::string name;
         if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
@@ -394,14 +369,16 @@
             offset_ptr = &eh_frame_hdr_offset_;
             size_ptr = &eh_frame_hdr_size_;
           }
-          if (offset_ptr != nullptr &&
-              memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
-              memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+          if (offset_ptr != nullptr) {
             *offset_ptr = shdr.sh_offset;
             *size_ptr = shdr.sh_size;
           }
         }
       }
+    } else if (shdr.sh_type == SHT_STRTAB) {
+      // In order to read soname, keep track of address to offset mapping.
+      strtabs_.push_back(std::make_pair<uint64_t, uint64_t>(static_cast<uint64_t>(shdr.sh_addr),
+                                                            static_cast<uint64_t>(shdr.sh_offset)));
     }
   }
   return true;
@@ -420,7 +397,7 @@
   soname_type_ = SONAME_INVALID;
 
   uint64_t soname_offset = 0;
-  uint64_t strtab_offset = 0;
+  uint64_t strtab_addr = 0;
   uint64_t strtab_size = 0;
 
   // Find the soname location from the dynamic headers section.
@@ -435,7 +412,7 @@
     }
 
     if (dyn.d_tag == DT_STRTAB) {
-      strtab_offset = dyn.d_un.d_ptr;
+      strtab_addr = dyn.d_un.d_ptr;
     } else if (dyn.d_tag == DT_STRSZ) {
       strtab_size = dyn.d_un.d_val;
     } else if (dyn.d_tag == DT_SONAME) {
@@ -445,16 +422,22 @@
     }
   }
 
-  soname_offset += strtab_offset;
-  if (soname_offset >= strtab_offset + strtab_size) {
-    return false;
+  // Need to map the strtab address to the real offset.
+  for (const auto& entry : strtabs_) {
+    if (entry.first == strtab_addr) {
+      soname_offset = entry.second + soname_offset;
+      if (soname_offset >= entry.second + strtab_size) {
+        return false;
+      }
+      if (!memory_->ReadString(soname_offset, &soname_)) {
+        return false;
+      }
+      soname_type_ = SONAME_VALID;
+      *soname = soname_;
+      return true;
+    }
   }
-  if (!memory_->ReadString(soname_offset, &soname_)) {
-    return false;
-  }
-  soname_type_ = SONAME_VALID;
-  *soname = soname_;
-  return true;
+  return false;
 }
 
 template <typename SymType>
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 0c15335..39378a3 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -117,23 +117,15 @@
   if (Elf::CachingEnabled() && !name.empty()) {
     Elf::CacheLock();
     locked = true;
-    if (offset != 0) {
-      std::string hash(name + ':' + std::to_string(offset));
-      if (Elf::CacheGet(hash, &elf)) {
-        Elf::CacheUnlock();
-        return elf.get();
-      }
-    } else if (Elf::CacheGet(name, &elf)) {
+    if (Elf::CacheGet(this)) {
       Elf::CacheUnlock();
       return elf.get();
     }
   }
 
   Memory* memory = CreateMemory(process_memory);
-  if (locked && offset != 0 && elf_offset != 0) {
-    // In this case, the whole file is the elf, need to see if the elf
-    // data was cached.
-    if (Elf::CacheGet(name, &elf)) {
+  if (locked) {
+    if (Elf::CacheAfterCreateMemory(this)) {
       delete memory;
       Elf::CacheUnlock();
       return elf.get();
diff --git a/libunwindstack/RegsInfo.h b/libunwindstack/RegsInfo.h
new file mode 100644
index 0000000..47825f5
--- /dev/null
+++ b/libunwindstack/RegsInfo.h
@@ -0,0 +1,66 @@
+/*
+ * 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_REGS_INFO_H
+#define _LIBUNWINDSTACK_REGS_INFO_H
+
+#include <stdint.h>
+
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+template <typename AddressType>
+struct RegsInfo {
+  RegsInfo(RegsImpl<AddressType>* regs) : regs(regs) {}
+
+  RegsImpl<AddressType>* regs = nullptr;
+  uint64_t saved_reg_map = 0;
+  AddressType saved_regs[64];
+
+  inline AddressType Get(uint32_t reg) {
+    if (IsSaved(reg)) {
+      return saved_regs[reg];
+    }
+    return (*regs)[reg];
+  }
+
+  inline AddressType* Save(uint32_t reg) {
+    if (reg > sizeof(saved_regs) / sizeof(AddressType)) {
+      // This should never happen as since all currently supported
+      // architectures have the total number of registers < 64.
+      abort();
+    }
+    saved_reg_map |= 1 << reg;
+    saved_regs[reg] = (*regs)[reg];
+    return &(*regs)[reg];
+  }
+
+  inline bool IsSaved(uint32_t reg) {
+    if (reg > sizeof(saved_regs) / sizeof(AddressType)) {
+      // This should never happen as since all currently supported
+      // architectures have the total number of registers < 64.
+      abort();
+    }
+    return saved_reg_map & (1 << reg);
+  }
+
+  inline uint16_t Total() { return regs->total_regs(); }
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_INFO_H
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 644bfa8..d52a0bf 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -48,6 +48,7 @@
   size_t frame_num = frames_.size();
   frames_.resize(frame_num + 1);
   FrameData* frame = &frames_.at(frame_num);
+  frame->num = frame_num;
 
   uint64_t dex_pc = regs_->dex_pc();
   frame->pc = dex_pc;
@@ -91,7 +92,7 @@
     return;
   }
 
-  frame->pc = map_info->start + adjusted_rel_pc;
+  frame->pc = map_info->start + adjusted_rel_pc - elf->GetLoadBias() - map_info->elf_offset;
   frame->map_name = map_info->name;
   frame->map_offset = map_info->offset;
   frame->map_start = map_info->start;
@@ -175,6 +176,8 @@
       if (regs_->dex_pc() != 0) {
         // Add a frame to represent the dex file.
         FillInDexFrame();
+        // Clear the dex pc so that we don't repeat this frame later.
+        regs_->set_dex_pc(0);
       }
 
       FillInFrame(map_info, elf, adjusted_rel_pc, adjusted_pc);
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 03f40d6..da91fd0 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -32,6 +32,8 @@
 // Forward declarations.
 class Memory;
 class Regs;
+template <typename AddressType>
+struct RegsInfo;
 
 class DwarfSection {
  public:
@@ -149,8 +151,8 @@
   bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) override;
 
  protected:
-  bool EvalExpression(const DwarfLocation& loc, uint8_t version, Memory* regular_memory,
-                      AddressType* value);
+  bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
+                      RegsInfo<AddressType>* regs_info, bool* is_dex_pc);
 
   bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
 
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index a874709..385973e 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -23,6 +23,7 @@
 #include <mutex>
 #include <string>
 #include <unordered_map>
+#include <utility>
 
 #include <unwindstack/ElfInterface.h>
 #include <unwindstack/Memory.h>
@@ -103,7 +104,8 @@
   static void CacheLock();
   static void CacheUnlock();
   static void CacheAdd(MapInfo* info);
-  static bool CacheGet(const std::string& name, std::shared_ptr<Elf>* elf);
+  static bool CacheGet(MapInfo* info);
+  static bool CacheAfterCreateMemory(MapInfo* info);
 
  protected:
   bool valid_ = false;
@@ -120,7 +122,7 @@
   std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
 
   static bool cache_enabled_;
-  static std::unordered_map<std::string, std::shared_ptr<Elf>>* cache_;
+  static std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* cache_;
   static std::mutex* cache_lock_;
 };
 
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index ea9ec9d..3a221bc 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -157,6 +157,7 @@
   ElfInterface* gnu_debugdata_interface_ = nullptr;
 
   std::vector<Symbols*> symbols_;
+  std::vector<std::pair<uint64_t, uint64_t>> strtabs_;
 };
 
 class ElfInterface32 : public ElfInterface {
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index a2ae5eb..4240419 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -134,7 +134,7 @@
 
   auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
   ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x1384U, info->pc);
+  EXPECT_EQ(0x1380U, info->pc);
   EXPECT_EQ(0x1540U, info->offset);
 }
 
@@ -149,7 +149,7 @@
 
   auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
   ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x3344U, info->pc);
+  EXPECT_EQ(0x3340U, info->pc);
   EXPECT_EQ(0x3500U, info->offset);
 }
 
@@ -163,7 +163,7 @@
 
   auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
   ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x344U, info->pc);
+  EXPECT_EQ(0x340U, info->pc);
   EXPECT_EQ(0x500U, info->offset);
 
   // Clear the memory so that this will fail if it doesn't read cached data.
@@ -171,7 +171,7 @@
 
   info = this->eh_frame_->GetFdeInfoFromIndex(2);
   ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x344U, info->pc);
+  EXPECT_EQ(0x340U, info->pc);
   EXPECT_EQ(0x500U, info->offset);
 }
 
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 036226d..6e15227 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -52,14 +52,14 @@
 
 TYPED_TEST_P(DwarfOpTest, decode) {
   // Memory error.
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   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_TRUE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_NONE, this->op_->LastErrorCode());
   ASSERT_EQ(0x96U, this->op_->cur_op());
   ASSERT_EQ(1U, this->mem_->cur_offset());
@@ -67,14 +67,14 @@
 
 TYPED_TEST_P(DwarfOpTest, eval) {
   // Memory error.
-  ASSERT_FALSE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Eval(0, 2));
   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.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x50});
-  ASSERT_TRUE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 1));
   ASSERT_TRUE(this->op_->is_register());
   ASSERT_EQ(1U, this->mem_->cur_offset());
   ASSERT_EQ(1U, this->op_->StackSize());
@@ -85,7 +85,7 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Eval(0, 8, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 8));
   ASSERT_EQ(DWARF_ERROR_NONE, this->op_->LastErrorCode());
   ASSERT_FALSE(this->op_->is_register());
   ASSERT_EQ(8U, this->mem_->cur_offset());
@@ -97,7 +97,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_FALSE(this->op_->Eval(0, 4));
   ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->LastErrorCode());
   ASSERT_FALSE(this->op_->is_register());
   ASSERT_EQ(0U, this->op_->StackSize());
@@ -112,29 +112,7 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   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_->LastErrorCode());
-    ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
-  }
-}
-
-TYPED_TEST_P(DwarfOpTest, illegal_in_version3) {
-  std::vector<uint8_t> opcode_buffer = {0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d};
-  this->op_memory_.SetMemory(0, opcode_buffer);
-
-  for (size_t i = 0; i < opcode_buffer.size(); i++) {
-    ASSERT_FALSE(this->op_->Decode(2));
-    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
-    ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
-  }
-}
-
-TYPED_TEST_P(DwarfOpTest, illegal_in_version4) {
-  std::vector<uint8_t> opcode_buffer = {0x9e, 0x9f};
-  this->op_memory_.SetMemory(0, opcode_buffer);
-
-  for (size_t i = 0; i < opcode_buffer.size(); i++) {
-    ASSERT_FALSE(this->op_->Decode(3));
+    ASSERT_FALSE(this->op_->Decode());
     ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
     ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
   }
@@ -174,12 +152,12 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // Push the stack values.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
+  ASSERT_TRUE(this->op_->Decode());
+  ASSERT_TRUE(this->op_->Decode());
 
   while (this->mem_->cur_offset() < opcode_buffer.size()) {
-    ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+    ASSERT_FALSE(this->op_->Decode());
     ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->LastErrorCode());
   }
 }
@@ -194,7 +172,7 @@
   }
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x03, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -217,17 +195,17 @@
   TypeParam value = 0x12345678;
   this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x06, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(value, this->op_->StackAt(0));
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
   ASSERT_EQ(0x12345678U, this->op_->LastErrorAddress());
 }
@@ -237,14 +215,14 @@
   TypeParam value = 0x12345678;
   this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   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++) {
     this->op_memory_.SetMemory(
         0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, static_cast<uint8_t>(i)});
-    ASSERT_TRUE(this->op_->Eval(0, 5, DWARF_VERSION_MAX)) << "Failed at size " << i;
+    ASSERT_TRUE(this->op_->Eval(0, 5)) << "Failed at size " << i;
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed at size " << i;
     ASSERT_EQ(0x94, this->op_->cur_op()) << "Failed at size " << i;
     TypeParam expected_value = 0;
@@ -254,17 +232,17 @@
 
   // 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_FALSE(this->op_->Eval(0, 5));
   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_FALSE(this->op_->Eval(0, 5));
   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_FALSE(this->op_->Eval(0, 5));
   ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
   EXPECT_EQ(0x4010U, this->op_->LastErrorAddress());
 }
@@ -284,40 +262,40 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // const1u
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x08, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x12U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x08, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0xffU, this->op_->StackAt(0));
 
   // const2u
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0a, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x1245U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0a, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(0xff00U, this->op_->StackAt(0));
 
   // const4u
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0c, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0c, this->op_->cur_op());
   ASSERT_EQ(6U, this->op_->StackSize());
   ASSERT_EQ(0xff010203U, this->op_->StackAt(0));
 
   // const8u
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0e, this->op_->cur_op());
   ASSERT_EQ(7U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -326,7 +304,7 @@
     ASSERT_EQ(0x0102030405060708ULL, this->op_->StackAt(0));
   }
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0e, this->op_->cur_op());
   ASSERT_EQ(8U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -351,40 +329,40 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // const1s
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x09, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x12U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x09, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
 
   // const2s
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0b, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x3221U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0b, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-248), this->op_->StackAt(0));
 
   // const4s
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0d, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   ASSERT_EQ(0x12233445U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0d, this->op_->cur_op());
   ASSERT_EQ(6U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-16580095), this->op_->StackAt(0));
 
   // const8s
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0f, this->op_->cur_op());
   ASSERT_EQ(7U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -393,7 +371,7 @@
     ASSERT_EQ(0x1223344556677889ULL, this->op_->StackAt(0));
   }
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0f, this->op_->cur_op());
   ASSERT_EQ(8U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -414,28 +392,28 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // Single byte ULEB128
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x22U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x7fU, this->op_->StackAt(0));
 
   // Multi byte ULEB128
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x1122U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(0x3a22U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -444,7 +422,7 @@
     ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
   }
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(6U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -480,28 +458,28 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // Single byte SLEB128
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x22U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
 
   // Multi byte SLEB128
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x1122U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-1502), this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -510,7 +488,7 @@
     ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
   }
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(6U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -531,21 +509,21 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x12, this->op_->cur_op());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x12, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x15U, this->op_->StackAt(0));
   ASSERT_EQ(0x15U, this->op_->StackAt(1));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x12, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(0x23U, this->op_->StackAt(0));
@@ -565,21 +543,21 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x13, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x10U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x13, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x13, this->op_->cur_op());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
@@ -597,24 +575,24 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x14, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x1aU, this->op_->StackAt(0));
   ASSERT_EQ(0xedU, this->op_->StackAt(1));
   ASSERT_EQ(0x1aU, this->op_->StackAt(2));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x14, this->op_->cur_op());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
@@ -632,14 +610,14 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x15, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(0xedU, this->op_->StackAt(0));
@@ -647,7 +625,7 @@
   ASSERT_EQ(0xedU, this->op_->StackAt(2));
   ASSERT_EQ(0x1aU, this->op_->StackAt(3));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x15, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   ASSERT_EQ(0x1aU, this->op_->StackAt(0));
@@ -656,7 +634,7 @@
   ASSERT_EQ(0xedU, this->op_->StackAt(3));
   ASSERT_EQ(0x1aU, this->op_->StackAt(4));
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x15, this->op_->cur_op());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
@@ -672,23 +650,23 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0xabU, this->op_->StackAt(0));
   ASSERT_EQ(0x26U, this->op_->StackAt(1));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x16, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x26U, this->op_->StackAt(0));
   ASSERT_EQ(0xabU, this->op_->StackAt(1));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x16, this->op_->cur_op());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
@@ -706,28 +684,28 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x30U, this->op_->StackAt(0));
   ASSERT_EQ(0x20U, this->op_->StackAt(1));
   ASSERT_EQ(0x10U, this->op_->StackAt(2));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x17, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x20U, this->op_->StackAt(0));
@@ -756,30 +734,30 @@
   opcode_buffer.push_back(0x19);
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x10U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x19, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x10U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x19, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x1U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x19, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -808,56 +786,56 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   // Two positive values.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1b, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x10U, this->op_->StackAt(0));
 
   // Two negative values.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1b, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x04U, this->op_->StackAt(0));
 
   // One negative value, one positive value.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(4U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1b, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-4), this->op_->StackAt(0));
 
   // Divide by zero.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(4U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(5U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
@@ -874,19 +852,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1a, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x40U, this->op_->StackAt(0));
@@ -905,19 +883,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1c, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x44U, this->op_->StackAt(0));
@@ -938,29 +916,29 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1d, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x03U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
@@ -977,19 +955,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1e, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x120U, this->op_->StackAt(0));
@@ -1006,21 +984,21 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1f, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-72), this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1f, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x01U, this->op_->StackAt(0));
@@ -1037,21 +1015,21 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x20, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-5), this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x20, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x03U, this->op_->StackAt(0));
@@ -1070,19 +1048,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x21, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0xfcU, this->op_->StackAt(0));
@@ -1101,19 +1079,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x22, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x1f1U, this->op_->StackAt(0));
@@ -1128,13 +1106,13 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x23, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x28d0U, this->op_->StackAt(0));
@@ -1153,19 +1131,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x24, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x338U, this->op_->StackAt(0));
@@ -1184,19 +1162,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x25, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -1219,19 +1197,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x26, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-2), this->op_->StackAt(0));
@@ -1250,19 +1228,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x27, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x50U, this->op_->StackAt(0));
@@ -1283,48 +1261,48 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   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));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
   uint64_t offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x28, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset + 0x102, this->mem_->cur_offset());
 
   // Push on a zero value with a positive branch.
   this->mem_->set_cur_offset(offset);
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
   offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x28, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset - 5, this->mem_->cur_offset());
 
   // Push on a non-zero value with a negative branch.
   this->mem_->set_cur_offset(offset);
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
   offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x28, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset - 4, this->mem_->cur_offset());
 
   // Push on a zero value with a negative branch.
   this->mem_->set_cur_offset(offset);
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
   offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x28, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset + 16, this->mem_->cur_offset());
@@ -1344,11 +1322,11 @@
     opcode_buffer[3] = opcode;
     this->op_memory_.SetMemory(0, opcode_buffer);
 
-    ASSERT_FALSE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+    ASSERT_FALSE(this->op_->Eval(0, 1));
     ASSERT_EQ(opcode, this->op_->cur_op());
     ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-    ASSERT_FALSE(this->op_->Eval(1, 4, DWARF_VERSION_MAX));
+    ASSERT_FALSE(this->op_->Eval(1, 4));
     ASSERT_EQ(opcode, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize());
     ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
@@ -1387,7 +1365,7 @@
     opcode_buffer[14] = expected[i];
     this->op_memory_.SetMemory(0, opcode_buffer);
 
-    ASSERT_TRUE(this->op_->Eval(0, 15, DWARF_VERSION_MAX))
+    ASSERT_TRUE(this->op_->Eval(0, 15))
         << "Op: 0x" << std::hex << static_cast<uint32_t>(expected[i]) << " failed";
 
     ASSERT_EQ(3U, this->op_->StackSize());
@@ -1407,14 +1385,14 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   uint64_t offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x2f, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset + 0x2010, this->mem_->cur_offset());
 
   this->mem_->set_cur_offset(offset);
   offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x2f, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset - 3, this->mem_->cur_offset());
@@ -1431,7 +1409,7 @@
 
   for (size_t i = 0; i < opcode_buffer.size(); i++) {
     uint32_t op = opcode_buffer[i];
-    ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+    ASSERT_TRUE(this->op_->Eval(i, i + 1)) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op - 0x30U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
@@ -1449,7 +1427,7 @@
 
   for (size_t i = 0; i < opcode_buffer.size(); i++) {
     uint32_t op = opcode_buffer[i];
-    ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+    ASSERT_TRUE(this->op_->Eval(i, i + 1)) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op, this->op_->cur_op());
     ASSERT_TRUE(this->op_->is_register()) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
@@ -1463,13 +1441,13 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 2));
   ASSERT_EQ(0x90, this->op_->cur_op());
   ASSERT_TRUE(this->op_->is_register());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x02U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Eval(2, 5, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(2, 5));
   ASSERT_EQ(0x90, this->op_->cur_op());
   ASSERT_TRUE(this->op_->is_register());
   ASSERT_EQ(1U, this->op_->StackSize());
@@ -1494,21 +1472,20 @@
   for (size_t i = 0; i < 32; i++) {
     regs[i] = i + 10;
   }
-  this->op_->set_regs(&regs);
+  RegsInfo<TypeParam> regs_info(&regs);
+  this->op_->set_regs_info(&regs_info);
 
   uint64_t offset = 0;
   for (uint32_t op = 0x70; op <= 0x8f; op++) {
     // Positive value added to register.
-    ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
-                                                                        << std::hex << op;
+    ASSERT_TRUE(this->op_->Eval(offset, offset + 2)) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op - 0x70 + 10 + 0x12, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
     offset += 2;
 
     // Negative value added to register.
-    ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
-                                                                        << std::hex << op;
+    ASSERT_TRUE(this->op_->Eval(offset, offset + 2)) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op - 0x70 + 10 - 2, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
@@ -1526,16 +1503,17 @@
   for (size_t i = 0; i < 16; i++) {
     regs[i] = i + 10;
   }
-  this->op_->set_regs(&regs);
+  RegsInfo<TypeParam> regs_info(&regs);
+  this->op_->set_regs_info(&regs_info);
 
   // Should pass since this references the last regsister.
-  ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 2));
   ASSERT_EQ(0x7fU, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x2bU, this->op_->StackAt(0));
 
   // Should fail since this references a non-existent register.
-  ASSERT_FALSE(this->op_->Eval(2, 4, DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Eval(2, 4));
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
@@ -1551,38 +1529,55 @@
   RegsImplFake<TypeParam> regs(10, 10);
   regs[5] = 0x45;
   regs[6] = 0x190;
-  this->op_->set_regs(&regs);
+  RegsInfo<TypeParam> regs_info(&regs);
+  this->op_->set_regs_info(&regs_info);
 
-  ASSERT_TRUE(this->op_->Eval(0, 3, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 3));
   ASSERT_EQ(0x92, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x65U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Eval(3, 7, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(3, 7));
   ASSERT_EQ(0x92, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x90U, this->op_->StackAt(0));
 
-  ASSERT_FALSE(this->op_->Eval(7, 12, DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Eval(7, 12));
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_nop) {
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x96, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, illegal_in_version3,
-                           illegal_in_version4, not_implemented, op_addr, op_deref, op_deref_size,
-                           const_unsigned, const_signed, const_uleb, const_sleb, op_dup, op_drop,
-                           op_over, op_pick, op_swap, op_rot, op_abs, op_and, op_div, op_minus,
-                           op_mod, op_mul, op_neg, op_not, op_or, op_plus, op_plus_uconst, op_shl,
-                           op_shr, op_shra, op_xor, op_bra, compare_opcode_stack_error,
-                           compare_opcodes, op_skip, op_lit, op_reg, op_regx, op_breg,
-                           op_breg_invalid_register, op_bregx, op_nop);
+TYPED_TEST_P(DwarfOpTest, is_dex_pc) {
+  // Special sequence that indicates this is a dex pc.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '1', 0x13});
+
+  ASSERT_TRUE(this->op_->Eval(0, 6));
+  EXPECT_TRUE(this->op_->dex_pc_set());
+
+  // Try without the last op.
+  ASSERT_TRUE(this->op_->Eval(0, 5));
+  EXPECT_FALSE(this->op_->dex_pc_set());
+
+  // Change the constant.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '2', 0x13});
+  ASSERT_TRUE(this->op_->Eval(0, 6));
+  EXPECT_FALSE(this->op_->dex_pc_set());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, not_implemented, op_addr,
+                           op_deref, op_deref_size, const_unsigned, const_signed, const_uleb,
+                           const_sleb, op_dup, op_drop, op_over, op_pick, op_swap, op_rot, op_abs,
+                           op_and, op_div, op_minus, op_mod, op_mul, op_neg, op_not, op_or, op_plus,
+                           op_plus_uconst, op_shl, op_shr, op_shra, op_xor, op_bra,
+                           compare_opcode_stack_error, compare_opcodes, op_skip, op_lit, op_reg,
+                           op_regx, op_breg, op_breg_invalid_register, op_bregx, op_nop, is_dex_pc);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 7e10935..37305b2 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -99,7 +99,7 @@
   regs.set_sp(0x2000);
   regs[5] = 0x20;
   regs[9] = 0x3000;
-  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
@@ -116,7 +116,7 @@
   regs[5] = 0x20;
   regs[9] = 0x3000;
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
-  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->LastErrorCode());
@@ -134,7 +134,7 @@
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
   TypeParam cfa_value = 0x12345;
   this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
-  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
   bool finished;
   ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_FALSE(finished);
@@ -152,7 +152,7 @@
   regs[5] = 0x20;
   regs[9] = 0x3000;
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
-  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
   bool finished;
   ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   ASSERT_FALSE(finished);
@@ -170,7 +170,7 @@
   regs[5] = 0x20;
   regs[9] = 0x3000;
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
-  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->LastErrorCode());
@@ -322,7 +322,8 @@
   regs[0] = 0x10;
   regs[8] = 0x20;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  loc_regs[0x20444558] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 1}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x8, 0x5008}};
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '1', 0x13, 0x08, 0x11});
   bool finished;
   ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(0x10U, regs[0]);
@@ -462,7 +463,7 @@
   TypeParam cfa_value = 0x12345;
   this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
   bool finished;
   ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_FALSE(finished);
@@ -480,7 +481,7 @@
   regs[8] = 0x3000;
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
   bool finished;
   ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_FALSE(finished);
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 0086c9e..89331ea 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -60,6 +60,7 @@
 
   void VerifyWithinSameMap(bool cache_enabled);
   void VerifySameMap(bool cache_enabled);
+  void VerifyWithinSameMapNeverReadAtZero(bool cache_enabled);
 
   static std::shared_ptr<Memory> memory_;
 };
@@ -198,4 +199,66 @@
   VerifyWithinSameMap(true);
 }
 
+// Verify that when reading from multiple non-zero offsets in the same map
+// that when cached, all of the elf objects are the same.
+void ElfCacheTest::VerifyWithinSameMapNeverReadAtZero(bool cache_enabled) {
+  if (!cache_enabled) {
+    Elf::SetCachingEnabled(false);
+  }
+
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  WriteElfFile(0, &tf, EM_ARM);
+  lseek(tf.fd, 0x500, SEEK_SET);
+  uint8_t value = 0;
+  write(tf.fd, &value, 1);
+  close(tf.fd);
+
+  uint64_t start = 0x1000;
+  uint64_t end = 0x20000;
+  // Multiple info sections at different offsets will have non-zero elf offsets.
+  MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
+  MapInfo info400_1(start, end, 0x400, 0x5, tf.path);
+  MapInfo info400_2(start, end, 0x400, 0x5, tf.path);
+
+  Elf* elf300_1 = info300_1.GetElf(memory_, true);
+  ASSERT_TRUE(elf300_1->valid());
+  EXPECT_EQ(ARCH_ARM, elf300_1->arch());
+  Elf* elf300_2 = info300_2.GetElf(memory_, true);
+  ASSERT_TRUE(elf300_2->valid());
+  EXPECT_EQ(ARCH_ARM, elf300_2->arch());
+  EXPECT_EQ(0x300U, info300_1.elf_offset);
+  EXPECT_EQ(0x300U, info300_2.elf_offset);
+  if (cache_enabled) {
+    EXPECT_EQ(elf300_1, elf300_2);
+  } else {
+    EXPECT_NE(elf300_1, elf300_2);
+  }
+
+  Elf* elf400_1 = info400_1.GetElf(memory_, true);
+  ASSERT_TRUE(elf400_1->valid());
+  EXPECT_EQ(ARCH_ARM, elf400_1->arch());
+  Elf* elf400_2 = info400_2.GetElf(memory_, true);
+  ASSERT_TRUE(elf400_2->valid());
+  EXPECT_EQ(ARCH_ARM, elf400_2->arch());
+  EXPECT_EQ(0x400U, info400_1.elf_offset);
+  EXPECT_EQ(0x400U, info400_2.elf_offset);
+  if (cache_enabled) {
+    EXPECT_EQ(elf400_1, elf400_2);
+    EXPECT_EQ(elf300_1, elf400_1);
+  } else {
+    EXPECT_NE(elf400_1, elf400_2);
+    EXPECT_NE(elf300_1, elf400_1);
+  }
+}
+
+TEST_F(ElfCacheTest, no_caching_valid_elf_offset_non_zero_never_read_at_zero) {
+  VerifyWithinSameMapNeverReadAtZero(false);
+}
+
+TEST_F(ElfCacheTest, caching_valid_elf_offset_non_zero_never_read_at_zero) {
+  VerifyWithinSameMapNeverReadAtZero(true);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 042c5fb..bf97e30 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -63,15 +63,28 @@
   template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
   void ManyPhdrs();
 
-  template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+  enum SonameTestEnum : uint8_t {
+    SONAME_NORMAL,
+    SONAME_DTNULL_AFTER,
+    SONAME_DTSIZE_SMALL,
+    SONAME_MISSING_MAP,
+  };
+
+  template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
+  void SonameInit(SonameTestEnum test_type = SONAME_NORMAL);
+
+  template <typename ElfInterfaceType>
   void Soname();
 
-  template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+  template <typename ElfInterfaceType>
   void SonameAfterDtNull();
 
-  template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+  template <typename ElfInterfaceType>
   void SonameSize();
 
+  template <typename ElfInterfaceType>
+  void SonameMissingMap();
+
   template <typename ElfType>
   void InitHeadersEhFrameTest();
 
@@ -465,17 +478,29 @@
   ASSERT_EQ(2U, elf_arm.total_entries());
 }
 
-template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
-void ElfInterfaceTest::Soname() {
-  std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
-
+template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
+void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) {
   Ehdr ehdr;
   memset(&ehdr, 0, sizeof(ehdr));
+  ehdr.e_shoff = 0x200;
+  ehdr.e_shnum = 2;
+  ehdr.e_shentsize = sizeof(Shdr);
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 1;
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
+  Shdr shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  if (test_type == SONAME_MISSING_MAP) {
+    shdr.sh_addr = 0x20100;
+  } else {
+    shdr.sh_addr = 0x10100;
+  }
+  shdr.sh_offset = 0x10000;
+  memory_.SetMemory(0x200 + sizeof(shdr), &shdr, sizeof(shdr));
+
   Phdr phdr;
   memset(&phdr, 0, sizeof(phdr));
   phdr.p_type = PT_DYNAMIC;
@@ -487,15 +512,25 @@
   Dyn dyn;
 
   dyn.d_tag = DT_STRTAB;
-  dyn.d_un.d_ptr = 0x10000;
+  dyn.d_un.d_ptr = 0x10100;
   memory_.SetMemory(offset, &dyn, sizeof(dyn));
   offset += sizeof(dyn);
 
   dyn.d_tag = DT_STRSZ;
-  dyn.d_un.d_val = 0x1000;
+  if (test_type == SONAME_DTSIZE_SMALL) {
+    dyn.d_un.d_val = 0x10;
+  } else {
+    dyn.d_un.d_val = 0x1000;
+  }
   memory_.SetMemory(offset, &dyn, sizeof(dyn));
   offset += sizeof(dyn);
 
+  if (test_type == SONAME_DTNULL_AFTER) {
+    dyn.d_tag = DT_NULL;
+    memory_.SetMemory(offset, &dyn, sizeof(dyn));
+    offset += sizeof(dyn);
+  }
+
   dyn.d_tag = DT_SONAME;
   dyn.d_un.d_val = 0x10;
   memory_.SetMemory(offset, &dyn, sizeof(dyn));
@@ -505,6 +540,11 @@
   memory_.SetMemory(offset, &dyn, sizeof(dyn));
 
   SetStringMemory(0x10010, "fake_soname.so");
+}
+
+template <typename ElfInterfaceType>
+void ElfInterfaceTest::Soname() {
+  std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
@@ -516,55 +556,19 @@
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname) {
-  Soname<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+  SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>();
+  Soname<ElfInterface32>();
 }
 
 TEST_F(ElfInterfaceTest, elf64_soname) {
-  Soname<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+  SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>();
+  Soname<ElfInterface64>();
 }
 
-template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+template <typename ElfInterfaceType>
 void ElfInterfaceTest::SonameAfterDtNull() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
-  ehdr.e_phoff = 0x100;
-  ehdr.e_phnum = 1;
-  ehdr.e_phentsize = sizeof(Phdr);
-  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
-
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
-  phdr.p_type = PT_DYNAMIC;
-  phdr.p_offset = 0x2000;
-  phdr.p_memsz = sizeof(Dyn) * 3;
-  memory_.SetMemory(0x100, &phdr, sizeof(phdr));
-
-  Dyn dyn;
-  uint64_t offset = 0x2000;
-
-  dyn.d_tag = DT_STRTAB;
-  dyn.d_un.d_ptr = 0x10000;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_STRSZ;
-  dyn.d_un.d_val = 0x1000;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_NULL;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_SONAME;
-  dyn.d_un.d_val = 0x10;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  SetStringMemory(0x10010, "fake_soname.so");
-
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
   EXPECT_EQ(0U, load_bias);
@@ -574,54 +578,19 @@
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
-  SonameAfterDtNull<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+  SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTNULL_AFTER);
+  SonameAfterDtNull<ElfInterface32>();
 }
 
 TEST_F(ElfInterfaceTest, elf64_soname_after_dt_null) {
-  SonameAfterDtNull<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+  SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTNULL_AFTER);
+  SonameAfterDtNull<ElfInterface64>();
 }
 
-template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+template <typename ElfInterfaceType>
 void ElfInterfaceTest::SonameSize() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
-  ehdr.e_phoff = 0x100;
-  ehdr.e_phnum = 1;
-  ehdr.e_phentsize = sizeof(Phdr);
-  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
-
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
-  phdr.p_type = PT_DYNAMIC;
-  phdr.p_offset = 0x2000;
-  phdr.p_memsz = sizeof(Dyn);
-  memory_.SetMemory(0x100, &phdr, sizeof(phdr));
-
-  Dyn dyn;
-  uint64_t offset = 0x2000;
-
-  dyn.d_tag = DT_STRTAB;
-  dyn.d_un.d_ptr = 0x10000;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_STRSZ;
-  dyn.d_un.d_val = 0x10;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_SONAME;
-  dyn.d_un.d_val = 0x10;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_NULL;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-
-  SetStringMemory(0x10010, "fake_soname.so");
-
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
   EXPECT_EQ(0U, load_bias);
@@ -631,11 +600,37 @@
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname_size) {
-  SonameSize<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+  SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTSIZE_SMALL);
+  SonameSize<ElfInterface32>();
 }
 
 TEST_F(ElfInterfaceTest, elf64_soname_size) {
-  SonameSize<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+  SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTSIZE_SMALL);
+  SonameSize<ElfInterface64>();
+}
+
+// Verify that there is no map from STRTAB in the dynamic section to a
+// STRTAB entry in the section headers.
+template <typename ElfInterfaceType>
+void ElfInterfaceTest::SonameMissingMap() {
+  std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
+
+  std::string name;
+  ASSERT_FALSE(elf->GetSoname(&name));
+}
+
+TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {
+  SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_MISSING_MAP);
+  SonameMissingMap<ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, elf64_soname_missing_map) {
+  SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_MISSING_MAP);
+  SonameMissingMap<ElfInterface64>();
 }
 
 template <typename ElfType>
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index e499593..df262f5 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -23,23 +23,154 @@
 #include <gtest/gtest.h>
 
 #include <string>
+#include <unordered_map>
 #include <vector>
 
+#include <android-base/file.h>
+
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/MachineArm.h>
 #include <unwindstack/MachineArm64.h>
 #include <unwindstack/MachineX86.h>
+#include <unwindstack/MachineX86_64.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/RegsArm.h>
 #include <unwindstack/RegsArm64.h>
 #include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
 #include <unwindstack/Unwinder.h>
 
 #include "ElfTestUtils.h"
 
 namespace unwindstack {
 
+class UnwindOfflineTest : public ::testing::Test {
+ protected:
+  void TearDown() override {
+    if (cwd_ != nullptr) {
+      ASSERT_EQ(0, chdir(cwd_));
+    }
+    free(cwd_);
+  }
+
+  void Init(const char* file_dir, ArchEnum arch) {
+    dir_ = TestGetFileDirectory() + "offline/" + file_dir;
+
+    std::string data;
+    ASSERT_TRUE(android::base::ReadFileToString((dir_ + "maps.txt"), &data));
+
+    maps_.reset(new BufferMaps(data.c_str()));
+    ASSERT_TRUE(maps_->Parse());
+
+    std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
+    ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
+    process_memory_.reset(stack_memory.release());
+
+    switch (arch) {
+      case ARCH_ARM: {
+        RegsArm* regs = new RegsArm;
+        regs_.reset(regs);
+        ReadRegs<uint32_t>(regs, arm_regs_);
+        break;
+      }
+      case ARCH_ARM64: {
+        RegsArm64* regs = new RegsArm64;
+        regs_.reset(regs);
+        ReadRegs<uint64_t>(regs, arm64_regs_);
+        break;
+      }
+      case ARCH_X86: {
+        RegsX86* regs = new RegsX86;
+        regs_.reset(regs);
+        ReadRegs<uint32_t>(regs, x86_regs_);
+        break;
+      }
+      case ARCH_X86_64: {
+        RegsX86_64* regs = new RegsX86_64;
+        regs_.reset(regs);
+        ReadRegs<uint64_t>(regs, x86_64_regs_);
+        break;
+      }
+      default:
+        ASSERT_TRUE(false) << "Unknown arch " << std::to_string(arch);
+    }
+    cwd_ = getcwd(nullptr, 0);
+    // Make dir_ an absolute directory.
+    if (dir_.empty() || dir_[0] != '/') {
+      dir_ = std::string(cwd_) + '/' + dir_;
+    }
+    ASSERT_EQ(0, chdir(dir_.c_str()));
+  }
+
+  template <typename AddressType>
+  void ReadRegs(RegsImpl<AddressType>* regs,
+                const std::unordered_map<std::string, uint32_t>& name_to_reg) {
+    FILE* fp = fopen((dir_ + "regs.txt").c_str(), "r");
+    ASSERT_TRUE(fp != nullptr);
+    while (!feof(fp)) {
+      uint64_t value;
+      char reg_name[100];
+      ASSERT_EQ(2, fscanf(fp, "%s %" SCNx64 "\n", reg_name, &value));
+      std::string name(reg_name);
+      if (!name.empty()) {
+        // Remove the : from the end.
+        name.resize(name.size() - 1);
+      }
+      auto entry = name_to_reg.find(name);
+      ASSERT_TRUE(entry != name_to_reg.end()) << "Unknown register named " << name;
+      (*regs)[entry->second] = value;
+    }
+    fclose(fp);
+    regs->SetFromRaw();
+  }
+
+  static std::unordered_map<std::string, uint32_t> arm_regs_;
+  static std::unordered_map<std::string, uint32_t> arm64_regs_;
+  static std::unordered_map<std::string, uint32_t> x86_regs_;
+  static std::unordered_map<std::string, uint32_t> x86_64_regs_;
+
+  char* cwd_ = nullptr;
+  std::string dir_;
+  std::unique_ptr<Regs> regs_;
+  std::unique_ptr<Maps> maps_;
+  std::shared_ptr<Memory> process_memory_;
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm_regs_ = {
+    {"r0", ARM_REG_R0},  {"r1", ARM_REG_R1}, {"r2", ARM_REG_R2},   {"r3", ARM_REG_R3},
+    {"r4", ARM_REG_R4},  {"r5", ARM_REG_R5}, {"r6", ARM_REG_R6},   {"r7", ARM_REG_R7},
+    {"r8", ARM_REG_R8},  {"r9", ARM_REG_R9}, {"r10", ARM_REG_R10}, {"r11", ARM_REG_R11},
+    {"ip", ARM_REG_R12}, {"sp", ARM_REG_SP}, {"lr", ARM_REG_LR},   {"pc", ARM_REG_PC},
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm64_regs_ = {
+    {"x0", ARM64_REG_R0},   {"x1", ARM64_REG_R1},   {"x2", ARM64_REG_R2},   {"x3", ARM64_REG_R3},
+    {"x4", ARM64_REG_R4},   {"x5", ARM64_REG_R5},   {"x6", ARM64_REG_R6},   {"x7", ARM64_REG_R7},
+    {"x8", ARM64_REG_R8},   {"x9", ARM64_REG_R9},   {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
+    {"x12", ARM64_REG_R12}, {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14}, {"x15", ARM64_REG_R15},
+    {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17}, {"x18", ARM64_REG_R18}, {"x19", ARM64_REG_R19},
+    {"x20", ARM64_REG_R20}, {"x21", ARM64_REG_R21}, {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
+    {"x24", ARM64_REG_R24}, {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26}, {"x27", ARM64_REG_R27},
+    {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29}, {"sp", ARM64_REG_SP},   {"lr", ARM64_REG_LR},
+    {"pc", ARM64_REG_PC},
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_regs_ = {
+    {"eax", X86_REG_EAX}, {"ebx", X86_REG_EBX}, {"ecx", X86_REG_ECX},
+    {"edx", X86_REG_EDX}, {"ebp", X86_REG_EBP}, {"edi", X86_REG_EDI},
+    {"esi", X86_REG_ESI}, {"esp", X86_REG_ESP}, {"eip", X86_REG_EIP},
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_64_regs_ = {
+    {"rax", X86_64_REG_RAX}, {"rbx", X86_64_REG_RBX}, {"rcx", X86_64_REG_RCX},
+    {"rdx", X86_64_REG_RDX}, {"r8", X86_64_REG_R8},   {"r9", X86_64_REG_R9},
+    {"r10", X86_64_REG_R10}, {"r11", X86_64_REG_R11}, {"r12", X86_64_REG_R12},
+    {"r13", X86_64_REG_R13}, {"r14", X86_64_REG_R14}, {"r15", X86_64_REG_R15},
+    {"rdi", X86_64_REG_RDI}, {"rsi", X86_64_REG_RSI}, {"rbp", X86_64_REG_RBP},
+    {"rsp", X86_64_REG_RSP}, {"rip", X86_64_REG_RIP},
+};
+
 static std::string DumpFrames(Unwinder& unwinder) {
   std::string str;
   for (size_t i = 0; i < unwinder.NumFrames(); i++) {
@@ -48,45 +179,11 @@
   return str;
 }
 
-TEST(UnwindOfflineTest, pc_straddle_arm) {
-  std::string dir(TestGetFileDirectory() + "offline/straddle_arm/");
+TEST_F(UnwindOfflineTest, pc_straddle_arm) {
+  Init("straddle_arm/", ARCH_ARM);
 
-  MemoryOffline* memory = new MemoryOffline;
-  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
-
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsArm regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_PC] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_SP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_LR] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_ARM, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  Unwinder unwinder(128, &maps, &regs, process_memory);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -98,43 +195,11 @@
       frame_info);
 }
 
-TEST(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
-  std::string dir(TestGetFileDirectory() + "offline/gnu_debugdata_arm/");
+TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
+  Init("gnu_debugdata_arm/", ARCH_ARM);
 
-  MemoryOffline* memory = new MemoryOffline;
-  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
-
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsArm regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_PC] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_SP] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_ARM, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  Unwinder unwinder(128, &maps, &regs, process_memory);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -146,47 +211,11 @@
       frame_info);
 }
 
-TEST(UnwindOfflineTest, pc_straddle_arm64) {
-  std::string dir(TestGetFileDirectory() + "offline/straddle_arm64/");
+TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
+  Init("straddle_arm64/", ARCH_ARM64);
 
-  MemoryOffline* memory = new MemoryOffline;
-  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
-
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsArm64 regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_PC] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_SP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_LR] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "x29: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_R29] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_ARM64, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  Unwinder unwinder(128, &maps, &regs, process_memory);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -208,64 +237,22 @@
   parts->Add(memory);
 }
 
-TEST(UnwindOfflineTest, jit_debug_x86) {
-  std::string dir(TestGetFileDirectory() + "offline/jit_debug_x86/");
+TEST_F(UnwindOfflineTest, jit_debug_x86) {
+  Init("jit_debug_x86/", ARCH_X86);
 
   MemoryOfflineParts* memory = new MemoryOfflineParts;
-  AddMemory(dir + "descriptor.data", memory);
-  AddMemory(dir + "stack.data", memory);
+  AddMemory(dir_ + "descriptor.data", memory);
+  AddMemory(dir_ + "stack.data", memory);
   for (size_t i = 0; i < 7; i++) {
-    AddMemory(dir + "entry" + std::to_string(i) + ".data", memory);
-    AddMemory(dir + "jit" + std::to_string(i) + ".data", memory);
+    AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
+    AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
   }
+  process_memory_.reset(memory);
 
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsX86 regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "eax: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EAX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ebx: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EBX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ecx: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_ECX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "edx: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EDX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ebp: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EBP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "edi: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EDI] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "esi: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_ESI] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "esp: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_ESP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "eip: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EIP] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_X86, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  JitDebug jit_debug(process_memory);
-  Unwinder unwinder(128, &maps, &regs, process_memory);
-  unwinder.SetJitDebug(&jit_debug, regs.Arch());
+  JitDebug jit_debug(process_memory_);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.SetJitDebug(&jit_debug, regs_->Arch());
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -405,79 +392,23 @@
       frame_info);
 }
 
-TEST(UnwindOfflineTest, jit_debug_arm) {
-  std::string dir(TestGetFileDirectory() + "offline/jit_debug_arm/");
+TEST_F(UnwindOfflineTest, jit_debug_arm) {
+  Init("jit_debug_arm/", ARCH_ARM);
 
   MemoryOfflineParts* memory = new MemoryOfflineParts;
-  AddMemory(dir + "descriptor.data", memory);
-  AddMemory(dir + "descriptor1.data", memory);
-  AddMemory(dir + "stack.data", memory);
+  AddMemory(dir_ + "descriptor.data", memory);
+  AddMemory(dir_ + "descriptor1.data", memory);
+  AddMemory(dir_ + "stack.data", memory);
   for (size_t i = 0; i < 7; i++) {
-    AddMemory(dir + "entry" + std::to_string(i) + ".data", memory);
-    AddMemory(dir + "jit" + std::to_string(i) + ".data", memory);
+    AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
+    AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
   }
+  process_memory_.reset(memory);
 
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsArm regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r0: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R0] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r1: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R1] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r2: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R2] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r3: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R3] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r4: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R4] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r5: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R5] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r6: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R6] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r7: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R7] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r8: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R8] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r9: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R9] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r10: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R10] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r11: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R11] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ip: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R12] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_SP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_LR] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_PC] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_ARM, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  JitDebug jit_debug(process_memory);
-  Unwinder unwinder(128, &maps, &regs, process_memory);
-  unwinder.SetJitDebug(&jit_debug, regs.Arch());
+  JitDebug jit_debug(process_memory_);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.SetJitDebug(&jit_debug, regs_->Arch());
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -627,47 +558,11 @@
 // The eh_frame_hdr data is present but set to zero fdes. This should
 // fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
 // No .gnu_debugdata section in the elf file, so no symbols.
-TEST(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
-  std::string dir(TestGetFileDirectory() + "offline/bad_eh_frame_hdr_arm64/");
+TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
+  Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64);
 
-  MemoryOffline* memory = new MemoryOffline;
-  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
-
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsArm64 regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_PC] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_SP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_LR] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "x29: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_R29] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_ARM64, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  Unwinder unwinder(128, &maps, &regs, process_memory);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -682,59 +577,11 @@
 
 // The elf has bad eh_frame unwind information for the pcs. If eh_frame
 // is used first, the unwind will not match the expected output.
-TEST(UnwindOfflineTest, debug_frame_first_x86) {
-  std::string dir(TestGetFileDirectory() + "offline/debug_frame_first_x86/");
+TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
+  Init("debug_frame_first_x86/", ARCH_X86);
 
-  MemoryOffline* memory = new MemoryOffline;
-  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
-
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsX86 regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "eax: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EAX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ebx: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EBX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ecx: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_ECX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "edx: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EDX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ebp: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EBP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "edi: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EDI] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "esi: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_ESI] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "esp: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_ESP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "eip: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EIP] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_X86, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  JitDebug jit_debug(process_memory);
-  Unwinder unwinder(128, &maps, &regs, process_memory);
-  unwinder.SetJitDebug(&jit_debug, regs.Arch());
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -747,4 +594,22 @@
       frame_info);
 }
 
+// Make sure that a pc that is at the beginning of an fde unwinds correctly.
+TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
+  Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64);
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0000000000000a80  unwind_test64 (calling3)\n"
+      "  #01 pc 0000000000000dd9  unwind_test64 (calling2+633)\n"
+      "  #02 pc 000000000000121e  unwind_test64 (calling1+638)\n"
+      "  #03 pc 00000000000013ed  unwind_test64 (main+13)\n"
+      "  #04 pc 00000000000202b0  libc.so\n",
+      frame_info);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 09c6e04..7fbae4c 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -102,6 +102,22 @@
     info->load_bias = 0;
     maps_.FakeAddMapInfo(info);
 
+    info = new MapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                       "/fake/fake_load_bias.so");
+    elf = new ElfFake(new MemoryFake);
+    info->elf.reset(elf);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    elf->FakeSetLoadBias(0x5000);
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                       "/fake/fake_offset.oat");
+    elf = new ElfFake(new MemoryFake);
+    info->elf.reset(elf);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    info->elf_offset = 0x8000;
+    maps_.FakeAddMapInfo(info);
+
     process_memory_.reset(new MemoryFake);
   }
 
@@ -180,6 +196,62 @@
   EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
 }
 
+TEST_F(UnwinderTest, non_zero_load_bias) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.FakeSetPc(0xa5500);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x5500U, frame->rel_pc);
+  EXPECT_EQ(0xa5500U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake_load_bias.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa5000U, frame->map_start);
+  EXPECT_EQ(0xa6000U, frame->map_end);
+  EXPECT_EQ(0x5000U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, non_zero_elf_offset) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.FakeSetPc(0xa7500);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x8500U, frame->rel_pc);
+  EXPECT_EQ(0xa7500U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake_offset.oat", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa7000U, frame->map_start);
+  EXPECT_EQ(0xa8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
 TEST_F(UnwinderTest, non_zero_map_offset) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
 
@@ -752,6 +824,64 @@
   EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
 }
 
+TEST_F(UnwinderTest, dex_pc_multiple_frames) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+  regs_.FakeSetDexPc(0xa3400);
+  ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0xa3400U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa3000U, frame->map_start);
+  EXPECT_EQ(0xa4000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0x33400U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/fake/compressed.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x33000U, frame->map_start);
+  EXPECT_EQ(0x34000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
 // Verify format frame code.
 TEST_F(UnwinderTest, format_frame_static) {
   FrameData frame;
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so
new file mode 100644
index 0000000..46b6f45
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt
new file mode 100644
index 0000000..ac2e564
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt
@@ -0,0 +1,2 @@
+561550b17000-561550b1a000 r-xp 0 00:00 0   unwind_test64
+7f4de61f6000-7f4de638b000 r-xp 0 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt
new file mode 100644
index 0000000..38af274
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt
@@ -0,0 +1,11 @@
+rax: 92134c6fbbdc12ff
+rbx: 0
+rcx: 92134c6fbbdc1200
+rdx: 92134c6fbbdc1200
+r8: 561552153034
+r12: 561550b17930
+r13: 7ffcc8597270
+rsi: 561552153034
+rbp: 7ffcc8596f30
+rsp: 7ffcc8596ce8
+rip: 561550b17a80
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data
new file mode 100644
index 0000000..cc7882b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64 b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64
new file mode 100644
index 0000000..ab0ef8f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64
Binary files differ
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 7f2d11d..a0abcca 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -120,6 +120,11 @@
     return 1;
   }
 
+  std::string soname;
+  if (elf.GetSoname(&soname)) {
+    printf("Soname: %s\n", soname.c_str());
+  }
+
   ElfInterface* interface = elf.interface();
   if (elf.machine_type() == EM_ARM) {
     DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
new file mode 100644
index 0000000..47a4f91
--- /dev/null
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -0,0 +1,242 @@
+/*
+ * 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.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+
+#include "DwarfOp.h"
+
+namespace unwindstack {
+
+void PrintSignedValue(int64_t value) {
+  if (value < 0) {
+    printf("- %" PRId64, -value);
+  } else if (value > 0) {
+    printf("+ %" PRId64, value);
+  }
+}
+
+void PrintExpression(Memory* memory, uint8_t class_type, uint64_t end, uint64_t length) {
+  std::vector<std::string> lines;
+  DwarfMemory dwarf_memory(memory);
+  if (class_type == ELFCLASS32) {
+    DwarfOp<uint32_t> op(&dwarf_memory, nullptr);
+    op.GetLogInfo(end - length, end, &lines);
+  } else {
+    DwarfOp<uint64_t> op(&dwarf_memory, nullptr);
+    op.GetLogInfo(end - length, end, &lines);
+  }
+  for (auto& line : lines) {
+    printf("    %s\n", line.c_str());
+  }
+}
+
+void PrintRegInformation(DwarfSection* section, Memory* memory, uint64_t pc, uint8_t class_type) {
+  const DwarfFde* fde = section->GetFdeFromPc(pc);
+  if (fde == nullptr) {
+    printf("  No fde found.\n");
+    return;
+  }
+
+  dwarf_loc_regs_t regs;
+  if (!section->GetCfaLocationInfo(pc, fde, &regs)) {
+    printf("  Cannot get location information.\n");
+    return;
+  }
+
+  std::vector<std::pair<uint32_t, DwarfLocation>> loc_regs;
+  for (auto& loc : regs) {
+    loc_regs.push_back(loc);
+  }
+  std::sort(loc_regs.begin(), loc_regs.end(), [](auto a, auto b) {
+    if (a.first == CFA_REG) {
+      return true;
+    } else if (b.first == CFA_REG) {
+      return false;
+    }
+    return a.first < b.first;
+  });
+
+  for (auto& entry : loc_regs) {
+    const DwarfLocation* loc = &entry.second;
+    if (entry.first == CFA_REG) {
+      printf("  cfa = ");
+    } else {
+      printf("  r%d = ", entry.first);
+    }
+    switch (loc->type) {
+      case DWARF_LOCATION_OFFSET:
+        printf("[cfa ");
+        PrintSignedValue(loc->values[0]);
+        printf("]\n");
+        break;
+
+      case DWARF_LOCATION_VAL_OFFSET:
+        printf("cfa ");
+        PrintSignedValue(loc->values[0]);
+        printf("\n");
+        break;
+
+      case DWARF_LOCATION_REGISTER:
+        printf("r%" PRId64 " ", loc->values[0]);
+        PrintSignedValue(loc->values[1]);
+        printf("\n");
+        break;
+
+      case DWARF_LOCATION_EXPRESSION: {
+        printf("EXPRESSION\n");
+        PrintExpression(memory, class_type, loc->values[1], loc->values[0]);
+        break;
+      }
+
+      case DWARF_LOCATION_VAL_EXPRESSION: {
+        printf("VAL EXPRESSION\n");
+        PrintExpression(memory, class_type, loc->values[1], loc->values[0]);
+        break;
+      }
+
+      case DWARF_LOCATION_UNDEFINED:
+        printf("undefine\n");
+        break;
+
+      case DWARF_LOCATION_INVALID:
+        printf("INVALID\n");
+        break;
+    }
+  }
+}
+
+int GetInfo(const char* file, uint64_t pc) {
+  MemoryFileAtOffset* memory = new MemoryFileAtOffset;
+  if (!memory->Init(file, 0)) {
+    // Initializatation failed.
+    printf("Failed to init\n");
+    return 1;
+  }
+
+  Elf elf(memory);
+  if (!elf.Init(true) || !elf.valid()) {
+    printf("%s is not a valid elf file.\n", file);
+    return 1;
+  }
+
+  ElfInterface* interface = elf.interface();
+  uint64_t load_bias = elf.GetLoadBias();
+  if (pc < load_bias) {
+    printf("PC is less than load bias.\n");
+    return 1;
+  }
+
+  std::string soname;
+  if (elf.GetSoname(&soname)) {
+    printf("Soname: %s\n\n", soname.c_str());
+  }
+
+  printf("PC 0x%" PRIx64 ":\n", pc);
+
+  DwarfSection* section = interface->eh_frame();
+  if (section != nullptr) {
+    printf("\neh_frame:\n");
+    PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+  } else {
+    printf("\nno eh_frame information\n");
+  }
+
+  section = interface->debug_frame();
+  if (section != nullptr) {
+    printf("\ndebug_frame:\n");
+    PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+    printf("\n");
+  } else {
+    printf("\nno debug_frame information\n");
+  }
+
+  // If there is a gnu_debugdata interface, dump the information for that.
+  ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface();
+  if (gnu_debugdata_interface != nullptr) {
+    section = gnu_debugdata_interface->eh_frame();
+    if (section != nullptr) {
+      printf("\ngnu_debugdata (eh_frame):\n");
+      PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
+      printf("\n");
+    } else {
+      printf("\nno gnu_debugdata (eh_frame)\n");
+    }
+
+    section = gnu_debugdata_interface->debug_frame();
+    if (section != nullptr) {
+      printf("\ngnu_debugdata (debug_frame):\n");
+      PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
+      printf("\n");
+    } else {
+      printf("\nno gnu_debugdata (debug_frame)\n");
+    }
+  } else {
+    printf("\nno valid gnu_debugdata information\n");
+  }
+
+  return 0;
+}
+
+}  // namespace unwindstack
+
+int main(int argc, char** argv) {
+  if (argc != 3) {
+    printf("Usage: unwind_reg_info ELF_FILE PC\n");
+    printf("  ELF_FILE\n");
+    printf("    The path to an elf file.\n");
+    printf("  PC\n");
+    printf("    The pc for which the register information should be obtained.\n");
+    return 1;
+  }
+
+  struct stat st;
+  if (stat(argv[1], &st) == -1) {
+    printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+    return 1;
+  }
+  if (!S_ISREG(st.st_mode)) {
+    printf("%s is not a regular file.\n", argv[1]);
+    return 1;
+  }
+
+  uint64_t pc = 0;
+  char* end;
+  pc = strtoull(argv[2], &end, 16);
+  if (*end != '\0') {
+    printf("Malformed OFFSET value: %s\n", argv[2]);
+    return 1;
+  }
+
+  return unwindstack::GetInfo(argv[1], pc);
+}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 697e4cd..086dffe 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -71,6 +71,11 @@
     return 1;
   }
 
+  std::string soname;
+  if (elf.GetSoname(&soname)) {
+    printf("Soname: %s\n\n", soname.c_str());
+  }
+
   switch (elf.machine_type()) {
     case EM_ARM:
       printf("ABI: arm\n");
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 07d60e9..415488f 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -64,10 +64,6 @@
 // Some devices fail to send string descriptors if we attempt reading > 255 bytes
 #define MAX_STRING_DESCRIPTOR_LENGTH    255
 
-// From drivers/usb/core/devio.c
-// I don't know why this isn't in a kernel header
-#define MAX_USBFS_BUFFER_SIZE   16384
-
 #define MAX_USBFS_WD_COUNT      10
 
 struct usb_host_context {
@@ -675,10 +671,6 @@
 {
     struct usbdevfs_bulktransfer  ctrl;
 
-    // need to limit request size to avoid EINVAL
-    if (length > MAX_USBFS_BUFFER_SIZE)
-        length = MAX_USBFS_BUFFER_SIZE;
-
     memset(&ctrl, 0, sizeof(ctrl));
     ctrl.ep = endpoint;
     ctrl.len = length;
@@ -738,11 +730,7 @@
 
     urb->status = -1;
     urb->buffer = req->buffer;
-    // need to limit request size to avoid EINVAL
-    if (req->buffer_length > MAX_USBFS_BUFFER_SIZE)
-        urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
-    else
-        urb->buffer_length = req->buffer_length;
+    urb->buffer_length = req->buffer_length;
 
     do {
         res = ioctl(req->dev->fd, USBDEVFS_SUBMITURB, urb);
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index f4509a4..feb100e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -145,13 +145,21 @@
 # $(1): Input source file (ld.config.txt)
 # $(2): Output built module
 # $(3): VNDK version suffix
+# $(4): true if libz must be included in llndk not in vndk-sp
 define update_and_install_ld_config
+# If $(4) is true, move libz to llndk from vndk-sp.
+$(if $(filter true,$(4)),\
+  $(eval llndk_libraries_list := $(LLNDK_LIBRARIES) libz) \
+  $(eval vndksp_libraries_list := $(filter-out libz,$(VNDK_SAMEPROCESS_LIBRARIES))),\
+  $(eval llndk_libraries_list := $(LLNDK_LIBRARIES)) \
+  $(eval vndksp_libraries_list := $(VNDK_SAMEPROCESS_LIBRARIES)))
+
 llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
-  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(LLNDK_LIBRARIES))))
+  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(llndk_libraries_list))))
 private_llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
-  $(filter $(VNDK_PRIVATE_LIBRARIES),$(LLNDK_LIBRARIES))))
+  $(filter $(VNDK_PRIVATE_LIBRARIES),$(llndk_libraries_list))))
 vndk_sameprocess_libraries := $(call normalize-path-list,$(addsuffix .so,\
-  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_SAMEPROCESS_LIBRARIES))))
+  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(vndksp_libraries_list))))
 vndk_core_libraries := $(call normalize-path-list,$(addsuffix .so,\
   $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_CORE_LIBRARIES))))
 sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
@@ -180,6 +188,8 @@
 	$$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $$@
 	$$(hide) sed -i -e 's?%VNDK_VER%?$$(PRIVATE_VNDK_VERSION)?g' $$@
 
+llndk_libraries_list :=
+vndksp_libraries_list :=
 llndk_libraries :=
 private_llndk_libraries :=
 vndk_sameprocess_libraries :=
@@ -228,7 +238,8 @@
 $(eval $(call update_and_install_ld_config,\
   $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt,\
   $(LOCAL_BUILT_MODULE),\
-  $(if $(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION))))
+  $(if $(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION)),\
+  true))
 
 else
 # for legacy non-treblized devices
@@ -258,7 +269,8 @@
 $(eval $(call update_and_install_ld_config,\
   $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt,\
   $(LOCAL_BUILT_MODULE),\
-  $(PLATFORM_VNDK_VERSION)))
+  $(PLATFORM_VNDK_VERSION),\
+  true))
 
 #######################################
 # llndk.libraries.txt
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6cefde2..4d823ea 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -28,12 +28,12 @@
     restorecon /postinstall
 
     # Mount cgroup mount point for cpu accounting
-    mount cgroup none /acct cpuacct
+    mount cgroup none /acct nodev noexec nosuid cpuacct
     mkdir /acct/uid
 
     # root memory control cgroup, used by lmkd
     mkdir /dev/memcg 0700 root system
-    mount cgroup none /dev/memcg memory
+    mount cgroup none /dev/memcg nodev noexec nosuid memory
     # app mem cgroups, used by activity manager, lmkd and zygote
     mkdir /dev/memcg/apps/ 0755 system system
     # cgroup for system_server and surfaceflinger
@@ -59,7 +59,7 @@
 
     # Create energy-aware scheduler tuning nodes
     mkdir /dev/stune
-    mount cgroup none /dev/stune schedtune
+    mount cgroup none /dev/stune nodev noexec nosuid schedtune
     mkdir /dev/stune/foreground
     mkdir /dev/stune/background
     mkdir /dev/stune/top-app
@@ -82,10 +82,10 @@
 
     # Mount staging areas for devices managed by vold
     # See storage config details at http://source.android.com/tech/storage/
-    mount tmpfs tmpfs /mnt mode=0755,uid=0,gid=1000
+    mount tmpfs tmpfs /mnt nodev noexec nosuid mode=0755,uid=0,gid=1000
     restorecon_recursive /mnt
 
-    mount configfs none /config
+    mount configfs none /config nodev noexec nosuid
     chmod 0770 /config/sdcardfs
     chown system package_info /config/sdcardfs
 
@@ -155,7 +155,7 @@
 
     # Create cgroup mount points for process groups
     mkdir /dev/cpuctl
-    mount cgroup none /dev/cpuctl cpu
+    mount cgroup none /dev/cpuctl nodev noexec nosuid cpu
     chown system system /dev/cpuctl
     chown system system /dev/cpuctl/tasks
     chmod 0666 /dev/cpuctl/tasks
@@ -164,7 +164,7 @@
 
     # sets up initial cpusets for ActivityManager
     mkdir /dev/cpuset
-    mount cpuset none /dev/cpuset
+    mount cpuset none /dev/cpuset nodev noexec nosuid
 
     # this ensures that the cpusets are present and usable, but the device's
     # init.rc must actually set the correct cpus
@@ -219,17 +219,17 @@
     chmod 0644 /dev/xt_qtaguid
 
     mkdir /dev/cg2_bpf
-    mount cgroup2 cg2_bpf /dev/cg2_bpf
+    mount cgroup2 cg2_bpf /dev/cg2_bpf nodev noexec nosuid
     chown root root /dev/cg2_bpf
     chmod 0600 /dev/cg2_bpf
-    mount bpf bpf /sys/fs/bpf
+    mount bpf bpf /sys/fs/bpf nodev noexec nosuid
 
     # Create location for fs_mgr to store abbreviated output from filesystem
     # checker programs.
     mkdir /dev/fscklogs 0770 root system
 
     # pstore/ramoops previous console log
-    mount pstore pstore /sys/fs/pstore
+    mount pstore pstore /sys/fs/pstore nodev noexec nosuid
     chown system log /sys/fs/pstore/console-ramoops
     chmod 0440 /sys/fs/pstore/console-ramoops
     chown system log /sys/fs/pstore/console-ramoops-0
@@ -318,8 +318,9 @@
     start hwservicemanager
     start vndservicemanager
 
-    # once everything is setup, no need to modify /
-    mount rootfs rootfs / ro remount
+    # Once everything is setup, no need to modify /.
+    # The bind+ro combination avoids modifying any other mount flags.
+    mount rootfs rootfs / remount bind ro
     # Mount shared so changes propagate into child namespaces
     mount rootfs rootfs / shared rec
     # Mount default storage into root namespace