Merge "Remove VNDK warning." into oc-dev
am: ca243f2fcb

Change-Id: I19e120f593dc4a6ee7cea76b78a676ee3f71fb70
diff --git a/.clang-format-2 b/.clang-format-2
index aab4665..41591ce 100644
--- a/.clang-format-2
+++ b/.clang-format-2
@@ -1,4 +1,5 @@
 BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Inline
 ColumnLimit: 100
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
diff --git a/.clang-format-4 b/.clang-format-4
index 1497447..ae4a451 100644
--- a/.clang-format-4
+++ b/.clang-format-4
@@ -1,5 +1,6 @@
 BasedOnStyle: Google
 AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
 ColumnLimit: 100
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
diff --git a/adb/Android.mk b/adb/Android.mk
index e841205..d17b063 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -81,12 +81,14 @@
 
 LIBADB_darwin_SRC_FILES := \
     sysdeps_unix.cpp \
+    sysdeps/posix/network.cpp \
     client/usb_dispatch.cpp \
     client/usb_libusb.cpp \
     client/usb_osx.cpp \
 
 LIBADB_linux_SRC_FILES := \
     sysdeps_unix.cpp \
+    sysdeps/posix/network.cpp \
     client/usb_dispatch.cpp \
     client/usb_libusb.cpp \
     client/usb_linux.cpp \
@@ -123,6 +125,7 @@
     $(LIBADB_SRC_FILES) \
     adbd_auth.cpp \
     jdwp_service.cpp \
+    sysdeps/posix/network.cpp \
 
 LOCAL_SANITIZE := $(adb_target_sanitize)
 
@@ -217,9 +220,9 @@
 LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
 LOCAL_SRC_FILES_windows := $(LIBADB_TEST_windows_SRCS)
 LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_SHARED_LIBRARIES := libbase
 LOCAL_STATIC_LIBRARIES := \
     libadb \
+    libbase \
     libcrypto_utils \
     libcrypto \
     libcutils \
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/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 13b7674..3a45dbd 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -574,7 +574,7 @@
     register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
 }
 
-static void device_poll_thread(void*) {
+static void device_poll_thread() {
     adb_thread_setname("device poll");
     D("Created device thread");
     while (true) {
@@ -593,8 +593,6 @@
     actions.sa_handler = [](int) {};
     sigaction(SIGALRM, &actions, nullptr);
 
-    if (!adb_thread_create(device_poll_thread, nullptr)) {
-        fatal_errno("cannot create device_poll thread");
-    }
+    std::thread(device_poll_thread).detach();
 }
 } // namespace native
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index d4fc7c0..8713b2c 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -405,7 +405,7 @@
 
 std::mutex& operate_device_lock = *new std::mutex();
 
-static void RunLoopThread(void* unused) {
+static void RunLoopThread() {
     adb_thread_setname("RunLoop");
 
     VLOG(USB) << "RunLoopThread started";
@@ -436,9 +436,7 @@
 
         usb_inited_flag = false;
 
-        if (!adb_thread_create(RunLoopThread, nullptr)) {
-            fatal_errno("cannot create RunLoop thread");
-        }
+        std::thread(RunLoopThread).detach();
 
         // Wait for initialization to finish
         while (!usb_inited_flag) {
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 640e91e..9e00a5d 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -103,7 +103,7 @@
 
 /// Entry point for thread that polls (every second) for new usb interfaces.
 /// This routine calls find_devices in infinite loop.
-static void device_poll_thread(void*);
+static void device_poll_thread();
 
 /// Initializes this module
 void usb_init();
@@ -174,7 +174,7 @@
   return 1;
 }
 
-void device_poll_thread(void*) {
+void device_poll_thread() {
   adb_thread_setname("Device Poll");
   D("Created device thread");
 
@@ -203,7 +203,7 @@
   return DefWindowProcW(hwnd, uMsg, wParam, lParam);
 }
 
-static void _power_notification_thread(void*) {
+static void _power_notification_thread() {
   // This uses a thread with its own window message pump to get power
   // notifications. If adb runs from a non-interactive service account, this
   // might not work (not sure). If that happens to not work, we could use
@@ -258,12 +258,8 @@
 }
 
 void usb_init() {
-  if (!adb_thread_create(device_poll_thread, nullptr)) {
-    fatal_errno("cannot create device poll thread");
-  }
-  if (!adb_thread_create(_power_notification_thread, nullptr)) {
-    fatal_errno("cannot create power notification thread");
-  }
+  std::thread(device_poll_thread).detach();
+  std::thread(_power_notification_thread).detach();
 }
 
 usb_handle* do_usb_open(const wchar_t* interface_name) {
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 4979eef..7702b0e 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -169,15 +169,7 @@
         "     '-k': keep the data and cache directories\n"
         "\n"
         "backup/restore:\n"
-        " backup [-f FILE] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] [PACKAGE...]\n"
-        "     write an archive of the device's data to FILE [default=backup.adb]\n"
-        "     package list optional if -all/-shared are supplied\n"
-        "     -apk/-noapk: do/don't back up .apk files (default -noapk)\n"
-        "     -obb/-noobb: do/don't back up .obb files (default -noobb)\n"
-        "     -shared|-noshared: do/don't back up shared storage (default -noshared)\n"
-        "     -all: back up all installed applications\n"
-        "     -system|-nosystem: include system apps in -all (default -system)\n"
-        " restore FILE             restore device contents from FILE\n"
+        "   to show usage run \"adb shell bu help\"\n"
         "\n"
         "debugging:\n"
         " bugreport [PATH]\n"
@@ -663,13 +655,8 @@
 #endif
 
     // TODO: combine read_and_dump with stdin_read_thread to make life simpler?
-    int exit_code = 1;
-    if (!adb_thread_create(stdin_read_thread_loop, args)) {
-        PLOG(ERROR) << "error starting stdin read thread";
-        delete args;
-    } else {
-        exit_code = read_and_dump(fd, use_shell_protocol);
-    }
+    std::thread(stdin_read_thread_loop, args).detach();
+    int exit_code = read_and_dump(fd, use_shell_protocol);
 
     // TODO: properly exit stdin_read_thread_loop and close |fd|.
 
diff --git a/adb/daemon/mdns.cpp b/adb/daemon/mdns.cpp
index 7811143..849378f 100644
--- a/adb/daemon/mdns.cpp
+++ b/adb/daemon/mdns.cpp
@@ -17,12 +17,14 @@
 #include "adb_mdns.h"
 #include "sysdeps.h"
 
-#include <chrono>
 #include <dns_sd.h>
 #include <endian.h>
-#include <mutex>
 #include <unistd.h>
 
+#include <chrono>
+#include <mutex>
+#include <thread>
+
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 
@@ -58,7 +60,7 @@
     }
 }
 
-static void setup_mdns_thread(void* /* unused */) {
+static void setup_mdns_thread() {
     start_mdns();
     std::lock_guard<std::mutex> lock(mdns_lock);
 
@@ -88,7 +90,7 @@
 
 void setup_mdns(int port_in) {
     port = port_in;
-    adb_thread_create(setup_mdns_thread, nullptr, nullptr);
+    std::thread(setup_mdns_thread).detach();
 
     // TODO: Make this more robust against a hard kill.
     atexit(teardown_mdns);
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 92e9039..7e46b02 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -451,9 +451,7 @@
     h->close = usb_ffs_close;
 
     D("[ usb_init - starting thread ]");
-    if (!adb_thread_create(usb_ffs_open_thread, h)) {
-        fatal_errno("[ cannot create usb thread ]\n");
-    }
+    std::thread(usb_ffs_open_thread, h).detach();
 }
 
 void usb_init() {
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index c933ed5..bdb973a 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -21,6 +21,7 @@
 #include <limits>
 #include <queue>
 #include <string>
+#include <thread>
 #include <vector>
 
 #include "adb_io.h"
@@ -77,9 +78,9 @@
 };
 
 TEST_F(FdeventTest, fdevent_terminate) {
-    adb_thread_t thread;
     PrepareThread();
-    ASSERT_TRUE(adb_thread_create([](void*) { fdevent_loop(); }, nullptr, &thread));
+
+    std::thread thread(fdevent_loop);
     TerminateThread(thread);
 }
 
@@ -112,7 +113,6 @@
     int fd_pair2[2];
     ASSERT_EQ(0, adb_socketpair(fd_pair1));
     ASSERT_EQ(0, adb_socketpair(fd_pair2));
-    adb_thread_t thread;
     ThreadArg thread_arg;
     thread_arg.first_read_fd = fd_pair1[0];
     thread_arg.last_write_fd = fd_pair2[1];
@@ -121,8 +121,7 @@
     int reader = fd_pair2[0];
 
     PrepareThread();
-    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(FdEventThreadFunc), &thread_arg,
-                                  &thread));
+    std::thread thread(FdEventThreadFunc, &thread_arg);
 
     for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
         std::string read_buffer = MESSAGE;
@@ -152,7 +151,7 @@
     }
 }
 
-static void InvalidFdThreadFunc(void*) {
+static void InvalidFdThreadFunc() {
     const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
     size_t happened_event_count = 0;
     InvalidFdArg read_arg;
@@ -171,7 +170,6 @@
 }
 
 TEST_F(FdeventTest, invalid_fd) {
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(InvalidFdThreadFunc, nullptr, &thread));
-    ASSERT_TRUE(adb_thread_join(thread));
+    std::thread thread(InvalidFdThreadFunc);
+    thread.join();
 }
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index ef65b74..f4215ae 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -16,6 +16,8 @@
 
 #include <gtest/gtest.h>
 
+#include <thread>
+
 #include "socket.h"
 #include "sysdeps.h"
 
@@ -59,10 +61,10 @@
 #endif
     }
 
-    void TerminateThread(adb_thread_t thread) {
+    void TerminateThread(std::thread& thread) {
         fdevent_terminate_loop();
         ASSERT_TRUE(WriteFdExactly(dummy, "", 1));
-        ASSERT_TRUE(adb_thread_join(thread));
+        thread.join();
         ASSERT_EQ(0, adb_close(dummy));
     }
 };
diff --git a/adb/services.cpp b/adb/services.cpp
index c9ea039..4327044 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -31,6 +31,8 @@
 #include <unistd.h>
 #endif
 
+#include <thread>
+
 #include <android-base/file.h>
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
@@ -259,13 +261,7 @@
     sti->cookie = cookie;
     sti->fd = s[1];
 
-    if (!adb_thread_create(service_bootstrap_func, sti)) {
-        free(sti);
-        adb_close(s[0]);
-        adb_close(s[1]);
-        printf("cannot create service thread\n");
-        return -1;
-    }
+    std::thread(service_bootstrap_func, sti).detach();
 
     D("service thread started, %d:%d",s[0], s[1]);
     return s[0];
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index d4f334b..ee821f8 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -90,6 +90,7 @@
 
 #include <memory>
 #include <string>
+#include <thread>
 #include <unordered_map>
 #include <vector>
 
@@ -392,12 +393,7 @@
 
 bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
     Subprocess* raw = subprocess.release();
-    if (!adb_thread_create(ThreadHandler, raw)) {
-        *error =
-            android::base::StringPrintf("failed to create subprocess thread: %s", strerror(errno));
-        kill(raw->pid_, SIGKILL);
-        return false;
-    }
+    std::thread(ThreadHandler, raw).detach();
 
     return true;
 }
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index f56f7f7..f7c66db 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -42,10 +42,6 @@
 
 class LocalSocketTest : public FdeventTest {};
 
-static void FdEventThreadFunc(void*) {
-    fdevent_loop();
-}
-
 constexpr auto SLEEP_FOR_FDEVENT = 100ms;
 
 TEST_F(LocalSocketTest, smoke) {
@@ -88,8 +84,7 @@
     connect(prev_tail, end);
 
     PrepareThread();
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(FdEventThreadFunc, nullptr, &thread));
+    std::thread thread(fdevent_loop);
 
     for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
         std::string read_buffer = MESSAGE;
@@ -152,9 +147,7 @@
     arg.cause_close_fd = cause_close_fd[1];
 
     PrepareThread();
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
-                                  &arg, &thread));
+    std::thread thread(CloseWithPacketThreadFunc, &arg);
     // Wait until the fdevent_loop() starts.
     std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
     ASSERT_EQ(0, adb_close(cause_close_fd[0]));
@@ -177,9 +170,7 @@
     arg.cause_close_fd = cause_close_fd[1];
 
     PrepareThread();
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
-                                  &arg, &thread));
+    std::thread thread(CloseWithPacketThreadFunc, &arg);
     // Wait until the fdevent_loop() starts.
     std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
     ASSERT_EQ(0, adb_close(cause_close_fd[0]));
@@ -211,10 +202,7 @@
     arg.cause_close_fd = cause_close_fd[1];
 
     PrepareThread();
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
-                                  &arg, &thread));
-
+    std::thread thread(CloseWithPacketThreadFunc, &arg);
     // Wait until the fdevent_loop() starts.
     std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
     EXPECT_EQ(2u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
@@ -252,9 +240,7 @@
     int listen_fd = network_inaddr_any_server(5038, SOCK_STREAM, &error);
     ASSERT_GE(listen_fd, 0);
 
-    adb_thread_t client_thread;
-    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(ClientThreadFunc), nullptr,
-                                  &client_thread));
+    std::thread client_thread(ClientThreadFunc);
 
     int accept_fd = adb_socket_accept(listen_fd, nullptr, nullptr);
     ASSERT_GE(accept_fd, 0);
@@ -262,16 +248,14 @@
     arg.socket_fd = accept_fd;
 
     PrepareThread();
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseRdHupSocketThreadFunc),
-                                  &arg, &thread));
+    std::thread thread(CloseRdHupSocketThreadFunc, &arg);
 
     // Wait until the fdevent_loop() starts.
     std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
     EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
 
     // Wait until the client closes its socket.
-    ASSERT_TRUE(adb_thread_join(client_thread));
+    client_thread.join();
 
     std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index f195b4e..49c7847 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -35,6 +35,7 @@
 #include <android-base/utf8.h>
 
 #include "sysdeps/errno.h"
+#include "sysdeps/network.h"
 #include "sysdeps/stat.h"
 
 /*
@@ -98,64 +99,6 @@
     return c == '\\' || c == '/';
 }
 
-typedef void (*adb_thread_func_t)(void* arg);
-typedef HANDLE adb_thread_t;
-
-struct adb_winthread_args {
-    adb_thread_func_t func;
-    void* arg;
-};
-
-static unsigned __stdcall adb_winthread_wrapper(void* heap_args) {
-    // Move the arguments from the heap onto the thread's stack.
-    adb_winthread_args thread_args = *static_cast<adb_winthread_args*>(heap_args);
-    delete static_cast<adb_winthread_args*>(heap_args);
-    thread_args.func(thread_args.arg);
-    return 0;
-}
-
-static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg,
-                                         adb_thread_t* thread = nullptr) {
-    adb_winthread_args* args = new adb_winthread_args{.func = func, .arg = arg};
-    uintptr_t handle = _beginthreadex(nullptr, 0, adb_winthread_wrapper, args, 0, nullptr);
-    if (handle != static_cast<uintptr_t>(0)) {
-        if (thread) {
-            *thread = reinterpret_cast<HANDLE>(handle);
-        } else {
-            CloseHandle(thread);
-        }
-        return true;
-    }
-    return false;
-}
-
-static __inline__ bool adb_thread_join(adb_thread_t thread) {
-    switch (WaitForSingleObject(thread, INFINITE)) {
-        case WAIT_OBJECT_0:
-            CloseHandle(thread);
-            return true;
-
-        case WAIT_FAILED:
-            fprintf(stderr, "adb_thread_join failed: %s\n",
-                    android::base::SystemErrorCodeToString(GetLastError()).c_str());
-            break;
-
-        default:
-            abort();
-    }
-
-    return false;
-}
-
-static __inline__ bool adb_thread_detach(adb_thread_t thread) {
-    CloseHandle(thread);
-    return true;
-}
-
-static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
-    _endthreadex(0);
-}
-
 static __inline__ int adb_thread_setname(const std::string& name) {
     // TODO: See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for how to set
     // the thread name in Windows. Unfortunately, it only works during debugging, but
@@ -163,14 +106,6 @@
     return 0;
 }
 
-static __inline__ adb_thread_t adb_thread_self() {
-    return GetCurrentThread();
-}
-
-static __inline__ bool adb_thread_equal(adb_thread_t lhs, adb_thread_t rhs) {
-    return GetThreadId(lhs) == GetThreadId(rhs);
-}
-
 static __inline__  unsigned long adb_thread_id()
 {
     return GetCurrentThreadId();
@@ -248,8 +183,6 @@
 int unix_isatty(int fd);
 #define  isatty  ___xxx_isatty
 
-int network_loopback_client(int port, int type, std::string* error);
-int network_loopback_server(int port, int type, std::string* error);
 int network_inaddr_any_server(int port, int type, std::string* error);
 
 inline int network_local_client(const char* name, int namespace_id, int type, std::string* error) {
@@ -268,10 +201,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);
 
@@ -591,17 +520,6 @@
   return fd;
 }
 
-inline int network_loopback_client(int port, int type, std::string* error) {
-  return _fd_set_error_str(socket_network_client("localhost", port, type), error);
-}
-
-inline int network_loopback_server(int port, int type, std::string* error) {
-  int fd = socket_loopback_server(port, type);
-  if (fd < 0 && errno == EAFNOSUPPORT)
-      return _fd_set_error_str(socket_loopback_server6(port, type), error);
-  return _fd_set_error_str(fd, error);
-}
-
 inline int network_inaddr_any_server(int port, int type, std::string* error) {
   return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
 }
@@ -660,55 +578,6 @@
 #define  unix_write  adb_write
 #define  unix_close  adb_close
 
-// Win32 is limited to DWORDs for thread return values; limit the POSIX systems to this as well to
-// ensure compatibility.
-typedef void (*adb_thread_func_t)(void* arg);
-typedef pthread_t adb_thread_t;
-
-struct adb_pthread_args {
-    adb_thread_func_t func;
-    void* arg;
-};
-
-static void* adb_pthread_wrapper(void* heap_args) {
-    // Move the arguments from the heap onto the thread's stack.
-    adb_pthread_args thread_args = *reinterpret_cast<adb_pthread_args*>(heap_args);
-    delete static_cast<adb_pthread_args*>(heap_args);
-    thread_args.func(thread_args.arg);
-    return nullptr;
-}
-
-static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg,
-                                         adb_thread_t* thread = nullptr) {
-    pthread_t temp;
-    pthread_attr_t attr;
-    pthread_attr_init(&attr);
-    pthread_attr_setdetachstate(&attr, thread ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED);
-    auto* pthread_args = new adb_pthread_args{.func = start, .arg = arg};
-    errno = pthread_create(&temp, &attr, adb_pthread_wrapper, pthread_args);
-    if (errno == 0) {
-        if (thread) {
-            *thread = temp;
-        }
-        return true;
-    }
-    return false;
-}
-
-static __inline__ bool adb_thread_join(adb_thread_t thread) {
-    errno = pthread_join(thread, nullptr);
-    return errno == 0;
-}
-
-static __inline__ bool adb_thread_detach(adb_thread_t thread) {
-    errno = pthread_detach(thread);
-    return errno == 0;
-}
-
-static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
-    pthread_exit(nullptr);
-}
-
 static __inline__ int adb_thread_setname(const std::string& name) {
 #ifdef __APPLE__
     return pthread_setname_np(name.c_str());
diff --git a/bootstat/uptime_parser.h b/adb/sysdeps/network.h
similarity index 65%
rename from bootstat/uptime_parser.h
rename to adb/sysdeps/network.h
index 756ae9b..83ce371 100644
--- a/bootstat/uptime_parser.h
+++ b/adb/sysdeps/network.h
@@ -1,5 +1,7 @@
+#pragma once
+
 /*
- * 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 +16,7 @@
  * limitations under the License.
  */
 
-#ifndef UPTIME_PARSER_H_
-#define UPTIME_PARSER_H_
+#include <string>
 
-#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
+int network_loopback_client(int port, int type, std::string* error);
+int network_loopback_server(int port, int type, std::string* error);
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
new file mode 100644
index 0000000..45da5af
--- /dev/null
+++ b/adb/sysdeps/posix/network.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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 "sysdeps/network.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include <string>
+
+#include "adb_unique_fd.h"
+
+static void set_error(std::string* error) {
+    if (error) {
+        *error = strerror(errno);
+    }
+}
+
+static sockaddr* loopback_addr4(sockaddr_storage* addr, socklen_t* addrlen, int port) {
+    struct sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(addr);
+    *addrlen = sizeof(*addr4);
+
+    addr4->sin_family = AF_INET;
+    addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    addr4->sin_port = htons(port);
+    return reinterpret_cast<sockaddr*>(addr);
+}
+
+static sockaddr* loopback_addr6(sockaddr_storage* addr, socklen_t* addrlen, int port) {
+    struct sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(addr);
+    *addrlen = sizeof(*addr6);
+
+    addr6->sin6_family = AF_INET6;
+    addr6->sin6_addr = in6addr_loopback;
+    addr6->sin6_port = htons(port);
+    return reinterpret_cast<sockaddr*>(addr);
+}
+
+static int _network_loopback_client(bool ipv6, int port, int type, std::string* error) {
+    unique_fd s(socket(ipv6 ? AF_INET6 : AF_INET, type, 0));
+    if (s == -1) {
+        set_error(error);
+        return -1;
+    }
+
+    struct sockaddr_storage addr_storage = {};
+    socklen_t addrlen = sizeof(addr_storage);
+    sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, 0);
+
+    if (bind(s.get(), addr, addrlen) != 0) {
+        set_error(error);
+        return -1;
+    }
+
+    addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);
+
+    if (connect(s.get(), addr, addrlen) != 0) {
+        set_error(error);
+        return -1;
+    }
+
+    return s.release();
+}
+
+int network_loopback_client(int port, int type, std::string* error) {
+    // Try IPv4 first, use IPv6 as a fallback.
+    int rc = _network_loopback_client(false, port, type, error);
+    if (rc == -1) {
+        return _network_loopback_client(true, port, type, error);
+    }
+    return rc;
+}
+
+static int _network_loopback_server(bool ipv6, int port, int type, std::string* error) {
+    unique_fd s(socket(ipv6 ? AF_INET6 : AF_INET, type, 0));
+    if (s == -1) {
+        set_error(error);
+        return -1;
+    }
+
+    int n = 1;
+    setsockopt(s.get(), SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+    struct sockaddr_storage addr_storage = {};
+    socklen_t addrlen = sizeof(addr_storage);
+    sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);
+
+    if (bind(s, addr, addrlen) != 0) {
+        set_error(error);
+        return -1;
+    }
+
+    if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
+        // Arbitrarily selected value, ported from libcutils.
+        if (listen(s, 4) != 0) {
+            set_error(error);
+            return -1;
+        }
+    }
+
+    return s.release();
+}
+
+int network_loopback_server(int port, int type, std::string* error) {
+    int rc = _network_loopback_server(false, port, type, error);
+
+    // Only attempt to listen on IPv6 if IPv4 is unavailable.
+    // We don't want to start an IPv6 server if there's already an IPv4 one running.
+    if (rc == -1 && (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT)) {
+        return _network_loopback_server(true, port, type, error);
+    }
+    return rc;
+}
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
index 9007e75..edb0fb3 100644
--- a/adb/sysdeps_test.cpp
+++ b/adb/sysdeps_test.cpp
@@ -25,54 +25,6 @@
 #include "sysdeps.h"
 #include "sysdeps/chrono.h"
 
-static void increment_atomic_int(void* c) {
-    std::this_thread::sleep_for(1s);
-    reinterpret_cast<std::atomic<int>*>(c)->fetch_add(1);
-}
-
-TEST(sysdeps_thread, smoke) {
-    std::atomic<int> counter(0);
-
-    for (int i = 0; i < 100; ++i) {
-        ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter));
-    }
-
-    std::this_thread::sleep_for(2s);
-    ASSERT_EQ(100, counter.load());
-}
-
-TEST(sysdeps_thread, join) {
-    std::atomic<int> counter(0);
-    std::vector<adb_thread_t> threads(500);
-    for (size_t i = 0; i < threads.size(); ++i) {
-        ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter, &threads[i]));
-    }
-
-    int current = counter.load();
-    ASSERT_GE(current, 0);
-    // Make sure that adb_thread_create actually creates threads, and doesn't do something silly
-    // like synchronously run the function passed in. The sleep in increment_atomic_int should be
-    // enough to keep this from being flakey.
-    ASSERT_LT(current, 500);
-
-    for (const auto& thread : threads) {
-        ASSERT_TRUE(adb_thread_join(thread));
-    }
-
-    ASSERT_EQ(500, counter.load());
-}
-
-TEST(sysdeps_thread, exit) {
-    adb_thread_t thread;
-    ASSERT_TRUE(adb_thread_create(
-        [](void*) {
-            adb_thread_exit();
-            for (;;) continue;
-        },
-        nullptr, &thread));
-    ASSERT_TRUE(adb_thread_join(thread));
-}
-
 TEST(sysdeps_socketpair, smoke) {
     int fds[2];
     ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
@@ -254,13 +206,13 @@
     static std::mutex &m = *new std::mutex();
     m.lock();
     ASSERT_FALSE(m.try_lock());
-    adb_thread_create([](void*) {
+    std::thread thread([]() {
         ASSERT_FALSE(m.try_lock());
         m.lock();
         finished.store(true);
         std::this_thread::sleep_for(200ms);
         m.unlock();
-    }, nullptr);
+    });
 
     ASSERT_FALSE(finished.load());
     std::this_thread::sleep_for(100ms);
@@ -270,6 +222,8 @@
     m.lock();
     ASSERT_TRUE(finished.load());
     m.unlock();
+
+    thread.join();
 }
 
 TEST(sysdeps_mutex, recursive_mutex_smoke) {
@@ -279,12 +233,12 @@
     ASSERT_TRUE(m.try_lock());
     m.unlock();
 
-    adb_thread_create([](void*) {
+    std::thread thread([]() {
         ASSERT_FALSE(m.try_lock());
         m.lock();
         std::this_thread::sleep_for(500ms);
         m.unlock();
-    }, nullptr);
+    });
 
     std::this_thread::sleep_for(100ms);
     m.unlock();
@@ -292,6 +246,8 @@
     ASSERT_FALSE(m.try_lock());
     m.lock();
     m.unlock();
+
+    thread.join();
 }
 
 TEST(sysdeps_condition_variable, smoke) {
@@ -300,14 +256,16 @@
     static volatile bool flag = false;
 
     std::unique_lock<std::mutex> lock(m);
-    adb_thread_create([](void*) {
+    std::thread thread([]() {
         m.lock();
         flag = true;
         cond.notify_one();
         m.unlock();
-    }, nullptr);
+    });
 
     while (!flag) {
         cond.wait(lock);
     }
+
+    thread.join();
 }
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.cpp b/adb/transport.cpp
index c951f5b..4686841 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -29,6 +29,7 @@
 #include <algorithm>
 #include <list>
 #include <mutex>
+#include <thread>
 
 #include <android-base/logging.h>
 #include <android-base/parsenetaddress.h>
@@ -509,13 +510,8 @@
 
         fdevent_set(&(t->transport_fde), FDE_READ);
 
