Merge "NativeBridge: add "linked namespace" semantic corresponding to linker"
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/adb.cpp b/adb/adb.cpp
index 577e9b9..cf6b359 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -59,10 +59,12 @@
 
 std::string adb_version() {
     // Don't change the format of this --- it's parsed by ddmlib.
-    return android::base::StringPrintf("Android Debug Bridge version %d.%d.%d\n"
-                                       "Revision %s\n",
-                                       ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
-                                       ADB_REVISION);
+    return android::base::StringPrintf(
+        "Android Debug Bridge version %d.%d.%d\n"
+        "Revision %s\n"
+        "Installed as %s\n",
+        ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_REVISION,
+        android::base::GetExecutablePath().c_str());
 }
 
 void fatal(const char *fmt, ...) {
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/sysdeps.h b/adb/sysdeps.h
index f195b4e..f95a855 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -268,10 +268,6 @@
 #undef   accept
 #define  accept  ___xxx_accept
 
-int adb_getsockname(int fd, struct sockaddr* sockaddr, socklen_t* optlen);
-#undef getsockname
-#define getsockname(...) ___xxx_getsockname(__VA__ARGS__)
-
 // Returns the local port number of a bound socket, or -1 on failure.
 int adb_socket_get_local_port(int fd);
 
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index f997e6b..5873b2b 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -984,7 +984,7 @@
         return -1;
     }
 
-    int result = (getsockname)(fh->fh_socket, sockaddr, optlen);
+    int result = getsockname(fh->fh_socket, sockaddr, optlen);
     if (result == SOCKET_ERROR) {
         const DWORD err = WSAGetLastError();
         D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd,
@@ -1042,11 +1042,6 @@
     int local_port = -1;
     std::string error;
 
-    struct sockaddr_storage peer_addr = {};
-    struct sockaddr_storage client_addr = {};
-    socklen_t peer_socklen = sizeof(peer_addr);
-    socklen_t client_socklen = sizeof(client_addr);
-
     server = network_loopback_server(0, SOCK_STREAM, &error);
     if (server < 0) {
         D("adb_socketpair: failed to create server: %s", error.c_str());
@@ -1066,32 +1061,12 @@
         goto fail;
     }
 
-    // Make sure that the peer that connected to us and the client are the same.
-    accepted = adb_socket_accept(server, reinterpret_cast<sockaddr*>(&peer_addr), &peer_socklen);
+    accepted = adb_socket_accept(server, nullptr, nullptr);
     if (accepted < 0) {
         D("adb_socketpair: failed to accept: %s", strerror(errno));
         goto fail;
     }
-
-    if (adb_getsockname(client, reinterpret_cast<sockaddr*>(&client_addr), &client_socklen) != 0) {
-        D("adb_socketpair: failed to getpeername: %s", strerror(errno));
-        goto fail;
-    }
-
-    if (peer_socklen != client_socklen) {
-        D("adb_socketpair: client and peer sockaddrs have different lengths");
-        errno = EIO;
-        goto fail;
-    }
-
-    if (memcmp(&peer_addr, &client_addr, peer_socklen) != 0) {
-        D("adb_socketpair: client and peer sockaddrs don't match");
-        errno = EIO;
-        goto fail;
-    }
-
     adb_close(server);
-
     sv[0] = client;
     sv[1] = accepted;
     return 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/base/Android.bp b/base/Android.bp
index 7b1dc8e..3af7686 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -42,24 +42,36 @@
             srcs: [
                 "errors_unix.cpp",
                 "properties.cpp",
+                "chrono_utils.cpp",
             ],
             cppflags: ["-Wexit-time-destructors"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
+
         },
         darwin: {
-            srcs: ["errors_unix.cpp"],
+            srcs: [
+                "chrono_utils.cpp",
+                "errors_unix.cpp",
+            ],
             cppflags: ["-Wexit-time-destructors"],
         },
         linux_bionic: {
-            srcs: ["errors_unix.cpp"],
+            srcs: [
+                "chrono_utils.cpp",
+                "errors_unix.cpp",
+            ],
             cppflags: ["-Wexit-time-destructors"],
             enabled: true,
         },
         linux: {
-            srcs: ["errors_unix.cpp"],
+            srcs: [
+                "chrono_utils.cpp",
+                "errors_unix.cpp",
+            ],
             cppflags: ["-Wexit-time-destructors"],
+            host_ldlibs: ["-lrt"],
         },
         windows: {
             srcs: [
@@ -86,17 +98,25 @@
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
         "quick_exit_test.cpp",
+        "scopeguard_test.cpp",
         "stringprintf_test.cpp",
         "strings_test.cpp",
         "test_main.cpp",
     ],
     target: {
         android: {
-            srcs: ["properties_test.cpp"],
+            srcs: [
+                "chrono_utils_test.cpp",
+                "properties_test.cpp"
+            ],
             sanitize: {
                 misc_undefined: ["integer"],
             },
         },
+        linux: {
+            srcs: ["chrono_utils_test.cpp"],
+            host_ldlibs: ["-lrt"],
+        },
         windows: {
             srcs: ["utf8_test.cpp"],
             enabled: true,
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
new file mode 100644
index 0000000..5eedf3b
--- /dev/null
+++ b/base/chrono_utils.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 "android-base/chrono_utils.h"
+
+#include <time.h>
+
+namespace android {
+namespace base {
+
+boot_clock::time_point boot_clock::now() {
+#ifdef __ANDROID__
+  timespec ts;
+  clock_gettime(CLOCK_BOOTTIME, &ts);
+  return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
+                                std::chrono::nanoseconds(ts.tv_nsec));
+#else
+  // Darwin does not support clock_gettime.
+  return boot_clock::time_point();
+#endif  // __ANDROID__
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp
new file mode 100644
index 0000000..057132d
--- /dev/null
+++ b/base/chrono_utils_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "android-base/chrono_utils.h"
+
+#include <time.h>
+
+#include <chrono>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+std::chrono::seconds GetBootTimeSeconds() {
+  struct timespec now;
+  clock_gettime(CLOCK_BOOTTIME, &now);
+
+  auto now_tp = boot_clock::time_point(std::chrono::seconds(now.tv_sec) +
+                                       std::chrono::nanoseconds(now.tv_nsec));
+  return std::chrono::duration_cast<std::chrono::seconds>(now_tp.time_since_epoch());
+}
+
+// Tests (at least) the seconds accuracy of the boot_clock::now() method.
+TEST(ChronoUtilsTest, BootClockNowSeconds) {
+  auto now = GetBootTimeSeconds();
+  auto boot_seconds =
+      std::chrono::duration_cast<std::chrono::seconds>(boot_clock::now().time_since_epoch());
+  EXPECT_EQ(now, boot_seconds);
+}
+
+}  // namespace base
+}  // namespace android
\ No newline at end of file
diff --git a/base/file.cpp b/base/file.cpp
index d4e5894..7fbebc5 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -28,8 +28,9 @@
 #include <string>
 #include <vector>
 
-#include "android-base/macros.h"  // For TEMP_FAILURE_RETRY on Darwin.
 #include "android-base/logging.h"
+#include "android-base/macros.h"  // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/unique_fd.h"
 #include "android-base/utf8.h"
 #include "utils/Compat.h"
 
@@ -69,13 +70,11 @@
   content->clear();
 
   int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
-  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
   if (fd == -1) {
     return false;
   }
-  bool result = ReadFdToString(fd, content);
-  close(fd);
-  return result;
+  return ReadFdToString(fd, content);
 }
 
 bool WriteStringToFd(const std::string& content, int fd) {
@@ -106,7 +105,7 @@
                        bool follow_symlinks) {
   int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
               (follow_symlinks ? 0 : O_NOFOLLOW);
-  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
   if (fd == -1) {
     PLOG(ERROR) << "android::WriteStringToFile open failed";
     return false;
@@ -126,7 +125,6 @@
     PLOG(ERROR) << "android::WriteStringToFile write failed";
     return CleanUpAfterFailedWrite(path);
   }
-  close(fd);
   return true;
 }
 #endif
@@ -135,14 +133,11 @@
                        bool follow_symlinks) {
   int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
               (follow_symlinks ? 0 : O_NOFOLLOW);
-  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE)));
   if (fd == -1) {
     return false;
   }
-
-  bool result = WriteStringToFd(content, fd);
-  close(fd);
-  return result || CleanUpAfterFailedWrite(path);
+  return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
 }
 
 bool ReadFully(int fd, void* data, size_t byte_count) {
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
new file mode 100644
index 0000000..0086425
--- /dev/null
+++ b/base/include/android-base/chrono_utils.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_BASE_CHRONO_UTILS_H
+#define ANDROID_BASE_CHRONO_UTILS_H
+
+#include <chrono>
+
+namespace android {
+namespace base {
+
+// A std::chrono clock based on CLOCK_BOOTTIME.
+class boot_clock {
+ public:
+  typedef std::chrono::nanoseconds duration;
+  typedef std::chrono::time_point<boot_clock, duration> time_point;
+
+  static time_point now();
+};
+
+}  // namespace base
+}  // namespace android
+
+#endif  // ANDROID_BASE_CHRONO_UTILS_H
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
new file mode 100644
index 0000000..abcf4bc
--- /dev/null
+++ b/base/include/android-base/scopeguard.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 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_BASE_SCOPEGUARD_H
+#define ANDROID_BASE_SCOPEGUARD_H
+
+#include <utility>  // for std::move
+
+namespace android {
+namespace base {
+
+template <typename F>
+class ScopeGuard {
+ public:
+  ScopeGuard(F f) : f_(f), active_(true) {}
+
+  ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+    that.active_ = false;
+  }
+
+  ~ScopeGuard() {
+    if (active_) f_();
+  }
+
+  ScopeGuard() = delete;
+  ScopeGuard(const ScopeGuard&) = delete;
+  void operator=(const ScopeGuard&) = delete;
+  void operator=(ScopeGuard&& that) = delete;
+
+  void Disable() { active_ = false; }
+
+  bool active() const { return active_; }
+
+ private:
+  F f_;
+  bool active_;
+};
+
+template <typename T>
+ScopeGuard<T> make_scope_guard(T f) {
+  return ScopeGuard<T>(f);
+}
+
+}  // namespace base
+}  // namespace android
+
+#endif  // ANDROID_BASE_SCOPEGUARD_H
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
new file mode 100644
index 0000000..e11154a
--- /dev/null
+++ b/base/scopeguard_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "android-base/scopeguard.h"
+
+#include <utility>
+
+#include <gtest/gtest.h>
+
+TEST(scopeguard, normal) {
+  bool guarded_var = true;
+  {
+    auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+  }
+  ASSERT_FALSE(guarded_var);
+}
+
+TEST(scopeguard, disabled) {
+  bool guarded_var = true;
+  {
+    auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+    scopeguard.Disable();
+  }
+  ASSERT_TRUE(guarded_var);
+}
+
+TEST(scopeguard, moved) {
+  int guarded_var = true;
+  auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+  { decltype(scopeguard) new_guard(std::move(scopeguard)); }
+  EXPECT_FALSE(scopeguard.active());
+  ASSERT_FALSE(guarded_var);
+}
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index f744ad1..bc90a6e 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -16,7 +16,6 @@
 
 bootstat_lib_src_files = [
     "boot_event_record_store.cpp",
-    "uptime_parser.cpp",
 ]
 
 cc_defaults {
@@ -75,6 +74,7 @@
 // -----------------------------------------------------------------------------
 cc_test {
     name: "bootstat_tests",
+    test_suites: ["device-tests"],
     defaults: ["bootstat_defaults"],
     host_supported: true,
     static_libs: [
diff --git a/bootstat/AndroidTest.xml b/bootstat/AndroidTest.xml
new file mode 100644
index 0000000..f3783fa
--- /dev/null
+++ b/bootstat/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for bootstat_tests">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="bootstat_tests->/data/local/tmp/bootstat_tests" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="bootstat_tests" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 2648594..99d9405 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -20,13 +20,16 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <utime.h>
+
+#include <chrono>
 #include <cstdlib>
 #include <string>
 #include <utility>
+
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
-#include "uptime_parser.h"
 
 namespace {
 
@@ -55,7 +58,9 @@
 }
 
 void BootEventRecordStore::AddBootEvent(const std::string& event) {
-  AddBootEventWithValue(event, bootstat::ParseUptime());
+    auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
+        android::base::boot_clock::now().time_since_epoch());
+    AddBootEventWithValue(event, uptime.count());
 }
 
 // The implementation of AddBootEventValue makes use of the mtime file
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 90f6513..d98169b 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -21,15 +21,18 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <unistd.h>
+
+#include <chrono>
 #include <cstdint>
 #include <cstdlib>
+
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
 #include <gmock/gmock.h>
-#include "uptime_parser.h"
+#include <gtest/gtest.h>
 
 using testing::UnorderedElementsAreArray;
 
@@ -89,6 +92,13 @@
   rmdir(path.c_str());
 }
 
+// Returns the time in seconds since boot.
+time_t GetUptimeSeconds() {
+    return std::chrono::duration_cast<std::chrono::seconds>(
+               android::base::boot_clock::now().time_since_epoch())
+        .count();
+}
+
 class BootEventRecordStoreTest : public ::testing::Test {
  public:
   BootEventRecordStoreTest() {
@@ -126,7 +136,7 @@
   BootEventRecordStore store;
   store.SetStorePath(GetStorePathForTesting());
 
-  time_t uptime = bootstat::ParseUptime();
+  time_t uptime = GetUptimeSeconds();
   ASSERT_NE(-1, uptime);
 
   store.AddBootEvent("cenozoic");
@@ -141,7 +151,7 @@
   BootEventRecordStore store;
   store.SetStorePath(GetStorePathForTesting());
 
-  time_t uptime = bootstat::ParseUptime();
+  time_t uptime = GetUptimeSeconds();
   ASSERT_NE(-1, uptime);
 
   store.AddBootEvent("cretaceous");
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a4cc5f2..6f25d96 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -21,6 +21,7 @@
 #include <getopt.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <cmath>
 #include <cstddef>
 #include <cstdio>
@@ -30,15 +31,15 @@
 #include <string>
 #include <vector>
 
-#include <android/log.h>
+#include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
+#include <android/log.h>
 #include <cutils/properties.h>
 #include <metricslogger/metrics_logger.h>
 
 #include "boot_event_record_store.h"
-#include "uptime_parser.h"
 
 namespace {
 
@@ -255,7 +256,8 @@
   BootEventRecordStore boot_event_store;
   BootEventRecordStore::BootEventRecord record;
 
-  time_t uptime = bootstat::ParseUptime();
+  auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
+      android::base::boot_clock::now().time_since_epoch());
   time_t current_time_utc = time(nullptr);
 
   if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
@@ -282,22 +284,21 @@
     // Log the amount of time elapsed until the device is decrypted, which
     // includes the variable amount of time the user takes to enter the
     // decryption password.
-    boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime);
+    boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime.count());
 
     // Subtract the decryption time to normalize the boot cycle timing.
-    time_t boot_complete = uptime - record.second;
+    std::chrono::seconds boot_complete = std::chrono::seconds(uptime.count() - record.second);
     boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt",
-                                           boot_complete);
-
+                                           boot_complete.count());
 
   } else {
-    boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
-                                           uptime);
+      boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
+                                             uptime.count());
   }
 
   // Record the total time from device startup to boot complete, regardless of
   // encryption state.
-  boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime);
+  boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime.count());
 
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init");
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux");
diff --git a/bootstat/uptime_parser.cpp b/bootstat/uptime_parser.cpp
deleted file mode 100644
index 7c2034c..0000000
--- a/bootstat/uptime_parser.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 "uptime_parser.h"
-
-#include <time.h>
-#include <cstdlib>
-#include <string>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-
-namespace bootstat {
-
-time_t ParseUptime() {
-  std::string uptime_str;
-  if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
-    PLOG(ERROR) << "Failed to read /proc/uptime";
-    return -1;
-  }
-
-  // Cast intentionally rounds down.
-  return static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
-}
-
-}  // namespace bootstat
\ No newline at end of file
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 024bc3d..4783d6e 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -163,6 +163,7 @@
             srcs: [
                 "client/debuggerd_client_test.cpp",
                 "debuggerd_test.cpp",
+                "tombstoned_client.cpp",
                 "util.cpp"
             ],
         },
@@ -176,7 +177,8 @@
     ],
 
     static_libs: [
-        "libdebuggerd"
+        "libdebuggerd",
+        "libc_logging",
     ],
 
     local_include_dirs: [
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index b9fb512..2be13c6 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -58,31 +58,35 @@
 }
 
 bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
-                            int timeout_ms) {
+                            unsigned int timeout_ms) {
   LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
   unique_fd sockfd;
   const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
-  auto time_left = [timeout_ms, &end]() { return end - std::chrono::steady_clock::now(); };
+  auto time_left = [&end]() { return end - std::chrono::steady_clock::now(); };
   auto set_timeout = [timeout_ms, &time_left](int sockfd) {
     if (timeout_ms <= 0) {
-      return true;
+      return sockfd;
     }
 
     auto remaining = time_left();
     if (remaining < decltype(remaining)::zero()) {
-      return false;
+      LOG(ERROR) << "libdebuggerd_client: timeout expired";
+      return -1;
     }
+
     struct timeval timeout;
     populate_timeval(&timeout, remaining);
 
     if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
-      return false;
+      PLOG(ERROR) << "libdebuggerd_client: failed to set receive timeout";
+      return -1;
     }
     if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
-      return false;
+      PLOG(ERROR) << "libdebuggerd_client: failed to set send timeout";
+      return -1;
     }
 
-    return true;
+    return sockfd;
   };
 
   sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
@@ -91,12 +95,7 @@
     return false;
   }
 
-  if (!set_timeout(sockfd)) {
-    PLOG(ERROR) << "libdebugger_client: failed to set timeout";
-    return false;
-  }
-
-  if (socket_local_client_connect(sockfd.get(), kTombstonedInterceptSocketName,
+  if (socket_local_client_connect(set_timeout(sockfd.get()), kTombstonedInterceptSocketName,
                                   ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {
     PLOG(ERROR) << "libdebuggerd_client: failed to connect to tombstoned";
     return false;
@@ -115,21 +114,37 @@
     return false;
   }
 
-  if (send_fd(sockfd.get(), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
+  if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
     PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
     return false;
   }
 
-  bool backtrace = dump_type == kDebuggerdBacktrace;
-  send_signal(pid, backtrace);
-
-  if (!set_timeout(sockfd)) {
-    PLOG(ERROR) << "libdebuggerd_client: failed to set timeout";
+  // Check to make sure we've successfully registered.
+  InterceptResponse response;
+  ssize_t rc =
+      TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+  if (rc == 0) {
+    LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+    return false;
+  } else if (rc != sizeof(response)) {
+    LOG(ERROR)
+        << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
+        << sizeof(response) << ", received " << rc;
     return false;
   }
 
-  InterceptResponse response;
-  ssize_t rc = TEMP_FAILURE_RETRY(recv(sockfd.get(), &response, sizeof(response), MSG_TRUNC));
+  if (response.status != InterceptStatus::kRegistered) {
+    LOG(ERROR) << "libdebuggerd_client: unexpected registration response: "
+               << static_cast<int>(response.status);
+    return false;
+  }
+
+  bool backtrace = dump_type == kDebuggerdBacktrace;
+  if (!send_signal(pid, backtrace)) {
+    return false;
+  }
+
+  rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
   if (rc == 0) {
     LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
     return false;
@@ -140,7 +155,7 @@
     return false;
   }
 
-  if (response.success != 1) {
+  if (response.status != InterceptStatus::kStarted) {
     response.error_message[sizeof(response.error_message) - 1] = '\0';
     LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
     return false;
@@ -148,8 +163,10 @@
 
   // Forward output from the pipe to the output fd.
   while (true) {
-    auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_left());
-    if (remaining_ms <= 1ms) {
+    auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_left()).count();
+    if (timeout_ms <= 0) {
+      remaining_ms = -1;
+    } else if (remaining_ms < 0) {
       LOG(ERROR) << "libdebuggerd_client: timeout expired";
       return false;
     }
@@ -158,7 +175,7 @@
         .fd = pipe_read.get(), .events = POLLIN, .revents = 0,
     };
 
-    rc = poll(&pfd, 1, remaining_ms.count());
+    rc = poll(&pfd, 1, remaining_ms);
     if (rc == -1) {
       if (errno == EINTR) {
         continue;
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 86d0314..aff03e5 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -89,3 +89,23 @@
 
   EXPECT_EQ(1, found_end) << "\nOutput: \n" << result;
 }
+
+TEST(debuggerd_client, no_timeout) {
+  unique_fd pipe_read, pipe_write;
+  ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));
+
+  pid_t forkpid = fork();
+  ASSERT_NE(-1, forkpid);
+  if (forkpid == 0) {
+    pipe_write.reset();
+    char dummy;
+    TEMP_FAILURE_RETRY(read(pipe_read.get(), &dummy, sizeof(dummy)));
+    exit(0);
+  }
+
+  pipe_read.reset();
+
+  unique_fd output_read, output_write;
+  ASSERT_TRUE(Pipe(&output_read, &output_write));
+  ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(output_write), kDebuggerdBacktrace, 0));
+}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 1a27f3f..b705e27 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -36,6 +36,7 @@
 #include <cutils/sockets.h>
 #include <debuggerd/handler.h>
 #include <debuggerd/protocol.h>
+#include <debuggerd/tombstoned.h>
 #include <debuggerd/util.h>
 #include <gtest/gtest.h>
 
@@ -77,6 +78,54 @@
     }                                                                           \
   } while (0)
 
+static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) {
+  intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
+                                          ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+  if (intercept_fd->get() == -1) {
+    FAIL() << "failed to contact tombstoned: " << strerror(errno);
+  }
+
+  InterceptRequest req = {.pid = target_pid};
+
+  unique_fd output_pipe_write;
+  if (!Pipe(output_fd, &output_pipe_write)) {
+    FAIL() << "failed to create output pipe: " << strerror(errno);
+  }
+
+  std::string pipe_size_str;
+  int pipe_buffer_size;
+  if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
+    FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
+  }
+
+  pipe_size_str = android::base::Trim(pipe_size_str);
+
+  if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
+    FAIL() << "failed to parse pipe max size";
+  }
+
+  if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
+    FAIL() << "failed to set pipe size: " << strerror(errno);
+  }
+
+  if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+    FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
+  }
+
+  InterceptResponse response;
+  ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+  if (rc == -1) {
+    FAIL() << "failed to read response from tombstoned: " << strerror(errno);
+  } else if (rc == 0) {
+    FAIL() << "failed to read response from tombstoned (EOF)";
+  } else if (rc != sizeof(response)) {
+    FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
+           << ", received " << rc;
+  }
+
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status);
+}
+
 class CrasherTest : public ::testing::Test {
  public:
   pid_t crasher_pid = -1;
@@ -118,38 +167,7 @@
     FAIL() << "crasher hasn't been started";
   }
 
-  intercept_fd.reset(socket_local_client(kTombstonedInterceptSocketName,
-                                         ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
-  if (intercept_fd == -1) {
-    FAIL() << "failed to contact tombstoned: " << strerror(errno);
-  }
-
-  InterceptRequest req = {.pid = crasher_pid };
-
-  unique_fd output_pipe_write;
-  if (!Pipe(output_fd, &output_pipe_write)) {
-    FAIL() << "failed to create output pipe: " << strerror(errno);
-  }
-
-  std::string pipe_size_str;
-  int pipe_buffer_size;
-  if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
-    FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
-  }
-
-  pipe_size_str = android::base::Trim(pipe_size_str);
-
-  if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
-    FAIL() << "failed to parse pipe max size";
-  }
-
-  if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
-    FAIL() << "failed to set pipe size: " << strerror(errno);
-  }
-
-  if (send_fd(intercept_fd.get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
-    FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
-  }
+  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd);
 }
 
 void CrasherTest::FinishIntercept(int* result) {
@@ -165,7 +183,7 @@
     FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
            << ", received " << rc;
   } else {
-    *result = response.success;
+    *result = response.status == InterceptStatus::kStarted ? 1 : 0;
   }
 }
 
@@ -461,6 +479,7 @@
       err(1, "failed to drop ambient capabilities");
     }
 
+    pthread_setname_np(pthread_self(), "thread_name");
     raise(SIGSYS);
   });
 
@@ -474,15 +493,13 @@
   FinishIntercept(&intercept_result);
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(name: thread_name\s+>>> .+debuggerd_test(32|64) <<<)");
   ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
 }
 
 TEST(crash_dump, zombie) {
   pid_t forkpid = fork();
 
-  int pipefd[2];
-  ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
-
   pid_t rc;
   int status;
 
@@ -508,3 +525,97 @@
     ASSERT_EQ(0, WEXITSTATUS(status));
   }
 }
