Merge "logcatd: introduce logcatd executable"
diff --git a/adb/Android.mk b/adb/Android.mk
index 8a43e37..e841205 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -124,12 +124,11 @@
     adbd_auth.cpp \
     jdwp_service.cpp \
 
-LOCAL_C_INCLUDES := system/core/qemu_pipe/include
 LOCAL_SANITIZE := $(adb_target_sanitize)
 
 # Even though we're building a static library (and thus there's no link step for
 # this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libqemu_pipe libbase
+LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase
 
 LOCAL_WHOLE_STATIC_LIBRARIES := libadbd_usb
 
@@ -362,7 +361,6 @@
 LOCAL_STATIC_LIBRARIES := \
     libadbd \
     libbase \
-    libqemu_pipe \
     libbootloader_message \
     libfs_mgr \
     libfec \
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 7adb262..c48a251 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -306,14 +306,6 @@
             }
             device_serial.resize(rc);
 
-            // Try to reset the device.
-            rc = libusb_reset_device(handle_raw);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to reset opened device '" << device_serial
-                             << "': " << libusb_error_name(rc);
-                continue;
-            }
-
             // WARNING: this isn't released via RAII.
             rc = libusb_claim_interface(handle.get(), interface_num);
             if (rc != 0) {
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 12b98ba..4198a52 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -289,7 +289,7 @@
 #define open    adb_open
 #define read    adb_read
 #define write   adb_write
-#include <qemu_pipe.h>
+#include <system/qemu_pipe.h>
 #undef open
 #undef read
 #undef write
diff --git a/fingerprintd/FingerprintDaemonProxy.cpp b/fingerprintd/FingerprintDaemonProxy.cpp
index 1c7da30..b3c0cd7 100644
--- a/fingerprintd/FingerprintDaemonProxy.cpp
+++ b/fingerprintd/FingerprintDaemonProxy.cpp
@@ -31,7 +31,7 @@
 FingerprintDaemonProxy* FingerprintDaemonProxy::sInstance = NULL;
 
 // Supported fingerprint HAL version
-static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 0);
+static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 1);
 
 FingerprintDaemonProxy::FingerprintDaemonProxy() : mModule(NULL), mDevice(NULL), mCallback(NULL) {
 
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index d4a92e5..96bda07 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -166,7 +166,14 @@
                     enrolled_password_handle, enrolled_password_handle_length);
         }
 
-        if (ret == 0) {
+        if (ret == GATEKEEPER_RESPONSE_OK && (*enrolled_password_handle == nullptr ||
+            *enrolled_password_handle_length != sizeof(password_handle_t))) {
+            ret = GATEKEEPER_RESPONSE_ERROR;
+            ALOGE("HAL: password_handle=%p size_of_handle=%" PRIu32 "\n",
+                  *enrolled_password_handle, *enrolled_password_handle_length);
+        }
+
+        if (ret == GATEKEEPER_RESPONSE_OK) {
             gatekeeper::password_handle_t *handle =
                     reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle);
             store_sid(uid, handle->user_id);
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 7c5e35b..b292725 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -21,6 +21,10 @@
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
+LOCAL_SHARED_LIBRARIES += libsuspend
+endif
 LOCAL_SRC_FILES := \
     healthd_mode_android.cpp \
     healthd_mode_charger.cpp \
@@ -90,6 +94,14 @@
 
 LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
 
+ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
+LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
+endif
+
+ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
+LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
+endif
+
 LOCAL_STATIC_LIBRARIES := \
     libhealthd_internal \
     libbatterymonitor \
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index 20a6bf6..aa6735d 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -35,9 +35,19 @@
 
 using namespace android;
 
-// Periodic chores intervals in seconds
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
+#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
+  // Periodic chores fast interval in seconds
+  #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
+#else
+  #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (BOARD_PERIODIC_CHORES_INTERVAL_FAST)
+#endif
+
+#ifndef BOARD_PERIODIC_CHORES_INTERVAL_SLOW
+  // Periodic chores fast interval in seconds
+  #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
+#else
+  #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
+#endif
 
 static struct healthd_config healthd_config = {
     .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
@@ -143,10 +153,14 @@
     struct android::BatteryProperties* /*props*/) {
 }
 
-int healthd_register_event(int fd, void (*handler)(uint32_t)) {
+int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
     struct epoll_event ev;
 
-    ev.events = EPOLLIN | EPOLLWAKEUP;
+    ev.events = EPOLLIN;
+
+    if (wakeup == EVENT_WAKEUP_FD)
+        ev.events |= EPOLLWAKEUP;
+
     ev.data.ptr = (void *)handler;
     if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
         KLOG_ERROR(LOG_TAG,
@@ -252,7 +266,7 @@
     }
 
     fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
-    if (healthd_register_event(uevent_fd, uevent_event))
+    if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))
         KLOG_ERROR(LOG_TAG,
                    "register for uevent events failed\n");
 }
@@ -275,7 +289,7 @@
         return;
     }
 
-    if (healthd_register_event(wakealarm_fd, wakealarm_event))
+    if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
         KLOG_ERROR(LOG_TAG,
                    "Registration of wakealarm event failed\n");
 
@@ -283,17 +297,22 @@
 }
 
 static void healthd_mainloop(void) {
+    int nevents = 0;
     while (1) {
         struct epoll_event events[eventct];
-        int nevents;
         int timeout = awake_poll_interval;
         int mode_timeout;
 
+        /* Don't wait for first timer timeout to run periodic chores */
+        if (!nevents)
+            periodic_chores();
+
+        healthd_mode_ops->heartbeat();
+
         mode_timeout = healthd_mode_ops->preparetowait();
         if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
             timeout = mode_timeout;
         nevents = epoll_wait(epollfd, events, eventct, timeout);
-
         if (nevents == -1) {
             if (errno == EINTR)
                 continue;
@@ -305,11 +324,6 @@
             if (events[n].data.ptr)
                 (*(void (*)(int))events[n].data.ptr)(events[n].events);
         }
-
-        if (!nevents)
-            periodic_chores();
-
-        healthd_mode_ops->heartbeat();
     }
 
     return;
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 2f69372..91774c6 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -343,14 +343,19 @@
 
 static void draw_percent(const animation& anim)
 {
-    if (anim.cur_level <= 0 || anim.cur_status != BATTERY_STATUS_CHARGING) return;
+    int cur_level = anim.cur_level;
+    if (anim.cur_status == BATTERY_STATUS_FULL) {
+        cur_level = 100;
+    }
+
+    if (cur_level <= 0) return;
 
     const animation::text_field& field = anim.text_percent;
     if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
         return;
     }
 
-    std::string str = base::StringPrintf("%d%%", anim.cur_level);
+    std::string str = base::StringPrintf("%d%%", cur_level);
 
     int x, y;
     determine_xy(field, str.size(), &x, &y);
@@ -840,7 +845,7 @@
                             std::placeholders::_2));
     if (!ret) {
         epollfd = ev_get_epollfd();
-        healthd_register_event(epollfd, charger_event_handler);
+        healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
     }
 
     struct animation* anim = init_animation();
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index 34ea55f..17efbd6 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -73,9 +73,14 @@
     bool (*screen_on)(android::BatteryProperties *props);
 };
 
+enum EventWakeup {
+    EVENT_NO_WAKEUP_FD,
+    EVENT_WAKEUP_FD,
+};
+
 // Global helper functions
 
-int healthd_register_event(int fd, void (*handler)(uint32_t));
+int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup = EVENT_NO_WAKEUP_FD);
 void healthd_battery_update();
 android::status_t healthd_get_property(int id,
     struct android::BatteryProperty *val);
diff --git a/include/system/qemu_pipe.h b/include/system/qemu_pipe.h
new file mode 100644
index 0000000..af25079
--- /dev/null
+++ b/include/system/qemu_pipe.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 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 ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
+#define ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
+// occurs during pipe operations. The macro should simply take a printf-style
+// formatting string followed by optional arguments.
+#ifndef QEMU_PIPE_DEBUG
+#  define  QEMU_PIPE_DEBUG(...)   (void)0
+#endif
+
+// Try to open a new Qemu fast-pipe. This function returns a file descriptor
+// that can be used to communicate with a named service managed by the
+// emulator.
+//
+// This file descriptor can be used as a standard pipe/socket descriptor.
+//
+// 'pipeName' is the name of the emulator service you want to connect to,
+// and must begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
+//
+// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
+//
+// EINVAL  -> unknown/unsupported pipeName
+// ENOSYS  -> fast pipes not available in this system.
+//
+// ENOSYS should never happen, except if you're trying to run within a
+// misconfigured emulator.
+//
+// You should be able to open several pipes to the same pipe service,
+// except for a few special cases (e.g. GSM modem), where EBUSY will be
+// returned if more than one client tries to connect to it.
+static __inline__ int qemu_pipe_open(const char* pipeName) {
+    // Sanity check.
+    if (!pipeName || memcmp(pipeName, "pipe:", 5) != 0) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
+    if (fd < 0) {
+        QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
+                        strerror(errno));
+        return -1;
+    }
+
+    // Write the pipe name, *including* the trailing zero which is necessary.
+    size_t pipeNameLen = strlen(pipeName);
+    ssize_t ret = TEMP_FAILURE_RETRY(write(fd, pipeName, pipeNameLen + 1U));
+    if (ret != (ssize_t)pipeNameLen + 1) {
+        QEMU_PIPE_DEBUG("%s: Could not connect to %s pipe service: %s",
+                        __FUNCTION__, pipeName, strerror(errno));
+        if (ret == 0) {
+            errno = ECONNRESET;
+        } else if (ret > 0) {
+            errno = EINVAL;
+        }
+        return -1;
+    }
+    return fd;
+}
+
+// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
+// This really adds a 4-hexchar prefix describing the payload size.
+// Returns 0 on success, and -1 on error.
+static int __inline__ qemu_pipe_frame_send(int fd,
+                                           const void* buff,
+                                           size_t len) {
+    char header[5];
+    snprintf(header, sizeof(header), "%04zx", len);
+    ssize_t ret = TEMP_FAILURE_RETRY(write(fd, header, 4));
+    if (ret != 4) {
+        QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
+        return -1;
+    }
+    ret = TEMP_FAILURE_RETRY(write(fd, buff, len));
+    if (ret != (ssize_t)len) {
+        QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
+// If the framed message is larger than |len|, then this returns -1 and the
+// content is lost. Otherwise, this returns the size of the message. NOTE:
+// empty messages are possible in a framed wire protocol and do not mean
+// end-of-stream.
+static int __inline__ qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
+    char header[5];
+    ssize_t ret = TEMP_FAILURE_RETRY(read(fd, header, 4));
+    if (ret != 4) {
+        QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
+        return -1;
+    }
+    header[4] = '\0';
+    size_t size;
+    if (sscanf(header, "%04zx", &size) != 1) {
+        QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
+        return -1;
+    }
+    if (size > len) {
+        QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
+                        len);
+        return -1;
+    }
+    ret = TEMP_FAILURE_RETRY(read(fd, buff, size));
+    if (ret != (ssize_t)size) {
+        QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
+                        strerror(errno));
+        return -1;
+    }
+    return size;
+}
+
+#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_PIPE_H */
diff --git a/init/README.md b/init/README.md
index 822d81e..fc50730 100644
--- a/init/README.md
+++ b/init/README.md
@@ -293,6 +293,11 @@
 `copy <src> <dst>`
 > Copies a file. Similar to write, but useful for binary/large
   amounts of data.