-        if (!adb_thread_create(write_transport_thread, t)) {
-            fatal_errno("cannot create write_transport thread");
-        }
-
-        if (!adb_thread_create(read_transport_thread, t)) {
-            fatal_errno("cannot create read_transport thread");
-        }
+        std::thread(write_transport_thread, t).detach();
+        std::thread(read_transport_thread, t).detach();
     }
 
     {
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 4198a52..408f51f 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -199,7 +199,7 @@
 std::mutex &retry_ports_lock = *new std::mutex;
 std::condition_variable &retry_ports_cond = *new std::condition_variable;
 
-static void client_socket_thread(void* x) {
+static void client_socket_thread(int) {
     adb_thread_setname("client_socket_thread");
     D("transport: client_socket_thread() starting");
     PollAllLocalPortsForEmulator();
@@ -244,9 +244,8 @@
 
 #else // ADB_HOST
 
-static void server_socket_thread(void* arg) {
+static void server_socket_thread(int port) {
     int serverfd, fd;
-    int port = (int) (uintptr_t) arg;
 
     adb_thread_setname("server socket");
     D("transport: server_socket_thread() starting");
@@ -325,7 +324,7 @@
  *   the transport registration is completed. That's why we need to send the
  *   'start' request after the transport is registered.
  */
-static void qemu_socket_thread(void* arg) {
+static void qemu_socket_thread(int port) {
     /* 'accept' request to the adb QEMUD service. */
     static const char _accept_req[] = "accept";
     /* 'start' request to the adb QEMUD service. */
@@ -333,7 +332,6 @@
     /* 'ok' reply from the adb QEMUD service. */
     static const char _ok_resp[] = "ok";
 
-    const int port = (int) (uintptr_t) arg;
     int fd;
     char tmp[256];
     char con_name[32];
@@ -350,7 +348,7 @@
         /* This could be an older version of the emulator, that doesn't
          * implement adb QEMUD service. Fall back to the old TCP way. */
         D("adb service is not available. Falling back to TCP socket.");
-        adb_thread_create(server_socket_thread, arg);
+        std::thread(server_socket_thread, port).detach();
         return;
     }
 
@@ -394,7 +392,7 @@
 
 void local_init(int port)
 {
-    adb_thread_func_t func;
+    void (*func)(int);
     const char* debug_name = "";
 
 #if ADB_HOST
@@ -414,9 +412,7 @@
 #endif // !ADB_HOST
 
     D("transport: local %s init", debug_name);
-    if (!adb_thread_create(func, (void *) (uintptr_t) port)) {
-        fatal_errno("cannot create local socket %s thread", debug_name);
-    }
+    std::thread(func, port).detach();
 }
 
 static void remote_kick(atransport *t)
diff --git a/base/Android.bp b/base/Android.bp
index a8dda5d..afb7dd3 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -64,24 +64,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: [
@@ -108,17 +120,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/properties.h b/base/include/android-base/properties.h
index 4de5e57..041586c 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -62,15 +62,14 @@
 // Waits for the system property `key` to have the value `expected_value`.
 // Times out after `relative_timeout`.
 // Returns true on success, false on timeout.
-bool WaitForProperty(const std::string& key,
-                     const std::string& expected_value,
-                     std::chrono::milliseconds relative_timeout);
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
+                     std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
 
 // Waits for the system property `key` to be created.
 // Times out after `relative_timeout`.
 // Returns true on success, false on timeout.
-bool WaitForPropertyCreation(const std::string& key,
-                             std::chrono::milliseconds relative_timeout);
+bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
+                                                         std::chrono::milliseconds::max());
 
 } // namespace base
 } // namespace android
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/properties.cpp b/base/properties.cpp
index 32c0128..816bca0 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -101,22 +101,24 @@
 }
 
 // TODO: chrono_utils?
-static void DurationToTimeSpec(timespec& ts, std::chrono::nanoseconds d) {
+static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
   auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
   auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
   ts.tv_sec = s.count();
   ts.tv_nsec = ns.count();
 }
 
+// TODO: boot_clock?
 using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
 
-static void UpdateTimeSpec(timespec& ts,
-                           const AbsTime& timeout) {
+static void UpdateTimeSpec(timespec& ts, std::chrono::milliseconds relative_timeout,
+                           const AbsTime& start_time) {
   auto now = std::chrono::steady_clock::now();
-  auto remaining_timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout - now);
-  if (remaining_timeout < 0ns) {
+  auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+  if (time_elapsed >= relative_timeout) {
     ts = { 0, 0 };
   } else {
+    auto remaining_timeout = relative_timeout - time_elapsed;
     DurationToTimeSpec(ts, remaining_timeout);
   }
 }
@@ -127,11 +129,7 @@
 // Returns nullptr on timeout.
 static const prop_info* WaitForPropertyCreation(const std::string& key,
                                                 const std::chrono::milliseconds& relative_timeout,
-                                                AbsTime& absolute_timeout) {
-  // TODO: boot_clock?
-  auto now = std::chrono::steady_clock::now();
-  absolute_timeout = now + relative_timeout;
-
+                                                const AbsTime& start_time) {
   // Find the property's prop_info*.
   const prop_info* pi;
   unsigned global_serial = 0;
@@ -139,17 +137,16 @@
     // The property doesn't even exist yet.
     // Wait for a global change and then look again.
     timespec ts;
-    UpdateTimeSpec(ts, absolute_timeout);
+    UpdateTimeSpec(ts, relative_timeout, start_time);
     if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr;
   }
   return pi;
 }
 
-bool WaitForProperty(const std::string& key,
-                     const std::string& expected_value,
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
                      std::chrono::milliseconds relative_timeout) {
-  AbsTime absolute_timeout;
-  const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, absolute_timeout);
+  auto start_time = std::chrono::steady_clock::now();
+  const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, start_time);
   if (pi == nullptr) return false;
 
   WaitForPropertyData data;
@@ -162,7 +159,7 @@
     if (data.done) return true;
 
     // It didn't, so wait for the property to change before checking again.
-    UpdateTimeSpec(ts, absolute_timeout);
+    UpdateTimeSpec(ts, relative_timeout, start_time);
     uint32_t unused;
     if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
   }
@@ -170,8 +167,8 @@
 
 bool WaitForPropertyCreation(const std::string& key,
                              std::chrono::milliseconds relative_timeout) {
-  AbsTime absolute_timeout;
-  return (WaitForPropertyCreation(key, relative_timeout, absolute_timeout) != nullptr);
+  auto start_time = std::chrono::steady_clock::now();
+  return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
 }
 
 }  // namespace base
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index 1bbe482..de5f3dc 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -151,6 +151,38 @@
   ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
 }
 
+TEST(properties, WaitForProperty_MaxTimeout) {
+  std::atomic<bool> flag{false};
+  std::thread thread([&]() {
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+    while (!flag) std::this_thread::yield();
+    std::this_thread::sleep_for(500ms);
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+  });
+
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+  flag = true;
+  // Test that this does not immediately return false due to overflow issues with the timeout.
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
+  thread.join();
+}
+
+TEST(properties, WaitForProperty_NegativeTimeout) {
+  std::atomic<bool> flag{false};
+  std::thread thread([&]() {
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+    while (!flag) std::this_thread::yield();
+    std::this_thread::sleep_for(500ms);
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+  });
+
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+  flag = true;
+  // Assert that this immediately returns with a negative timeout
+  ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
+  thread.join();
+}
+
 TEST(properties, WaitForPropertyCreation) {
   std::thread thread([&]() {
     std::this_thread::sleep_for(100ms);
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/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 3b84853..2be13c6 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -140,7 +140,9 @@
   }
 
   bool backtrace = dump_type == kDebuggerdBacktrace;
-  send_signal(pid, backtrace);
+  if (!send_signal(pid, backtrace)) {
+    return false;
+  }
 
   rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
   if (rc == 0) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 22fde5e..7f450e6 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -22,16 +22,22 @@
 #include <signal.h>
 #include <string.h>
 #include <sys/ptrace.h>
+#include <sys/uio.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
 #include <string>
 
+#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 <backtrace/Backtrace.h>
 #include <log/log.h>
 
+using android::base::unique_fd;
+
 // Whitelist output desired in the logcat output.
 bool is_allowed_in_logcat(enum logtype ltype) {
   if ((ltype == HEADER)
@@ -42,6 +48,19 @@
   return false;
 }
 
+static bool should_write_to_kmsg() {
+  // Write to kmsg if tombstoned isn't up, and we're able to do so.
+  if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+    return false;
+  }
+
+  if (android::base::GetProperty("init.svc.tombstoned", "") == "running") {
+    return false;
+  }
+
+  return true;
+}
+
 __attribute__((__weak__, visibility("default")))
 void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) {
   bool write_to_tombstone = (log->tfd != -1);
@@ -49,6 +68,7 @@
                       && log->crashed_tid != -1
                       && log->current_tid != -1
                       && (log->crashed_tid == log->current_tid);
+  static bool write_to_kmsg = should_write_to_kmsg();
 
   char buf[512];
   va_list ap;
@@ -70,6 +90,30 @@
     if (log->amfd_data != nullptr) {
       *log->amfd_data += buf;
     }
+
+    if (write_to_kmsg) {
+      unique_fd kmsg_fd(open("/dev/kmsg_debug", O_WRONLY | O_APPEND | O_CLOEXEC));
+      if (kmsg_fd.get() >= 0) {
+        // Our output might contain newlines which would otherwise be handled by the android logger.
+        // Split the lines up ourselves before sending to the kernel logger.
+        if (buf[len - 1] == '\n') {
+          buf[len - 1] = '\0';
+        }
+
+        std::vector<std::string> fragments = android::base::Split(buf, "\n");
+        for (const std::string& fragment : fragments) {
+          static constexpr char prefix[] = "<3>DEBUG: ";
+          struct iovec iov[3];
+          iov[0].iov_base = const_cast<char*>(prefix);
+          iov[0].iov_len = strlen(prefix);
+          iov[1].iov_base = const_cast<char*>(fragment.c_str());
+          iov[1].iov_len = fragment.length();
+          iov[2].iov_base = const_cast<char*>("\n");
+          iov[2].iov_len = 1;
+          TEMP_FAILURE_RETRY(writev(kmsg_fd.get(), iov, 3));
+        }
+      }
+    }
   }
 }
 
@@ -205,7 +249,7 @@
 }
 
 void read_with_default(const char* path, char* buf, size_t len, const char* default_value) {
-  android::base::unique_fd fd(open(path, O_RDONLY));
+  unique_fd fd(open(path, O_RDONLY | O_CLOEXEC));
   if (fd != -1) {
     int rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, len - 1));
     if (rc != -1) {
diff --git a/demangle/demangle.cpp b/demangle/demangle.cpp
index be4d2dd..66e5e58 100644
--- a/demangle/demangle.cpp
+++ b/demangle/demangle.cpp
@@ -20,22 +20,25 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <cctype>
 #include <string>
 
 #include <demangle.h>
 
 extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
 
-void usage(const char* prog_name) {
-  printf("Usage: %s [-c] <NAME_TO_DEMANGLE>\n", prog_name);
-  printf("  -c\n");
-  printf("    Compare the results of __cxa_demangle against the current\n");
-  printf("    demangler.\n");
+static void Usage(const char* prog_name) {
+  printf("usage: %s [-c] [NAME_TO_DEMANGLE...]\n", prog_name);
+  printf("\n");
+  printf("Demangles C++ mangled names if supplied on the command-line, or found\n");
+  printf("reading from stdin otherwise.\n");
+  printf("\n");
+  printf("-c\tCompare against __cxa_demangle\n");
+  printf("\n");
 }
 
-std::string DemangleWithCxa(const char* name) {
+static std::string DemangleWithCxa(const char* name) {
   const char* cxa_demangle = __cxa_demangle(name, nullptr, nullptr, nullptr);
-
   if (cxa_demangle == nullptr) {
     return name;
   }
@@ -54,6 +57,49 @@
   return demangled_str;
 }
 
+static void Compare(const char* name, const std::string& demangled_name) {
+  std::string cxa_demangled_name(DemangleWithCxa(name));
+  if (cxa_demangled_name != demangled_name) {
+    printf("\nMismatch!\n");
+    printf("\tmangled name: %s\n", name);
+    printf("\tour demangle: %s\n", demangled_name.c_str());
+    printf("\tcxa demangle: %s\n", cxa_demangled_name.c_str());
+    exit(1);
+  }
+}
+
+static int Filter(bool compare) {
+  char* line = nullptr;
+  size_t line_length = 0;
+
+  while ((getline(&line, &line_length, stdin)) != -1) {
+    char* p = line;
+    char* name;
+    while ((name = strstr(p, "_Z")) != nullptr) {
+      // Output anything before the identifier.
+      *name = 0;
+      printf("%s", p);
+      *name = '_';
+
+      // Extract the identifier.
+      p = name;
+      while (*p && (std::isalnum(*p) || *p == '_' || *p == '.' || *p == '$')) ++p;
+
+      // Demangle and output.
+      std::string identifier(name, p);
+      std::string demangled_name = demangle(identifier.c_str());
+      printf("%s", demangled_name.c_str());
+
+      if (compare) Compare(identifier.c_str(), demangled_name);
+    }
+    // Output anything after the last identifier.
+    printf("%s", p);
+  }
+
+  free(line);
+  return 0;
+}
+
 int main(int argc, char** argv) {
 #ifdef __BIONIC__
   const char* prog_name = getprogname();
@@ -67,31 +113,21 @@
     if (opt_char == 'c') {
       compare = true;
     } else {
-      usage(prog_name);
+      Usage(prog_name);
       return 1;
     }
   }
-  if (optind >= argc || argc - optind != 1) {
-    printf("Must supply a single argument.\n\n");
-    usage(prog_name);
-    return 1;
-  }
-  const char* name = argv[optind];
 
-  std::string demangled_name = demangle(name);
+  // With no arguments, act as a filter.
+  if (optind == argc) return Filter(compare);
 
-  printf("%s\n", demangled_name.c_str());
+  // Otherwise demangle each argument.
+  while (optind < argc) {
+    const char* name = argv[optind++];
+    std::string demangled_name = demangle(name);
+    printf("%s\n", demangled_name.c_str());
 
-  if (compare) {
-    std::string cxa_demangle_str(DemangleWithCxa(name));
-
-    if (cxa_demangle_str != demangled_name) {
-      printf("Mismatch\n");
-      printf("cxa demangle: %s\n", cxa_demangle_str.c_str());
-      return 1;
-    } else {
-      printf("Match\n");
-    }
+    if (compare) Compare(name, demangled_name);
   }
   return 0;
 }
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 5610cc0..2c578a9 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -51,7 +51,7 @@
 
 LOCAL_SRC_FILES_windows := usb_windows.cpp
 LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
-LOCAL_REQUIRED_MODULES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
 LOCAL_LDLIBS_windows := -lws2_32
 LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
 
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 728dcb8..bf887c9 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -44,6 +44,7 @@
 #define OP_NOTICE     4
 #define OP_DOWNLOAD_SPARSE 5
 #define OP_WAIT_FOR_DISCONNECT 6
+#define OP_DOWNLOAD_FD 7
 
 typedef struct Action Action;
 
@@ -56,6 +57,7 @@
     char cmd[CMD_SIZE];
     const char* prod;
     void* data;
+    int fd;
 
     // The protocol only supports 32-bit sizes, so you'll have to break
     // anything larger into chunks.
@@ -142,7 +144,20 @@
     a->msg = mkmsg("erasing '%s'", ptn);
 }
 
-void fb_queue_flash(const char *ptn, void *data, unsigned sz)
+void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz)
+{
+    Action *a;
+
+    a = queue_action(OP_DOWNLOAD_FD, "");
+    a->fd = fd;
+    a->size = sz;
+    a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
+
+    a = queue_action(OP_COMMAND, "flash:%s", ptn);
+    a->msg = mkmsg("writing '%s'", ptn);
+}
+
+void fb_queue_flash(const char *ptn, void *data, uint32_t sz)
 {
     Action *a;
 
@@ -155,7 +170,7 @@
     a->msg = mkmsg("writing '%s'", ptn);
 }
 
-void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, unsigned sz, size_t current,
+void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
                            size_t total) {
     Action *a;
 
@@ -282,7 +297,7 @@
     return 0;
 }
 
-void fb_queue_query_save(const char *var, char *dest, unsigned dest_size)
+void fb_queue_query_save(const char *var, char *dest, uint32_t dest_size)
 {
     Action *a;
     a = queue_action(OP_QUERY, "getvar:%s", var);
@@ -309,7 +324,7 @@
     a->msg = msg;
 }
 
-void fb_queue_download(const char *name, void *data, unsigned size)
+void fb_queue_download(const char *name, void *data, uint32_t size)
 {
     Action *a = queue_action(OP_DOWNLOAD, "");
     a->data = data;
@@ -328,11 +343,11 @@
     queue_action(OP_WAIT_FOR_DISCONNECT, "");
 }
 
-int fb_execute_queue(Transport* transport)
+int64_t fb_execute_queue(Transport* transport)
 {
     Action *a;
     char resp[FB_RESPONSE_SZ+1];
-    int status = 0;
+    int64_t status = 0;
 
     a = action_list;
     if (!a)
@@ -351,6 +366,10 @@
             status = fb_download_data(transport, a->data, a->size);
             status = a->func(a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
+        } else if (a->op == OP_DOWNLOAD_FD) {
+            status = fb_download_data_fd(transport, a->fd, a->size);
+            status = a->func(a, status, status ? fb_get_error().c_str() : "");
+            if (status) break;
         } else if (a->op == OP_COMMAND) {
             status = fb_command(transport, a->cmd);
             status = a->func(a, status, status ? fb_get_error().c_str() : "");
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2927b16..3b524ac 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -55,6 +55,7 @@
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
 
@@ -67,6 +68,8 @@
 #include "udp.h"
 #include "usb.h"
 
+using android::base::unique_fd;
+
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
@@ -78,6 +81,10 @@
 static const char* cmdline = nullptr;
 static unsigned short vendor_id = 0;
 static int long_listing = 0;
+// Don't resparse files in too-big chunks.
+// libsparse will support INT_MAX, but this results in large allocations, so
+// let's keep it at 1GB to avoid memory pressure on the host.
+static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024;
 static int64_t sparse_limit = -1;
 static int64_t target_sparse_limit = -1;
 
@@ -91,7 +98,7 @@
 static const std::string convert_fbe_marker_filename("convert_fbe");
 
 enum fb_buffer_type {
-    FB_BUFFER,
+    FB_BUFFER_FD,
     FB_BUFFER_SPARSE,
 };
 
@@ -99,6 +106,7 @@
     enum fb_buffer_type type;
     void* data;
     int64_t sz;
+    int fd;
 };
 
 static struct {
@@ -115,6 +123,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 +153,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")) {
@@ -786,7 +797,7 @@
     }
 
     if (size > limit) {
-        return limit;
+        return std::min(limit, RESPARSE_LIMIT);
     }
 
     return 0;
@@ -819,10 +830,9 @@
         buf->type = FB_BUFFER_SPARSE;
         buf->data = s;
     } else {
-        void* data = load_fd(fd, &sz);
-        if (data == nullptr) return -1;
-        buf->type = FB_BUFFER;
-        buf->data = data;
+        buf->type = FB_BUFFER_FD;
+        buf->data = nullptr;
+        buf->fd = fd;
         buf->sz = sz;
     }
 
@@ -830,11 +840,22 @@
 }
 
 static bool load_buf(Transport* transport, const char* fname, struct fastboot_buffer* buf) {
-    int fd = open(fname, O_RDONLY | O_BINARY);
+    unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
+
     if (fd == -1) {
         return false;
     }
-    return load_buf_fd(transport, fd, buf);
+
+    struct stat s;
+    if (fstat(fd, &s)) {
+        return false;
+    }
+    if (!S_ISREG(s.st_mode)) {
+        errno = S_ISDIR(s.st_mode) ? EISDIR : EINVAL;
+        return false;
+    }
+
+    return load_buf_fd(transport, fd.release(), buf);
 }
 
 static void flash_buf(const char *pname, struct fastboot_buffer *buf)
@@ -857,9 +878,8 @@
             }
             break;
         }
-
-        case FB_BUFFER:
-            fb_queue_flash(pname, buf->data, buf->sz);
+        case FB_BUFFER_FD:
+            fb_queue_flash_fd(pname, buf->fd, buf->sz);
             break;
         default:
             die("unknown buffer type: %d", buf->type);
@@ -1536,6 +1556,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/fastboot/fastboot.h b/fastboot/fastboot.h
index 6699b6a..3f95270 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -41,7 +41,8 @@
 /* protocol.c - fastboot protocol */
 int fb_command(Transport* transport, const char* cmd);
 int fb_command_response(Transport* transport, const char* cmd, char* response);
-int fb_download_data(Transport* transport, const void* data, uint32_t size);
+int64_t fb_download_data(Transport* transport, const void* data, uint32_t size);
+int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size);
 int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
 const std::string fb_get_error();
 
@@ -51,6 +52,7 @@
 /* engine.c - high level command queue engine */
 bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
 void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
+void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz);
 void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
                            size_t total);
 void fb_queue_erase(const char *ptn);
@@ -64,7 +66,7 @@
 void fb_queue_download(const char *name, void *data, uint32_t size);
 void fb_queue_notice(const char *notice);
 void fb_queue_wait_for_disconnect(void);
-int fb_execute_queue(Transport* transport);
+int64_t fb_execute_queue(Transport* transport);
 void fb_set_active(const char *slot);
 
 /* util stuff */
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
index bfa83b0..334f81f 100644
--- a/fastboot/protocol.cpp
+++ b/fastboot/protocol.cpp
@@ -38,6 +38,7 @@
 
 #include <android-base/stringprintf.h>
 #include <sparse/sparse.h>
+#include <utils/FileMap.h>
 
 #include "fastboot.h"
 #include "transport.h"
@@ -48,7 +49,7 @@
     return g_error;
 }
 