+
+TEST(tombstoned, no_notify) {
+  // Do this a few times.
+  for (int i = 0; i < 3; ++i) {
+    pid_t pid = 123'456'789 + i;
+
+    unique_fd intercept_fd, output_fd;
+    tombstoned_intercept(pid, &intercept_fd, &output_fd);
+
+    {
+      unique_fd tombstoned_socket, input_fd;
+      ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+      ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
+    }
+
+    pid_t read_pid;
+    ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));
+    ASSERT_EQ(read_pid, pid);
+  }
+}
+
+TEST(tombstoned, stress) {
+  // Spawn threads to simultaneously do a bunch of failing dumps and a bunch of successful dumps.
+  static constexpr int kDumpCount = 100;
+
+  std::atomic<bool> start(false);
+  std::vector<std::thread> threads;
+  threads.emplace_back([&start]() {
+    while (!start) {
+      continue;
+    }
+
+    // Use a way out of range pid, to avoid stomping on an actual process.
+    pid_t pid_base = 1'000'000;
+
+    for (int dump = 0; dump < kDumpCount; ++dump) {
+      pid_t pid = pid_base + dump;
+
+      unique_fd intercept_fd, output_fd;
+      tombstoned_intercept(pid, &intercept_fd, &output_fd);
+
+      // Pretend to crash, and then immediately close the socket.
+      unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
+                                           ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+      if (sockfd == -1) {
+        FAIL() << "failed to connect to tombstoned: " << strerror(errno);
+      }
+      TombstonedCrashPacket packet = {};
+      packet.packet_type = CrashPacketType::kDumpRequest;
+      packet.packet.dump_request.pid = pid;
+      if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
+        FAIL() << "failed to write to tombstoned: " << strerror(errno);
+      }
+
+      continue;
+    }
+  });
+
+  threads.emplace_back([&start]() {
+    while (!start) {
+      continue;
+    }
+
+    // Use a way out of range pid, to avoid stomping on an actual process.
+    pid_t pid_base = 2'000'000;
+
+    for (int dump = 0; dump < kDumpCount; ++dump) {
+      pid_t pid = pid_base + dump;
+
+      unique_fd intercept_fd, output_fd;
+      tombstoned_intercept(pid, &intercept_fd, &output_fd);
+
+      {
+        unique_fd tombstoned_socket, input_fd;
+        ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+        ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
+        tombstoned_notify_completion(tombstoned_socket.get());
+      }
+
+      // TODO: Fix the race that requires this sleep.
+      std::this_thread::sleep_for(50ms);
+
+      pid_t read_pid;
+      ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));
+      ASSERT_EQ(read_pid, pid);
+    }
+  });
+
+  start = true;
+
+  for (std::thread& thread : threads) {
+    thread.join();
+  }
+}
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
index 91f143b..01de57b 100644
--- a/debuggerd/include/debuggerd/client.h
+++ b/debuggerd/include/debuggerd/client.h
@@ -28,9 +28,9 @@
 };
 
 // Trigger a dump of specified process to output_fd.
-// output_fd is *not* consumed, timeouts <= 0 will wait forever.
+// output_fd is consumed, timeout of 0 will wait forever.
 bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd,
-                            enum DebuggerdDumpType dump_type, int timeout_ms);
+                            enum DebuggerdDumpType dump_type, unsigned int timeout_ms);
 
 int dump_backtrace_to_file(pid_t tid, int fd);
 int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/include/debuggerd/protocol.h
index bb2ab0d..0756876 100644
--- a/debuggerd/include/debuggerd/protocol.h
+++ b/debuggerd/include/debuggerd/protocol.h
@@ -56,8 +56,14 @@
   int32_t pid;
 };
 
+enum class InterceptStatus : uint8_t {
+  kFailed,
+  kStarted,
+  kRegistered,
+};
+
 // Sent either immediately upon failure, or when the intercept has been used.
 struct InterceptResponse {
-  uint8_t success;          // 0 or 1
+  InterceptStatus status;
   char error_message[127];  // always null-terminated
 };
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 789260d..dff942c 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -105,6 +105,7 @@
     // We trust the other side, so only do minimal validity checking.
     if (intercept_request.pid <= 0 || intercept_request.pid > std::numeric_limits<pid_t>::max()) {
       InterceptResponse response = {};
+      response.status = InterceptStatus::kFailed;
       snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
                intercept_request.pid);
       TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
@@ -113,9 +114,10 @@
 
     intercept->intercept_pid = intercept_request.pid;
 
-    // Register the intercept with the InterceptManager.
+    // Check if it's already registered.
     if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
       InterceptResponse response = {};
+      response.status = InterceptStatus::kFailed;
       snprintf(response.error_message, sizeof(response.error_message),
                "pid %" PRId32 " already intercepted", intercept_request.pid);
       TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
@@ -123,6 +125,15 @@
       goto fail;
     }
 
+    // Let the other side know that the intercept has been registered, now that we know we can't
+    // fail. tombstoned is single threaded, so this isn't racy.
+    InterceptResponse response = {};
+    response.status = InterceptStatus::kRegistered;
+    if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {
+      PLOG(WARNING) << "failed to notify interceptor of registration";
+      goto fail;
+    }
+
     intercept->output_fd = std::move(rcv_fd);
     intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
     intercept->registered = true;
@@ -174,7 +185,7 @@
 
   LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
   InterceptResponse response = {};
-  response.success = 1;
+  response.status = InterceptStatus::kStarted;
   TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
   *out_fd = std::move(intercept->output_fd);
 
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 6754508..2248a21 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -126,9 +126,7 @@
   return result;
 }
 
-static void dequeue_request(Crash* crash) {
-  ++num_concurrent_dumps;
-
+static void perform_request(Crash* crash) {
   unique_fd output_fd;
   if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
     output_fd = get_tombstone_fd();
@@ -153,12 +151,22 @@
                  crash_completed_cb, crash);
     event_add(crash->crash_event, &timeout);
   }
+
+  ++num_concurrent_dumps;
   return;
 
 fail:
   delete crash;
 }
 
+static void dequeue_requests() {
+  while (!queued_requests.empty() && num_concurrent_dumps < kMaxConcurrentDumps) {
+    Crash* next_crash = queued_requests.front();
+    queued_requests.pop_front();
+    perform_request(next_crash);
+  }
+}
+
 static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
                             void*) {
   event_base* base = evconnlistener_get_base(listener);
@@ -207,7 +215,7 @@
     LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
     queued_requests.push_back(crash);
   } else {
-    dequeue_request(crash);
+    perform_request(crash);
   }
 
   return;
@@ -247,11 +255,7 @@
   delete crash;
 
   // If there's something queued up, let them proceed.
-  if (!queued_requests.empty()) {
-    Crash* next_crash = queued_requests.front();
-    queued_requests.pop_front();
-    dequeue_request(next_crash);
-  }
+  dequeue_requests();
 }
 
 int main(int, char* []) {
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2927b16..704dc43 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -115,6 +115,7 @@
     {"system_other.img", "system.sig", "system", true, true},
     {"vendor.img", "vendor.sig", "vendor", true, false},
     {"vendor_other.img", "vendor.sig", "vendor", true, true},
+    {"vbmeta.img", "vbmeta.sig", "vbmeta", true, false},
 };
 
 static std::string find_item_given_name(const char* img_name, const char* product) {
@@ -144,6 +145,8 @@
         fn = "system.img";
     } else if(!strcmp(item,"vendor")) {
         fn = "vendor.img";
+    } else if(!strcmp(item,"vbmeta")) {
+        fn = "vbmeta.img";
     } else if(!strcmp(item,"userdata")) {
         fn = "userdata.img";
     } else if(!strcmp(item,"cache")) {
@@ -1536,6 +1539,7 @@
                 setvbuf(stderr, nullptr, _IONBF, 0);
             } else if (strcmp("version", longopts[longindex].name) == 0) {
                 fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
+                fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
                 return 0;
             } else if (strcmp("slot", longopts[longindex].name) == 0) {
                 slot_override = std::string(optarg);
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/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index a762038..7512eb9 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -390,10 +390,12 @@
             continue;
         }
 
-        // Ensures that hashtree descriptor is either in /vbmeta or in
+        // Ensures that hashtree descriptor is in /vbmeta or /boot or in
         // the same partition for verity setup.
         std::string vbmeta_partition_name(verify_data.vbmeta_images[i].partition_name);