+  Regarding to the src file, copying from symbolic link file and world-writable
+  or group-writable files are not allowed.
+  Regarding to the dst file, the default mode created is 0600 if it does not
+  exist. And it will be truncated if dst file is a normal regular file and
+  already exists.
 
 `domainname <name>`
 > Set the domain name.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 4ad36e0..64c00e9 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -700,61 +700,11 @@
 }
 
 static int do_copy(const std::vector<std::string>& args) {
-    char *buffer = NULL;
-    int rc = 0;
-    int fd1 = -1, fd2 = -1;
-    struct stat info;
-    int brtw, brtr;
-    char *p;
-
-    if (stat(args[1].c_str(), &info) < 0)
-        return -1;
-
-    if ((fd1 = open(args[1].c_str(), O_RDONLY|O_CLOEXEC)) < 0)
-        goto out_err;
-
-    if ((fd2 = open(args[2].c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
-        goto out_err;
-
-    if (!(buffer = (char*) malloc(info.st_size)))
-        goto out_err;
-
-    p = buffer;
-    brtr = info.st_size;
-    while(brtr) {
-        rc = read(fd1, p, brtr);
-        if (rc < 0)
-            goto out_err;
-        if (rc == 0)
-            break;
-        p += rc;
-        brtr -= rc;
+    std::string data;
+    if (read_file(args[1], &data)) {
+        return write_file(args[2], data) ? 0 : 1;
     }
-
-    p = buffer;
-    brtw = info.st_size;
-    while(brtw) {
-        rc = write(fd2, p, brtw);
-        if (rc < 0)
-            goto out_err;
-        if (rc == 0)
-            break;
-        p += rc;
-        brtw -= rc;
-    }
-
-    rc = 0;
-    goto out;
-out_err:
-    rc = -1;
-out:
-    if (buffer)
-        free(buffer);
-    if (fd1 >= 0)
-        close(fd1);
-    if (fd2 >= 0)
-        close(fd2);
-    return rc;
+    return 1;
 }
 
 static int do_chown(const std::vector<std::string>& args) {
diff --git a/init/util.cpp b/init/util.cpp
index d1e0b85..bf4109c 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -188,8 +188,8 @@
 }
 
 bool write_file(const std::string& path, const std::string& content) {
-    android::base::unique_fd fd(
-        TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 0600)));
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+        open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
     if (fd == -1) {
         PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
         return false;
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 6134998..0c0350a 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -17,7 +17,10 @@
 #include "util.h"
 
 #include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
 
+#include <android-base/stringprintf.h>
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
@@ -29,6 +32,35 @@
   EXPECT_EQ("", s); // s was cleared.
 }
 
+TEST(util, read_file_group_writeable) {
+    std::string s("hello");
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    EXPECT_TRUE(write_file(tf.path, s)) << strerror(errno);
+    EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
+    EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+    EXPECT_EQ("", s);  // s was cleared.
+}
+
+TEST(util, read_file_world_writeable) {
+    std::string s("hello");
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
+    EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
+    EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+    EXPECT_EQ("", s);  // s was cleared.
+}
+
+TEST(util, read_file_symbolic_link) {
+    std::string s("hello");
+    errno = 0;
+    // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
+    EXPECT_FALSE(read_file("/charger", &s));
+    EXPECT_EQ(ELOOP, errno);
+    EXPECT_EQ("", s);  // s was cleared.
+}
+
 TEST(util, read_file_success) {
   std::string s("hello");
   EXPECT_TRUE(read_file("/proc/version", &s));
@@ -55,6 +87,34 @@
     EXPECT_EQ(10u, read_back_contents.size());
 }
 
+TEST(util, write_file_not_exist) {
+    std::string s("hello");
+    std::string s2("hello");
+    TemporaryDir test_dir;
+    std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
+    EXPECT_TRUE(write_file(path, s));
+    EXPECT_TRUE(read_file(path, &s2));
+    EXPECT_EQ(s, s2);
+    struct stat sb;
+    int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+    EXPECT_NE(-1, fd);
+    EXPECT_EQ(0, fstat(fd, &sb));
+    EXPECT_EQ((const unsigned int)(S_IRUSR | S_IWUSR), sb.st_mode & 0777);
+    EXPECT_EQ(0, unlink(path.c_str()));
+}
+
+TEST(util, write_file_exist) {
+    std::string s2("");
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    EXPECT_TRUE(write_file(tf.path, "1hello1")) << strerror(errno);
+    EXPECT_TRUE(read_file(tf.path, &s2));
+    EXPECT_STREQ("1hello1", s2.c_str());
+    EXPECT_TRUE(write_file(tf.path, "2ll2"));
+    EXPECT_TRUE(read_file(tf.path, &s2));
+    EXPECT_STREQ("2ll2", s2.c_str());
+}
+
 TEST(util, decode_uid) {
   EXPECT_EQ(0U, decode_uid("root"));
   EXPECT_EQ(UINT_MAX, decode_uid("toot"));
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 6a57a41..daa9ff5 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -35,22 +35,13 @@
 
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
 #include <utils/Compat.h>
 
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
 
-/* The following structure is stored little endian */
-struct fs_path_config_from_file {
-    uint16_t len;
-    uint16_t mode;
-    uint16_t uid;
-    uint16_t gid;
-    uint64_t capabilities;
-    char prefix[];
-} __attribute__((__aligned__(sizeof(uint64_t))));
-
 /* My kingdom for <endian.h> */
 static inline uint16_t get2LE(const uint8_t* src) { return src[0] | (src[1] << 8); }
 
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
new file mode 100644
index 0000000..7dad668
--- /dev/null
+++ b/libcutils/include/private/fs_config.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 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 _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
+#define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
+
+#include <stdint.h>
+
+/*
+ * binary format for the runtime <partition>/etc/fs_config_(dirs|files)
+ * filesystem override files.
+ */
+
+/* The following structure is stored little endian */
+struct fs_path_config_from_file {
+    uint16_t len;
+    uint16_t mode;
+    uint16_t uid;
+    uint16_t gid;
+    uint64_t capabilities;
+    char prefix[];
+} __attribute__((__aligned__(sizeof(uint64_t))));
+
+#endif /* _LIBS_CUTILS_PRIVATE_FS_CONFIG_H */
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
index 2da204a..4dedf7f 100644
--- a/libsuspend/autosuspend_wakeup_count.c
+++ b/libsuspend/autosuspend_wakeup_count.c
@@ -24,7 +24,6 @@
 #include <stddef.h>
 #include <stdbool.h>
 #include <string.h>
-#include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -36,24 +35,12 @@
 #define SYS_POWER_STATE "/sys/power/state"
 #define SYS_POWER_WAKEUP_COUNT "/sys/power/wakeup_count"
 
-#define BASE_SLEEP_TIME 100000
-
 static int state_fd;
 static int wakeup_count_fd;
 static pthread_t suspend_thread;
 static sem_t suspend_lockout;
 static const char *sleep_state = "mem";
 static void (*wakeup_func)(bool success) = NULL;
-static int sleep_time = BASE_SLEEP_TIME;
-
-static void update_sleep_time(bool success) {
-    if (success) {
-        sleep_time = BASE_SLEEP_TIME;
-        return;
-    }
-    // double sleep time after each failure up to one minute
-    sleep_time = MIN(sleep_time * 2, 60000000);
-}
 
 static void *suspend_thread_func(void *arg __attribute__((unused)))
 {
@@ -61,12 +48,10 @@
     char wakeup_count[20];
     int wakeup_count_len;
     int ret;
-    bool success = true;
+    bool success;
 
     while (1) {
-        update_sleep_time(success);
-        usleep(sleep_time);
-        success = false;
+        usleep(100000);
         ALOGV("%s: read wakeup_count\n", __func__);
         lseek(wakeup_count_fd, 0, SEEK_SET);
         wakeup_count_len = TEMP_FAILURE_RETRY(read(wakeup_count_fd, wakeup_count,
@@ -90,6 +75,7 @@
             continue;
         }
 
+        success = true;
         ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count);
         ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len));
         if (ret < 0) {
@@ -98,8 +84,8 @@
         } else {
             ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE);
             ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state)));
-            if (ret >= 0) {
-                success = true;
+            if (ret < 0) {
+                success = false;
             }
             void (*func)(bool success) = wakeup_func;
             if (func != NULL) {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index dabeac1..17ede51 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -48,6 +48,7 @@
     srcs: [
         "ArmExidx.cpp",
         "DwarfMemory.cpp",
+        "DwarfOp.cpp",
         "Elf.cpp",
         "ElfInterface.cpp",
         "ElfInterfaceArm.cpp",
@@ -89,6 +90,8 @@
         "tests/ArmExidxDecodeTest.cpp",
         "tests/ArmExidxExtractTest.cpp",
         "tests/DwarfMemoryTest.cpp",
+        "tests/DwarfOpLogTest.cpp",
+        "tests/DwarfOpTest.cpp",
         "tests/ElfInterfaceArmTest.cpp",
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h
new file mode 100644
index 0000000..824c307
--- /dev/null
+++ b/libunwindstack/DwarfError.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_ERROR_H
+#define _LIBUNWINDSTACK_DWARF_ERROR_H
+
+#include <stdint.h>
+
+enum DwarfError : uint8_t {
+  DWARF_ERROR_NONE,
+  DWARF_ERROR_MEMORY_INVALID,
+  DWARF_ERROR_ILLEGAL_VALUE,
+  DWARF_ERROR_ILLEGAL_STATE,
+  DWARF_ERROR_STACK_INDEX_NOT_VALID,
+  DWARF_ERROR_NOT_IMPLEMENTED,
+  DWARF_ERROR_TOO_MANY_ITERATIONS,
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
new file mode 100644
index 0000000..507ca08
--- /dev/null
+++ b/libunwindstack/DwarfOp.cpp
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Memory.h"
+#include "Regs.h"
+
+template <typename AddressType>
+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;
+  is_register_ = false;
+  stack_.clear();
+  memory_->set_cur_offset(start);
+  while (memory_->cur_offset() < end) {
+    if (!Decode(dwarf_version)) {
+      return false;
+    }
+    // To protect against a branch that creates an infinite loop,
+    // terminate if the number of iterations gets too high.
+    if (iterations++ == 1000) {
+      last_error_ = DWARF_ERROR_TOO_MANY_ITERATIONS;
+      return false;
+    }
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::Decode(uint8_t dwarf_version) {
+  last_error_ = DWARF_ERROR_NONE;
+  if (!memory_->ReadBytes(&cur_op_, 1)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  const auto* op = &kCallbackTable[cur_op_];
+  const auto handle_func = op->handle_func;
+  if (handle_func == nullptr) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+
+  // Check for an unsupported opcode.
+  if (dwarf_version < op->supported_version) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+
+  // Make sure that the required number of stack elements is available.
+  if (stack_.size() < op->num_required_stack_values) {
+    last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+    return false;
+  }
+
+  operands_.clear();
+  for (size_t i = 0; i < op->num_operands; i++) {
+    uint64_t value;
+    if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    operands_.push_back(value);
+  }
+  return (this->*handle_func)();
+}
+
+template <typename AddressType>
+void DwarfOp<AddressType>::GetLogInfo(uint64_t start, uint64_t end,
+                                      std::vector<std::string>* lines) {
+  memory_->set_cur_offset(start);
+  while (memory_->cur_offset() < end) {
+    uint8_t cur_op;
+    if (!memory_->ReadBytes(&cur_op, 1)) {
+      return;
+    }
+
+    std::string raw_string(android::base::StringPrintf("Raw Data: 0x%02x", cur_op));
+    std::string log_string;
+    const auto* op = &kCallbackTable[cur_op];
+    if (op->handle_func == nullptr) {
+      log_string = "Illegal";
+    } else {
+      log_string = op->name;
+      uint64_t start_offset = memory_->cur_offset();
+      for (size_t i = 0; i < op->num_operands; i++) {
+        uint64_t value;
+        if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
+          return;
+        }
+        log_string += ' ' + std::to_string(value);
+      }
+      uint64_t end_offset = memory_->cur_offset();
+
+      memory_->set_cur_offset(start_offset);
+      for (size_t i = start_offset; i < end_offset; i++) {
+        uint8_t byte;
+        if (!memory_->ReadBytes(&byte, 1)) {
+          return;
+        }
+        raw_string += android::base::StringPrintf(" 0x%02x", byte);
+      }
+      memory_->set_cur_offset(end_offset);
+    }
+    lines->push_back(std::move(log_string));
+    lines->push_back(std::move(raw_string));
+  }
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_deref() {
+  // Read the address and dereference it.
+  AddressType addr = StackPop();
+  AddressType value;
+  if (!regular_memory()->Read(addr, &value, sizeof(value))) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  stack_.push_front(value);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_deref_size() {
+  AddressType bytes_to_read = OperandAt(0);
+  if (bytes_to_read > sizeof(AddressType) || bytes_to_read == 0) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+  // Read the address and dereference it.
+  AddressType addr = StackPop();
+  AddressType value = 0;
+  if (!regular_memory()->Read(addr, &value, bytes_to_read)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  stack_.push_front(value);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_push() {
+  // Push all of the operands.
+  for (auto operand : operands_) {
+    stack_.push_front(operand);
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_dup() {
+  stack_.push_front(StackAt(0));
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_drop() {
+  StackPop();
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_over() {
+  stack_.push_front(StackAt(1));
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_pick() {
+  AddressType index = OperandAt(0);
+  if (index > StackSize()) {
+    last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+    return false;
+  }
+  stack_.push_front(StackAt(index));
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_swap() {
+  AddressType old_value = stack_[0];
+  stack_[0] = stack_[1];
+  stack_[1] = old_value;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_rot() {
+  AddressType top = stack_[0];
+  stack_[0] = stack_[1];
+  stack_[1] = stack_[2];
+  stack_[2] = top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_abs() {
+  SignedType signed_value = static_cast<SignedType>(stack_[0]);
+  if (signed_value < 0) {
+    signed_value = -signed_value;
+  }
+  stack_[0] = static_cast<AddressType>(signed_value);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_and() {
+  AddressType top = StackPop();
+  stack_[0] &= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_div() {
+  AddressType top = StackPop();
+  if (top == 0) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+  SignedType signed_divisor = static_cast<SignedType>(top);
+  SignedType signed_dividend = static_cast<SignedType>(stack_[0]);
+  stack_[0] = static_cast<AddressType>(signed_dividend / signed_divisor);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_minus() {
+  AddressType top = StackPop();
+  stack_[0] -= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_mod() {
+  AddressType top = StackPop();
+  if (top == 0) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+  stack_[0] %= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_mul() {
+  AddressType top = StackPop();
+  stack_[0] *= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_neg() {
+  SignedType signed_value = static_cast<SignedType>(stack_[0]);
+  stack_[0] = static_cast<AddressType>(-signed_value);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_not() {
+  stack_[0] = ~stack_[0];
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_or() {
+  AddressType top = StackPop();
+  stack_[0] |= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_plus() {
+  AddressType top = StackPop();
+  stack_[0] += top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_plus_uconst() {
+  stack_[0] += OperandAt(0);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shl() {
+  AddressType top = StackPop();
+  stack_[0] <<= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shr() {
+  AddressType top = StackPop();
+  stack_[0] >>= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shra() {
+  AddressType top = StackPop();
+  SignedType signed_value = static_cast<SignedType>(stack_[0]) >> top;
+  stack_[0] = static_cast<AddressType>(signed_value);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_xor() {
+  AddressType top = StackPop();
+  stack_[0] ^= top;
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_bra() {
+  // Requires one stack element.
+  AddressType top = StackPop();
+  int16_t offset = static_cast<int16_t>(OperandAt(0));
+  uint64_t cur_offset;
+  if (top != 0) {
+    cur_offset = memory_->cur_offset() + offset;
+  } else {
+    cur_offset = memory_->cur_offset() - offset;
+  }
+  memory_->set_cur_offset(cur_offset);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_eq() {
+  AddressType top = StackPop();
+  stack_[0] = bool_to_dwarf_bool(stack_[0] == top);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_ge() {
+  AddressType top = StackPop();
+  stack_[0] = bool_to_dwarf_bool(stack_[0] >= top);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_gt() {
+  AddressType top = StackPop();
+  stack_[0] = bool_to_dwarf_bool(stack_[0] > top);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_le() {
+  AddressType top = StackPop();
+  stack_[0] = bool_to_dwarf_bool(stack_[0] <= top);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_lt() {
+  AddressType top = StackPop();
+  stack_[0] = bool_to_dwarf_bool(stack_[0] < top);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_ne() {
+  AddressType top = StackPop();
+  stack_[0] = bool_to_dwarf_bool(stack_[0] != top);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_skip() {
+  int16_t offset = static_cast<int16_t>(OperandAt(0));
+  uint64_t cur_offset = memory_->cur_offset() + offset;
+  memory_->set_cur_offset(cur_offset);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_lit() {
+  stack_.push_front(cur_op() - 0x30);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_reg() {
+  is_register_ = true;
+  stack_.push_front(cur_op() - 0x50);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_regx() {
+  is_register_ = true;
+  stack_.push_front(OperandAt(0));
+  return true;
+}
+
+// It's not clear for breg/bregx, if this op should read the current
+// value of the register, or where we think that register is located.
+// For simplicity, the code will read the value before doing the unwind.
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_breg() {
+  uint16_t reg = cur_op() - 0x70;
+  if (reg >= regs_->total_regs()) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+  stack_.push_front((*regs_)[reg] + OperandAt(0));
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_bregx() {
+  AddressType reg = OperandAt(0);
+  if (reg >= regs_->total_regs()) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+  stack_.push_front((*regs_)[reg] + OperandAt(1));
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_nop() {
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_not_implemented() {
+  last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+  return false;
+}
+
+// Explicitly instantiate DwarfOp.
+template class DwarfOp<uint32_t>;
+template class DwarfOp<uint64_t>;
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
new file mode 100644
index 0000000..ed6537a
--- /dev/null
+++ b/libunwindstack/DwarfOp.h
@@ -0,0 +1,1636 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_OP_H
+#define _LIBUNWINDSTACK_DWARF_OP_H
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "DwarfEncoding.h"
+
+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;
+template <typename AddressType>
+class RegsTmpl;
+
+template <typename AddressType>
+class DwarfOp {
+  // Signed version of AddressType
+  typedef typename std::make_signed<AddressType>::type SignedType;
+
+  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];
+  };
+
+ public:
+  DwarfOp(DwarfMemory* memory, Memory* regular_memory)
+      : memory_(memory), regular_memory_(regular_memory) {}
+  virtual ~DwarfOp() = default;
+
+  bool Decode(uint8_t dwarf_version);
+
+  bool Eval(uint64_t start, uint64_t end, uint8_t dwarf_version);
+
+  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(RegsTmpl<AddressType>* regs) { regs_ = regs; }
+
+  DwarfError last_error() { return last_error_; }
+
+  bool is_register() { return is_register_; }
+
+  uint8_t cur_op() { return cur_op_; }
+
+  Memory* regular_memory() { return regular_memory_; }
+
+ protected:
+  AddressType OperandAt(size_t index) { return operands_[index]; }
+  size_t OperandsSize() { return operands_.size(); }
+
+  AddressType StackPop() {
+    AddressType value = stack_.front();
+    stack_.pop_front();
+    return value;
+  }
+
+ private:
+  DwarfMemory* memory_;
+  Memory* regular_memory_;
+
+  RegsTmpl<AddressType>* regs_;
+  bool is_register_ = false;
+  DwarfError last_error_ = DWARF_ERROR_NONE;
+  uint8_t cur_op_;
+  std::vector<AddressType> operands_;
+  std::deque<AddressType> stack_;
+
+  inline AddressType bool_to_dwarf_bool(bool value) { return value ? 1 : 0; }
+
+  // Op processing functions.
+  bool op_deref();
+  bool op_deref_size();
+  bool op_push();
+  bool op_dup();
+  bool op_drop();
+  bool op_over();
+  bool op_pick();
+  bool op_swap();
+  bool op_rot();
+  bool op_abs();
+  bool op_and();
+  bool op_div();
+  bool op_minus();
+  bool op_mod();
+  bool op_mul();
+  bool op_neg();
+  bool op_not();
+  bool op_or();
+  bool op_plus();
+  bool op_plus_uconst();
+  bool op_shl();
+  bool op_shr();
+  bool op_shra();
+  bool op_xor();
+  bool op_bra();
+  bool op_eq();
+  bool op_ge();
+  bool op_gt();
+  bool op_le();
+  bool op_lt();
+  bool op_ne();
+  bool op_skip();
+  bool op_lit();
+  bool op_reg();
+  bool op_regx();
+  bool op_breg();
+  bool op_bregx();
+  bool op_nop();
+  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
+      {
+          // 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
+      {
+          // 0x06 DW_OP_deref
+          "DW_OP_deref",
+          &DwarfOp::op_deref,
+          DWARF_VERSION_2,
+          1,
+          0,
+          {},
+      },
+      {nullptr, nullptr, 0, 0, 0, {}},  // 0x07 illegal op
+      {
+          // 0x08 DW_OP_const1u
+          "DW_OP_const1u",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_udata1},
+      },
+      {
+          // 0x09 DW_OP_const1s
+          "DW_OP_const1s",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sdata1},
+      },
+      {
+          // 0x0a DW_OP_const2u
+          "DW_OP_const2u",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_udata2},
+      },
+      {
+          // 0x0b DW_OP_const2s
+          "DW_OP_const2s",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sdata2},
+      },
+      {
+          // 0x0c DW_OP_const4u
+          "DW_OP_const4u",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_udata4},
+      },
+      {
+          // 0x0d DW_OP_const4s
+          "DW_OP_const4s",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sdata4},
+      },
+      {
+          // 0x0e DW_OP_const8u
+          "DW_OP_const8u",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_udata8},
+      },
+      {
+          // 0x0f DW_OP_const8s
+          "DW_OP_const8s",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sdata8},
+      },
+      {
+          // 0x10 DW_OP_constu
+          "DW_OP_constu",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_uleb128},
+      },
+      {
+          // 0x11 DW_OP_consts
+          "DW_OP_consts",
+          &DwarfOp::op_push,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x12 DW_OP_dup
+          "DW_OP_dup",
+          &DwarfOp::op_dup,
+          DWARF_VERSION_2,
+          1,
+          0,
+          {},
+      },
+      {
+          // 0x13 DW_OP_drop
+          "DW_OP_drop",
+          &DwarfOp::op_drop,
+          DWARF_VERSION_2,
+          1,
+          0,
+          {},
+      },
+      {
+          // 0x14 DW_OP_over
+          "DW_OP_over",
+          &DwarfOp::op_over,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x15 DW_OP_pick
+          "DW_OP_pick",
+          &DwarfOp::op_pick,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_udata1},
+      },
+      {
+          // 0x16 DW_OP_swap
+          "DW_OP_swap",
+          &DwarfOp::op_swap,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x17 DW_OP_rot
+          "DW_OP_rot",
+          &DwarfOp::op_rot,
+          DWARF_VERSION_2,
+          3,
+          0,
+          {},
+      },
+      {
+          // 0x18 DW_OP_xderef
+          "DW_OP_xderef",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x19 DW_OP_abs
+          "DW_OP_abs",
+          &DwarfOp::op_abs,
+          DWARF_VERSION_2,
+          1,
+          0,
+          {},
+      },
+      {
+          // 0x1a DW_OP_and
+          "DW_OP_and",
+          &DwarfOp::op_and,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x1b DW_OP_div
+          "DW_OP_div",
+          &DwarfOp::op_div,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x1c DW_OP_minus
+          "DW_OP_minus",
+          &DwarfOp::op_minus,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x1d DW_OP_mod
+          "DW_OP_mod",
+          &DwarfOp::op_mod,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x1e DW_OP_mul
+          "DW_OP_mul",
+          &DwarfOp::op_mul,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x1f DW_OP_neg
+          "DW_OP_neg",
+          &DwarfOp::op_neg,
+          DWARF_VERSION_2,
+          1,
+          0,
+          {},
+      },
+      {
+          // 0x20 DW_OP_not
+          "DW_OP_not",
+          &DwarfOp::op_not,
+          DWARF_VERSION_2,
+          1,
+          0,
+          {},
+      },
+      {
+          // 0x21 DW_OP_or
+          "DW_OP_or",
+          &DwarfOp::op_or,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x22 DW_OP_plus
+          "DW_OP_plus",
+          &DwarfOp::op_plus,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x23 DW_OP_plus_uconst
+          "DW_OP_plus_uconst",
+          &DwarfOp::op_plus_uconst,
+          DWARF_VERSION_2,
+          1,
+          1,
+          {DW_EH_PE_uleb128},
+      },
+      {
+          // 0x24 DW_OP_shl
+          "DW_OP_shl",
+          &DwarfOp::op_shl,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x25 DW_OP_shr
+          "DW_OP_shr",
+          &DwarfOp::op_shr,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x26 DW_OP_shra
+          "DW_OP_shra",
+          &DwarfOp::op_shra,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x27 DW_OP_xor
+          "DW_OP_xor",
+          &DwarfOp::op_xor,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x28 DW_OP_bra
+          "DW_OP_bra",
+          &DwarfOp::op_bra,
+          DWARF_VERSION_2,
+          1,
+          1,
+          {DW_EH_PE_sdata2},
+      },
+      {
+          // 0x29 DW_OP_eq
+          "DW_OP_eq",
+          &DwarfOp::op_eq,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x2a DW_OP_ge
+          "DW_OP_ge",
+          &DwarfOp::op_ge,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x2b DW_OP_gt
+          "DW_OP_gt",
+          &DwarfOp::op_gt,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x2c DW_OP_le
+          "DW_OP_le",
+          &DwarfOp::op_le,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x2d DW_OP_lt
+          "DW_OP_lt",
+          &DwarfOp::op_lt,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x2e DW_OP_ne
+          "DW_OP_ne",
+          &DwarfOp::op_ne,
+          DWARF_VERSION_2,
+          2,
+          0,
+          {},
+      },
+      {
+          // 0x2f DW_OP_skip
+          "DW_OP_skip",
+          &DwarfOp::op_skip,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sdata2},
+      },
+      {
+          // 0x30 DW_OP_lit0
+          "DW_OP_lit0",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x31 DW_OP_lit1
+          "DW_OP_lit1",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x32 DW_OP_lit2
+          "DW_OP_lit2",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x33 DW_OP_lit3
+          "DW_OP_lit3",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x34 DW_OP_lit4
+          "DW_OP_lit4",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x35 DW_OP_lit5
+          "DW_OP_lit5",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x36 DW_OP_lit6
+          "DW_OP_lit6",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x37 DW_OP_lit7
+          "DW_OP_lit7",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x38 DW_OP_lit8
+          "DW_OP_lit8",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x39 DW_OP_lit9
+          "DW_OP_lit9",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x3a DW_OP_lit10
+          "DW_OP_lit10",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x3b DW_OP_lit11
+          "DW_OP_lit11",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x3c DW_OP_lit12
+          "DW_OP_lit12",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x3d DW_OP_lit13
+          "DW_OP_lit13",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x3e DW_OP_lit14
+          "DW_OP_lit14",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x3f DW_OP_lit15
+          "DW_OP_lit15",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x40 DW_OP_lit16
+          "DW_OP_lit16",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x41 DW_OP_lit17
+          "DW_OP_lit17",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x42 DW_OP_lit18
+          "DW_OP_lit18",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x43 DW_OP_lit19
+          "DW_OP_lit19",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x44 DW_OP_lit20
+          "DW_OP_lit20",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x45 DW_OP_lit21
+          "DW_OP_lit21",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x46 DW_OP_lit22
+          "DW_OP_lit22",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x47 DW_OP_lit23
+          "DW_OP_lit23",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x48 DW_OP_lit24
+          "DW_OP_lit24",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x49 DW_OP_lit25
+          "DW_OP_lit25",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x4a DW_OP_lit26
+          "DW_OP_lit26",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x4b DW_OP_lit27
+          "DW_OP_lit27",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x4c DW_OP_lit28
+          "DW_OP_lit28",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x4d DW_OP_lit29
+          "DW_OP_lit29",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x4e DW_OP_lit30
+          "DW_OP_lit30",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x4f DW_OP_lit31
+          "DW_OP_lit31",
+          &DwarfOp::op_lit,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x50 DW_OP_reg0
+          "DW_OP_reg0",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x51 DW_OP_reg1
+          "DW_OP_reg1",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x52 DW_OP_reg2
+          "DW_OP_reg2",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x53 DW_OP_reg3
+          "DW_OP_reg3",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x54 DW_OP_reg4
+          "DW_OP_reg4",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x55 DW_OP_reg5
+          "DW_OP_reg5",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x56 DW_OP_reg6
+          "DW_OP_reg6",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x57 DW_OP_reg7
+          "DW_OP_reg7",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x58 DW_OP_reg8
+          "DW_OP_reg8",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x59 DW_OP_reg9
+          "DW_OP_reg9",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x5a DW_OP_reg10
+          "DW_OP_reg10",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x5b DW_OP_reg11
+          "DW_OP_reg11",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x5c DW_OP_reg12
+          "DW_OP_reg12",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x5d DW_OP_reg13
+          "DW_OP_reg13",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x5e DW_OP_reg14
+          "DW_OP_reg14",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x5f DW_OP_reg15
+          "DW_OP_reg15",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x60 DW_OP_reg16
+          "DW_OP_reg16",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x61 DW_OP_reg17
+          "DW_OP_reg17",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x62 DW_OP_reg18
+          "DW_OP_reg18",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x63 DW_OP_reg19
+          "DW_OP_reg19",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x64 DW_OP_reg20
+          "DW_OP_reg20",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x65 DW_OP_reg21
+          "DW_OP_reg21",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x66 DW_OP_reg22
+          "DW_OP_reg22",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x67 DW_OP_reg23
+          "DW_OP_reg23",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x68 DW_OP_reg24
+          "DW_OP_reg24",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x69 DW_OP_reg25
+          "DW_OP_reg25",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x6a DW_OP_reg26
+          "DW_OP_reg26",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x6b DW_OP_reg27
+          "DW_OP_reg27",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x6c DW_OP_reg28
+          "DW_OP_reg28",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x6d DW_OP_reg29
+          "DW_OP_reg29",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x6e DW_OP_reg30
+          "DW_OP_reg30",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x6f DW_OP_reg31
+          "DW_OP_reg31",
+          &DwarfOp::op_reg,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x70 DW_OP_breg0
+          "DW_OP_breg0",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x71 DW_OP_breg1
+          "DW_OP_breg1",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x72 DW_OP_breg2
+          "DW_OP_breg2",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x73 DW_OP_breg3
+          "DW_OP_breg3",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x74 DW_OP_breg4
+          "DW_OP_breg4",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x75 DW_OP_breg5
+          "DW_OP_breg5",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x76 DW_OP_breg6
+          "DW_OP_breg6",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x77 DW_OP_breg7
+          "DW_OP_breg7",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x78 DW_OP_breg8
+          "DW_OP_breg8",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x79 DW_OP_breg9
+          "DW_OP_breg9",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x7a DW_OP_breg10
+          "DW_OP_breg10",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x7b DW_OP_breg11
+          "DW_OP_breg11",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x7c DW_OP_breg12
+          "DW_OP_breg12",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x7d DW_OP_breg13
+          "DW_OP_breg13",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x7e DW_OP_breg14
+          "DW_OP_breg14",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x7f DW_OP_breg15
+          "DW_OP_breg15",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x80 DW_OP_breg16
+          "DW_OP_breg16",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x81 DW_OP_breg17
+          "DW_OP_breg17",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x82 DW_OP_breg18
+          "DW_OP_breg18",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x83 DW_OP_breg19
+          "DW_OP_breg19",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x84 DW_OP_breg20
+          "DW_OP_breg20",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x85 DW_OP_breg21
+          "DW_OP_breg21",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x86 DW_OP_breg22
+          "DW_OP_breg22",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x87 DW_OP_breg23
+          "DW_OP_breg23",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x88 DW_OP_breg24
+          "DW_OP_breg24",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x89 DW_OP_breg25
+          "DW_OP_breg25",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x8a DW_OP_breg26
+          "DW_OP_breg26",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x8b DW_OP_breg27
+          "DW_OP_breg27",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x8c DW_OP_breg28
+          "DW_OP_breg28",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x8d DW_OP_breg29
+          "DW_OP_breg29",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x8e DW_OP_breg30
+          "DW_OP_breg30",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x8f DW_OP_breg31
+          "DW_OP_breg31",
+          &DwarfOp::op_breg,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x90 DW_OP_regx
+          "DW_OP_regx",
+          &DwarfOp::op_regx,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_uleb128},
+      },
+      {
+          // 0x91 DW_OP_fbreg
+          "DW_OP_fbreg",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_sleb128},
+      },
+      {
+          // 0x92 DW_OP_bregx
+          "DW_OP_bregx",
+          &DwarfOp::op_bregx,
+          DWARF_VERSION_2,
+          0,
+          2,
+          {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+      },
+      {
+          // 0x93 DW_OP_piece
+          "DW_OP_piece",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_uleb128},
+      },
+      {
+          // 0x94 DW_OP_deref_size
+          "DW_OP_deref_size",
+          &DwarfOp::op_deref_size,
+          DWARF_VERSION_2,
+          1,
+          1,
+          {DW_EH_PE_udata1},
+      },
+      {
+          // 0x95 DW_OP_xderef_size
+          "DW_OP_xderef_size",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_2,
+          0,
+          1,
+          {DW_EH_PE_udata1},
+      },
+      {
+          // 0x96 DW_OP_nop
+          "DW_OP_nop",
+          &DwarfOp::op_nop,
+          DWARF_VERSION_2,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x97 DW_OP_push_object_address
+          "DW_OP_push_object_address",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_3,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x98 DW_OP_call2
+          "DW_OP_call2",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_3,
+          0,
+          1,
+          {DW_EH_PE_udata2},
+      },
+      {
+          // 0x99 DW_OP_call4
+          "DW_OP_call4",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_3,
+          0,
+          1,
+          {DW_EH_PE_udata4},
+      },
+      {
+          // 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).
+          {},
+      },
+      {
+          // 0x9b DW_OP_form_tls_address
+          "DW_OP_form_tls_address",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_3,
+          0,
+          0,
+          {},
+      },
+      {
+          // 0x9c DW_OP_call_frame_cfa
+          "DW_OP_call_frame_cfa",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_3,
+          0,
+          0,
+          {},
+      },
+      {
+          // 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},
+      },
+      {
+          // 0x9e DW_OP_implicit_value
+          "DW_OP_implicit_value",
+          &DwarfOp::op_not_implemented,
+          DWARF_VERSION_4,
+          0,
+          1,
+          {DW_EH_PE_uleb128},
+      },
+      {
+          // 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
+  };
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index d59e9d8..087457c 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -42,7 +42,7 @@
   uint64_t offset = ehdr.e_phoff;
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
     PhdrType phdr;
-    if (!memory_->Read(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
+    if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
       return false;
     }
 
@@ -54,20 +54,20 @@
     case PT_LOAD:
     {
       // Get the flags first, if this isn't an executable header, ignore it.
-      if (!memory_->Read(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
         return false;
       }
       if ((phdr.p_flags & PF_X) == 0) {
         continue;
       }
 
-      if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
         return false;
       }
-      if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
         return false;
       }
-      if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
         return false;
       }
       pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
@@ -79,22 +79,22 @@
     }
 
     case PT_GNU_EH_FRAME:
-      if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
         return false;
       }
       eh_frame_offset_ = phdr.p_offset;
-      if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
         return false;
       }
       eh_frame_size_ = phdr.p_memsz;
       break;
 
     case PT_DYNAMIC:
-      if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
         return false;
       }
       dynamic_offset_ = phdr.p_offset;
-      if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
         return false;
       }
       dynamic_size_ = phdr.p_memsz;
@@ -116,8 +116,8 @@
   ShdrType shdr;
   if (ehdr.e_shstrndx < ehdr.e_shnum) {
     uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
-    if (memory_->Read(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
-        && memory_->Read(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+    if (memory_->ReadField(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+        memory_->ReadField(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
       sec_offset = shdr.sh_offset;
       sec_size = shdr.sh_size;
     }
@@ -125,27 +125,27 @@
 
   // Skip the first header, it's always going to be NULL.
   for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
-    if (!memory_->Read(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
+    if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
       return false;
     }
 
     if (shdr.sh_type == SHT_PROGBITS) {
       // Look for the .debug_frame and .gnu_debugdata.
-      if (!memory_->Read(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
+      if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
         return false;
       }
       if (shdr.sh_name < sec_size) {
         std::string name;
         if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
           if (name == ".debug_frame") {
-            if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
-                && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+            if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+                memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
               debug_frame_offset_ = shdr.sh_offset;
               debug_frame_size_ = shdr.sh_size;
             }
           } else if (name == ".gnu_debugdata") {
-            if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
-                && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+            if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+                memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
               gnu_debugdata_offset_ = shdr.sh_offset;
               gnu_debugdata_size_ = shdr.sh_size;
             }
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index e157320..bab84cc 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -85,10 +85,10 @@
   }
 
   Elf32_Phdr phdr;
-  if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+  if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
     return true;
   }
-  if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+  if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
     return true;
   }
   // The load_bias_ should always be set by this time.
@@ -98,13 +98,15 @@
 }
 
 bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
-  return StepExidx(pc, regs, process_memory) ||
-      ElfInterface32::Step(pc, regs, process_memory);
+  // Dwarf unwind information is precise about whether a pc is covered or not,
+  // but arm unwind information only has ranges of pc. In order to avoid
+  // incorrectly doing a bad unwind using arm unwind information for a
+  // different function, always try and unwind with the dwarf information first.
+  return ElfInterface32::Step(pc, regs, process_memory) || StepExidx(pc, regs, process_memory);
 }
 
 bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) {
   RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
-  // First try arm, then try dwarf.
   uint64_t entry_offset;
   if (!FindEntry(pc, &entry_offset)) {
     return false;
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 1fcf842..9e46509 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -96,10 +96,16 @@
 
   offset_ = offset & (getpagesize() - 1);
   uint64_t aligned_offset = offset & ~(getpagesize() - 1);
+  if (aligned_offset > static_cast<uint64_t>(buf.st_size) ||
+      offset > static_cast<uint64_t>(buf.st_size)) {
+    return false;
+  }
+
   size_ = buf.st_size - aligned_offset;
-  if (size < (UINT64_MAX - offset_) && size + offset_ < size_) {
+  uint64_t max_size;
+  if (!__builtin_add_overflow(size, offset_, &max_size) && max_size < size_) {
     // Truncate the mapped size.
-    size_ = size + offset_;
+    size_ = max_size;
   }
   void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
   if (map == MAP_FAILED) {
@@ -113,14 +119,15 @@
 }
 
 bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
-  if (addr + size > size_) {
+  uint64_t max_size;
+  if (__builtin_add_overflow(addr, size, &max_size) || max_size > size_) {
     return false;
   }
   memcpy(dst, &data_[addr], size);
   return true;
 }
 
-static bool PtraceRead(pid_t pid, uint64_t addr, long* value) {
+bool MemoryRemote::PtraceRead(uint64_t addr, long* value) {
 #if !defined(__LP64__)
   // Cannot read an address greater than 32 bits.
   if (addr > UINT32_MAX) {
@@ -130,7 +137,7 @@
   // ptrace() returns -1 and sets errno when the operation fails.
   // To disambiguate -1 from a valid result, we clear errno beforehand.
   errno = 0;
-  *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
+  *value = ptrace(PTRACE_PEEKTEXT, pid_, reinterpret_cast<void*>(addr), nullptr);
   if (*value == -1 && errno) {
     return false;
   }
@@ -138,11 +145,17 @@
 }
 
 bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
+  // Make sure that there is no overflow.
+  uint64_t max_size;
+  if (__builtin_add_overflow(addr, bytes, &max_size)) {
+    return false;
+  }
+
   size_t bytes_read = 0;
   long data;
   size_t align_bytes = addr & (sizeof(long) - 1);
   if (align_bytes != 0) {
-    if (!PtraceRead(pid_, addr & ~(sizeof(long) - 1), &data)) {
+    if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) {
       return false;
     }
     size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
@@ -154,7 +167,7 @@
   }
 
   for (size_t i = 0; i < bytes / sizeof(long); i++) {
-    if (!PtraceRead(pid_, addr, &data)) {
+    if (!PtraceRead(addr, &data)) {
       return false;
     }
     memcpy(dst, &data, sizeof(long));
@@ -165,7 +178,7 @@
 
   size_t left_over = bytes & (sizeof(long) - 1);
   if (left_over) {
-    if (!PtraceRead(pid_, addr, &data)) {
+    if (!PtraceRead(addr, &data)) {
       return false;
     }
     memcpy(dst, &data, left_over);
@@ -175,7 +188,13 @@
 }
 
 bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
-  // The process_vm_readv call does will not always work on remote
+  // Make sure that there is no overflow.
+  uint64_t max_size;
+  if (__builtin_add_overflow(addr, size, &max_size)) {
+    return false;
+  }
+
+  // The process_vm_readv call will not always work on remote
   // processes, so only use it for reads from the current pid.
   // Use this method to avoid crashes if an address is invalid since
   // unwind data could try to access any part of the address space.
@@ -208,9 +227,29 @@
 }
 
 bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
-  if (addr < start_ || addr + size > start_ + offset_ + size_) {
+  uint64_t max_size;
+  if (__builtin_add_overflow(addr, size, &max_size)) {
+    return false;
+  }
+
+  uint64_t real_size;
+  if (__builtin_add_overflow(start_, offset_, &real_size) ||
+      __builtin_add_overflow(real_size, size_, &real_size)) {
+    return false;
+  }
+
+  if (addr < start_ || max_size > real_size) {
     return false;
   }
   memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
   return true;
 }
+
+bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
+  uint64_t max_read;
+  if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) {
+    return false;
+  }
+  // The check above guarantees that addr + begin_ will not overflow.
+  return memory_->Read(addr + begin_, dst, size);
+}
diff --git a/libunwindstack/Memory.h b/libunwindstack/Memory.h
index c5316a1..f9f6d56 100644
--- a/libunwindstack/Memory.h
+++ b/libunwindstack/Memory.h
@@ -17,6 +17,7 @@
 #ifndef _LIBUNWINDSTACK_MEMORY_H
 #define _LIBUNWINDSTACK_MEMORY_H
 
+#include <assert.h>
 #include <stdint.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -33,9 +34,16 @@
 
   virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
 
-  inline bool Read(uint64_t addr, void* start, void* field, size_t size) {
-    return Read(addr + reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start),
-                field, size);
+  inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) {
+    if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) {
+      return false;
+    }
+    uint64_t offset = reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start);
+    if (__builtin_add_overflow(addr, offset, &offset)) {
+      return false;
+    }
+    // The read will check if offset + size overflows.
+    return Read(offset, field, size);
   }
 
   inline bool Read32(uint64_t addr, uint32_t* dst) {
@@ -103,6 +111,9 @@
 
   pid_t pid() { return pid_; }
 
+ protected:
+  virtual bool PtraceRead(uint64_t addr, long* value);
+
  private:
   pid_t pid_;
 };
@@ -118,15 +129,12 @@
 class MemoryRange : public Memory {
  public:
   MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
-      : memory_(memory), begin_(begin), length_(end - begin_) {}
+      : memory_(memory), begin_(begin), length_(end - begin) {
+    assert(end > begin);
+  }
   virtual ~MemoryRange() { delete memory_; }
 
-  inline bool Read(uint64_t addr, void* dst, size_t size) override {
-    if (addr + size <= length_) {
-      return memory_->Read(addr + begin_, dst, size);
-    }
-    return false;
-  }
+  bool Read(uint64_t addr, void* dst, size_t size) override;
 
  private:
   Memory* memory_;
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
new file mode 100644
index 0000000..d18aad0
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Regs.h"
+
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class DwarfOpLogTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    op_memory_.Clear();
+    regular_memory_.Clear();
+    mem_.reset(new DwarfMemory(&op_memory_));
+    op_.reset(new DwarfOp<TypeParam>(mem_.get(), &regular_memory_));
+  }
+
+  MemoryFake op_memory_;
+  MemoryFake regular_memory_;
+
+  std::unique_ptr<DwarfMemory> mem_;
+  std::unique_ptr<DwarfOp<TypeParam>> op_;
+};
+TYPED_TEST_CASE_P(DwarfOpLogTest);
+
+TYPED_TEST_P(DwarfOpLogTest, multiple_ops) {
+  // Multi operation opcodes.
+  std::vector<uint8_t> opcode_buffer = {
+      0x0a, 0x20, 0x10, 0x08, 0x03, 0x12, 0x27,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  std::vector<std::string> lines;
+  this->op_->GetLogInfo(0, opcode_buffer.size(), &lines);
+  std::vector<std::string> expected{
+      "DW_OP_const2u 4128", "Raw Data: 0x0a 0x20 0x10", "DW_OP_const1u 3", "Raw Data: 0x08 0x03",
+      "DW_OP_dup",          "Raw Data: 0x12",           "DW_OP_xor",       "Raw Data: 0x27"};
+  ASSERT_EQ(expected, lines);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfOpLogTest, multiple_ops);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
new file mode 100644
index 0000000..520c545
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -0,0 +1,1593 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Regs.h"
+
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class RegsFake : public RegsTmpl<TypeParam> {
+ public:
+  RegsFake(uint16_t total_regs, uint16_t sp_reg)
+      : RegsTmpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+  virtual ~RegsFake() = default;
+
+  uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
+  uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+  bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+};
+
+template <typename TypeParam>
+class DwarfOpTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    op_memory_.Clear();
+    regular_memory_.Clear();
+    mem_.reset(new DwarfMemory(&op_memory_));
+    op_.reset(new DwarfOp<TypeParam>(mem_.get(), &regular_memory_));
+  }
+
+  MemoryFake op_memory_;
+  MemoryFake regular_memory_;
+
+  std::unique_ptr<DwarfMemory> mem_;
+  std::unique_ptr<DwarfOp<TypeParam>> op_;
+};
+TYPED_TEST_CASE_P(DwarfOpTest);
+
+TYPED_TEST_P(DwarfOpTest, decode) {
+  // Memory error.
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+
+  // No error.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
+  this->mem_->set_cur_offset(0);
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+  ASSERT_EQ(0x96U, this->op_->cur_op());
+  ASSERT_EQ(1U, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, eval) {
+  // Memory error.
+  ASSERT_FALSE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+
+  // 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_->is_register());
+  ASSERT_EQ(1U, this->mem_->cur_offset());
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  // Multi operation opcodes.
+  std::vector<uint8_t> opcode_buffer = {
+      0x08, 0x04, 0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Eval(0, 8, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+  ASSERT_FALSE(this->op_->is_register());
+  ASSERT_EQ(8U, this->mem_->cur_offset());
+  ASSERT_EQ(4U, this->op_->StackSize());
+  ASSERT_EQ(1U, this->op_->StackAt(0));
+  ASSERT_EQ(2U, this->op_->StackAt(1));
+  ASSERT_EQ(3U, this->op_->StackAt(2));
+  ASSERT_EQ(4U, this->op_->StackAt(3));
+
+  // Infinite loop.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x2f, 0xfd, 0xff});
+  ASSERT_FALSE(this->op_->Eval(0, 4, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->last_error());
+  ASSERT_FALSE(this->op_->is_register());
+  ASSERT_EQ(0U, this->op_->StackSize());
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_opcode) {
+  // Fill the buffer with all of the illegal opcodes.
+  std::vector<uint8_t> opcode_buffer = {0x00, 0x01, 0x02, 0x04, 0x05, 0x07};
+  for (size_t opcode = 0xa0; opcode < 256; opcode++) {
+    opcode_buffer.push_back(opcode);
+  }
+  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_->last_error());
+    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_->last_error());
+    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_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+    ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, not_implemented) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Push values so that any not implemented ops will return the right error.
+      0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
+      // xderef
+      0x18,
+      // fbreg
+      0x91, 0x01,
+      // piece
+      0x93, 0x01,
+      // xderef_size
+      0x95, 0x01,
+      // push_object_address
+      0x97,
+      // call2
+      0x98, 0x01, 0x02,
+      // call4
+      0x99, 0x01, 0x02, 0x03, 0x04,
+      // call_ref
+      0x9a,
+      // form_tls_address
+      0x9b,
+      // call_frame_cfa
+      0x9c,
+      // bit_piece
+      0x9d, 0x01, 0x01,
+      // implicit_value
+      0x9e, 0x01,
+      // stack_value
+      0x9f,
+  };
+  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));
+
+  while (this->mem_->cur_offset() < opcode_buffer.size()) {
+    ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+    ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->last_error());
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_addr) {
+  std::vector<uint8_t> opcode_buffer = {0x03, 0x12, 0x23, 0x34, 0x45};
+  if (sizeof(TypeParam) == 8) {
+    opcode_buffer.push_back(0x56);
+    opcode_buffer.push_back(0x67);
+    opcode_buffer.push_back(0x78);
+    opcode_buffer.push_back(0x89);
+  }
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x03, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x8978675645342312UL, this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_deref) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Try a dereference with nothing on the stack.
+      0x06,
+      // Add an address, then dereference.
+      0x0a, 0x10, 0x20, 0x06,
+      // Now do another dereference that should fail in memory.
+      0x06,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+  TypeParam value = 0x12345678;
+  this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_deref_size) {
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x94});
+  TypeParam value = 0x12345678;
+  this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  // 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_EQ(1U, this->op_->StackSize()) << "Failed at size " << i;
+    ASSERT_EQ(0x94, this->op_->cur_op()) << "Failed at size " << i;
+    TypeParam expected_value = 0;
+    memcpy(&expected_value, &value, i);
+    ASSERT_EQ(expected_value, this->op_->StackAt(0)) << "Failed at size " << i;
+  }
+
+  // Zero byte read.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, 0x00});
+  ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+
+  // Read too many bytes.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, sizeof(TypeParam) + 1});
+  ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+
+  // Force bad memory read.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x40, 0x94, 0x01});
+  ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, const_unsigned) {
+  std::vector<uint8_t> opcode_buffer = {
+      // const1u
+      0x08, 0x12, 0x08, 0xff,
+      // const2u
+      0x0a, 0x45, 0x12, 0x0a, 0x00, 0xff,
+      // const4u
+      0x0c, 0x12, 0x23, 0x34, 0x45, 0x0c, 0x03, 0x02, 0x01, 0xff,
+      // const8u
+      0x0e, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x0e, 0x87, 0x98, 0xa9, 0xba, 0xcb,
+      0xdc, 0xed, 0xfe,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  // const1u
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_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_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_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_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_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_EQ(0x0e, this->op_->cur_op());
+  ASSERT_EQ(7U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x05060708U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x0102030405060708ULL, this->op_->StackAt(0));
+  }
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0e, this->op_->cur_op());
+  ASSERT_EQ(8U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0xbaa99887UL, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0xfeeddccbbaa99887ULL, this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_signed) {
+  std::vector<uint8_t> opcode_buffer = {
+      // const1s
+      0x09, 0x12, 0x09, 0xff,
+      // const2s
+      0x0b, 0x21, 0x32, 0x0b, 0x08, 0xff,
+      // const4s
+      0x0d, 0x45, 0x34, 0x23, 0x12, 0x0d, 0x01, 0x02, 0x03, 0xff,
+      // const8s
+      0x0f, 0x89, 0x78, 0x67, 0x56, 0x45, 0x34, 0x23, 0x12, 0x0f, 0x04, 0x03, 0x02, 0x01, 0xef,
+      0xef, 0xef, 0xff,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  // const1s
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_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_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_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_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_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_EQ(0x0f, this->op_->cur_op());
+  ASSERT_EQ(7U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x56677889ULL, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x1223344556677889ULL, this->op_->StackAt(0));
+  }
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x0f, this->op_->cur_op());
+  ASSERT_EQ(8U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x01020304U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(static_cast<TypeParam>(-4521264810949884LL), this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_uleb) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Single byte ULEB128
+      0x10, 0x22, 0x10, 0x7f,
+      // Multi byte ULEB128
+      0x10, 0xa2, 0x22, 0x10, 0xa2, 0x74, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+      0x09, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x79,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  // Single byte ULEB128
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_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_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_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_EQ(0x10, this->op_->cur_op());
+  ASSERT_EQ(5U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
+  }
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x10, this->op_->cur_op());
+  ASSERT_EQ(6U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x79101c305080c101ULL, this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_sleb) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Single byte SLEB128
+      0x11, 0x22, 0x11, 0x7f,
+      // Multi byte SLEB128
+      0x11, 0xa2, 0x22, 0x11, 0xa2, 0x74, 0x11, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+      0x09, 0x11,
+  };
+  if (sizeof(TypeParam) == 4) {
+    opcode_buffer.push_back(0xb8);
+    opcode_buffer.push_back(0xd3);
+    opcode_buffer.push_back(0x63);
+  } else {
+    opcode_buffer.push_back(0x81);
+    opcode_buffer.push_back(0x82);
+    opcode_buffer.push_back(0x83);
+    opcode_buffer.push_back(0x84);
+    opcode_buffer.push_back(0x85);
+    opcode_buffer.push_back(0x86);
+    opcode_buffer.push_back(0x87);
+    opcode_buffer.push_back(0x88);
+    opcode_buffer.push_back(0x79);
+  }
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  // Single byte SLEB128
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_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_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_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_EQ(0x11, this->op_->cur_op());
+  ASSERT_EQ(5U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
+  }
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x11, this->op_->cur_op());
+  ASSERT_EQ(6U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(static_cast<TypeParam>(-464456), this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(static_cast<TypeParam>(-499868564803501823LL), this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_dup) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Should fail since nothing is on the stack.
+      0x12,
+      // Push on a value and dup.
+      0x08, 0x15, 0x12,
+      // Do it again.
+      0x08, 0x23, 0x12,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x12, this->op_->cur_op());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(3U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x12, this->op_->cur_op());
+  ASSERT_EQ(4U, this->op_->StackSize());
+  ASSERT_EQ(0x23U, this->op_->StackAt(0));
+  ASSERT_EQ(0x23U, this->op_->StackAt(1));
+  ASSERT_EQ(0x15U, this->op_->StackAt(2));
+  ASSERT_EQ(0x15U, this->op_->StackAt(3));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_drop) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Push a couple of values.
+      0x08, 0x10, 0x08, 0x20,
+      // Drop the values.
+      0x13, 0x13,
+      // Attempt to drop empty stack.
+      0x13,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(0x13, this->op_->cur_op());
+  ASSERT_EQ(0U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x13, this->op_->cur_op());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_over) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Push a couple of values.
+      0x08, 0x1a, 0x08, 0xed,
+      // Copy a value.
+      0x14,
+      // Remove all but one element.
+      0x13, 0x13,
+      // Provoke a failure with this opcode.
+      0x14,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(2U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x14, this->op_->cur_op());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_pick) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Push a few values.
+      0x08, 0x1a, 0x08, 0xed, 0x08, 0x34,
+      // Copy the value at offset 2.
+      0x15, 0x01,
+      // Copy the last value in the stack.
+      0x15, 0x03,
+      // Choose an invalid index.
+      0x15, 0x10,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(3U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x15, this->op_->cur_op());
+  ASSERT_EQ(4U, this->op_->StackSize());
+  ASSERT_EQ(0xedU, this->op_->StackAt(0));
+  ASSERT_EQ(0x34U, this->op_->StackAt(1));
+  ASSERT_EQ(0xedU, this->op_->StackAt(2));
+  ASSERT_EQ(0x1aU, this->op_->StackAt(3));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x15, this->op_->cur_op());
+  ASSERT_EQ(5U, this->op_->StackSize());
+  ASSERT_EQ(0x1aU, this->op_->StackAt(0));
+  ASSERT_EQ(0xedU, this->op_->StackAt(1));
+  ASSERT_EQ(0x34U, this->op_->StackAt(2));
+  ASSERT_EQ(0xedU, this->op_->StackAt(3));
+  ASSERT_EQ(0x1aU, this->op_->StackAt(4));
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x15, this->op_->cur_op());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_swap) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Push a couple of values.
+      0x08, 0x26, 0x08, 0xab,
+      // Swap values.
+      0x16,
+      // Pop a value to cause a failure.
+      0x13, 0x16,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_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_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x16, this->op_->cur_op());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_rot) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Rotate that should cause a failure.
+      0x17, 0x08, 0x10,
+      // Only 1 value on stack, should fail.
+      0x17, 0x08, 0x20,
+      // Only 2 values on stack, should fail.
+      0x17, 0x08, 0x30,
+      // Should rotate properly.
+      0x17,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(0x17, this->op_->cur_op());
+  ASSERT_EQ(3U, this->op_->StackSize());
+  ASSERT_EQ(0x20U, this->op_->StackAt(0));
+  ASSERT_EQ(0x10U, this->op_->StackAt(1));
+  ASSERT_EQ(0x30U, this->op_->StackAt(2));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_abs) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Abs that should fail.
+      0x19,
+      // A value that is already positive.
+      0x08, 0x10, 0x19,
+      // A value that is negative.
+      0x11, 0x7f, 0x19,
+      // A value that is large and negative.
+      0x11, 0x81, 0x80, 0x80, 0x80,
+  };
+  if (sizeof(TypeParam) == 4) {
+    opcode_buffer.push_back(0x08);
+  } else {
+    opcode_buffer.push_back(0x80);
+    opcode_buffer.push_back(0x80);
+    opcode_buffer.push_back(0x01);
+  }
+  opcode_buffer.push_back(0x19);
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(3U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x19, this->op_->cur_op());
+  ASSERT_EQ(3U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(2147483647U, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(4398046511105UL, this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_and) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x1b,
+      // Push a single value.
+      0x08, 0x20,
+      // One element stack, and op will fail.
+      0x1b,
+      // Push another value.
+      0x08, 0x02, 0x1b,
+      // Push on two negative values.
+      0x11, 0x7c, 0x11, 0x7f, 0x1b,
+      // Push one negative, one positive.
+      0x11, 0x10, 0x11, 0x7c, 0x1b,
+      // Divide by zero.
+      0x11, 0x10, 0x11, 0x00, 0x1b,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  // Two positive values.
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(3U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(3U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(4U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(4U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(5U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_div) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x1a,
+      // Push a single value.
+      0x08, 0x48,
+      // One element stack, and op will fail.
+      0x1a,
+      // Push another value.
+      0x08, 0xf0, 0x1a,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1a, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x40U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_minus) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x1c,
+      // Push a single value.
+      0x08, 0x48,
+      // One element stack, and op will fail.
+      0x1c,
+      // Push another value.
+      0x08, 0x04, 0x1c,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1c, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x44U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_mod) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x1d,
+      // Push a single value.
+      0x08, 0x47,
+      // One element stack, and op will fail.
+      0x1d,
+      // Push another value.
+      0x08, 0x04, 0x1d,
+      // Try a mod of zero.
+      0x08, 0x01, 0x08, 0x00, 0x1d,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(2U, this->op_->StackSize());
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(3U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_mul) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x1e,
+      // Push a single value.
+      0x08, 0x48,
+      // One element stack, and op will fail.
+      0x1e,
+      // Push another value.
+      0x08, 0x04, 0x1e,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1e, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x120U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_neg) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x1f,
+      // Push a single value.
+      0x08, 0x48, 0x1f,
+      // Push a negative value.
+      0x11, 0x7f, 0x1f,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x1f, this->op_->cur_op());
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(0x01U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_not) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x20,
+      // Push a single value.
+      0x08, 0x4, 0x20,
+      // Push a negative value.
+      0x11, 0x7c, 0x20,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x20, this->op_->cur_op());
+  ASSERT_EQ(2U, this->op_->StackSize());
+  ASSERT_EQ(0x03U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_or) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x21,
+      // Push a single value.
+      0x08, 0x48,
+      // One element stack, and op will fail.
+      0x21,
+      // Push another value.
+      0x08, 0xf4, 0x21,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x21, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0xfcU, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_plus) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x22,
+      // Push a single value.
+      0x08, 0xff,
+      // One element stack, and op will fail.
+      0x22,
+      // Push another value.
+      0x08, 0xf2, 0x22,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x22, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x1f1U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_plus_uconst) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x23,
+      // Push a single value.
+      0x08, 0x50, 0x23, 0x80, 0x51,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x23, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x28d0U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shl) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x24,
+      // Push a single value.
+      0x08, 0x67,
+      // One element stack, and op will fail.
+      0x24,
+      // Push another value.
+      0x08, 0x03, 0x24,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x24, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x338U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shr) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x25,
+      // Push a single value.
+      0x11, 0x70,
+      // One element stack, and op will fail.
+      0x25,
+      // Push another value.
+      0x08, 0x03, 0x25,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x25, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  if (sizeof(TypeParam) == 4) {
+    ASSERT_EQ(0x1ffffffeU, this->op_->StackAt(0));
+  } else {
+    ASSERT_EQ(0x1ffffffffffffffeULL, this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shra) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x26,
+      // Push a single value.
+      0x11, 0x70,
+      // One element stack, and op will fail.
+      0x26,
+      // Push another value.
+      0x08, 0x03, 0x26,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x26, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(static_cast<TypeParam>(-2), this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_xor) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x27,
+      // Push a single value.
+      0x08, 0x11,
+      // One element stack, and op will fail.
+      0x27,
+      // Push another value.
+      0x08, 0x41, 0x27,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(2U, this->op_->StackSize());
+
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x27, this->op_->cur_op());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0x50U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_bra) {
+  std::vector<uint8_t> opcode_buffer = {
+      // No stack, and op will fail.
+      0x28,
+      // Push on a non-zero value with a positive branch.
+      0x08, 0x11, 0x28, 0x02, 0x01,
+      // Push on a zero value with a positive branch.
+      0x08, 0x00, 0x28, 0x05, 0x00,
+      // Push on a non-zero value with a negative branch.
+      0x08, 0x11, 0x28, 0xfc, 0xff,
+      // Push on a zero value with a negative branch.
+      0x08, 0x00, 0x28, 0xf0, 0xff,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+  // Push on a non-zero value with a positive branch.
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(1U, this->op_->StackSize());
+
+  uint64_t offset = this->mem_->cur_offset() + 3;
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(1U, this->op_->StackSize());
+
+  offset = this->mem_->cur_offset() + 3;
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(1U, this->op_->StackSize());
+
+  offset = this->mem_->cur_offset() + 3;
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(1U, this->op_->StackSize());
+
+  offset = this->mem_->cur_offset() + 3;
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_EQ(0x28, this->op_->cur_op());
+  ASSERT_EQ(0U, this->op_->StackSize());
+  ASSERT_EQ(offset + 16, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, compare_opcode_stack_error) {
+  // All of the ops require two stack elements. Loop through all of these
+  // ops with potential errors.
+  std::vector<uint8_t> opcode_buffer = {
+      0xff,  // Place holder for compare op.
+      0x08, 0x11,
+      0xff,  // Place holder for compare op.
+  };
+
+  for (uint8_t opcode = 0x29; opcode <= 0x2e; opcode++) {
+    opcode_buffer[0] = opcode;
+    opcode_buffer[3] = opcode;
+    this->op_memory_.SetMemory(0, opcode_buffer);
+
+    ASSERT_FALSE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+    ASSERT_EQ(opcode, this->op_->cur_op());
+    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+    ASSERT_FALSE(this->op_->Eval(1, 4, DWARF_VERSION_MAX));
+    ASSERT_EQ(opcode, this->op_->cur_op());
+    ASSERT_EQ(1U, this->op_->StackSize());
+    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, compare_opcodes) {
+  // Have three different checks for each compare op:
+  //   - Both values the same.
+  //   - The first value larger than the second.
+  //   - The second value larger than the first.
+  std::vector<uint8_t> opcode_buffer = {
+      // Values the same.
+      0x08, 0x11, 0x08, 0x11,
+      0xff,  // Placeholder.
+      // First value larger.
+      0x08, 0x12, 0x08, 0x10,
+      0xff,  // Placeholder.
+      // Second value larger.
+      0x08, 0x10, 0x08, 0x12,
+      0xff,  // Placeholder.
+  };
+
+  // Opcode followed by the expected values on the stack.
+  std::vector<uint8_t> expected = {
+      0x29, 1, 0, 0,  // eq
+      0x2a, 1, 1, 0,  // ge
+      0x2b, 0, 1, 0,  // gt
+      0x2c, 1, 0, 1,  // le
+      0x2d, 0, 0, 1,  // lt
+      0x2e, 0, 1, 1,  // ne
+  };
+  for (size_t i = 0; i < expected.size(); i += 4) {
+    opcode_buffer[4] = expected[i];
+    opcode_buffer[9] = expected[i];
+    opcode_buffer[14] = expected[i];
+    this->op_memory_.SetMemory(0, opcode_buffer);
+
+    ASSERT_TRUE(this->op_->Eval(0, 15, DWARF_VERSION_MAX))
+        << "Op: 0x" << std::hex << static_cast<uint32_t>(expected[i]) << " failed";
+
+    ASSERT_EQ(3U, this->op_->StackSize());
+    ASSERT_EQ(expected[i + 1], this->op_->StackAt(2));
+    ASSERT_EQ(expected[i + 2], this->op_->StackAt(1));
+    ASSERT_EQ(expected[i + 3], this->op_->StackAt(0));
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_skip) {
+  std::vector<uint8_t> opcode_buffer = {
+      // Positive value.
+      0x2f, 0x10, 0x20,
+      // Negative value.
+      0x2f, 0xfd, 0xff,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  uint64_t offset = this->mem_->cur_offset() + 3;
+  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  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_EQ(0x2f, this->op_->cur_op());
+  ASSERT_EQ(0U, this->op_->StackSize());
+  ASSERT_EQ(offset - 3, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_lit) {
+  std::vector<uint8_t> opcode_buffer;
+
+  // Verify every lit opcode.
+  for (uint8_t op = 0x30; op <= 0x4f; op++) {
+    opcode_buffer.push_back(op);
+  }
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  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_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;
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_reg) {
+  std::vector<uint8_t> opcode_buffer;
+
+  // Verify every reg opcode.
+  for (uint8_t op = 0x50; op <= 0x6f; op++) {
+    opcode_buffer.push_back(op);
+  }
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  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_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;
+    ASSERT_EQ(op - 0x50U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_regx) {
+  std::vector<uint8_t> opcode_buffer = {
+      0x90, 0x02, 0x90, 0x80, 0x15,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  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_EQ(0x90, this->op_->cur_op());
+  ASSERT_TRUE(this->op_->is_register());
+  ASSERT_EQ(1U, this->op_->StackSize());
+  ASSERT_EQ(0xa80U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_breg) {
+  std::vector<uint8_t> opcode_buffer;
+
+  // Verify every reg opcode.
+  for (uint8_t op = 0x70; op <= 0x8f; op++) {
+    // Positive value added to register.
+    opcode_buffer.push_back(op);
+    opcode_buffer.push_back(0x12);
+    // Negative value added to register.
+    opcode_buffer.push_back(op);
+    opcode_buffer.push_back(0x7e);
+  }
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  RegsFake<TypeParam> regs(32, 10);
+  for (size_t i = 0; i < 32; i++) {
+    regs[i] = i + 10;
+  }
+  this->op_->set_regs(&regs);
+
+  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_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_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;
+    offset += 2;
+  }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_breg_invalid_register) {
+  std::vector<uint8_t> opcode_buffer = {
+      0x7f, 0x12, 0x80, 0x12,
+  };
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  RegsFake<TypeParam> regs(16, 10);
+  for (size_t i = 0; i < 16; i++) {
+    regs[i] = i + 10;
+  }
+  this->op_->set_regs(&regs);
+
+  // Should pass since this references the last regsister.
+  ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  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_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_bregx) {
+  std::vector<uint8_t> opcode_buffer = {// Positive value added to register.
+                                        0x92, 0x05, 0x20,
+                                        // Negative value added to register.
+                                        0x92, 0x06, 0x80, 0x7e,
+                                        // Illegal register.
+                                        0x92, 0x80, 0x15, 0x80, 0x02};
+  this->op_memory_.SetMemory(0, opcode_buffer);
+
+  RegsFake<TypeParam> regs(10, 10);
+  regs[5] = 0x45;
+  regs[6] = 0x190;
+  this->op_->set_regs(&regs);
+
+  ASSERT_TRUE(this->op_->Eval(0, 3, DWARF_VERSION_MAX));
+  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_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_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+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_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);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
diff --git a/libunwindstack/tests/MemoryBuffer.cpp b/libunwindstack/tests/MemoryBuffer.cpp
new file mode 100644
index 0000000..af3d6b9
--- /dev/null
+++ b/libunwindstack/tests/MemoryBuffer.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryBufferTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+    memory_.reset(new MemoryBuffer);
+  }
+  std::unique_ptr<MemoryBuffer> memory_;
+};
+
+TEST_F(MemoryBufferTest, empty) {
+  ASSERT_EQ(0U, memory_->Size());
+  std::vector<uint8_t> buffer(1024);
+  ASSERT_FALSE(memory_->Read(0, buffer.data(), 1));
+  ASSERT_EQ(nullptr, memory_->GetPtr(0));
+  ASSERT_EQ(nullptr, memory_->GetPtr(1));
+}
+
+TEST_F(MemoryBufferTest, write_read) {
+  memory_->Resize(256);
+  ASSERT_EQ(256U, memory_->Size());
+  ASSERT_TRUE(memory_->GetPtr(0) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(1) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(255) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(256) == nullptr);
+
+  uint8_t* data = memory_->GetPtr(0);
+  for (size_t i = 0; i < memory_->Size(); i++) {
+    data[i] = i;
+  }
+
+  std::vector<uint8_t> buffer(memory_->Size());
+  ASSERT_TRUE(memory_->Read(0, buffer.data(), buffer.size()));
+  for (size_t i = 0; i < buffer.size(); i++) {
+    ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MemoryBufferTest, read_failures) {
+  memory_->Resize(100);
+  std::vector<uint8_t> buffer(200);
+  ASSERT_FALSE(memory_->Read(0, buffer.data(), 101));
+  ASSERT_FALSE(memory_->Read(100, buffer.data(), 1));
+  ASSERT_FALSE(memory_->Read(101, buffer.data(), 2));
+  ASSERT_FALSE(memory_->Read(99, buffer.data(), 2));
+  ASSERT_TRUE(memory_->Read(99, buffer.data(), 1));
+}
+
+TEST_F(MemoryBufferTest, read_failure_overflow) {
+  memory_->Resize(100);
+  std::vector<uint8_t> buffer(200);
+
+  ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200));
+}
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index e05736b..70ef30a 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -75,4 +75,16 @@
   }
 };
 
+class MemoryFakeRemote : public MemoryRemote {
+ public:
+  MemoryFakeRemote() : MemoryRemote(0) {}
+  virtual ~MemoryFakeRemote() = default;
+
+ protected:
+  bool PtraceRead(uint64_t, long* value) override {
+    *value = 0;
+    return true;
+  }
+};
+
 #endif  // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index 870ca19..aa7a23a 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <string>
+#include <vector>
+
 #include <android-base/test_utils.h>
 #include <android-base/file.h>
 #include <gtest/gtest.h>
@@ -39,7 +42,7 @@
   TemporaryFile* tf_ = nullptr;
 };
 
-TEST_F(MemoryFileTest, offset_0) {
+TEST_F(MemoryFileTest, init_offset_0) {
   WriteTestData();
 
   ASSERT_TRUE(memory_.Init(tf_->path, 0));
@@ -49,7 +52,7 @@
   ASSERT_STREQ("0123456789", buffer.data());
 }
 
-TEST_F(MemoryFileTest, offset_non_zero) {
+TEST_F(MemoryFileTest, init_offset_non_zero) {
   WriteTestData();
 
   ASSERT_TRUE(memory_.Init(tf_->path, 10));
@@ -59,7 +62,7 @@
   ASSERT_STREQ("abcdefghij", buffer.data());
 }
 
-TEST_F(MemoryFileTest, offset_non_zero_larger_than_pagesize) {
+TEST_F(MemoryFileTest, init_offset_non_zero_larger_than_pagesize) {
   size_t pagesize = getpagesize();
   std::string large_string;
   for (size_t i = 0; i < pagesize; i++) {
@@ -75,7 +78,7 @@
   ASSERT_STREQ("abcdefgh", buffer.data());
 }
 
-TEST_F(MemoryFileTest, offset_pagesize_aligned) {
+TEST_F(MemoryFileTest, init_offset_pagesize_aligned) {
   size_t pagesize = getpagesize();
   std::string data;
   for (size_t i = 0; i < 2 * pagesize; i++) {
@@ -96,7 +99,7 @@
   ASSERT_STREQ(expected_str.c_str(), buffer.data());
 }
 
-TEST_F(MemoryFileTest, offset_pagesize_aligned_plus_extra) {
+TEST_F(MemoryFileTest, init_offset_pagesize_aligned_plus_extra) {
   size_t pagesize = getpagesize();
   std::string data;
   for (size_t i = 0; i < 2 * pagesize; i++) {
@@ -117,6 +120,23 @@
   ASSERT_STREQ(expected_str.c_str(), buffer.data());
 }
 
+TEST_F(MemoryFileTest, init_offset_greater_than_filesize) {
+  size_t pagesize = getpagesize();
+  std::string data;
+  uint64_t file_size = 2 * pagesize + pagesize / 2;
+  for (size_t i = 0; i < file_size; i++) {
+    data += static_cast<char>((i / pagesize) + '0');
+  }
+  ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+
+  // Check offset > file size fails and aligned_offset > file size.
+  ASSERT_FALSE(memory_.Init(tf_->path, file_size + 2 * pagesize));
+  // Check offset == filesize fails.
+  ASSERT_FALSE(memory_.Init(tf_->path, file_size));
+  // Check aligned_offset < filesize, but offset > filesize fails.
+  ASSERT_FALSE(memory_.Init(tf_->path, 2 * pagesize + pagesize / 2 + pagesize / 4));
+}
+
 TEST_F(MemoryFileTest, read_error) {
   std::string data;
   for (size_t i = 0; i < 5000; i++) {
@@ -137,32 +157,9 @@
   ASSERT_TRUE(memory_.Read(4990, buffer.data(), 10));
   ASSERT_FALSE(memory_.Read(4999, buffer.data(), 2));
   ASSERT_TRUE(memory_.Read(4999, buffer.data(), 1));
-}
 
-TEST_F(MemoryFileTest, read_string) {
-  std::string value("name_in_file");
-  ASSERT_TRUE(android::base::WriteFully(tf_->fd, value.c_str(), value.size() + 1));
-
-  std::string name;
-  ASSERT_TRUE(memory_.Init(tf_->path, 0));
-  ASSERT_TRUE(memory_.ReadString(0, &name));
-  ASSERT_EQ("name_in_file", name);
-  ASSERT_TRUE(memory_.ReadString(5, &name));
-  ASSERT_EQ("in_file", name);
-}
-
-TEST_F(MemoryFileTest, read_string_error) {
-  std::vector<uint8_t> buffer = { 0x23, 0x32, 0x45 };
-  ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
-
-  std::string name;
-  ASSERT_TRUE(memory_.Init(tf_->path, 0));
-
-  // Read from a non-existant address.
-  ASSERT_FALSE(memory_.ReadString(100, &name));
-
-  // This should fail because there is no terminating \0
-  ASSERT_FALSE(memory_.ReadString(0, &name));
+  // Check that overflow fails properly.
+  ASSERT_FALSE(memory_.Read(UINT64_MAX - 100, buffer.data(), 200));
 }
 
 TEST_F(MemoryFileTest, read_past_file_within_mapping) {
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index 0ba5f1c..ab999da 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -47,25 +47,6 @@
   }
 }
 
-TEST(MemoryLocalTest, read_string) {
-  std::string name("string_in_memory");
-
-  MemoryLocal local;
-
-  std::vector<uint8_t> dst(1024);
-  std::string dst_name;
-  ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(name.c_str()), &dst_name));
-  ASSERT_EQ("string_in_memory", dst_name);
-
-  ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name));
-  ASSERT_EQ("in_memory", dst_name);
-
-  ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 10));
-  ASSERT_EQ("in_memory", dst_name);
-
-  ASSERT_FALSE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 9));
-}
-
 TEST(MemoryLocalTest, read_illegal) {
   MemoryLocal local;
 
@@ -73,3 +54,13 @@
   ASSERT_FALSE(local.Read(0, dst.data(), 1));
   ASSERT_FALSE(local.Read(0, dst.data(), 100));
 }
+
+TEST(MemoryLocalTest, read_overflow) {
+  MemoryLocal local;
+
+  // On 32 bit this test doesn't necessarily cause an overflow. The 64 bit
+  // version will always go through the overflow check.
+  std::vector<uint8_t> dst(100);
+  uint64_t value;
+  ASSERT_FALSE(local.Read(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
+}
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index d636ec4..ee5ba01 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -17,6 +17,7 @@
 #include <stdint.h>
 #include <string.h>
 
+#include <memory>
 #include <vector>
 
 #include <gtest/gtest.h>
@@ -65,35 +66,14 @@
   ASSERT_FALSE(range.Read(1020, dst.data(), 5));
   ASSERT_FALSE(range.Read(1024, dst.data(), 1));
   ASSERT_FALSE(range.Read(1024, dst.data(), 1024));
+
+  // Verify that reading up to the end works.
+  ASSERT_TRUE(range.Read(1020, dst.data(), 4));
 }
 
-TEST_F(MemoryRangeTest, read_string_past_end) {
-  std::string name("0123456789");
-  memory_->SetMemory(0, name);
+TEST_F(MemoryRangeTest, read_overflow) {
+  std::vector<uint8_t> buffer(100);
 
-  // Verify a read past the range fails.
-  MemoryRange range(memory_, 0, 5);
-  std::string dst_name;
-  ASSERT_FALSE(range.ReadString(0, &dst_name));
-}
-
-TEST_F(MemoryRangeTest, read_string_to_end) {
-  std::string name("0123456789");
-  memory_->SetMemory(30, name);
-
-  // Verify the range going to the end of the string works.
-  MemoryRange range(memory_, 30, 30 + name.size() + 1);
-  std::string dst_name;
-  ASSERT_TRUE(range.ReadString(0, &dst_name));
-  ASSERT_EQ("0123456789", dst_name);
-}
-
-TEST_F(MemoryRangeTest, read_string_fencepost) {
-  std::string name("0123456789");
-  memory_->SetMemory(10, name);
-
-  // Verify the range set to one byte less than the end of the string fails.
-  MemoryRange range(memory_, 10, 10 + name.size());
-  std::string dst_name;
-  ASSERT_FALSE(range.ReadString(0, &dst_name));
+  std::unique_ptr<MemoryRange> overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200));
+  ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
 }
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 7664c3e..e48edf7 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -33,6 +33,8 @@
 
 #include "Memory.h"
 
+#include "MemoryFake.h"
+
 class MemoryRemoteTest : public ::testing::Test {
  protected:
   static uint64_t NanoTime() {
@@ -121,6 +123,9 @@
   ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
   ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
 
+  // Check overflow condition is caught properly.
+  ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+
   ASSERT_EQ(0, munmap(src, pagesize));
 
   ASSERT_TRUE(Detach(pid));
@@ -128,6 +133,14 @@
   kill(pid, SIGKILL);
 }
 
+TEST_F(MemoryRemoteTest, read_overflow) {
+  MemoryFakeRemote remote;
+
+  // Check overflow condition is caught properly.
+  std::vector<uint8_t> dst(200);
+  ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+}
+
 TEST_F(MemoryRemoteTest, read_illegal) {
   pid_t pid;
   if ((pid = fork()) == 0) {
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
new file mode 100644
index 0000000..51b5d7d
--- /dev/null
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "MemoryFake.h"
+
+TEST(MemoryTest, read32) {
+  MemoryFakeAlwaysReadZero memory;
+
+  uint32_t data = 0xffffffff;
+  ASSERT_TRUE(memory.Read32(0, &data));
+  ASSERT_EQ(0U, data);
+}
+
+TEST(MemoryTest, read64) {
+  MemoryFakeAlwaysReadZero memory;
+
+  uint64_t data = 0xffffffffffffffffULL;
+  ASSERT_TRUE(memory.Read64(0, &data));
+  ASSERT_EQ(0U, data);
+}
+
+struct FakeStruct {
+  int one;
+  bool two;
+  uint32_t three;
+  uint64_t four;
+};
+
+TEST(MemoryTest, read_field) {
+  MemoryFakeAlwaysReadZero memory;
+
+  FakeStruct data;
+  memset(&data, 0xff, sizeof(data));
+  ASSERT_TRUE(memory.ReadField(0, &data, &data.one, sizeof(data.one)));
+  ASSERT_EQ(0, data.one);
+
+  memset(&data, 0xff, sizeof(data));
+  ASSERT_TRUE(memory.ReadField(0, &data, &data.two, sizeof(data.two)));
+  ASSERT_FALSE(data.two);
+
+  memset(&data, 0xff, sizeof(data));
+  ASSERT_TRUE(memory.ReadField(0, &data, &data.three, sizeof(data.three)));
+  ASSERT_EQ(0U, data.three);
+
+  memset(&data, 0xff, sizeof(data));
+  ASSERT_TRUE(memory.ReadField(0, &data, &data.four, sizeof(data.four)));
+  ASSERT_EQ(0U, data.four);
+}
+
+TEST(MemoryTest, read_field_fails) {
+  MemoryFakeAlwaysReadZero memory;
+
+  FakeStruct data;
+  memset(&data, 0xff, sizeof(data));
+
+  ASSERT_FALSE(memory.ReadField(UINT64_MAX, &data, &data.three, sizeof(data.three)));
+
+  // Field and start reversed, should fail.
+  ASSERT_FALSE(memory.ReadField(100, &data.two, &data, sizeof(data.two)));
+  ASSERT_FALSE(memory.ReadField(0, &data.two, &data, sizeof(data.two)));
+}
+
+TEST(MemoryTest, read_string) {
+  std::string name("string_in_memory");
+
+  MemoryFake memory;
+
+  memory.SetMemory(100, name.c_str(), name.size() + 1);
+
+  std::string dst_name;
+  ASSERT_TRUE(memory.ReadString(100, &dst_name));
+  ASSERT_EQ("string_in_memory", dst_name);
+
+  ASSERT_TRUE(memory.ReadString(107, &dst_name));
+  ASSERT_EQ("in_memory", dst_name);
+
+  // Set size greater than string.
+  ASSERT_TRUE(memory.ReadString(107, &dst_name, 10));
+  ASSERT_EQ("in_memory", dst_name);
+
+  ASSERT_FALSE(memory.ReadString(107, &dst_name, 9));
+}
+
+TEST(MemoryTest, read_string_error) {
+  std::string name("short");
+
+  MemoryFake memory;
+
+  // Save everything except the terminating '\0'.
+  memory.SetMemory(0, name.c_str(), name.size());
+
+  std::string dst_name;
+  // Read from a non-existant address.
+  ASSERT_FALSE(memory.ReadString(100, &dst_name));
+
+  // This should fail because there is no terminating '\0'.
+  ASSERT_FALSE(memory.ReadString(0, &dst_name));
+
+  // This should pass because there is a terminating '\0'.
+  memory.SetData8(name.size(), '\0');
+  ASSERT_TRUE(memory.ReadString(0, &dst_name));
+  ASSERT_EQ("short", dst_name);
+}
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 0984e81..0c7019a 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -1091,8 +1091,10 @@
         it = mLogElements.begin();
     } else {
         LogBufferElementCollection::iterator last;
-        // 30 second limit to continue search for out-of-order entries.
-        log_time min = start - log_time(30, 0);
+        // 3 second limit to continue search for out-of-order entries.
+        log_time min = start - log_time(3, 0);
+        // Cap to 300 iterations we look back for out-of-order entries.
+        size_t count = 300;
         // Client wants to start from some specified time. Chances are
         // we are better off starting from the end of the time sorted list.
         for (last = it = mLogElements.end(); it != mLogElements.begin();
@@ -1101,7 +1103,7 @@
             LogBufferElement* element = *it;
             if (element->getRealTime() > start) {
                 last = it;
-            } else if (element->getRealTime() < min) {
+            } else if (!--count || (element->getRealTime() < min)) {
                 break;
             }
         }
diff --git a/qemu_pipe/Android.mk b/qemu_pipe/Android.mk
deleted file mode 100644
index 6e0144c..0000000
--- a/qemu_pipe/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright 2011 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-common_static_libraries := \
-    libbase
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-LOCAL_SRC_FILES:= \
-    qemu_pipe.cpp
-LOCAL_C_INCLUDES := \
-    $(LOCAL_PATH)/include \
-    system/base/include
-LOCAL_MODULE:= libqemu_pipe
-LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-include $(BUILD_STATIC_LIBRARY)
diff --git a/qemu_pipe/include/qemu_pipe.h b/qemu_pipe/include/qemu_pipe.h
deleted file mode 100644
index 0987498..0000000
--- a/qemu_pipe/include/qemu_pipe.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2011 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 ANDROID_CORE_INCLUDE_QEMU_PIPE_H
-#define ANDROID_CORE_INCLUDE_QEMU_PIPE_H
-
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-// Try to open a new Qemu fast-pipe. This function returns a file descriptor
-// that can be used to communicate with a named service managed by the
-// emulator.
-//
-// This file descriptor can be used as a standard pipe/socket descriptor.
-//
-// 'pipeName' is the name of the emulator service you want to connect to,
-// and should begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
-// For backward compatibility, the 'pipe:' prefix can be omitted, and in
-// that case, qemu_pipe_open will add it for you.
-
-// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
-//
-// EINVAL  -> unknown/unsupported pipeName
-// ENOSYS  -> fast pipes not available in this system.
-//
-// ENOSYS should never happen, except if you're trying to run within a
-// misconfigured emulator.
-//
-// You should be able to open several pipes to the same pipe service,
-// except for a few special cases (e.g. GSM modem), where EBUSY will be
-// returned if more than one client tries to connect to it.
-int qemu_pipe_open(const char* pipeName);
-
-// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
-// This really adds a 4-hexchar prefix describing the payload size.
-// Returns 0 on success, and -1 on error.
-int qemu_pipe_frame_send(int fd, const void* buff, size_t len);
-
-// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
-// If the framed message is larger than |len|, then this returns -1 and the
-// content is lost. Otherwise, this returns the size of the message. NOTE:
-// empty messages are possible in a framed wire protocol and do not mean
-// end-of-stream.
-int qemu_pipe_frame_recv(int fd, void* buff, size_t len);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ANDROID_CORE_INCLUDE_QEMU_PIPE_H */
diff --git a/qemu_pipe/qemu_pipe.cpp b/qemu_pipe/qemu_pipe.cpp
deleted file mode 100644
index beeccb0..0000000
--- a/qemu_pipe/qemu_pipe.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2011 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 "qemu_pipe.h"
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-
-#include <android-base/file.h>
-
-using android::base::ReadFully;
-using android::base::WriteFully;
-
-// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
-// occurs during pipe operations. The macro should simply take a printf-style
-// formatting string followed by optional arguments.
-#ifndef QEMU_PIPE_DEBUG
-#  define  QEMU_PIPE_DEBUG(...)   (void)0
-#endif
-
-int qemu_pipe_open(const char* pipeName) {
-    // Sanity check.
-    if (!pipeName) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
-    if (fd < 0) {
-        QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
-                        strerror(errno));
-        return -1;
-    }
-
-    // Write the pipe name, *including* the trailing zero which is necessary.
-    size_t pipeNameLen = strlen(pipeName);
-    if (WriteFully(fd, pipeName, pipeNameLen + 1U)) {
-        return fd;
-    }
-
-    // now, add 'pipe:' prefix and try again
-    // Note: host side will wait for the trailing '\0' to start
-    // service lookup.
-    const char pipe_prefix[] = "pipe:";
-    if (WriteFully(fd, pipe_prefix, strlen(pipe_prefix)) &&
-            WriteFully(fd, pipeName, pipeNameLen + 1U)) {
-        return fd;
-    }
-    QEMU_PIPE_DEBUG("%s: Could not write to %s pipe service: %s",
-            __FUNCTION__, pipeName, strerror(errno));
-    close(fd);
-    return -1;
-}
-
-int qemu_pipe_frame_send(int fd, const void* buff, size_t len) {
-    char header[5];
-    snprintf(header, sizeof(header), "%04zx", len);
-    if (!WriteFully(fd, header, 4)) {
-        QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
-        return -1;
-    }
-    if (!WriteFully(fd, buff, len)) {
-        QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
-        return -1;
-    }
-    return 0;
-}
-
-int qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
-    char header[5];
-    if (!ReadFully(fd, header, 4)) {
-        QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
-        return -1;
-    }
-    header[4] = '\0';
-    size_t size;
-    if (sscanf(header, "%04zx", &size) != 1) {
-        QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
-        return -1;
-    }
-    if (size > len) {
-        QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
-                        len);
-        return -1;
-    }
-    if (!ReadFully(fd, buff, size)) {
-        QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
-                        strerror(errno));
-        return -1;
-    }
-    return size;
-}