-static int check_response(Transport* transport, uint32_t size, char* response) {
+static int64_t check_response(Transport* transport, uint32_t size, char* response) {
     char status[65];
 
     while (true) {
@@ -105,7 +106,7 @@
     return -1;
 }
 
-static int _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
+static int64_t _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
     size_t cmdsize = strlen(cmd);
     if (cmdsize > 64) {
         g_error = android::base::StringPrintf("command too large (%zu)", cmdsize);
@@ -125,14 +126,14 @@
     return check_response(transport, size, response);
 }
 
-static int _command_data(Transport* transport, const void* data, uint32_t size) {
-    int r = transport->Write(data, size);
+static int64_t _command_data(Transport* transport, const void* data, uint32_t size) {
+    int64_t r = transport->Write(data, size);
     if (r < 0) {
         g_error = android::base::StringPrintf("data transfer failure (%s)", strerror(errno));
         transport->Close();
         return -1;
     }
-    if (r != ((int) size)) {
+    if (r != static_cast<int64_t>(size)) {
         g_error = "data transfer failure (short transfer)";
         transport->Close();
         return -1;
@@ -140,17 +141,17 @@
     return r;
 }
 
-static int _command_end(Transport* transport) {
+static int64_t _command_end(Transport* transport) {
     return check_response(transport, 0, 0) < 0 ? -1 : 0;
 }
 
-static int _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
+static int64_t _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
                          char* response) {
     if (size == 0) {
         return -1;
     }
 
-    int r = _command_start(transport, cmd, size, response);
+    int64_t r = _command_start(transport, cmd, size, response);
     if (r < 0) {
         return -1;
     }
@@ -168,6 +169,39 @@
     return size;
 }
 
+static int64_t _command_send_fd(Transport* transport, const char* cmd, int fd, uint32_t size,
+                                char* response) {
+    static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
+    off64_t offset = 0;
+    uint32_t remaining = size;
+
+    if (_command_start(transport, cmd, size, response) < 0) {
+        return -1;
+    }
+
+    while (remaining) {
+        android::FileMap filemap;
+        size_t len = std::min(remaining, MAX_MAP_SIZE);
+
+        if (!filemap.create(NULL, fd, offset, len, true)) {
+            return -1;
+        }
+
+        if (_command_data(transport, filemap.getDataPtr(), len) < 0) {
+            return -1;
+        }
+
+        remaining -= len;
+        offset += len;
+    }
+
+    if (_command_end(transport) < 0) {
+        return -1;
+    }
+
+    return size;
+}
+
 static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
     return _command_start(transport, cmd, 0, response);
 }
@@ -180,10 +214,14 @@
     return _command_send_no_data(transport, cmd, response);
 }
 
-int fb_download_data(Transport* transport, const void* data, uint32_t size) {
-    char cmd[64];
-    snprintf(cmd, sizeof(cmd), "download:%08x", size);
-    return _command_send(transport, cmd, data, size, 0) < 0 ? -1 : 0;
+int64_t fb_download_data(Transport* transport, const void* data, uint32_t size) {
+    std::string cmd(android::base::StringPrintf("download:%08x", size));
+    return _command_send(transport, cmd.c_str(), data, size, 0) < 0 ? -1 : 0;
+}
+
+int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size) {
+    std::string cmd(android::base::StringPrintf("download:%08x", size));
+    return _command_send_fd(transport, cmd.c_str(), fd, size, 0) < 0 ? -1 : 0;
 }
 
 #define TRANSPORT_BUF_SIZE 1024
@@ -242,7 +280,8 @@
 
 static int fb_download_data_sparse_flush(Transport* transport) {
     if (transport_buf_len > 0) {
-        if (_command_data(transport, transport_buf, transport_buf_len) != transport_buf_len) {
+        int64_t r = _command_data(transport, transport_buf, transport_buf_len);
+        if (r != static_cast<int64_t>(transport_buf_len)) {
             return -1;
         }
         transport_buf_len = 0;
@@ -256,9 +295,8 @@
         return -1;
     }
 
-    char cmd[64];
-    snprintf(cmd, sizeof(cmd), "download:%08x", size);
-    int r = _command_start(transport, cmd, size, 0);
+    std::string cmd(android::base::StringPrintf("download:%08x", size));
+    int r = _command_start(transport, cmd.c_str(), size, 0);
     if (r < 0) {
         return -1;
     }
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 7c82bb1..83bf8a7 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -493,6 +493,10 @@
         return nullptr;
     }
 
+    // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
+    avb_handle->avb_version_ =
+        android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
+
     // Verifies vbmeta images against the digest passed from bootloader.
     if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
         LERROR << "VerifyVbmetaImages failed";
diff --git a/fs_mgr/include/fs_mgr_avb.h b/fs_mgr/include/fs_mgr_avb.h
index 526a5ce..a66ff42 100644
--- a/fs_mgr/include/fs_mgr_avb.h
+++ b/fs_mgr/include/fs_mgr_avb.h
@@ -72,7 +72,8 @@
     // Otherwise, returns false.
     bool SetUpAvb(fstab_rec* fstab_entry, bool wait_for_verity_dev);
 
-    bool AvbHashtreeDisabled() { return status_ == kFsManagerAvbHandleHashtreeDisabled; }
+    bool hashtree_disabled() const { return status_ == kFsManagerAvbHandleHashtreeDisabled; }
+    const std::string& avb_version() const { return avb_version_; }
 
     FsManagerAvbHandle(const FsManagerAvbHandle&) = delete;             // no copy
     FsManagerAvbHandle& operator=(const FsManagerAvbHandle&) = delete;  // no assignment
@@ -92,6 +93,7 @@
   private:
     AvbSlotVerifyData* avb_slot_data_;
     FsManagerAvbHandleStatus status_;
+    std::string avb_version_;
 };
 
 #endif /* __CORE_FS_MGR_AVB_H */
diff --git a/include/system b/include/system
index b443c36..91d45be 120000
--- a/include/system
+++ b/include/system
@@ -1 +1 @@
-../libsystem/include/system
\ No newline at end of file
+../libsystem/include/system/
\ No newline at end of file
diff --git a/init/Android.mk b/init/Android.mk
index 3c878b5..f2c0842 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -9,12 +9,14 @@
     -DALLOW_LOCAL_PROP_OVERRIDE=1 \
     -DALLOW_PERMISSIVE_SELINUX=1 \
     -DREBOOT_BOOTLOADER_ON_PANIC=1 \
+    -DWORLD_WRITABLE_KMSG=1 \
     -DDUMP_ON_UMOUNT_FAILURE=1
 else
 init_options += \
     -DALLOW_LOCAL_PROP_OVERRIDE=0 \
     -DALLOW_PERMISSIVE_SELINUX=0 \
     -DREBOOT_BOOTLOADER_ON_PANIC=0 \
+    -DWORLD_WRITABLE_KMSG=0 \
     -DDUMP_ON_UMOUNT_FAILURE=0
 endif
 
@@ -64,6 +66,7 @@
     action.cpp \
     capabilities.cpp \
     descriptors.cpp \
+    devices.cpp \
     import_parser.cpp \
     init_parser.cpp \
     log.cpp \
@@ -83,7 +86,6 @@
 LOCAL_SRC_FILES:= \
     bootchart.cpp \
     builtins.cpp \
-    devices.cpp \
     init.cpp \
     keychords.cpp \
     property_service.cpp \
@@ -139,13 +141,15 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := init_tests
 LOCAL_SRC_FILES := \
+    devices_test.cpp \
     init_parser_test.cpp \
     property_service_test.cpp \
     util_test.cpp \
 
 LOCAL_SHARED_LIBRARIES += \
-    libcutils \
     libbase \
+    libcutils \
+    libselinux \
 
 LOCAL_STATIC_LIBRARIES := libinit
 LOCAL_SANITIZE := integer
diff --git a/init/README.md b/init/README.md
index 822d81e..fc50730 100644
--- a/init/README.md
+++ b/init/README.md
@@ -293,6 +293,11 @@
 `copy <src> <dst>`
 > Copies a file. Similar to write, but useful for binary/large
   amounts of data.
+  Regarding to the src file, copying from symbolic link file and world-writable
+  or group-writable files are not allowed.
+  Regarding to the dst file, the default mode created is 0600 if it does not
+  exist. And it will be truncated if dst file is a normal regular file and
+  already exists.
 
 `domainname <name>`
 > Set the domain name.
diff --git a/init/action.cpp b/init/action.cpp
index 2ccf0bc..347edb0 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -16,16 +16,11 @@
 
 #include "action.h"
 
-#include <errno.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 "util.h"
 
 using android::base::Join;
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index beabea1..825603a 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -17,7 +17,6 @@
 #include "bootchart.h"
 
 #include <dirent.h>
-#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -31,9 +30,7 @@
 #include <condition_variable>
 #include <memory>
 #include <mutex>
-#include <string>
 #include <thread>
-#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 2be70bf..2327cdf 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,33 +19,28 @@
 #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/system_properties.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 <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -55,14 +50,14 @@
 #include <ext4_utils/ext4_crypt.h>
 #include <ext4_utils/ext4_crypt_init_extensions.h>
 #include <fs_mgr.h>
-#include <logwrap/logwrap.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"
@@ -155,7 +150,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) {
@@ -179,7 +174,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) {
@@ -612,7 +607,7 @@
     bool runFsck = false;
     bool commandInvalid = false;
 
-    if (cmd_params.size() > 2) {
+    if (cmd_params.size() > 3) {
         commandInvalid = true;
     } else if (cmd_params[0] == "shutdown") {
         cmd = ANDROID_RB_POWEROFF;
@@ -624,7 +619,7 @@
         }
     } else if (cmd_params[0] == "reboot") {
         cmd = ANDROID_RB_RESTART2;
-        if (cmd_params.size() == 2) {
+        if (cmd_params.size() >= 2) {
             reboot_target = cmd_params[1];
             // When rebooting to the bootloader notify the bootloader writing
             // also the BCB.
@@ -636,6 +631,10 @@
                                << err;
                 }
             }
+            // If there is an additional bootloader parameter, pass it along
+            if (cmd_params.size() == 3) {
+                reboot_target += "," + cmd_params[2];
+            }
         }
     } else if (command == "thermal-shutdown") {  // no additional parameter allowed
         cmd = ANDROID_RB_THERMOFF;
@@ -696,57 +695,15 @@
 }
 
 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) {
-    char* buffer = NULL;
-    int rc = 0;
-    int fd1 = -1, fd2 = -1;
-    struct stat info;
-    int brtw, brtr;
-    char* p;
-
-    if (stat(args[1].c_str(), &info) < 0) return -1;
-
-    if ((fd1 = open(args[1].c_str(), O_RDONLY | O_CLOEXEC)) < 0) goto out_err;
-
-    if ((fd2 = open(args[2].c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0660)) < 0)
-        goto out_err;
-
-    if (!(buffer = (char*)malloc(info.st_size))) goto out_err;
-
-    p = buffer;
-    brtr = info.st_size;
-    while (brtr) {
-        rc = read(fd1, p, brtr);
-        if (rc < 0) goto out_err;
-        if (rc == 0) break;
-        p += rc;
-        brtr -= rc;
+    std::string data;
+    if (read_file(args[1], &data)) {
+        return write_file(args[2], data) ? 0 : 1;
     }
-
-    p = buffer;
-    brtw = info.st_size;
-    while (brtw) {
-        rc = write(fd2, p, brtw);
-        if (rc < 0) goto out_err;
-        if (rc == 0) break;
-        p += rc;
-        brtw -= rc;
-    }
-
-    rc = 0;
-    goto out;
-out_err:
-    rc = -1;
-out:
-    if (buffer) free(buffer);
-    if (fd1 >= 0) close(fd1);
-    if (fd2 >= 0) close(fd2);
-    return rc;
+    return 1;
 }
 
 static int do_chown(const std::vector<std::string>& args) {
diff --git a/init/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..6e13863 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -14,166 +14,102 @@
  * limitations under the License.
  */
 
+#include "devices.h"
+
 #include <dirent.h>
 #include <errno.h>
 #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 <algorithm>
 #include <memory>
 #include <thread>
 
-#include <selinux/selinux.h>
-#include <selinux/label.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.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 <private/android_filesystem_config.h>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <cutils/list.h>
-#include <cutils/uevent.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",
-                                       "/vendor/firmware",
-                                       "/firmware/image" };
 
 extern struct selabel_handle *sehandle;
 
 static android::base::unique_fd device_fd;
 
-struct perms_ {
-    char *name;
-    char *attr;
-    mode_t perm;
-    unsigned int uid;
-    unsigned int gid;
-    unsigned short prefix;
-    unsigned short wildcard;
-};
-
-struct perm_node {
-    struct perms_ dp;
-    struct listnode plist;
-};
-
-struct platform_node {
-    char *name;
-    char *path;
-    int path_len;
-    struct listnode list;
-};
-
-static list_declare(sys_perms);
-static list_declare(dev_perms);
-static list_declare(platform_names);
-
-int add_dev_perms(const char *name, const char *attr,
-                  mode_t perm, unsigned int uid, unsigned int gid,
-                  unsigned short prefix,
-                  unsigned short wildcard) {
-    struct perm_node *node = (perm_node*) calloc(1, sizeof(*node));
-    if (!node)
-        return -ENOMEM;
-
-    node->dp.name = strdup(name);
-    if (!node->dp.name) {
-        free(node);
-        return -ENOMEM;
+Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
+    : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
+    // If the first * is the last character, then we'll treat name_ as a prefix
+    // Otherwise, if a * is present, then we do a full fnmatch().
+    auto wildcard_position = name_.find('*');
+    if (wildcard_position == name_.length() - 1) {
+        prefix_ = true;
+        name_.pop_back();
+    } else if (wildcard_position != std::string::npos) {
+        wildcard_ = true;
     }
-
-    if (attr) {
-        node->dp.attr = strdup(attr);
-        if (!node->dp.attr) {
-            free(node->dp.name);
-            free(node);
-            return -ENOMEM;
-        }
-    }
-
-    node->dp.perm = perm;
-    node->dp.uid = uid;
-    node->dp.gid = gid;
-    node->dp.prefix = prefix;
-    node->dp.wildcard = wildcard;
-
-    if (attr)
-        list_add_tail(&sys_perms, &node->plist);
-    else
-        list_add_tail(&dev_perms, &node->plist);
-
-    return 0;
 }
 
-static bool perm_path_matches(const char *path, struct perms_ *dp)
-{
-    if (dp->prefix) {
-        if (strncmp(path, dp->name, strlen(dp->name)) == 0)
-            return true;
-    } else if (dp->wildcard) {
-        if (fnmatch(dp->name, path, FNM_PATHNAME) == 0)
-            return true;
+bool Permissions::Match(const std::string& path) const {
+    if (prefix_) {
+        return android::base::StartsWith(path, name_.c_str());
+    } else if (wildcard_) {
+        return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
     } else {
-        if (strcmp(path, dp->name) == 0)
-            return true;
+        return path == name_;
     }
 
     return false;
 }
 
-static bool match_subsystem(perms_* dp, const char* pattern,
-                            const char* path, const char* subsystem) {
-    if (!pattern || !subsystem || strstr(dp->name, subsystem) == NULL) {
-        return false;
+bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
+                                          const std::string& subsystem) const {
+    std::string path_basename = android::base::Basename(path);
+    if (name().find(subsystem) != std::string::npos) {
+        if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
+        if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
     }
-
-    std::string subsys_path = android::base::StringPrintf(pattern, subsystem, basename(path));
-    return perm_path_matches(subsys_path.c_str(), dp);
+    return Match(path);
 }
 
-static void fixup_sys_perms(const char* upath, const char* subsystem) {
+void SysfsPermissions::SetPermissions(const std::string& path) const {
+    std::string attribute_file = path + "/" + attribute_;
+    LOG(INFO) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
+              << perm();
+    chown(attribute_file.c_str(), uid(), gid());
+    chmod(attribute_file.c_str(), perm());
+}
+
+// TODO: Move these to be member variables of a future devices class.
+std::vector<Permissions> dev_permissions;
+std::vector<SysfsPermissions> sysfs_permissions;
+
+static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) {
     // upaths omit the "/sys" that paths in this list
     // contain, so we prepend it...
-    std::string path = std::string(SYSFS_PREFIX) + upath;
+    std::string path = "/sys" + upath;
 
-    listnode* node;
-    list_for_each(node, &sys_perms) {
-        perms_* dp = &(node_to_item(node, perm_node, plist))->dp;
-        if (match_subsystem(dp, SYSFS_PREFIX "/class/%s/%s", path.c_str(), subsystem)) {
-            ; // matched
-        } else if (match_subsystem(dp, SYSFS_PREFIX "/bus/%s/devices/%s", path.c_str(), subsystem)) {
-            ; // matched
-        } else if (!perm_path_matches(path.c_str(), dp)) {
-            continue;
-        }
-
-        std::string attr_file = path + "/" + dp->attr;
-        LOG(INFO) << "fixup " << attr_file
-                  << " " << dp->uid << " " << dp->gid << " " << std::oct << dp->perm;
-        chown(attr_file.c_str(), dp->uid, dp->gid);
-        chmod(attr_file.c_str(), dp->perm);
+    for (const auto& s : sysfs_permissions) {
+        if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
     }
 
     if (access(path.c_str(), F_OK) == 0) {
@@ -182,63 +118,34 @@
     }
 }
 
-static mode_t get_device_perm(const char *path, const char **links,
-                unsigned *uid, unsigned *gid)
-{
-    struct listnode *node;
-    struct perm_node *perm_node;
-    struct perms_ *dp;
-
-    /* search the perms list in reverse so that ueventd.$hardware can
-     * override ueventd.rc
-     */
-    list_for_each_reverse(node, &dev_perms) {
-        bool match = false;
-
-        perm_node = node_to_item(node, struct perm_node, plist);
-        dp = &perm_node->dp;
-
-        if (perm_path_matches(path, dp)) {
-            match = true;
-        } else {
-            if (links) {
-                int i;
-                for (i = 0; links[i]; i++) {
-                    if (perm_path_matches(links[i], dp)) {
-                        match = true;
-                        break;
-                    }
-                }
-            }
-        }
-
-        if (match) {
-            *uid = dp->uid;
-            *gid = dp->gid;
-            return dp->perm;
+static std::tuple<mode_t, uid_t, gid_t> get_device_permissions(
+    const std::string& path, const std::vector<std::string>& links) {
+    // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
+    for (auto it = dev_permissions.rbegin(); it != dev_permissions.rend(); ++it) {
+        if (it->Match(path) || std::any_of(links.begin(), links.end(),
+                                           [it](const auto& link) { return it->Match(link); })) {
+            return {it->perm(), it->uid(), it->gid()};
         }
     }
     /* Default if nothing found. */
-    *uid = 0;
-    *gid = 0;
-    return 0600;
+    return {0600, 0, 0};
 }
 
-static void make_device(const char *path,
-                        const char */*upath*/,
-                        int block, int major, int minor,
-                        const char **links)
-{
-    unsigned uid;
-    unsigned gid;
-    mode_t mode;
+static void make_device(const std::string& path, int block, int major, int minor,
+                        const std::vector<std::string>& links) {
     dev_t dev;
     char *secontext = NULL;
 
-    mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
+    auto [mode, uid, gid] = get_device_permissions(path, links);
+    mode |= (block ? S_IFBLK : S_IFCHR);
 
     if (sehandle) {
-        if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) {
+        std::vector<const char*> c_links;
+        for (const auto& link : links) {
+            c_links.emplace_back(link.c_str());
+        }
+        c_links.emplace_back(nullptr);
+        if (selabel_lookup_best_match(sehandle, &secontext, path.c_str(), &c_links[0], mode)) {
             PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
             return;
         }
@@ -257,10 +164,9 @@
     }
     /* If the node already exists update its SELinux label to handle cases when
      * it was created with the wrong context during coldboot procedure. */
-    if (mknod(path, mode, dev) && (errno == EEXIST) && secontext) {
-
+    if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
         char* fcon = nullptr;
-        int rc = lgetfilecon(path, &fcon);
+        int rc = lgetfilecon(path.c_str(), &fcon);
         if (rc < 0) {
             PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
             goto out;
@@ -269,13 +175,13 @@
         bool different = strcmp(fcon, secontext) != 0;
         freecon(fcon);
 
-        if (different && lsetfilecon(path, secontext)) {
+        if (different && lsetfilecon(path.c_str(), secontext)) {
             PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path << "' device";
         }
     }
 
 out:
-    chown(path, uid, -1);
+    chown(path.c_str(), uid, -1);
     if (setegid(AID_ROOT)) {
         PLOG(FATAL) << "setegid(AID_ROOT) failed";
     }
@@ -286,151 +192,86 @@
     }
 }
 
-static void add_platform_device(const char *path)
-{
-    int path_len = strlen(path);
-    struct platform_node *bus;
-    const char *name = path;
+// TODO: Move this to be a member variable of a future devices class.
+std::vector<std::string> platform_devices;
 
-    if (!strncmp(path, "/devices/", 9)) {
-        name += 9;
-        if (!strncmp(name, "platform/", 9))
-            name += 9;
-    }
-
-    LOG(VERBOSE) << "adding platform device " << name << " (" << path << ")";
-
-    bus = (platform_node*) calloc(1, sizeof(struct platform_node));
-    bus->path = strdup(path);
-    bus->path_len = path_len;
-    bus->name = bus->path + (name - path);
-    list_add_tail(&platform_names, &bus->list);
-}
-
-/*
- * given a path that may start with a platform device, find the length of the
- * platform device prefix.  If it doesn't start with a platform device, return
- * 0.
- */
-static struct platform_node *find_platform_device(const char *path)
-{
-    int path_len = strlen(path);
-    struct listnode *node;
-    struct platform_node *bus;
-
-    list_for_each_reverse(node, &platform_names) {
-        bus = node_to_item(node, struct platform_node, list);
-        if ((bus->path_len < path_len) &&
-                (path[bus->path_len] == '/') &&
-                !strncmp(path, bus->path, bus->path_len))
-            return bus;
-    }
-
-    return NULL;
-}
-
-static void remove_platform_device(const char *path)
-{
-    struct listnode *node;
-    struct platform_node *bus;
-
-    list_for_each_reverse(node, &platform_names) {
-        bus = node_to_item(node, struct platform_node, list);
-        if (!strcmp(path, bus->path)) {
-            LOG(INFO) << "removing platform device " << bus->name;
-            free(bus->path);
-            list_remove(node);
-            free(bus);
-            return;
+// Given a path that may start with a platform device, find the length of the
+// platform device prefix.  If it doesn't start with a platform device, return false
+bool find_platform_device(const std::string& path, std::string* out_path) {
+    out_path->clear();
+    // platform_devices is searched backwards, since parents are added before their children,
+    // and we want to match as deep of a child as we can.
+    for (auto it = platform_devices.rbegin(); it != platform_devices.rend(); ++it) {
+        auto platform_device_path_length = it->length();
+        if (platform_device_path_length < path.length() &&
+            path[platform_device_path_length] == '/' &&
+            android::base::StartsWith(path, it->c_str())) {
+            *out_path = *it;
+            return true;
         }
     }
-}
-
-static void destroy_platform_devices() {
-    struct listnode* node;
-    struct listnode* n;
-    struct platform_node* bus;
-
-    list_for_each_safe(node, n, &platform_names) {
-        list_remove(node);
-        bus = node_to_item(node, struct platform_node, list);
-        free(bus->path);
-        free(bus);
-    }
+    return false;
 }
 
 /* Given a path that may start with a PCI device, populate the supplied buffer
  * with the PCI domain/bus number and the peripheral ID and return 0.
  * If it doesn't start with a PCI device, or there is some error, return -1 */
-static int find_pci_device_prefix(const char *path, char *buf, ssize_t buf_sz)
-{
-    const char *start, *end;
+static bool find_pci_device_prefix(const std::string& path, std::string* result) {
+    result->clear();
 
-    if (strncmp(path, "/devices/pci", 12))
-        return -1;
+    if (!android::base::StartsWith(path, "/devices/pci")) return false;
 
     /* Beginning of the prefix is the initial "pci" after "/devices/" */
-    start = path + 9;
+    std::string::size_type start = 9;
 
     /* End of the prefix is two path '/' later, capturing the domain/bus number
      * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
-    end = strchr(start, '/');
-    if (!end)
-        return -1;
-    end = strchr(end + 1, '/');
-    if (!end)
-        return -1;
+    auto end = path.find('/', start);
+    if (end == std::string::npos) return false;
 
-    /* Make sure we have enough room for the string plus null terminator */
-    if (end - start + 1 > buf_sz)
-        return -1;
+    end = path.find('/', end + 1);
+    if (end == std::string::npos) return false;
 
-    strncpy(buf, start, end - start);
-    buf[end - start] = '\0';
-    return 0;
+    auto length = end - start;
+    if (length <= 4) {
+        // The minimum string that will get to this check is 'pci/', which is malformed,
+        // so return false
+        return false;
+    }
+
+    *result = path.substr(start, length);
+    return true;
 }
 
 /* Given a path that may start with a virtual block device, populate
  * the supplied buffer with the virtual block device ID and return 0.
  * If it doesn't start with a virtual block device, or there is some
  * error, return -1 */
-static int find_vbd_device_prefix(const char *path, char *buf, ssize_t buf_sz)
-{
-    const char *start, *end;
+static bool find_vbd_device_prefix(const std::string& path, std::string* result) {
+    result->clear();
+
+    if (!android::base::StartsWith(path, "/devices/vbd-")) return false;
 
     /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
-    if (strncmp(path, "/devices/vbd-", 13))
-        return -1;
+    std::string::size_type start = 13;
 
     /* End of the prefix is one path '/' later, capturing the
        virtual block device ID. Example: 768 */
-    start = path + 13;
-    end = strchr(start, '/');
-    if (!end)
-        return -1;
+    auto end = path.find('/', start);
+    if (end == std::string::npos) return false;
 
-    /* Make sure we have enough room for the string plus null terminator */
-    if (end - start + 1 > buf_sz)
-        return -1;
+    auto length = end - start;
+    if (length == 0) return false;
 
-    strncpy(buf, start, end - start);
-    buf[end - start] = '\0';
-    return 0;
+    *result = path.substr(start, length);
+    return true;
 }
 
-static void parse_event(const char *msg, struct uevent *uevent)
-{
-    uevent->action = "";
-    uevent->path = "";
-    uevent->subsystem = "";
-    uevent->firmware = "";
+void parse_event(const char* msg, uevent* uevent) {
+    uevent->partition_num = -1;
     uevent->major = -1;
     uevent->minor = -1;
-    uevent->partition_name = NULL;
-    uevent->partition_num = -1;
-    uevent->device_name = NULL;
-
-        /* currently ignoring SEQNUM */
+    // currently ignoring SEQNUM
     while(*msg) {
         if(!strncmp(msg, "ACTION=", 7)) {
             msg += 7;
@@ -461,280 +302,194 @@
             uevent->device_name = msg;
         }
 
-        /* advance to after the next \0 */
+        // advance to after the next \0
         while(*msg++)
             ;
     }
 
     if (LOG_UEVENTS) {
-        LOG(INFO) << android::base::StringPrintf("event { '%s', '%s', '%s', '%s', %d, %d }",
-                                                 uevent->action, uevent->path, uevent->subsystem,
-                                                 uevent->firmware, uevent->major, uevent->minor);
+        LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
+                  << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
+                  << ", " << uevent->minor << " }";
     }
 }
 
-static char **get_character_device_symlinks(struct uevent *uevent)
-{
-    const char *parent;
-    const char *slash;
-    char **links;
-    int link_num = 0;
-    int width;
-    struct platform_node *pdev;
+std::vector<std::string> get_character_device_symlinks(uevent* uevent) {
+    std::string parent_device;
+    if (!find_platform_device(uevent->path, &parent_device)) return {};
 
-    pdev = find_platform_device(uevent->path);
-    if (!pdev)
-        return NULL;
+    // skip path to the parent driver
+    std::string path = uevent->path.substr(parent_device.length());
 
-    links = (char**) malloc(sizeof(char *) * 2);
-    if (!links)
-        return NULL;
-    memset(links, 0, sizeof(char *) * 2);
+    if (!android::base::StartsWith(path, "/usb")) return {};
 
-    /* skip "/devices/platform/<driver>" */
-    parent = strchr(uevent->path + pdev->path_len, '/');
-    if (!parent)
-        goto err;
+    // skip root hub name and device. use device interface
+    // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th.
+    // then extract what comes between the 3rd and 4th slash
+    // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name"
 
-    if (!strncmp(parent, "/usb", 4)) {
-        /* skip root hub name and device. use device interface */
-        while (*++parent && *parent != '/');
-        if (*parent)
-            while (*++parent && *parent != '/');
-        if (!*parent)
-            goto err;
-        slash = strchr(++parent, '/');
-        if (!slash)
-            goto err;
-        width = slash - parent;
-        if (width <= 0)
-            goto err;
+    std::string::size_type start = 0;
+    start = path.find('/', start + 1);
+    if (start == std::string::npos) return {};
 
-        if (asprintf(&links[link_num], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0)
-            link_num++;
-        else
-            links[link_num] = NULL;
-        mkdir("/dev/usb", 0755);
-    }
-    else {
-        goto err;
-    }
+    start = path.find('/', start + 1);
+    if (start == std::string::npos) return {};
+
+    auto end = path.find('/', start + 1);
+    if (end == std::string::npos) return {};
+
+    start++;  // Skip the first '/'
+
+    auto length = end - start;
+    if (length == 0) return {};
+
+    auto name_string = path.substr(start, length);
+
+    std::vector<std::string> links;
+    links.emplace_back("/dev/usb/" + uevent->subsystem + name_string);
+
+    mkdir("/dev/usb", 0755);
 
     return links;
-err:
-    free(links);
-    return NULL;
 }
 
-static char **get_block_device_symlinks(struct uevent *uevent)
-{
-    const char *device;
-    struct platform_node *pdev;
-    const char *slash;
-    const char *type;
-    char buf[256];
-    char link_path[256];
-    int link_num = 0;
-    char *p;
+// replaces any unacceptable characters with '_', the
+// length of the resulting string is equal to the input string
+void sanitize_partition_name(std::string* string) {
+    const char* accept =
+        "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "0123456789"
+        "_-.";
 
-    pdev = find_platform_device(uevent->path);
-    if (pdev) {
-        device = pdev->name;
+    if (!string) return;
+
+    std::string::size_type pos = 0;
+    while ((pos = string->find_first_not_of(accept, pos)) != std::string::npos) {
+        (*string)[pos] = '_';
+    }
+}
+
+std::vector<std::string> get_block_device_symlinks(uevent* uevent) {
+    std::string device;
+    std::string type;
+
+    if (find_platform_device(uevent->path, &device)) {
+        // Skip /devices/platform or /devices/ if present
+        static const std::string devices_platform_prefix = "/devices/platform/";
+        static const std::string devices_prefix = "/devices/";
+
+        if (android::base::StartsWith(device, devices_platform_prefix.c_str())) {
+            device = device.substr(devices_platform_prefix.length());
+        } else if (android::base::StartsWith(device, devices_prefix.c_str())) {
+            device = device.substr(devices_prefix.length());
+        }
+
         type = "platform";
-    } else if (!find_pci_device_prefix(uevent->path, buf, sizeof(buf))) {
-        device = buf;
+    } else if (find_pci_device_prefix(uevent->path, &device)) {
         type = "pci";
-    } else if (!find_vbd_device_prefix(uevent->path, buf, sizeof(buf))) {
-        device = buf;
+    } else if (find_vbd_device_prefix(uevent->path, &device)) {
         type = "vbd";
     } else {
-        return NULL;
+        return {};
     }
 
-    char **links = (char**) malloc(sizeof(char *) * 4);
-    if (!links)
-        return NULL;
-    memset(links, 0, sizeof(char *) * 4);
+    std::vector<std::string> links;
 
     LOG(VERBOSE) << "found " << type << " device " << device;
 
-    snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);
+    auto link_path = "/dev/block/" + type + "/" + device;
 
-    if (uevent->partition_name) {
-        p = strdup(uevent->partition_name);
-        sanitize(p);
-        if (strcmp(uevent->partition_name, p)) {
-            LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '" << p << "'";
+    if (!uevent->partition_name.empty()) {
+        std::string partition_name_sanitized(uevent->partition_name);
+        sanitize_partition_name(&partition_name_sanitized);
+        if (partition_name_sanitized != uevent->partition_name) {
+            LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '"
+                         << partition_name_sanitized << "'";
         }
-        if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
-            link_num++;
-        else
-            links[link_num] = NULL;
-        free(p);
+        links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
     }
 
     if (uevent->partition_num >= 0) {
-        if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0)
-            link_num++;
-        else
-            links[link_num] = NULL;
+        links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent->partition_num));
     }
 
-    slash = strrchr(uevent->path, '/');
-    if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
-        link_num++;
-    else
-        links[link_num] = NULL;
+    auto last_slash = uevent->path.rfind('/');
+    links.emplace_back(link_path + "/" + uevent->path.substr(last_slash + 1));
 
     return links;
 }
 
-static void make_link_init(const char* oldpath, const char* newpath) {
-  const char* slash = strrchr(newpath, '/');
-  if (!slash) return;
+static void make_link_init(const std::string& oldpath, const std::string& newpath) {
+    if (mkdir_recursive(dirname(newpath.c_str()), 0755)) {
+        PLOG(ERROR) << "Failed to create directory " << dirname(newpath.c_str());
+    }
 
-  if (mkdir_recursive(dirname(newpath), 0755)) {
-    PLOG(ERROR) << "Failed to create directory " << dirname(newpath);
-  }
-
-  if (symlink(oldpath, newpath) && errno != EEXIST) {
-    PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
-  }
+    if (symlink(oldpath.c_str(), newpath.c_str()) && errno != EEXIST) {
+        PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
+    }
 }
 
-static void remove_link(const char* oldpath, const char* newpath) {
-  std::string path;
-  if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath);
+static void remove_link(const std::string& oldpath, const std::string& newpath) {
+    std::string path;
+    if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath.c_str());
 }
 
-static void handle_device(const char *action, const char *devpath,
-        const char *path, int block, int major, int minor, char **links)
-{
-    if(!strcmp(action, "add")) {
-        make_device(devpath, path, block, major, minor, (const char **)links);
-        if (links) {
-            for (int i = 0; links[i]; i++) {
-                make_link_init(devpath, links[i]);
-            }
+static void handle_device(const std::string& action, const std::string& devpath, int block,
+                          int major, int minor, const std::vector<std::string>& links) {
+    if (action == "add") {
+        make_device(devpath, block, major, minor, links);
+        for (const auto& link : links) {
+            make_link_init(devpath, link);
         }
     }
 
-    if(!strcmp(action, "remove")) {
-        if (links) {
-            for (int i = 0; links[i]; i++) {
-                remove_link(devpath, links[i]);
-            }
+    if (action == "remove") {
+        for (const auto& link : links) {
+            remove_link(devpath, link);
         }
-        unlink(devpath);
-    }
-
-    if (links) {
-        for (int i = 0; links[i]; i++) {
-            free(links[i]);
-        }
-        free(links);
+        unlink(devpath.c_str());
     }
 }
 
-static void handle_platform_device_event(struct uevent *uevent)
-{
-    const char *path = uevent->path;
-
-    if (!strcmp(uevent->action, "add"))
-        add_platform_device(path);
-    else if (!strcmp(uevent->action, "remove"))
-        remove_platform_device(path);
-}
-
-static const char *parse_device_name(struct uevent *uevent, unsigned int len)
-{
-    const char *name;
-
-    /* if it's not a /dev device, nothing else to do */
-    if((uevent->major < 0) || (uevent->minor < 0))
-        return NULL;
-
-    /* do we have a name? */
-    name = strrchr(uevent->path, '/');
-    if(!name)
-        return NULL;
-    name++;
-
-    /* too-long names would overrun our buffer */
-    if(strlen(name) > len) {
-        LOG(ERROR) << "DEVPATH=" << name << " exceeds " << len << "-character limit on filename; ignoring event";
-        return NULL;
+void handle_platform_device_event(uevent* uevent) {
+    if (uevent->action == "add") {
+        platform_devices.emplace_back(uevent->path);
+    } else if (uevent->action == "remove") {
+        auto it = std::find(platform_devices.begin(), platform_devices.end(), uevent->path);
+        if (it != platform_devices.end()) platform_devices.erase(it);
     }
-
-    return name;
 }
 
-#define DEVPATH_LEN 96
-#define MAX_DEV_NAME 64
+static void handle_block_device_event(uevent* uevent) {
+    // if it's not a /dev device, nothing to do
+    if (uevent->major < 0 || uevent->minor < 0) return;
 
-static void handle_block_device_event(struct uevent *uevent)
-{
-    const char *base = "/dev/block/";
-    const char *name;
-    char devpath[DEVPATH_LEN];
-    char **links = NULL;
-
-    name = parse_device_name(uevent, MAX_DEV_NAME);
-    if (!name)
-        return;
-
-    snprintf(devpath, sizeof(devpath), "%s%s", base, name);
+    const char* base = "/dev/block/";
     make_dir(base, 0755);
 
-    if (!strncmp(uevent->path, "/devices/", 9))
+    std::string name = android::base::Basename(uevent->path);
+    std::string devpath = base + name;
+
+    std::vector<std::string> links;
+    if (android::base::StartsWith(uevent->path, "/devices")) {
         links = get_block_device_symlinks(uevent);
-
-    handle_device(uevent->action, devpath, uevent->path, 1,
-            uevent->major, uevent->minor, links);
-}
-
-static bool assemble_devpath(char *devpath, const char *dirname,
-        const char *devname)
-{
-    int s = snprintf(devpath, DEVPATH_LEN, "%s/%s", dirname, devname);
-    if (s < 0) {
-        PLOG(ERROR) << "failed to assemble device path; ignoring event";
-        return false;
-    } else if (s >= DEVPATH_LEN) {
-        LOG(ERROR) << dirname << "/" << devname
-                   << " exceeds " << DEVPATH_LEN << "-character limit on path; ignoring event";
-        return false;
     }
-    return true;
+
+    handle_device(uevent->action, devpath, 1, uevent->major, uevent->minor, links);
 }
 
-static void mkdir_recursive_for_devpath(const char *devpath)
-{
-    char dir[DEVPATH_LEN];
-    char *slash;
+static void handle_generic_device_event(uevent* uevent) {
+    // if it's not a /dev device, nothing to do
+    if (uevent->major < 0 || uevent->minor < 0) return;
 
-    strcpy(dir, devpath);
-    slash = strrchr(dir, '/');
-    *slash = '\0';
-    mkdir_recursive(dir, 0755);
-}
+    std::string name = android::base::Basename(uevent->path);
+    ueventd_subsystem* subsystem = ueventd_subsystem_find_by_name(uevent->subsystem.c_str());
 
-static void handle_generic_device_event(struct uevent *uevent)
-{
-    const char *base;
-    const char *name;
-    char devpath[DEVPATH_LEN] = {0};
-    char **links = NULL;
-
-    name = parse_device_name(uevent, MAX_DEV_NAME);
-    if (!name)
-        return;
-
-    struct ueventd_subsystem *subsystem =
-            ueventd_subsystem_find_by_name(uevent->subsystem);
+    std::string devpath;
 
     if (subsystem) {
-        const char *devname;
+        std::string devname;
 
         switch (subsystem->devname_src) {
         case DEVNAME_UEVENT_DEVNAME:
@@ -750,82 +505,44 @@
             return;
         }
 
-        if (!assemble_devpath(devpath, subsystem->dirname, devname))
+        // TODO: Remove std::string()
+        devpath = std::string(subsystem->dirname) + "/" + devname;
+        mkdir_recursive(android::base::Dirname(devpath), 0755);
+    } else if (android::base::StartsWith(uevent->subsystem, "usb")) {
+        if (uevent->subsystem == "usb") {
+            if (!uevent->device_name.empty()) {
+                devpath = "/dev/" + uevent->device_name;
+            } else {
+                // This imitates the file system that would be created
+                // if we were using devfs instead.
+                // Minors are broken up into groups of 128, starting at "001"
+                int bus_id = uevent->minor / 128 + 1;
+                int device_id = uevent->minor % 128 + 1;
+                devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
+            }
+            mkdir_recursive(android::base::Dirname(devpath), 0755);
+        } else {
+            // ignore other USB events
             return;
-        mkdir_recursive_for_devpath(devpath);
-    } else if (!strncmp(uevent->subsystem, "usb", 3)) {
-         if (!strcmp(uevent->subsystem, "usb")) {
-            if (uevent->device_name) {
-                if (!assemble_devpath(devpath, "/dev", uevent->device_name))
-                    return;
-                mkdir_recursive_for_devpath(devpath);
-             }
-             else {
-                 /* This imitates the file system that would be created
-                  * if we were using devfs instead.
-                  * Minors are broken up into groups of 128, starting at "001"
-                  */
-                 int bus_id = uevent->minor / 128 + 1;
-                 int device_id = uevent->minor % 128 + 1;
-                 /* build directories */
-                 make_dir("/dev/bus", 0755);
-                 make_dir("/dev/bus/usb", 0755);
-                 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
-                 make_dir(devpath, 0755);
-                 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
-             }
-         } else {
-             /* ignore other USB events */
-             return;
-         }
-     } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
-         base = "/dev/graphics/";
-         make_dir(base, 0755);
-     } else if (!strncmp(uevent->subsystem, "drm", 3)) {
-         base = "/dev/dri/";
-         make_dir(base, 0755);
-     } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
-         base = "/dev/oncrpc/";
-         make_dir(base, 0755);
-     } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
-         base = "/dev/adsp/";
-         make_dir(base, 0755);
-     } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
-         base = "/dev/msm_camera/";
-         make_dir(base, 0755);
-     } else if(!strncmp(uevent->subsystem, "input", 5)) {
-         base = "/dev/input/";
-         make_dir(base, 0755);
-     } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
-         base = "/dev/mtd/";
-         make_dir(base, 0755);
-     } else if(!strncmp(uevent->subsystem, "sound", 5)) {
-         base = "/dev/snd/";
-         make_dir(base, 0755);
-     } else if(!strncmp(uevent->subsystem, "misc", 4) && !strncmp(name, "log_", 4)) {
-         LOG(INFO) << "kernel logger is deprecated";
-         base = "/dev/log/";
-         make_dir(base, 0755);
-         name += 4;
-     } else
-         base = "/dev/";
-     links = get_character_device_symlinks(uevent);
+        }
+    } else {
+        devpath = "/dev/" + name;
+    }
 
-     if (!devpath[0])
-         snprintf(devpath, sizeof(devpath), "%s%s", base, name);
+    auto links = get_character_device_symlinks(uevent);
 
-     handle_device(uevent->action, devpath, uevent->path, 0,
-             uevent->major, uevent->minor, links);
+    handle_device(uevent->action, devpath, 0, uevent->major, uevent->minor, links);
 }
 
 static void handle_device_event(struct uevent *uevent)
 {
-    if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online"))
-        fixup_sys_perms(uevent->path, uevent->subsystem);
+    if (uevent->action == "add" || uevent->action == "change" || uevent->action == "online") {
+        fixup_sys_permissions(uevent->path, uevent->subsystem);
+    }
 
-    if (!strncmp(uevent->subsystem, "block", 5)) {
+    if (uevent->subsystem == "block") {
         handle_block_device_event(uevent);
-    } else if (!strncmp(uevent->subsystem, "platform", 8)) {
+    } else if (uevent->subsystem == "platform") {
         handle_platform_device_event(uevent);
     } else {
         handle_generic_device_event(uevent);
@@ -858,7 +575,7 @@
 
     LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
 
-    std::string root = android::base::StringPrintf("/sys%s", uevent->path);
+    std::string root = "/sys" + uevent->path;
     std::string loading = root + "/loading";
     std::string data = root + "/data";
 
@@ -874,9 +591,12 @@
         return;
     }
 
+    static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
+                                          "/firmware/image/"};
+
 try_loading_again:
     for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
-        std::string file = android::base::StringPrintf("%s/%s", firmware_dirs[i], uevent->firmware);
+        std::string file = firmware_dirs[i] + uevent->firmware;
         android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY|O_CLOEXEC));
         struct stat sb;
         if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
@@ -900,8 +620,7 @@
 }
 
 static void handle_firmware_event(uevent* uevent) {
-    if (strcmp(uevent->subsystem, "firmware")) return;
-    if (strcmp(uevent->action, "add")) return;
+    if (uevent->subsystem != "firmware" || uevent->action != "add") return;
 
     // Loading the firmware in a child means we can do that in parallel...
     // (We ignore SIGCHLD rather than wait for our children.)
@@ -935,7 +654,7 @@
         msg[n] = '\0';
         msg[n+1] = '\0';
 
-        struct uevent uevent;
+        uevent uevent;
         parse_event(msg, &uevent);
         coldboot_action_t act = handle_uevent(&uevent);
         if (should_stop_coldboot(act))
@@ -1079,7 +798,7 @@
 }
 
 void device_close() {
-    destroy_platform_devices();
+    platform_devices.clear();
     device_fd.reset();
     selinux_status_close();
 }
diff --git a/init/devices.h b/init/devices.h
index 26a064b..2cbae66 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,8 +17,12 @@
 #ifndef _INIT_DEVICES_H
 #define _INIT_DEVICES_H
 
-#include <functional>
 #include <sys/stat.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+#include <vector>
 
 enum coldboot_action_t {
     // coldboot continues without creating the device for the uevent
@@ -33,26 +37,67 @@
 };
 
 struct uevent {
-    const char* action;
-    const char* path;
-    const char* subsystem;
-    const char* firmware;
-    const char* partition_name;
-    const char* device_name;
+    std::string action;
+    std::string path;
+    std::string subsystem;
+    std::string firmware;
+    std::string partition_name;
+    std::string device_name;
     int partition_num;
     int major;
     int minor;
 };
 
+class Permissions {
+  public:
+    Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
+
+    bool Match(const std::string& path) const;
+
+    mode_t perm() const { return perm_; }
+    uid_t uid() const { return uid_; }
+    gid_t gid() const { return gid_; }
+
+  protected:
+    const std::string& name() const { return name_; }
+
+  private:
+    std::string name_;
+    mode_t perm_;
+    uid_t uid_;
+    gid_t gid_;
+    bool prefix_;
+    bool wildcard_;
+};
+
+class SysfsPermissions : public Permissions {
+  public:
+    SysfsPermissions(const std::string& name, const std::string& attribute, mode_t perm, uid_t uid,
+                     gid_t gid)
+        : Permissions(name, perm, uid, gid), attribute_(attribute) {}
+
+    bool MatchWithSubsystem(const std::string& path, const std::string& subsystem) const;
+    void SetPermissions(const std::string& path) const;
+
+  private:
+    const std::string attribute_;
+};
+
+extern std::vector<Permissions> dev_permissions;
+extern std::vector<SysfsPermissions> sysfs_permissions;
+
 typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
 extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
 extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
 extern void device_close();
-
-extern int add_dev_perms(const char *name, const char *attr,
-                         mode_t perm, unsigned int uid,
-                         unsigned int gid, unsigned short prefix,
-                         unsigned short wildcard);
 int get_device_fd();
 
-#endif	/* _INIT_DEVICES_H */
+// Exposed for testing
+extern std::vector<std::string> platform_devices;
+bool find_platform_device(const std::string& path, std::string* out_path);
+std::vector<std::string> get_character_device_symlinks(uevent* uevent);
+std::vector<std::string> get_block_device_symlinks(uevent* uevent);
+void sanitize_partition_name(std::string* string);
+void handle_platform_device_event(uevent* uevent);
+
+#endif /* _INIT_DEVICES_H */
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
new file mode 100644
index 0000000..66521db
--- /dev/null
+++ b/init/devices_test.cpp
@@ -0,0 +1,409 @@
+/*
+ * 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>
+
+void add_platform_device(const std::string& path) {
+    uevent uevent = {
+        .action = "add", .subsystem = "platform", .path = path,
+    };
+    handle_platform_device_event(&uevent);
+}
+
+void remove_platform_device(const std::string& path) {
+    uevent uevent = {
+        .action = "remove", .subsystem = "platform", .path = path,
+    };
+    handle_platform_device_event(&uevent);
+}
+
+template <std::vector<std::string> (*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);
+    auto platform_device_remover = android::base::make_scope_guard(
+        [&platform_device_name]() { remove_platform_device(platform_device_name); });
+
+    std::vector<std::string> result = Function(uevent);
+
+    auto expected_size = expected_links.size();
+    ASSERT_EQ(expected_size, result.size());
+    if (expected_size == 0) return;
+
+    // Explicitly iterate so the results are visible if a failure occurs
+    for (unsigned int i = 0; i < expected_size; ++i) {
+        EXPECT_EQ(expected_links[i], result[i]);
+    }
+}
+
+TEST(devices, handle_platform_device_event) {
+    platform_devices.clear();
+    add_platform_device("/devices/platform/some_device_name");
+    ASSERT_EQ(1U, platform_devices.size());
+    remove_platform_device("/devices/platform/some_device_name");
+    ASSERT_EQ(0U, platform_devices.size());
+}
+
+TEST(devices, find_platform_device) {
+    platform_devices.clear();
+    add_platform_device("/devices/platform/some_device_name");
+    add_platform_device("/devices/platform/some_device_name/longer");
+    add_platform_device("/devices/platform/other_device_name");
+    EXPECT_EQ(3U, platform_devices.size());
+
+    std::string out_path;
+    EXPECT_FALSE(find_platform_device("/devices/platform/not_found", &out_path));
+    EXPECT_EQ("", out_path);
+
+    EXPECT_FALSE(
+        find_platform_device("/devices/platform/some_device_name_with_same_prefix", &out_path));
+
+    EXPECT_TRUE(
+        find_platform_device("/devices/platform/some_device_name/longer/longer_child", &out_path));
+    EXPECT_EQ("/devices/platform/some_device_name/longer", out_path);
+
+    EXPECT_TRUE(find_platform_device("/devices/platform/some_device_name/other_child", &out_path));
+    EXPECT_EQ("/devices/platform/some_device_name", out_path);
+}
+
+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 = "",
+        .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 = "",
+        .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 = "", .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_pci_bad_format) {
+    const char* platform_device = "/devices/do/not/match";
+    uevent uevent = {
+        .path = "/devices/pci//mmcblk0", .partition_name = "", .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{};
+
+    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 = "", .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_vbd_bad_format) {
+    const char* platform_device = "/devices/do/not/match";
+    uevent uevent = {
+        .path = "/devices/vbd-/mmcblk0", .partition_name = "", .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{};
+
+    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 = "",
+        .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);
+    EXPECT_EQ(0u, empty.size());
+}
+
+TEST(devices, sanitize_allgood) {
+    std::string good =
+        "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "0123456789"
+        "_-.";
+    std::string good_copy = good;
+    sanitize_partition_name(&good);
+    EXPECT_EQ(good_copy, good);
+}
+
+TEST(devices, sanitize_somebad) {
+    std::string string = "abc!@#$%^&*()";
+    sanitize_partition_name(&string);
+    EXPECT_EQ("abc__________", string);
+}
+
+TEST(devices, sanitize_allbad) {
+    std::string string = "!@#$%^&*()";
+    sanitize_partition_name(&string);
+    EXPECT_EQ("__________", string);
+}
+
+TEST(devices, sanitize_onebad) {
+    std::string string = ")";
+    sanitize_partition_name(&string);
+    EXPECT_EQ("_", string);
+}
+
+TEST(devices, DevPermissionsMatchNormal) {
+    // Basic from ueventd.rc
+    // /dev/null                 0666   root       root
+    Permissions permissions("/dev/null", 0666, 0, 0);
+    EXPECT_TRUE(permissions.Match("/dev/null"));
+    EXPECT_FALSE(permissions.Match("/dev/nullsuffix"));
+    EXPECT_FALSE(permissions.Match("/dev/nul"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(0U, permissions.gid());
+}
+
+TEST(devices, DevPermissionsMatchPrefix) {
+    // Prefix from ueventd.rc
+    // /dev/dri/*                0666   root       graphics
+    Permissions permissions("/dev/dri/*", 0666, 0, 1000);
+    EXPECT_TRUE(permissions.Match("/dev/dri/some_dri_device"));
+    EXPECT_TRUE(permissions.Match("/dev/dri/some_other_dri_device"));
+    EXPECT_TRUE(permissions.Match("/dev/dri/"));
+    EXPECT_FALSE(permissions.Match("/dev/dr/non_match"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(devices, DevPermissionsMatchWildcard) {
+    // Wildcard example
+    // /dev/device*name                0666   root       graphics
+    Permissions permissions("/dev/device*name", 0666, 0, 1000);
+    EXPECT_TRUE(permissions.Match("/dev/devicename"));
+    EXPECT_TRUE(permissions.Match("/dev/device123name"));
+    EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+    EXPECT_FALSE(permissions.Match("/dev/device123name/subdevice"));
+    EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(devices, DevPermissionsMatchWildcardPrefix) {
+    // Wildcard+Prefix example
+    // /dev/device*name*                0666   root       graphics
+    Permissions permissions("/dev/device*name*", 0666, 0, 1000);
+    EXPECT_TRUE(permissions.Match("/dev/devicename"));
+    EXPECT_TRUE(permissions.Match("/dev/device123name"));
+    EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+    EXPECT_TRUE(permissions.Match("/dev/device123namesomething"));
+    // FNM_PATHNAME doesn't match '/' with *
+    EXPECT_FALSE(permissions.Match("/dev/device123name/something"));
+    EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(devices, SysfsPermissionsMatchWithSubsystemNormal) {
+    // /sys/devices/virtual/input/input*   enable      0660  root   input
+    SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
+    EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/not_input0", "input"));
+    EXPECT_EQ(0660U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1001U, permissions.gid());
+}
+
+TEST(devices, SysfsPermissionsMatchWithSubsystemClass) {
+    // /sys/class/input/event*   enable      0660  root   input
+    SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
+    EXPECT_TRUE(permissions.MatchWithSubsystem(
+        "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "input"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem(
+        "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/not_event0", "input"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem(
+        "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "not_input"));
+    EXPECT_EQ(0660U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1001U, permissions.gid());
+}
+
+TEST(devices, SysfsPermissionsMatchWithSubsystemBus) {
+    // /sys/bus/i2c/devices/i2c-*   enable      0660  root   input
+    SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
+    EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/not-i2c", "i2c"));
+    EXPECT_FALSE(
+        permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "not_i2c"));
+    EXPECT_EQ(0660U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1001U, permissions.gid());
+}
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 b05311a..dedca29 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,9 @@
 #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>
@@ -59,7 +63,6 @@
 #include "fs_mgr.h"
 #include "fs_mgr_avb.h"
 #include "import_parser.h"
-#include "init.h"
 #include "init_parser.h"
 #include "keychords.h"
 #include "log.h"
@@ -70,6 +73,7 @@
 #include "util.h"
 #include "watchdogd.h"
 
+using android::base::boot_clock;
 using android::base::GetProperty;
 using android::base::StringPrintf;
 
@@ -936,6 +940,9 @@
     LOG(INFO) << "Running restorecon...";
     restorecon("/dev");
     restorecon("/dev/kmsg");
+    if constexpr (WORLD_WRITABLE_KMSG) {
+      restorecon("/dev/kmsg_debug");
+    }
     restorecon("/dev/socket");
     restorecon("/dev/random");
     restorecon("/dev/urandom");
@@ -982,7 +989,7 @@
     const std::string syspath = "/sys/block/" + device_name;
 
     device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
-        if (uevent->device_name && device_name == uevent->device_name) {
+        if (uevent->device_name == device_name) {
             LOG(VERBOSE) << "early_mount: creating dm-verity device : " << dm_device;
             return COLDBOOT_STOP;
         }
@@ -1034,10 +1041,11 @@
         return false;
     }
 
+    setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
     for (auto rec : fstab_recs) {
         bool need_create_dm_device = false;
         if (fs_mgr_is_avb(rec)) {
-            if (avb_handle->AvbHashtreeDisabled()) {
+            if (avb_handle->hashtree_disabled()) {
                 LOG(INFO) << "avb hashtree disabled for '" << rec->mount_point << "'";
             } else if (avb_handle->SetUpAvb(rec, false /* wait_for_verity_dev */)) {
                 need_create_dm_device = true;
@@ -1079,21 +1087,17 @@
         return;
     }
     device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
-        if (!strncmp(uevent->subsystem, "firmware", 8)) {
-            return COLDBOOT_CONTINUE;
-        }
-
         // we need platform devices to create symlinks
-        if (!strncmp(uevent->subsystem, "platform", 8)) {
+        if (uevent->subsystem == "platform") {
             return COLDBOOT_CREATE;
         }
 
         // Ignore everything that is not a block device
-        if (strncmp(uevent->subsystem, "block", 5)) {
+        if (uevent->subsystem != "block") {
             return COLDBOOT_CONTINUE;
         }
 
-        if (uevent->partition_name) {
+        if (!uevent->partition_name.empty()) {
             // match partition names to create device nodes for partitions
             // both partition_names and uevent->partition_name have A/B suffix when A/B is used
             auto iter = partition_names->find(uevent->partition_name);
@@ -1320,7 +1324,13 @@
         setgroups(arraysize(groups), groups);
         mount("sysfs", "/sys", "sysfs", 0, NULL);
         mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
+
         mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
+
+        if constexpr (WORLD_WRITABLE_KMSG) {
+          mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
+        }
+
         mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
         mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
 
@@ -1384,12 +1394,14 @@
     property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
 
     // Set libavb version for Framework-only OTA match in Treble build.
-    property_set("ro.boot.init.avb_version", std::to_string(AVB_MAJOR_VERSION).c_str());
+    const char* avb_version = getenv("INIT_AVB_VERSION");
+    if (avb_version) property_set("ro.boot.avb_version", avb_version);
 
     // Clean up our environment.
     unsetenv("INIT_SECOND_STAGE");
     unsetenv("INIT_STARTED_AT");
     unsetenv("INIT_SELINUX_TOOK");
+    unsetenv("INIT_AVB_VERSION");
 
     // Now set up SELinux for second stage.
     selinux_initialize(false);
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 5801ea8..c572cee 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -14,19 +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 "service.h"
 
 static struct input_keychord *keychords = 0;
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 a4d8b5f..2aa89ff 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -14,46 +14,45 @@
  * 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 <stddef.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;
 
diff --git a/init/property_service.h b/init/property_service.h
index 994da63..9a5b6f6 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -17,9 +17,8 @@
 #ifndef _INIT_PROPERTY_H
 #define _INIT_PROPERTY_H
 
-#include <stddef.h>
 #include <sys/socket.h>
-#include <sys/system_properties.h>
+
 #include <string>
 
 struct property_audit_data {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index e887677..d9ebd91 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 <linux/fs.h>
@@ -29,11 +32,11 @@
 
 #include <memory>
 #include <set>
-#include <string>
 #include <thread>
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -45,11 +48,8 @@
 #include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
 
-#include "log.h"
 #include "property_service.h"
-#include "reboot.h"
 #include "service.h"
-#include "util.h"
 
 using android::base::StringPrintf;
 
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 3db34db..4adbbf0 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -24,30 +24,27 @@
 #include <sys/prctl.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
+#include <sys/system_properties.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;
diff --git a/init/service.h b/init/service.h
index f08a03f..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"
@@ -144,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_;
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 f27be64..b6c6a01 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 "util.h"
 
 int ueventd_main(int argc, char **argv)
 {
@@ -94,15 +94,13 @@
     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;
     mode_t perm;
     uid_t uid;
     gid_t gid;
-    int prefix = 0;
-    int wildcard = 0;
     char *endptr;
 
     if (nargs == 0)
@@ -121,46 +119,33 @@
     }
 
     if (nargs != 4) {
-        LOG(ERROR) << "invalid line ueventd.rc line for '" << args[0] << "'";
+        LOG(ERROR) << "invalid line (" << fn << ":" << line << ") line for '" << args[0] << "'";
         return;
     }
 
-    int len = strlen(name);
-    char *wildcard_chr = strchr(name, '*');
-    if ((name[len - 1] == '*') && (wildcard_chr == (name + len - 1))) {
-        prefix = 1;
-        name[len - 1] = '\0';
-    } else if (wildcard_chr) {
-        wildcard = 1;
-    }
-
     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;
 
-    if (add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard) != 0) {
-        PLOG(ERROR) << "add_dev_perms(name=" << name <<
-                       ", attr=" << attr <<
-                       ", perm=" << std::oct << perm << std::dec <<
-                       ", uid=" << uid << ", gid=" << gid <<
-                       ", prefix=" << prefix << ", wildcard=" << wildcard <<
-                       ")";
-        return;
+    if (attr) {
+        sysfs_permissions.emplace_back(name, attr, perm, uid, gid);
+    } else {
+        dev_permissions.emplace_back(name, perm, uid, 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 73d97ed..32ae93d 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -14,26 +14,21 @@
  * 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>
@@ -42,15 +37,15 @@
 #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 "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) {
-    android::base::unique_fd fd(
-        TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 0600)));
+bool write_file(const std::string& path, const std::string& content) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+        open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
     if (fd == -1) {
         PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
         return false;
@@ -198,68 +194,21 @@
     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];
-    const char *slash;
-    const char *p = pathname;
-    int width;
-    int ret;
-    struct stat info;
-
-    while ((slash = strchr(p, '/')) != NULL) {
-        width = slash - pathname;
-        p = slash + 1;
-        if (width < 0)
-            break;
-        if (width == 0)
-            continue;
-        if ((unsigned int)width > sizeof(buf) - 1) {
-            LOG(ERROR) << "path too long for mkdir_recursive";
-            return -1;
-        }
-        memcpy(buf, pathname, width);
-        buf[width] = 0;
-        if (stat(buf, &info) != 0) {
-            ret = make_dir(buf, mode);
-            if (ret && errno != EEXIST)
-                return ret;
+int mkdir_recursive(const std::string& path, mode_t mode) {
+    std::string::size_type slash = 0;
+    while ((slash = path.find('/', slash + 1)) != std::string::npos) {
+        auto directory = path.substr(0, slash);
+        struct stat info;
+        if (stat(directory.c_str(), &info) != 0) {
+            auto ret = make_dir(directory.c_str(), mode);
+            if (ret && errno != EEXIST) return ret;
         }
     }
-    ret = make_dir(pathname, mode);
-    if (ret && errno != EEXIST)
-        return ret;
+    auto ret = make_dir(path.c_str(), mode);
+    if (ret && errno != EEXIST) return ret;
     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) {
diff --git a/init/util.h b/init/util.h
index 81c64d7..0bb9cdf 100644
--- a/init/util.h
+++ b/init/util.h
@@ -25,50 +25,42 @@
 #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);
 
 unsigned int decode_uid(const char *s);
 
-int mkdir_recursive(const char *pathname, mode_t mode);
-void sanitize(char *p);
+int mkdir_recursive(const std::string& pathname, mode_t mode);
 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 24c75c4..b8b409a 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -17,7 +17,11 @@
 #include "util.h"
 
 #include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
 
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
 TEST(util, read_file_ENOENT) {
@@ -28,6 +32,35 @@
   EXPECT_EQ("", s); // s was cleared.
 }
 
+TEST(util, read_file_group_writeable) {
+    std::string s("hello");
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    EXPECT_TRUE(write_file(tf.path, s)) << strerror(errno);
+    EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
+    EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+    EXPECT_EQ("", s);  // s was cleared.
+}
+
+TEST(util, read_file_world_writeable) {
+    std::string s("hello");
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
+    EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
+    EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+    EXPECT_EQ("", s);  // s was cleared.
+}
+
+TEST(util, read_file_symbolic_link) {
+    std::string s("hello");
+    errno = 0;
+    // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
+    EXPECT_FALSE(read_file("/charger", &s));
+    EXPECT_EQ(ELOOP, errno);
+    EXPECT_EQ("", s);  // s was cleared.
+}
+
 TEST(util, read_file_success) {
   std::string s("hello");
   EXPECT_TRUE(read_file("/proc/version", &s));
@@ -37,8 +70,89 @@
   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, s));
+    EXPECT_TRUE(read_file(path, &s2));
+    EXPECT_EQ(s, s2);
+    struct stat sb;
+    int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+    EXPECT_NE(-1, fd);
+    EXPECT_EQ(0, fstat(fd, &sb));
+    EXPECT_EQ((const unsigned int)(S_IRUSR | S_IWUSR), sb.st_mode & 0777);
+    EXPECT_EQ(0, unlink(path.c_str()));
+}
+
+TEST(util, write_file_exist) {
+    std::string s2("");
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    EXPECT_TRUE(write_file(tf.path, "1hello1")) << strerror(errno);
+    EXPECT_TRUE(read_file(tf.path, &s2));
+    EXPECT_STREQ("1hello1", s2.c_str());
+    EXPECT_TRUE(write_file(tf.path, "2ll2"));
+    EXPECT_TRUE(read_file(tf.path, &s2));
+    EXPECT_STREQ("2ll2", s2.c_str());
+}
+
 TEST(util, decode_uid) {
   EXPECT_EQ(0U, decode_uid("root"));
   EXPECT_EQ(UINT_MAX, decode_uid("toot"));
   EXPECT_EQ(123U, decode_uid("123"));
 }
+
+TEST(util, is_dir) {
+    TemporaryDir test_dir;
+    EXPECT_TRUE(is_dir(test_dir.path));
+    TemporaryFile tf;
+    EXPECT_FALSE(is_dir(tf.path));
+}
+
+// sehandle is needed for make_dir()
+// TODO: Remove once sehandle is encapsulated
+#include <selinux/label.h>
+selabel_handle* sehandle;
+
+TEST(util, mkdir_recursive) {
+    TemporaryDir test_dir;
+    std::string path = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
+    EXPECT_EQ(0, mkdir_recursive(path, 0755));
+    std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
+    EXPECT_TRUE(is_dir(path1.c_str()));
+    std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
+    EXPECT_TRUE(is_dir(path1.c_str()));
+    std::string path3 = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
+    EXPECT_TRUE(is_dir(path1.c_str()));
+}
+
+TEST(util, mkdir_recursive_extra_slashes) {
+    TemporaryDir test_dir;
+    std::string path = android::base::StringPrintf("%s/three////directories/deep//", test_dir.path);
+    EXPECT_EQ(0, mkdir_recursive(path, 0755));
+    std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
+    EXPECT_TRUE(is_dir(path1.c_str()));
+    std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
+    EXPECT_TRUE(is_dir(path1.c_str()));
+    std::string path3 = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
+    EXPECT_TRUE(is_dir(path1.c_str()));
+}
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 0d57814..b0ac5c4 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -25,6 +25,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/Android.bp b/libcutils/Android.bp
index 513736d..58170ec 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -24,7 +24,6 @@
     "socket_inaddr_any_server_unix.c",
     "socket_local_client_unix.c",
     "socket_local_server_unix.c",
-    "socket_loopback_server_unix.c",
     "socket_network_client_unix.c",
     "sockets_unix.cpp",
     "str_parms.c",
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c
index e0e6a34..96ca566 100644
--- a/libcutils/canned_fs_config.c
+++ b/libcutils/canned_fs_config.c
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -41,7 +42,7 @@
 }
 
 int load_canned_fs_config(const char* fn) {
-    char line[PATH_MAX + 200];
+    char buf[PATH_MAX + 200];
     FILE* f;
 
     f = fopen(fn, "r");
@@ -50,17 +51,21 @@
         return -1;
     }
 
-    while (fgets(line, sizeof(line), f)) {
+    while (fgets(buf, sizeof(buf), f)) {
         Path* p;
         char* token;
+        char* line = buf;
+        bool rootdir;
 
         while (canned_used >= canned_alloc) {
             canned_alloc = (canned_alloc+1) * 2;
             canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
         }
         p = canned_data + canned_used;
-        p->path = strdup(strtok(line, " "));
-        p->uid = atoi(strtok(NULL, " "));
+        if (line[0] == '/') line++;
+        rootdir = line[0] == ' ';
+        p->path = strdup(rootdir ? "" : strtok(line, " "));
+        p->uid = atoi(strtok(rootdir ? line : NULL, " "));
         p->gid = atoi(strtok(NULL, " "));
         p->mode = strtol(strtok(NULL, " "), NULL, 8);   // mode is in octal
         p->capabilities = 0;
diff --git a/libcutils/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
index d724dd6..b24468b 100644
--- a/libcutils/include/cutils/sockets.h
+++ b/libcutils/include/cutils/sockets.h
@@ -88,8 +88,6 @@
 cutils_socket_t socket_network_client(const char* host, int port, int type);
 int socket_network_client_timeout(const char* host, int port, int type,
                                   int timeout, int* getaddrinfo_error);
-int socket_loopback_server(int port, int type);
-int socket_loopback_server6(int port, int type);
 int socket_local_server(const char* name, int namespaceId, int type);
 int socket_local_server_bind(int s, const char* name, int namespaceId);
 int socket_local_client_connect(int fd, const char *name, int namespaceId,
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index 3837ca7..4a0b035 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -269,6 +269,8 @@
             *policy = SP_FOREGROUND;
         } else if (!strcmp(grpBuf, "foreground")) {
             *policy = SP_FOREGROUND;
+        } else if (!strcmp(grpBuf, "system-background")) {
+            *policy = SP_SYSTEM;
         } else if (!strcmp(grpBuf, "background")) {
             *policy = SP_BACKGROUND;
         } else if (!strcmp(grpBuf, "top-app")) {
diff --git a/libcutils/socket_loopback_server_unix.c b/libcutils/socket_loopback_server_unix.c
deleted file mode 100644
index 7b92fd6..0000000
--- a/libcutils/socket_loopback_server_unix.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
-** Copyright 2006, 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 <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#define LISTEN_BACKLOG 4
-
-#if !defined(_WIN32)
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#endif
-
-#include <cutils/sockets.h>
-
-static int _socket_loopback_server(int family, int type, struct sockaddr * addr, size_t size)
-{
-    int s, n;
-
-    s = socket(family, type, 0);
-    if(s < 0)
-        return -1;
-
-    n = 1;
-    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
-
-    if(bind(s, addr, size) < 0) {
-        close(s);
-        return -1;
-    }
-
-    if (type == SOCK_STREAM) {
-        int ret;
-
-        ret = listen(s, LISTEN_BACKLOG);
-
-        if (ret < 0) {
-            close(s);
-            return -1;
-        }
-    }
-
-    return s;
-}
-
-/* open listen() port on loopback IPv6 interface */
-int socket_loopback_server6(int port, int type)
-{
-    struct sockaddr_in6 addr;
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sin6_family = AF_INET6;
-    addr.sin6_port = htons(port);
-    addr.sin6_addr = in6addr_loopback;
-
-    return _socket_loopback_server(AF_INET6, type, (struct sockaddr *) &addr, sizeof(addr));
-}
-
-/* open listen() port on loopback interface */
-int socket_loopback_server(int port, int type)
-{
-    struct sockaddr_in addr;
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sin_family = AF_INET;
-    addr.sin_port = htons(port);
-    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
-    return _socket_loopback_server(AF_INET, type, (struct sockaddr *) &addr, sizeof(addr));
-}
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/event.logtags b/liblog/event.logtags
index 301e885..0a3b650 100644
--- a/liblog/event.logtags
+++ b/liblog/event.logtags
@@ -29,6 +29,7 @@
 # 4: Number of allocations
 # 5: Id
 # 6: Percent
+# s: Number of seconds (monotonic time)
 # Default value for data of type int/long is 2 (bytes).
 #
 # TODO: generate ".java" and ".h" files with integer constants from this file.
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 231f4d9..5fc7e35 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -230,9 +230,16 @@
   return it->second;
 }
 
+// The position after the end of a valid section of the tag string,
+// caller makes sure delimited appropriately.
+static const char* endOfTag(const char* cp) {
+  while (*cp && (isalnum(*cp) || strchr("_.-@,", *cp))) ++cp;
+  return cp;
+}
+
 // Scan one tag line.
 //
-// "*pData" should be pointing to the first digit in the tag number.  On
+// "pData" should be pointing to the first digit in the tag number.  On
 // successful return, it will be pointing to the last character in the
 // tag line (i.e. the character before the start of the next line).
 //
@@ -242,10 +249,11 @@
 // data and it will outlive the call.
 //
 // Returns 0 on success, nonzero on failure.
-static int scanTagLine(EventTagMap* map, char** pData, int lineNum) {
-  char* cp;
-  unsigned long val = strtoul(*pData, &cp, 10);
-  if (cp == *pData) {
+static int scanTagLine(EventTagMap* map, const char*& pData, int lineNum) {
+  char* ep;
+  unsigned long val = strtoul(pData, &ep, 10);
+  const char* cp = ep;
+  if (cp == pData) {
     if (lineNum) {
       fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
     }
@@ -274,14 +282,13 @@
   }
 
   const char* tag = cp;
-  // Determine whether "c" is a valid tag char.
-  while (isalnum(*++cp) || (*cp == '_')) {
-  }
+  cp = endOfTag(cp);
   size_t tagLen = cp - tag;
 
   if (!isspace(*cp)) {
     if (lineNum) {
-      fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n", lineNum);
+      fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp,
+              lineNum);
     }
     errno = EINVAL;
     return -1;
@@ -312,9 +319,9 @@
 
   while (*cp != '\n') ++cp;
 #ifdef DEBUG
-  fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
+  fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - pData), pData);
 #endif
-  *pData = cp;
+  pData = cp;
 
   if (lineNum) {
     if (map->emplaceUnique(tagIndex,
@@ -342,9 +349,9 @@
 
 // Parse the tags out of the file.
 static int parseMapLines(EventTagMap* map, size_t which) {
-  char* cp = static_cast<char*>(map->mapAddr[which]);
+  const char* cp = static_cast<char*>(map->mapAddr[which]);
   size_t len = map->mapLen[which];
-  char* endp = cp + len;
+  const char* endp = cp + len;
 
   // insist on EOL at EOF; simplifies parsing and null-termination
   if (!len || (*(endp - 1) != '\n')) {
@@ -371,7 +378,7 @@
         lineStart = false;
       } else if (isdigit(*cp)) {
         // looks like a tag; scan it out
-        if (scanTagLine(map, &cp, lineNum) != 0) {
+        if (scanTagLine(map, cp, lineNum) != 0) {
           if (!which || (errno != EMLINK)) {
             return -1;
           }
@@ -446,7 +453,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)) {
@@ -466,6 +473,7 @@
       delete newTagMap;
       return NULL;
     }
+    /* See 'fd DONE' comments above and below, no need to clean up here */
   }
 
   return newTagMap;
@@ -474,7 +482,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;
@@ -495,14 +503,13 @@
   int ret = asprintf(&buf, command_template, tag);
   if (ret > 0) {
     // Add some buffer margin for an estimate of the full return content.
-    char* cp;
     size_t size =
         ret - strlen(command_template) +
         strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
     if (size > (size_t)ret) {
-      cp = static_cast<char*>(realloc(buf, size));
-      if (cp) {
-        buf = cp;
+      char* np = static_cast<char*>(realloc(buf, size));
+      if (np) {
+        buf = np;
       } else {
         size = ret;
       }
@@ -512,10 +519,12 @@
     // Ask event log tag service for an existing entry
     if (__send_log_msg(buf, size) >= 0) {
       buf[size - 1] = '\0';
-      unsigned long val = strtoul(buf, &cp, 10);        // return size
+      char* ep;
+      unsigned long val = strtoul(buf, &ep, 10);  // return size
+      const char* cp = ep;
       if ((buf != cp) && (val > 0) && (*cp == '\n')) {  // truncation OK
         ++cp;
-        if (!scanTagLine(map, &cp, 0)) {
+        if (!scanTagLine(map, cp, 0)) {
           free(buf);
           return map->find(tag);
         }
@@ -573,8 +582,9 @@
 LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
                                                 const char* tagname,
                                                 const char* format, int prio) {
-  size_t len = strlen(tagname);
-  if (!len) {
+  const char* ep = endOfTag(tagname);
+  size_t len = ep - tagname;
+  if (!len || *ep) {
     errno = EINVAL;
     return -1;
   }
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index 55953fc..057be5d 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -17,6 +17,7 @@
 #ifndef _LIBS_LOG_EVENT_LIST_H
 #define _LIBS_LOG_EVENT_LIST_H
 
+#include <errno.h>
 #include <stdint.h>
 
 #if (defined(__cplusplus) && defined(_USING_LIBCXX))
@@ -148,6 +149,7 @@
     return ctx;
   }
 
+  /* return errors or transmit status */
   int status() const {
     return ret;
   }
@@ -209,14 +211,16 @@
   }
 
   int write(log_id_t id = LOG_ID_EVENTS) {
+    /* facilitate -EBUSY retry */
+    if ((ret == -EBUSY) || (ret > 0)) ret = 0;
     int retval = android_log_write_list(ctx, id);
-    if (retval < 0) ret = retval;
+    /* existing errors trump transmission errors */
+    if (!ret) ret = retval;
     return ret;
   }
 
   int operator<<(log_id_t id) {
-    int retval = android_log_write_list(ctx, id);
-    if (retval < 0) ret = retval;
+    write(id);
     android_log_destroy(&ctx);
     return ret;
   }
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/logprint.c b/liblog/logprint.c
index 2ade7b0..b62f8b4 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -326,6 +326,7 @@
   else if (!strcmp(formatString, "uid")) format = FORMAT_MODIFIER_UID;
   else if (!strcmp(formatString, "descriptive")) format = FORMAT_MODIFIER_DESCRIPT;
   /* clang-format on */
+
 #ifndef __MINGW32__
   else {
     extern char* tzname[2];
@@ -637,7 +638,8 @@
   TYPE_MILLISECONDS = '3',
   TYPE_ALLOCATIONS = '4',
   TYPE_ID = '5',
-  TYPE_PERCENT = '6'
+  TYPE_PERCENT = '6',
+  TYPE_MONOTONIC = 's'
 };
 
 static int android_log_printBinaryEvent(const unsigned char** pEventData,
@@ -651,7 +653,7 @@
   size_t outBufLen = *pOutBufLen;
   size_t outBufLenSave = outBufLen;
   unsigned char type;
-  size_t outCount;
+  size_t outCount = 0;
   int result = 0;
   const char* cp;
   size_t len;
@@ -690,6 +692,7 @@
    * 4: Number of allocations
    * 5: Id
    * 6: Percent
+   * s: Number of seconds (monotonic time)
    * Default value for data of type int/long is 2 (bytes).
    */
   if (!cp || !findChar(&cp, &len, '(')) {
@@ -921,6 +924,42 @@
             outCount = snprintf(outBuf, outBufLen, "ms");
           }
           break;
+        case TYPE_MONOTONIC: {
+          static const uint64_t minute = 60;
+          static const uint64_t hour = 60 * minute;
+          static const uint64_t day = 24 * hour;
+
+          /* Repaint as unsigned seconds, minutes, hours ... */
+          outBuf -= outCount;
+          outBufLen += outCount;
+          uint64_t val = lval;
+          if (val >= day) {
+            outCount = snprintf(outBuf, outBufLen, "%" PRIu64 "d ", val / day);
+            if (outCount >= outBufLen) break;
+            outBuf += outCount;
+            outBufLen -= outCount;
+            val = (val % day) + day;
+          }
+          if (val >= minute) {
+            if (val >= hour) {
+              outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":",
+                                  (val / hour) % (day / hour));
+              if (outCount >= outBufLen) break;
+              outBuf += outCount;
+              outBufLen -= outCount;
+            }
+            outCount =
+                snprintf(outBuf, outBufLen,
+                         (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+                         (val / minute) % (hour / minute));
+            if (outCount >= outBufLen) break;
+            outBuf += outCount;
+            outBufLen -= outCount;
+          }
+          outCount = snprintf(outBuf, outBufLen,
+                              (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
+                              val % minute);
+        } break;
         case TYPE_ALLOCATIONS:
           outCount = 0;
           /* outCount = snprintf(outBuf, outBufLen, " allocations"); */
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/libnativebridge/.clang-format b/libnativebridge/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libnativebridge/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 7976f67..050373a 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -575,15 +575,15 @@
 
 bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
                                         const char* anon_ns_library_path) {
-    if (NativeBridgeInitialized()) {
-        if (isCompatibleWith(NAMESPACE_VERSION)) {
-            return callbacks->initAnonymousNamespace(public_ns_sonames, anon_ns_library_path);
-        } else {
-            ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
-        }
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->initAnonymousNamespace(public_ns_sonames, anon_ns_library_path);
+    } else {
+      ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
     }
+  }
 
-    return false;
+  return false;
 }
 
 native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
@@ -610,15 +610,15 @@
 
 bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
                                 const char* shared_libs_sonames) {
-    if (NativeBridgeInitialized()) {
-        if (isCompatibleWith(NAMESPACE_VERSION)) {
-            return callbacks->linkNamespaces(from, to, shared_libs_sonames);
-        } else {
-            ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
-        }
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->linkNamespaces(from, to, shared_libs_sonames);
+    } else {
+      ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
     }
+  }
 
-    return false;
+  return false;
 }
 
 void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp
index b0dd6d0..4ef1c82 100644
--- a/libnativebridge/tests/DummyNativeBridge3.cpp
+++ b/libnativebridge/tests/DummyNativeBridge3.cpp
@@ -78,7 +78,7 @@
 
 extern "C" bool native_bridge3_initAnonymousNamespace(const char* /* public_ns_sonames */,
                                                       const char* /* anon_ns_library_path */) {
-    return true;
+  return true;
 }
 
 extern "C" android::native_bridge_namespace_t*
@@ -94,7 +94,7 @@
 extern "C" bool native_bridge3_linkNamespaces(android::native_bridge_namespace_t* /* from */,
                                               android::native_bridge_namespace_t* /* to */,
                                               const char* /* shared_libs_soname */) {
-    return true;
+  return true;
 }
 
 extern "C" void* native_bridge3_loadLibraryExt(const char* /* libpath */,
diff --git a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
index 989a819..b0d6b09 100644
--- a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
+++ b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
@@ -21,19 +21,19 @@
 constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
 
 TEST_F(NativeBridgeTest, V3_InitAnonymousNamespace) {
-    // Init
-    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
-    ASSERT_TRUE(NativeBridgeAvailable());
-    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
-    ASSERT_TRUE(NativeBridgeAvailable());
-    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
-    ASSERT_TRUE(NativeBridgeAvailable());
+  // Init
+  ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+  ASSERT_TRUE(NativeBridgeAvailable());
+  ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+  ASSERT_TRUE(NativeBridgeAvailable());
+  ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+  ASSERT_TRUE(NativeBridgeAvailable());
 
-    ASSERT_EQ(3U, NativeBridgeGetVersion());
-    ASSERT_EQ(true, NativeBridgeInitAnonymousNamespace(nullptr, nullptr));
+  ASSERT_EQ(3U, NativeBridgeGetVersion());
+  ASSERT_EQ(true, NativeBridgeInitAnonymousNamespace(nullptr, nullptr));
 
-    // Clean-up code_cache
-    ASSERT_EQ(0, rmdir(kCodeCache));
+  // Clean-up code_cache
+  ASSERT_EQ(0, rmdir(kCodeCache));
 }
 
 }  // namespace android
diff --git a/libnativeloader/.clang-format b/libnativeloader/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libnativeloader/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index ebd6d4b..f3391d1 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -184,8 +184,8 @@
       }
 
       if (!NativeBridgeLinkNamespaces(ns, nullptr, public_libraries_.c_str())) {
-          *error_msg = NativeBridgeGetError();
-          return false;
+        *error_msg = NativeBridgeGetError();
+        return false;
       }
 
       native_loader_ns = NativeLoaderNamespace(ns);
@@ -329,10 +329,10 @@
 
     // and now initialize native bridge namespaces if necessary.
     if (NativeBridgeInitialized()) {
-        initialized_ = NativeBridgeInitAnonymousNamespace(
-            public_libraries_.c_str(), is_native_bridge ? library_path : nullptr);
-        if (!initialized_) {
-            *error_msg = NativeBridgeGetError();
+      initialized_ = NativeBridgeInitAnonymousNamespace(public_libraries_.c_str(),
+                                                        is_native_bridge ? library_path : nullptr);
+      if (!initialized_) {
+        *error_msg = NativeBridgeGetError();
       }
     }
 
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/logcat/event.logtags b/logcat/event.logtags
index 909f8e2..efcc817 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -30,12 +30,15 @@
 # 4: Number of allocations
 # 5: Id
 # 6: Percent
+# s: Number of seconds (monotonic time)
 # Default value for data of type int/long is 2 (bytes).
 #
 # TODO: generate ".java" and ".h" files with integer constants from this file.
 
 # 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/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 21868f2..e487a97 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -17,6 +17,7 @@
 #include <ctype.h>
 #include <dirent.h>
 #include <signal.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -31,6 +32,7 @@
 
 #include <android-base/file.h>
 #include <gtest/gtest.h>
+#include <log/event_tag_map.h>
 #include <log/log.h>
 #include <log/log_event_list.h>
 
@@ -47,6 +49,16 @@
 
 #define BIG_BUFFER (5 * 1024)
 
+// rest(), let the logs settle.
+//
+// logd is in a background cgroup and under extreme load can take up to
+// 3 seconds to land a log entry. Under moderate load we can do with 200ms.
+static void rest() {
+    static const useconds_t restPeriod = 200000;
+
+    usleep(restPeriod);
+}
+
 // enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
 // non-syscall libs. Since we are only using this in the emergency of
 // a signal to stuff a terminating code into the logs, we will spin rather
@@ -70,7 +82,7 @@
 #undef LOG_TAG
 #define LOG_TAG "inject"
     RLOGE(logcat_executable ".buckets");
-    sleep(1);
+    rest();
 
     ASSERT_TRUE(NULL !=
                 (fp = logcat_popen(
@@ -1412,7 +1424,7 @@
     LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
                                           logcat_regex_prefix "_aaaa"));
     // Let the logs settle
-    sleep(1);
+    rest();
 
     ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
@@ -1450,8 +1462,7 @@
     LOG_FAILURE_RETRY(
         __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
 
-    // Let the logs settle
-    sleep(1);
+    rest();
 
     ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
@@ -1476,8 +1487,7 @@
 
 static bool End_to_End(const char* tag, const char* fmt, ...) {
     logcat_define(ctx);
-    FILE* fp = logcat_popen(ctx,
-                            "logcat"
+    FILE* fp = logcat_popen(ctx, logcat_executable
                             " -v brief"
                             " -b events"
                             " -v descriptive"
@@ -1523,13 +1533,12 @@
         // Help us pinpoint where things went wrong ...
         fprintf(stderr, "Closest match for\n    %s\n  is\n    %s",
                 expect.c_str(), lastMatch.c_str());
-    } else if (count > 2) {
+    } else if (count > 3) {
         fprintf(stderr, "Too many matches (%d) for %s\n", count, expect.c_str());
     }
 
-    // Expect one the first time around as either liblogcat.descriptive or
-    // logcat.descriptive.  Expect two the second time as the other.
-    return count == 1 || count == 2;
+    // Three different known tests, we can see pollution from the others
+    return count && (count <= 3);
 }
 
 TEST(logcat, descriptive) {
@@ -1537,24 +1546,28 @@
         uint32_t tagNo;
         const char* tagStr;
     };
+    int ret;
 
     {
         static const struct tag hhgtg = { 42, "answer" };
         android_log_event_list ctx(hhgtg.tagNo);
         static const char theAnswer[] = "what is five by seven";
         ctx << theAnswer;
-        ctx.write();
+        // crafted to rest at least once after, and rest between retries.
+        for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+        EXPECT_LE(0, ret);
         EXPECT_TRUE(
             End_to_End(hhgtg.tagStr, "to life the universe etc=%s", theAnswer));
     }
 
     {
         static const struct tag sync = { 2720, "sync" };
-        static const char id[] = "logcat.decriptive";
+        static const char id[] = ___STRING(logcat) ".descriptive-sync";
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
             EXPECT_TRUE(End_to_End(sync.tagStr,
                                    "[id=%s,event=42,source=-1,account=0]", id));
         }
@@ -1563,7 +1576,8 @@
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << id << (int32_t)43 << (int64_t)-1 << (int32_t)0;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
             EXPECT_TRUE(End_to_End(sync.tagStr, "[id=%s,event=43,-1,0]", id));
         }
 
@@ -1571,7 +1585,8 @@
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << id << (int32_t)44 << (int32_t)-1 << (int64_t)0;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
             fprintf(stderr, "Expect a \"Closest match\" message\n");
             EXPECT_FALSE(End_to_End(
                 sync.tagStr, "[id=%s,event=44,source=-1,account=0]", id));
@@ -1583,7 +1598,8 @@
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint64_t)30 << (int32_t)2;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
             EXPECT_TRUE(
                 End_to_End(sync.tagStr, "[aggregation time=30ms,count=2]"));
         }
@@ -1591,7 +1607,8 @@
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint64_t)31570 << (int32_t)911;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
             EXPECT_TRUE(
                 End_to_End(sync.tagStr, "[aggregation time=31.57s,count=911]"));
         }
@@ -1602,42 +1619,48 @@
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)512;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=512B"));
         }
 
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)3072;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=3KB"));
         }
 
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)2097152;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=2MB"));
         }
 
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)2097153;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=2097153B"));
         }
 
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)1073741824;
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=1GB"));
         }
 
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint32_t)3221225472;  // 3MB, but on purpose overflowed
-            ctx.write();
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB"));
         }
     }
@@ -1645,9 +1668,52 @@
     {
         static const struct tag sync = { 27501, "notification_panel_hidden" };
         android_log_event_list ctx(sync.tagNo);
-        ctx.write();
+        for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+        EXPECT_LE(0, ret);
         EXPECT_TRUE(End_to_End(sync.tagStr, ""));
     }
+
+    {
+        // Invent new entries because existing can not serve
+        EventTagMap* map = android_openEventTagMap(nullptr);
+        ASSERT_TRUE(nullptr != map);
+        static const char name[] = ___STRING(logcat) ".descriptive-monotonic";
+        int myTag = android_lookupEventTagNum(map, name, "(new|1|s)",
+                                              ANDROID_LOG_UNKNOWN);
+        android_closeEventTagMap(map);
+        ASSERT_NE(-1, myTag);
+
+        const struct tag sync = { (uint32_t)myTag, name };
+
+        {
+            android_log_event_list ctx(sync.tagNo);
+            ctx << (uint32_t)7;
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
+            EXPECT_TRUE(End_to_End(sync.tagStr, "new=7s"));
+        }
+        {
+            android_log_event_list ctx(sync.tagNo);
+            ctx << (uint32_t)62;
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
+            EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:02"));
+        }
+        {
+            android_log_event_list ctx(sync.tagNo);
+            ctx << (uint32_t)3673;
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
+            EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:01:13"));
+        }
+        {
+            android_log_event_list ctx(sync.tagNo);
+            ctx << (uint32_t)(86400 + 7200 + 180 + 58);
+            for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+            EXPECT_LE(0, ret);
+            EXPECT_TRUE(End_to_End(sync.tagStr, "new=1d 2:03:58"));
+        }
+    }
 }
 
 static bool reportedSecurity(const char* command) {
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index cc30f77..d3167ad 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -248,18 +248,38 @@
                       std::string(isprune ? "NUM" : ""));
 }
 
+// Helper to truncate name, if too long, and add name dressings
+static void formatTmp(const LogStatistics& stat, const char* nameTmp, uid_t uid,
+                      std::string& name, std::string& size, size_t nameLen) {
+    const char* allocNameTmp = nullptr;
+    if (!nameTmp) nameTmp = allocNameTmp = stat.uidToName(uid);
+    if (nameTmp) {
+        size_t lenSpace = std::max(nameLen - name.length(), (size_t)1);
+        size_t len = EntryBaseConstants::total_len -
+                     EntryBaseConstants::pruned_len - size.length() -
+                     name.length() - lenSpace - 2;
+        size_t lenNameTmp = strlen(nameTmp);
+        while ((len < lenNameTmp) && (lenSpace > 1)) {
+            ++len;
+            --lenSpace;
+        }
+        name += android::base::StringPrintf("%*s", (int)lenSpace, "");
+        if (len < lenNameTmp) {
+            name += "...";
+            nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
+        }
+        name += nameTmp;
+        free(const_cast<char*>(allocNameTmp));
+    }
+}
+
 std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
     uid_t uid = getUid();
     std::string name = android::base::StringPrintf("%u", uid);
-    const char* nameTmp = stat.uidToName(uid);
-    if (nameTmp) {
-        name += android::base::StringPrintf(
-            "%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", nameTmp);
-        free(const_cast<char*>(nameTmp));
-    }
-
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
+    formatTmp(stat, nullptr, uid, name, size, 6);
+
     std::string pruned = "";
     if (worstUidEnabledForLogid(id)) {
         size_t totalDropped = 0;
@@ -366,18 +386,10 @@
     uid_t uid = getUid();
     pid_t pid = getPid();
     std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
-    const char* nameTmp = getName();
-    if (nameTmp) {
-        name += android::base::StringPrintf(
-            "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
-    } else if ((nameTmp = stat.uidToName(uid))) {
-        name += android::base::StringPrintf(
-            "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
-        free(const_cast<char*>(nameTmp));
-    }
-
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
+    formatTmp(stat, getName(), uid, name, size, 12);
+
     std::string pruned = "";
     size_t dropped = getDropped();
     if (dropped) {
@@ -398,21 +410,10 @@
                              log_id_t /* id */) const {
     uid_t uid = getUid();
     std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
-    const char* nameTmp = getName();
-    if (nameTmp) {
-        name += android::base::StringPrintf(
-            "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
-    } else if ((nameTmp = stat.uidToName(uid))) {
-        // if we do not have a PID name, lets punt to try UID name?
-        name += android::base::StringPrintf(
-            "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
-        free(const_cast<char*>(nameTmp));
-        // We tried, better to not have a name at all, we still
-        // have TID/UID by number to report in any case.
-    }
-
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
+    formatTmp(stat, getName(), uid, name, size, 12);
+
     std::string pruned = "";
     size_t dropped = getDropped();
     if (dropped) {
diff --git a/logd/event.logtags b/logd/event.logtags
index 39063a9..fa13a62 100644
--- a/logd/event.logtags
+++ b/logd/event.logtags
@@ -29,6 +29,7 @@
 # 4: Number of allocations
 # 5: Id
 # 6: Percent
+# s: Number of seconds (monotonic time)
 # Default value for data of type int/long is 2 (bytes).
 #
 # TODO: generate ".java" and ".h" files with integer constants from this file.
diff --git a/logd/main.cpp b/logd/main.cpp
index 946485b..18029eb 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -407,6 +407,11 @@
 // logging plugins like auditd and restart control. Additional
 // transitory per-client threads are created for each reader.
 int main(int argc, char* argv[]) {
+    // logd is written under the assumption that the timezone is UTC.
+    // If TZ is not set, persist.sys.timezone is looked up in some time utility
+    // libc functions, including mktime. It confuses the logd time handling,
+    // so here explicitly set TZ to UTC, which overrides the property.
+    setenv("TZ", "UTC", 1);
     // issue reinit command. KISS argument parsing.
     if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
         return issueReinit();
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/rootdir/init.rc b/rootdir/init.rc
index 15bbd32..f6b7039 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -157,25 +157,25 @@
     # this ensures that the cpusets are present and usable, but the device's
     # init.rc must actually set the correct cpus
     mkdir /dev/cpuset/foreground
-    write /dev/cpuset/foreground/cpus 0
-    write /dev/cpuset/foreground/mems 0
+    copy /dev/cpuset/cpus /dev/cpuset/foreground/cpus
+    copy /dev/cpuset/mems /dev/cpuset/foreground/mems
     mkdir /dev/cpuset/foreground/boost
-    write /dev/cpuset/foreground/boost/cpus 0
-    write /dev/cpuset/foreground/boost/mems 0
+    copy /dev/cpuset/cpus /dev/cpuset/foreground/boost/cpus
+    copy /dev/cpuset/mems /dev/cpuset/foreground/boost/mems
     mkdir /dev/cpuset/background
-    write /dev/cpuset/background/cpus 0
-    write /dev/cpuset/background/mems 0
+    copy /dev/cpuset/cpus /dev/cpuset/background/cpus
+    copy /dev/cpuset/mems /dev/cpuset/background/mems
 
     # system-background is for system tasks that should only run on
     # little cores, not on bigs
     # to be used only by init, so don't change system-bg permissions
     mkdir /dev/cpuset/system-background
-    write /dev/cpuset/system-background/cpus 0
-    write /dev/cpuset/system-background/mems 0
+    copy /dev/cpuset/cpus /dev/cpuset/system-background/cpus
+    copy /dev/cpuset/mems /dev/cpuset/system-background/mems
 
     mkdir /dev/cpuset/top-app
-    write /dev/cpuset/top-app/cpus 0
-    write /dev/cpuset/top-app/mems 0
+    copy /dev/cpuset/cpus /dev/cpuset/top-app/cpus
+    copy /dev/cpuset/mems /dev/cpuset/top-app/mems
 
     # change permissions for all cpusets we'll touch at runtime
     chown system system /dev/cpuset
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index f5c93b7..29fea01 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,5 +1,37 @@
 subsystem adf
-	devname uevent_devname
+    devname uevent_devname
+
+subsystem graphics
+    devname uevent_devpath
+    dirname /dev/graphics
+
+subsystem drm
+    devname uevent_devpath
+    dirname /dev/dri
+
+subsystem oncrpc
+    devname uevent_devpath
+    dirname /dev/oncrpc
+
+subsystem adsp
+    devname uevent_devpath
+    dirname /dev/adsp
+
+subsystem msm_camera
+    devname uevent_devpath
+    dirname /dev/msm_camera
+
+subsystem input
+    devname uevent_devpath
+    dirname /dev/input
+
+subsystem mtd
+    devname uevent_devpath
+    dirname /dev/mtd
+
+subsystem sound
+    devname uevent_devpath
+    dirname /dev/snd
 
 # ueventd can only set permissions on device nodes and their associated
 # sysfs attributes, not on arbitrary paths.
@@ -22,9 +54,6 @@
 /dev/hwbinder             0666   root       root
 /dev/vndbinder            0666   root       root
 
-# Anyone can read the logs, but if they're not in the "logs"
-# group, then they'll only see log entries for their UID.
-/dev/log/*                0666   root       log
 /dev/pmsg0                0222   root       log
 
 # the msm hw3d client device node is world writable/readable.
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",
+    ],
+}
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
new file mode 100644
index 0000000..5d10c18
--- /dev/null
+++ b/shell_and_utilities/README.md
@@ -0,0 +1,157 @@
+Android's shell and utilities
+=============================
+
+Since IceCreamSandwich Android has used
+[mksh](https://www.mirbsd.org/mksh.htm) as its shell. Before then it used
+[ash](https://en.wikipedia.org/wiki/Almquist_shell) (which actually
+remained in the tree up to and including KitKat).
+
+Initially Android had a very limited command-line provided by its
+own "toolbox" binary. These days almost everything is supplied by
+[toybox](http://landley.net/toybox/) instead.
+
+We started moving a few of the more important tools to full
+BSD implementations in JellyBean before we started in earnest in
+Lollipop. Lollipop was a major break with the past in many ways (LP64
+support and the switch to ART both having lots of knock-on effects around
+the system), so although this was the beginning of the end of toolbox it
+(a) didn't stand out given all the other systems-level changes and (b)
+in Marshmallow we changed direction and started the move to toybox.
+
+The lists below show what tools were provided and where they came from in
+each release starting with Gingerbread. This doesn't tell the full story,
+because the toolbox implementations did have bugs fixed and options added
+over the years. Gingerbread's rm, for example, supported `-r`/`-R` but not
+`-f`. But this gives you an idea of what was available in any given release,
+and how usable it was likely to be.
+
+
+Android 2.3 (Gingerbread)
+-------------------------
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+umount uptime vmstat watchprops wipe
+
+
+Android 4.0 (IceCreamSandwich)
+------------------------------
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+touch umount uptime vmstat watchprops wipe
+
+
+Android 4.1-4.3 (JellyBean)
+---------------------------
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mount mv nandread netstat notify
+printenv ps reboot renice restorecon rm rmdir rmmod route runcon schedtop
+sendevent setconsole setenforce setprop setsebool sleep smd start stop
+sync top touch umount uptime vmstat watchprops wipe
+
+
+Android 4.4 (KitKat)
+--------------------
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mkswap mount mv nandread netstat
+notify printenv ps readlink renice restorecon rm rmdir rmmod route runcon
+schedtop sendevent setconsole setenforce setprop setsebool sleep smd start
+stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
+
+
+Android 5.0 (Lollipop)
+----------------------
+
+BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
+
+toolbox: chcon chmod clear cmp date df dmesg getenforce getevent getprop
+getsebool hd id ifconfig iftop insmod ioctl ionice load\_policy log ls
+lsmod lsof md5 mkdir mknod mkswap mount nandread netstat newfs\_msdos
+nohup notify ps readlink renice restorecon rmmod route runcon schedtop
+sendevent setenforce setprop setsebool smd start stop swapoff swapon
+top touch umount uptime vmstat watchprops wipe
+
+
+Android 6.0 (Marshmallow)
+-------------------------
+
+BSD: dd du grep
+
+toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
+newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
+
+toybox: acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
+env expand expr fallocate false find free getenforce getprop groups
+head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
+logname losetup lsmod lsusb md5sum mkdir mknod mkswap mktemp modinfo
+more mountpoint mv netstat nice nl nohup od paste patch pgrep pidof
+pkill pmap printenv printf pwd readlink realpath restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate umount uname uniq unix2dos usleep
+vmstat wc which whoami xargs yes
+
+
+Android 7.0 (Nougat)
+--------------------
+
+BSD: dd grep
+
+toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
+sendevent start stop top
+
+toybox: acpi base64 basename blockdev bzcat cal cat chcon chgrp chmod
+chown chroot cksum clear comm cmp cp cpio cut date df dirname dmesg
+dos2unix du echo env expand expr fallocate false find flock free
+getenforce getprop groups head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall load\_policy ln logname losetup ls
+lsmod lsof lsusb md5sum mkdir mknod mkswap mktemp modinfo more mount
+mountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill
+pmap printenv printf pwd readlink realpath renice restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate tty ulimit umount uname uniq unix2dos
+uptime usleep vmstat wc which whoami xargs xxd yes
+
+
+Current AOSP
+------------
+
+BSD: dd grep
+
+bzip2: bzcat bzip2 bunzip2
+
+toolbox: getevent gzip newfs\_msdos gunzip zcat
+
+toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
+dos2unix du echo env expand expr fallocate false file find flock free
+getenforce getprop groups head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall ln load\_policy log logname losetup
+ls lsmod lsof lsusb md5sum microcom mkdir mknod mkswap mktemp modinfo
+modprobe more mount mountpoint mv netstat nice nl nohup od paste patch
+pgrep pidof pkill pmap printenv printf ps pwd readlink realpath renice
+restorecon rm rmdir rmmod runcon sed sendevent seq setenforce setprop
+setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
+start stat stop strings swapoff swapon sync sysctl tac tail tar taskset
+tee time timeout top touch tr true truncate tty ulimit umount uname uniq
+unix2dos uptime usleep uudecode uuencode vmstat wc which whoami xargs
+xxd yes
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index d6ead1a..aa755ed 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -95,3 +95,13 @@
 LOCAL_MODULE := grep
 LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,egrep fgrep,ln -sf grep $(TARGET_OUT)/bin/$(t);)
 include $(BUILD_EXECUTABLE)
+
+
+# We build gzip separately, so it can provide gunzip and zcat too.
+include $(CLEAR_VARS)
+LOCAL_MODULE := gzip
+LOCAL_SRC_FILES := gzip.c
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_SHARED_LIBRARIES += libz
+LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,gunzip zcat,ln -sf gzip $(TARGET_OUT)/bin/$(t);)
+include $(BUILD_EXECUTABLE)
diff --git a/toolbox/gzip.c b/toolbox/gzip.c
new file mode 100644
index 0000000..62c4518
--- /dev/null
+++ b/toolbox/gzip.c
@@ -0,0 +1,261 @@
+/* gzip.c - gzip/gunzip/zcat tools for gzip data
+ *
+ * Copyright 2017 The Android Open Source Project
+ *
+ * GZIP RFC: http://www.ietf.org/rfc/rfc1952.txt
+
+TODO: port to toybox.
+
+*/
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <zlib.h>
+
+// toybox-style flags/globals.
+#define FLAG_c 1
+#define FLAG_d 2
+#define FLAG_f 4
+#define FLAG_k 8
+static struct {
+  int optflags;
+} toys;
+static struct {
+  int level;
+} TT;
+
+static void xstat(const char *path, struct stat *sb)
+{
+  if (stat(path, sb)) error(1, errno, "stat %s", path);
+}
+
+static void fix_time(const char *path, struct stat *sb)
+{
+  struct timespec times[] = { sb->st_atim, sb->st_mtim };
+
+  if (utimensat(AT_FDCWD, path, times, 0)) error(1, errno, "utimes");
+}
+
+static FILE *xfdopen(const char *name, int flags, mode_t open_mode,
+    const char *mode)
+{
+  FILE *fp;
+  int fd;
+
+  if (!strcmp(name, "-")) fd = dup((*mode == 'r') ? 0 : 1);
+  else fd = open(name, flags, open_mode);
+
+  if (fd == -1) error(1, errno, "open %s (%s)", name, mode);
+  fp = fdopen(fd, mode);
+  if (fp == NULL) error(1, errno, "fopen %s (%s)", name, mode);
+  return fp;
+}
+
+static gzFile xgzopen(const char *name, int flags, mode_t open_mode,
+    const char *mode)
+{
+  gzFile f;
+  int fd;
+
+  if (!strcmp(name, "-")) fd = dup((*mode == 'r') ? 0 : 1);
+  else fd = open(name, flags, open_mode);
+
+  if (fd == -1) error(1, errno, "open %s (%s)", name, mode);
+  f = gzdopen(fd, mode);
+  if (f == NULL) error(1, errno, "gzdopen %s (%s)", name, mode);
+  return f;
+}
+
+static void gzfatal(gzFile f, char *what)
+{
+  int err;
+  const char *msg = gzerror(f, &err);
+
+  error(1, (err == Z_ERRNO) ? errno : 0, "%s: %s", what, msg);
+}
+
+static void gunzip(char *arg)
+{
+  struct stat sb;
+  char buf[BUFSIZ];
+  int len, both_files;
+  char *in_name, *out_name;
+  gzFile in;
+  FILE *out;
+
+  // "gunzip x.gz" will decompress "x.gz" to "x".
+  len = strlen(arg);
+  if (len > 3 && !strcmp(arg+len-3, ".gz")) {
+    in_name = strdup(arg);
+    out_name = strdup(arg);
+    out_name[len-3] = '\0';
+  } else if (!strcmp(arg, "-")) {
+    // "-" means stdin; assume output to stdout.
+    // TODO: require -f to read compressed data from tty?
+    in_name = strdup("-");
+    out_name = strdup("-");
+  } else error(1, 0, "unknown suffix");
+
+  if (toys.optflags&FLAG_c) {
+    free(out_name);
+    out_name = strdup("-");
+  }
+
+  both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
+  if (both_files) xstat(in_name, &sb);
+
+  in = xgzopen(in_name, O_RDONLY, 0, "r");
+  out = xfdopen(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
+      both_files?sb.st_mode:0666, "w");
+
+  while ((len = gzread(in, buf, sizeof(buf))) > 0) {
+    if (fwrite(buf, 1, len, out) != (size_t) len) error(1, errno, "fwrite");
+  }
+  if (len < 0) gzfatal(in, "gzread");
+  if (fclose(out)) error(1, errno, "fclose");
+  if (gzclose(in) != Z_OK) error(1, 0, "gzclose");
+
+  if (both_files) fix_time(out_name, &sb);
+  if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
+  free(in_name);
+  free(out_name);
+}
+
+static void gzip(char *in_name)
+{
+  char buf[BUFSIZ];
+  size_t len;
+  char *out_name;
+  FILE *in;
+  gzFile out;
+  struct stat sb;
+  int both_files;
+
+  if (toys.optflags&FLAG_c) {
+    out_name = strdup("-");
+  } else {
+    if (asprintf(&out_name, "%s.gz", in_name) == -1) {
+      error(1, errno, "asprintf");
+    }
+  }
+
+  both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
+  if (both_files) xstat(in_name, &sb);
+
+  snprintf(buf, sizeof(buf), "w%d", TT.level);
+  in = xfdopen(in_name, O_RDONLY, 0, "r");
+  out = xgzopen(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
+      both_files?sb.st_mode:0, buf);
+
+  while ((len = fread(buf, 1, sizeof(buf), in)) > 0) {
+    if (gzwrite(out, buf, len) != (int) len) gzfatal(out, "gzwrite");
+  }
+  if (ferror(in)) error(1, errno, "fread");
+  if (fclose(in)) error(1, errno, "fclose");
+  if (gzclose(out) != Z_OK) error(1, 0, "gzclose");
+
+  if (both_files) fix_time(out_name, &sb);
+  if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
+  free(out_name);
+}
+
+static void do_file(char *arg)
+{
+  if (toys.optflags&FLAG_d) gunzip(arg);
+  else gzip(arg);
+}
+
+static void usage()
+{
+  char *cmd = basename(getprogname());
+
+  printf("usage: %s [-c] [-d] [-f] [-#] [FILE...]\n", cmd);
+  printf("\n");
+  if (!strcmp(cmd, "zcat")) {
+    printf("Decompress files to stdout. Like `gzip -dc`.\n");
+    printf("\n");
+    printf("-c\tOutput to stdout\n");
+    printf("-f\tForce: allow read from tty\n");
+  } else if (!strcmp(cmd, "gunzip")) {
+    printf("Decompress files. With no files, decompresses stdin to stdout.\n");
+    printf("On success, the input files are removed and replaced by new\n");
+    printf("files without the .gz suffix.\n");
+    printf("\n");
+    printf("-c\tOutput to stdout\n");
+    printf("-f\tForce: allow read from tty\n");
+    printf("-k\tKeep input files (don't remove)\n");
+  } else { // gzip
+    printf("Compress files. With no files, compresses stdin to stdout.\n");
+    printf("On success, the input files are removed and replaced by new\n");
+    printf("files with the .gz suffix.\n");
+    printf("\n");
+    printf("-c\tOutput to stdout\n");
+    printf("-d\tDecompress (act as gunzip)\n");
+    printf("-f\tForce: allow overwrite of output file\n");
+    printf("-k\tKeep input files (don't remove)\n");
+    printf("-#\tCompression level 1-9 (1:fastest, 6:default, 9:best)\n");
+  }
+  printf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+  char *cmd = basename(argv[0]);
+  int opt_ch;
+
+  toys.optflags = 0;
+  TT.level = 6;
+
+  if (!strcmp(cmd, "gunzip")) {
+    // gunzip == gzip -d
+    toys.optflags = FLAG_d;
+  } else if (!strcmp(cmd, "zcat")) {
+    // zcat == gzip -dc
+    toys.optflags = (FLAG_c|FLAG_d);
+  }
+
+  while ((opt_ch = getopt(argc, argv, "cdfhk123456789")) != -1) {
+    switch (opt_ch) {
+    case 'c': toys.optflags |= FLAG_c; break;
+    case 'd': toys.optflags |= FLAG_d; break;
+    case 'f': toys.optflags |= FLAG_f; break;
+    case 'k': toys.optflags |= FLAG_k; break;
+
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      TT.level = opt_ch - '0';
+      break;
+
+    default:
+      usage();
+      return 1;
+    }
+  }
+
+  if (optind == argc) {
+    // With no arguments, we go from stdin to stdout.
+    toys.optflags |= FLAG_c;
+    do_file("-");
+    return 0;
+  }
+
+  // Otherwise process each file in turn.
+  while (optind < argc) do_file(argv[optind++]);
+  return 0;
+}
diff --git a/trusty/keymaster/Android.mk b/trusty/keymaster/Android.mk
index 0ebf52d..2d614ae 100644
--- a/trusty/keymaster/Android.mk
+++ b/trusty/keymaster/Android.mk
@@ -32,7 +32,7 @@
 LOCAL_MODULE := trusty_keymaster_tipc
 LOCAL_SRC_FILES := \
 	trusty_keymaster_device.cpp \
-	trusty_keymaster_ipc.c \
+	trusty_keymaster_ipc.cpp \
 	trusty_keymaster_main.cpp
 LOCAL_SHARED_LIBRARIES := \
 	libcrypto \
@@ -40,6 +40,7 @@
 	libkeymaster1 \
 	libtrusty \
 	libkeymaster_messages \
+	libsoftkeymasterdevice \
 	liblog
 
 include $(BUILD_EXECUTABLE)
@@ -53,7 +54,7 @@
 LOCAL_MODULE := keystore.trusty
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_SRC_FILES := module.cpp \
-	trusty_keymaster_ipc.c \
+	trusty_keymaster_ipc.cpp \
 	trusty_keymaster_device.cpp
 LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
 LOCAL_SHARED_LIBRARIES := \
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
index 48fa53d..b38eb05 100644
--- a/trusty/keymaster/keymaster_ipc.h
+++ b/trusty/keymaster/keymaster_ipc.h
@@ -16,13 +16,15 @@
 
 #pragma once
 
+// clang-format off
+
 #define KEYMASTER_PORT "com.android.trusty.keymaster"
 #define KEYMASTER_MAX_BUFFER_LENGTH 4096
 
 // Commands
-enum keymaster_command {
-	KEYMASTER_RESP_BIT              = 1,
-	KEYMASTER_REQ_SHIFT             = 1,
+enum keymaster_command : uint32_t {
+    KEYMASTER_RESP_BIT              = 1,
+    KEYMASTER_REQ_SHIFT             = 1,
 
     KM_GENERATE_KEY                 = (0 << KEYMASTER_REQ_SHIFT),
     KM_BEGIN_OPERATION              = (1 << KEYMASTER_REQ_SHIFT),
@@ -40,6 +42,9 @@
     KM_GET_SUPPORTED_IMPORT_FORMATS = (13 << KEYMASTER_REQ_SHIFT),
     KM_GET_SUPPORTED_EXPORT_FORMATS = (14 << KEYMASTER_REQ_SHIFT),
     KM_GET_KEY_CHARACTERISTICS      = (15 << KEYMASTER_REQ_SHIFT),
+    KM_ATTEST_KEY                   = (16 << KEYMASTER_REQ_SHIFT),
+    KM_UPGRADE_KEY                  = (17 << KEYMASTER_REQ_SHIFT),
+    KM_CONFIGURE                    = (18 << KEYMASTER_REQ_SHIFT),
 };
 
 #ifdef __ANDROID__
@@ -50,8 +55,8 @@
  * @payload: start of the serialized command specific payload
  */
 struct keymaster_message {
-	uint32_t cmd;
-	uint8_t payload[0];
+    uint32_t cmd;
+    uint8_t payload[0];
 };
 
 #endif
diff --git a/trusty/keymaster/module.cpp b/trusty/keymaster/module.cpp
index 81597d9..b472680 100644
--- a/trusty/keymaster/module.cpp
+++ b/trusty/keymaster/module.cpp
@@ -26,14 +26,15 @@
 /*
  * Generic device handling
  */
-static int trusty_keymaster_open(const hw_module_t* module, const char* name,
-                                 hw_device_t** device) {
-    if (strcmp(name, KEYSTORE_KEYMASTER) != 0)
+static int trusty_keymaster_open(const hw_module_t* module, const char* name, hw_device_t** device) {
+    if (strcmp(name, KEYSTORE_KEYMASTER) != 0) {
         return -EINVAL;
+    }
 
     TrustyKeymasterDevice* dev = new TrustyKeymasterDevice(module);
-    if (dev == NULL)
+    if (dev == NULL) {
         return -ENOMEM;
+    }
     *device = dev->hw_device();
     // Do not delete dev; it will get cleaned up when the caller calls device->close(), and must
     // exist until then.
@@ -47,14 +48,14 @@
 struct keystore_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
     .common =
         {
-         .tag = HARDWARE_MODULE_TAG,
-         .module_api_version = KEYMASTER_MODULE_API_VERSION_0_3,
-         .hal_api_version = HARDWARE_HAL_API_VERSION,
-         .id = KEYSTORE_HARDWARE_MODULE_ID,
-         .name = "Trusty Keymaster HAL",
-         .author = "The Android Open Source Project",
-         .methods = &keystore_module_methods,
-         .dso = 0,
-         .reserved = {},
+            .tag = HARDWARE_MODULE_TAG,
+            .module_api_version = KEYMASTER_MODULE_API_VERSION_2_0,
+            .hal_api_version = HARDWARE_HAL_API_VERSION,
+            .id = KEYSTORE_HARDWARE_MODULE_ID,
+            .name = "Trusty Keymaster HAL",
+            .author = "The Android Open Source Project",
+            .methods = &keystore_module_methods,
+            .dso = 0,
+            .reserved = {},
         },
 };
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
index 1368f88..5f16fd0 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -25,49 +25,53 @@
 #include <string.h>
 #include <time.h>
 
+#include <algorithm>
 #include <type_traits>
 
-#include <hardware/keymaster0.h>
+#include <hardware/keymaster2.h>
 #include <keymaster/authorization_set.h>
 #include <log/log.h>
 
+#include "keymaster_ipc.h"
 #include "trusty_keymaster_device.h"
 #include "trusty_keymaster_ipc.h"
-#include "keymaster_ipc.h"
 
-const uint32_t SEND_BUF_SIZE = 8192;
-const uint32_t RECV_BUF_SIZE = 8192;
+const uint32_t RECV_BUF_SIZE = PAGE_SIZE;
+const uint32_t SEND_BUF_SIZE = (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
+
+const size_t kMaximumAttestationChallengeLength = 128;
+const size_t kMaximumFinishInputLength = 2048;
 
 namespace keymaster {
 
 static keymaster_error_t translate_error(int err) {
     switch (err) {
-    case 0:
-        return KM_ERROR_OK;
-    case -EPERM:
-    case -EACCES:
-        return KM_ERROR_SECURE_HW_ACCESS_DENIED;
+        case 0:
+            return KM_ERROR_OK;
+        case -EPERM:
+        case -EACCES:
+            return KM_ERROR_SECURE_HW_ACCESS_DENIED;
 
-    case -ECANCELED:
-        return KM_ERROR_OPERATION_CANCELLED;
+        case -ECANCELED:
+            return KM_ERROR_OPERATION_CANCELLED;
 
-    case -ENODEV:
-        return KM_ERROR_UNIMPLEMENTED;
+        case -ENODEV:
+            return KM_ERROR_UNIMPLEMENTED;
 
-    case -ENOMEM:
-        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        case -ENOMEM:
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
 
-    case -EBUSY:
-        return KM_ERROR_SECURE_HW_BUSY;
+        case -EBUSY:
+            return KM_ERROR_SECURE_HW_BUSY;
 
-    case -EIO:
-        return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+        case -EIO:
+            return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
 
-    case -EOVERFLOW:
-        return KM_ERROR_INVALID_INPUT_LENGTH;
+        case -EOVERFLOW:
+            return KM_ERROR_INVALID_INPUT_LENGTH;
 
-    default:
-        return KM_ERROR_UNKNOWN_ERROR;
+        default:
+            return KM_ERROR_UNKNOWN_ERROR;
     }
 }
 
@@ -75,31 +79,36 @@
     static_assert(std::is_standard_layout<TrustyKeymasterDevice>::value,
                   "TrustyKeymasterDevice must be standard layout");
     static_assert(offsetof(TrustyKeymasterDevice, device_) == 0,
-                  "device_ must be the first member of KeymasterOpenSsl");
+                  "device_ must be the first member of TrustyKeymasterDevice");
     static_assert(offsetof(TrustyKeymasterDevice, device_.common) == 0,
-                  "common must be the first member of keymaster_device");
+                  "common must be the first member of keymaster2_device");
 
     ALOGI("Creating device");
     ALOGD("Device address: %p", this);
 
-    memset(&device_, 0, sizeof(device_));
+    device_ = {};
 
     device_.common.tag = HARDWARE_DEVICE_TAG;
     device_.common.version = 1;
     device_.common.module = const_cast<hw_module_t*>(module);
     device_.common.close = close_device;
 
-    device_.flags = KEYMASTER_BLOBS_ARE_STANDALONE | KEYMASTER_SUPPORTS_EC;
+    device_.flags = KEYMASTER_SUPPORTS_EC;
 
-    device_.generate_keypair = generate_keypair;
-    device_.import_keypair = import_keypair;
-    device_.get_keypair_public = get_keypair_public;
-    device_.delete_keypair = NULL;
-    device_.delete_all = NULL;
-    device_.sign_data = sign_data;
-    device_.verify_data = verify_data;
-
-    device_.context = NULL;
+    device_.configure = configure;
+    device_.add_rng_entropy = add_rng_entropy;
+    device_.generate_key = generate_key;
+    device_.get_key_characteristics = get_key_characteristics;
+    device_.import_key = import_key;
+    device_.export_key = export_key;
+    device_.attest_key = attest_key;
+    device_.upgrade_key = upgrade_key;
+    device_.delete_key = nullptr;
+    device_.delete_all_keys = nullptr;
+    device_.begin = begin;
+    device_.update = update;
+    device_.finish = finish;
+    device_.abort = abort;
 
     int rc = trusty_keymaster_connect();
     error_ = translate_error(rc);
@@ -110,11 +119,11 @@
 
     GetVersionRequest version_request;
     GetVersionResponse version_response;
-    error_ = Send(version_request, &version_response);
+    error_ = Send(KM_GET_VERSION, version_request, &version_response);
     if (error_ == KM_ERROR_INVALID_ARGUMENT || error_ == KM_ERROR_UNIMPLEMENTED) {
-        ALOGI("\"Bad parameters\" error on GetVersion call.  Assuming version 0.");
-        message_version_ = 0;
-        error_ = KM_ERROR_OK;
+        ALOGE("\"Bad parameters\" error on GetVersion call.  Version 0 is not supported.");
+        error_ = KM_ERROR_VERSION_MISMATCH;
+        return;
     }
     message_version_ = MessageVersion(version_response.major_ver, version_response.minor_ver,
                                       version_response.subminor_ver);
@@ -130,261 +139,510 @@
     trusty_keymaster_disconnect();
 }
 
-const uint64_t HUNDRED_YEARS = 1000LL * 60 * 60 * 24 * 365 * 100;
+namespace {
 
-int TrustyKeymasterDevice::generate_keypair(const keymaster_keypair_t key_type,
-                                            const void* key_params, uint8_t** key_blob,
-                                            size_t* key_blob_length) {
-    ALOGD("Device received generate_keypair");
+// Allocates a new buffer with malloc and copies the contents of |buffer| to it. Caller takes
+// ownership of the returned buffer.
+uint8_t* DuplicateBuffer(const uint8_t* buffer, size_t size) {
+    uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(size));
+    if (tmp) {
+        memcpy(tmp, buffer, size);
+    }
+    return tmp;
+}
 
-    if (error_ != KM_ERROR_OK)
+template <typename RequestType>
+void AddClientAndAppData(const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+                         RequestType* request) {
+    request->additional_params.Clear();
+    if (client_id) {
+        request->additional_params.push_back(TAG_APPLICATION_ID, *client_id);
+    }
+    if (app_data) {
+        request->additional_params.push_back(TAG_APPLICATION_DATA, *app_data);
+    }
+}
+
+}  //  unnamed namespace
+
+keymaster_error_t TrustyKeymasterDevice::configure(const keymaster_key_param_set_t* params) {
+    ALOGD("Device received configure\n");
+
+    if (error_ != KM_ERROR_OK) {
         return error_;
-
-    GenerateKeyRequest req(message_version_);
-    StoreNewKeyParams(&req.key_description);
-
-    switch (key_type) {
-    case TYPE_RSA: {
-        req.key_description.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA);
-        const keymaster_rsa_keygen_params_t* rsa_params =
-            static_cast<const keymaster_rsa_keygen_params_t*>(key_params);
-        ALOGD("Generating RSA pair, modulus size: %u, public exponent: %lu",
-              rsa_params->modulus_size, rsa_params->public_exponent);
-        req.key_description.push_back(TAG_KEY_SIZE, rsa_params->modulus_size);
-        req.key_description.push_back(TAG_RSA_PUBLIC_EXPONENT, rsa_params->public_exponent);
-        break;
+    }
+    if (!params) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
     }
 
-    case TYPE_EC: {
-        req.key_description.push_back(TAG_ALGORITHM, KM_ALGORITHM_EC);
-        const keymaster_ec_keygen_params_t* ec_params =
-            static_cast<const keymaster_ec_keygen_params_t*>(key_params);
-        ALOGD("Generating ECDSA pair, key size: %u", ec_params->field_size);
-        req.key_description.push_back(TAG_KEY_SIZE, ec_params->field_size);
-        break;
-    }
-    default:
-        ALOGD("Received request for unsuported key type %d", key_type);
-        return KM_ERROR_UNSUPPORTED_ALGORITHM;
+    AuthorizationSet params_copy(*params);
+    ConfigureRequest request;
+    if (!params_copy.GetTagValue(TAG_OS_VERSION, &request.os_version) ||
+        !params_copy.GetTagValue(TAG_OS_PATCHLEVEL, &request.os_patchlevel)) {
+        ALOGD("Configuration parameters must contain OS version and patch level");
+        return KM_ERROR_INVALID_ARGUMENT;
     }
 
-    GenerateKeyResponse rsp(message_version_);
-    ALOGD("Sending generate request");
-    keymaster_error_t err = Send(req, &rsp);
+    ConfigureResponse response;
+    keymaster_error_t err = Send(KM_CONFIGURE, request, &response);
     if (err != KM_ERROR_OK) {
-        ALOGE("Got error %d from send", err);
         return err;
     }
 
-    *key_blob_length = rsp.key_blob.key_material_size;
-    *key_blob = static_cast<uint8_t*>(malloc(*key_blob_length));
-    memcpy(*key_blob, rsp.key_blob.key_material, *key_blob_length);
-    ALOGD("Returning %d bytes in key blob\n", (int)*key_blob_length);
-
     return KM_ERROR_OK;
 }
 
-struct EVP_PKEY_Delete {
-    void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
-};
+keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const uint8_t* data, size_t data_length) {
+    ALOGD("Device received add_rng_entropy");
 
-struct PKCS8_PRIV_KEY_INFO_Delete {
-    void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); }
-};
-
-int TrustyKeymasterDevice::import_keypair(const uint8_t* key, const size_t key_length,
-                                          uint8_t** key_blob, size_t* key_blob_length) {
-    ALOGD("Device received import_keypair");
-    if (error_ != KM_ERROR_OK)
+    if (error_ != KM_ERROR_OK) {
         return error_;
+    }
 
-    if (!key)
+    AddEntropyRequest request;
+    request.random_data.Reinitialize(data, data_length);
+    AddEntropyResponse response;
+    return Send(KM_ADD_RNG_ENTROPY, request, &response);
+}
+
+keymaster_error_t TrustyKeymasterDevice::generate_key(
+    const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob,
+    keymaster_key_characteristics_t* characteristics) {
+    ALOGD("Device received generate_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!params) {
         return KM_ERROR_UNEXPECTED_NULL_POINTER;
-
-    if (!key_blob || !key_blob_length)
+    }
+    if (!key_blob) {
         return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    GenerateKeyRequest request(message_version_);
+    request.key_description.Reinitialize(*params);
+    request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+
+    GenerateKeyResponse response(message_version_);
+    keymaster_error_t err = Send(KM_GENERATE_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    key_blob->key_material_size = response.key_blob.key_material_size;
+    key_blob->key_material =
+        DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+    if (!key_blob->key_material) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+
+    if (characteristics) {
+        response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+        response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
+    const keymaster_key_blob_t* key_blob, const keymaster_blob_t* client_id,
+    const keymaster_blob_t* app_data, keymaster_key_characteristics_t* characteristics) {
+    ALOGD("Device received get_key_characteristics");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!key_blob || !key_blob->key_material) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!characteristics) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    GetKeyCharacteristicsRequest request;
+    request.SetKeyMaterial(*key_blob);
+    AddClientAndAppData(client_id, app_data, &request);
+
+    GetKeyCharacteristicsResponse response;
+    keymaster_error_t err = Send(KM_GET_KEY_CHARACTERISTICS, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+    response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::import_key(
+    const keymaster_key_param_set_t* params, keymaster_key_format_t key_format,
+    const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+    keymaster_key_characteristics_t* characteristics) {
+    ALOGD("Device received import_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!params || !key_data) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!key_blob) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
 
     ImportKeyRequest request(message_version_);
-    StoreNewKeyParams(&request.key_description);
-    keymaster_algorithm_t algorithm;
-    keymaster_error_t err = GetPkcs8KeyAlgorithm(key, key_length, &algorithm);
-    if (err != KM_ERROR_OK)
-        return err;
-    request.key_description.push_back(TAG_ALGORITHM, algorithm);
+    request.key_description.Reinitialize(*params);
+    request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
 
-    request.SetKeyMaterial(key, key_length);
-    request.key_format = KM_KEY_FORMAT_PKCS8;
+    request.key_format = key_format;
+    request.SetKeyMaterial(key_data->data, key_data->data_length);
+
     ImportKeyResponse response(message_version_);
-    err = Send(request, &response);
-    if (err != KM_ERROR_OK)
+    keymaster_error_t err = Send(KM_IMPORT_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
         return err;
+    }
 
-    *key_blob_length = response.key_blob.key_material_size;
-    *key_blob = static_cast<uint8_t*>(malloc(*key_blob_length));
-    memcpy(*key_blob, response.key_blob.key_material, *key_blob_length);
-    printf("Returning %d bytes in key blob\n", (int)*key_blob_length);
+    key_blob->key_material_size = response.key_blob.key_material_size;
+    key_blob->key_material =
+        DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+    if (!key_blob->key_material) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+
+    if (characteristics) {
+        response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+        response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+    }
 
     return KM_ERROR_OK;
 }
 
-keymaster_error_t TrustyKeymasterDevice::GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length,
-                                                              keymaster_algorithm_t* algorithm) {
-    if (key == NULL) {
-        ALOGE("No key specified for import");
+keymaster_error_t TrustyKeymasterDevice::export_key(keymaster_key_format_t export_format,
+                                                    const keymaster_key_blob_t* key_to_export,
+                                                    const keymaster_blob_t* client_id,
+                                                    const keymaster_blob_t* app_data,
+                                                    keymaster_blob_t* export_data) {
+    ALOGD("Device received export_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!key_to_export || !key_to_export->key_material) {
         return KM_ERROR_UNEXPECTED_NULL_POINTER;
     }
-
-    UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> pkcs8(
-        d2i_PKCS8_PRIV_KEY_INFO(NULL, &key, key_length));
-    if (pkcs8.get() == NULL) {
-        ALOGE("Could not parse PKCS8 key blob");
-        return KM_ERROR_INVALID_KEY_BLOB;
+    if (!export_data) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
     }
 
-    UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKCS82PKEY(pkcs8.get()));
-    if (pkey.get() == NULL) {
-        ALOGE("Could not extract key from PKCS8 key blob");
-        return KM_ERROR_INVALID_KEY_BLOB;
-    }
-
-    switch (EVP_PKEY_type(pkey->type)) {
-    case EVP_PKEY_RSA:
-        *algorithm = KM_ALGORITHM_RSA;
-        break;
-    case EVP_PKEY_EC:
-        *algorithm = KM_ALGORITHM_EC;
-        break;
-    default:
-        ALOGE("Unsupported algorithm %d", EVP_PKEY_type(pkey->type));
-        return KM_ERROR_UNSUPPORTED_ALGORITHM;
-    }
-
-    return KM_ERROR_OK;
-}
-
-int TrustyKeymasterDevice::get_keypair_public(const uint8_t* key_blob, const size_t key_blob_length,
-                                              uint8_t** x509_data, size_t* x509_data_length) {
-    ALOGD("Device received get_keypair_public");
-    if (error_ != KM_ERROR_OK)
-        return error_;
+    export_data->data = nullptr;
+    export_data->data_length = 0;
 
     ExportKeyRequest request(message_version_);
-    request.SetKeyMaterial(key_blob, key_blob_length);
-    request.key_format = KM_KEY_FORMAT_X509;
+    request.key_format = export_format;
+    request.SetKeyMaterial(*key_to_export);
+    AddClientAndAppData(client_id, app_data, &request);
+
     ExportKeyResponse response(message_version_);
-    keymaster_error_t err = Send(request, &response);
-    if (err != KM_ERROR_OK)
+    keymaster_error_t err = Send(KM_EXPORT_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
         return err;
+    }
 
-    *x509_data_length = response.key_data_length;
-    *x509_data = static_cast<uint8_t*>(malloc(*x509_data_length));
-    memcpy(*x509_data, response.key_data, *x509_data_length);
-    printf("Returning %d bytes in x509 key\n", (int)*x509_data_length);
+    export_data->data_length = response.key_data_length;
+    export_data->data = DuplicateBuffer(response.key_data, response.key_data_length);
+    if (!export_data->data) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
 
     return KM_ERROR_OK;
 }
 
-int TrustyKeymasterDevice::sign_data(const void* signing_params, const uint8_t* key_blob,
-                                     const size_t key_blob_length, const uint8_t* data,
-                                     const size_t data_length, uint8_t** signed_data,
-                                     size_t* signed_data_length) {
-    ALOGD("Device received sign_data, %d", error_);
-    if (error_ != KM_ERROR_OK)
+keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster_key_blob_t* key_to_attest,
+                                                    const keymaster_key_param_set_t* attest_params,
+                                                    keymaster_cert_chain_t* cert_chain) {
+    ALOGD("Device received attest_key");
+
+    if (error_ != KM_ERROR_OK) {
         return error_;
+    }
+    if (!key_to_attest || !attest_params) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!cert_chain) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
 
-    BeginOperationRequest begin_request(message_version_);
-    begin_request.purpose = KM_PURPOSE_SIGN;
-    begin_request.SetKeyMaterial(key_blob, key_blob_length);
-    keymaster_error_t err = StoreSigningParams(signing_params, key_blob, key_blob_length,
-                                               &begin_request.additional_params);
+    cert_chain->entry_count = 0;
+    cert_chain->entries = nullptr;
+
+    AttestKeyRequest request;
+    request.SetKeyMaterial(*key_to_attest);
+    request.attest_params.Reinitialize(*attest_params);
+
+    keymaster_blob_t attestation_challenge = {};
+    request.attest_params.GetTagValue(TAG_ATTESTATION_CHALLENGE, &attestation_challenge);
+    if (attestation_challenge.data_length > kMaximumAttestationChallengeLength) {
+        ALOGE("%zu-byte attestation challenge; only %zu bytes allowed",
+              attestation_challenge.data_length, kMaximumAttestationChallengeLength);
+        return KM_ERROR_INVALID_INPUT_LENGTH;
+    }
+
+    AttestKeyResponse response;
+    keymaster_error_t err = Send(KM_ATTEST_KEY, request, &response);
     if (err != KM_ERROR_OK) {
-        ALOGE("Error extracting signing params: %d", err);
         return err;
     }
 
-    BeginOperationResponse begin_response(message_version_);
-    ALOGD("Sending signing request begin");
-    err = Send(begin_request, &begin_response);
-    if (err != KM_ERROR_OK) {
-        ALOGE("Error sending sign begin: %d", err);
-        return err;
+    // Allocate and clear storage for cert_chain.
+    keymaster_cert_chain_t& rsp_chain = response.certificate_chain;
+    cert_chain->entries = reinterpret_cast<keymaster_blob_t*>(
+        malloc(rsp_chain.entry_count * sizeof(*cert_chain->entries)));
+    if (!cert_chain->entries) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+    cert_chain->entry_count = rsp_chain.entry_count;
+    for (keymaster_blob_t& entry : array_range(cert_chain->entries, cert_chain->entry_count)) {
+        entry = {};
     }
 
-    UpdateOperationRequest update_request(message_version_);
-    update_request.op_handle = begin_response.op_handle;
-    update_request.input.Reinitialize(data, data_length);
-    UpdateOperationResponse update_response(message_version_);
-    ALOGD("Sending signing request update");
-    err = Send(update_request, &update_response);
-    if (err != KM_ERROR_OK) {
-        ALOGE("Error sending sign update: %d", err);
-        return err;
+    // Copy cert_chain contents
+    size_t i = 0;
+    for (keymaster_blob_t& entry : array_range(rsp_chain.entries, rsp_chain.entry_count)) {
+        cert_chain->entries[i].data = DuplicateBuffer(entry.data, entry.data_length);
+        if (!cert_chain->entries[i].data) {
+            keymaster_free_cert_chain(cert_chain);
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        }
+        cert_chain->entries[i].data_length = entry.data_length;
+        ++i;
     }
 
-    FinishOperationRequest finish_request(message_version_);
-    finish_request.op_handle = begin_response.op_handle;
-    FinishOperationResponse finish_response(message_version_);
-    ALOGD("Sending signing request finish");
-    err = Send(finish_request, &finish_response);
-    if (err != KM_ERROR_OK) {
-        ALOGE("Error sending sign finish: %d", err);
-        return err;
-    }
-
-    *signed_data_length = finish_response.output.available_read();
-    *signed_data = static_cast<uint8_t*>(malloc(*signed_data_length));
-    if (!finish_response.output.read(*signed_data, *signed_data_length)) {
-        ALOGE("Error reading response data: %d", err);
-        return KM_ERROR_UNKNOWN_ERROR;
-    }
     return KM_ERROR_OK;
 }
 
-int TrustyKeymasterDevice::verify_data(const void* signing_params, const uint8_t* key_blob,
-                                       const size_t key_blob_length, const uint8_t* signed_data,
-                                       const size_t signed_data_length, const uint8_t* signature,
-                                       const size_t signature_length) {
-    ALOGD("Device received verify_data");
-    if (error_ != KM_ERROR_OK)
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
+                                                     const keymaster_key_param_set_t* upgrade_params,
+                                                     keymaster_key_blob_t* upgraded_key) {
+    ALOGD("Device received upgrade_key");
+
+    if (error_ != KM_ERROR_OK) {
         return error_;
+    }
+    if (!key_to_upgrade || !upgrade_params) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!upgraded_key) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
 
-    BeginOperationRequest begin_request(message_version_);
-    begin_request.purpose = KM_PURPOSE_VERIFY;
-    begin_request.SetKeyMaterial(key_blob, key_blob_length);
-    keymaster_error_t err = StoreSigningParams(signing_params, key_blob, key_blob_length,
-                                               &begin_request.additional_params);
-    if (err != KM_ERROR_OK)
-        return err;
+    UpgradeKeyRequest request;
+    request.SetKeyMaterial(*key_to_upgrade);
+    request.upgrade_params.Reinitialize(*upgrade_params);
 
-    BeginOperationResponse begin_response(message_version_);
-    err = Send(begin_request, &begin_response);
-    if (err != KM_ERROR_OK)
+    UpgradeKeyResponse response;
+    keymaster_error_t err = Send(KM_UPGRADE_KEY, request, &response);
+    if (err != KM_ERROR_OK) {
         return err;
+    }
 
-    UpdateOperationRequest update_request(message_version_);
-    update_request.op_handle = begin_response.op_handle;
-    update_request.input.Reinitialize(signed_data, signed_data_length);
-    UpdateOperationResponse update_response(message_version_);
-    err = Send(update_request, &update_response);
-    if (err != KM_ERROR_OK)
-        return err;
+    upgraded_key->key_material_size = response.upgraded_key.key_material_size;
+    upgraded_key->key_material = DuplicateBuffer(response.upgraded_key.key_material,
+                                                 response.upgraded_key.key_material_size);
+    if (!upgraded_key->key_material) {
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
 
-    FinishOperationRequest finish_request(message_version_);
-    finish_request.op_handle = begin_response.op_handle;
-    finish_request.signature.Reinitialize(signature, signature_length);
-    FinishOperationResponse finish_response(message_version_);
-    err = Send(finish_request, &finish_response);
-    if (err != KM_ERROR_OK)
-        return err;
     return KM_ERROR_OK;
 }
 
+keymaster_error_t TrustyKeymasterDevice::begin(keymaster_purpose_t purpose,
+                                               const keymaster_key_blob_t* key,
+                                               const keymaster_key_param_set_t* in_params,
+                                               keymaster_key_param_set_t* out_params,
+                                               keymaster_operation_handle_t* operation_handle) {
+    ALOGD("Device received begin");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!key || !key->key_material) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!operation_handle) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    if (out_params) {
+        *out_params = {};
+    }
+
+    BeginOperationRequest request;
+    request.purpose = purpose;
+    request.SetKeyMaterial(*key);
+    request.additional_params.Reinitialize(*in_params);
+
+    BeginOperationResponse response;
+    keymaster_error_t err = Send(KM_BEGIN_OPERATION, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    if (response.output_params.size() > 0) {
+        if (out_params) {
+            response.output_params.CopyToParamSet(out_params);
+        } else {
+            return KM_ERROR_OUTPUT_PARAMETER_NULL;
+        }
+    }
+    *operation_handle = response.op_handle;
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::update(keymaster_operation_handle_t operation_handle,
+                                                const keymaster_key_param_set_t* in_params,
+                                                const keymaster_blob_t* input,
+                                                size_t* input_consumed,
+                                                keymaster_key_param_set_t* out_params,
+                                                keymaster_blob_t* output) {
+    ALOGD("Device received update");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (!input) {
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+    }
+    if (!input_consumed) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    if (out_params) {
+        *out_params = {};
+    }
+    if (output) {
+        *output = {};
+    }
+
+    UpdateOperationRequest request;
+    request.op_handle = operation_handle;
+    if (in_params) {
+        request.additional_params.Reinitialize(*in_params);
+    }
+    if (input && input->data_length > 0) {
+        size_t max_input_size = SEND_BUF_SIZE - request.SerializedSize();
+        request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
+    }
+
+    UpdateOperationResponse response;
+    keymaster_error_t err = Send(KM_UPDATE_OPERATION, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    if (response.output_params.size() > 0) {
+        if (out_params) {
+            response.output_params.CopyToParamSet(out_params);
+        } else {
+            return KM_ERROR_OUTPUT_PARAMETER_NULL;
+        }
+    }
+    *input_consumed = response.input_consumed;
+    if (output) {
+        output->data_length = response.output.available_read();
+        output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+        if (!output->data) {
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        }
+    } else if (response.output.available_read() > 0) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::finish(keymaster_operation_handle_t operation_handle,
+                                                const keymaster_key_param_set_t* in_params,
+                                                const keymaster_blob_t* input,
+                                                const keymaster_blob_t* signature,
+                                                keymaster_key_param_set_t* out_params,
+                                                keymaster_blob_t* output) {
+    ALOGD("Device received finish");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+    if (input && input->data_length > kMaximumFinishInputLength) {
+        return KM_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (out_params) {
+        *out_params = {};
+    }
+    if (output) {
+        *output = {};
+    }
+
+    FinishOperationRequest request;
+    request.op_handle = operation_handle;
+    if (signature && signature->data && signature->data_length > 0) {
+        request.signature.Reinitialize(signature->data, signature->data_length);
+    }
+    if (input && input->data && input->data_length) {
+        request.input.Reinitialize(input->data, input->data_length);
+    }
+    if (in_params) {
+        request.additional_params.Reinitialize(*in_params);
+    }
+
+    FinishOperationResponse response;
+    keymaster_error_t err = Send(KM_FINISH_OPERATION, request, &response);
+    if (err != KM_ERROR_OK) {
+        return err;
+    }
+
+    if (response.output_params.size() > 0) {
+        if (out_params) {
+            response.output_params.CopyToParamSet(out_params);
+        } else {
+            return KM_ERROR_OUTPUT_PARAMETER_NULL;
+        }
+    }
+    if (output) {
+        output->data_length = response.output.available_read();
+        output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+        if (!output->data) {
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        }
+    } else if (response.output.available_read() > 0) {
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+    }
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::abort(keymaster_operation_handle_t operation_handle) {
+    ALOGD("Device received abort");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+
+    AbortOperationRequest request;
+    request.op_handle = operation_handle;
+    AbortOperationResponse response;
+    return Send(KM_ABORT_OPERATION, request, &response);
+}
+
 hw_device_t* TrustyKeymasterDevice::hw_device() {
     return &device_.common;
 }
 
-static inline TrustyKeymasterDevice* convert_device(const keymaster0_device_t* dev) {
-    return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster0_device_t*>(dev));
+static inline TrustyKeymasterDevice* convert_device(const keymaster2_device_t* dev) {
+    return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster2_device_t*>(dev));
 }
 
 /* static */
@@ -394,52 +652,111 @@
 }
 
 /* static */
-int TrustyKeymasterDevice::generate_keypair(const keymaster0_device_t* dev,
-                                            const keymaster_keypair_t key_type,
-                                            const void* key_params, uint8_t** keyBlob,
-                                            size_t* keyBlobLength) {
-    ALOGD("Generate keypair, sending to device: %p", convert_device(dev));
-    return convert_device(dev)->generate_keypair(key_type, key_params, keyBlob, keyBlobLength);
+keymaster_error_t TrustyKeymasterDevice::configure(const keymaster2_device_t* dev,
+                                                   const keymaster_key_param_set_t* params) {
+    return convert_device(dev)->configure(params);
 }
 
 /* static */
-int TrustyKeymasterDevice::import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
-                                          const size_t key_length, uint8_t** key_blob,
-                                          size_t* key_blob_length) {
-    return convert_device(dev)->import_keypair(key, key_length, key_blob, key_blob_length);
+keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const keymaster2_device_t* dev,
+                                                         const uint8_t* data, size_t data_length) {
+    return convert_device(dev)->add_rng_entropy(data, data_length);
 }
 
 /* static */
-int TrustyKeymasterDevice::get_keypair_public(const keymaster0_device_t* dev,
-                                              const uint8_t* key_blob, const size_t key_blob_length,
-                                              uint8_t** x509_data, size_t* x509_data_length) {
-    return convert_device(dev)
-        ->get_keypair_public(key_blob, key_blob_length, x509_data, x509_data_length);
+keymaster_error_t TrustyKeymasterDevice::generate_key(
+    const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+    keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+    return convert_device(dev)->generate_key(params, key_blob, characteristics);
 }
 
 /* static */
-int TrustyKeymasterDevice::sign_data(const keymaster0_device_t* dev, const void* params,
-                                     const uint8_t* keyBlob, const size_t keyBlobLength,
-                                     const uint8_t* data, const size_t dataLength,
-                                     uint8_t** signedData, size_t* signedDataLength) {
-    return convert_device(dev)
-        ->sign_data(params, keyBlob, keyBlobLength, data, dataLength, signedData, signedDataLength);
+keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
+    const keymaster2_device_t* dev, const keymaster_key_blob_t* key_blob,
+    const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+    keymaster_key_characteristics_t* characteristics) {
+    return convert_device(dev)->get_key_characteristics(key_blob, client_id, app_data,
+                                                        characteristics);
 }
 
 /* static */
-int TrustyKeymasterDevice::verify_data(const keymaster0_device_t* dev, const void* params,
-                                       const uint8_t* keyBlob, const size_t keyBlobLength,
-                                       const uint8_t* signedData, const size_t signedDataLength,
-                                       const uint8_t* signature, const size_t signatureLength) {
-    return convert_device(dev)->verify_data(params, keyBlob, keyBlobLength, signedData,
-                                            signedDataLength, signature, signatureLength);
+keymaster_error_t TrustyKeymasterDevice::import_key(
+    const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+    keymaster_key_format_t key_format, const keymaster_blob_t* key_data,
+    keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+    return convert_device(dev)->import_key(params, key_format, key_data, key_blob, characteristics);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::export_key(const keymaster2_device_t* dev,
+                                                    keymaster_key_format_t export_format,
+                                                    const keymaster_key_blob_t* key_to_export,
+                                                    const keymaster_blob_t* client_id,
+                                                    const keymaster_blob_t* app_data,
+                                                    keymaster_blob_t* export_data) {
+    return convert_device(dev)->export_key(export_format, key_to_export, client_id, app_data,
+                                           export_data);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster2_device_t* dev,
+                                                    const keymaster_key_blob_t* key_to_attest,
+                                                    const keymaster_key_param_set_t* attest_params,
+                                                    keymaster_cert_chain_t* cert_chain) {
+    return convert_device(dev)->attest_key(key_to_attest, attest_params, cert_chain);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(const keymaster2_device_t* dev,
+                                                     const keymaster_key_blob_t* key_to_upgrade,
+                                                     const keymaster_key_param_set_t* upgrade_params,
+                                                     keymaster_key_blob_t* upgraded_key) {
+    return convert_device(dev)->upgrade_key(key_to_upgrade, upgrade_params, upgraded_key);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::begin(const keymaster2_device_t* dev,
+                                               keymaster_purpose_t purpose,
+                                               const keymaster_key_blob_t* key,
+                                               const keymaster_key_param_set_t* in_params,
+                                               keymaster_key_param_set_t* out_params,
+                                               keymaster_operation_handle_t* operation_handle) {
+    return convert_device(dev)->begin(purpose, key, in_params, out_params, operation_handle);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::update(
+    const keymaster2_device_t* dev, keymaster_operation_handle_t operation_handle,
+    const keymaster_key_param_set_t* in_params, const keymaster_blob_t* input,
+    size_t* input_consumed, keymaster_key_param_set_t* out_params, keymaster_blob_t* output) {
+    return convert_device(dev)->update(operation_handle, in_params, input, input_consumed,
+                                       out_params, output);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::finish(const keymaster2_device_t* dev,
+                                                keymaster_operation_handle_t operation_handle,
+                                                const keymaster_key_param_set_t* in_params,
+                                                const keymaster_blob_t* input,
+                                                const keymaster_blob_t* signature,
+                                                keymaster_key_param_set_t* out_params,
+                                                keymaster_blob_t* output) {
+    return convert_device(dev)->finish(operation_handle, in_params, input, signature, out_params,
+                                       output);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::abort(const keymaster2_device_t* dev,
+                                               keymaster_operation_handle_t operation_handle) {
+    return convert_device(dev)->abort(operation_handle);
 }
 
 keymaster_error_t TrustyKeymasterDevice::Send(uint32_t command, const Serializable& req,
                                               KeymasterResponse* rsp) {
     uint32_t req_size = req.SerializedSize();
-    if (req_size > SEND_BUF_SIZE)
+    if (req_size > SEND_BUF_SIZE) {
         return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
     uint8_t send_buf[SEND_BUF_SIZE];
     Eraser send_buf_eraser(send_buf, SEND_BUF_SIZE);
     req.Serialize(send_buf, send_buf + req_size);
@@ -448,7 +765,7 @@
     uint8_t recv_buf[RECV_BUF_SIZE];
     Eraser recv_buf_eraser(recv_buf, RECV_BUF_SIZE);
     uint32_t rsp_size = RECV_BUF_SIZE;
-    printf("Sending %d byte request\n", (int)req.SerializedSize());
+    ALOGV("Sending %d byte request\n", (int)req.SerializedSize());
     int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
     if (rc < 0) {
         ALOGE("tipc error: %d\n", rc);
@@ -458,8 +775,8 @@
         ALOGV("Received %d byte response\n", rsp_size);
     }
 
-    const keymaster_message* msg = (keymaster_message *) recv_buf;
-    const uint8_t *p = msg->payload;
+    const keymaster_message* msg = (keymaster_message*)recv_buf;
+    const uint8_t* p = msg->payload;
     if (!rsp->Deserialize(&p, p + rsp_size)) {
         ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
         return KM_ERROR_UNKNOWN_ERROR;
@@ -470,65 +787,4 @@
     return rsp->error;
 }
 
-keymaster_error_t TrustyKeymasterDevice::StoreSigningParams(const void* signing_params,
-                                                            const uint8_t* key_blob,
-                                                            size_t key_blob_length,
-                                                            AuthorizationSet* auth_set) {
-    uint8_t* pub_key_data;
-    size_t pub_key_data_length;
-    int err = get_keypair_public(&device_, key_blob, key_blob_length, &pub_key_data,
-                                 &pub_key_data_length);
-    if (err < 0) {
-        ALOGE("Error %d extracting public key to determine algorithm", err);
-        return KM_ERROR_INVALID_KEY_BLOB;
-    }
-    UniquePtr<uint8_t, Malloc_Delete> pub_key(pub_key_data);
-
-    const uint8_t* p = pub_key_data;
-    UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(
-        d2i_PUBKEY(nullptr /* allocate new struct */, &p, pub_key_data_length));
-
-    switch (EVP_PKEY_type(pkey->type)) {
-    case EVP_PKEY_RSA: {
-        const keymaster_rsa_sign_params_t* rsa_params =
-            reinterpret_cast<const keymaster_rsa_sign_params_t*>(signing_params);
-        if (rsa_params->digest_type != DIGEST_NONE)
-            return KM_ERROR_UNSUPPORTED_DIGEST;
-        if (rsa_params->padding_type != PADDING_NONE)
-            return KM_ERROR_UNSUPPORTED_PADDING_MODE;
-        if (!auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE) ||
-            !auth_set->push_back(TAG_PADDING, KM_PAD_NONE))
-            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    } break;
-    case EVP_PKEY_EC: {
-        const keymaster_ec_sign_params_t* ecdsa_params =
-            reinterpret_cast<const keymaster_ec_sign_params_t*>(signing_params);
-        if (ecdsa_params->digest_type != DIGEST_NONE)
-            return KM_ERROR_UNSUPPORTED_DIGEST;
-        if (!auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE))
-            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    } break;
-    default:
-        return KM_ERROR_UNSUPPORTED_ALGORITHM;
-    }
-    return KM_ERROR_OK;
-}
-
-void TrustyKeymasterDevice::StoreNewKeyParams(AuthorizationSet* auth_set) {
-    auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN);
-    auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY);
-    auth_set->push_back(TAG_ALL_USERS);
-    auth_set->push_back(TAG_NO_AUTH_REQUIRED);
-    uint64_t now = java_time(time(NULL));
-    auth_set->push_back(TAG_CREATION_DATETIME, now);
-    auth_set->push_back(TAG_ORIGINATION_EXPIRE_DATETIME, now + HUNDRED_YEARS);
-    if (message_version_ == 0) {
-        auth_set->push_back(TAG_DIGEST_OLD, KM_DIGEST_NONE);
-        auth_set->push_back(TAG_PADDING_OLD, KM_PAD_NONE);
-    } else {
-        auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE);
-        auth_set->push_back(TAG_PADDING, KM_PAD_NONE);
-    }
-}
-
 }  // namespace keymaster
diff --git a/trusty/keymaster/trusty_keymaster_device.h b/trusty/keymaster/trusty_keymaster_device.h
index 68cf40c..cfada1b 100644
--- a/trusty/keymaster/trusty_keymaster_device.h
+++ b/trusty/keymaster/trusty_keymaster_device.h
@@ -14,19 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
-#define EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
 
-#include <hardware/keymaster0.h>
-
+#include <hardware/keymaster2.h>
 #include <keymaster/android_keymaster_messages.h>
 
-#include "keymaster_ipc.h"
-
 namespace keymaster {
 
 /**
- * Software OpenSSL-based Keymaster device.
+ * Trusty Keymaster device.
  *
  * IMPORTANT MAINTAINER NOTE: Pointers to instances of this class must be castable to hw_device_t
  * and keymaster_device. This means it must remain a standard layout class (no virtual functions and
@@ -46,79 +43,111 @@
 
     keymaster_error_t session_error() { return error_; }
 
-    int generate_keypair(const keymaster_keypair_t key_type, const void* key_params,
-                         uint8_t** key_blob, size_t* key_blob_length);
-    int import_keypair(const uint8_t* key, const size_t key_length, uint8_t** key_blob,
-                       size_t* key_blob_length);
-    int get_keypair_public(const uint8_t* key_blob, const size_t key_blob_length,
-                           uint8_t** x509_data, size_t* x509_data_length);
-    int sign_data(const void* signing_params, const uint8_t* key_blob, const size_t key_blob_length,
-                  const uint8_t* data, const size_t data_length, uint8_t** signed_data,
-                  size_t* signed_data_length);
-    int verify_data(const void* signing_params, const uint8_t* key_blob,
-                    const size_t key_blob_length, const uint8_t* signed_data,
-                    const size_t signed_data_length, const uint8_t* signature,
-                    const size_t signature_length);
+    keymaster_error_t configure(const keymaster_key_param_set_t* params);
+    keymaster_error_t add_rng_entropy(const uint8_t* data, size_t data_length);
+    keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
+                                   keymaster_key_blob_t* key_blob,
+                                   keymaster_key_characteristics_t* characteristics);
+    keymaster_error_t get_key_characteristics(const keymaster_key_blob_t* key_blob,
+                                              const keymaster_blob_t* client_id,
+                                              const keymaster_blob_t* app_data,
+                                              keymaster_key_characteristics_t* character);
+    keymaster_error_t import_key(const keymaster_key_param_set_t* params,
+                                 keymaster_key_format_t key_format,
+                                 const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+                                 keymaster_key_characteristics_t* characteristics);
+    keymaster_error_t export_key(keymaster_key_format_t export_format,
+                                 const keymaster_key_blob_t* key_to_export,
+                                 const keymaster_blob_t* client_id,
+                                 const keymaster_blob_t* app_data, keymaster_blob_t* export_data);
+    keymaster_error_t attest_key(const keymaster_key_blob_t* key_to_attest,
+                                 const keymaster_key_param_set_t* attest_params,
+                                 keymaster_cert_chain_t* cert_chain);
+    keymaster_error_t upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
+                                  const keymaster_key_param_set_t* upgrade_params,
+                                  keymaster_key_blob_t* upgraded_key);
+    keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
+                            const keymaster_key_param_set_t* in_params,
+                            keymaster_key_param_set_t* out_params,
+                            keymaster_operation_handle_t* operation_handle);
+    keymaster_error_t update(keymaster_operation_handle_t operation_handle,
+                             const keymaster_key_param_set_t* in_params,
+                             const keymaster_blob_t* input, size_t* input_consumed,
+                             keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+    keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
+                             const keymaster_key_param_set_t* in_params,
+                             const keymaster_blob_t* input, const keymaster_blob_t* signature,
+                             keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+    keymaster_error_t abort(keymaster_operation_handle_t operation_handle);
 
   private:
     keymaster_error_t Send(uint32_t command, const Serializable& request,
                            KeymasterResponse* response);
-    keymaster_error_t Send(const GenerateKeyRequest& request, GenerateKeyResponse* response) {
-        return Send(KM_GENERATE_KEY, request, response);
-    }
-    keymaster_error_t Send(const BeginOperationRequest& request, BeginOperationResponse* response) {
-        return Send(KM_BEGIN_OPERATION, request, response);
-    }
-    keymaster_error_t Send(const UpdateOperationRequest& request,
-                           UpdateOperationResponse* response) {
-        return Send(KM_UPDATE_OPERATION, request, response);
-    }
-    keymaster_error_t Send(const FinishOperationRequest& request,
-                           FinishOperationResponse* response) {
-        return Send(KM_FINISH_OPERATION, request, response);
-    }
-    keymaster_error_t Send(const ImportKeyRequest& request, ImportKeyResponse* response) {
-        return Send(KM_IMPORT_KEY, request, response);
-    }
-    keymaster_error_t Send(const ExportKeyRequest& request, ExportKeyResponse* response) {
-        return Send(KM_EXPORT_KEY, request, response);
-    }
-    keymaster_error_t Send(const GetVersionRequest& request, GetVersionResponse* response) {
-        return Send(KM_GET_VERSION, request, response);
-    }
-
-    keymaster_error_t StoreSigningParams(const void* signing_params, const uint8_t* key_blob,
-                                         size_t key_blob_length, AuthorizationSet* auth_set);
-    void StoreNewKeyParams(AuthorizationSet* auth_set);
-    keymaster_error_t GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length,
-                                           keymaster_algorithm_t* algorithm);
 
     /*
      * These static methods are the functions referenced through the function pointers in
      * keymaster_device.  They're all trivial wrappers.
      */
     static int close_device(hw_device_t* dev);
-    static int generate_keypair(const keymaster0_device_t* dev, const keymaster_keypair_t key_type,
-                                const void* key_params, uint8_t** keyBlob, size_t* keyBlobLength);
-    static int import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
-                              const size_t key_length, uint8_t** key_blob, size_t* key_blob_length);
-    static int get_keypair_public(const keymaster0_device_t* dev, const uint8_t* key_blob,
-                                  const size_t key_blob_length, uint8_t** x509_data,
-                                  size_t* x509_data_length);
-    static int sign_data(const keymaster0_device_t* dev, const void* signing_params,
-                         const uint8_t* key_blob, const size_t key_blob_length, const uint8_t* data,
-                         const size_t data_length, uint8_t** signed_data,
-                         size_t* signed_data_length);
-    static int verify_data(const keymaster0_device_t* dev, const void* signing_params,
-                           const uint8_t* key_blob, const size_t key_blob_length,
-                           const uint8_t* signed_data, const size_t signed_data_length,
-                           const uint8_t* signature, const size_t signature_length);
+    static keymaster_error_t configure(const keymaster2_device_t* dev,
+                                       const keymaster_key_param_set_t* params);
+    static keymaster_error_t add_rng_entropy(const keymaster2_device_t* dev, const uint8_t* data,
+                                             size_t data_length);
+    static keymaster_error_t generate_key(const keymaster2_device_t* dev,
+                                          const keymaster_key_param_set_t* params,
+                                          keymaster_key_blob_t* key_blob,
+                                          keymaster_key_characteristics_t* characteristics);
+    static keymaster_error_t get_key_characteristics(const keymaster2_device_t* dev,
+                                                     const keymaster_key_blob_t* key_blob,
+                                                     const keymaster_blob_t* client_id,
+                                                     const keymaster_blob_t* app_data,
+                                                     keymaster_key_characteristics_t* character);
+    static keymaster_error_t import_key(const keymaster2_device_t* dev,
+                                        const keymaster_key_param_set_t* params,
+                                        keymaster_key_format_t key_format,
+                                        const keymaster_blob_t* key_data,
+                                        keymaster_key_blob_t* key_blob,
+                                        keymaster_key_characteristics_t* characteristics);
+    static keymaster_error_t export_key(const keymaster2_device_t* dev,
+                                        keymaster_key_format_t export_format,
+                                        const keymaster_key_blob_t* key_to_export,
+                                        const keymaster_blob_t* client_id,
+                                        const keymaster_blob_t* app_data,
+                                        keymaster_blob_t* export_data);
+    static keymaster_error_t attest_key(const keymaster2_device_t* dev,
+                                        const keymaster_key_blob_t* key_to_attest,
+                                        const keymaster_key_param_set_t* attest_params,
+                                        keymaster_cert_chain_t* cert_chain);
+    static keymaster_error_t upgrade_key(const keymaster2_device_t* dev,
+                                         const keymaster_key_blob_t* key_to_upgrade,
+                                         const keymaster_key_param_set_t* upgrade_params,
+                                         keymaster_key_blob_t* upgraded_key);
+    static keymaster_error_t delete_key(const keymaster2_device_t* dev,
+                                        const keymaster_key_blob_t* key);
+    static keymaster_error_t delete_all_keys(const keymaster2_device_t* dev);
+    static keymaster_error_t begin(const keymaster2_device_t* dev, keymaster_purpose_t purpose,
+                                   const keymaster_key_blob_t* key,
+                                   const keymaster_key_param_set_t* in_params,
+                                   keymaster_key_param_set_t* out_params,
+                                   keymaster_operation_handle_t* operation_handle);
+    static keymaster_error_t update(const keymaster2_device_t* dev,
+                                    keymaster_operation_handle_t operation_handle,
+                                    const keymaster_key_param_set_t* in_params,
+                                    const keymaster_blob_t* input, size_t* input_consumed,
+                                    keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+    static keymaster_error_t finish(const keymaster2_device_t* dev,
+                                    keymaster_operation_handle_t operation_handle,
+                                    const keymaster_key_param_set_t* in_params,
+                                    const keymaster_blob_t* input, const keymaster_blob_t* signature,
+                                    keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+    static keymaster_error_t abort(const keymaster2_device_t* dev,
+                                   keymaster_operation_handle_t operation_handle);
 
-    keymaster0_device_t device_;
+    keymaster2_device_t device_;
     keymaster_error_t error_;
     int32_t message_version_;
 };
 
 }  // namespace keymaster
 
-#endif  // EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#endif  // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
diff --git a/trusty/keymaster/trusty_keymaster_ipc.c b/trusty/keymaster/trusty_keymaster_ipc.cpp
similarity index 75%
rename from trusty/keymaster/trusty_keymaster_ipc.c
rename to trusty/keymaster/trusty_keymaster_ipc.cpp
index 88546af..cdc2778 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.c
+++ b/trusty/keymaster/trusty_keymaster_ipc.cpp
@@ -26,8 +26,8 @@
 #include <log/log.h>
 #include <trusty/tipc.h>
 
-#include "trusty_keymaster_ipc.h"
 #include "keymaster_ipc.h"
+#include "trusty_keymaster_ipc.h"
 
 #define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
 
@@ -43,15 +43,15 @@
     return 0;
 }
 
-int trusty_keymaster_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
-                          uint32_t *out_size)  {
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+                          uint32_t* out_size) {
     if (handle_ == 0) {
         ALOGE("not connected\n");
         return -EINVAL;
     }
 
     size_t msg_size = in_size + sizeof(struct keymaster_message);
-    struct keymaster_message *msg = malloc(msg_size);
+    struct keymaster_message* msg = reinterpret_cast<struct keymaster_message*>(malloc(msg_size));
     msg->cmd = cmd;
     memcpy(msg->payload, in, in_size);
 
@@ -59,31 +59,30 @@
     free(msg);
 
     if (rc < 0) {
-        ALOGE("failed to send cmd (%d) to %s: %s\n", cmd,
-                KEYMASTER_PORT, strerror(errno));
+        ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
         return -errno;
     }
 
     rc = read(handle_, out, *out_size);
     if (rc < 0) {
-        ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n",
-                cmd, KEYMASTER_PORT, strerror(errno));
+        ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
+              strerror(errno));
         return -errno;
     }
 
-    if ((size_t) rc < sizeof(struct keymaster_message)) {
-        ALOGE("invalid response size (%d)\n", (int) rc);
+    if ((size_t)rc < sizeof(struct keymaster_message)) {
+        ALOGE("invalid response size (%d)\n", (int)rc);
         return -EINVAL;
     }
 
-    msg = (struct keymaster_message *) out;
+    msg = (struct keymaster_message*)out;
 
     if ((cmd | KEYMASTER_RESP_BIT) != msg->cmd) {
         ALOGE("invalid command (%d)", msg->cmd);
         return -EINVAL;
     }
 
-    *out_size = ((size_t) rc) - sizeof(struct keymaster_message);
+    *out_size = ((size_t)rc) - sizeof(struct keymaster_message);
     return rc;
 }
 
@@ -92,4 +91,3 @@
         tipc_close(handle_);
     }
 }
-
diff --git a/trusty/keymaster/trusty_keymaster_ipc.h b/trusty/keymaster/trusty_keymaster_ipc.h
index 9785247..c15f7c1 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.h
+++ b/trusty/keymaster/trusty_keymaster_ipc.h
@@ -14,11 +14,16 @@
  * limitations under the License.
  */
 
+#ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
+#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
+
 __BEGIN_DECLS
 
 int trusty_keymaster_connect(void);
-int trusty_keymaster_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
-         uint32_t *out_size);
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+                          uint32_t* out_size);
 void trusty_keymaster_disconnect(void);
 
 __END_DECLS
+
+#endif  // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
diff --git a/trusty/keymaster/trusty_keymaster_main.cpp b/trusty/keymaster/trusty_keymaster_main.cpp
index 7ed880e..9c2ae2d 100644
--- a/trusty/keymaster/trusty_keymaster_main.cpp
+++ b/trusty/keymaster/trusty_keymaster_main.cpp
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
+#include <keymaster/keymaster_configuration.h>
+
 #include <stdio.h>
+#include <memory>
 
 #include <openssl/evp.h>
 #include <openssl/x509.h>
@@ -102,6 +105,28 @@
     0xd1, 0x1f, 0xd4, 0x49, 0x49, 0xe0, 0xb2, 0x18, 0x3b, 0xfe};
 unsigned int ec_privkey_pk8_der_len = 138;
 
+keymaster_key_param_t ec_params[] = {
+    keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_EC),
+    keymaster_param_long(KM_TAG_EC_CURVE, KM_EC_CURVE_P_521),
+    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+    keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+    keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t ec_param_set = {ec_params, sizeof(ec_params) / sizeof(*ec_params)};
+
+keymaster_key_param_t rsa_params[] = {
+    keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA),
+    keymaster_param_int(KM_TAG_KEY_SIZE, 1024),
+    keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, 65537),
+    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+    keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+    keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+    keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t rsa_param_set = {rsa_params, sizeof(rsa_params) / sizeof(*rsa_params)};
+
 struct EVP_PKEY_Delete {
     void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
 };
@@ -110,41 +135,70 @@
     void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
 };
 
+static bool do_operation(TrustyKeymasterDevice* device, keymaster_purpose_t purpose,
+                         keymaster_key_blob_t* key, keymaster_blob_t* input,
+                         keymaster_blob_t* signature, keymaster_blob_t* output) {
+    keymaster_key_param_t params[] = {
+        keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+        keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+    };
+    keymaster_key_param_set_t param_set = {params, sizeof(params) / sizeof(*params)};
+    keymaster_operation_handle_t op_handle;
+    keymaster_error_t error = device->begin(purpose, key, &param_set, nullptr, &op_handle);
+    if (error != KM_ERROR_OK) {
+        printf("Keymaster begin() failed: %d\n", error);
+        return false;
+    }
+    size_t input_consumed;
+    error = device->update(op_handle, nullptr, input, &input_consumed, nullptr, nullptr);
+    if (error != KM_ERROR_OK) {
+        printf("Keymaster update() failed: %d\n", error);
+        return false;
+    }
+    if (input_consumed != input->data_length) {
+        // This should never happen. If it does, it's a bug in the keymaster implementation.
+        printf("Keymaster update() did not consume all data.\n");
+        device->abort(op_handle);
+        return false;
+    }
+    error = device->finish(op_handle, nullptr, nullptr, signature, nullptr, output);
+    if (error != KM_ERROR_OK) {
+        printf("Keymaster finish() failed: %d\n", error);
+        return false;
+    }
+    return true;
+}
+
 static bool test_import_rsa(TrustyKeymasterDevice* device) {
     printf("===================\n");
     printf("= RSA Import Test =\n");
     printf("===================\n\n");
 
     printf("=== Importing RSA keypair === \n");
-    uint8_t* key;
-    size_t size;
-    int error = device->import_keypair(rsa_privkey_pk8_der, rsa_privkey_pk8_der_len, &key, &size);
+    keymaster_key_blob_t key;
+    keymaster_blob_t private_key = {rsa_privkey_pk8_der, rsa_privkey_pk8_der_len};
+    int error = device->import_key(&rsa_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
     if (error != KM_ERROR_OK) {
-        printf("Error importing key pair: %d\n\n", error);
+        printf("Error importing RSA key: %d\n\n", error);
         return false;
     }
-    UniquePtr<uint8_t[]> key_deleter(key);
+    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
 
     printf("=== Signing with imported RSA key ===\n");
-    keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = 1024 / 8;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     memset(message.get(), 'a', message_len);
-    uint8_t* signature;
-    size_t signature_len;
-    error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
-                              &signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error signing data with imported RSA key: %d\n\n", error);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with imported RSA key\n\n");
         return false;
     }
-    UniquePtr<uint8_t[]> signature_deleter(signature);
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
 
     printf("=== Verifying with imported RSA key === \n");
-    error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
-                                signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error verifying data with imported RSA key: %d\n\n", error);
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with imported RSA key\n\n");
         return false;
     }
 
@@ -158,67 +212,58 @@
     printf("============\n\n");
 
     printf("=== Generating RSA key pair ===\n");
-    keymaster_rsa_keygen_params_t params;
-    params.public_exponent = 65537;
-    params.modulus_size = 2048;
-
-    uint8_t* key;
-    size_t size;
-    int error = device->generate_keypair(TYPE_RSA, &params, &key, &size);
+    keymaster_key_blob_t key;
+    int error = device->generate_key(&rsa_param_set, &key, nullptr);
     if (error != KM_ERROR_OK) {
         printf("Error generating RSA key pair: %d\n\n", error);
         return false;
     }
-    UniquePtr<uint8_t[]> deleter(key);
+    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
 
     printf("=== Signing with RSA key === \n");
-    keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE};
-    size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    size_t message_len = 1024 / 8;
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     memset(message.get(), 'a', message_len);
-    uint8_t* signature;
-    size_t signature_len;
-    error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
-                              &signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error signing data with RSA key: %d\n\n", error);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with RSA key\n\n");
         return false;
     }
-    UniquePtr<uint8_t[]> signature_deleter(signature);
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
 
     printf("=== Verifying with RSA key === \n");
-    error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
-                                signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error verifying data with RSA key: %d\n\n", error);
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with RSA key\n\n");
         return false;
     }
 
     printf("=== Exporting RSA public key ===\n");
-    uint8_t* exported_key;
-    size_t exported_size;
-    error = device->get_keypair_public(key, size, &exported_key, &exported_size);
+    keymaster_blob_t exported_key;
+    error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
     if (error != KM_ERROR_OK) {
         printf("Error exporting RSA public key: %d\n\n", error);
         return false;
     }
 
     printf("=== Verifying with exported key ===\n");
-    const uint8_t* tmp = exported_key;
-    UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &tmp, exported_size));
-    UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+    const uint8_t* tmp = exported_key.data;
+    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+        d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
     if (EVP_PKEY_verify_init(ctx.get()) != 1) {
-        printf("Error initializing openss EVP context\n");
+        printf("Error initializing openss EVP context\n\n");
         return false;
     }
     if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
-        printf("Exported key was the wrong type?!?\n");
+        printf("Exported key was the wrong type?!?\n\n");
         return false;
     }
 
     EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
-    if (EVP_PKEY_verify(ctx.get(), signature, signature_len, message.get(), message_len) != 1) {
-        printf("Verification with exported pubkey failed.\n");
+    if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+                        message_len) != 1) {
+        printf("Verification with exported pubkey failed.\n\n");
         return false;
     } else {
         printf("Verification succeeded\n");
@@ -234,35 +279,31 @@
     printf("=====================\n\n");
 
     printf("=== Importing ECDSA keypair === \n");
-    uint8_t* key;
-    size_t size;
-    int error = device->import_keypair(ec_privkey_pk8_der, ec_privkey_pk8_der_len, &key, &size);
+    keymaster_key_blob_t key;
+    keymaster_blob_t private_key = {ec_privkey_pk8_der, ec_privkey_pk8_der_len};
+    int error = device->import_key(&ec_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
     if (error != KM_ERROR_OK) {
-        printf("Error importing key pair: %d\n\n", error);
+        printf("Error importing ECDSA key: %d\n\n", error);
         return false;
     }
-    UniquePtr<uint8_t[]> deleter(key);
+    std::unique_ptr<const uint8_t[]> deleter(key.key_material);
 
     printf("=== Signing with imported ECDSA key ===\n");
     keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
     size_t message_len = 30 /* arbitrary */;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     memset(message.get(), 'a', message_len);
-    uint8_t* signature;
-    size_t signature_len;
-    error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
-                              &signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error signing data with imported ECDSA key: %d\n\n", error);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with imported ECDSA key\n\n");
         return false;
     }
-    UniquePtr<uint8_t[]> signature_deleter(signature);
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
 
     printf("=== Verifying with imported ECDSA key === \n");
-    error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
-                                signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error verifying data with imported ECDSA key: %d\n\n", error);
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with imported ECDSA key\n\n");
         return false;
     }
 
@@ -276,64 +317,57 @@
     printf("==============\n\n");
 
     printf("=== Generating ECDSA key pair ===\n");
-    keymaster_ec_keygen_params_t params;
-    params.field_size = 521;
-    uint8_t* key;
-    size_t size;
-    int error = device->generate_keypair(TYPE_EC, &params, &key, &size);
-    if (error != 0) {
+    keymaster_key_blob_t key;
+    int error = device->generate_key(&ec_param_set, &key, nullptr);
+    if (error != KM_ERROR_OK) {
         printf("Error generating ECDSA key pair: %d\n\n", error);
         return false;
     }
-    UniquePtr<uint8_t[]> deleter(key);
+    std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
 
     printf("=== Signing with ECDSA key === \n");
-    keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
     size_t message_len = 30 /* arbitrary */;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     memset(message.get(), 'a', message_len);
-    uint8_t* signature;
-    size_t signature_len;
-    error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
-                              &signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error signing data with ECDSA key: %d\n\n", error);
+    keymaster_blob_t input = {message.get(), message_len}, signature;
+
+    if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+        printf("Error signing data with ECDSA key\n\n");
         return false;
     }
-    UniquePtr<uint8_t[]> signature_deleter(signature);
+    std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
 
     printf("=== Verifying with ECDSA key === \n");
-    error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
-                                signature_len);
-    if (error != KM_ERROR_OK) {
-        printf("Error verifying data with ECDSA key: %d\n\n", error);
+    if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+        printf("Error verifying data with ECDSA key\n\n");
         return false;
     }
 
     printf("=== Exporting ECDSA public key ===\n");
-    uint8_t* exported_key;
-    size_t exported_size;
-    error = device->get_keypair_public(key, size, &exported_key, &exported_size);
+    keymaster_blob_t exported_key;
+    error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
     if (error != KM_ERROR_OK) {
         printf("Error exporting ECDSA public key: %d\n\n", error);
         return false;
     }
 
     printf("=== Verifying with exported key ===\n");
-    const uint8_t* tmp = exported_key;
-    UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &tmp, exported_size));
-    UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+    const uint8_t* tmp = exported_key.data;
+    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+        d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
     if (EVP_PKEY_verify_init(ctx.get()) != 1) {
-        printf("Error initializing openss EVP context\n");
+        printf("Error initializing openssl EVP context\n\n");
         return false;
     }
     if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
-        printf("Exported key was the wrong type?!?\n");
+        printf("Exported key was the wrong type?!?\n\n");
         return false;
     }
 
-    if (EVP_PKEY_verify(ctx.get(), signature, signature_len, message.get(), message_len) != 1) {
-        printf("Verification with exported pubkey failed.\n");
+    if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+                        message_len) != 1) {
+        printf("Verification with exported pubkey failed.\n\n");
         return false;
     } else {
         printf("Verification succeeded\n");
@@ -344,8 +378,8 @@
 }
 
 int main(void) {
-
     TrustyKeymasterDevice device(NULL);
+    keymaster::ConfigureDevice(reinterpret_cast<keymaster2_device_t*>(&device));
     if (device.session_error() != KM_ERROR_OK) {
         printf("Failed to initialize Trusty session: %d\n", device.session_error());
         return 1;