-        if (vbmeta_partition_name != "vbmeta" && vbmeta_partition_name != partition_name) {
+        if (vbmeta_partition_name != "vbmeta" &&
+            vbmeta_partition_name != "boot" &&  // for legacy device to append top-level vbmeta
+            vbmeta_partition_name != partition_name) {
             LWARNING << "Skip vbmeta image at " << verify_data.vbmeta_images[i].partition_name
                      << " for partition: " << partition_name.c_str();
             continue;
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index 2c14c9b..8e49663 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -39,7 +39,28 @@
 #include "fs_mgr_avb_ops.h"
 #include "fs_mgr_priv.h"
 
-static struct fstab* fs_mgr_fstab = nullptr;
+static std::string fstab_by_name_prefix;
+
+static std::string extract_by_name_prefix(struct fstab* fstab) {
+    // In AVB, we can assume that there's an entry for the /misc mount
+    // point in the fstab file and use that to get the device file for
+    // the misc partition. The device needs not to have an actual /misc
+    // partition. Then returns the prefix by removing the trailing "misc":
+    //
+    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/
+
+    struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+    if (fstab_entry == nullptr) {
+        LERROR << "/misc mount point not found in fstab";
+        return "";
+    }
+
+    std::string full_path(fstab_entry->blk_device);
+    size_t end_slash = full_path.find_last_of("/");
+
+    return full_path.substr(0, end_slash + 1);
+}
 
 static AvbIOResult read_from_partition(AvbOps* ops ATTRIBUTE_UNUSED, const char* partition,
                                        int64_t offset, size_t num_bytes, void* buffer,
@@ -49,30 +70,14 @@
     // for partitions having 'slotselect' optin in fstab file, but it
     // won't be appended to the mount point.
     //
-    // In AVB, we can assume that there's an entry for the /misc mount
-    // point and use that to get the device file for the misc partition.
-    // From there we'll assume that a by-name scheme is used
-    // so we can just replace the trailing "misc" by the given
-    // |partition|, e.g.
+    // Appends |partition| to the fstab_by_name_prefix, which is obtained
+    // by removing the trailing "misc" from the device file of /misc mount
+    // point. e.g.,
     //
-    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/ ->
     //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
 
-    struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc");
-
-    if (fstab_entry == nullptr) {
-        LERROR << "/misc mount point not found in fstab";
-        return AVB_IO_RESULT_ERROR_IO;
-    }
-
-    std::string partition_name(partition);
-    std::string path(fstab_entry->blk_device);
-    // Replaces the last field of device file if it's not misc.
-    if (!android::base::StartsWith(partition_name, "misc")) {
-        size_t end_slash = path.find_last_of("/");
-        std::string by_name_prefix(path.substr(0, end_slash + 1));
-        path = by_name_prefix + partition_name;
-    }
+    std::string path = fstab_by_name_prefix + partition;
 
     // Ensures the device path (a symlink created by init) is ready to
     // access. fs_mgr_test_access() will test a few iterations if the
@@ -168,8 +173,8 @@
 AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab) {
     AvbOps* ops;
 
-    // Assigns the fstab to the static variable for later use.
-    fs_mgr_fstab = fstab;
+    fstab_by_name_prefix = extract_by_name_prefix(fstab);
+    if (fstab_by_name_prefix.empty()) return nullptr;
 
     ops = (AvbOps*)calloc(1, sizeof(AvbOps));
     if (ops == nullptr) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 720ec03..0406efd 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -625,17 +625,6 @@
     return fstab;
 }
 
-/* combines fstab entries passed in from device tree with
- * the ones found from fstab_path
- */
-struct fstab *fs_mgr_read_fstab_with_dt(const char *fstab_path)
-{
-    struct fstab *fstab_dt = fs_mgr_read_fstab_dt();
-    struct fstab *fstab = fs_mgr_read_fstab(fstab_path);
-
-    return in_place_merge(fstab_dt, fstab);
-}
-
 /*
  * tries to load default fstab.<hardware> file from /odm/etc, /vendor/etc
  * or /. loads the first one found and also combines fstab entries passed
@@ -658,7 +647,12 @@
         LWARNING << __FUNCTION__ << "(): failed to find device hardware name";
     }
 
-    return fs_mgr_read_fstab_with_dt(default_fstab.c_str());
+    // combines fstab entries passed in from device tree with
+    // the ones found from default_fstab file
+    struct fstab *fstab_dt = fs_mgr_read_fstab_dt();
+    struct fstab *fstab = fs_mgr_read_fstab(default_fstab.c_str());
+
+    return in_place_merge(fstab_dt, fstab);
 }
 
 void fs_mgr_free_fstab(struct fstab *fstab)
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index e4aeb48..4b626de 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -88,7 +88,6 @@
 struct fstab *fs_mgr_read_fstab_default();
 struct fstab *fs_mgr_read_fstab_dt();
 struct fstab *fs_mgr_read_fstab(const char *fstab_path);
-struct fstab *fs_mgr_read_fstab_with_dt(const char *fstab_path);
 void fs_mgr_free_fstab(struct fstab *fstab);
 
 #define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
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 b/include/system
new file mode 120000
index 0000000..91d45be
--- /dev/null
+++ b/include/system
@@ -0,0 +1 @@
+../libsystem/include/system/
\ No newline at end of file
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 41ca2e1..08ead48 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -75,7 +75,8 @@
     uint32_t uncompressed_size;
     uint16_t last_mod_time;
     uint16_t last_mod_date;
-    uint32_t local_file_header_offset;
+    uint32_t padding_length;
+    off64_t local_file_header_offset;
   };
 
   static const char* ErrorCodeString(int32_t error_code);
@@ -172,6 +173,7 @@
   };
 
   FILE* file_;
+  bool seekable_;
   off64_t current_offset_;
   State state_;
   std::vector<FileEntry> files_;
diff --git a/init/Android.mk b/init/Android.mk
index b52c949..730ffc4 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -16,6 +16,14 @@
     -DREBOOT_BOOTLOADER_ON_PANIC=0
 endif
 
+ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
+init_options += \
+    -DSHUTDOWN_ZERO_TIMEOUT=1
+else
+init_options += \
+    -DSHUTDOWN_ZERO_TIMEOUT=0
+endif
+
 init_options += -DLOG_UEVENTS=0
 
 init_cflags += \
@@ -54,6 +62,7 @@
     action.cpp \
     capabilities.cpp \
     descriptors.cpp \
+    devices.cpp \
     import_parser.cpp \
     init_parser.cpp \
     log.cpp \
@@ -73,7 +82,6 @@
 LOCAL_SRC_FILES:= \
     bootchart.cpp \
     builtins.cpp \
-    devices.cpp \
     init.cpp \
     keychords.cpp \
     property_service.cpp \
@@ -130,6 +138,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := init_tests
 LOCAL_SRC_FILES := \
+    devices_test.cpp \
     init_parser_test.cpp \
     property_service_test.cpp \
     util_test.cpp \
diff --git a/init/README.md b/init/README.md
index 024d559..fc50730 100644
--- a/init/README.md
+++ b/init/README.md
@@ -198,6 +198,13 @@
   is in the class "default" if one is not specified via the
   class option. Additional classnames beyond the (required) first
   one are used to group services.
+`animation class`
+> 'animation' class should include all services necessary for both
+  boot animation and shutdown animation. As these services can be
+  launched very early during bootup and can run until the last stage
+  of shutdown, access to /data partition is not guaranteed. These
+  services can check files under /data but it should not keep files opened
+  and should work when /data is not available.
 
 `onrestart`
 > Execute a Command (see below) when service restarts.
@@ -286,7 +293,7 @@
 `copy <src> <dst>`
 > Copies a file. Similar to write, but useful for binary/large
   amounts of data.
-  Regarding to the src file, copying from symbol link file and world-writable
+  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
@@ -311,6 +318,12 @@
   groups can be provided. No other commands will be run until this one
   finishes. _seclabel_ can be a - to denote default. Properties are expanded
   within _argument_.
+  Init halts executing commands until the forked process exits.
+
+`exec_start <service>`
+> Start service a given service and halt processing of additional init commands
+  until it returns.  It functions similarly to the `exec` command, but uses an
+  existing service definition instead of providing them as arguments.
 
 `export <name> <value>`
 > Set the environment variable _name_ equal to _value_ in the
diff --git a/init/action.cpp b/init/action.cpp
index 1bba0f2..347edb0 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -16,16 +16,11 @@
 
 #include "action.h"
 
-#include <errno.h>
-
-#include <android-base/strings.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 
-#include "builtins.h"
-#include "error.h"
-#include "init_parser.h"
-#include "log.h"
-#include "property_service.h"
 #include "util.h"
 
 using android::base::Join;
@@ -219,9 +214,8 @@
                 found = true;
             }
         } else {
-            std::string prop_val = property_get(trigger_name.c_str());
-            if (prop_val.empty() || (trigger_value != "*" &&
-                                     trigger_value != prop_val)) {
+            std::string prop_val = android::base::GetProperty(trigger_name, "");
+            if (prop_val.empty() || (trigger_value != "*" && trigger_value != prop_val)) {
                 return false;
             }
         }
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 4a9c32e..825603a 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -16,10 +16,7 @@
 
 #include "bootchart.h"
 
-#include "property_service.h"
-
 #include <dirent.h>
-#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -33,12 +30,11 @@
 #include <condition_variable>
 #include <memory>
 #include <mutex>
-#include <string>
 #include <thread>
-#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 
 using android::base::StringPrintf;
@@ -72,7 +68,7 @@
   utsname uts;
   if (uname(&uts) == -1) return;
 
-  std::string fingerprint = property_get("ro.build.fingerprint");
+  std::string fingerprint = android::base::GetProperty("ro.build.fingerprint", "");
   if (fingerprint.empty()) return;
 
   std::string kernel_cmdline;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index ec55b03..e0a98f5 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,49 +19,44 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/loop.h>
+#include <linux/module.h>
 #include <mntent.h>
 #include <net/if.h>
-#include <signal.h>
 #include <sched.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/socket.h>
 #include <sys/mount.h>
 #include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/time.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 #include <sys/wait.h>
 #include <unistd.h>
-#include <linux/loop.h>
-#include <linux/module.h>
 
-#include <string>
-#include <thread>
-
-#include <selinux/android.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-
-#include <fs_mgr.h>
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/parseint.h>
-#include <android-base/strings.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
 #include <ext4_utils/ext4_crypt.h>
 #include <ext4_utils/ext4_crypt_init_extensions.h>
-#include <logwrap/logwrap.h>
+#include <fs_mgr.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
 
 #include "action.h"
 #include "bootchart.h"
-#include "devices.h"
 #include "init.h"
 #include "init_parser.h"
-#include "log.h"
 #include "property_service.h"
 #include "reboot.h"
 #include "service.h"
@@ -154,7 +149,7 @@
 }
 
 static int do_domainname(const std::vector<std::string>& args) {
-    return write_file("/proc/sys/kernel/domainname", args[1].c_str()) ? 0 : 1;
+    return write_file("/proc/sys/kernel/domainname", args[1]) ? 0 : 1;
 }
 
 static int do_enable(const std::vector<std::string>& args) {
@@ -166,19 +161,11 @@
 }
 
 static int do_exec(const std::vector<std::string>& args) {
-    Service* svc = ServiceManager::GetInstance().MakeExecOneshotService(args);
-    if (!svc) {
-        return -1;
-    }
-    if (!start_waiting_for_exec()) {
-        return -1;
-    }
-    if (!svc->Start()) {
-        stop_waiting_for_exec();
-        ServiceManager::GetInstance().RemoveService(*svc);
-        return -1;
-    }
-    return 0;
+    return ServiceManager::GetInstance().Exec(args) ? 0 : -1;
+}
+
+static int do_exec_start(const std::vector<std::string>& args) {
+    return ServiceManager::GetInstance().ExecStart(args[1]) ? 0 : -1;
 }
 
 static int do_export(const std::vector<std::string>& args) {
@@ -186,7 +173,7 @@
 }
 
 static int do_hostname(const std::vector<std::string>& args) {
-    return write_file("/proc/sys/kernel/hostname", args[1].c_str()) ? 0 : 1;
+    return write_file("/proc/sys/kernel/hostname", args[1]) ? 0 : 1;
 }
 
 static int do_ifup(const std::vector<std::string>& args) {
@@ -703,15 +690,13 @@
 }
 
 static int do_write(const std::vector<std::string>& args) {
-    const char* path = args[1].c_str();
-    const char* value = args[2].c_str();
-    return write_file(path, value) ? 0 : 1;
+    return write_file(args[1], args[2]) ? 0 : 1;
 }
 
 static int do_copy(const std::vector<std::string>& args) {
     std::string data;
-    if (read_file(args[1].c_str(), &data)) {
-        return write_file(args[2].c_str(), data.data()) ? 0 : 1;
+    if (read_file(args[1], &data)) {
+        return write_file(args[2], data) ? 0 : 1;
     }
     return 1;
 }
@@ -879,16 +864,21 @@
 }
 
 static bool is_file_crypto() {
-    std::string value = property_get("ro.crypto.type");
-    return value == "file";
+    return android::base::GetProperty("ro.crypto.type", "") == "file";
 }
 
 static int do_installkey(const std::vector<std::string>& args) {
     if (!is_file_crypto()) {
         return 0;
     }
-    return e4crypt_create_device_key(args[1].c_str(),
-                                     do_installkeys_ensure_dir_exists);
+    auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
+    if (do_installkeys_ensure_dir_exists(unencrypted_dir.c_str())) {
+        PLOG(ERROR) << "Failed to create " << unencrypted_dir;
+        return -1;
+    }
+    std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+                                          "enablefilecrypto"};
+    return do_exec(exec_args);
 }
 
 static int do_init_user0(const std::vector<std::string>& args) {
@@ -897,6 +887,7 @@
 
 BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+    // clang-format off
     static const Map builtin_functions = {
         {"bootchart",               {1,     1,    do_bootchart}},
         {"chmod",                   {2,     2,    do_chmod}},
@@ -909,6 +900,7 @@
         {"domainname",              {1,     1,    do_domainname}},
         {"enable",                  {1,     1,    do_enable}},
         {"exec",                    {1,     kMax, do_exec}},
+        {"exec_start",              {1,     1,    do_exec_start}},
         {"export",                  {2,     2,    do_export}},
         {"hostname",                {1,     1,    do_hostname}},
         {"ifup",                    {1,     1,    do_ifup}},
@@ -942,5 +934,6 @@
         {"wait_for_prop",           {2,     2,    do_wait_for_prop}},
         {"write",                   {2,     2,    do_write}},
     };
+    // clang-format on
     return builtin_functions;
 }
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index 6e457cd..bc6bc8d 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -19,16 +19,15 @@
 #include <ctype.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <cutils/android_get_control_file.h>
 #include <cutils/sockets.h>
 
 #include "init.h"
-#include "log.h"
 #include "util.h"
 
 DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
diff --git a/init/devices.cpp b/init/devices.cpp
index 405f92e..760e0f5 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -19,41 +19,37 @@
 #include <fcntl.h>
 #include <fnmatch.h>
 #include <libgen.h>
+#include <linux/netlink.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/sendfile.h>
 #include <sys/socket.h>
-#include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <linux/netlink.h>
-
 #include <memory>
 #include <thread>
 
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-#include <selinux/android.h>
-#include <selinux/avc.h>
-
-#include <private/android_filesystem_config.h>
-
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <cutils/list.h>
 #include <cutils/uevent.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+#include <selinux/avc.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
 
 #include "devices.h"
 #include "ueventd_parser.h"
 #include "util.h"
-#include "log.h"
 
 #define SYSFS_PREFIX    "/sys"
 static const char *firmware_dirs[] = { "/etc/firmware",
@@ -286,8 +282,7 @@
     }
 }
 
-static void add_platform_device(const char *path)
-{
+void add_platform_device(const char* path) {
     int path_len = strlen(path);
     struct platform_node *bus;
     const char *name = path;
@@ -329,8 +324,7 @@
     return NULL;
 }
 
-static void remove_platform_device(const char *path)
-{
+void remove_platform_device(const char* path) {
     struct listnode *node;
     struct platform_node *bus;
 
@@ -473,8 +467,7 @@
     }
 }
 
-static char **get_character_device_symlinks(struct uevent *uevent)
-{
+char** get_character_device_symlinks(struct uevent* uevent) {
     const char *parent;
     const char *slash;
     char **links;
@@ -526,8 +519,24 @@
     return NULL;
 }
 
-static char **get_block_device_symlinks(struct uevent *uevent)
-{
+// replaces any unacceptable characters with '_', the
+// length of the resulting string is equal to the input string
+void sanitize_partition_name(char* s) {
+    const char* accept =
+        "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "0123456789"
+        "_-.";
+
+    if (!s) return;
+
+    while (*s) {
+        s += strspn(s, accept);
+        if (*s) *s++ = '_';
+    }
+}
+
+char** get_block_device_symlinks(struct uevent* uevent) {
     const char *device;
     struct platform_node *pdev;
     const char *slash;
@@ -562,7 +571,7 @@
 
     if (uevent->partition_name) {
         p = strdup(uevent->partition_name);
-        sanitize(p);
+        sanitize_partition_name(p);
         if (strcmp(uevent->partition_name, p)) {
             LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '" << p << "'";
         }
diff --git a/init/devices.h b/init/devices.h
index 26a064b..abf1e7c 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,9 +17,10 @@
 #ifndef _INIT_DEVICES_H
 #define _INIT_DEVICES_H
 
-#include <functional>
 #include <sys/stat.h>
 
+#include <functional>
+
 enum coldboot_action_t {
     // coldboot continues without creating the device for the uevent
     COLDBOOT_CONTINUE = 0,
@@ -55,4 +56,11 @@
                          unsigned short wildcard);
 int get_device_fd();
 
-#endif	/* _INIT_DEVICES_H */
+// Exposed for testing
+void add_platform_device(const char* path);
+void remove_platform_device(const char* path);
+char** get_character_device_symlinks(uevent* uevent);
+char** get_block_device_symlinks(struct uevent* uevent);
+void sanitize_partition_name(char* s);
+
+#endif /* _INIT_DEVICES_H */
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
new file mode 100644
index 0000000..f79c96d
--- /dev/null
+++ b/init/devices_test.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 "devices.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/scopeguard.h>
+#include <gtest/gtest.h>
+
+template <char** (*Function)(uevent*)>
+void test_get_symlinks(const std::string& platform_device_name, uevent* uevent,
+                       const std::vector<std::string> expected_links) {
+    add_platform_device(platform_device_name.c_str());
+    auto platform_device_remover = android::base::make_scope_guard(
+        [&platform_device_name]() { remove_platform_device(platform_device_name.c_str()); });
+
+    char** result = Function(uevent);
+    auto result_freer = android::base::make_scope_guard([result]() {
+        if (result) {
+            for (int i = 0; result[i]; i++) {
+                free(result[i]);
+            }
+            free(result);
+        }
+    });
+
+    auto expected_size = expected_links.size();
+    if (expected_size == 0) {
+        ASSERT_EQ(nullptr, result);
+    } else {
+        ASSERT_NE(nullptr, result);
+        // First assert size is equal, so we don't overrun expected_links
+        unsigned int size = 0;
+        while (result[size]) ++size;
+        ASSERT_EQ(expected_size, size);
+
+        for (unsigned int i = 0; i < size; ++i) {
+            EXPECT_EQ(expected_links[i], result[i]);
+        }
+    }
+}
+
+TEST(devices, get_character_device_symlinks_success) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
+        .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result{"/dev/usb/ttyname"};
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_pdev_match) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_nothing_after_platform_device) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_usb_found) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_roothub) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_usb_device) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_final_slash) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_final_name) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform) {
+    // These are actual paths from bullhead
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0",
+        .partition_name = nullptr,
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition) {
+    // These are actual paths from bullhead
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = "modem",
+        .partition_num = 1,
+    };
+    std::vector<std::string> expected_result{
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+        "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+    };
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_num) {
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = nullptr,
+        .partition_num = 1,
+    };
+    std::vector<std::string> expected_result{
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+        "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+    };
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_name) {
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = "modem",
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+        "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+    };
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_pci) {
+    const char* platform_device = "/devices/do/not/match";
+    uevent uevent = {
+        .path = "/devices/pci0000:00/0000:00:1f.2/mmcblk0",
+        .partition_name = nullptr,
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_vbd) {
+    const char* platform_device = "/devices/do/not/match";
+    uevent uevent = {
+        .path = "/devices/vbd-1234/mmcblk0", .partition_name = nullptr, .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_no_matches) {
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = nullptr,
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, sanitize_null) {
+    sanitize_partition_name(nullptr);
+}
+
+TEST(devices, sanitize_empty) {
+    std::string empty;
+    sanitize_partition_name(&empty[0]);
+    EXPECT_EQ(0u, empty.size());
+}
+
+TEST(devices, sanitize_allgood) {
+    std::string good =
+        "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "0123456789"
+        "_-.";
+    std::string good_copy = good;
+    sanitize_partition_name(&good[0]);
+    EXPECT_EQ(good_copy, good);
+}
+
+TEST(devices, sanitize_somebad) {
+    std::string string = "abc!@#$%^&*()";
+    sanitize_partition_name(&string[0]);
+    EXPECT_EQ("abc__________", string);
+}
+
+TEST(devices, sanitize_allbad) {
+    std::string string = "!@#$%^&*()";
+    sanitize_partition_name(&string[0]);
+    EXPECT_EQ("__________", string);
+}
+
+TEST(devices, sanitize_onebad) {
+    std::string string = ")";
+    sanitize_partition_name(&string[0]);
+    EXPECT_EQ("_", string);
+}
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index d52247b..8a2bcc2 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -16,12 +16,8 @@
 
 #include "import_parser.h"
 
-#include "errno.h"
+#include <android-base/logging.h>
 
-#include <string>
-#include <vector>
-
-#include "log.h"
 #include "util.h"
 
 bool ImportParser::ParseSection(const std::vector<std::string>& args,
diff --git a/init/init.cpp b/init/init.cpp
index b337c64..023d727 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "init.h"
+
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
@@ -40,7 +42,10 @@
 #include <selinux/label.h>
 #include <selinux/android.h>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -57,7 +62,6 @@
 #include "devices.h"
 #include "fs_mgr.h"
 #include "import_parser.h"
-#include "init.h"
 #include "init_parser.h"
 #include "keychords.h"
 #include "log.h"
@@ -68,6 +72,8 @@
 #include "util.h"
 #include "watchdogd.h"
 
+using android::base::boot_clock;
+using android::base::GetProperty;
 using android::base::StringPrintf;
 
 struct selabel_handle *sehandle;
@@ -82,8 +88,6 @@
 
 const char *ENV[32];
 
-static std::unique_ptr<Timer> waiting_for_exec(nullptr);
-
 static int epoll_fd = -1;
 
 static std::unique_ptr<Timer> waiting_for_prop(nullptr);
@@ -131,29 +135,12 @@
     return -1;
 }
 
-bool start_waiting_for_exec()
-{
-    if (waiting_for_exec) {
-        return false;
-    }
-    waiting_for_exec.reset(new Timer());
-    return true;
-}
-
-void stop_waiting_for_exec()
-{
-    if (waiting_for_exec) {
-        LOG(INFO) << "Wait for exec took " << *waiting_for_exec;
-        waiting_for_exec.reset();
-    }
-}
-
 bool start_waiting_for_property(const char *name, const char *value)
 {
     if (waiting_for_prop) {
         return false;
     }
-    if (property_get(name) != value) {
+    if (GetProperty(name, "") != value) {
         // Current property value is not equal to expected value
         wait_prop_name = name;
         wait_prop_value = value;
@@ -441,7 +428,7 @@
 
 static int console_init_action(const std::vector<std::string>& args)
 {
-    std::string console = property_get("ro.boot.console");
+    std::string console = GetProperty("ro.boot.console", "");
     if (!console.empty()) {
         default_console = "/dev/" + console;
     }
@@ -465,11 +452,11 @@
 }
 
 static void export_oem_lock_status() {
-    if (property_get("ro.oem_unlock_supported") != "1") {
+    if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
         return;
     }
 
-    std::string value = property_get("ro.boot.verifiedbootstate");
+    std::string value = GetProperty("ro.boot.verifiedbootstate", "");
 
     if (!value.empty()) {
         property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
@@ -490,7 +477,7 @@
         { "ro.boot.revision",   "ro.revision",   "0", },
     };
     for (size_t i = 0; i < arraysize(prop_map); i++) {
-        std::string value = property_get(prop_map[i].src_prop);
+        std::string value = GetProperty(prop_map[i].src_prop, "");
         property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
     }
 }
@@ -731,14 +718,18 @@
         return false;
     }
     std::string actual_plat_id;
-    if (!read_first_line("/system/etc/selinux/plat_sepolicy.cil.sha256", &actual_plat_id)) {
-        PLOG(INFO) << "Failed to read /system/etc/selinux/plat_sepolicy.cil.sha256";
+    if (!read_first_line("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256",
+                         &actual_plat_id)) {
+        PLOG(INFO) << "Failed to read "
+                      "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
         return false;
     }
     std::string precompiled_plat_id;
-    if (!read_first_line("/vendor/etc/selinux/precompiled_sepolicy.plat.sha256",
+    if (!read_first_line("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
                          &precompiled_plat_id)) {
-        PLOG(INFO) << "Failed to read /vendor/etc/selinux/precompiled_sepolicy.plat.sha256";
+        PLOG(INFO) << "Failed to read "
+                      "/vendor/etc/selinux/"
+                      "precompiled_sepolicy.plat_and_mapping.sha256";
         return false;
     }
     if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
@@ -810,7 +801,7 @@
         "-M", "true",
         // Target the highest policy language version supported by the kernel
         "-c", std::to_string(max_policy_version).c_str(),
-        "/vendor/etc/selinux/mapping_sepolicy.cil",
+        "/system/etc/selinux/mapping_sepolicy.cil",
         "/vendor/etc/selinux/nonplat_sepolicy.cil",
         "-o", compiled_sepolicy,
         // We don't care about file_contexts output by the compiler
@@ -892,6 +883,24 @@
     }
 }
 
+// The files and directories that were created before initial sepolicy load
+// need to have their security context restored to the proper value.
+// This must happen before /dev is populated by ueventd.
+static void selinux_restore_context() {
+    LOG(INFO) << "Running restorecon...";
+    restorecon("/dev");
+    restorecon("/dev/kmsg");
+    restorecon("/dev/socket");
+    restorecon("/dev/random");
+    restorecon("/dev/urandom");
+    restorecon("/dev/__properties__");
+    restorecon("/plat_property_contexts");
+    restorecon("/nonplat_property_contexts");
+    restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+    restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+    restorecon("/dev/device-mapper");
+}
+
 // Set the UDC controller for the ConfigFS USB Gadgets.
 // Read the UDC controller in use from "/sys/class/udc".
 // In case of multiple UDC controllers select the first one.
@@ -1230,22 +1239,7 @@
 
     // Now set up SELinux for second stage.
     selinux_initialize(false);
-
-    // These directories were necessarily created before initial policy load
-    // and therefore need their security context restored to the proper value.
-    // This must happen before /dev is populated by ueventd.
-    LOG(INFO) << "Running restorecon...";
-    restorecon("/dev");
-    restorecon("/dev/kmsg");
-    restorecon("/dev/socket");
-    restorecon("/dev/random");
-    restorecon("/dev/urandom");
-    restorecon("/dev/__properties__");
-    restorecon("/plat_property_contexts");
-    restorecon("/nonplat_property_contexts");
-    restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
-    restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
-    restorecon("/dev/device-mapper");
+    selinux_restore_context();
 
     epoll_fd = epoll_create1(EPOLL_CLOEXEC);
     if (epoll_fd == -1) {
@@ -1267,7 +1261,7 @@
     parser.AddSectionParser("service",std::make_unique<ServiceParser>());
     parser.AddSectionParser("on", std::make_unique<ActionParser>());
     parser.AddSectionParser("import", std::make_unique<ImportParser>());
-    std::string bootscript = property_get("ro.boot.init_rc");
+    std::string bootscript = GetProperty("ro.boot.init_rc", "");
     if (bootscript.empty()) {
         parser.ParseConfig("/init.rc");
         parser.set_is_system_etc_init_loaded(
@@ -1307,7 +1301,7 @@
     am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
 
     // Don't mount filesystems or start core system services in charger mode.
-    std::string bootmode = property_get("ro.bootmode");
+    std::string bootmode = GetProperty("ro.bootmode", "");
     if (bootmode == "charger") {
         am.QueueEventTrigger("charger");
     } else {
@@ -1321,10 +1315,10 @@
         // By default, sleep until something happens.
         int epoll_timeout_ms = -1;
 
-        if (!(waiting_for_exec || waiting_for_prop)) {
+        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
             am.ExecuteOneCommand();
         }
-        if (!(waiting_for_exec || waiting_for_prop)) {
+        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
             restart_processes();
 
             // If there's a process that needs restarting, wake up in time for that.
diff --git a/init/init.h b/init/init.h
index b4d25fb..fe850ef 100644
--- a/init/init.h
+++ b/init/init.h
@@ -32,10 +32,6 @@
 
 int add_environment(const char* key, const char* val);
 
-bool start_waiting_for_exec();
-
-void stop_waiting_for_exec();
-
 bool start_waiting_for_property(const char *name, const char *value);
 
 #endif  /* _INIT_INIT_H */
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 326ebf2..53e670b 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -14,18 +14,17 @@
  * limitations under the License.
  */
 
+#include "init_parser.h"
+
 #include <dirent.h>
-#include <errno.h>
 #include <fcntl.h>
 
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
 #include "action.h"
-#include "init_parser.h"
-#include "log.h"
 #include "parser.h"
 #include "service.h"
-#include "util.h"
-
-#include <android-base/stringprintf.h>
 
 Parser::Parser() {
 }
@@ -96,7 +95,7 @@
     LOG(INFO) << "Parsing file " << path << "...";
     Timer t;
     std::string data;
-    if (!read_file(path.c_str(), &data)) {
+    if (!read_file(path, &data)) {
         return false;
     }
 
diff --git a/init/init_parser.h b/init/init_parser.h
index f66ba52..6935fdf 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -18,6 +18,7 @@
 #define _INIT_INIT_PARSER_H_
 
 #include <map>
+#include <memory>
 #include <string>
 #include <vector>
 
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
index 52aaa37..d8fd2ba 100644
--- a/init/init_parser_test.cpp
+++ b/init/init_parser_test.cpp
@@ -18,9 +18,7 @@
 
 #include "init.h"
 #include "service.h"
-#include "util.h"
 
-#include <errno.h>
 #include <gtest/gtest.h>
 
 #include <string>
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 3dbb2f0..c572cee 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -14,18 +14,17 @@
  * limitations under the License.
  */
 
-#include <errno.h>
 #include <fcntl.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <linux/keychord.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
 #include "init.h"
-#include "log.h"
-#include "property_service.h"
 #include "service.h"
 
 static struct input_keychord *keychords = 0;
@@ -74,7 +73,7 @@
     }
 
     // Only handle keychords if adb is enabled.
-    std::string adb_enabled = property_get("init.svc.adbd");
+    std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
     if (adb_enabled == "running") {
         Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
         if (svc) {
diff --git a/init/log.cpp b/init/log.cpp
index 6b32526..ee6489b 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -17,9 +17,10 @@
 #include "log.h"
 
 #include <fcntl.h>
+#include <linux/audit.h>
 #include <string.h>
 
-#include <linux/audit.h>
+#include <android-base/logging.h>
 #include <netlink/netlink.h>
 #include <selinux/selinux.h>
 
diff --git a/init/log.h b/init/log.h
index 8fa6d74..29a27af 100644
--- a/init/log.h
+++ b/init/log.h
@@ -17,7 +17,7 @@
 #ifndef _INIT_LOG_H_
 #define _INIT_LOG_H_
 
-#include <android-base/logging.h>
+#include <sys/cdefs.h>
 
 void InitKernelLogging(char* argv[]);
 
diff --git a/init/parser.cpp b/init/parser.cpp
index 45862b7..5953a88 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -4,7 +4,7 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "log.h"
+#include <android-base/logging.h>
 
 void parse_error(struct parse_state *state, const char *fmt, ...)
 {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 6960279..9857223 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -14,46 +14,44 @@
  * limitations under the License.
  */
 
+#include "property_service.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <inttypes.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <dirent.h>
-#include <limits.h>
-#include <errno.h>
+#include <sys/mman.h>
 #include <sys/poll.h>
-
-#include <memory>
-#include <vector>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
 
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <sys/mman.h>
+#include <memory>
+#include <vector>
 
-#include <selinux/android.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-
-#include <fs_mgr.h>
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include "bootimg.h"
+#include <bootimg.h>
+#include <fs_mgr.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
 
-#include "property_service.h"
 #include "init.h"
 #include "util.h"
-#include "log.h"
 
 using android::base::StringPrintf;
 
@@ -114,12 +112,6 @@
     return check_mac_perms(ctl_name, sctx, cr);
 }
 
-std::string property_get(const char* name) {
-    char value[PROP_VALUE_MAX] = {0};
-    __system_property_get(name, value);
-    return value;
-}
-
 static void write_persistent_property(const char *name, const char *value)
 {
     char tempPath[PATH_MAX];
@@ -588,10 +580,7 @@
 
 static void load_override_properties() {
     if (ALLOW_LOCAL_PROP_OVERRIDE) {
-        std::string debuggable = property_get("ro.debuggable");
-        if (debuggable == "1") {
-            load_properties_from_file("/data/local.prop", NULL);
-        }
+        load_properties_from_file("/data/local.prop", NULL);
     }
 }
 
diff --git a/init/property_service.h b/init/property_service.h
index 5d59473..585cd0f 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -20,6 +20,7 @@
 #include <stddef.h>
 #include <sys/socket.h>
 #include <sys/system_properties.h>
+
 #include <string>
 
 struct property_audit_data {
@@ -32,7 +33,6 @@
 void load_persist_props(void);
 void load_system_props(void);
 void start_property_service(void);
-std::string property_get(const char* name);
 uint32_t property_set(const std::string& name, const std::string& value);
 bool is_legal_property_name(const std::string& name);
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 0b49ba1..53bdeb1 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -13,6 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include "reboot.h"
+
 #include <dirent.h>
 #include <fcntl.h>
 #include <mntent.h>
@@ -26,13 +29,14 @@
 #include <sys/wait.h>
 
 #include <memory>
-#include <string>
+#include <set>
 #include <thread>
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/macros.h>
-#include <android-base/parseint.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <bootloader_message/bootloader_message.h>
@@ -40,11 +44,8 @@
 #include <fs_mgr.h>
 #include <logwrap/logwrap.h>
 
-#include "log.h"
 #include "property_service.h"
-#include "reboot.h"
 #include "service.h"
-#include "util.h"
 
 using android::base::StringPrintf;
 
@@ -249,8 +250,9 @@
                                               flags);
                 } else {
                     umountDone = false;
-                    PLOG(WARNING) << StringPrintf("cannot umount %s, flags:0x%x",
-                                                  entry.mnt_fsname().c_str(), flags);
+                    PLOG(WARNING) << StringPrintf("cannot umount %s, mnt_dir %s, flags:0x%x",
+                                                  entry.mnt_fsname().c_str(),
+                                                  entry.mnt_dir().c_str(), flags);
                 }
             }
         }
@@ -341,33 +343,51 @@
         abort();
     }
 
-    std::string timeout = property_get("ro.build.shutdown_timeout");
     /* TODO update default waiting time based on usage data */
-    unsigned int shutdownTimeout = 10;  // default value
-    if (android::base::ParseUint(timeout, &shutdownTimeout)) {
-        LOG(INFO) << "ro.build.shutdown_timeout set:" << shutdownTimeout;
+    constexpr unsigned int shutdownTimeoutDefault = 10;
+    unsigned int shutdownTimeout = shutdownTimeoutDefault;
+    if (SHUTDOWN_ZERO_TIMEOUT) {  // eng build
+        shutdownTimeout = 0;
+    } else {
+        shutdownTimeout =
+            android::base::GetUintProperty("ro.build.shutdown_timeout", shutdownTimeoutDefault);
+    }
+    LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;
+
+    // keep debugging tools until non critical ones are all gone.
+    const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
+    // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
+    const std::set<std::string> to_starts{"watchdogd", "vold"};
+    ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
+        if (kill_after_apps.count(s->name())) {
+            s->SetShutdownCritical();
+        } else if (to_starts.count(s->name())) {
+            s->Start();
+            s->SetShutdownCritical();
+        }
+    });
+
+    Service* bootAnim = ServiceManager::GetInstance().FindServiceByName("bootanim");
+    Service* surfaceFlinger = ServiceManager::GetInstance().FindServiceByName("surfaceflinger");
+    if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
+        property_set("service.bootanim.exit", "0");
+        // Could be in the middle of animation. Stop and start so that it can pick
+        // up the right mode.
+        bootAnim->Stop();
+        // start all animation classes if stopped.
+        ServiceManager::GetInstance().ForEachServiceInClass("animation", [](Service* s) {
+            s->Start();
+            s->SetShutdownCritical();  // will not check animation class separately
+        });
+        bootAnim->Start();
+        surfaceFlinger->SetShutdownCritical();
+        bootAnim->SetShutdownCritical();
     }
 
-    static const constexpr char* shutdown_critical_services[] = {"vold", "watchdogd"};
-    for (const char* name : shutdown_critical_services) {
-        Service* s = ServiceManager::GetInstance().FindServiceByName(name);
-        if (s == nullptr) {
-            LOG(WARNING) << "Shutdown critical service not found:" << name;
-            continue;
-        }
-        s->Start();  // make sure that it is running.
-        s->SetShutdownCritical();
-    }
     // optional shutdown step
     // 1. terminate all services except shutdown critical ones. wait for delay to finish
     if (shutdownTimeout > 0) {
         LOG(INFO) << "terminating init services";
-        // tombstoned can write to data when other services are killed. so finish it first.
-        static const constexpr char* first_to_kill[] = {"tombstoned"};
-        for (const char* name : first_to_kill) {
-            Service* s = ServiceManager::GetInstance().FindServiceByName(name);
-            if (s != nullptr) s->Stop();
-        }
 
         // Ask all services to terminate except shutdown critical ones.
         ServiceManager::GetInstance().ForEachService([](Service* s) {
@@ -406,8 +426,8 @@
 
     // minimum safety steps before restarting
     // 2. kill all services except ones that are necessary for the shutdown sequence.
-    ServiceManager::GetInstance().ForEachService([](Service* s) {
-        if (!s->IsShutdownCritical()) s->Stop();
+    ServiceManager::GetInstance().ForEachService([&kill_after_apps](Service* s) {
+        if (!s->IsShutdownCritical() || kill_after_apps.count(s->name())) s->Stop();
     });
     ServiceManager::GetInstance().ReapAnyOutstandingChildren();
 
diff --git a/init/reboot.h b/init/reboot.h
index 3956249..6432fa5 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -17,6 +17,8 @@
 #ifndef _INIT_REBOOT_H
 #define _INIT_REBOOT_H
 
+#include <string>
+
 /* Reboot / shutdown the system.
  * cmd ANDROID_RB_* as defined in android_reboot.h
  * reason Reason string like "reboot", "userrequested"
diff --git a/init/service.cpp b/init/service.cpp
index c6ef838..8cb8dc1 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -25,28 +25,25 @@
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/time.h>
-#include <sys/types.h>
 #include <sys/wait.h>
 #include <termios.h>
 #include <unistd.h>
 
-#include <selinux/selinux.h>
-
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/parseint.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <processgroup/processgroup.h>
+#include <selinux/selinux.h>
 #include <system/thread_defs.h>
 
-#include <processgroup/processgroup.h>
-
-#include "action.h"
 #include "init.h"
-#include "init_parser.h"
-#include "log.h"
 #include "property_service.h"
 #include "util.h"
 
+using android::base::boot_clock;
 using android::base::ParseInt;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
@@ -191,8 +188,8 @@
 }
 
 void Service::NotifyStateChange(const std::string& new_state) const {
-    if ((flags_ & SVC_EXEC) != 0) {
-        // 'exec' commands don't have properties tracking their state.
+    if ((flags_ & SVC_TEMPORARY) != 0) {
+        // Services created by 'exec' are temporary and don't have properties tracking their state.
         return;
     }
 
@@ -210,7 +207,13 @@
     LOG(INFO) << "Sending signal " << signal
               << " to service '" << name_
               << "' (pid " << pid_ << ") process group...";
-    if (killProcessGroup(uid_, pid_, signal) == -1) {
+    int r;
+    if (signal == SIGTERM) {
+        r = killProcessGroupOnce(uid_, pid_, signal);
+    } else {
+        r = killProcessGroup(uid_, pid_, signal);
+    }
+    if (r == -1) {
         PLOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
     }
     if (kill(-pid_, signal) == -1) {
@@ -259,7 +262,7 @@
     }
 }
 
-bool Service::Reap() {
+void Service::Reap() {
     if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
         KillProcessGroup(SIGKILL);
     }
@@ -270,7 +273,10 @@
 
     if (flags_ & SVC_EXEC) {
         LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished...";
-        return true;
+    }
+
+    if (flags_ & SVC_TEMPORARY) {
+        return;
     }
 
     pid_ = 0;
@@ -285,7 +291,7 @@
     // Disabled and reset processes do not get restarted automatically.
     if (flags_ & (SVC_DISABLED | SVC_RESET))  {
         NotifyStateChange("stopped");
-        return false;
+        return;
     }
 
     // If we crash > 4 times in 4 minutes, reboot into recovery.
@@ -309,7 +315,7 @@
     onrestart_.ExecuteAllCommands();
 
     NotifyStateChange("restarting");
-    return false;
+    return;
 }
 
 void Service::DumpState() const {
@@ -577,6 +583,18 @@
     return (this->*parser)(args, err);
 }
 
+bool Service::ExecStart(std::unique_ptr<Timer>* exec_waiter) {
+    flags_ |= SVC_EXEC | SVC_ONESHOT;
+
+    exec_waiter->reset(new Timer);
+
+    if (!Start()) {
+        exec_waiter->reset();
+        return false;
+    }
+    return true;
+}
+
 bool Service::Start() {
     // Starting a service removes it from the disabled or reset state and
     // immediately takes it out of the restarting state if it was in there.
@@ -657,7 +675,7 @@
         if (iter == writepid_files_.end()) {
             // There were no "writepid" instructions for cpusets, check if the system default
             // cpuset is specified to be used for the process.
-            std::string default_cpuset = property_get("ro.cpuset.default");
+            std::string default_cpuset = android::base::GetProperty("ro.cpuset.default", "");
             if (!default_cpuset.empty()) {
                 // Make sure the cpuset name starts and ends with '/'.
                 // A single '/' means the 'root' cpuset.
@@ -863,6 +881,35 @@
     services_.emplace_back(std::move(service));
 }
 
+bool ServiceManager::Exec(const std::vector<std::string>& args) {
+    Service* svc = MakeExecOneshotService(args);
+    if (!svc) {
+        LOG(ERROR) << "Could not create exec service";
+        return false;
+    }
+    if (!svc->ExecStart(&exec_waiter_)) {
+        LOG(ERROR) << "Could not start exec service";
+        ServiceManager::GetInstance().RemoveService(*svc);
+        return false;
+    }
+    return true;
+}
+
+bool ServiceManager::ExecStart(const std::string& name) {
+    Service* svc = FindServiceByName(name);
+    if (!svc) {
+        LOG(ERROR) << "ExecStart(" << name << "): Service not found";
+        return false;
+    }
+    if (!svc->ExecStart(&exec_waiter_)) {
+        LOG(ERROR) << "ExecStart(" << name << "): Could not start Service";
+        return false;
+    }
+    return true;
+}
+
+bool ServiceManager::IsWaitingForExec() const { return exec_waiter_ != nullptr; }
+
 Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
     // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
     // SECLABEL can be a - to denote default
@@ -886,7 +933,7 @@
 
     exec_count_++;
     std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
-    unsigned flags = SVC_EXEC | SVC_ONESHOT;
+    unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
     CapSet no_capabilities;
     unsigned namespace_flags = 0;
 
@@ -1026,8 +1073,13 @@
         return true;
     }
 
-    if (svc->Reap()) {
-        stop_waiting_for_exec();
+    svc->Reap();
+
+    if (svc->flags() & SVC_EXEC) {
+        LOG(INFO) << "Wait for exec took " << *exec_waiter_;
+        exec_waiter_.reset();
+    }
+    if (svc->flags() & SVC_TEMPORARY) {
         RemoveService(*svc);
     }
 
diff --git a/init/service.h b/init/service.h
index 9a9046b..5e89b9f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -19,13 +19,14 @@
 
 #include <sys/types.h>
 
-#include <cutils/iosched_policy.h>
-
 #include <memory>
 #include <set>
 #include <string>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
+#include <cutils/iosched_policy.h>
+
 #include "action.h"
 #include "capabilities.h"
 #include "descriptors.h"
@@ -44,10 +45,13 @@
 #define SVC_RC_DISABLED 0x080     // Remember if the disabled flag was set in the rc script.
 #define SVC_RESTART 0x100         // Use to safely restart (stop, wait, start) a service.
 #define SVC_DISABLED_START 0x200  // A start was requested but it was disabled at the time.
-#define SVC_EXEC 0x400            // This synthetic service corresponds to an 'exec'.
+#define SVC_EXEC 0x400  // This service was started by either 'exec' or 'exec_start' and stops
+                        // init from processing more commands until it completes
 
 #define SVC_SHUTDOWN_CRITICAL 0x800  // This service is critical for shutdown and
                                      // should not be killed during shutdown
+#define SVC_TEMPORARY 0x1000  // This service was started by 'exec' and should be removed from the
+                              // service list once it is reaped.
 
 #define NR_SVC_SUPP_GIDS 12    // twelve supplementary groups
 
@@ -72,6 +76,7 @@
 
     bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
     bool ParseLine(const std::vector<std::string>& args, std::string* err);
+    bool ExecStart(std::unique_ptr<Timer>* exec_waiter);
     bool Start();
     bool StartIfNotDisabled();
     bool Enable();
@@ -80,7 +85,7 @@
     void Terminate();
     void Restart();
     void RestartIfNeeded(time_t* process_needs_restart_at);
-    bool Reap();
+    void Reap();
     void DumpState() const;
     void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
     bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
@@ -140,8 +145,8 @@
 
     unsigned flags_;
     pid_t pid_;
-    boot_clock::time_point time_started_; // time of last start
-    boot_clock::time_point time_crashed_; // first crash within inspection window
+    android::base::boot_clock::time_point time_started_;  // time of last start
+    android::base::boot_clock::time_point time_crashed_;  // first crash within inspection window
     int crash_count_;                     // number of times crashed within window
 
     uid_t uid_;
@@ -178,6 +183,9 @@
 
     void AddService(std::unique_ptr<Service> service);
     Service* MakeExecOneshotService(const std::vector<std::string>& args);
+    bool Exec(const std::vector<std::string>& args);
+    bool ExecStart(const std::string& name);
+    bool IsWaitingForExec() const;
     Service* FindServiceByName(const std::string& name) const;
     Service* FindServiceByPid(pid_t pid) const;
     Service* FindServiceByKeychord(int keychord_id) const;
@@ -198,6 +206,8 @@
     bool ReapOneProcess();
 
     static int exec_count_; // Every service needs a unique name.
+    std::unique_ptr<Timer> exec_waiter_;
+
     std::vector<std::unique_ptr<Service>> services_;
 };
 
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 5e3acac..4d56d84 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -14,22 +14,17 @@
  * limitations under the License.
  */
 
-#include <errno.h>
-#include <fcntl.h>
 #include <signal.h>
-#include <stdio.h>
+#include <string.h>
 #include <sys/socket.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
-#include "action.h"
 #include "init.h"
-#include "log.h"
 #include "service.h"
-#include "util.h"
 
 static int signal_write_fd = -1;
 static int signal_read_fd = -1;
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 915afbd..ea767a8 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "ueventd.h"
+
 #include <ctype.h>
 #include <fcntl.h>
 #include <grp.h>
@@ -24,17 +26,15 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <sys/types.h>
-
+#include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <selinux/selinux.h>
 
-#include "ueventd.h"
-#include "log.h"
-#include "util.h"
 #include "devices.h"
+#include "log.h"
 #include "ueventd_parser.h"
-#include "property_service.h"
+#include "util.h"
 
 int ueventd_main(int argc, char **argv)
 {
@@ -71,7 +71,7 @@
      * TODO: cleanup platform ueventd.rc to remove vendor specific
      * device node entries (b/34968103)
      */
-    std::string hardware = property_get("ro.hardware");
+    std::string hardware = android::base::GetProperty("ro.hardware", "");
     ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
 
     device_init();
@@ -94,7 +94,7 @@
     return 0;
 }
 
-void set_device_permission(int nargs, char **args)
+void set_device_permission(const char* fn, int line, int nargs, char **args)
 {
     char *name;
     char *attr = 0;
@@ -121,7 +121,7 @@
     }
 
     if (nargs != 4) {
-        LOG(ERROR) << "invalid line ueventd.rc line for '" << args[0] << "'";
+        LOG(ERROR) << "invalid line (" << fn << ":" << line << ") line for '" << args[0] << "'";
         return;
     }
 
@@ -136,20 +136,20 @@
 
     perm = strtol(args[1], &endptr, 8);
     if (!endptr || *endptr != '\0') {
-        LOG(ERROR) << "invalid mode '" << args[1] << "'";
+        LOG(ERROR) << "invalid mode (" << fn << ":" << line << ") '" << args[1] << "'";
         return;
     }
 
     struct passwd* pwd = getpwnam(args[2]);
     if (!pwd) {
-        LOG(ERROR) << "invalid uid '" << args[2] << "'";
+        LOG(ERROR) << "invalid uid (" << fn << ":" << line << ") '" << args[2] << "'";
         return;
     }
     uid = pwd->pw_uid;
 
     struct group* grp = getgrnam(args[3]);
     if (!grp) {
-        LOG(ERROR) << "invalid gid '" << args[3] << "'";
+        LOG(ERROR) << "invalid gid (" << fn << ":" << line << ") '" << args[3] << "'";
         return;
     }
     gid = grp->gr_gid;
diff --git a/init/ueventd.h b/init/ueventd.h
index d12d7fe..d44d1ca 100644
--- a/init/ueventd.h
+++ b/init/ueventd.h
@@ -17,9 +17,10 @@
 #ifndef _INIT_UEVENTD_H_
 #define _INIT_UEVENTD_H_
 
-#include <cutils/list.h>
 #include <sys/types.h>
 
+#include <cutils/list.h>
+
 enum devname_src_t {
     DEVNAME_UNKNOWN = 0,
     DEVNAME_UEVENT_DEVNAME,
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index baff58c..510d7bb 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
+#include "ueventd_parser.h"
+
 #include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
 #include <stdarg.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
-#include "ueventd.h"
-#include "ueventd_parser.h"
+#include <android-base/logging.h>
+
 #include "parser.h"
-#include "log.h"
 #include "util.h"
 
 static list_declare(subsystem_list);
@@ -236,6 +236,6 @@
     return 0;
 }
 
-static void parse_line_device(parse_state*, int nargs, char** args) {
-    set_device_permission(nargs, args);
+static void parse_line_device(parse_state* state, int nargs, char** args) {
+    set_device_permission(state->filename, state->line, nargs, args);
 }
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 907cc49..4d69897 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -22,7 +22,7 @@
 #define UEVENTD_PARSER_MAXARGS 5
 
 int ueventd_parse_config_file(const char *fn);
-void set_device_permission(int nargs, char **args);
+void set_device_permission(const char* fn, int line, int nargs, char **args);
 struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name);
 
 #endif
diff --git a/init/util.cpp b/init/util.cpp
index 0ba9800..bbc59cb 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -14,43 +14,38 @@
  * limitations under the License.
  */
 
+#include "util.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <ftw.h>
 #include <pwd.h>
 #include <stdarg.h>
-#include <stdlib.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 #include <time.h>
 #include <unistd.h>
 
-#include <selinux/android.h>
-#include <selinux/label.h>
-
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-
 #include <thread>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-
 #include <cutils/android_reboot.h>
-/* for ANDROID_SOCKET_* */
 #include <cutils/sockets.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
 
 #include "init.h"
-#include "log.h"
-#include "property_service.h"
 #include "reboot.h"
-#include "util.h"
+
+using android::base::boot_clock;
 
 static unsigned int do_decode_uid(const char *s)
 {
@@ -161,10 +156,11 @@
     return -1;
 }
 
-bool read_file(const char* path, std::string* content) {
+bool read_file(const std::string& path, std::string* content) {
     content->clear();
 
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+    android::base::unique_fd fd(
+        TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
     if (fd == -1) {
         return false;
     }
@@ -184,9 +180,9 @@
     return android::base::ReadFdToString(fd, content);
 }
 
-bool write_file(const char* path, const char* content) {
+bool write_file(const std::string& path, const std::string& content) {
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(
-        open(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
+        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;
@@ -198,13 +194,6 @@
     return success;
 }
 
-boot_clock::time_point boot_clock::now() {
-  timespec ts;
-  clock_gettime(CLOCK_BOOTTIME, &ts);
-  return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
-                                std::chrono::nanoseconds(ts.tv_nsec));
-}
-
 int mkdir_recursive(const char *pathname, mode_t mode)
 {
     char buf[128];
@@ -239,27 +228,6 @@
     return 0;
 }
 
-/*
- * replaces any unacceptable characters with '_', the
- * length of the resulting string is equal to the input string
- */
-void sanitize(char *s)
-{
-    const char* accept =
-            "abcdefghijklmnopqrstuvwxyz"
-            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-            "0123456789"
-            "_-.";
-
-    if (!s)
-        return;
-
-    while (*s) {
-        s += strspn(s, accept);
-        if (*s) *s++ = '_';
-    }
-}
-
 int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
     boot_clock::time_point timeout_time = boot_clock::now() + timeout;
     while (boot_clock::now() < timeout_time) {
@@ -395,7 +363,7 @@
             return false;
         }
 
-        std::string prop_val = property_get(prop_name.c_str());
+        std::string prop_val = android::base::GetProperty(prop_name, "");
         if (prop_val.empty()) {
             if (def_val.empty()) {
                 LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
diff --git a/init/util.h b/init/util.h
index 81c64d7..38a7bdb 100644
--- a/init/util.h
+++ b/init/util.h
@@ -25,42 +25,35 @@
 #include <ostream>
 #include <string>
 
+#include <android-base/chrono_utils.h>
+
 #define COLDBOOT_DONE "/dev/.coldboot_done"
 
+using android::base::boot_clock;
 using namespace std::chrono_literals;
 
 int create_socket(const char *name, int type, mode_t perm,
                   uid_t uid, gid_t gid, const char *socketcon);
 
-bool read_file(const char* path, std::string* content);
-bool write_file(const char* path, const char* content);
-
-// A std::chrono clock based on CLOCK_BOOTTIME.
-class boot_clock {
- public:
-  typedef std::chrono::nanoseconds duration;
-  typedef std::chrono::time_point<boot_clock, duration> time_point;
-  static constexpr bool is_steady = true;
-
-  static time_point now();
-};
+bool read_file(const std::string& path, std::string* content);
+bool write_file(const std::string& path, const std::string& content);
 
 class Timer {
- public:
-  Timer() : start_(boot_clock::now()) {
-  }
+  public:
+    Timer() : start_(boot_clock::now()) {}
 
-  double duration_s() const {
-    typedef std::chrono::duration<double> double_duration;
-    return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
-  }
+    double duration_s() const {
+        typedef std::chrono::duration<double> double_duration;
+        return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
+    }
 
-  int64_t duration_ms() const {
-    return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_).count();
-  }
+    int64_t duration_ms() const {
+        return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_)
+            .count();
+    }
 
- private:
-  boot_clock::time_point start_;
+  private:
+    android::base::boot_clock::time_point start_;
 };
 
 std::ostream& operator<<(std::ostream& os, const Timer& t);
@@ -68,7 +61,6 @@
 unsigned int decode_uid(const char *s);
 
 int mkdir_recursive(const char *pathname, mode_t mode);
-void sanitize(char *p);
 int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
 void import_kernel_cmdline(bool in_qemu,
                            const std::function<void(const std::string&, const std::string&, bool)>&);
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 4e82e76..0c0350a 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -18,13 +18,11 @@
 
 #include <errno.h>
 #include <fcntl.h>
-
 #include <sys/stat.h>
 
-#include <gtest/gtest.h>
-
 #include <android-base/stringprintf.h>
 #include <android-base/test_utils.h>
+#include <gtest/gtest.h>
 
 TEST(util, read_file_ENOENT) {
   std::string s("hello");
@@ -38,7 +36,7 @@
     std::string s("hello");
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
+    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.
@@ -54,7 +52,7 @@
     EXPECT_EQ("", s);  // s was cleared.
 }
 
-TEST(util, read_file_symbol_link) {
+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
@@ -72,27 +70,36 @@
   EXPECT_STREQ("Linux", s.c_str());
 }
 
+TEST(util, write_file_binary) {
+    std::string contents("abcd");
+    contents.push_back('\0');
+    contents.push_back('\0');
+    contents.append("dcba");
+    ASSERT_EQ(10u, contents.size());
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    EXPECT_TRUE(write_file(tf.path, contents)) << strerror(errno);
+
+    std::string read_back_contents;
+    EXPECT_TRUE(read_file(tf.path, &read_back_contents)) << strerror(errno);
+    EXPECT_EQ(contents, read_back_contents);
+    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.c_str(), s.c_str()));
-    EXPECT_TRUE(read_file(path.c_str(), &s2));
+    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_NE(0u, sb.st_mode & S_IRUSR);
-    EXPECT_NE(0u, sb.st_mode & S_IWUSR);
-    EXPECT_EQ(0u, sb.st_mode & S_IXUSR);
-    EXPECT_EQ(0u, sb.st_mode & S_IRGRP);
-    EXPECT_EQ(0u, sb.st_mode & S_IWGRP);
-    EXPECT_EQ(0u, sb.st_mode & S_IXGRP);
-    EXPECT_EQ(0u, sb.st_mode & S_IROTH);
-    EXPECT_EQ(0u, sb.st_mode & S_IWOTH);
-    EXPECT_EQ(0u, sb.st_mode & S_IXOTH);
+    EXPECT_EQ((const unsigned int)(S_IRUSR | S_IWUSR), sb.st_mode & 0777);
     EXPECT_EQ(0, unlink(path.c_str()));
 }
 
@@ -103,9 +110,9 @@
     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, "2hello2"));
+    EXPECT_TRUE(write_file(tf.path, "2ll2"));
     EXPECT_TRUE(read_file(tf.path, &s2));
-    EXPECT_STREQ("2hello2", s2.c_str());
+    EXPECT_STREQ("2ll2", s2.c_str());
 }
 
 TEST(util, decode_uid) {
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index b196147..21c1e5b 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -16,11 +16,12 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/watchdog.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <linux/watchdog.h>
+#include <android-base/logging.h>
 
 #include "log.h"
 #include "util.h"
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index f729faf..e659f79 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -24,6 +24,7 @@
 
 cc_test {
     name: "libappfuse_test",
+    test_suites: ["device-tests"],
     defaults: ["libappfuse_defaults"],
     shared_libs: ["libappfuse"],
     srcs: [
diff --git a/libappfuse/AndroidTest.xml b/libappfuse/AndroidTest.xml
new file mode 100644
index 0000000..a9cd754
--- /dev/null
+++ b/libappfuse/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for libappfuse_test">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="libappfuse_test->/data/local/tmp/libappfuse_test" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="libappfuse_test" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 6a57a41..5fc2386 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); }
 
@@ -236,21 +227,24 @@
     /* clang-format on */
 };
 
+static size_t strip(const char* path, size_t len, const char suffix[]) {
+    if (len < strlen(suffix)) return len;
+    if (strncmp(path + len - strlen(suffix), suffix, strlen(suffix))) return len;
+    return len - strlen(suffix);
+}
+
 static int fs_config_open(int dir, int which, const char* target_out_path) {
     int fd = -1;
 
     if (target_out_path && *target_out_path) {
         /* target_out_path is the path to the directory holding content of
-         * system partition but as we cannot guaranty it ends with '/system'
-         * we need this below skip_len logic */
+         * system partition but as we cannot guarantee it ends with '/system'
+         * or with or without a trailing slash, need to strip them carefully. */
         char* name = NULL;
-        int target_out_path_len = strlen(target_out_path);
-        int skip_len = strlen("/system");
-
-        if (target_out_path[target_out_path_len] == '/') {
-            skip_len++;
-        }
-        if (asprintf(&name, "%s%s", target_out_path, conf[which][dir] + skip_len) != -1) {
+        size_t len = strlen(target_out_path);
+        len = strip(target_out_path, len, "/");
+        len = strip(target_out_path, len, "/system");
+        if (asprintf(&name, "%.*s%s", (int)len, target_out_path, conf[which][dir]) != -1) {
             fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
             free(name);
         }
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/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index 718d76b..a0b1d7b 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -62,6 +62,7 @@
 
 cc_test {
     name: "libcutils_test",
+    test_suites: ["device-tests"],
     defaults: ["libcutils_test_default"],
     host_supported: true,
     shared_libs: test_libraries,
@@ -69,6 +70,7 @@
 
 cc_test {
     name: "libcutils_test_static",
+    test_suites: ["device-tests"],
     defaults: ["libcutils_test_default"],
     static_libs: ["libc"] + test_libraries,
     stl: "libc++_static",
diff --git a/libcutils/tests/AndroidTest.xml b/libcutils/tests/AndroidTest.xml
new file mode 100644
index 0000000..dd7aca2
--- /dev/null
+++ b/libcutils/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for libcutils_test">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="libcutils_test->/data/local/tmp/libcutils_test" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="libcutils_test" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/liblog/Android.bp b/liblog/Android.bp
index bb8c3af..bc262db 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -107,14 +107,16 @@
     license: "NOTICE",
 }
 
-cc_library_headers {
-    name: "liblog_vndk_headers",
-    export_include_dirs: ["include_vndk"],
-}
-
 ndk_library {
     name: "liblog.ndk",
     symbol_file: "liblog.map.txt",
     first_version: "9",
     unversioned_until: "current",
 }
+
+llndk_library {
+    name: "liblog.llndk",
+    symbol_file: "liblog.map.txt",
+    unversioned: true,
+    export_include_dirs: ["include_vndk"],
+}
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index bdad2c2..0b977c2 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -445,7 +445,7 @@
           mmap(NULL, end[which], which ? PROT_READ : PROT_READ | PROT_WRITE,
                which ? MAP_SHARED : MAP_PRIVATE, fd[which], 0);
       save_errno = errno;
-      close(fd[which]);
+      close(fd[which]); /* fd DONE */
       fd[which] = -1;
       if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
           (newTagMap->mapAddr[which] != NULL)) {
@@ -465,6 +465,7 @@
       delete newTagMap;
       return NULL;
     }
+    /* See 'fd DONE' comments above and below, no need to clean up here */
   }
 
   return newTagMap;
@@ -473,7 +474,7 @@
   save_errno = EINVAL;
   delete newTagMap;
 fail_close:
-  for (which = 0; which < NUM_MAPS; ++which) close(fd[which]);
+  for (which = 0; which < NUM_MAPS; ++which) close(fd[which]); /* fd DONE */
 fail_errno:
   errno = save_errno;
   return NULL;
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 599dc90..c00f2a0 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -1,6 +1,10 @@
 LIBLOG {
   global:
+    android_name_to_log_id; # vndk
+    android_log_id_to_name; # vndk
     __android_log_assert;
+    __android_log_buf_print; # vndk
+    __android_log_buf_write; # vndk
     __android_log_print;
     __android_log_vprint;
     __android_log_write;
@@ -8,8 +12,27 @@
     *;
 };
 
+LIBLOG_L {
+  global:
+    android_logger_clear; # vndk
+    android_logger_get_id; # vndk
+    android_logger_get_log_readable_size; # vndk
+    android_logger_get_log_version; # vndk
+    android_logger_get_log_size; # vndk
+    android_logger_list_alloc; # vndk
+    android_logger_list_alloc_time; # vndk
+    android_logger_list_free; # vndk
+    android_logger_list_open; # vndk
+    android_logger_list_read; # vndk
+    android_logger_open; # vndk
+    android_logger_set_log_size; # vndk
+};
+
 LIBLOG_M {
   global:
+    android_logger_get_prune_list; # vndk
+    android_logger_set_prune_list; # vndk
+    android_logger_get_statistics; # vndk
     __android_log_is_loggable;
 };
 
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index d322c0f..84feb20 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -410,16 +410,46 @@
   if (!tag) tag = "";
 
   /* XXX: This needs to go! */
-  if ((bufID != LOG_ID_RADIO) &&
-      (!strcmp(tag, "HTC_RIL") ||
-       !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
-       !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
-       !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") ||
-       !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || !strcmp(tag, "SMS"))) {
-    bufID = LOG_ID_RADIO;
-    /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
-    snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
-    tag = tmp_tag;
+  if (bufID != LOG_ID_RADIO) {
+    switch (tag[0]) {
+      case 'H':
+        if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
+        goto inform;
+      case 'R':
+        /* Any log tag with "RIL" as the prefix */
+        if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
+        goto inform;
+      case 'Q':
+        /* Any log tag with "QC_RIL" as the prefix */
+        if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
+        goto inform;
+      case 'I':
+        /* Any log tag with "IMS" as the prefix */
+        if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
+        goto inform;
+      case 'A':
+        if (strcmp(tag + 1, "AT" + 1)) break;
+        goto inform;
+      case 'G':
+        if (strcmp(tag + 1, "GSM" + 1)) break;
+        goto inform;
+      case 'S':
+        if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
+        goto inform;
+      case 'C':
+        if (strcmp(tag + 1, "CDMA" + 1)) break;
+        goto inform;
+      case 'P':
+        if (strcmp(tag + 1, "PHONE" + 1)) break;
+      /* FALLTHRU */
+      inform:
+        bufID = LOG_ID_RADIO;
+        snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+        tag = tmp_tag;
+      /* FALLTHRU */
+      default:
+        break;
+    }
   }
 
 #if __BIONIC__
diff --git a/liblog/properties.c b/liblog/properties.c
index 0b0ef52..adf1900 100644
--- a/liblog/properties.c
+++ b/liblog/properties.c
@@ -95,7 +95,7 @@
   /* calculate the size of our key temporary buffer */
   const size_t taglen = tag ? len : 0;
   /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
-  char key[sizeof(log_namespace) + taglen]; /* may be > PROP_NAME_MAX */
+  char key[sizeof(log_namespace) + taglen];
   char* kp;
   size_t i;
   char c = 0;
@@ -108,7 +108,8 @@
    * Where the missing tag matches all tags and becomes the
    * system global default. We do not support ro.log.tag* .
    */
-  static char last_tag[PROP_NAME_MAX];
+  static char* last_tag;
+  static size_t last_tag_len;
   static uint32_t global_serial;
   /* some compilers erroneously see uninitialized use. !not_locked */
   uint32_t current_global_serial = 0;
@@ -147,25 +148,29 @@
   if (taglen) {
     int local_change_detected = change_detected;
     if (!not_locked) {
-      if (!last_tag[0] || (last_tag[0] != tag[0]) ||
-          strncmp(
-              last_tag + 1, tag + 1,
-              (len < sizeof(last_tag)) ? (len - 1) : (sizeof(last_tag) - 1)) ||
-          ((len < sizeof(last_tag)) && last_tag[len])) {
+      if (!last_tag || !last_tag[0] || (last_tag[0] != tag[0]) ||
+          strncmp(last_tag + 1, tag + 1, last_tag_len - 1)) {
         /* invalidate log.tag.<tag> cache */
         for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
           tag_cache[i].cache.pinfo = NULL;
           tag_cache[i].c = '\0';
         }
-        last_tag[0] = '\0';
+        if (last_tag) last_tag[0] = '\0';
         local_change_detected = 1;
       }
-      if (!last_tag[0]) {
-        if (len < sizeof(last_tag)) {
+      if (!last_tag || !last_tag[0]) {
+        if (!last_tag) {
+          last_tag = calloc(1, len + 1);
+          last_tag_len = 0;
+          if (last_tag) last_tag_len = len + 1;
+        } else if (len >= last_tag_len) {
+          last_tag = realloc(last_tag, len + 1);
+          last_tag_len = 0;
+          if (last_tag) last_tag_len = len + 1;
+        }
+        if (last_tag) {
           strncpy(last_tag, tag, len);
           last_tag[len] = '\0';
-        } else {
-          strncpy(last_tag, tag, sizeof(last_tag));
         }
       }
     }
@@ -435,7 +440,7 @@
                                                            int flag) {
   struct cache_property property = { { NULL, -1 }, { 0 } };
   if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
-    char newkey[PROP_NAME_MAX];
+    char newkey[strlen("persist.") + strlen(key) + 1];
     snprintf(newkey, sizeof(newkey), "ro.%s", key);
     refresh_cache_property(&property, newkey);
     property.cache.pinfo = NULL;
@@ -600,8 +605,8 @@
     evaluate_property_get_size
     /* clang-format on */
   };
-  char key_persist[PROP_NAME_MAX];
-  char key_ro[PROP_NAME_MAX];
+  char key_persist[strlen(global_tunable) + strlen(".security") + 1];
+  char key_ro[strlen(global_default) + strlen(".security") + 1];
   struct cache2_property_size local = {
     /* clang-format off */
     PTHREAD_MUTEX_INITIALIZER, 0,
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 3f79552..c4bf959 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -527,11 +527,13 @@
 /*
  *	Measure the time it takes to submit the android event logging call
  * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
- * under light load. Expect this to be a dozen or so syscall periods (40us)
+ * under light load. Expect this to be a long path to logger to convert the
+ * unknown tag (0) into a tagname (less than 200us).
  */
 static void BM_log_event_overhead(int iters) {
   for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
     StartBenchmarkTiming();
+    // log tag number 0 is not known, nor shall it ever be known
     __android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
     StopBenchmarkTiming();
     logd_yield();
@@ -539,6 +541,28 @@
 }
 BENCHMARK(BM_log_event_overhead);
 
+/*
+ *	Measure the time it takes to submit the android event logging call
+ * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * under light load with a known logtag.  Expect this to be a dozen or so
+ * syscall periods (less than 40us)
+ */
+static void BM_log_event_overhead_42(int iters) {
+  for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
+    StartBenchmarkTiming();
+    // In system/core/logcat/event.logtags:
+    // # These are used for testing, do not modify without updating
+    // # tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+    // # system/core/liblog/tests/liblog_benchmark.cpp
+    // # system/core/liblog/tests/liblog_test.cpp
+    // 42    answer (to life the universe etc|3)
+    __android_log_btwrite(42, EVENT_TYPE_LONG, &i, sizeof(i));
+    StopBenchmarkTiming();
+    logd_yield();
+  }
+}
+BENCHMARK(BM_log_event_overhead_42);
+
 static void BM_log_event_overhead_null(int iters) {
   set_log_null();
   BM_log_event_overhead(iters);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 0538c4c..70b8a28 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1984,6 +1984,8 @@
 
   EXPECT_EQ(0, setuid(AID_SYSTEM));  // only one that can read security buffer
 
+  uid = getuid();
+  gid = getgid();
   pid_t pid = getpid();
 
   ASSERT_TRUE(NULL !=
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 11bd8cc..47f6ff3 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -24,6 +24,8 @@
 
 int killProcessGroup(uid_t uid, int initialPid, int signal);
 
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
+
 int createProcessGroup(uid_t uid, int initialPid);
 
 void removeAllProcessGroups(void);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index eb66727..1572cb3 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -252,8 +252,7 @@
     }
 }
 
-static int killProcessGroupOnce(uid_t uid, int initialPid, int signal)
-{
+static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
     int processes = 0;
     struct ctx ctx;
     pid_t pid;
@@ -282,13 +281,11 @@
     return processes;
 }
 
-int killProcessGroup(uid_t uid, int initialPid, int signal)
-{
+static int killProcessGroup(uid_t uid, int initialPid, int signal, int retry) {
     std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
 
-    int retry = 40;
     int processes;
-    while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
+    while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
         LOG(VERBOSE) << "killed " << processes << " processes for processgroup " << initialPid;
         if (retry > 0) {
             std::this_thread::sleep_for(5ms);
@@ -313,6 +310,14 @@
     }
 }
 
+int killProcessGroup(uid_t uid, int initialPid, int signal) {
+    return killProcessGroup(uid, initialPid, signal, 40 /*maxRetry*/);
+}
+
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
+    return killProcessGroup(uid, initialPid, signal, 0 /*maxRetry*/);
+}
+
 static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
 {
     if (mkdir(path, mode) == -1 && errno != EEXIST) {
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/libsystem/Android.bp b/libsystem/Android.bp
new file mode 100644
index 0000000..4d076d5
--- /dev/null
+++ b/libsystem/Android.bp
@@ -0,0 +1,4 @@
+cc_library_headers {
+    name: "libsystem_headers",
+    export_include_dirs: ["include"],
+}
diff --git a/include/system/camera.h b/libsystem/include/system/camera.h
similarity index 100%
rename from include/system/camera.h
rename to libsystem/include/system/camera.h
diff --git a/include/system/graphics.h b/libsystem/include/system/graphics.h
similarity index 100%
rename from include/system/graphics.h
rename to libsystem/include/system/graphics.h
diff --git a/libsystem/include/system/qemu_pipe.h b/libsystem/include/system/qemu_pipe.h
new file mode 100644
index 0000000..af25079
--- /dev/null
+++ b/libsystem/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/include/system/radio.h b/libsystem/include/system/radio.h
similarity index 100%
rename from include/system/radio.h
rename to libsystem/include/system/radio.h
diff --git a/include/system/thread_defs.h b/libsystem/include/system/thread_defs.h
similarity index 100%
rename from include/system/thread_defs.h
rename to libsystem/include/system/thread_defs.h
diff --git a/include/system/window.h b/libsystem/include/system/window.h
similarity index 100%
rename from include/system/window.h
rename to libsystem/include/system/window.h
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ece623b..e35593f 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -47,12 +47,16 @@
 
     srcs: [
         "ArmExidx.cpp",
+        "DwarfMemory.cpp",
+        "DwarfOp.cpp",
         "Elf.cpp",
         "ElfInterface.cpp",
         "ElfInterfaceArm.cpp",
         "Log.cpp",
         "Regs.cpp",
+        "Maps.cpp",
         "Memory.cpp",
+        "Symbols.cpp",
     ],
 
     shared_libs: [
@@ -87,16 +91,21 @@
     srcs: [
         "tests/ArmExidxDecodeTest.cpp",
         "tests/ArmExidxExtractTest.cpp",
+        "tests/DwarfMemoryTest.cpp",
+        "tests/DwarfOpLogTest.cpp",
+        "tests/DwarfOpTest.cpp",
         "tests/ElfInterfaceArmTest.cpp",
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
         "tests/LogFake.cpp",
+        "tests/MapsTest.cpp",
         "tests/MemoryFake.cpp",
         "tests/MemoryFileTest.cpp",
         "tests/MemoryLocalTest.cpp",
         "tests/MemoryRangeTest.cpp",
         "tests/MemoryRemoteTest.cpp",
         "tests/RegsTest.cpp",
+        "tests/SymbolsTest.cpp",
     ],
 
     cflags: [
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
new file mode 100644
index 0000000..0ff3b8c
--- /dev/null
+++ b/libunwindstack/DwarfEncoding.h
@@ -0,0 +1,47 @@
+/*
+ * 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_ENCODING_H
+#define _LIBUNWINDSTACK_DWARF_ENCODING_H
+
+#include <stdint.h>
+
+enum DwarfEncoding : uint8_t {
+  DW_EH_PE_omit = 0xff,
+
+  DW_EH_PE_absptr = 0x00,
+  DW_EH_PE_uleb128 = 0x01,
+  DW_EH_PE_udata2 = 0x02,
+  DW_EH_PE_udata4 = 0x03,
+  DW_EH_PE_udata8 = 0x04,
+  DW_EH_PE_sleb128 = 0x09,
+  DW_EH_PE_sdata2 = 0x0a,
+  DW_EH_PE_sdata4 = 0x0b,
+  DW_EH_PE_sdata8 = 0x0c,
+
+  DW_EH_PE_pcrel = 0x10,
+  DW_EH_PE_textrel = 0x20,
+  DW_EH_PE_datarel = 0x30,
+  DW_EH_PE_funcrel = 0x40,
+  DW_EH_PE_aligned = 0x50,
+
+  // The following are special values used to encode CFA and OP operands.
+  DW_EH_PE_udata1 = 0x0d,
+  DW_EH_PE_sdata1 = 0x0e,
+  DW_EH_PE_block = 0x0f,
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/bootstat/uptime_parser.h b/libunwindstack/DwarfError.h
similarity index 61%
copy from bootstat/uptime_parser.h
copy to libunwindstack/DwarfError.h
index 756ae9b..824c307 100644
--- a/bootstat/uptime_parser.h
+++ b/libunwindstack/DwarfError.h
@@ -14,16 +14,19 @@
  * limitations under the License.
  */
 
-#ifndef UPTIME_PARSER_H_
-#define UPTIME_PARSER_H_
+#ifndef _LIBUNWINDSTACK_DWARF_ERROR_H
+#define _LIBUNWINDSTACK_DWARF_ERROR_H
 
-#include <time.h>
+#include <stdint.h>
 
-namespace bootstat {
+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,
+};
 
-// Returns the number of seconds the system has been on since reboot.
-time_t ParseUptime();
-
-}  // namespace bootstat
-
-#endif  // UPTIME_PARSER_H_
\ No newline at end of file
+#endif  // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
new file mode 100644
index 0000000..11806ea
--- /dev/null
+++ b/libunwindstack/DwarfMemory.cpp
@@ -0,0 +1,248 @@
+/*
+ * 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 <assert.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "DwarfEncoding.h"
+#include "DwarfMemory.h"
+#include "Memory.h"
+
+bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) {
+  if (!memory_->Read(cur_offset_, dst, num_bytes)) {
+    return false;
+  }
+  cur_offset_ += num_bytes;
+  return true;
+}
+
+template <typename SignedType>
+bool DwarfMemory::ReadSigned(uint64_t* value) {
+  SignedType signed_value;
+  if (!ReadBytes(&signed_value, sizeof(SignedType))) {
+    return false;
+  }
+  *value = static_cast<int64_t>(signed_value);
+  return true;
+}
+
+bool DwarfMemory::ReadULEB128(uint64_t* value) {
+  uint64_t cur_value = 0;
+  uint64_t shift = 0;
+  uint8_t byte;
+  do {
+    if (!ReadBytes(&byte, 1)) {
+      return false;
+    }
+    cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
+    shift += 7;
+  } while (byte & 0x80);
+  *value = cur_value;
+  return true;
+}
+
+bool DwarfMemory::ReadSLEB128(int64_t* value) {
+  uint64_t cur_value = 0;
+  uint64_t shift = 0;
+  uint8_t byte;
+  do {
+    if (!ReadBytes(&byte, 1)) {
+      return false;
+    }
+    cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
+    shift += 7;
+  } while (byte & 0x80);
+  if (byte & 0x40) {
+    // Negative value, need to sign extend.
+    cur_value |= static_cast<uint64_t>(-1) << shift;
+  }
+  *value = static_cast<int64_t>(cur_value);
+  return true;
+}
+
+template <typename AddressType>
+size_t DwarfMemory::GetEncodedSize(uint8_t encoding) {
+  switch (encoding & 0x0f) {
+    case DW_EH_PE_absptr:
+      return sizeof(AddressType);
+    case DW_EH_PE_udata1:
+    case DW_EH_PE_sdata1:
+      return 1;
+    case DW_EH_PE_udata2:
+    case DW_EH_PE_sdata2:
+      return 2;
+    case DW_EH_PE_udata4:
+    case DW_EH_PE_sdata4:
+      return 4;
+    case DW_EH_PE_udata8:
+    case DW_EH_PE_sdata8:
+      return 8;
+    case DW_EH_PE_uleb128:
+    case DW_EH_PE_sleb128:
+    default:
+      return 0;
+  }
+}
+
+bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) {
+  assert((encoding & 0x0f) == 0);
+  assert(encoding != DW_EH_PE_aligned);
+
+  // Handle the encoding.
+  switch (encoding) {
+    case DW_EH_PE_absptr:
+      // Nothing to do.
+      break;
+    case DW_EH_PE_pcrel:
+      if (pc_offset_ == static_cast<uint64_t>(-1)) {
+        // Unsupported encoding.
+        return false;
+      }
+      *value += pc_offset_;
+      break;
+    case DW_EH_PE_textrel:
+      if (text_offset_ == static_cast<uint64_t>(-1)) {
+        // Unsupported encoding.
+        return false;
+      }
+      *value += text_offset_;
+      break;
+    case DW_EH_PE_datarel:
+      if (data_offset_ == static_cast<uint64_t>(-1)) {
+        // Unsupported encoding.
+        return false;
+      }
+      *value += data_offset_;
+      break;
+    case DW_EH_PE_funcrel:
+      if (func_offset_ == static_cast<uint64_t>(-1)) {
+        // Unsupported encoding.
+        return false;
+      }
+      *value += func_offset_;
+      break;
+    default:
+      return false;
+  }
+
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfMemory::ReadEncodedValue(uint8_t encoding, uint64_t* value) {
+  if (encoding == DW_EH_PE_omit) {
+    *value = 0;
+    return true;
+  } else if (encoding == DW_EH_PE_aligned) {
+    if (__builtin_add_overflow(cur_offset_, sizeof(AddressType) - 1, &cur_offset_)) {
+      return false;
+    }
+    cur_offset_ &= -sizeof(AddressType);
+
+    if (sizeof(AddressType) != sizeof(uint64_t)) {
+      *value = 0;
+    }
+    return ReadBytes(value, sizeof(AddressType));
+  }
+
+  // Get the data.
+  switch (encoding & 0x0f) {
+    case DW_EH_PE_absptr:
+      if (sizeof(AddressType) != sizeof(uint64_t)) {
+        *value = 0;
+      }
+      if (!ReadBytes(value, sizeof(AddressType))) {
+        return false;
+      }
+      break;
+    case DW_EH_PE_uleb128:
+      if (!ReadULEB128(value)) {
+        return false;
+      }
+      break;
+    case DW_EH_PE_sleb128:
+      int64_t signed_value;
+      if (!ReadSLEB128(&signed_value)) {
+        return false;
+      }
+      *value = static_cast<uint64_t>(signed_value);
+      break;
+    case DW_EH_PE_udata1: {
+      uint8_t value8;
+      if (!ReadBytes(&value8, 1)) {
+        return false;
+      }
+      *value = value8;
+    } break;
+    case DW_EH_PE_sdata1:
+      if (!ReadSigned<int8_t>(value)) {
+        return false;
+      }
+      break;
+    case DW_EH_PE_udata2: {
+      uint16_t value16;
+      if (!ReadBytes(&value16, 2)) {
+        return false;
+      }
+      *value = value16;
+    } break;
+    case DW_EH_PE_sdata2:
+      if (!ReadSigned<int16_t>(value)) {
+        return false;
+      }
+      break;
+    case DW_EH_PE_udata4: {
+      uint32_t value32;
+      if (!ReadBytes(&value32, 4)) {
+        return false;
+      }
+      *value = value32;
+    } break;
+    case DW_EH_PE_sdata4:
+      if (!ReadSigned<int32_t>(value)) {
+        return false;
+      }
+      break;
+    case DW_EH_PE_udata8:
+      if (!ReadBytes(value, sizeof(uint64_t))) {
+        return false;
+      }
+      break;
+    case DW_EH_PE_sdata8:
+      if (!ReadSigned<int64_t>(value)) {
+        return false;
+      }
+      break;
+    default:
+      return false;
+  }
+
+  return AdjustEncodedValue(encoding & 0xf0, value);
+}
+
+// Instantiate all of the needed template functions.
+template bool DwarfMemory::ReadSigned<int8_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int16_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int32_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int64_t>(uint64_t*);
+
+template size_t DwarfMemory::GetEncodedSize<uint32_t>(uint8_t);
+template size_t DwarfMemory::GetEncodedSize<uint64_t>(uint8_t);
+
+template bool DwarfMemory::ReadEncodedValue<uint32_t>(uint8_t, uint64_t*);
+template bool DwarfMemory::ReadEncodedValue<uint64_t>(uint8_t, uint64_t*);
diff --git a/libunwindstack/DwarfMemory.h b/libunwindstack/DwarfMemory.h
new file mode 100644
index 0000000..a304dd9
--- /dev/null
+++ b/libunwindstack/DwarfMemory.h
@@ -0,0 +1,72 @@
+/*
+ * 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_MEMORY_H
+#define _LIBUNWINDSTACK_DWARF_MEMORY_H
+
+#include <stdint.h>
+
+// Forward declarations.
+class Memory;
+
+class DwarfMemory {
+ public:
+  DwarfMemory(Memory* memory) : memory_(memory) {}
+  virtual ~DwarfMemory() = default;
+
+  bool ReadBytes(void* dst, size_t num_bytes);
+
+  template <typename SignedType>
+  bool ReadSigned(uint64_t* value);
+
+  bool ReadULEB128(uint64_t* value);
+
+  bool ReadSLEB128(int64_t* value);
+
+  template <typename AddressType>
+  size_t GetEncodedSize(uint8_t encoding);
+
+  bool AdjustEncodedValue(uint8_t encoding, uint64_t* value);
+
+  template <typename AddressType>
+  bool ReadEncodedValue(uint8_t encoding, uint64_t* value);
+
+  uint64_t cur_offset() { return cur_offset_; }
+  void set_cur_offset(uint64_t cur_offset) { cur_offset_ = cur_offset; }
+
+  void set_pc_offset(uint64_t offset) { pc_offset_ = offset; }
+  void clear_pc_offset() { pc_offset_ = static_cast<uint64_t>(-1); }
+
+  void set_data_offset(uint64_t offset) { data_offset_ = offset; }
+  void clear_data_offset() { data_offset_ = static_cast<uint64_t>(-1); }
+
+  void set_func_offset(uint64_t offset) { func_offset_ = offset; }
+  void clear_func_offset() { func_offset_ = static_cast<uint64_t>(-1); }
+
+  void set_text_offset(uint64_t offset) { text_offset_ = offset; }
+  void clear_text_offset() { text_offset_ = static_cast<uint64_t>(-1); }
+
+ private:
+  Memory* memory_;
+  uint64_t cur_offset_ = 0;
+
+  uint64_t pc_offset_ = static_cast<uint64_t>(-1);
+  uint64_t data_offset_ = static_cast<uint64_t>(-1);
+  uint64_t func_offset_ = static_cast<uint64_t>(-1);
+  uint64_t text_offset_ = static_cast<uint64_t>(-1);
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_MEMORY_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/Maps.cpp b/libunwindstack/Maps.cpp
new file mode 100644
index 0000000..b369c43
--- /dev/null
+++ b/libunwindstack/Maps.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "Maps.h"
+
+MapInfo* Maps::Find(uint64_t pc) {
+  if (maps_.empty()) {
+    return nullptr;
+  }
+  size_t first = 0;
+  size_t last = maps_.size();
+  while (first < last) {
+    size_t index = (first + last) / 2;
+    MapInfo* cur = &maps_[index];
+    if (pc >= cur->start && pc < cur->end) {
+      return cur;
+    } else if (pc < cur->start) {
+      last = index;
+    } else {
+      first = index + 1;
+    }
+  }
+  return nullptr;
+}
+
+bool Maps::ParseLine(const char* line, MapInfo* map_info) {
+  char permissions[5];
+  int name_pos;
+  // Linux /proc/<pid>/maps lines:
+  // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so
+  if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*x:%*x %*d %n", &map_info->start,
+             &map_info->end, permissions, &map_info->offset, &name_pos) != 4) {
+    return false;
+  }
+  map_info->flags = PROT_NONE;
+  if (permissions[0] == 'r') {
+    map_info->flags |= PROT_READ;
+  }
+  if (permissions[1] == 'w') {
+    map_info->flags |= PROT_WRITE;
+  }
+  if (permissions[2] == 'x') {
+    map_info->flags |= PROT_EXEC;
+  }
+
+  map_info->name = &line[name_pos];
+  size_t length = map_info->name.length() - 1;
+  if (map_info->name[length] == '\n') {
+    map_info->name.erase(length);
+  }
+  // Mark a device map in /dev/and not in /dev/ashmem/ specially.
+  if (!map_info->name.empty() && map_info->name.substr(0, 5) == "/dev/" &&
+      map_info->name.substr(5, 7) != "ashmem/") {
+    map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
+  }
+
+  return true;
+}
+
+bool Maps::Parse() {
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(GetMapsFile().c_str(), "re"), fclose);
+  if (!fp) {
+    return false;
+  }
+
+  bool valid = true;
+  char* line = nullptr;
+  size_t line_len;
+  while (getline(&line, &line_len, fp.get()) > 0) {
+    MapInfo map_info;
+    if (!ParseLine(line, &map_info)) {
+      valid = false;
+      break;
+    }
+
+    maps_.push_back(map_info);
+  }
+  free(line);
+
+  return valid;
+}
+
+Maps::~Maps() {
+  for (auto& map : maps_) {
+    delete map.elf;
+    map.elf = nullptr;
+  }
+}
+
+bool BufferMaps::Parse() {
+  const char* start_of_line = buffer_;
+  do {
+    std::string line;
+    const char* end_of_line = strchr(start_of_line, '\n');
+    if (end_of_line == nullptr) {
+      line = start_of_line;
+    } else {
+      end_of_line++;
+      line = std::string(start_of_line, end_of_line - start_of_line);
+    }
+
+    MapInfo map_info;
+    if (!ParseLine(line.c_str(), &map_info)) {
+      return false;
+    }
+    maps_.push_back(map_info);
+
+    start_of_line = end_of_line;
+  } while (start_of_line != nullptr && *start_of_line != '\0');
+  return true;
+}
+
+const std::string RemoteMaps::GetMapsFile() const {
+  return "/proc/" + std::to_string(pid_) + "/maps";
+}
+
+bool OfflineMaps::Parse() {
+  // Format of maps information:
+  //   <uint64_t> StartOffset
+  //   <uint64_t> EndOffset
+  //   <uint64_t> offset
+  //   <uint16_t> flags
+  //   <uint16_t> MapNameLength
+  //   <VariableLengthValue> MapName
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY)));
+  if (fd == -1) {
+    return false;
+  }
+
+  std::vector<char> name;
+  while (true) {
+    MapInfo map_info;
+    ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.start, sizeof(map_info.start)));
+    if (bytes == 0) {
+      break;
+    }
+    if (bytes == -1 || bytes != sizeof(map_info.start)) {
+      return false;
+    }
+    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.end, sizeof(map_info.end)));
+    if (bytes == -1 || bytes != sizeof(map_info.end)) {
+      return false;
+    }
+    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.offset, sizeof(map_info.offset)));
+    if (bytes == -1 || bytes != sizeof(map_info.offset)) {
+      return false;
+    }
+    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.flags, sizeof(map_info.flags)));
+    if (bytes == -1 || bytes != sizeof(map_info.flags)) {
+      return false;
+    }
+    uint16_t len;
+    bytes = TEMP_FAILURE_RETRY(read(fd, &len, sizeof(len)));
+    if (bytes == -1 || bytes != sizeof(len)) {
+      return false;
+    }
+    if (len > 0) {
+      name.resize(len);
+      bytes = TEMP_FAILURE_RETRY(read(fd, name.data(), len));
+      if (bytes == -1 || bytes != len) {
+        return false;
+      }
+      map_info.name = std::string(name.data(), len);
+    }
+    maps_.push_back(map_info);
+  }
+  return true;
+}
diff --git a/libunwindstack/Maps.h b/libunwindstack/Maps.h
new file mode 100644
index 0000000..239b64a
--- /dev/null
+++ b/libunwindstack/Maps.h
@@ -0,0 +1,107 @@
+/*
+ * 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_MAPS_H
+#define _LIBUNWINDSTACK_MAPS_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "Elf.h"
+#include "MapInfo.h"
+
+// Special flag to indicate a map is in /dev/. However, a map in
+// /dev/ashmem/... does not set this flag.
+static constexpr int MAPS_FLAGS_DEVICE_MAP = 0x8000;
+
+class Maps {
+ public:
+  Maps() = default;
+  virtual ~Maps();
+
+  MapInfo* Find(uint64_t pc);
+
+  bool ParseLine(const char* line, MapInfo* map_info);
+
+  virtual bool Parse();
+
+  virtual const std::string GetMapsFile() const { return ""; }
+
+  typedef std::vector<MapInfo>::iterator iterator;
+  iterator begin() { return maps_.begin(); }
+  iterator end() { return maps_.end(); }
+
+  typedef std::vector<MapInfo>::const_iterator const_iterator;
+  const_iterator begin() const { return maps_.begin(); }
+  const_iterator end() const { return maps_.end(); }
+
+  size_t Total() { return maps_.size(); }
+
+ protected:
+  std::vector<MapInfo> maps_;
+};
+
+class RemoteMaps : public Maps {
+ public:
+  RemoteMaps(pid_t pid) : pid_(pid) {}
+  virtual ~RemoteMaps() = default;
+
+  virtual const std::string GetMapsFile() const override;
+
+ private:
+  pid_t pid_;
+};
+
+class LocalMaps : public RemoteMaps {
+ public:
+  LocalMaps() : RemoteMaps(getpid()) {}
+  virtual ~LocalMaps() = default;
+};
+
+class BufferMaps : public Maps {
+ public:
+  BufferMaps(const char* buffer) : buffer_(buffer) {}
+  virtual ~BufferMaps() = default;
+
+  bool Parse() override;
+
+ private:
+  const char* buffer_;
+};
+
+class FileMaps : public Maps {
+ public:
+  FileMaps(const std::string& file) : file_(file) {}
+  virtual ~FileMaps() = default;
+
+  const std::string GetMapsFile() const override { return file_; }
+
+ protected:
+  const std::string file_;
+};
+
+class OfflineMaps : public FileMaps {
+ public:
+  OfflineMaps(const std::string& file) : FileMaps(file) {}
+  virtual ~OfflineMaps() = default;
+
+  bool Parse() override;
+};
+
+#endif  // _LIBUNWINDSTACK_MAPS_H
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/Symbols.cpp b/libunwindstack/Symbols.cpp
new file mode 100644
index 0000000..86c1233
--- /dev/null
+++ b/libunwindstack/Symbols.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 <assert.h>
+#include <elf.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "Memory.h"
+#include "Symbols.h"
+
+Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
+                 uint64_t str_size)
+    : cur_offset_(offset),
+      offset_(offset),
+      end_(offset + size),
+      entry_size_(entry_size),
+      str_offset_(str_offset),
+      str_end_(str_offset_ + str_size) {}
+
+const Symbols::Info* Symbols::GetInfoFromCache(uint64_t addr) {
+  // Binary search the table.
+  size_t first = 0;
+  size_t last = symbols_.size();
+  while (first < last) {
+    size_t current = first + (last - first) / 2;
+    const Info* info = &symbols_[current];
+    if (addr < info->start_offset) {
+      last = current;
+    } else if (addr < info->end_offset) {
+      return info;
+    } else {
+      first = current + 1;
+    }
+  }
+  return nullptr;
+}
+
+template <typename SymType>
+bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
+                      uint64_t* func_offset) {
+  addr += load_bias;
+
+  if (symbols_.size() != 0) {
+    const Info* info = GetInfoFromCache(addr);
+    if (info) {
+      assert(addr >= info->start_offset && addr <= info->end_offset);
+      *func_offset = addr - info->start_offset;
+      return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
+    }
+  }
+
+  bool symbol_added = false;
+  bool return_value = false;
+  while (cur_offset_ + entry_size_ <= end_) {
+    SymType entry;
+    if (!elf_memory->Read(cur_offset_, &entry, sizeof(entry))) {
+      // Stop all processing, something looks like it is corrupted.
+      cur_offset_ = UINT64_MAX;
+      return false;
+    }
+    cur_offset_ += entry_size_;
+
+    if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
+      // Treat st_value as virtual address.
+      uint64_t start_offset = entry.st_value;
+      if (entry.st_shndx != SHN_ABS) {
+        start_offset += load_bias;
+      }
+      uint64_t end_offset = start_offset + entry.st_size;
+
+      // Cache the value.
+      symbols_.emplace_back(start_offset, end_offset, str_offset_ + entry.st_name);
+      symbol_added = true;
+
+      if (addr >= start_offset && addr < end_offset) {
+        *func_offset = addr - start_offset;
+        uint64_t offset = str_offset_ + entry.st_name;
+        if (offset < str_end_) {
+          return_value = elf_memory->ReadString(offset, name, str_end_ - offset);
+        }
+        break;
+      }
+    }
+  }
+
+  if (symbol_added) {
+    std::sort(symbols_.begin(), symbols_.end(),
+              [](const Info& a, const Info& b) { return a.start_offset < b.start_offset; });
+  }
+  return return_value;
+}
+
+// Instantiate all of the needed template functions.
+template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
new file mode 100644
index 0000000..3c0d033
--- /dev/null
+++ b/libunwindstack/Symbols.h
@@ -0,0 +1,64 @@
+/*
+ * 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_SYMBOLS_H
+#define _LIBUNWINDSTACK_SYMBOLS_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+// Forward declaration.
+class Memory;
+
+class Symbols {
+  struct Info {
+    Info(uint64_t start_offset, uint64_t end_offset, uint64_t str_offset)
+        : start_offset(start_offset), end_offset(end_offset), str_offset(str_offset) {}
+    uint64_t start_offset;
+    uint64_t end_offset;
+    uint64_t str_offset;
+  };
+
+ public:
+  Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
+          uint64_t str_size);
+  virtual ~Symbols() = default;
+
+  const Info* GetInfoFromCache(uint64_t addr);
+
+  template <typename SymType>
+  bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
+               uint64_t* func_offset);
+
+  void ClearCache() {
+    symbols_.clear();
+    cur_offset_ = offset_;
+  }
+
+ private:
+  uint64_t cur_offset_;
+  uint64_t offset_;
+  uint64_t end_;
+  uint64_t entry_size_;
+  uint64_t str_offset_;
+  uint64_t str_end_;
+
+  std::vector<Info> symbols_;
+};
+
+#endif  // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
new file mode 100644
index 0000000..4877f36
--- /dev/null
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -0,0 +1,472 @@
+/*
+ * 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 <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfMemory.h"
+
+#include "MemoryFake.h"
+
+class DwarfMemoryTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    dwarf_mem_.reset(new DwarfMemory(&memory_));
+  }
+
+  template <typename AddressType>
+  void GetEncodedSizeTest(uint8_t value, size_t expected);
+  template <typename AddressType>
+  void ReadEncodedValue_omit();
+  template <typename AddressType>
+  void ReadEncodedValue_leb128();
+  template <typename AddressType>
+  void ReadEncodedValue_data1();
+  template <typename AddressType>
+  void ReadEncodedValue_data2();
+  template <typename AddressType>
+  void ReadEncodedValue_data4();
+  template <typename AddressType>
+  void ReadEncodedValue_data8();
+  template <typename AddressType>
+  void ReadEncodedValue_non_zero_adjust();
+  template <typename AddressType>
+  void ReadEncodedValue_overflow();
+
+  MemoryFake memory_;
+  std::unique_ptr<DwarfMemory> dwarf_mem_;
+};
+
+TEST_F(DwarfMemoryTest, ReadBytes) {
+  memory_.SetMemory(0, std::vector<uint8_t>{0x10, 0x18, 0xff, 0xfe});
+
+  uint8_t byte;
+  ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+  ASSERT_EQ(0x10U, byte);
+  ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+  ASSERT_EQ(0x18U, byte);
+  ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+  ASSERT_EQ(0xffU, byte);
+  ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+  ASSERT_EQ(0xfeU, byte);
+  ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+
+  dwarf_mem_->set_cur_offset(2);
+  ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+  ASSERT_EQ(0xffU, byte);
+  ASSERT_EQ(3U, dwarf_mem_->cur_offset());
+}
+
+TEST_F(DwarfMemoryTest, ReadSigned_check) {
+  uint64_t value;
+
+  // Signed 8 byte reads.
+  memory_.SetData8(0, static_cast<uint8_t>(-10));
+  memory_.SetData8(1, 200);
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int8_t>(&value));
+  ASSERT_EQ(static_cast<int8_t>(-10), static_cast<int8_t>(value));
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int8_t>(&value));
+  ASSERT_EQ(static_cast<int8_t>(200), static_cast<int8_t>(value));
+
+  // Signed 16 byte reads.
+  memory_.SetData16(0x10, static_cast<uint16_t>(-1000));
+  memory_.SetData16(0x12, 50100);
+  dwarf_mem_->set_cur_offset(0x10);
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int16_t>(&value));
+  ASSERT_EQ(static_cast<int16_t>(-1000), static_cast<int16_t>(value));
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int16_t>(&value));
+  ASSERT_EQ(static_cast<int16_t>(50100), static_cast<int16_t>(value));
+
+  // Signed 32 byte reads.
+  memory_.SetData32(0x100, static_cast<uint32_t>(-1000000000));
+  memory_.SetData32(0x104, 3000000000);
+  dwarf_mem_->set_cur_offset(0x100);
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int32_t>(&value));
+  ASSERT_EQ(static_cast<int32_t>(-1000000000), static_cast<int32_t>(value));
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int32_t>(&value));
+  ASSERT_EQ(static_cast<int32_t>(3000000000), static_cast<int32_t>(value));
+
+  // Signed 64 byte reads.
+  memory_.SetData64(0x200, static_cast<uint64_t>(-2000000000000LL));
+  memory_.SetData64(0x208, 5000000000000LL);
+  dwarf_mem_->set_cur_offset(0x200);
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int64_t>(&value));
+  ASSERT_EQ(static_cast<int64_t>(-2000000000000), static_cast<int64_t>(value));
+  ASSERT_TRUE(dwarf_mem_->ReadSigned<int64_t>(&value));
+  ASSERT_EQ(static_cast<int64_t>(5000000000000), static_cast<int64_t>(value));
+}
+
+TEST_F(DwarfMemoryTest, ReadULEB128) {
+  memory_.SetMemory(0, std::vector<uint8_t>{0x01, 0x80, 0x24, 0xff, 0xc3, 0xff, 0x7f});
+
+  uint64_t value;
+  ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+  ASSERT_EQ(1U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(1U, value);
+
+  ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+  ASSERT_EQ(3U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x1200U, value);
+
+  ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+  ASSERT_EQ(7U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0xfffe1ffU, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadSLEB128) {
+  memory_.SetMemory(0, std::vector<uint8_t>{0x06, 0x40, 0x82, 0x34, 0x89, 0x64, 0xf9, 0xc3, 0x8f,
+                                            0x2f, 0xbf, 0xc3, 0xf7, 0x5f});
+
+  int64_t value;
+  ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+  ASSERT_EQ(1U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(6U, value);
+
+  ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+  ASSERT_EQ(2U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0xffffffffffffffc0ULL, static_cast<uint64_t>(value));
+
+  ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+  ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x1a02U, value);
+
+  ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+  ASSERT_EQ(6U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0xfffffffffffff209ULL, static_cast<uint64_t>(value));
+
+  ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+  ASSERT_EQ(10U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x5e3e1f9U, value);
+
+  ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+  ASSERT_EQ(14U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0xfffffffffbfde1bfULL, static_cast<uint64_t>(value));
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::GetEncodedSizeTest(uint8_t value, size_t expected) {
+  for (size_t i = 0; i < 16; i++) {
+    uint8_t encoding = (i << 4) | value;
+    ASSERT_EQ(expected, dwarf_mem_->GetEncodedSize<AddressType>(encoding))
+        << "encoding 0x" << std::hex << static_cast<uint32_t>(encoding) << " test value 0x"
+        << static_cast<size_t>(value);
+  }
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint32_t) {
+  GetEncodedSizeTest<uint32_t>(0, sizeof(uint32_t));
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint64_t) {
+  GetEncodedSizeTest<uint64_t>(0, sizeof(uint64_t));
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data1) {
+  // udata1
+  GetEncodedSizeTest<uint32_t>(0x0d, 1);
+  GetEncodedSizeTest<uint64_t>(0x0d, 1);
+
+  // sdata1
+  GetEncodedSizeTest<uint32_t>(0x0e, 1);
+  GetEncodedSizeTest<uint64_t>(0x0e, 1);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data2) {
+  // udata2
+  GetEncodedSizeTest<uint32_t>(0x02, 2);
+  GetEncodedSizeTest<uint64_t>(0x02, 2);
+
+  // sdata2
+  GetEncodedSizeTest<uint32_t>(0x0a, 2);
+  GetEncodedSizeTest<uint64_t>(0x0a, 2);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data4) {
+  // udata4
+  GetEncodedSizeTest<uint32_t>(0x03, 4);
+  GetEncodedSizeTest<uint64_t>(0x03, 4);
+
+  // sdata4
+  GetEncodedSizeTest<uint32_t>(0x0b, 4);
+  GetEncodedSizeTest<uint64_t>(0x0b, 4);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data8) {
+  // udata8
+  GetEncodedSizeTest<uint32_t>(0x04, 8);
+  GetEncodedSizeTest<uint64_t>(0x04, 8);
+
+  // sdata8
+  GetEncodedSizeTest<uint32_t>(0x0c, 8);
+  GetEncodedSizeTest<uint64_t>(0x0c, 8);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_unknown) {
+  GetEncodedSizeTest<uint32_t>(0x01, 0);
+  GetEncodedSizeTest<uint64_t>(0x01, 0);
+
+  GetEncodedSizeTest<uint32_t>(0x09, 0);
+  GetEncodedSizeTest<uint64_t>(0x09, 0);
+
+  GetEncodedSizeTest<uint32_t>(0x0f, 0);
+  GetEncodedSizeTest<uint64_t>(0x0f, 0);
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_omit() {
+  uint64_t value = 123;
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0xff, &value));
+  ASSERT_EQ(0U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) { ReadEncodedValue_omit<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) { ReadEncodedValue_omit<uint64_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) {
+  uint64_t value = 100;
+  ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x00, &value));
+
+  memory_.SetData32(0, 0x12345678);
+
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x00, &value));
+  ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x12345678U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint64_t) {
+  uint64_t value = 100;
+  ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x00, &value));
+
+  memory_.SetData64(0, 0x12345678f1f2f3f4ULL);
+
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x00, &value));
+  ASSERT_EQ(8U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x12345678f1f2f3f4ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint32_t) {
+  uint64_t value = 100;
+  dwarf_mem_->set_cur_offset(1);
+  ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x50, &value));
+
+  memory_.SetData32(4, 0x12345678);
+
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x50, &value));
+  ASSERT_EQ(8U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x12345678U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint64_t) {
+  uint64_t value = 100;
+  dwarf_mem_->set_cur_offset(1);
+  ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x50, &value));
+
+  memory_.SetData64(8, 0x12345678f1f2f3f4ULL);
+
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x50, &value));
+  ASSERT_EQ(16U, dwarf_mem_->cur_offset());
+  ASSERT_EQ(0x12345678f1f2f3f4ULL, value);
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_leb128() {
+  memory_.SetMemory(0, std::vector<uint8_t>{0x80, 0x42});
+
+  uint64_t value = 100;
+  // uleb128
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x01, &value));
+  ASSERT_EQ(0x2100U, value);
+
+  dwarf_mem_->set_cur_offset(0);
+  // sleb128
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x09, &value));
+  ASSERT_EQ(0xffffffffffffe100ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) { ReadEncodedValue_leb128<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) { ReadEncodedValue_leb128<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data1() {
+  memory_.SetData8(0, 0xe0);
+
+  uint64_t value = 0;
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0d, &value));
+  ASSERT_EQ(0xe0U, value);
+
+  dwarf_mem_->set_cur_offset(0);
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0e, &value));
+  ASSERT_EQ(0xffffffffffffffe0ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) { ReadEncodedValue_data1<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) { ReadEncodedValue_data1<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data2() {
+  memory_.SetData16(0, 0xe000);
+
+  uint64_t value = 0;
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x02, &value));
+  ASSERT_EQ(0xe000U, value);
+
+  dwarf_mem_->set_cur_offset(0);
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0a, &value));
+  ASSERT_EQ(0xffffffffffffe000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) { ReadEncodedValue_data2<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) { ReadEncodedValue_data2<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data4() {
+  memory_.SetData32(0, 0xe0000000);
+
+  uint64_t value = 0;
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x03, &value));
+  ASSERT_EQ(0xe0000000U, value);
+
+  dwarf_mem_->set_cur_offset(0);
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0b, &value));
+  ASSERT_EQ(0xffffffffe0000000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) { ReadEncodedValue_data4<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) { ReadEncodedValue_data4<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data8() {
+  memory_.SetData64(0, 0xe000000000000000ULL);
+
+  uint64_t value = 0;
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x04, &value));
+  ASSERT_EQ(0xe000000000000000ULL, value);
+
+  dwarf_mem_->set_cur_offset(0);
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0c, &value));
+  ASSERT_EQ(0xe000000000000000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) { ReadEncodedValue_data8<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) { ReadEncodedValue_data8<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() {
+  memory_.SetData64(0, 0xe000000000000000ULL);
+
+  uint64_t value = 0;
+  dwarf_mem_->set_pc_offset(0x2000);
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x14, &value));
+  ASSERT_EQ(0xe000000000002000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint32_t) {
+  ReadEncodedValue_non_zero_adjust<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint64_t) {
+  ReadEncodedValue_non_zero_adjust<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_overflow() {
+  memory_.SetData64(0, 0);
+
+  uint64_t value = 0;
+  dwarf_mem_->set_cur_offset(UINT64_MAX);
+  ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<AddressType>(0x50, &value));
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint32_t) {
+  ReadEncodedValue_overflow<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint64_t) {
+  ReadEncodedValue_overflow<uint64_t>();
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) {
+  uint64_t value = 0x1234;
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value));
+  ASSERT_EQ(0x1234U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_pcrel) {
+  uint64_t value = 0x1234;
+  ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+
+  dwarf_mem_->set_pc_offset(0x2000);
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+  ASSERT_EQ(0x3234U, value);
+
+  dwarf_mem_->set_pc_offset(static_cast<uint64_t>(-4));
+  value = 0x1234;
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+  ASSERT_EQ(0x1230U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_textrel) {
+  uint64_t value = 0x8234;
+  ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+
+  dwarf_mem_->set_text_offset(0x1000);
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+  ASSERT_EQ(0x9234U, value);
+
+  dwarf_mem_->set_text_offset(static_cast<uint64_t>(-16));
+  value = 0x8234;
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+  ASSERT_EQ(0x8224U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_datarel) {
+  uint64_t value = 0xb234;
+  ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+
+  dwarf_mem_->set_data_offset(0x1200);
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+  ASSERT_EQ(0xc434U, value);
+
+  dwarf_mem_->set_data_offset(static_cast<uint64_t>(-256));
+  value = 0xb234;
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+  ASSERT_EQ(0xb134U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_funcrel) {
+  uint64_t value = 0x15234;
+  ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+
+  dwarf_mem_->set_func_offset(0x60000);
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+  ASSERT_EQ(0x75234U, value);
+
+  dwarf_mem_->set_func_offset(static_cast<uint64_t>(-4096));
+  value = 0x15234;
+  ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+  ASSERT_EQ(0x14234U, value);
+}
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/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
new file mode 100644
index 0000000..7eb9bae
--- /dev/null
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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 <sys/mman.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Maps.h"
+
+TEST(MapsTest, parse_permissions) {
+  BufferMaps maps(
+      "1000-2000 ---- 00000000 00:00 0\n"
+      "2000-3000 r--- 00000000 00:00 0\n"
+      "3000-4000 -w-- 00000000 00:00 0\n"
+      "4000-5000 --x- 00000000 00:00 0\n"
+      "5000-6000 rwx- 00000000 00:00 0\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(5U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(PROT_NONE, it->flags);
+  ASSERT_EQ(0x1000U, it->start);
+  ASSERT_EQ(0x2000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_READ, it->flags);
+  ASSERT_EQ(0x2000U, it->start);
+  ASSERT_EQ(0x3000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_WRITE, it->flags);
+  ASSERT_EQ(0x3000U, it->start);
+  ASSERT_EQ(0x4000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_EXEC, it->flags);
+  ASSERT_EQ(0x4000U, it->start);
+  ASSERT_EQ(0x5000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, it->flags);
+  ASSERT_EQ(0x5000U, it->start);
+  ASSERT_EQ(0x6000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, parse_name) {
+  BufferMaps maps(
+      "720b29b000-720b29e000 rw-p 00000000 00:00 0\n"
+      "720b29e000-720b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+      "720b29f000-720b2a0000 rw-p 00000000 00:00 0");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(3U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ("", it->name);
+  ASSERT_EQ(0x720b29b000U, it->start);
+  ASSERT_EQ(0x720b29e000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ++it;
+  ASSERT_EQ("/system/lib/fake.so", it->name);
+  ASSERT_EQ(0x720b29e000U, it->start);
+  ASSERT_EQ(0x720b29f000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ++it;
+  ASSERT_EQ("", it->name);
+  ASSERT_EQ(0x720b29f000U, it->start);
+  ASSERT_EQ(0x720b2a0000U, it->end);
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, parse_offset) {
+  BufferMaps maps(
+      "a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+      "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(2U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(0U, it->offset);
+  ASSERT_EQ(0xa000U, it->start);
+  ASSERT_EQ(0xe000U, it->end);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ASSERT_EQ("/system/lib/fake.so", it->name);
+  ++it;
+  ASSERT_EQ(0xa12345U, it->offset);
+  ASSERT_EQ(0xe000U, it->start);
+  ASSERT_EQ(0xf000U, it->end);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+  ASSERT_EQ("/system/lib/fake.so", it->name);
+  ++it;
+  ASSERT_EQ(maps.end(), it);
+}
+
+TEST(MapsTest, device) {
+  BufferMaps maps(
+      "a000-e000 rw-p 00000000 00:00 0 /dev/\n"
+      "f000-f100 rw-p 00000000 00:00 0 /dev/does_not_exist\n"
+      "f100-f200 rw-p 00000000 00:00 0 /dev/ashmem/does_not_exist\n"
+      "f200-f300 rw-p 00000000 00:00 0 /devsomething/does_not_exist\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(4U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_TRUE(it->flags & 0x8000);
+  ASSERT_EQ("/dev/", it->name);
+  ++it;
+  ASSERT_TRUE(it->flags & 0x8000);
+  ASSERT_EQ("/dev/does_not_exist", it->name);
+  ++it;
+  ASSERT_FALSE(it->flags & 0x8000);
+  ASSERT_EQ("/dev/ashmem/does_not_exist", it->name);
+  ++it;
+  ASSERT_FALSE(it->flags & 0x8000);
+  ASSERT_EQ("/devsomething/does_not_exist", it->name);
+}
+
+TEST(MapsTest, file_smoke) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_TRUE(
+      android::base::WriteStringToFile("720b29b000-720b29e000 r-xp a0000000 00:00 0   /fake.so\n"
+                                       "720b2b0000-720b2e0000 r-xp b0000000 00:00 0   /fake2.so\n"
+                                       "720b2e0000-720b2f0000 r-xp c0000000 00:00 0   /fake3.so\n",
+                                       tf.path, 0660, getuid(), getgid()));
+
+  FileMaps maps(tf.path);
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(3U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(0x720b29b000U, it->start);
+  ASSERT_EQ(0x720b29e000U, it->end);
+  ASSERT_EQ(0xa0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake.so", it->name);
+  ++it;
+  ASSERT_EQ(0x720b2b0000U, it->start);
+  ASSERT_EQ(0x720b2e0000U, it->end);
+  ASSERT_EQ(0xb0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake2.so", it->name);
+  ++it;
+  ASSERT_EQ(0x720b2e0000U, it->start);
+  ASSERT_EQ(0x720b2f0000U, it->end);
+  ASSERT_EQ(0xc0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake3.so", it->name);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, find) {
+  BufferMaps maps(
+      "1000-2000 r--p 00000010 00:00 0 /system/lib/fake1.so\n"
+      "3000-4000 -w-p 00000020 00:00 0 /system/lib/fake2.so\n"
+      "6000-8000 --xp 00000030 00:00 0 /system/lib/fake3.so\n"
+      "a000-b000 rw-p 00000040 00:00 0 /system/lib/fake4.so\n"
+      "e000-f000 rwxp 00000050 00:00 0 /system/lib/fake5.so\n");
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(5U, maps.Total());
+
+  ASSERT_TRUE(maps.Find(0x500) == nullptr);
+  ASSERT_TRUE(maps.Find(0x2000) == nullptr);
+  ASSERT_TRUE(maps.Find(0x5010) == nullptr);
+  ASSERT_TRUE(maps.Find(0x9a00) == nullptr);
+  ASSERT_TRUE(maps.Find(0xf000) == nullptr);
+  ASSERT_TRUE(maps.Find(0xf010) == nullptr);
+
+  MapInfo* info = maps.Find(0x1000);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0x1000U, info->start);
+  ASSERT_EQ(0x2000U, info->end);
+  ASSERT_EQ(0x10U, info->offset);
+  ASSERT_EQ(PROT_READ, info->flags);
+  ASSERT_EQ("/system/lib/fake1.so", info->name);
+
+  info = maps.Find(0x3020);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0x3000U, info->start);
+  ASSERT_EQ(0x4000U, info->end);
+  ASSERT_EQ(0x20U, info->offset);
+  ASSERT_EQ(PROT_WRITE, info->flags);
+  ASSERT_EQ("/system/lib/fake2.so", info->name);
+
+  info = maps.Find(0x6020);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0x6000U, info->start);
+  ASSERT_EQ(0x8000U, info->end);
+  ASSERT_EQ(0x30U, info->offset);
+  ASSERT_EQ(PROT_EXEC, info->flags);
+  ASSERT_EQ("/system/lib/fake3.so", info->name);
+
+  info = maps.Find(0xafff);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0xa000U, info->start);
+  ASSERT_EQ(0xb000U, info->end);
+  ASSERT_EQ(0x40U, info->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE, info->flags);
+  ASSERT_EQ("/system/lib/fake4.so", info->name);
+
+  info = maps.Find(0xe500);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(0xe000U, info->start);
+  ASSERT_EQ(0xf000U, info->end);
+  ASSERT_EQ(0x50U, info->offset);
+  ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+  ASSERT_EQ("/system/lib/fake5.so", info->name);
+}
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/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
new file mode 100644
index 0000000..a0a21e6
--- /dev/null
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -0,0 +1,335 @@
+/*
+ * 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 <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+#include "MemoryFake.h"
+#include "Symbols.h"
+
+template <typename TypeParam>
+class SymbolsTest : public ::testing::Test {
+ protected:
+  void SetUp() override { memory_.Clear(); }
+
+  void InitSym(TypeParam* sym, uint32_t st_value, uint32_t st_size, uint32_t st_name) {
+    memset(sym, 0, sizeof(*sym));
+    sym->st_info = STT_FUNC;
+    sym->st_value = st_value;
+    sym->st_size = st_size;
+    sym->st_name = st_name;
+    sym->st_shndx = SHN_COMMON;
+  }
+
+  MemoryFake memory_;
+};
+TYPED_TEST_CASE_P(SymbolsTest);
+
+TYPED_TEST_P(SymbolsTest, function_bounds_check) {
+  Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+  TypeParam sym;
+  this->InitSym(&sym, 0x5000, 0x10, 0x40);
+  uint64_t offset = 0x1000;
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+  std::string fake_name("fake_function");
+  this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+  std::string name;
+  uint64_t func_offset;
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("fake_function", name);
+  ASSERT_EQ(0U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("fake_function", name);
+  ASSERT_EQ(0xfU, func_offset);
+
+  // Check one before and one after the function.
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, 0, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, no_symbol) {
+  Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+  TypeParam sym;
+  this->InitSym(&sym, 0x5000, 0x10, 0x40);
+  uint64_t offset = 0x1000;
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+  std::string fake_name("fake_function");
+  this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+  // First verify that we can get the name.
+  std::string name;
+  uint64_t func_offset;
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("fake_function", name);
+  ASSERT_EQ(0U, func_offset);
+
+  // Now modify the info field so it's no longer a function.
+  sym.st_info = 0;
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  // Clear the cache to force the symbol data to be re-read.
+  symbols.ClearCache();
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+
+  // Set the function back, and set the shndx to UNDEF.
+  sym.st_info = STT_FUNC;
+  sym.st_shndx = SHN_UNDEF;
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  // Clear the cache to force the symbol data to be re-read.
+  symbols.ClearCache();
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, multiple_entries) {
+  Symbols symbols(0x1000, sizeof(TypeParam) * 3, sizeof(TypeParam), 0x2000, 0x500);
+
+  TypeParam sym;
+  uint64_t offset = 0x1000;
+  std::string fake_name;
+
+  this->InitSym(&sym, 0x5000, 0x10, 0x40);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  fake_name = "function_one";
+  this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+  offset += sizeof(sym);
+
+  this->InitSym(&sym, 0x3004, 0x200, 0x100);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  fake_name = "function_two";
+  this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
+  offset += sizeof(sym);
+
+  this->InitSym(&sym, 0xa010, 0x20, 0x230);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  fake_name = "function_three";
+  this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
+
+  std::string name;
+  uint64_t func_offset;
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_two", name);
+  ASSERT_EQ(1U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_one", name);
+  ASSERT_EQ(4U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_three", name);
+  ASSERT_EQ(1U, func_offset);
+
+  // Reget some of the others to verify getting one function name doesn't
+  // affect any of the next calls.
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_one", name);
+  ASSERT_EQ(8U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_two", name);
+  ASSERT_EQ(4U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_three", name);
+  ASSERT_EQ(0xaU, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, multiple_entries_nonstandard_size) {
+  uint64_t entry_size = sizeof(TypeParam) + 5;
+  Symbols symbols(0x1000, entry_size * 3, entry_size, 0x2000, 0x500);
+
+  TypeParam sym;
+  uint64_t offset = 0x1000;
+  std::string fake_name;
+
+  this->InitSym(&sym, 0x5000, 0x10, 0x40);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  fake_name = "function_one";
+  this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+  offset += entry_size;
+
+  this->InitSym(&sym, 0x3004, 0x200, 0x100);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  fake_name = "function_two";
+  this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
+  offset += entry_size;
+
+  this->InitSym(&sym, 0xa010, 0x20, 0x230);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  fake_name = "function_three";
+  this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
+
+  std::string name;
+  uint64_t func_offset;
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_two", name);
+  ASSERT_EQ(1U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_one", name);
+  ASSERT_EQ(4U, func_offset);
+
+  name.clear();
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function_three", name);
+  ASSERT_EQ(1U, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, load_bias) {
+  Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+  TypeParam sym;
+  this->InitSym(&sym, 0x5000, 0x10, 0x40);
+  uint64_t offset = 0x1000;
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+  std::string fake_name("fake_function");
+  this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+  // Set a non-zero load_bias that should be a valid function offset.
+  std::string name;
+  uint64_t func_offset;
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("fake_function", name);
+  ASSERT_EQ(4U, func_offset);
+
+  // Set a flag that should cause the load_bias to be ignored.
+  sym.st_shndx = SHN_ABS;
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  // Clear the cache to force the symbol data to be re-read.
+  symbols.ClearCache();
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, symtab_value_out_of_bounds) {
+  Symbols symbols_end_at_100(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x100);
+  Symbols symbols_end_at_200(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x200);
+
+  TypeParam sym;
+  uint64_t offset = 0x1000;
+
+  this->InitSym(&sym, 0x5000, 0x10, 0xfb);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  offset += sizeof(sym);
+
+  this->InitSym(&sym, 0x3000, 0x10, 0x100);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+  // Put the name across the end of the tab.
+  std::string fake_name("fake_function");
+  this->memory_.SetMemory(0x20fb, fake_name.c_str(), fake_name.size() + 1);
+
+  std::string name;
+  uint64_t func_offset;
+  // Verify that we can get the function name properly for both entries.
+  ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("fake_function", name);
+  ASSERT_EQ(0U, func_offset);
+  ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("function", name);
+  ASSERT_EQ(0U, func_offset);
+
+  // Now use the symbol table that ends at 0x100.
+  ASSERT_FALSE(
+      symbols_end_at_100.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(
+      symbols_end_at_100.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+}
+
+// Verify the entire func table is cached.
+TYPED_TEST_P(SymbolsTest, symtab_read_cached) {
+  Symbols symbols(0x1000, 3 * sizeof(TypeParam), sizeof(TypeParam), 0xa000, 0x1000);
+
+  TypeParam sym;
+  uint64_t offset = 0x1000;
+
+  // Make sure that these entries are not in ascending order.
+  this->InitSym(&sym, 0x5000, 0x10, 0x100);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  offset += sizeof(sym);
+
+  this->InitSym(&sym, 0x2000, 0x300, 0x200);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  offset += sizeof(sym);
+
+  this->InitSym(&sym, 0x1000, 0x100, 0x300);
+  this->memory_.SetMemory(offset, &sym, sizeof(sym));
+  offset += sizeof(sym);
+
+  // Do call that should cache all of the entries (except the string data).
+  std::string name;
+  uint64_t func_offset;
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+  this->memory_.Clear();
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+
+  // Clear the memory and only put the symbol data string data in memory.
+  this->memory_.Clear();
+
+  std::string fake_name;
+  fake_name = "first_entry";
+  this->memory_.SetMemory(0xa100, fake_name.c_str(), fake_name.size() + 1);
+  fake_name = "second_entry";
+  this->memory_.SetMemory(0xa200, fake_name.c_str(), fake_name.size() + 1);
+  fake_name = "third_entry";
+  this->memory_.SetMemory(0xa300, fake_name.c_str(), fake_name.size() + 1);
+
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("first_entry", name);
+  ASSERT_EQ(1U, func_offset);
+
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("second_entry", name);
+  ASSERT_EQ(2U, func_offset);
+
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, 0, &this->memory_, &name, &func_offset));
+  ASSERT_EQ("third_entry", name);
+  ASSERT_EQ(3U, func_offset);
+}
+
+REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
+                           multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds,
+                           symtab_read_cached);
+
+typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 7600528..2edf224 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -18,6 +18,7 @@
 
 #include <cstdio>
 #include <sys/param.h>
+#include <sys/stat.h>
 #include <zlib.h>
 #define DEF_MEM_LEVEL 8                // normally in zutil.h?
 
@@ -84,11 +85,19 @@
   delete stream;
 }
 
-ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip),
-                                z_stream_(nullptr, DeleteZStream), buffer_(kBufSize) {
+ZipWriter::ZipWriter(FILE* f) : file_(f), seekable_(false), current_offset_(0),
+                                state_(State::kWritingZip), z_stream_(nullptr, DeleteZStream),
+                                buffer_(kBufSize) {
+  // Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
+  // will fail as well.
+  struct stat file_stats;
+  if (fstat(fileno(f), &file_stats) == 0) {
+    seekable_ = S_ISREG(file_stats.st_mode);
+  }
 }
 
 ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
+                                           seekable_(writer.seekable_),
                                            current_offset_(writer.current_offset_),
                                            state_(writer.state_),
                                            files_(std::move(writer.files_)),
@@ -100,6 +109,7 @@
 
 ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
   file_ = writer.file_;
+  seekable_ = writer.seekable_;
   current_offset_ = writer.current_offset_;
   state_ = writer.state_;
   files_ = std::move(writer.files_);
@@ -159,6 +169,30 @@
   *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
 }
 
+static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
+                              LocalFileHeader* dst) {
+  dst->lfh_signature = LocalFileHeader::kSignature;
+  if (use_data_descriptor) {
+    // Set this flag to denote that a DataDescriptor struct will appear after the data,
+    // containing the crc and size fields.
+    dst->gpb_flags |= kGPBDDFlagMask;
+
+    // The size and crc fields must be 0.
+    dst->compressed_size = 0u;
+    dst->uncompressed_size = 0u;
+    dst->crc32 = 0u;
+  } else {
+    dst->compressed_size = src.compressed_size;
+    dst->uncompressed_size = src.uncompressed_size;
+    dst->crc32 = src.crc32;
+  }
+  dst->compression_method = src.compression_method;
+  dst->last_mod_time = src.last_mod_time;
+  dst->last_mod_date = src.last_mod_date;
+  dst->file_name_length = src.path.size();
+  dst->extra_field_length = src.padding_length;
+}
+
 int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
                                              time_t time, uint32_t alignment) {
   if (state_ != State::kWritingZip) {
@@ -173,66 +207,58 @@
     return kInvalidAlignment;
   }
 
-  current_file_entry_ = {};
-  current_file_entry_.path = path;
-  current_file_entry_.local_file_header_offset = current_offset_;
+  FileEntry file_entry = {};
+  file_entry.local_file_header_offset = current_offset_;
+  file_entry.path = path;
 
-  if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(current_file_entry_.path.data()),
-                        current_file_entry_.path.size())) {
+  if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()),
+                        file_entry.path.size())) {
     return kInvalidEntryName;
   }
 
-  LocalFileHeader header = {};
-  header.lfh_signature = LocalFileHeader::kSignature;
-
-  // Set this flag to denote that a DataDescriptor struct will appear after the data,
-  // containing the crc and size fields.
-  header.gpb_flags |= kGPBDDFlagMask;
-
   if (flags & ZipWriter::kCompress) {
-    current_file_entry_.compression_method = kCompressDeflated;
+    file_entry.compression_method = kCompressDeflated;
 
     int32_t result = PrepareDeflate();
     if (result != kNoError) {
       return result;
     }
   } else {
-    current_file_entry_.compression_method = kCompressStored;
+    file_entry.compression_method = kCompressStored;
   }
-  header.compression_method = current_file_entry_.compression_method;
 
-  ExtractTimeAndDate(time, &current_file_entry_.last_mod_time, &current_file_entry_.last_mod_date);
-  header.last_mod_time = current_file_entry_.last_mod_time;
-  header.last_mod_date = current_file_entry_.last_mod_date;
+  ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
 
-  header.file_name_length = current_file_entry_.path.size();
-
-  off64_t offset = current_offset_ + sizeof(header) + current_file_entry_.path.size();
+  off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
   std::vector<char> zero_padding;
   if (alignment != 0 && (offset & (alignment - 1))) {
     // Pad the extra field so the data will be aligned.
     uint16_t padding = alignment - (offset % alignment);
-    header.extra_field_length = padding;
+    file_entry.padding_length = padding;
     offset += padding;
-    zero_padding.resize(padding);
-    memset(zero_padding.data(), 0, zero_padding.size());
+    zero_padding.resize(padding, 0);
   }
 
+  LocalFileHeader header = {};
+  // Always start expecting a data descriptor. When the data has finished being written,
+  // if it is possible to seek back, the GPB flag will reset and the sizes written.
+  CopyFromFileEntry(file_entry, true /*use_data_descriptor*/, &header);
+
   if (fwrite(&header, sizeof(header), 1, file_) != 1) {
     return HandleError(kIoError);
   }
 
-  if (fwrite(path, sizeof(*path), current_file_entry_.path.size(), file_)
-      != current_file_entry_.path.size()) {
+  if (fwrite(path, sizeof(*path), file_entry.path.size(), file_) != file_entry.path.size()) {
     return HandleError(kIoError);
   }
 
-  if (header.extra_field_length != 0 &&
-      fwrite(zero_padding.data(), 1, header.extra_field_length, file_)
-      != header.extra_field_length) {
+  if (file_entry.padding_length != 0 &&
+      fwrite(zero_padding.data(), 1, file_entry.padding_length, file_)
+      != file_entry.padding_length) {
     return HandleError(kIoError);
   }
 
+  current_file_entry_ = std::move(file_entry);
   current_offset_ = offset;
   state_ = State::kWritingEntry;
   return kNoError;
@@ -405,23 +431,41 @@
     }
   }
 
-  const uint32_t sig = DataDescriptor::kOptSignature;
-  if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
-    state_ = State::kError;
-    return kIoError;
-  }
+  if ((current_file_entry_.compression_method & kCompressDeflated) || !seekable_) {
+    // Some versions of ZIP don't allow STORED data to have a trailing DataDescriptor.
+    // If this file is not seekable, or if the data is compressed, write a DataDescriptor.
+    const uint32_t sig = DataDescriptor::kOptSignature;
+    if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
+      return HandleError(kIoError);
+    }
 
-  DataDescriptor dd = {};
-  dd.crc32 = current_file_entry_.crc32;
-  dd.compressed_size = current_file_entry_.compressed_size;
-  dd.uncompressed_size = current_file_entry_.uncompressed_size;
-  if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
-    return HandleError(kIoError);
+    DataDescriptor dd = {};
+    dd.crc32 = current_file_entry_.crc32;
+    dd.compressed_size = current_file_entry_.compressed_size;
+    dd.uncompressed_size = current_file_entry_.uncompressed_size;
+    if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
+      return HandleError(kIoError);
+    }
+    current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
+  } else {
+    // Seek back to the header and rewrite to include the size.
+    if (fseeko(file_, current_file_entry_.local_file_header_offset, SEEK_SET) != 0) {
+      return HandleError(kIoError);
+    }
+
+    LocalFileHeader header = {};
+    CopyFromFileEntry(current_file_entry_, false /*use_data_descriptor*/, &header);
+
+    if (fwrite(&header, sizeof(header), 1, file_) != 1) {
+      return HandleError(kIoError);
+    }
+
+    if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
+      return HandleError(kIoError);
+    }
   }
 
   files_.emplace_back(std::move(current_file_entry_));
-
-  current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
   state_ = State::kWritingZip;
   return kNoError;
 }
@@ -431,7 +475,7 @@
     return kInvalidState;
   }
 
-  off64_t startOfCdr = current_offset_;
+  off_t startOfCdr = current_offset_;
   for (FileEntry& file : files_) {
     CentralDirectoryRecord cdr = {};
     cdr.record_signature = CentralDirectoryRecord::kSignature;
@@ -443,7 +487,7 @@
     cdr.compressed_size = file.compressed_size;
     cdr.uncompressed_size = file.uncompressed_size;
     cdr.file_name_length = file.path.size();
-    cdr.local_file_header_offset = file.local_file_header_offset;
+    cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset);
     if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
       return HandleError(kIoError);
     }
@@ -473,7 +517,7 @@
   // Since we can BackUp() and potentially finish writing at an offset less than one we had
   // already written at, we must truncate the file.
 
-  if (ftruncate64(fileno(file_), current_offset_) != 0) {
+  if (ftruncate(fileno(file_), current_offset_) != 0) {
     return HandleError(kIoError);
   }
 
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 30f4950..5b526a4 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -64,6 +64,7 @@
   ZipEntry data;
   ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
   EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(0u, data.has_data_descriptor);
   EXPECT_EQ(strlen(expected), data.compressed_length);
   ASSERT_EQ(strlen(expected), data.uncompressed_length);
   ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
diff --git a/logcat/Android.mk b/logcat/Android.mk
index f564f0f..4e11ca9 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -15,6 +15,16 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_MODULE := logcatd
+LOCAL_MODULE_TAGS := debug
+LOCAL_SRC_FILES := logcatd_main.cpp event.logtags
+LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
 LOCAL_MODULE := liblogcat
 LOCAL_SRC_FILES := logcat.cpp getopt_long.cpp logcat_system.cpp
 LOCAL_SHARED_LIBRARIES := $(logcatLibs)
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 909f8e2..9f05351 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -36,6 +36,8 @@
 
 # These are used for testing, do not modify without updating
 # tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+# system/core/liblog/tests/liblog_benchmark.cpp
+# system/core/liblog/tests/liblog_test.cpp
 42    answer (to life the universe etc|3)
 314   pi
 2718  e
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 8134936..64d1d2f 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -882,6 +882,7 @@
           { "grep",          required_argument, nullptr, 'e' },
           // hidden and undocumented reserved alias for --max-count
           { "head",          required_argument, nullptr, 'm' },
+          { "help",          no_argument,       nullptr, 'h' },
           { id_str,          required_argument, nullptr, 0 },
           { "last",          no_argument,       nullptr, 'L' },
           { "max-count",     required_argument, nullptr, 'm' },
@@ -900,9 +901,8 @@
         };
         // clang-format on
 
-        ret = getopt_long_r(argc, argv,
-                            ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
-                            &option_index, &optctx);
+        ret = getopt_long_r(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
+                            long_options, &option_index, &optctx);
         if (ret < 0) break;
 
         switch (ret) {
@@ -1304,6 +1304,11 @@
                              "Option -%c needs an argument\n", optctx.optopt);
                 goto exit;
 
+            case 'h':
+                show_help(context);
+                show_format_help(context);
+                goto exit;
+
             default:
                 logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
                              optctx.optopt);
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index b082a64..06cc90d 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -34,9 +34,6 @@
 on property:logd.logpersistd.enable=true && property:logd.logpersistd=logcatd
     # all exec/services are called with umask(077), so no gain beyond 0700
     mkdir /data/misc/logd 0700 logd log
-    # logd for write to /data/misc/logd, log group for read from pstore (-L)
-    # b/28788401 b/30041146 b/30612424
-    # exec - logd log -- /system/bin/logcat -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
     start logcatd
 
 # stop logcatd service and clear data
@@ -57,7 +54,7 @@
     stop logcatd
 
 # logcatd service
-service logcatd /system/bin/logcat -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
+service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
     class late_start
     disabled
     # logd for write to /data/misc/logd, log group for read from log daemon
diff --git a/logcat/logcatd_main.cpp b/logcat/logcatd_main.cpp
new file mode 100644
index 0000000..9109eb1
--- /dev/null
+++ b/logcat/logcatd_main.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include <log/logcat.h>
+
+int main(int argc, char** argv, char** envp) {
+    android_logcat_context ctx = create_android_logcat();
+    if (!ctx) return -1;
+
+    signal(SIGPIPE, exit);
+
+    // Save and detect presence of -L or --last flag
+    std::vector<std::string> args;
+    bool last = false;
+    for (int i = 0; i < argc; ++i) {
+        if (!argv[i]) continue;
+        args.push_back(std::string(argv[i]));
+        if (!strcmp(argv[i], "-L") || !strcmp(argv[i], "--last")) last = true;
+    }
+
+    // Generate argv from saved content
+    std::vector<const char*> argv_hold;
+    for (auto& str : args) argv_hold.push_back(str.c_str());
+    argv_hold.push_back(nullptr);
+
+    int ret = 0;
+    if (last) {
+        // Run logcat command with -L flag
+        ret = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
+                                         (char* const*)&argv_hold[0], envp);
+        // Remove -L and --last flags from argument list
+        for (std::vector<const char*>::iterator it = argv_hold.begin();
+             it != argv_hold.end();) {
+            if (!*it || (strcmp(*it, "-L") && strcmp(*it, "--last"))) {
+                ++it;
+            } else {
+                it = argv_hold.erase(it);
+            }
+        }
+        // fall through to re-run the command regardless of the arguments
+        // passed in.  For instance, we expect -h to report help stutter.
+    }
+
+    // Run logcat command without -L flag
+    int retval = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
+                                            (char* const*)&argv_hold[0], envp);
+    if (!ret) ret = retval;
+    retval = android_logcat_destroy(&ctx);
+    if (!ret) ret = retval;
+    return ret;
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 22aca17..defd3c4 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -50,6 +50,7 @@
 
 test_src_files := \
     logcat_test.cpp \
+    logcatd_test.cpp \
     liblogcat_test.cpp \
 
 # Build tests for the device (with .so). Run with:
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index a3a0176..21868f2 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/cdefs.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -39,6 +40,10 @@
 #define logcat_pclose(context, fp) pclose(fp)
 #define logcat_system(command) system(command)
 #endif
+#ifndef logcat_executable
+#define USING_LOGCAT_EXECUTABLE_DEFAULT
+#define logcat_executable "logcat"
+#endif
 
 #define BIG_BUFFER (5 * 1024)
 
@@ -64,14 +69,13 @@
 
 #undef LOG_TAG
 #define LOG_TAG "inject"
-    RLOGE("logcat.buckets");
+    RLOGE(logcat_executable ".buckets");
     sleep(1);
 
-    ASSERT_TRUE(
-        NULL !=
-        (fp = logcat_popen(
-             ctx,
-             "logcat -b radio -b events -b system -b main -d 2>/dev/null")));
+    ASSERT_TRUE(NULL !=
+                (fp = logcat_popen(
+                     ctx, logcat_executable
+                     " -b radio -b events -b system -b main -d 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -101,8 +105,8 @@
     logcat_define(ctx);
 
     ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(ctx,
-                                   "logcat -b events -d -s auditd "
+                (fp = logcat_popen(ctx, logcat_executable
+                                   " -b events -d -s auditd "
                                    "am_proc_start am_pss am_proc_bound "
                                    "dvm_lock_sample am_wtf 2>/dev/null")));
 
@@ -170,10 +174,9 @@
 #endif
         strftime(needle, sizeof(needle), "[ %Y-", ptm);
 
-        ASSERT_TRUE(
-            NULL !=
-            (fp = logcat_popen(
-                 ctx, "logcat -v long -v year -b all -t 3 2>/dev/null")));
+        ASSERT_TRUE(NULL != (fp = logcat_popen(
+                                 ctx, logcat_executable
+                                 " -v long -v year -b all -t 3 2>/dev/null")));
 
         char buffer[BIG_BUFFER];
 
@@ -237,8 +240,8 @@
         logcat_define(ctx);
 
         ASSERT_TRUE(NULL !=
-                    (fp = logcat_popen(ctx,
-                                       "logcat -v long -v America/Los_Angeles "
+                    (fp = logcat_popen(ctx, logcat_executable
+                                       " -v long -v America/Los_Angeles "
                                        "-b all -t 3 2>/dev/null")));
 
         char buffer[BIG_BUFFER];
@@ -264,10 +267,9 @@
     FILE* fp;
     logcat_define(ctx);
 
-    ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(ctx,
-                                   "logcat -v long -v America/Los_Angeles -v "
-                                   "zone -b all -t 3 2>/dev/null")));
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, logcat_executable
+                                           " -v long -v America/Los_Angeles -v "
+                                           "zone -b all -t 3 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -435,11 +437,11 @@
 }
 
 TEST(logcat, tail_time) {
-    do_tail_time("logcat -v long -v nsec -b all");
+    do_tail_time(logcat_executable " -v long -v nsec -b all");
 }
 
 TEST(logcat, tail_time_epoch) {
-    do_tail_time("logcat -v long -v nsec -v epoch -b all");
+    do_tail_time(logcat_executable " -v long -v nsec -v epoch -b all");
 }
 
 TEST(logcat, End_to_End) {
@@ -452,8 +454,8 @@
     FILE* fp;
     logcat_define(ctx);
     ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(
-                     ctx, "logcat -v brief -b events -t 100 2>/dev/null")));
+                (fp = logcat_popen(ctx, logcat_executable
+                                   " -v brief -b events -t 100 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -492,8 +494,8 @@
     size_t num = 0;
     do {
         EXPECT_TRUE(NULL !=
-                    (fp[num] = logcat_popen(
-                         ctx[num], "logcat -v brief -b events -t 100")));
+                    (fp[num] = logcat_popen(ctx[num], logcat_executable
+                                            " -v brief -b events -t 100")));
         if (!fp[num]) {
             fprintf(stderr,
                     "WARNING: limiting to %zu simultaneous logcat operations\n",
@@ -604,22 +606,23 @@
 }
 
 TEST(logcat, get_size) {
-    ASSERT_EQ(4, get_groups("logcat -v brief -b radio -b events -b system -b "
+    ASSERT_EQ(4, get_groups(logcat_executable
+                            " -v brief -b radio -b events -b system -b "
                             "main -g 2>/dev/null"));
 }
 
 // duplicate test for get_size, but use comma-separated list of buffers
 TEST(logcat, multiple_buffer) {
     ASSERT_EQ(
-        4, get_groups(
-               "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
+        4, get_groups(logcat_executable
+                      " -v brief -b radio,events,system,main -g 2>/dev/null"));
 }
 
 TEST(logcat, bad_buffer) {
-    ASSERT_EQ(
-        0,
-        get_groups(
-            "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
+    ASSERT_EQ(0,
+              get_groups(
+                  logcat_executable
+                  " -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
 }
 
 #ifndef logcat
@@ -774,8 +777,8 @@
     char buf[sizeof(form)];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(buf, form)));
 
-    static const char comm[] =
-        "logcat -b radio -b events -b system -b main"
+    static const char comm[] = logcat_executable
+        " -b radio -b events -b system -b main"
         " -d -f %s/log.txt -n 7 -r 1";
     char command[sizeof(buf) + sizeof(comm)];
     snprintf(command, sizeof(command), comm, buf);
@@ -820,8 +823,8 @@
     char tmp_out_dir[sizeof(tmp_out_dir_form)];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
 
-    static const char logcat_cmd[] =
-        "logcat -b radio -b events -b system -b main"
+    static const char logcat_cmd[] = logcat_executable
+        " -b radio -b events -b system -b main"
         " -d -f %s/log.txt -n 10 -r 1";
     char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
@@ -880,7 +883,7 @@
 
     static const char log_filename[] = "log.txt";
     static const char logcat_cmd[] =
-        "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024";
+        logcat_executable " -b all -v nsec -d -f %s/%s -n 256 -r 1024";
     static const char cleanup_cmd[] = "rm -rf %s";
     char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
@@ -1005,7 +1008,8 @@
 
     static const char log_filename[] = "log.txt";
     static const unsigned num_val = 32;
-    static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n %d -r 1";
+    static const char logcat_cmd[] =
+        logcat_executable " -b all -d -f %s/%s -n %d -r 1";
     static const char clear_cmd[] = " -c";
     static const char cleanup_cmd[] = "rm -rf %s";
     char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) +
@@ -1109,9 +1113,9 @@
 
 TEST(logcat, logrotate_id) {
     static const char logcat_cmd[] =
-        "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test";
+        logcat_executable " -b all -d -f %s/%s -n 32 -r 1 --id=test";
     static const char logcat_short_cmd[] =
-        "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
+        logcat_executable " -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
     static const char tmp_out_dir_form[] =
         "/data/local/tmp/logcat.logrotate.XXXXXX";
     static const char log_filename[] = "log.txt";
@@ -1155,8 +1159,8 @@
 
 TEST(logcat, logrotate_nodir) {
     // expect logcat to error out on writing content and not exit(0) for nodir
-    static const char command[] =
-        "logcat -b all -d"
+    static const char command[] = logcat_executable
+        " -b all -d"
         " -f /das/nein/gerfingerpoken/logcat/log.txt"
         " -n 256 -r 1024";
     EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
@@ -1292,7 +1296,7 @@
     FILE* fp;
     logcat_define(ctx);
 
-    fp = logcat_popen(ctx, "logcat -p 2>/dev/null");
+    fp = logcat_popen(ctx, logcat_executable " -p 2>/dev/null");
     if (fp == NULL) {
         fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
         return false;
@@ -1330,7 +1334,8 @@
 
     char buffer[BIG_BUFFER];
 
-    snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : "");
+    snprintf(buffer, sizeof(buffer), logcat_executable " -P '%s' 2>&1",
+             list ? list : "");
     fp = logcat_popen(ctx, buffer);
     if (fp == NULL) {
         fprintf(stderr, "ERROR: %s\n", buffer);
@@ -1392,15 +1397,11 @@
     int count = 0;
 
     char buffer[BIG_BUFFER];
-// Have to make liblogcat data unique from logcat data injection
-#ifdef logcat
-#define logcat_regex_prefix "lolcat_test"
-#else
-#define logcat_regex_prefix "logcat_test"
-#endif
+#define logcat_regex_prefix ___STRING(logcat) "_test"
 
     snprintf(buffer, sizeof(buffer),
-             "logcat --pid %d -d -e " logcat_regex_prefix "_a+b", getpid());
+             logcat_executable " --pid %d -d -e " logcat_regex_prefix "_a+b",
+             getpid());
 
     LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
                                           logcat_regex_prefix "_ab"));
@@ -1437,8 +1438,8 @@
 
     char buffer[BIG_BUFFER];
 
-    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3",
-             getpid());
+    snprintf(buffer, sizeof(buffer),
+             logcat_executable " --pid %d -d --max-count 3", getpid());
 
     LOG_FAILURE_RETRY(
         __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
@@ -1663,8 +1664,34 @@
 }
 
 TEST(logcat, security) {
-    EXPECT_FALSE(reportedSecurity("logcat -b all -g 2>&1"));
-    EXPECT_TRUE(reportedSecurity("logcat -b security -g 2>&1"));
-    EXPECT_TRUE(reportedSecurity("logcat -b security -c 2>&1"));
-    EXPECT_TRUE(reportedSecurity("logcat -b security -G 256K 2>&1"));
+    EXPECT_FALSE(reportedSecurity(logcat_executable " -b all -g 2>&1"));
+    EXPECT_TRUE(reportedSecurity(logcat_executable " -b security -g 2>&1"));
+    EXPECT_TRUE(reportedSecurity(logcat_executable " -b security -c 2>&1"));
+    EXPECT_TRUE(
+        reportedSecurity(logcat_executable " -b security -G 256K 2>&1"));
+}
+
+static size_t commandOutputSize(const char* command) {
+    logcat_define(ctx);
+    FILE* fp = logcat_popen(ctx, command);
+    if (!fp) return 0;
+
+    std::string ret;
+    if (!android::base::ReadFdToString(fileno(fp), &ret)) return 0;
+    if (logcat_pclose(ctx, fp) != 0) return 0;
+
+    return ret.size();
+}
+
+TEST(logcat, help) {
+    size_t logcatHelpTextSize = commandOutputSize(logcat_executable " -h 2>&1");
+    EXPECT_LT(4096UL, logcatHelpTextSize);
+    size_t logcatLastHelpTextSize =
+        commandOutputSize(logcat_executable " -L -h 2>&1");
+#ifdef USING_LOGCAT_EXECUTABLE_DEFAULT  // logcat and liblogcat
+    EXPECT_EQ(logcatHelpTextSize, logcatLastHelpTextSize);
+#else
+    // logcatd -L -h prints the help twice, as designed.
+    EXPECT_EQ(logcatHelpTextSize * 2, logcatLastHelpTextSize);
+#endif
 }
diff --git a/bootstat/uptime_parser.h b/logcat/tests/logcatd_test.cpp
similarity index 65%
rename from bootstat/uptime_parser.h
rename to logcat/tests/logcatd_test.cpp
index 756ae9b..bb7534e 100644
--- a/bootstat/uptime_parser.h
+++ b/logcat/tests/logcatd_test.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,16 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef UPTIME_PARSER_H_
-#define UPTIME_PARSER_H_
+#define logcat logcatd
+#define logcat_executable "logcatd"
 
-#include <time.h>
-
-namespace bootstat {
-
-// Returns the number of seconds the system has been on since reboot.
-time_t ParseUptime();
-
-}  // namespace bootstat
-
-#endif  // UPTIME_PARSER_H_
\ No newline at end of file
+#include "logcat_test.cpp"
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 86ea6b4..0c7019a 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -109,11 +109,11 @@
 
 LogBuffer::LogBuffer(LastLogTimes* times)
     : monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
-    pthread_mutex_init(&mLogElementsLock, NULL);
+    pthread_mutex_init(&mLogElementsLock, nullptr);
 
     log_id_for_each(i) {
-        lastLoggedElements[i] = NULL;
-        droppedElements[i] = NULL;
+        lastLoggedElements[i] = nullptr;
+        droppedElements[i] = nullptr;
     }
 
     init();
@@ -196,7 +196,7 @@
         new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
     if (log_id != LOG_ID_SECURITY) {
         int prio = ANDROID_LOG_INFO;
-        const char* tag = NULL;
+        const char* tag = nullptr;
         if (log_id == LOG_ID_EVENTS) {
             tag = tagToName(elem->getTag());
         } else {
@@ -222,24 +222,24 @@
         //
         // State Init
         //     incoming:
-        //         dropped = NULL
-        //         currentLast = NULL;
+        //         dropped = nullptr
+        //         currentLast = nullptr;
         //         elem = incoming message
         //     outgoing:
-        //         dropped = NULL -> State 0
+        //         dropped = nullptr -> State 0
         //         currentLast = copy of elem
         //         log elem
         // State 0
         //     incoming:
         //         count = 0
-        //         dropped = NULL
+        //         dropped = nullptr
         //         currentLast = copy of last message
         //         elem = incoming message
         //     outgoing: if match != DIFFERENT
         //         dropped = copy of first identical message -> State 1
         //         currentLast = reference to elem
         //     break: if match == DIFFERENT
-        //         dropped = NULL -> State 0
+        //         dropped = nullptr -> State 0
         //         delete copy of last message (incoming currentLast)
         //         currentLast = copy of elem
         //         log elem
@@ -266,7 +266,7 @@
         //             currentLast = reference to elem, sum liblog.
         //     break: if match == DIFFERENT
         //         delete dropped
-        //         dropped = NULL -> State 0
+        //         dropped = nullptr -> State 0
         //         log reference to last held-back (currentLast)
         //         currentLast = copy of elem
         //         log elem
@@ -285,7 +285,7 @@
         //         currentLast = reference to elem
         //     break: if match == DIFFERENT
         //         log dropped (chatty message)
-        //         dropped = NULL -> State 0
+        //         dropped = nullptr -> State 0
         //         log reference to last held-back (currentLast)
         //         currentLast = copy of elem
         //         log elem
@@ -350,7 +350,7 @@
             } else {           // State 1
                 delete dropped;
             }
-            droppedElements[log_id] = NULL;
+            droppedElements[log_id] = nullptr;
             log(currentLast);  // report last message in the series
         } else {               // State 0
             delete currentLast;
@@ -654,7 +654,7 @@
 // mLogElementsLock must be held when this function is called.
 //
 bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
-    LogTimeEntry* oldest = NULL;
+    LogTimeEntry* oldest = nullptr;
     bool busy = false;
     bool clearAll = pruneRows == ULONG_MAX;
 
@@ -1076,9 +1076,11 @@
     return retval;
 }
 
-log_time LogBuffer::flushTo(
-    SocketClient* reader, const log_time& start, bool privileged, bool security,
-    int (*filter)(const LogBufferElement* element, void* arg), void* arg) {
+log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start,
+                            pid_t* lastTid, bool privileged, bool security,
+                            int (*filter)(const LogBufferElement* element,
+                                          void* arg),
+                            void* arg) {
     LogBufferElementCollection::iterator it;
     uid_t uid = reader->getUid();
 
@@ -1089,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();
@@ -1099,7 +1103,7 @@
             LogBufferElement* element = *it;
             if (element->getRealTime() > start) {
                 last = it;
-            } else if (element->getRealTime() < min) {
+            } else if (!--count || (element->getRealTime() < min)) {
                 break;
             }
         }
@@ -1107,9 +1111,6 @@
     }
 
     log_time max = start;
-    // Help detect if the valid message before is from the same source so
-    // we can differentiate chatty filter types.
-    pid_t lastTid[LOG_ID_MAX] = { 0 };
 
     for (; it != mLogElements.end(); ++it) {
         LogBufferElement* element = *it;
@@ -1137,14 +1138,17 @@
             }
         }
 
-        bool sameTid = lastTid[element->getLogId()] == element->getTid();
-        // Dropped (chatty) immediately following a valid log from the
-        // same source in the same log buffer indicates we have a
-        // multiple identical squash.  chatty that differs source
-        // is due to spam filter.  chatty to chatty of different
-        // source is also due to spam filter.
-        lastTid[element->getLogId()] =
-            (element->getDropped() && !sameTid) ? 0 : element->getTid();
+        bool sameTid = false;
+        if (lastTid) {
+            sameTid = lastTid[element->getLogId()] == element->getTid();
+            // Dropped (chatty) immediately following a valid log from the
+            // same source in the same log buffer indicates we have a
+            // multiple identical squash.  chatty that differs source
+            // is due to spam filter.  chatty to chatty of different
+            // source is also due to spam filter.
+            lastTid[element->getLogId()] =
+                (element->getDropped() && !sameTid) ? 0 : element->getTid();
+        }
 
         pthread_mutex_unlock(&mLogElementsLock);
 
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index fcf6b9a..19d11cb 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -115,11 +115,15 @@
 
     int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
             const char* msg, unsigned short len);
+    // lastTid is an optional context to help detect if the last previous
+    // valid message was from the same source so we can differentiate chatty
+    // filter types (identical or expired)
     log_time flushTo(SocketClient* writer, const log_time& start,
+                     pid_t* lastTid,  // &lastTid[LOG_ID_MAX] or nullptr
                      bool privileged, bool security,
                      int (*filter)(const LogBufferElement* element,
-                                   void* arg) = NULL,
-                     void* arg = NULL);
+                                   void* arg) = nullptr,
+                     void* arg = nullptr);
 
     bool clear(log_id_t id, uid_t uid = AID_ROOT);
     unsigned long getSize(log_id_t id);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 620d4d0..af19279 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -182,7 +182,7 @@
         } logFindStart(pid, logMask, sequence,
                        logbuf().isMonotonic() && android::isMonotonic(start));
 
-        logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli),
+        logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli),
                          FlushCommand::hasSecurityLogs(cli),
                          logFindStart.callback, &logFindStart);
 
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 04e531f..ccc550a 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <errno.h>
+#include <string.h>
 #include <sys/prctl.h>
 
 #include <private/android_logger.h>
@@ -47,7 +48,8 @@
       mEnd(log_time(android_log_clockid())) {
     mTimeout.tv_sec = timeout / NS_PER_SEC;
     mTimeout.tv_nsec = timeout % NS_PER_SEC;
-    pthread_cond_init(&threadTriggeredCondition, NULL);
+    memset(mLastTid, 0, sizeof(mLastTid));
+    pthread_cond_init(&threadTriggeredCondition, nullptr);
     cleanSkip_Locked();
 }
 
@@ -98,7 +100,7 @@
             it++;
         }
 
-        me->mClient = NULL;
+        me->mClient = nullptr;
         reader.release(client);
     }
 
@@ -122,7 +124,7 @@
     SocketClient* client = me->mClient;
     if (!client) {
         me->error();
-        return NULL;
+        return nullptr;
     }
 
     LogBuffer& logbuf = me->mReader.logbuf();
@@ -151,12 +153,12 @@
         unlock();
 
         if (me->mTail) {
-            logbuf.flushTo(client, start, privileged, security, FilterFirstPass,
-                           me);
+            logbuf.flushTo(client, start, nullptr, privileged, security,
+                           FilterFirstPass, me);
             me->leadingDropped = true;
         }
-        start = logbuf.flushTo(client, start, privileged, security,
-                               FilterSecondPass, me);
+        start = logbuf.flushTo(client, start, me->mLastTid, privileged,
+                               security, FilterSecondPass, me);
 
         lock();
 
@@ -182,7 +184,7 @@
 
     pthread_cleanup_pop(true);
 
-    return NULL;
+    return nullptr;
 }
 
 // A first pass to count the number of elements
@@ -281,7 +283,5 @@
 }
 
 void LogTimeEntry::cleanSkip_Locked(void) {
-    for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t)(i + 1)) {
-        skipAhead[i] = 0;
-    }
+    memset(skipAhead, 0, sizeof(skipAhead));
 }
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 9a3ddab..ec8252e 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -44,6 +44,7 @@
     const unsigned int mLogMask;
     const pid_t mPid;
     unsigned int skipAhead[LOG_ID_MAX];
+    pid_t mLastTid[LOG_ID_MAX];
     unsigned long mCount;
     unsigned long mTail;
     unsigned long mIndex;
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index ddff393..88cb67a 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -426,7 +426,7 @@
                     " BM_log_maximum_retry"
                     " BM_log_maximum"
                     " BM_clock_overhead"
-                    " BM_log_overhead"
+                    " BM_log_print_overhead"
                     " BM_log_latency"
                     " BM_log_delay",
                     "r")));
@@ -434,13 +434,13 @@
     char buffer[5120];
 
     static const char* benchmarks[] = {
-        "BM_log_maximum_retry ", "BM_log_maximum ", "BM_clock_overhead ",
-        "BM_log_overhead ",      "BM_log_latency ", "BM_log_delay "
+        "BM_log_maximum_retry ",  "BM_log_maximum ", "BM_clock_overhead ",
+        "BM_log_print_overhead ", "BM_log_latency ", "BM_log_delay "
     };
     static const unsigned int log_maximum_retry = 0;
     static const unsigned int log_maximum = 1;
     static const unsigned int clock_overhead = 2;
-    static const unsigned int log_overhead = 3;
+    static const unsigned int log_print_overhead = 3;
     static const unsigned int log_latency = 4;
     static const unsigned int log_delay = 5;
 
@@ -469,21 +469,23 @@
     }
 
     EXPECT_GE(200000UL, ns[log_maximum_retry]);  // 104734 user
+    EXPECT_NE(0UL, ns[log_maximum_retry]);       // failure to parse
 
     EXPECT_GE(90000UL, ns[log_maximum]);  // 46913 user
+    EXPECT_NE(0UL, ns[log_maximum]);      // failure to parse
 
     EXPECT_GE(4096UL, ns[clock_overhead]);  // 4095
+    EXPECT_NE(0UL, ns[clock_overhead]);     // failure to parse
 
-    EXPECT_GE(250000UL, ns[log_overhead]);  // 126886 user
+    EXPECT_GE(250000UL, ns[log_print_overhead]);  // 126886 user
+    EXPECT_NE(0UL, ns[log_print_overhead]);       // failure to parse
 
     EXPECT_GE(10000000UL,
               ns[log_latency]);  // 1453559 user space (background cgroup)
+    EXPECT_NE(0UL, ns[log_latency]);  // failure to parse
 
     EXPECT_GE(20000000UL, ns[log_delay]);  // 10500289 user
-
-    for (unsigned i = 0; i < arraysize(ns); ++i) {
-        EXPECT_NE(0UL, ns[i]);
-    }
+    EXPECT_NE(0UL, ns[log_delay]);         // failure to parse
 
     alloc_statistics(&buf, &len);
 
@@ -997,16 +999,18 @@
     }
 
     // We may have DAC, but let's not have MAC
-    if (setcon("u:object_r:shell:s0") < 0) {
+    if ((setcon("u:object_r:shell:s0") < 0) && (setcon("u:r:shell:s0") < 0)) {
         int save_errno = errno;
         security_context_t context;
         getcon(&context);
-        fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n", context,
-                strerror(save_errno));
-        freecon(context);
-        _exit(-1);
-        // NOTREACHED
-        return 0;
+        if (strcmp(context, "u:r:shell:s0")) {
+            fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
+                    context, strerror(save_errno));
+            freecon(context);
+            _exit(-1);
+            // NOTREACHED
+            return 0;
+        }
     }
 
     // The key here is we are root, but we are in u:r:shell:s0,
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;
-}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 7091ab9..ff96c14 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -301,6 +301,9 @@
     start logd
     start hwservicemanager
 
+    # HALs required before data is mounted
+    class_start early_hal
+
     # once everything is setup, no need to modify /
     mount rootfs rootfs / ro remount
     # Mount shared so changes propagate into child namespaces
@@ -598,7 +601,7 @@
 
 on nonencrypted
     # A/B update verifier that marks a successful boot.
-    exec - root cache -- /system/bin/update_verifier nonencrypted
+    exec_start update_verifier_nonencrypted
     class_start main
     class_start late_start
 
@@ -621,12 +624,12 @@
 
 on property:vold.decrypt=trigger_restart_min_framework
     # A/B update verifier that marks a successful boot.
-    exec - root cache -- /system/bin/update_verifier trigger_restart_min_framework
+    exec_start update_verifier
     class_start main
 
 on property:vold.decrypt=trigger_restart_framework
     # A/B update verifier that marks a successful boot.
-    exec - root cache -- /system/bin/update_verifier trigger_restart_framework
+    exec_start update_verifier
     class_start main
     class_start late_start
 
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
new file mode 100644
index 0000000..81cf315
--- /dev/null
+++ b/shell_and_utilities/Android.bp
@@ -0,0 +1,13 @@
+phony {
+    name: "shell_and_utilities",
+    required: [
+        "bzip2",
+        "grep",
+        "gzip",
+        "mkshrc",
+        "reboot",
+        "sh",
+        "toolbox",
+        "toybox",
+    ],
+}