Merge "fastboot: fix USB_TRACE, make it better."
diff --git a/.clang-format-2 b/.clang-format-2
index 41591ce..ede5d7e 100644
--- a/.clang-format-2
+++ b/.clang-format-2
@@ -7,4 +7,3 @@
 PointerAlignment: Left
 TabWidth: 2
 UseTab: Never
-PenaltyExcessCharacter: 32
diff --git a/.clang-format-4 b/.clang-format-4
index ae4a451..55773a2 100644
--- a/.clang-format-4
+++ b/.clang-format-4
@@ -5,7 +5,7 @@
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
 IndentWidth: 4
+ContinuationIndentWidth: 8
 PointerAlignment: Left
 TabWidth: 4
 UseTab: Never
-PenaltyExcessCharacter: 32
diff --git a/CleanSpec.mk b/CleanSpec.mk
index dc45959..0e43dae 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -74,3 +74,7 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/)
diff --git a/OWNERS b/OWNERS
index 1d319af..682a067 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1 @@
 enh@google.com
-per-file libsysutils/src/Netlink* = ek@google.com
-per-file libsysutils/src/Netlink* = lorenzo@google.com
-per-file libsysutils/include/sysutils/Netlink* = ek@google.com
-per-file libsysutils/include/sysutils/Netlink* = lorenzo@google.com
diff --git a/adb/Android.bp b/adb/Android.bp
index 1f41e4f..2a9a579 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -19,17 +19,13 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-Wexit-time-destructors",
         "-Wno-unused-parameter",
         "-Wno-missing-field-initializers",
         "-Wvla",
     ],
     rtti: true,
 
-    clang_cflags: [
-        "-Wexit-time-destructors",
-        "-Wthread-safety",
-    ],
-
     use_version_lib: true,
 
     compile_multilib: "first",
@@ -85,6 +81,12 @@
                 "-luserenv",
             ],
         },
+
+        not_windows: {
+            cflags: [
+                "-Wthread-safety",
+            ],
+        },
     },
 }
 
@@ -96,6 +98,7 @@
     "adb_io.cpp",
     "adb_listeners.cpp",
     "adb_trace.cpp",
+    "adb_unique_fd.cpp",
     "adb_utils.cpp",
     "fdevent.cpp",
     "services.cpp",
@@ -103,6 +106,7 @@
     "socket_spec.cpp",
     "sysdeps/errno.cpp",
     "transport.cpp",
+    "transport_fd.cpp",
     "transport_local.cpp",
     "transport_usb.cpp",
 ]
@@ -276,6 +280,7 @@
 cc_library_static {
     name: "libadbd",
     defaults: ["adb_defaults"],
+    recovery_available: true,
 
     // libminadbd wants both, for some reason.
     compile_multilib: "both",
@@ -294,14 +299,17 @@
         "libqemu_pipe",
         "libbase",
     ],
+
+    export_include_dirs: [
+        "daemon/include",
+    ],
 }
 
 cc_binary {
     name: "adbd",
     defaults: ["adb_defaults"],
 
-    // adbd must be static, as it is copied into the recovery image.
-    static_executable: true,
+    recovery_available: true,
 
     srcs: [
         "daemon/main.cpp",
@@ -341,7 +349,6 @@
         "libselinux",
         "libsquashfs_utils",
         "libqemu_pipe",
-        "libdebuggerd_handler",
 
         "libbase",
         "libcutils",
@@ -368,6 +375,7 @@
         "liblog",
         "libusb",
         "libmdnssd",
+        "libselinux",
     ],
     test_suites: ["device-tests"],
 }
diff --git a/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT
index 29a6992..f0b184c 100644
--- a/adb/OVERVIEW.TXT
+++ b/adb/OVERVIEW.TXT
@@ -103,9 +103,6 @@
            4-byte hex length, followed by a string giving the reason
            for failure.
 
-        3. As a special exception, for 'host:version', a 4-byte
-           hex string corresponding to the server's internal version number
-
     Note that the connection is still alive after an OKAY, which allows the
     client to make other requests. But in certain cases, an OKAY will even
     change the state of the connection.
diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT
index 30c21f7..3e18a54 100644
--- a/adb/SERVICES.TXT
+++ b/adb/SERVICES.TXT
@@ -7,10 +7,6 @@
 host:version
     Ask the ADB server for its internal version number.
 
-    As a special exception, the server will respond with a 4-byte
-    hex string corresponding to its internal version number, without
-    any OKAY or FAIL.
-
 host:kill
     Ask the ADB server to quit immediately. This is used when the
     ADB client detects that an obsolete server is running after an
diff --git a/adb/adb.bash b/adb/adb.bash
index d36bec3..b1b3957 100644
--- a/adb/adb.bash
+++ b/adb/adb.bash
@@ -16,11 +16,11 @@
 #
 
 _adb() {
-    if ! type -t "$1" >/dev/null; then
+    if ! check_type "$1" >/dev/null; then
         return
     fi
 
-    if type -t _init_completion >/dev/null; then
+    if check_type _init_completion >/dev/null; then
         _init_completion || return
     fi
 
@@ -435,7 +435,7 @@
     fi
 
     # Since we're probably doing file completion here, don't add a space after.
-    if [[ $(type -t compopt) = "builtin" ]]; then
+    if [[ $(check_type compopt) == "builtin" ]]; then
         compopt -o nospace
     fi
 
@@ -451,7 +451,7 @@
     xspec=$2
 
     # Since we're probably doing file completion here, don't add a space after.
-    if [[ $(type -t compopt) = "builtin" ]]; then
+    if [[ $(check_type compopt) == "builtin" ]]; then
         compopt -o plusdirs
         if [[ "${xspec}" == "" ]]; then
             COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -- "${cur}") )
@@ -492,7 +492,7 @@
 }
 
 
-if [[ $(type -t compopt) = "builtin" ]]; then
+if [[ $(check_type compopt) == "builtin" ]]; then
     complete -F _adb adb
 else
     complete -o nospace -F _adb adb
diff --git a/adb/adb.cpp b/adb/adb.cpp
index f8a54c6..9b48702 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -264,15 +264,6 @@
     send_packet(cp, t);
 }
 
-// qual_overwrite is used to overwrite a qualifier string.  dst is a
-// pointer to a char pointer.  It is assumed that if *dst is non-NULL, it
-// was malloc'ed and needs to freed.  *dst will be set to a dup of src.
-// TODO: switch to std::string for these atransport fields instead.
-static void qual_overwrite(char** dst, const std::string& src) {
-    free(*dst);
-    *dst = strdup(src.c_str());
-}
-
 void parse_banner(const std::string& banner, atransport* t) {
     D("parse_banner: %s", banner.c_str());
 
@@ -296,11 +287,11 @@
             const std::string& key = key_value[0];
             const std::string& value = key_value[1];
             if (key == "ro.product.name") {
-                qual_overwrite(&t->product, value);
+                t->product = value;
             } else if (key == "ro.product.model") {
-                qual_overwrite(&t->model, value);
+                t->model = value;
             } else if (key == "ro.product.device") {
-                qual_overwrite(&t->device, value);
+                t->device = value;
             } else if (key == "features") {
                 t->SetFeatures(value);
             }
@@ -415,7 +406,7 @@
         if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
             asocket* s = find_local_socket(p->msg.arg1, 0);
             if (s) {
-                if(s->peer == 0) {
+                if(s->peer == nullptr) {
                     /* On first READY message, create the connection. */
                     s->peer = create_remote_socket(p->msg.arg0, t);
                     s->peer->peer = s;
@@ -424,8 +415,8 @@
                     /* Other READY messages must use the same local-id */
                     s->ready(s);
                 } else {
-                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s",
-                      p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial);
+                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s", p->msg.arg0,
+                      p->msg.arg1, s->peer->id, p->msg.arg1, t->serial.c_str());
                 }
             } else {
                 // When receiving A_OKAY from device for A_OPEN request, the host server may
@@ -451,8 +442,8 @@
                  * socket has a peer on the same transport.
                  */
                 if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) {
-                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s",
-                      p->msg.arg1, t->serial, s->peer->transport->serial);
+                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s", p->msg.arg1,
+                      t->serial.c_str(), s->peer->transport->serial.c_str());
                 } else {
                     s->close(s);
                 }
@@ -1171,7 +1162,7 @@
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
+            return SendOkay(reply_fd, !t->serial.empty() ? t->serial : "unknown");
         } else {
             return SendFail(reply_fd, error);
         }
@@ -1180,7 +1171,7 @@
         std::string error;
         atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
+            return SendOkay(reply_fd, !t->devpath.empty() ? t->devpath : "unknown");
         } else {
             return SendFail(reply_fd, error);
         }
diff --git a/adb/adb.h b/adb/adb.h
index ede55da..7e9af9e 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -124,8 +124,6 @@
 
 void print_packet(const char* label, apacket* p);
 
-// These use the system (v)fprintf, not the adb prefixed ones defined in sysdeps.h, so they
-// shouldn't be tagged with ADB_FORMAT_ARCHETYPE.
 void fatal(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
 void fatal_errno(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
 
@@ -158,11 +156,6 @@
 
 int handle_forward_request(const char* service, atransport* transport, int reply_fd);
 
-#if !ADB_HOST
-void framebuffer_service(int fd, void* cookie);
-void set_verity_enabled_state_service(int fd, void* cookie);
-#endif
-
 /* packet allocator */
 apacket* get_apacket(void);
 void put_apacket(apacket* p);
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 38e3116..6cc274b 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -49,7 +49,7 @@
     }
     buf[4] = 0;
 
-    unsigned long len = strtoul(buf, 0, 16);
+    unsigned long len = strtoul(buf, nullptr, 16);
     s->resize(len, '\0');
     if (!ReadFdExactly(fd, &(*s)[0], len)) {
         *error = perror_str("protocol fault (couldn't read status message)");
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index ea5a44e..f4a92e3 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -136,9 +136,10 @@
         }
         //  <device-serial> " " <local-name> " " <remote-name> "\n"
         // Entries from "adb reverse" have no serial.
-        android::base::StringAppendF(&result, "%s %s %s\n",
-                                     l->transport->serial ? l->transport->serial : "(reverse)",
-                                     l->local_name.c_str(), l->connect_to.c_str());
+        android::base::StringAppendF(
+                &result, "%s %s %s\n",
+                !l->transport->serial.empty() ? l->transport->serial.c_str() : "(reverse)",
+                l->local_name.c_str(), l->connect_to.c_str());
     }
     return result;
 }
diff --git a/adb/adb_unique_fd.cpp b/adb/adb_unique_fd.cpp
new file mode 100644
index 0000000..2079be1
--- /dev/null
+++ b/adb/adb_unique_fd.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb_unique_fd.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "sysdeps.h"
+
+#if !defined(_WIN32)
+bool Pipe(unique_fd* read, unique_fd* write, int flags) {
+    int pipefd[2];
+#if !defined(__APPLE__)
+    if (pipe2(pipefd, flags) != 0) {
+        return false;
+    }
+#else
+    // Darwin doesn't have pipe2. Implement it ourselves.
+    if (flags != 0 && (flags & ~(O_CLOEXEC | O_NONBLOCK)) != 0) {
+        errno = EINVAL;
+        return false;
+    }
+
+    if (pipe(pipefd) != 0) {
+        return false;
+    }
+
+    if (flags & O_CLOEXEC) {
+        if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
+            fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+            adb_close(pipefd[0]);
+            adb_close(pipefd[1]);
+            return false;
+        }
+    }
+
+    if (flags & O_NONBLOCK) {
+        if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
+            fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
+            adb_close(pipefd[0]);
+            adb_close(pipefd[1]);
+            return false;
+        }
+    }
+#endif
+
+    read->reset(pipefd[0]);
+    write->reset(pipefd[1]);
+    return true;
+}
+#endif
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index 7d2354d..be63262 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <errno.h>
 #include <unistd.h>
 
 #include <android-base/unique_fd.h>
@@ -28,40 +29,5 @@
 using unique_fd = android::base::unique_fd_impl<AdbCloser>;
 
 #if !defined(_WIN32)
-inline bool Pipe(unique_fd* read, unique_fd* write, int flags = 0) {
-    int pipefd[2];
-#if !defined(__APPLE__)
-    if (pipe2(pipefd, flags) != 0) {
-        return false;
-    }
-#else
-    // Darwin doesn't have pipe2. Implement it ourselves.
-    if (flags != 0 && (flags & ~(O_CLOEXEC | O_NONBLOCK)) != 0) {
-        errno = EINVAL;
-        return false;
-    }
-
-    if (pipe(pipefd) != 0) {
-        return false;
-    }
-
-    if (flags & O_CLOEXEC) {
-        if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
-            fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
-            PLOG(FATAL) << "failed to set FD_CLOEXEC on newly created pipe";
-        }
-    }
-
-    if (flags & O_NONBLOCK) {
-        if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
-            fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
-            PLOG(FATAL) << "failed to set O_NONBLOCK  on newly created pipe";
-        }
-    }
-#endif
-
-    read->reset(pipefd[0]);
-    write->reset(pipefd[1]);
-    return true;
-}
+bool Pipe(unique_fd* read, unique_fd* write, int flags = 0);
 #endif
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index b236fb3..0c3327f 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -79,22 +79,24 @@
 }
 
 std::string escape_arg(const std::string& s) {
-  std::string result = s;
-
   // Escape any ' in the string (before we single-quote the whole thing).
   // The correct way to do this for the shell is to replace ' with '\'' --- that is,
   // close the existing single-quoted string, escape a single single-quote, and start
   // a new single-quoted string. Like the C preprocessor, the shell will concatenate
   // these pieces into one string.
-  for (size_t i = 0; i < s.size(); ++i) {
-    if (s[i] == '\'') {
-      result.insert(i, "'\\'");
-      i += 2;
-    }
+
+  std::string result;
+  result.push_back('\'');
+
+  size_t base = 0;
+  while (true) {
+    size_t found = s.find('\'', base);
+    result.append(s, base, found - base);
+    if (found == s.npos) break;
+    result.append("'\\''");
+    base = found + 1;
   }
 
-  // Prefix and suffix the whole string with '.
-  result.insert(result.begin(), '\'');
   result.push_back('\'');
   return result;
 }
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index e1b6287..341323f 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -82,30 +82,38 @@
 #endif
 
 TEST(adb_utils, escape_arg) {
-  ASSERT_EQ(R"('')", escape_arg(""));
+  EXPECT_EQ(R"('')", escape_arg(""));
 
-  ASSERT_EQ(R"('abc')", escape_arg("abc"));
+  EXPECT_EQ(R"('abc')", escape_arg("abc"));
 
-  ASSERT_EQ(R"(' abc')", escape_arg(" abc"));
-  ASSERT_EQ(R"(''\''abc')", escape_arg("'abc"));
-  ASSERT_EQ(R"('"abc')", escape_arg("\"abc"));
-  ASSERT_EQ(R"('\abc')", escape_arg("\\abc"));
-  ASSERT_EQ(R"('(abc')", escape_arg("(abc"));
-  ASSERT_EQ(R"(')abc')", escape_arg(")abc"));
+  auto wrap = [](const std::string& x) { return '\'' + x + '\''; };
+  const std::string q = R"('\'')";
+  EXPECT_EQ(wrap(q), escape_arg("'"));
+  EXPECT_EQ(wrap(q + q), escape_arg("''"));
+  EXPECT_EQ(wrap(q + "abc" + q), escape_arg("'abc'"));
+  EXPECT_EQ(wrap(q + "abc"), escape_arg("'abc"));
+  EXPECT_EQ(wrap("abc" + q), escape_arg("abc'"));
+  EXPECT_EQ(wrap("abc" + q + "def"), escape_arg("abc'def"));
+  EXPECT_EQ(wrap("a" + q + "b" + q + "c"), escape_arg("a'b'c"));
+  EXPECT_EQ(wrap("a" + q + "bcde" + q + "f"), escape_arg("a'bcde'f"));
 
-  ASSERT_EQ(R"('abc abc')", escape_arg("abc abc"));
-  ASSERT_EQ(R"('abc'\''abc')", escape_arg("abc'abc"));
-  ASSERT_EQ(R"('abc"abc')", escape_arg("abc\"abc"));
-  ASSERT_EQ(R"('abc\abc')", escape_arg("abc\\abc"));
-  ASSERT_EQ(R"('abc(abc')", escape_arg("abc(abc"));
-  ASSERT_EQ(R"('abc)abc')", escape_arg("abc)abc"));
+  EXPECT_EQ(R"(' abc')", escape_arg(" abc"));
+  EXPECT_EQ(R"('"abc')", escape_arg("\"abc"));
+  EXPECT_EQ(R"('\abc')", escape_arg("\\abc"));
+  EXPECT_EQ(R"('(abc')", escape_arg("(abc"));
+  EXPECT_EQ(R"(')abc')", escape_arg(")abc"));
 
-  ASSERT_EQ(R"('abc ')", escape_arg("abc "));
-  ASSERT_EQ(R"('abc'\''')", escape_arg("abc'"));
-  ASSERT_EQ(R"('abc"')", escape_arg("abc\""));
-  ASSERT_EQ(R"('abc\')", escape_arg("abc\\"));
-  ASSERT_EQ(R"('abc(')", escape_arg("abc("));
-  ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
+  EXPECT_EQ(R"('abc abc')", escape_arg("abc abc"));
+  EXPECT_EQ(R"('abc"abc')", escape_arg("abc\"abc"));
+  EXPECT_EQ(R"('abc\abc')", escape_arg("abc\\abc"));
+  EXPECT_EQ(R"('abc(abc')", escape_arg("abc(abc"));
+  EXPECT_EQ(R"('abc)abc')", escape_arg("abc)abc"));
+
+  EXPECT_EQ(R"('abc ')", escape_arg("abc "));
+  EXPECT_EQ(R"('abc"')", escape_arg("abc\""));
+  EXPECT_EQ(R"('abc\')", escape_arg("abc\\"));
+  EXPECT_EQ(R"('abc(')", escape_arg("abc("));
+  EXPECT_EQ(R"('abc)')", escape_arg("abc)"));
 }
 
 void test_mkdirs(const std::string& basepath) {
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 849a6e7..1959258 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -46,7 +46,7 @@
 #include "sysdeps/chrono.h"
 
 static TransportType __adb_transport = kTransportAny;
-static const char* __adb_serial = NULL;
+static const char* __adb_serial = nullptr;
 static TransportId __adb_transport_id = 0;
 
 static const char* __adb_server_socket_spec;
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index 0f4dd33..5fbef09 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -109,7 +109,7 @@
     LOG(INFO) << "generate_key(" << file << ")...";
 
     mode_t old_mask;
-    FILE *f = NULL;
+    FILE *f = nullptr;
     int ret = 0;
 
     EVP_PKEY* pkey = EVP_PKEY_new();
@@ -121,7 +121,7 @@
     }
 
     BN_set_word(exponent, RSA_F4);
-    RSA_generate_key_ex(rsa, 2048, exponent, NULL);
+    RSA_generate_key_ex(rsa, 2048, exponent, nullptr);
     EVP_PKEY_set1_RSA(pkey, rsa);
 
     old_mask = umask(077);
@@ -135,7 +135,7 @@
 
     umask(old_mask);
 
-    if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
+    if (!PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr)) {
         D("Failed to write key");
         goto out;
     }
@@ -302,7 +302,7 @@
 static std::string adb_auth_sign(RSA* key, const char* token, size_t token_size) {
     if (token_size != TOKEN_SIZE) {
         D("Unexpected token size %zd", token_size);
-        return 0;
+        return nullptr;
     }
 
     std::string result;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 39983ab..7791895 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -362,9 +362,8 @@
 }
 
 static void copy_to_file(int inFd, int outFd) {
-    const size_t BUFSIZE = 32 * 1024;
-    char* buf = (char*) malloc(BUFSIZE);
-    if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
+    constexpr size_t BUFSIZE = 32 * 1024;
+    std::vector<char> buf(BUFSIZE);
     int len;
     long total = 0;
     int old_stdin_mode = -1;
@@ -376,9 +375,9 @@
 
     while (true) {
         if (inFd == STDIN_FILENO) {
-            len = unix_read(inFd, buf, BUFSIZE);
+            len = unix_read(inFd, buf.data(), BUFSIZE);
         } else {
-            len = adb_read(inFd, buf, BUFSIZE);
+            len = adb_read(inFd, buf.data(), BUFSIZE);
         }
         if (len == 0) {
             D("copy_to_file() : read 0 bytes; exiting");
@@ -389,10 +388,10 @@
             break;
         }
         if (outFd == STDOUT_FILENO) {
-            fwrite(buf, 1, len, stdout);
+            fwrite(buf.data(), 1, len, stdout);
             fflush(stdout);
         } else {
-            adb_write(outFd, buf, len);
+            adb_write(outFd, buf.data(), len);
         }
         total += len;
     }
@@ -400,7 +399,6 @@
     stdinout_raw_epilogue(inFd, outFd, old_stdin_mode, old_stdout_mode);
 
     D("copy_to_file() finished after %lu bytes", total);
-    free(buf);
 }
 
 static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
@@ -885,7 +883,7 @@
             return 0;
         }
 
-        int block = strtol(buf, NULL, 10);
+        int block = strtol(buf, nullptr, 10);
 
         size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
         if (offset >= static_cast<size_t>(sb.st_size)) {
@@ -968,7 +966,7 @@
             //argv[2] and beyond become ppp_args[1] and beyond
             ppp_args[i - 1] = argv[i];
         }
-        ppp_args[i-1] = NULL;
+        ppp_args[i-1] = nullptr;
 
         // child side
 
@@ -1136,31 +1134,28 @@
         cmd += " " + escape_arg(*argv++);
     }
 
-    // No need for shell protocol with logcat, always disable for simplicity.
-    return send_shell_command(cmd, true);
+    return send_shell_command(cmd);
 }
 
 static void write_zeros(int bytes, int fd) {
     int old_stdin_mode = -1;
     int old_stdout_mode = -1;
-    char* buf = (char*) calloc(1, bytes);
-    if (buf == nullptr) fatal("couldn't allocate buffer for write_zeros");
+    std::vector<char> buf(bytes);
 
     D("write_zeros(%d) -> %d", bytes, fd);
 
     stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
 
     if (fd == STDOUT_FILENO) {
-        fwrite(buf, 1, bytes, stdout);
+        fwrite(buf.data(), 1, bytes, stdout);
         fflush(stdout);
     } else {
-        adb_write(fd, buf, bytes);
+        adb_write(fd, buf.data(), bytes);
     }
 
     stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
 
     D("write_zeros() finished");
-    free(buf);
 }
 
 static int backup(int argc, const char** argv) {
@@ -1175,7 +1170,7 @@
                 argv[i++] = argv[j++];
             }
             argc -= 2;
-            argv[argc] = NULL;
+            argv[argc] = nullptr;
         }
     }
 
@@ -1796,6 +1791,11 @@
     }
     else if (!strcmp(argv[0], "track-devices")) {
         return adb_connect_command("host:track-devices");
+    } else if (!strcmp(argv[0], "raw")) {
+        if (argc != 2) {
+            return syntax_error("adb raw SERVICE");
+        }
+        return adb_connect_command(argv[1]);
     }
 
 
@@ -1861,7 +1861,7 @@
         cmd += " " + escape_arg(*argv++);
     }
 
-    return send_shell_command(cmd, false);
+    return send_shell_command(cmd);
 }
 
 static int install_app(int argc, const char** argv) {
@@ -1965,7 +1965,7 @@
         char* end = strrchr(buf, ']');
         if (start && end) {
             *end = '\0';
-            session_id = strtol(start + 1, NULL, 10);
+            session_id = strtol(start + 1, nullptr, 10);
         }
     }
     if (session_id < 0) {
@@ -2049,7 +2049,7 @@
         cmd += " " + escape_arg(*argv++);
     }
 
-    return send_shell_command(cmd, false);
+    return send_shell_command(cmd);
 }
 
 static int uninstall_app_legacy(int argc, const char** argv) {
@@ -2073,7 +2073,7 @@
 
 static int delete_file(const std::string& filename) {
     std::string cmd = "rm -f " + escape_arg(filename);
-    return send_shell_command(cmd, false);
+    return send_shell_command(cmd);
 }
 
 static int install_app_legacy(int argc, const char** argv) {
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index 36cd798..3aa03a7 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -83,6 +83,14 @@
     DISALLOW_COPY_AND_ASSIGN(DefaultStandardStreamsCallback);
 };
 
+class SilentStandardStreamsCallbackInterface : public StandardStreamsCallbackInterface {
+  public:
+    SilentStandardStreamsCallbackInterface() = default;
+    void OnStdout(const char*, int) override final {}
+    void OnStderr(const char*, int) override final {}
+    int Done(int status) override final { return status; }
+};
+
 // Singleton.
 extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
 
@@ -92,7 +100,7 @@
 // resulting output.
 // if |callback| is non-null, stdout/stderr output will be handled by it.
 int send_shell_command(
-    const std::string& command, bool disable_shell_protocol,
-    StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
+        const std::string& command, bool disable_shell_protocol = false,
+        StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
 
 #endif  // COMMANDLINE_H
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 26f8d83..a438dbb 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -44,6 +44,8 @@
 #include "sysdeps/errno.h"
 #include "sysdeps/stat.h"
 
+#include "client/commandline.h"
+
 #include <android-base/file.h>
 #include <android-base/strings.h>
 #include <android-base/stringprintf.h>
@@ -202,12 +204,11 @@
         max = SYNC_DATA_MAX; // TODO: decide at runtime.
 
         std::string error;
-        FeatureSet features;
-        if (!adb_get_feature_set(&features, &error)) {
+        if (!adb_get_feature_set(&features_, &error)) {
             fd = -1;
             Error("failed to get feature set: %s", error.c_str());
         } else {
-            have_stat_v2_ = CanUseFeature(features, kFeatureStat2);
+            have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
             fd = adb_connect("sync:", &error);
             if (fd < 0) {
                 Error("connect failed: %s", error.c_str());
@@ -232,6 +233,8 @@
         line_printer_.KeepInfoLine();
     }
 
+    const FeatureSet& Features() const { return features_; }
+
     bool IsValid() { return fd >= 0; }
 
     bool ReceivedError(const char* from, const char* to) {
@@ -510,8 +513,7 @@
         return false;
     }
 
-
-    void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+    void Printf(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
         std::string s;
 
         va_list ap;
@@ -522,7 +524,7 @@
         line_printer_.Print(s, LinePrinter::INFO);
     }
 
-    void Println(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+    void Println(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
         std::string s;
 
         va_list ap;
@@ -534,7 +536,7 @@
         line_printer_.KeepInfoLine();
     }
 
-    void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+    void Error(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
         std::string s = "adb: error: ";
 
         va_list ap;
@@ -545,7 +547,7 @@
         line_printer_.Print(s, LinePrinter::ERROR);
     }
 
-    void Warning(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+    void Warning(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
         std::string s = "adb: warning: ";
 
         va_list ap;
@@ -577,6 +579,7 @@
 
   private:
     bool expect_done_;
+    FeatureSet features_;
     bool have_stat_v2_;
 
     TransferLedger global_ledger_;
@@ -806,7 +809,7 @@
 }
 
 static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
-                             const std::string& lpath,
+                             std::vector<std::string>* directory_list, const std::string& lpath,
                              const std::string& rpath) {
     std::vector<copyinfo> dirlist;
     std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath.c_str()), closedir);
@@ -849,21 +852,9 @@
     // Close this directory and recurse.
     dir.reset();
 
-    // Add the current directory to the list if it was empty, to ensure that
-    // it gets created.
-    if (empty_dir) {
-        // TODO(b/25566053): Make pushing empty directories work.
-        // TODO(b/25457350): We don't preserve permissions on directories.
-        sc.Warning("skipping empty directory '%s'", lpath.c_str());
-        copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath),
-                    android::base::Basename(lpath), S_IFDIR);
-        ci.skip = true;
-        file_list->push_back(ci);
-        return true;
-    }
-
     for (const copyinfo& ci : dirlist) {
-        local_build_list(sc, file_list, ci.lpath, ci.rpath);
+        directory_list->push_back(ci.rpath);
+        local_build_list(sc, file_list, directory_list, ci.lpath, ci.rpath);
     }
 
     return true;
@@ -880,11 +871,54 @@
 
     // Recursively build the list of files to copy.
     std::vector<copyinfo> file_list;
+    std::vector<std::string> directory_list;
+
+    for (std::string dirpath = rpath; dirpath != "/"; dirpath = android::base::Dirname(dirpath)) {
+        directory_list.push_back(dirpath);
+    }
+    std::reverse(directory_list.begin(), directory_list.end());
+
     int skipped = 0;
-    if (!local_build_list(sc, &file_list, lpath, rpath)) {
+    if (!local_build_list(sc, &file_list, &directory_list, lpath, rpath)) {
         return false;
     }
 
+    // b/110953234:
+    // P shipped with a bug that causes directory creation as a side-effect of a push to fail.
+    // Work around this by explicitly doing a mkdir via shell.
+    //
+    // Devices that don't support shell_v2 are unhappy if we try to send a too-long packet to them,
+    // but they're not affected by this bug, so only apply the workaround if we have shell_v2.
+    //
+    // TODO(b/25457350): We don't preserve permissions on directories.
+    // TODO: Find all of the leaves and `mkdir -p` them instead?
+    if (CanUseFeature(sc.Features(), kFeatureShell2)) {
+        SilentStandardStreamsCallbackInterface cb;
+        std::string cmd = "mkdir";
+        for (const auto& dir : directory_list) {
+            std::string escaped_path = escape_arg(dir);
+            if (escaped_path.size() > 16384) {
+                // Somewhat arbitrarily limit that probably won't ever happen.
+                sc.Error("path too long: %s", escaped_path.c_str());
+                return false;
+            }
+
+            // The maximum should be 64kiB, but that's not including other stuff that gets tacked
+            // onto the command line, so let's be a bit conservative.
+            if (cmd.size() + escaped_path.size() > 32768) {
+                // Dispatch the command, ignoring failure (since the directory might already exist).
+                send_shell_command(cmd, false, &cb);
+                cmd = "mkdir";
+            }
+            cmd += " ";
+            cmd += escaped_path;
+        }
+
+        if (cmd != "mkdir") {
+            send_shell_command(cmd, false, &cb);
+        }
+    }
+
     if (check_timestamps) {
         for (const copyinfo& ci : file_list) {
             if (!sc.SendLstat(ci.rpath.c_str())) {
diff --git a/adb/client/line_printer.cpp b/adb/client/line_printer.cpp
index 64d10b6..9758526 100644
--- a/adb/client/line_printer.cpp
+++ b/adb/client/line_printer.cpp
@@ -52,7 +52,7 @@
   // MSDN says: "For some systems, [_IOLBF] provides line
   // buffering. However, for Win32, the behavior is the same as _IOFBF
   // - Full Buffering."
-  setvbuf(stdout, NULL, _IONBF, 0);
+  setvbuf(stdout, nullptr, _IONBF, 0);
   console_ = GetStdHandle(STD_OUTPUT_HANDLE);
   CONSOLE_SCREEN_BUFFER_INFO csbi;
   smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 44ed3a2..de6c723 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -89,10 +89,10 @@
     // unbuffer stdout and stderr just like if we were run at the console.
     // This also keeps stderr unbuffered when it is redirected to adb.log.
     if (is_daemon) {
-        if (setvbuf(stdout, NULL, _IONBF, 0) == -1) {
+        if (setvbuf(stdout, nullptr, _IONBF, 0) == -1) {
             fatal("cannot make stdout unbuffered: %s", strerror(errno));
         }
-        if (setvbuf(stderr, NULL, _IONBF, 0) == -1) {
+        if (setvbuf(stderr, nullptr, _IONBF, 0) == -1) {
             fatal("cannot make stderr unbuffered: %s", strerror(errno));
         }
     }
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 46c3f58..10b6090 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -589,7 +589,7 @@
 
     int rc = perform_usb_transfer(h, info, std::move(lock));
     LOG(DEBUG) << "usb_write(" << len << ") = " << rc;
-    return rc;
+    return info->transfer->actual_length;
 }
 
 int usb_read(usb_handle* h, void* d, int len) {
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 1f376a4..869e858 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -128,7 +128,7 @@
     if (!bus_dir) return;
 
     dirent* de;
-    while ((de = readdir(bus_dir.get())) != 0) {
+    while ((de = readdir(bus_dir.get())) != nullptr) {
         if (contains_non_digit(de->d_name)) continue;
 
         std::string bus_name = base + "/" + de->d_name;
@@ -418,11 +418,11 @@
     if (h->zero_mask && !(len & h->zero_mask)) {
         // If we need 0-markers and our transfer is an even multiple of the packet size,
         // then send a zero marker.
-        return usb_bulk_write(h, _data, 0);
+        return usb_bulk_write(h, _data, 0) == 0 ? n : -1;
     }
 
     D("-- usb_write --");
-    return 0;
+    return n;
 }
 
 int usb_read(usb_handle *h, void *_data, int len)
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 8a95a19..49baf36 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -497,8 +497,8 @@
         }
     }
 
-    if (0 == result)
-        return 0;
+    if (!result)
+        return len;
 
     LOG(ERROR) << "usb_write failed with status: " << std::hex << result;
     return -1;
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 9751ebf..e928377 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -75,7 +75,7 @@
 static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
 
 /// List of opened usb handles
-static std::vector<usb_handle*> handle_list;
+static std::vector<usb_handle*>& handle_list = *new std::vector<usb_handle*>();
 
 /// Locker for the list of opened usb handles
 static std::mutex& usb_lock = *new std::mutex();
@@ -126,11 +126,11 @@
 int usb_close(usb_handle* handle);
 
 int known_device_locked(const wchar_t* dev_name) {
-    if (NULL != dev_name) {
+    if (nullptr != dev_name) {
         // Iterate through the list looking for the name match.
         for (usb_handle* usb : handle_list) {
             // In Windows names are not case sensetive!
-            if ((NULL != usb->interface_name) && (0 == wcsicmp(usb->interface_name, dev_name))) {
+            if ((nullptr != usb->interface_name) && (0 == wcsicmp(usb->interface_name, dev_name))) {
                 return 1;
             }
         }
@@ -142,7 +142,7 @@
 int known_device(const wchar_t* dev_name) {
     int ret = 0;
 
-    if (NULL != dev_name) {
+    if (nullptr != dev_name) {
         std::lock_guard<std::mutex> lock(usb_lock);
         ret = known_device_locked(dev_name);
     }
@@ -151,7 +151,7 @@
 }
 
 int register_new_device(usb_handle* handle) {
-    if (NULL == handle) return 0;
+    if (nullptr == handle) return 0;
 
     std::lock_guard<std::mutex> lock(usb_lock);
 
@@ -209,7 +209,7 @@
 
     // Get the HINSTANCE corresponding to the module that _power_window_proc
     // is in (the main module).
-    const HINSTANCE instance = GetModuleHandleW(NULL);
+    const HINSTANCE instance = GetModuleHandleW(nullptr);
     if (!instance) {
         // This is such a common API call that this should never fail.
         fatal("GetModuleHandleW failed: %s",
@@ -228,14 +228,14 @@
     }
 
     if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
-                         L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0, NULL, NULL,
-                         instance, NULL)) {
+                         L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
+                         instance, nullptr)) {
         fatal("CreateWindowExW failed: %s",
               android::base::SystemErrorCodeToString(GetLastError()).c_str());
     }
 
     MSG msg;
-    while (GetMessageW(&msg, NULL, 0, 0)) {
+    while (GetMessageW(&msg, nullptr, 0, 0)) {
         TranslateMessage(&msg);
         DispatchMessageW(&msg);
     }
@@ -259,14 +259,14 @@
 
     // Allocate our handle
     usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
-    if (NULL == ret) {
+    if (nullptr == ret) {
         D("Could not allocate %u bytes for usb_handle: %s", sizeof(usb_handle), strerror(errno));
         goto fail;
     }
 
     // Create interface.
     ret->adb_interface = AdbCreateInterfaceByName(interface_name);
-    if (NULL == ret->adb_interface) {
+    if (nullptr == ret->adb_interface) {
         D("AdbCreateInterfaceByName failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
         goto fail;
@@ -275,7 +275,7 @@
     // Open read pipe (endpoint)
     ret->adb_read_pipe = AdbOpenDefaultBulkReadEndpoint(
         ret->adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite);
-    if (NULL == ret->adb_read_pipe) {
+    if (nullptr == ret->adb_read_pipe) {
         D("AdbOpenDefaultBulkReadEndpoint failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
         goto fail;
@@ -284,7 +284,7 @@
     // Open write pipe (endpoint)
     ret->adb_write_pipe = AdbOpenDefaultBulkWriteEndpoint(
         ret->adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite);
-    if (NULL == ret->adb_write_pipe) {
+    if (nullptr == ret->adb_write_pipe) {
         D("AdbOpenDefaultBulkWriteEndpoint failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
         goto fail;
@@ -292,7 +292,7 @@
 
     // Save interface name
     // First get expected name length
-    AdbGetInterfaceName(ret->adb_interface, NULL, &name_len, false);
+    AdbGetInterfaceName(ret->adb_interface, nullptr, &name_len, false);
     if (0 == name_len) {
         D("AdbGetInterfaceName returned name length of zero: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
@@ -300,7 +300,7 @@
     }
 
     ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0]));
-    if (NULL == ret->interface_name) {
+    if (nullptr == ret->interface_name) {
         D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno));
         goto fail;
     }
@@ -316,12 +316,12 @@
     return ret;
 
 fail:
-    if (NULL != ret) {
+    if (nullptr != ret) {
         usb_cleanup_handle(ret);
         free(ret);
     }
 
-    return NULL;
+    return nullptr;
 }
 
 int usb_write(usb_handle* handle, const void* data, int len) {
@@ -330,7 +330,7 @@
     int err = 0;
 
     D("usb_write %d", len);
-    if (NULL == handle) {
+    if (nullptr == handle) {
         D("usb_write was passed NULL handle");
         err = EINVAL;
         goto fail;
@@ -365,12 +365,12 @@
         }
     }
 
-    return 0;
+    return written;
 
 fail:
     // Any failure should cause us to kick the device instead of leaving it a
     // zombie state with potential to hang.
-    if (NULL != handle) {
+    if (nullptr != handle) {
         D("Kicking device due to error in usb_write");
         usb_kick(handle);
     }
@@ -387,7 +387,7 @@
     int orig_len = len;
 
     D("usb_read %d", len);
-    if (NULL == handle) {
+    if (nullptr == handle) {
         D("usb_read was passed NULL handle");
         err = EINVAL;
         goto fail;
@@ -411,7 +411,7 @@
 fail:
     // Any failure should cause us to kick the device instead of leaving it a
     // zombie state with potential to hang.
-    if (NULL != handle) {
+    if (nullptr != handle) {
         D("Kicking device due to error in usb_read");
         usb_kick(handle);
     }
@@ -431,19 +431,19 @@
 
 void usb_cleanup_handle(usb_handle* handle) {
     D("usb_cleanup_handle");
-    if (NULL != handle) {
-        if (NULL != handle->interface_name) free(handle->interface_name);
+    if (nullptr != handle) {
+        if (nullptr != handle->interface_name) free(handle->interface_name);
         // AdbCloseHandle(pipe) will break any threads out of pending IO calls and
         // wait until the pipe no longer uses the interface. Then we can
         // AdbCloseHandle() the interface.
-        if (NULL != handle->adb_write_pipe) _adb_close_handle(handle->adb_write_pipe);
-        if (NULL != handle->adb_read_pipe) _adb_close_handle(handle->adb_read_pipe);
-        if (NULL != handle->adb_interface) _adb_close_handle(handle->adb_interface);
+        if (nullptr != handle->adb_write_pipe) _adb_close_handle(handle->adb_write_pipe);
+        if (nullptr != handle->adb_read_pipe) _adb_close_handle(handle->adb_read_pipe);
+        if (nullptr != handle->adb_interface) _adb_close_handle(handle->adb_interface);
 
-        handle->interface_name = NULL;
-        handle->adb_write_pipe = NULL;
-        handle->adb_read_pipe = NULL;
-        handle->adb_interface = NULL;
+        handle->interface_name = nullptr;
+        handle->adb_write_pipe = nullptr;
+        handle->adb_read_pipe = nullptr;
+        handle->adb_interface = nullptr;
     }
 }
 
@@ -455,7 +455,7 @@
 
 void usb_kick(usb_handle* handle) {
     D("usb_kick");
-    if (NULL != handle) {
+    if (nullptr != handle) {
         std::lock_guard<std::mutex> lock(usb_lock);
         usb_kick_locked(handle);
     } else {
@@ -466,7 +466,7 @@
 int usb_close(usb_handle* handle) {
     D("usb_close");
 
-    if (NULL != handle) {
+    if (nullptr != handle) {
         // Remove handle from the list
         {
             std::lock_guard<std::mutex> lock(usb_lock);
@@ -487,7 +487,7 @@
 }
 
 int recognized_device(usb_handle* handle) {
-    if (NULL == handle) return 0;
+    if (nullptr == handle) return 0;
 
     // Check vendor and product id first
     USB_DEVICE_DESCRIPTOR device_desc;
@@ -532,7 +532,7 @@
 }
 
 void find_devices() {
-    usb_handle* handle = NULL;
+    usb_handle* handle = nullptr;
     char entry_buffer[2048];
     AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
     unsigned long entry_buffer_size = sizeof(entry_buffer);
@@ -540,7 +540,7 @@
     // Enumerate all present and active interfaces.
     ADBAPIHANDLE enum_handle = AdbEnumInterfaces(usb_class_id, true, true, true);
 
-    if (NULL == enum_handle) {
+    if (nullptr == enum_handle) {
         D("AdbEnumInterfaces failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return;
@@ -551,7 +551,7 @@
         if (!known_device(next_interface->device_name)) {
             // This seems to be a new device. Open it!
             handle = do_usb_open(next_interface->device_name);
-            if (NULL != handle) {
+            if (nullptr != handle) {
                 // Lets see if this interface (device) belongs to us
                 if (recognized_device(handle)) {
                     D("adding a new device %ls", next_interface->device_name);
@@ -569,7 +569,7 @@
                                            true)) {
                         // Lets make sure that we don't duplicate this device
                         if (register_new_device(handle)) {
-                            register_usb_transport(handle, serial_number, NULL, 1);
+                            register_usb_transport(handle, serial_number, nullptr, 1);
                         } else {
                             D("register_new_device failed for %ls", next_interface->device_name);
                             usb_cleanup_handle(handle);
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index f0c3629..180df8f 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -100,7 +100,7 @@
 
 static void usb_disconnected(void* unused, atransport* t) {
     LOG(INFO) << "USB disconnect";
-    usb_transport = NULL;
+    usb_transport = nullptr;
     needs_retry = false;
 }
 
@@ -200,7 +200,7 @@
         return;
     }
 
-    listener_fde = fdevent_create(fd, adbd_auth_listener, NULL);
+    listener_fde = fdevent_create(fd, adbd_auth_listener, nullptr);
     fdevent_add(listener_fde, FDE_READ);
 }
 
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 1128993..0b71363 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -69,17 +69,23 @@
 }
 
 static bool secure_mkdirs(const std::string& path) {
-    uid_t uid = -1;
-    gid_t gid = -1;
-    unsigned int mode = 0775;
-    uint64_t capabilities = 0;
-
     if (path[0] != '/') return false;
 
     std::vector<std::string> path_components = android::base::Split(path, "/");
     std::string partial_path;
     for (const auto& path_component : path_components) {
-        if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
+        uid_t uid = -1;
+        gid_t gid = -1;
+        unsigned int mode = 0775;
+        uint64_t capabilities = 0;
+
+        if (path_component.empty()) {
+            continue;
+        }
+
+        if (partial_path.empty() || partial_path.back() != OS_PATH_SEPARATOR) {
+            partial_path += OS_PATH_SEPARATOR;
+        }
         partial_path += path_component;
 
         if (should_use_fs_config(partial_path)) {
@@ -519,12 +525,11 @@
     return true;
 }
 
-void file_sync_service(int fd, void*) {
+void file_sync_service(android::base::unique_fd fd) {
     std::vector<char> buffer(SYNC_DATA_MAX);
 
-    while (handle_sync_command(fd, buffer)) {
+    while (handle_sync_command(fd.get(), buffer)) {
     }
 
     D("sync: done");
-    adb_close(fd);
 }
diff --git a/adb/daemon/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
index 6c3a225..9a620ab 100644
--- a/adb/daemon/framebuffer_service.cpp
+++ b/adb/daemon/framebuffer_service.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "framebuffer_service.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/fb.h>
@@ -55,8 +57,7 @@
     unsigned int alpha_length;
 } __attribute__((packed));
 
-void framebuffer_service(int fd, void *cookie)
-{
+void framebuffer_service(android::base::unique_fd fd) {
     struct fbinfo fbinfo;
     unsigned int i, bsize;
     char buf[640];
@@ -65,7 +66,7 @@
     int fds[2];
     pid_t pid;
 
-    if (pipe2(fds, O_CLOEXEC) < 0) goto pipefail;
+    if (pipe2(fds, O_CLOEXEC) < 0) return;
 
     pid = fork();
     if (pid < 0) goto done;
@@ -75,7 +76,7 @@
         adb_close(fds[0]);
         adb_close(fds[1]);
         const char* command = "screencap";
-        const char *args[2] = {command, NULL};
+        const char *args[2] = {command, nullptr};
         execvp(command, (char**)args);
         exit(1);
     }
@@ -168,7 +169,7 @@
     }
 
     /* write header */
-    if(!WriteFdExactly(fd, &fbinfo, sizeof(fbinfo))) goto done;
+    if (!WriteFdExactly(fd.get(), &fbinfo, sizeof(fbinfo))) goto done;
 
     /* write data */
     for(i = 0; i < fbinfo.size; i += bsize) {
@@ -176,13 +177,11 @@
       if (i + bsize > fbinfo.size)
         bsize = fbinfo.size - i;
       if(!ReadFdExactly(fd_screencap, buf, bsize)) goto done;
-      if(!WriteFdExactly(fd, buf, bsize)) goto done;
+      if (!WriteFdExactly(fd.get(), buf, bsize)) goto done;
     }
 
 done:
     adb_close(fds[0]);
 
-    TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
-pipefail:
-    adb_close(fd);
+    TEMP_FAILURE_RETRY(waitpid(pid, nullptr, 0));
 }
diff --git a/libbacktrace/thread_utils.c b/adb/daemon/framebuffer_service.h
similarity index 65%
rename from libbacktrace/thread_utils.c
rename to adb/daemon/framebuffer_service.h
index e75f56e..d99c6fe 100644
--- a/libbacktrace/thread_utils.c
+++ b/adb/daemon/framebuffer_service.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,11 @@
  * limitations under the License.
  */
 
-#include "thread_utils.h"
+#ifndef _DAEMON_FRAMEBUFFER_SERVICE_H_
+#define _DAEMON_FRAMEBUFFER_SERVICE_H_
 
-#if !defined(__BIONIC__)
+#include <android-base/unique_fd.h>
 
-// glibc doesn't implement or export tgkill.
-#include <unistd.h>
-#include <sys/syscall.h>
+void framebuffer_service(android::base::unique_fd fd);
 
-int tgkill(int tgid, int tid, int sig) {
-  return syscall(__NR_tgkill, tgid, tid, sig);
-}
-
-#endif
+#endif  // _DAEMON_FRAMEBUFFER_SERVICE_H_
diff --git a/adb/daemon/usb.h b/adb/daemon/include/adbd/usb.h
similarity index 92%
rename from adb/daemon/usb.h
rename to adb/daemon/include/adbd/usb.h
index 15a7f65..7905d9d 100644
--- a/adb/daemon/usb.h
+++ b/adb/daemon/include/adbd/usb.h
@@ -19,6 +19,7 @@
 #include <atomic>
 #include <condition_variable>
 #include <mutex>
+#include <vector>
 
 #include <asyncio/AsyncIO.h>
 
@@ -54,5 +55,9 @@
     // read and write threads.
     struct aio_block read_aiob;
     struct aio_block write_aiob;
+
+    bool reads_zero_packets;
+    size_t io_size;
 };
 
+usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size);
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 367695d..175e82e 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -264,7 +264,7 @@
 
             iov.iov_base = &dummy;
             iov.iov_len = 1;
-            msg.msg_name = NULL;
+            msg.msg_name = nullptr;
             msg.msg_namelen = 0;
             msg.msg_iov = &iov;
             msg.msg_iovlen = 1;
@@ -393,7 +393,7 @@
     control->listen_socket = s;
 
     control->fde = fdevent_create(s, jdwp_control_event, control);
-    if (control->fde == NULL) {
+    if (control->fde == nullptr) {
         D("could not create fdevent for jdwp control socket");
         adb_close(s);
         return -1;
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 232d9c5..c02cafa 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -38,7 +38,6 @@
 #include <scoped_minijail.h>
 
 #include <private/android_filesystem_config.h>
-#include "debuggerd/handler.h"
 #include "selinux/android.h"
 
 #include "adb.h"
@@ -274,7 +273,6 @@
 
     close_stdin();
 
-    debuggerd_init(nullptr);
     adb_trace_init(argv);
 
     D("Handling main()");
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index eb46903..dfdc365 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -26,13 +26,17 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mount.h>
+#include <sys/statvfs.h>
 #include <sys/vfs.h>
 #include <unistd.h>
 
+#include <set>
 #include <string>
 #include <vector>
 
 #include <android-base/properties.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
 #include <ext4_utils/ext4_utils.h>
 
 #include "adb.h"
@@ -40,6 +44,7 @@
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
 #include "fs_mgr.h"
+#include "set_verity_enable_state_service.h"
 
 // Returns the device used to mount a directory in /proc/mounts.
 static std::string find_proc_mount(const char* dir) {
@@ -142,7 +147,18 @@
     return true;
 }
 
-static bool remount_partition(int fd, const char* dir, std::vector<std::string>& dedup) {
+static unsigned long get_mount_flags(int fd, const char* dir) {
+    struct statvfs st_vfs;
+    if (statvfs(dir, &st_vfs) == -1) {
+        // Even though we could not get the original mount flags, assume that
+        // the mount was originally read-only.
+        WriteFdFmt(fd, "statvfs of the %s mount failed: %s.\n", dir, strerror(errno));
+        return MS_RDONLY;
+    }
+    return st_vfs.f_flag;
+}
+
+static bool remount_partition(int fd, const char* dir) {
     if (!directory_exists(dir)) {
         return true;
     }
@@ -160,78 +176,140 @@
                    dir, dev.c_str(), strerror(errno));
         return false;
     }
-    if (mount(dev.c_str(), dir, "none", MS_REMOUNT | MS_BIND, nullptr) == -1) {
+
+    unsigned long remount_flags = get_mount_flags(fd, dir);
+    if ((remount_flags & MS_RDONLY) == 0) {
+        // Mount is already writable.
+        return true;
+    }
+    remount_flags &= ~MS_RDONLY;
+    remount_flags |= MS_REMOUNT;
+
+    if (mount(dev.c_str(), dir, "none", remount_flags | MS_BIND, nullptr) == -1) {
         // This is useful for cases where the superblock is already marked as
         // read-write, but the mount itself is read-only, such as containers
         // where the remount with just MS_REMOUNT is forbidden by the kernel.
         WriteFdFmt(fd, "remount of the %s mount failed: %s.\n", dir, strerror(errno));
         return false;
     }
-    if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
-        if (errno == EROFS && fs_has_shared_blocks(dev.c_str())) {
-            if (!can_unshare_blocks(fd, dev.c_str())) {
-                return false;
-            }
-            // We return true so remount_service() can detect that the only
-            // failure was deduplicated filesystems.
-            dedup.push_back(dev);
-            return true;
-        }
+    if (mount(dev.c_str(), dir, "none", remount_flags, nullptr) == -1) {
         WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno));
         return false;
     }
     return true;
 }
 
-void remount_service(int fd, void* cookie) {
+static void reboot_for_remount(int fd, bool need_fsck) {
+    std::string reboot_cmd = "reboot";
+    if (need_fsck) {
+        const std::vector<std::string> options = {"--fsck_unshare_blocks"};
+        std::string err;
+        if (!write_bootloader_message(options, &err)) {
+            WriteFdFmt(fd, "Failed to set bootloader message: %s\n", err.c_str());
+            return;
+        }
+
+        WriteFdExactly(fd,
+                       "The device will now reboot to recovery and attempt "
+                       "un-deduplication.\n");
+        reboot_cmd = "reboot,recovery";
+    }
+
+    sync();
+    android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd.c_str());
+}
+
+void remount_service(android::base::unique_fd fd, const std::string& cmd) {
+    bool user_requested_reboot = cmd != "-R";
+
     if (getuid() != 0) {
-        WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
-        adb_close(fd);
+        WriteFdExactly(fd.get(), "Not running as root. Try \"adb root\" first.\n");
         return;
     }
 
     bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
     bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
 
-    if (system_verified || vendor_verified) {
-        // Allow remount but warn of likely bad effects
-        bool both = system_verified && vendor_verified;
-        WriteFdFmt(fd,
-                   "dm_verity is enabled on the %s%s%s partition%s.\n",
-                   system_verified ? "system" : "",
-                   both ? " and " : "",
-                   vendor_verified ? "vendor" : "",
-                   both ? "s" : "");
-        WriteFdExactly(fd,
-                       "Use \"adb disable-verity\" to disable verity.\n"
-                       "If you do not, remount may succeed, however, you will still "
-                       "not be able to write to these volumes.\n");
-    }
-
-    bool success = true;
-    std::vector<std::string> dedup;
+    std::vector<std::string> partitions = {"/odm", "/oem", "/product", "/vendor"};
     if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
-        success &= remount_partition(fd, "/", dedup);
+        partitions.push_back("/");
     } else {
-        success &= remount_partition(fd, "/system", dedup);
+        partitions.push_back("/system");
     }
-    success &= remount_partition(fd, "/odm", dedup);
-    success &= remount_partition(fd, "/oem", dedup);
-    success &= remount_partition(fd, "/product", dedup);
-    success &= remount_partition(fd, "/vendor", dedup);
 
-    if (!success) {
-        WriteFdExactly(fd, "remount failed\n");
-    } else if (dedup.empty()) {
-        WriteFdExactly(fd, "remount succeeded\n");
-    } else {
-        WriteFdExactly(fd,
-                       "The following partitions are deduplicated and could "
-                       "not be remounted:\n");
-        for (const std::string& name : dedup) {
-            WriteFdFmt(fd, "  %s\n", name.c_str());
+    // Find partitions that are deduplicated, and can be un-deduplicated.
+    std::set<std::string> dedup;
+    for (const auto& partition : partitions) {
+        std::string dev = find_mount(partition.c_str(), partition == "/");
+        if (dev.empty() || !fs_has_shared_blocks(dev.c_str())) {
+            continue;
+        }
+        if (can_unshare_blocks(fd.get(), dev.c_str())) {
+            dedup.emplace(partition);
         }
     }
 
-    adb_close(fd);
+    bool verity_enabled = (system_verified || vendor_verified);
+
+    // Reboot now if the user requested it (and an operation needs a reboot).
+    if (user_requested_reboot) {
+        if (!dedup.empty() || verity_enabled) {
+            if (verity_enabled) {
+                set_verity_enabled_state_service(android::base::unique_fd(dup(fd.get())), false);
+            }
+            reboot_for_remount(fd.get(), !dedup.empty());
+            return;
+        }
+        WriteFdExactly(fd.get(), "No reboot needed, skipping -R.\n");
+    }
+
+    // If we need to disable-verity, but we also need to perform a recovery
+    // fsck for deduplicated partitions, hold off on warning about verity. We
+    // can handle both verity and the recovery fsck in the same reboot cycle.
+    if (verity_enabled && dedup.empty()) {
+        // Allow remount but warn of likely bad effects
+        bool both = system_verified && vendor_verified;
+        WriteFdFmt(fd.get(), "dm_verity is enabled on the %s%s%s partition%s.\n",
+                   system_verified ? "system" : "", both ? " and " : "",
+                   vendor_verified ? "vendor" : "", both ? "s" : "");
+        WriteFdExactly(fd.get(),
+                       "Use \"adb disable-verity\" to disable verity.\n"
+                       "If you do not, remount may succeed, however, you will still "
+                       "not be able to write to these volumes.\n");
+        WriteFdExactly(fd.get(),
+                       "Alternately, use \"adb remount -R\" to disable verity "
+                       "and automatically reboot.\n");
+    }
+
+    bool success = true;
+    for (const auto& partition : partitions) {
+        // Don't try to remount partitions that need an fsck in recovery.
+        if (dedup.count(partition)) {
+            continue;
+        }
+        success &= remount_partition(fd.get(), partition.c_str());
+    }
+
+    if (!dedup.empty()) {
+        WriteFdExactly(fd.get(),
+                       "The following partitions are deduplicated and cannot "
+                       "yet be remounted:\n");
+        for (const std::string& name : dedup) {
+            WriteFdFmt(fd.get(), "  %s\n", name.c_str());
+        }
+
+        WriteFdExactly(fd.get(),
+                       "To reboot and un-deduplicate the listed partitions, "
+                       "please retry with adb remount -R.\n");
+        if (system_verified || vendor_verified) {
+            WriteFdExactly(fd.get(), "Note: verity will be automatically disabled after reboot.\n");
+        }
+        return;
+    }
+
+    if (!success) {
+        WriteFdExactly(fd.get(), "remount failed\n");
+    } else {
+        WriteFdExactly(fd.get(), "remount succeeded\n");
+    }
 }
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 0fcf89b..0f804e9 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -16,6 +16,7 @@
 
 #define TRACE_TAG ADB
 
+#include "set_verity_enable_state_service.h"
 #include "sysdeps.h"
 
 #include <fcntl.h>
@@ -25,8 +26,8 @@
 #include <stdio.h>
 #include <sys/stat.h>
 
-#include "android-base/properties.h"
-#include "android-base/stringprintf.h"
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <log/log_properties.h>
 
 #include "adb.h"
@@ -131,12 +132,9 @@
     return true;
 }
 
-void set_verity_enabled_state_service(int fd, void* cookie) {
-    unique_fd closer(fd);
+void set_verity_enabled_state_service(android::base::unique_fd fd, bool enable) {
     bool any_changed = false;
 
-    bool enable = (cookie != NULL);
-
     // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
     // contract, androidboot.vbmeta.digest is set by the bootloader
     // when using AVB).
@@ -147,12 +145,12 @@
     // VB1.0 dm-verity is only enabled on certain builds.
     if (!using_avb) {
         if (!kAllowDisableVerity) {
-            WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
+            WriteFdFmt(fd.get(), "%s-verity only works for userdebug builds\n",
                        enable ? "enable" : "disable");
         }
 
         if (!android::base::GetBoolProperty("ro.secure", false)) {
-            WriteFdFmt(fd, "verity not enabled - ENG build\n");
+            WriteFdFmt(fd.get(), "verity not enabled - ENG build\n");
             return;
         }
     }
@@ -160,7 +158,7 @@
     // Should never be possible to disable dm-verity on a USER build
     // regardless of using AVB or VB1.0.
     if (!__android_log_is_debuggable()) {
-        WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n");
+        WriteFdFmt(fd.get(), "verity cannot be disabled/enabled - USER build\n");
         return;
     }
 
@@ -168,10 +166,10 @@
         // Yep, the system is using AVB.
         AvbOps* ops = avb_ops_user_new();
         if (ops == nullptr) {
-            WriteFdFmt(fd, "Error getting AVB ops\n");
+            WriteFdFmt(fd.get(), "Error getting AVB ops\n");
             return;
         }
-        if (set_avb_verity_enabled_state(fd, ops, enable)) {
+        if (set_avb_verity_enabled_state(fd.get(), ops, enable)) {
             any_changed = true;
         }
         avb_ops_user_free(ops);
@@ -181,14 +179,14 @@
         // read all fstab entries at once from all sources
         fstab = fs_mgr_read_fstab_default();
         if (!fstab) {
-            WriteFdFmt(fd, "Failed to read fstab\nMaybe run adb root?\n");
+            WriteFdFmt(fd.get(), "Failed to read fstab\nMaybe run adb root?\n");
             return;
         }
 
         // Loop through entries looking for ones that vold manages.
         for (int i = 0; i < fstab->num_entries; i++) {
             if (fs_mgr_is_verified(&fstab->recs[i])) {
-                if (set_verity_enabled_state(fd, fstab->recs[i].blk_device,
+                if (set_verity_enabled_state(fd.get(), fstab->recs[i].blk_device,
                                              fstab->recs[i].mount_point, enable)) {
                     any_changed = true;
                 }
@@ -197,6 +195,6 @@
     }
 
     if (any_changed) {
-        WriteFdFmt(fd, "Now reboot your device for settings to take effect\n");
+        WriteFdFmt(fd.get(), "Now reboot your device for settings to take effect\n");
     }
 }
diff --git a/libbacktrace/thread_utils.h b/adb/daemon/set_verity_enable_state_service.h
similarity index 62%
rename from libbacktrace/thread_utils.h
rename to adb/daemon/set_verity_enable_state_service.h
index 9590657..9f84f35 100644
--- a/libbacktrace/thread_utils.h
+++ b/adb/daemon/set_verity_enable_state_service.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,19 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef _LIBBACKTRACE_THREAD_UTILS_H
-#define _LIBBACKTRACE_THREAD_UTILS_H
+#ifndef _DAEMON_SET_VERITY_ENABLED_STATE_SERVICE_H_
+#define _DAEMON_SET_VERITY_ENABLED_STATE_SERVICE_H_
 
-#include <unistd.h>
+#include <android-base/unique_fd.h>
 
-#if !defined(__ANDROID__)
-#include <cutils/threads.h>
-#endif
+void set_verity_enabled_state_service(android::base::unique_fd fd, bool enable);
 
-__BEGIN_DECLS
-
-int tgkill(int tgid, int tid, int sig);
-
-__END_DECLS
-
-#endif /* _LIBBACKTRACE_THREAD_UTILS_H */
+#endif  // _DAEMON_SET_VERITY_ENABLED_STATE_SERVICE_H_
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 401c99c..56d647a 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -98,6 +98,7 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <private/android_logger.h>
+#include <selinux/android.h>
 
 #include "adb.h"
 #include "adb_io.h"
@@ -343,6 +344,24 @@
                 adb_write(oom_score_adj_fd, oom_score_adj_value, strlen(oom_score_adj_value)));
         }
 
+#ifdef __ANDROID_RECOVERY__
+        // Special routine for recovery. Switch to shell domain when adbd is
+        // is running with dropped privileged (i.e. not running as root) and
+        // is built for the recovery mode. This is required because recovery
+        // rootfs is not labeled and everything is labeled just as rootfs.
+        char* con = nullptr;
+        if (getcon(&con) == 0) {
+            if (!strcmp(con, "u:r:adbd:s0")) {
+                if (selinux_android_setcon("u:r:shell:s0") < 0) {
+                    LOG(FATAL) << "Could not set SELinux context for subprocess";
+                }
+            }
+            freecon(con);
+        } else {
+            LOG(FATAL) << "Failed to get SELinux context";
+        }
+#endif
+
         if (command_.empty()) {
             // Spawn a login shell if we don't have a command.
             execle(_PATH_BSHELL, "-" _PATH_BSHELL, nullptr, cenv.data());
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index c724b11..c79d6fc 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -41,7 +41,7 @@
 #include <android-base/properties.h>
 
 #include "adb.h"
-#include "daemon/usb.h"
+#include "adbd/usb.h"
 #include "transport.h"
 
 using namespace std::chrono_literals;
@@ -53,7 +53,7 @@
 #define USB_FFS_BULK_SIZE 16384
 
 // Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
-#define USB_FFS_NUM_BUFS ((MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
+#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
 
 #define cpu_to_le16(x) htole16(x)
 #define cpu_to_le32(x) htole32(x)
@@ -226,16 +226,16 @@
     },
 };
 
-static void aio_block_init(aio_block* aiob) {
-    aiob->iocb.resize(USB_FFS_NUM_BUFS);
-    aiob->iocbs.resize(USB_FFS_NUM_BUFS);
-    aiob->events.resize(USB_FFS_NUM_BUFS);
+static void aio_block_init(aio_block* aiob, unsigned num_bufs) {
+    aiob->iocb.resize(num_bufs);
+    aiob->iocbs.resize(num_bufs);
+    aiob->events.resize(num_bufs);
     aiob->num_submitted = 0;
-    for (unsigned i = 0; i < USB_FFS_NUM_BUFS; i++) {
+    for (unsigned i = 0; i < num_bufs; i++) {
         aiob->iocbs[i] = &aiob->iocb[i];
     }
     memset(&aiob->ctx, 0, sizeof(aiob->ctx));
-    if (io_setup(USB_FFS_NUM_BUFS, &aiob->ctx)) {
+    if (io_setup(num_bufs, &aiob->ctx)) {
         D("[ aio: got error on io_setup (%d) ]", errno);
     }
 }
@@ -250,7 +250,7 @@
     }
 }
 
-bool init_functionfs(struct usb_handle* h) {
+static bool init_functionfs(struct usb_handle* h) {
     LOG(INFO) << "initializing functionfs";
 
     ssize_t ret;
@@ -318,6 +318,7 @@
 
     h->read_aiob.fd = h->bulk_out;
     h->write_aiob.fd = h->bulk_in;
+    h->reads_zero_packets = true;
     return true;
 
 err:
@@ -336,9 +337,7 @@
     return false;
 }
 
-static void usb_ffs_open_thread(void* x) {
-    struct usb_handle* usb = (struct usb_handle*)x;
-
+static void usb_ffs_open_thread(usb_handle *usb) {
     adb_thread_setname("usb ffs open");
 
     while (true) {
@@ -359,7 +358,7 @@
         }
 
         LOG(INFO) << "registering usb transport";
-        register_usb_transport(usb, 0, 0, 1);
+        register_usb_transport(usb, nullptr, nullptr, 1);
     }
 
     // never gets here
@@ -370,6 +369,7 @@
     D("about to write (fd=%d, len=%d)", h->bulk_in, len);
 
     const char* buf = static_cast<const char*>(data);
+    int orig_len = len;
     while (len > 0) {
         int write_len = std::min(USB_FFS_BULK_SIZE, len);
         int n = adb_write(h->bulk_in, buf, write_len);
@@ -382,13 +382,14 @@
     }
 
     D("[ done fd=%d ]", h->bulk_in);
-    return 0;
+    return orig_len;
 }
 
 static int usb_ffs_read(usb_handle* h, void* data, int len) {
     D("about to read (fd=%d, len=%d)", h->bulk_out, len);
 
     char* buf = static_cast<char*>(data);
+    int orig_len = len;
     while (len > 0) {
         int read_len = std::min(USB_FFS_BULK_SIZE, len);
         int n = adb_read(h->bulk_out, buf, read_len);
@@ -401,14 +402,14 @@
     }
 
     D("[ done fd=%d ]", h->bulk_out);
-    return 0;
+    return orig_len;
 }
 
 static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
     aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
     bool zero_packet = false;
 
-    int num_bufs = len / USB_FFS_BULK_SIZE + (len % USB_FFS_BULK_SIZE == 0 ? 0 : 1);
+    int num_bufs = len / h->io_size + (len % h->io_size == 0 ? 0 : 1);
     const char* cur_data = reinterpret_cast<const char*>(data);
     int packet_size = getMaxPacketSize(aiob->fd);
 
@@ -418,7 +419,7 @@
     }
 
     for (int i = 0; i < num_bufs; i++) {
-        int buf_len = std::min(len, USB_FFS_BULK_SIZE);
+        int buf_len = std::min(len, static_cast<int>(h->io_size));
         io_prep(&aiob->iocb[i], aiob->fd, cur_data, buf_len, 0, read);
 
         len -= buf_len;
@@ -427,7 +428,7 @@
         if (len == 0 && buf_len % packet_size == 0 && read) {
             // adb does not expect the device to send a zero packet after data transfer,
             // but the host *does* send a zero packet for the device to read.
-            zero_packet = true;
+            zero_packet = h->reads_zero_packets;
         }
     }
     if (zero_packet) {
@@ -449,6 +450,7 @@
         if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
             continue;
         }
+        int ret = 0;
         for (int i = 0; i < num_bufs; i++) {
             if (aiob->events[i].res < 0) {
                 errno = -aiob->events[i].res;
@@ -456,8 +458,9 @@
                             << " total bufs " << num_bufs;
                 return -1;
             }
+            ret += aiob->events[i].res;
         }
-        return 0;
+        return ret;
     }
 }
 
@@ -505,9 +508,7 @@
     h->notify.notify_one();
 }
 
-static void usb_ffs_init() {
-    D("[ usb_init - using FunctionFS ]");
-
+usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size) {
     usb_handle* h = new usb_handle();
 
     if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
@@ -518,20 +519,21 @@
     } else {
         h->write = usb_ffs_aio_write;
         h->read = usb_ffs_aio_read;
-        aio_block_init(&h->read_aiob);
-        aio_block_init(&h->write_aiob);
+        aio_block_init(&h->read_aiob, num_bufs);
+        aio_block_init(&h->write_aiob, num_bufs);
     }
+    h->io_size = io_size;
     h->kick = usb_ffs_kick;
     h->close = usb_ffs_close;
-
-    D("[ usb_init - starting thread ]");
-    std::thread(usb_ffs_open_thread, h).detach();
+    return h;
 }
 
 void usb_init() {
+    D("[ usb_init - using FunctionFS ]");
     dummy_fd = adb_open("/dev/null", O_WRONLY);
     CHECK_NE(dummy_fd, -1);
-    usb_ffs_init();
+
+    std::thread(usb_ffs_open_thread, create_usb_handle(USB_FFS_NUM_BUFS, USB_FFS_BULK_SIZE)).detach();
 }
 
 int usb_write(usb_handle* h, const void* data, int len) {
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 1b7758c..dee87bd 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -21,6 +21,7 @@
 #include "fdevent.h"
 
 #include <fcntl.h>
+#include <inttypes.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -75,6 +76,8 @@
 static bool main_thread_valid;
 static uint64_t main_thread_id;
 
+static uint64_t fdevent_id;
+
 static bool run_needs_flush = false;
 static auto& run_queue_notify_fd = *new unique_fd();
 static auto& run_queue_mutex = *new std::mutex();
@@ -111,7 +114,8 @@
     if (fde->state & FDE_ERROR) {
         state += "E";
     }
-    return android::base::StringPrintf("(fdevent %d %s)", fde->fd.get(), state.c_str());
+    return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
+                                       state.c_str());
 }
 
 void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
@@ -125,6 +129,7 @@
     CHECK_GE(fd, 0);
 
     fdevent* fde = new fdevent();
+    fde->id = fdevent_id++;
     fde->state = FDE_ACTIVE;
     fde->fd.reset(fd);
     fde->func = func;
@@ -144,7 +149,7 @@
 
 void fdevent_destroy(fdevent* fde) {
     check_main_thread();
-    if (fde == 0) return;
+    if (fde == nullptr) return;
     if (!(fde->state & FDE_CREATED)) {
         LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
     }
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 69c4072..d501b86 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -32,8 +32,7 @@
 typedef void (*fd_func)(int fd, unsigned events, void *userdata);
 
 struct fdevent {
-    fdevent* next = nullptr;
-    fdevent* prev = nullptr;
+    uint64_t id;
 
     unique_fd fd;
     int force_eof = 0;
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 6606efd..608b3ad 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -20,6 +20,8 @@
 #include <string>
 #include <vector>
 
+#include <android-base/unique_fd.h>
+
 #define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
 
 #define ID_LSTAT_V1 MKID('S','T','A','T')
@@ -79,7 +81,7 @@
     } status;
 };
 
-void file_sync_service(int fd, void* cookie);
+void file_sync_service(android::base::unique_fd fd);
 bool do_sync_ls(const char* path);
 bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
 bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
diff --git a/adb/remount_service.h b/adb/remount_service.h
index 7bda1be..45821ee 100644
--- a/adb/remount_service.h
+++ b/adb/remount_service.h
@@ -19,7 +19,9 @@
 
 #include <string>
 
+#include <android-base/unique_fd.h>
+
 bool make_block_device_writable(const std::string&);
-void remount_service(int, void*);
+void remount_service(android::base::unique_fd, const std::string&);
 
 #endif
diff --git a/adb/services.cpp b/adb/services.cpp
index 0b0c161..3d418cb 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -37,6 +37,7 @@
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 
 #if !ADB_HOST
@@ -49,6 +50,10 @@
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_utils.h"
+#if !ADB_HOST
+#include "daemon/framebuffer_service.h"
+#include "daemon/set_verity_enable_state_service.h"
+#endif
 #include "file_sync_service.h"
 #include "remount_service.h"
 #include "services.h"
@@ -57,81 +62,67 @@
 #include "sysdeps.h"
 #include "transport.h"
 
-struct stinfo {
-    const char* service_name;
-    void (*func)(int fd, void *cookie);
-    int fd;
-    void *cookie;
-};
+namespace {
 
-static void service_bootstrap_func(void* x) {
-    stinfo* sti = reinterpret_cast<stinfo*>(x);
-    adb_thread_setname(android::base::StringPrintf("%s svc %d", sti->service_name, sti->fd));
-    sti->func(sti->fd, sti->cookie);
-    free(sti);
+void service_bootstrap_func(std::string service_name,
+                            std::function<void(android::base::unique_fd)> func,
+                            android::base::unique_fd fd) {
+    adb_thread_setname(android::base::StringPrintf("%s svc %d", service_name.c_str(), fd.get()));
+    func(std::move(fd));
 }
 
 #if !ADB_HOST
 
-void restart_root_service(int fd, void *cookie) {
+void restart_root_service(android::base::unique_fd fd) {
     if (getuid() == 0) {
-        WriteFdExactly(fd, "adbd is already running as root\n");
-        adb_close(fd);
-    } else {
-        if (!__android_log_is_debuggable()) {
-            WriteFdExactly(fd, "adbd cannot run as root in production builds\n");
-            adb_close(fd);
-            return;
-        }
-
-        android::base::SetProperty("service.adb.root", "1");
-        WriteFdExactly(fd, "restarting adbd as root\n");
-        adb_close(fd);
+        WriteFdExactly(fd.get(), "adbd is already running as root\n");
+        return;
     }
+    if (!__android_log_is_debuggable()) {
+        WriteFdExactly(fd.get(), "adbd cannot run as root in production builds\n");
+        return;
+    }
+
+    android::base::SetProperty("service.adb.root", "1");
+    WriteFdExactly(fd.get(), "restarting adbd as root\n");
 }
 
-void restart_unroot_service(int fd, void *cookie) {
+void restart_unroot_service(android::base::unique_fd fd) {
     if (getuid() != 0) {
-        WriteFdExactly(fd, "adbd not running as root\n");
-        adb_close(fd);
-    } else {
-        android::base::SetProperty("service.adb.root", "0");
-        WriteFdExactly(fd, "restarting adbd as non root\n");
-        adb_close(fd);
+        WriteFdExactly(fd.get(), "adbd not running as root\n");
+        return;
     }
+    android::base::SetProperty("service.adb.root", "0");
+    WriteFdExactly(fd.get(), "restarting adbd as non root\n");
 }
 
-void restart_tcp_service(int fd, void *cookie) {
-    int port = (int) (uintptr_t) cookie;
+void restart_tcp_service(android::base::unique_fd fd, int port) {
     if (port <= 0) {
-        WriteFdFmt(fd, "invalid port %d\n", port);
-        adb_close(fd);
+        WriteFdFmt(fd.get(), "invalid port %d\n", port);
         return;
     }
 
     android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
-    WriteFdFmt(fd, "restarting in TCP mode port: %d\n", port);
-    adb_close(fd);
+    WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
 }
 
-void restart_usb_service(int fd, void *cookie) {
+void restart_usb_service(android::base::unique_fd fd) {
     android::base::SetProperty("service.adb.tcp.port", "0");
-    WriteFdExactly(fd, "restarting in USB mode\n");
-    adb_close(fd);
+    WriteFdExactly(fd.get(), "restarting in USB mode\n");
 }
 
-static bool reboot_service_impl(int fd, const char* arg) {
-    const char* reboot_arg = arg;
+bool reboot_service_impl(android::base::unique_fd fd, const std::string& arg) {
+    std::string reboot_arg = arg;
     bool auto_reboot = false;
 
-    if (strcmp(reboot_arg, "sideload-auto-reboot") == 0) {
+    if (reboot_arg == "sideload-auto-reboot") {
         auto_reboot = true;
         reboot_arg = "sideload";
     }
 
     // It reboots into sideload mode by setting "--sideload" or "--sideload_auto_reboot"
     // in the command file.
-    if (strcmp(reboot_arg, "sideload") == 0) {
+    if (reboot_arg == "sideload") {
         if (getuid() != 0) {
             WriteFdExactly(fd, "'adb root' is required for 'adb reboot sideload'.\n");
             return false;
@@ -151,8 +142,8 @@
 
     sync();
 
-    if (!reboot_arg || !reboot_arg[0]) reboot_arg = "adb";
-    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg);
+    if (reboot_arg.empty()) reboot_arg = "adb";
+    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
     if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
         WriteFdFmt(fd, "reboot (%s) failed\n", reboot_string.c_str());
         return false;
@@ -161,23 +152,19 @@
     return true;
 }
 
-void reboot_service(int fd, void* arg) {
-    if (reboot_service_impl(fd, static_cast<const char*>(arg))) {
-        // Don't return early. Give the reboot command time to take effect
-        // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
-        while (true) {
-            pause();
-        }
+void reboot_service(android::base::unique_fd fd, const std::string& arg) {
+    if (!reboot_service_impl(std::move(fd), arg)) {
+        return;
     }
-
-    free(arg);
-    adb_close(fd);
+    // Don't return early. Give the reboot command time to take effect
+    // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
+    while (true) {
+        pause();
+    }
 }
 
-static void reconnect_service(int fd, void* arg) {
+void reconnect_service(android::base::unique_fd fd, atransport* t) {
     WriteFdExactly(fd, "done");
-    adb_close(fd);
-    atransport* t = static_cast<atransport*>(arg);
     kick_transport(t);
 }
 
@@ -197,7 +184,7 @@
 
 // Shell service string can look like:
 //   shell[,arg1,arg2,...]:[command]
-static int ShellService(const std::string& args, const atransport* transport) {
+int ShellService(const std::string& args, const atransport* transport) {
     size_t delimiter_index = args.find(':');
     if (delimiter_index == std::string::npos) {
         LOG(ERROR) << "No ':' found in shell service arguments: " << args;
@@ -236,16 +223,17 @@
 
 #endif  // !ADB_HOST
 
-static int create_service_thread(const char* service_name, void (*func)(int, void*), void* cookie) {
+android::base::unique_fd create_service_thread(const char* service_name,
+                                               std::function<void(android::base::unique_fd)> func) {
     int s[2];
     if (adb_socketpair(s)) {
         printf("cannot create service socket pair\n");
-        return -1;
+        return android::base::unique_fd();
     }
     D("socketpair: (%d,%d)", s[0], s[1]);
 
 #if !ADB_HOST
-    if (func == &file_sync_service) {
+    if (strcmp(service_name, "sync") == 0) {
         // Set file sync service socket to maximum size
         int max_buf = LINUX_MAX_SOCKET_SIZE;
         adb_setsockopt(s[0], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
@@ -253,21 +241,14 @@
     }
 #endif // !ADB_HOST
 
-    stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
-    if (sti == nullptr) {
-        fatal("cannot allocate stinfo");
-    }
-    sti->service_name = service_name;
-    sti->func = func;
-    sti->cookie = cookie;
-    sti->fd = s[1];
-
-    std::thread(service_bootstrap_func, sti).detach();
+    std::thread(service_bootstrap_func, service_name, func, android::base::unique_fd(s[1])).detach();
 
     D("service thread started, %d:%d",s[0], s[1]);
-    return s[0];
+    return android::base::unique_fd(s[0]);
 }
 
+}  // namespace
+
 int service_to_fd(const char* name, atransport* transport) {
     int ret = -1;
 
@@ -280,52 +261,60 @@
 #if !ADB_HOST
     } else if(!strncmp("dev:", name, 4)) {
         ret = unix_open(name + 4, O_RDWR | O_CLOEXEC);
-    } else if(!strncmp(name, "framebuffer:", 12)) {
-        ret = create_service_thread("fb", framebuffer_service, nullptr);
+    } else if (!strncmp(name, "framebuffer:", 12)) {
+        ret = create_service_thread("fb", framebuffer_service).release();
     } else if (!strncmp(name, "jdwp:", 5)) {
-        ret = create_jdwp_connection_fd(atoi(name+5));
-    } else if(!strncmp(name, "shell", 5)) {
+        ret = create_jdwp_connection_fd(atoi(name + 5));
+    } else if (!strncmp(name, "shell", 5)) {
         ret = ShellService(name + 5, transport);
-    } else if(!strncmp(name, "exec:", 5)) {
+    } else if (!strncmp(name, "exec:", 5)) {
         ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if(!strncmp(name, "sync:", 5)) {
-        ret = create_service_thread("sync", file_sync_service, nullptr);
-    } else if(!strncmp(name, "remount:", 8)) {
-        ret = create_service_thread("remount", remount_service, nullptr);
-    } else if(!strncmp(name, "reboot:", 7)) {
-        void* arg = strdup(name + 7);
-        if (arg == NULL) return -1;
-        ret = create_service_thread("reboot", reboot_service, arg);
-        if (ret < 0) free(arg);
-    } else if(!strncmp(name, "root:", 5)) {
-        ret = create_service_thread("root", restart_root_service, nullptr);
-    } else if(!strncmp(name, "unroot:", 7)) {
-        ret = create_service_thread("unroot", restart_unroot_service, nullptr);
-    } else if(!strncmp(name, "backup:", 7)) {
-        ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s",
-                                                          (name + 7)).c_str(),
-                              nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if(!strncmp(name, "restore:", 8)) {
+    } else if (!strncmp(name, "sync:", 5)) {
+        ret = create_service_thread("sync", file_sync_service).release();
+    } else if (!strncmp(name, "remount:", 8)) {
+        std::string options(name + strlen("remount:"));
+        ret = create_service_thread("remount",
+                                    std::bind(remount_service, std::placeholders::_1, options))
+                      .release();
+    } else if (!strncmp(name, "reboot:", 7)) {
+        std::string arg(name + strlen("reboot:"));
+        ret = create_service_thread("reboot", std::bind(reboot_service, std::placeholders::_1, arg))
+                      .release();
+    } else if (!strncmp(name, "root:", 5)) {
+        ret = create_service_thread("root", restart_root_service).release();
+    } else if (!strncmp(name, "unroot:", 7)) {
+        ret = create_service_thread("unroot", restart_unroot_service).release();
+    } else if (!strncmp(name, "backup:", 7)) {
+        ret = StartSubprocess(
+                android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(),
+                nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+    } else if (!strncmp(name, "restore:", 8)) {
         ret = StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
                               SubprocessProtocol::kNone);
-    } else if(!strncmp(name, "tcpip:", 6)) {
+    } else if (!strncmp(name, "tcpip:", 6)) {
         int port;
         if (sscanf(name + 6, "%d", &port) != 1) {
             return -1;
         }
-        ret = create_service_thread("tcp", restart_tcp_service, reinterpret_cast<void*>(port));
-    } else if(!strncmp(name, "usb:", 4)) {
-        ret = create_service_thread("usb", restart_usb_service, nullptr);
+        ret = create_service_thread("tcp",
+                                    std::bind(restart_tcp_service, std::placeholders::_1, port))
+                      .release();
+    } else if (!strncmp(name, "usb:", 4)) {
+        ret = create_service_thread("usb", restart_usb_service).release();
     } else if (!strncmp(name, "reverse:", 8)) {
         ret = reverse_service(name + 8, transport);
-    } else if(!strncmp(name, "disable-verity:", 15)) {
-        ret = create_service_thread("verity-on", set_verity_enabled_state_service,
-                                    reinterpret_cast<void*>(0));
-    } else if(!strncmp(name, "enable-verity:", 15)) {
-        ret = create_service_thread("verity-off", set_verity_enabled_state_service,
-                                    reinterpret_cast<void*>(1));
+    } else if (!strncmp(name, "disable-verity:", 15)) {
+        ret = create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
+                                                           std::placeholders::_1, false))
+                      .release();
+    } else if (!strncmp(name, "enable-verity:", 15)) {
+        ret = create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
+                                                            std::placeholders::_1, true))
+                      .release();
     } else if (!strcmp(name, "reconnect")) {
-        ret = create_service_thread("reconnect", reconnect_service, transport);
+        ret = create_service_thread("reconnect",
+                                    std::bind(reconnect_service, std::placeholders::_1, transport))
+                      .release();
 #endif
     }
     if (ret >= 0) {
@@ -350,7 +339,7 @@
     while (true) {
         bool is_ambiguous = false;
         std::string error = "unknown error";
-        const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
+        const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : nullptr;
         atransport* t = acquire_one_transport(sinfo->transport_type, serial, sinfo->transport_id,
                                               &is_ambiguous, &error);
         if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
@@ -387,8 +376,8 @@
         return;
     }
 
-    int console_port = strtol(pieces[0].c_str(), NULL, 0);
-    int adb_port = strtol(pieces[1].c_str(), NULL, 0);
+    int console_port = strtol(pieces[0].c_str(), nullptr, 0);
+    int adb_port = strtol(pieces[1].c_str(), nullptr, 0);
     if (console_port <= 0 || adb_port <= 0) {
         *response = android::base::StringPrintf("Invalid port numbers: %s", port_spec.c_str());
         return;
@@ -418,19 +407,16 @@
     }
 }
 
-static void connect_service(int fd, void* data) {
-    char* host = reinterpret_cast<char*>(data);
+static void connect_service(android::base::unique_fd fd, std::string host) {
     std::string response;
-    if (!strncmp(host, "emu:", 4)) {
-        connect_emulator(host + 4, &response);
+    if (!strncmp(host.c_str(), "emu:", 4)) {
+        connect_emulator(host.c_str() + 4, &response);
     } else {
-        connect_device(host, &response);
+        connect_device(host.c_str(), &response);
     }
-    free(host);
 
     // Send response for emulator and device
-    SendProtocolString(fd, response);
-    adb_close(fd);
+    SendProtocolString(fd.get(), response);
 }
 #endif
 
@@ -443,7 +429,7 @@
     } else if (android::base::StartsWith(name, "wait-for-")) {
         name += strlen("wait-for-");
 
-        std::unique_ptr<state_info> sinfo(new state_info);
+        std::unique_ptr<state_info> sinfo = std::make_unique<state_info>();
         if (sinfo == nullptr) {
             fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
             return nullptr;
@@ -479,19 +465,20 @@
             return nullptr;
         }
 
-        int fd = create_service_thread("wait", wait_for_state, sinfo.get());
+        int fd = create_service_thread(
+                         "wait", std::bind(wait_for_state, std::placeholders::_1, sinfo.get()))
+                         .release();
         if (fd != -1) {
             sinfo.release();
         }
         return create_local_socket(fd);
     } else if (!strncmp(name, "connect:", 8)) {
-        char* host = strdup(name + 8);
-        int fd = create_service_thread("connect", connect_service, host);
-        if (fd == -1) {
-            free(host);
-        }
+        std::string host(name + strlen("connect:"));
+        int fd = create_service_thread("connect",
+                                       std::bind(connect_service, std::placeholders::_1, host))
+                         .release();
         return create_local_socket(fd);
     }
-    return NULL;
+    return nullptr;
 }
 #endif /* ADB_HOST */
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index de3215d..69b5180 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -337,7 +337,7 @@
     s->fd = fd;
     s->enqueue = local_socket_enqueue;
     s->ready = local_socket_ready;
-    s->shutdown = NULL;
+    s->shutdown = nullptr;
     s->close = local_socket_close;
     install_local_socket(s);
 
@@ -383,7 +383,7 @@
 
     s = host_service_to_socket(name, serial, transport_id);
 
-    if (s != NULL) {
+    if (s != nullptr) {
         D("LS(%d) bound to '%s'", s->id, name);
         return s;
     }
@@ -435,7 +435,7 @@
 
 static void remote_socket_close(asocket* s) {
     if (s->peer) {
-        s->peer->peer = 0;
+        s->peer->peer = nullptr;
         D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
         s->peer->close(s->peer);
     }
@@ -488,7 +488,7 @@
    send the go-ahead message when they connect */
 static void local_socket_ready_notify(asocket* s) {
     s->ready = local_socket_ready;
-    s->shutdown = NULL;
+    s->shutdown = nullptr;
     s->close = local_socket_close;
     SendOkay(s->fd);
     s->ready(s);
@@ -499,7 +499,7 @@
    connected (to avoid closing them without a status message) */
 static void local_socket_close_notify(asocket* s) {
     s->ready = local_socket_ready;
-    s->shutdown = NULL;
+    s->shutdown = nullptr;
     s->close = local_socket_close;
     SendFail(s->fd, "closed");
     s->close(s);
@@ -706,7 +706,7 @@
         ** and tear down here.
         */
         s2 = create_host_service_socket(service, serial, transport_id);
-        if (s2 == 0) {
+        if (s2 == nullptr) {
             D("SS(%d): couldn't create host service '%s'", s->id, service);
             SendFail(s->peer->fd, "unknown host service");
             goto fail;
@@ -726,7 +726,7 @@
         s->peer->close = local_socket_close;
         s->peer->peer = s2;
         s2->peer = s->peer;
-        s->peer = 0;
+        s->peer = nullptr;
         D("SS(%d): okay", s->id);
         s->close(s);
 
@@ -764,12 +764,12 @@
     s->peer->ready = local_socket_ready_notify;
     s->peer->shutdown = nullptr;
     s->peer->close = local_socket_close_notify;
-    s->peer->peer = 0;
+    s->peer->peer = nullptr;
     /* give him our transport and upref it */
     s->peer->transport = s->transport;
 
     connect_to_remote(s->peer, s->smart_socket_data.data() + 4);
-    s->peer = 0;
+    s->peer = nullptr;
     s->close(s);
     return 1;
 
@@ -789,9 +789,9 @@
 static void smart_socket_close(asocket* s) {
     D("SS(%d): closed", s->id);
     if (s->peer) {
-        s->peer->peer = 0;
+        s->peer->peer = nullptr;
         s->peer->close(s->peer);
-        s->peer = 0;
+        s->peer = nullptr;
     }
     delete s;
 }
@@ -801,7 +801,7 @@
     asocket* s = new asocket();
     s->enqueue = smart_socket_enqueue;
     s->ready = smart_socket_ready;
-    s->shutdown = NULL;
+    s->shutdown = nullptr;
     s->close = smart_socket_close;
 
     D("SS(%d)", s->id);
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 3be99f6..f2911e0 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -39,19 +39,6 @@
 #include "sysdeps/network.h"
 #include "sysdeps/stat.h"
 
-// Some printf-like functions are implemented in terms of
-// android::base::StringAppendV, so they should use the same attribute for
-// compile-time format string checking. On Windows, if the mingw version of
-// vsnprintf is used in StringAppendV, use `gnu_printf' which allows z in %zd
-// and PRIu64 (and related) to be recognized by the compile-time checking.
-#define ADB_FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef ADB_FORMAT_ARCHETYPE
-#define ADB_FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
-
 #ifdef _WIN32
 
 // Clang-only nullability specifiers
@@ -212,14 +199,12 @@
 extern int adb_utime(const char *, struct utimbuf *);
 extern int adb_chmod(const char *, int);
 
-extern int adb_vfprintf(FILE *stream, const char *format, va_list ap)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 0)));
-extern int adb_vprintf(const char *format, va_list ap)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 0)));
-extern int adb_fprintf(FILE *stream, const char *format, ...)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3)));
-extern int adb_printf(const char *format, ...)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
+extern int adb_vfprintf(FILE* stream, const char* format, va_list ap)
+        __attribute__((__format__(__printf__, 2, 0)));
+extern int adb_vprintf(const char* format, va_list ap) __attribute__((__format__(__printf__, 1, 0)));
+extern int adb_fprintf(FILE* stream, const char* format, ...)
+        __attribute__((__format__(__printf__, 2, 3)));
+extern int adb_printf(const char* format, ...) __attribute__((__format__(__printf__, 1, 2)));
 
 extern int adb_fputs(const char* buf, FILE* stream);
 extern int adb_fputc(int ch, FILE* stream);
@@ -329,7 +314,6 @@
 #else /* !_WIN32 a.k.a. Unix */
 
 #include <cutils/sockets.h>
-#include <cutils/threads.h>
 #include <fcntl.h>
 #include <poll.h>
 #include <signal.h>
diff --git a/adb/sysdeps/memory.h b/adb/sysdeps/memory.h
index 0e4c509..4108aff 100644
--- a/adb/sysdeps/memory.h
+++ b/adb/sysdeps/memory.h
@@ -23,6 +23,23 @@
 // We don't have C++14 on Windows yet.
 // Reimplement std::make_unique ourselves until we do.
 
+namespace internal {
+
+template <typename T>
+struct array_known_bounds;
+
+template <typename T>
+struct array_known_bounds<T[]> {
+    constexpr static bool value = false;
+};
+
+template <typename T, size_t N>
+struct array_known_bounds<T[N]> {
+    constexpr static bool value = true;
+};
+
+}  // namespace internal
+
 namespace std {
 
 template <typename T, typename... Args>
@@ -31,6 +48,18 @@
     return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
 }
 
+template <typename T>
+typename std::enable_if<std::is_array<T>::value && !internal::array_known_bounds<T>::value,
+                        std::unique_ptr<T>>::type
+make_unique(std::size_t size) {
+    return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]());
+}
+
+template <typename T, typename... Args>
+typename std::enable_if<std::is_array<T>::value && internal::array_known_bounds<T>::value,
+                        std::unique_ptr<T>>::type
+make_unique(Args&&... args) = delete;
+
 }  // namespace std
 
 #endif
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index bfac342..c94d13f 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -158,7 +158,7 @@
         D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
            func );
         errno = EBADF;
-        return NULL;
+        return nullptr;
     }
 
     f = &_win32_fhs[fd];
@@ -167,7 +167,7 @@
         D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
            func );
         errno = EBADF;
-        return NULL;
+        return nullptr;
     }
 
     return f;
@@ -186,12 +186,12 @@
 static FH
 _fh_alloc( FHClass  clazz )
 {
-    FH   f = NULL;
+    FH   f = nullptr;
 
     std::lock_guard<std::mutex> lock(_win32_lock);
 
     for (int i = _win32_fh_next; i < WIN32_MAX_FHS; ++i) {
-        if (_win32_fhs[i].clazz == NULL) {
+        if (_win32_fhs[i].clazz == nullptr) {
             f = &_win32_fhs[i];
             _win32_fh_next = i + 1;
             f->clazz = clazz;
@@ -226,7 +226,7 @@
         f->name[0] = '\0';
         f->eof     = 0;
         f->used    = 0;
-        f->clazz   = NULL;
+        f->clazz   = nullptr;
     }
     return 0;
 }
@@ -269,7 +269,7 @@
 static int _fh_file_read(FH f, void* buf, int len) {
     DWORD read_bytes;
 
-    if (!ReadFile(f->fh_handle, buf, (DWORD)len, &read_bytes, NULL)) {
+    if (!ReadFile(f->fh_handle, buf, (DWORD)len, &read_bytes, nullptr)) {
         D("adb_read: could not read %d bytes from %s", len, f->name);
         errno = EIO;
         return -1;
@@ -282,7 +282,7 @@
 static int _fh_file_write(FH f, const void* buf, int len) {
     DWORD wrote_bytes;
 
-    if (!WriteFile(f->fh_handle, buf, (DWORD)len, &wrote_bytes, NULL)) {
+    if (!WriteFile(f->fh_handle, buf, (DWORD)len, &wrote_bytes, nullptr)) {
         D("adb_file_write: could not write %d bytes from %s", len, f->name);
         errno = EIO;
         return -1;
@@ -337,7 +337,7 @@
             return -1;
     }
 
-    result = SetFilePointer(f->fh_handle, pos, NULL, method);
+    result = SetFilePointer(f->fh_handle, pos, nullptr, method);
     if (result == INVALID_SET_FILE_POINTER) {
         errno = EIO;
         return -1;
@@ -387,7 +387,7 @@
         return -1;
     }
     f->fh_handle =
-        CreateFileW(path_wide.c_str(), desiredAccess, shareMode, NULL, OPEN_EXISTING, 0, NULL);
+        CreateFileW(path_wide.c_str(), desiredAccess, shareMode, nullptr, OPEN_EXISTING, 0, nullptr);
 
     if (f->fh_handle == INVALID_HANDLE_VALUE) {
         const DWORD err = GetLastError();
@@ -430,7 +430,7 @@
         return -1;
     }
     f->fh_handle = CreateFileW(path_wide.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
-                               NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+                               nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
 
     if (f->fh_handle == INVALID_HANDLE_VALUE) {
         const DWORD err = GetLastError();
@@ -461,7 +461,7 @@
 int adb_read(int fd, void* buf, int len) {
     FH f = _fh_from_int(fd, __func__);
 
-    if (f == NULL) {
+    if (f == nullptr) {
         errno = EBADF;
         return -1;
     }
@@ -472,7 +472,7 @@
 int adb_write(int fd, const void* buf, int len) {
     FH f = _fh_from_int(fd, __func__);
 
-    if (f == NULL) {
+    if (f == nullptr) {
         errno = EBADF;
         return -1;
     }
@@ -483,7 +483,7 @@
 ssize_t adb_writev(int fd, const adb_iovec* iov, int iovcnt) {
     FH f = _fh_from_int(fd, __func__);
 
-    if (f == NULL) {
+    if (f == nullptr) {
         errno = EBADF;
         return -1;
     }
@@ -1700,7 +1700,7 @@
 
         // The following emulation code should write the output sequence to
         // either seqstr or to seqbuf and seqbuflen.
-        const char* seqstr = NULL;  // NULL terminated C-string
+        const char* seqstr = nullptr;  // NULL terminated C-string
         // Enough space for max sequence string below, plus modifiers and/or
         // escape prefix.
         char seqbuf[16];
@@ -1998,7 +1998,7 @@
         // * seqstr is set (and strlen can be used to determine the length).
         // * seqbuf and seqbuflen are set
         // Fallback to ch from Windows.
-        if (seqstr != NULL) {
+        if (seqstr != nullptr) {
             out = seqstr;
             outlen = strlen(seqstr);
         } else if (seqbuflen > 0) {
@@ -2068,9 +2068,9 @@
 }
 
 void stdin_raw_restore() {
-    if (_console_handle != NULL) {
+    if (_console_handle != nullptr) {
         const HANDLE in = _console_handle;
-        _console_handle = NULL;  // clear state
+        _console_handle = nullptr;  // clear state
 
         if (!SetConsoleMode(in, _old_console_mode)) {
             // This really should not fail.
@@ -2082,7 +2082,7 @@
 
 // Called by 'adb shell' and 'adb exec-in' (via unix_read()) to read from stdin.
 int unix_read_interruptible(int fd, void* buf, size_t len) {
-    if ((fd == STDIN_FILENO) && (_console_handle != NULL)) {
+    if ((fd == STDIN_FILENO) && (_console_handle != nullptr)) {
         // If it is a request to read from stdin, and stdin_raw_init() has been
         // called, and it successfully configured the console, then read from
         // the console using Win32 console APIs and partially emulate a unix
@@ -2442,7 +2442,7 @@
 
     // Write UTF-16 to the console.
     DWORD written = 0;
-    if (!WriteConsoleW(console, utf16.c_str(), utf16.length(), &written, NULL)) {
+    if (!WriteConsoleW(console, utf16.c_str(), utf16.length(), &written, nullptr)) {
         errno = EIO;
         return -1;
     }
@@ -2455,9 +2455,8 @@
 }
 
 // Function prototype because attributes cannot be placed on func definitions.
-static int _console_vfprintf(const HANDLE console, FILE* stream,
-                             const char *format, va_list ap)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 3, 0)));
+static int _console_vfprintf(const HANDLE console, FILE* stream, const char* format, va_list ap)
+        __attribute__((__format__(__printf__, 3, 0)));
 
 // Internal function to format a UTF-8 string and write it to a Win32 console.
 // Returns -1 on error.
@@ -2487,7 +2486,7 @@
 
     // If there is an associated Win32 console, write to it specially,
     // otherwise defer to the regular C Runtime, passing it UTF-8.
-    if (console != NULL) {
+    if (console != nullptr) {
         return _console_vfprintf(console, stream, format, ap);
     } else {
         // If vfprintf is a macro, undefine it, so we can call the real
@@ -2578,7 +2577,7 @@
 
     // If there is an associated Win32 console, write to it specially,
     // otherwise defer to the regular C Runtime, passing it UTF-8.
-    if (console != NULL) {
+    if (console != nullptr) {
         return _console_fwrite(ptr, size, nmemb, stream, console);
     } else {
         // If fwrite is a macro, undefine it, so we can call the real
diff --git a/adb/test_device.py b/adb/test_device.py
index f995be2..4abe7a7 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -750,7 +750,6 @@
             if host_dir is not None:
                 shutil.rmtree(host_dir)
 
-    @unittest.expectedFailure # b/25566053
     def test_push_empty(self):
         """Push a directory containing an empty directory to the device."""
         self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
@@ -866,6 +865,21 @@
             self.assertTrue('Permission denied' in output or
                             'Read-only file system' in output)
 
+    @requires_non_root
+    def test_push_directory_creation(self):
+        """Regression test for directory creation.
+
+        Bug: http://b/110953234
+        """
+        with tempfile.NamedTemporaryFile() as tmp_file:
+            tmp_file.write('\0' * 1024 * 1024)
+            tmp_file.flush()
+            remote_path = self.DEVICE_TEMP_DIR + '/test_push_directory_creation'
+            self.device.shell(['rm', '-rf', remote_path])
+
+            remote_path += '/filename'
+            self.device.push(local=tmp_file.name, remote=remote_path)
+
     def _test_pull(self, remote_file, checksum):
         tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
         tmp_write.close()
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 7db9bf2..638940c 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -173,18 +173,18 @@
             attempt = reconnect_queue_.front();
             reconnect_queue_.pop();
             if (attempt.transport->kicked()) {
-                D("transport %s was kicked. giving up on it.", attempt.transport->serial);
+                D("transport %s was kicked. giving up on it.", attempt.transport->serial.c_str());
                 remove_transport(attempt.transport);
                 continue;
             }
         }
-        D("attempting to reconnect %s", attempt.transport->serial);
+        D("attempting to reconnect %s", attempt.transport->serial.c_str());
 
         if (!attempt.transport->Reconnect()) {
-            D("attempting to reconnect %s failed.", attempt.transport->serial);
+            D("attempting to reconnect %s failed.", attempt.transport->serial.c_str());
             if (attempt.attempts_left == 0) {
                 D("transport %s exceeded the number of retry attempts. giving up on it.",
-                  attempt.transport->serial);
+                  attempt.transport->serial.c_str());
                 remove_transport(attempt.transport);
                 continue;
             }
@@ -197,7 +197,7 @@
             continue;
         }
 
-        D("reconnection to %s succeeded.", attempt.transport->serial);
+        D("reconnection to %s succeeded.", attempt.transport->serial.c_str());
         register_transport(attempt.transport);
     }
 }
@@ -402,14 +402,14 @@
         p->msg.data_check = calculate_apacket_checksum(p);
     }
 
-    VLOG(TRANSPORT) << dump_packet(t->serial, "to remote", p);
+    VLOG(TRANSPORT) << dump_packet(t->serial.c_str(), "to remote", p);
 
-    if (t == NULL) {
+    if (t == nullptr) {
         fatal("Transport is null");
     }
 
     if (t->Write(p) != 0) {
-        D("%s: failed to enqueue packet, closing transport", t->serial);
+        D("%s: failed to enqueue packet, closing transport", t->serial.c_str());
         t->Kick();
     }
 }
@@ -467,7 +467,7 @@
 
     D("device tracker %p removed", tracker);
     if (peer) {
-        peer->peer = NULL;
+        peer->peer = nullptr;
         peer->close(peer);
     }
     device_tracker_remove(tracker);
@@ -619,19 +619,13 @@
     t = m.transport;
 
     if (m.action == 0) {
-        D("transport: %s deleting", t->serial);
+        D("transport: %s deleting", t->serial.c_str());
 
         {
             std::lock_guard<std::recursive_mutex> lock(transport_lock);
             transport_list.remove(t);
         }
 
-        if (t->product) free(t->product);
-        if (t->serial) free(t->serial);
-        if (t->model) free(t->model);
-        if (t->device) free(t->device);
-        if (t->devpath) free(t->devpath);
-
         delete t;
 
         update_transports();
@@ -646,11 +640,11 @@
         t->connection()->SetTransportName(t->serial_name());
         t->connection()->SetReadCallback([t](Connection*, std::unique_ptr<apacket> p) {
             if (!check_header(p.get(), t)) {
-                D("%s: remote read: bad header", t->serial);
+                D("%s: remote read: bad header", t->serial.c_str());
                 return false;
             }
 
-            VLOG(TRANSPORT) << dump_packet(t->serial, "from remote", p.get());
+            VLOG(TRANSPORT) << dump_packet(t->serial.c_str(), "from remote", p.get());
             apacket* packet = p.release();
 
             // TODO: Does this need to run on the main thread?
@@ -658,7 +652,7 @@
             return true;
         });
         t->connection()->SetErrorCallback([t](Connection*, const std::string& error) {
-            D("%s: connection terminated: %s", t->serial, error.c_str());
+            D("%s: connection terminated: %s", t->serial.c_str(), error.c_str());
             fdevent_run_on_main_thread([t]() {
                 handle_offline(t);
                 transport_unref(t);
@@ -699,7 +693,7 @@
     transport_registration_recv = s[1];
 
     transport_registration_fde =
-        fdevent_create(transport_registration_recv, transport_registration_func, 0);
+        fdevent_create(transport_registration_recv, transport_registration_func, nullptr);
     fdevent_set(transport_registration_fde, FDE_READ);
 }
 
@@ -717,7 +711,7 @@
     tmsg m;
     m.transport = transport;
     m.action = 1;
-    D("transport: %s registered", transport->serial);
+    D("transport: %s registered", transport->serial.c_str());
     if (transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
@@ -727,7 +721,7 @@
     tmsg m;
     m.transport = transport;
     m.action = 0;
-    D("transport: %s removed", transport->serial);
+    D("transport: %s removed", transport->serial.c_str());
     if (transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
@@ -743,38 +737,38 @@
     if (t->ref_count == 0) {
         t->connection()->Stop();
         if (t->IsTcpDevice() && !t->kicked()) {
-            D("transport: %s unref (attempting reconnection) %d", t->serial, t->kicked());
+            D("transport: %s unref (attempting reconnection) %d", t->serial.c_str(), t->kicked());
             reconnect_handler.TrackTransport(t);
         } else {
-            D("transport: %s unref (kicking and closing)", t->serial);
+            D("transport: %s unref (kicking and closing)", t->serial.c_str());
             remove_transport(t);
         }
     } else {
-        D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
+        D("transport: %s unref (count=%zu)", t->serial.c_str(), t->ref_count);
     }
 }
 
-static int qual_match(const char* to_test, const char* prefix, const char* qual,
+static int qual_match(const std::string& to_test, const char* prefix, const std::string& qual,
                       bool sanitize_qual) {
-    if (!to_test || !*to_test) /* Return true if both the qual and to_test are null strings. */
-        return !qual || !*qual;
+    if (to_test.empty()) /* Return true if both the qual and to_test are empty strings. */
+        return qual.empty();
 
-    if (!qual) return 0;
+    if (qual.empty()) return 0;
 
+    const char* ptr = to_test.c_str();
     if (prefix) {
         while (*prefix) {
-            if (*prefix++ != *to_test++) return 0;
+            if (*prefix++ != *ptr++) return 0;
         }
     }
 
-    while (*qual) {
-        char ch = *qual++;
+    for (char ch : qual) {
         if (sanitize_qual && !isalnum(ch)) ch = '_';
-        if (ch != *to_test++) return 0;
+        if (ch != *ptr++) return 0;
     }
 
-    /* Everything matched so far.  Return true if *to_test is a NUL. */
-    return !*to_test;
+    /* Everything matched so far.  Return true if *ptr is a NUL. */
+    return !*ptr;
 }
 
 atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
@@ -921,7 +915,7 @@
 
 void atransport::Kick() {
     if (!kicked_.exchange(true)) {
-        D("kicking transport %p %s", this, this->serial);
+        D("kicking transport %p %s", this, this->serial.c_str());
         this->connection()->Stop();
     }
 }
@@ -1040,7 +1034,7 @@
 }
 
 bool atransport::MatchesTarget(const std::string& target) const {
-    if (serial) {
+    if (!serial.empty()) {
         if (target == serial) {
             return true;
         } else if (type == kTransportLocal) {
@@ -1069,10 +1063,9 @@
         }
     }
 
-    return (devpath && target == devpath) ||
-           qual_match(target.c_str(), "product:", product, false) ||
-           qual_match(target.c_str(), "model:", model, true) ||
-           qual_match(target.c_str(), "device:", device, false);
+    return (target == devpath) || qual_match(target, "product:", product, false) ||
+           qual_match(target, "model:", model, true) ||
+           qual_match(target, "device:", device, false);
 }
 
 void atransport::SetConnectionEstablished(bool success) {
@@ -1093,9 +1086,9 @@
     return str;
 }
 
-static void append_transport_info(std::string* result, const char* key, const char* value,
+static void append_transport_info(std::string* result, const char* key, const std::string& value,
                                   bool alphanumeric) {
-    if (value == nullptr || *value == '\0') {
+    if (value.empty()) {
         return;
     }
 
@@ -1105,8 +1098,8 @@
 }
 
 static void append_transport(const atransport* t, std::string* result, bool long_listing) {
-    const char* serial = t->serial;
-    if (!serial || !serial[0]) {
+    std::string serial = t->serial;
+    if (serial.empty()) {
         serial = "(no serial number)";
     }
 
@@ -1115,7 +1108,8 @@
         *result += '\t';
         *result += t->connection_state_name();
     } else {
-        android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name().c_str());
+        android::base::StringAppendF(result, "%-22s %s", serial.c_str(),
+                                     t->connection_state_name().c_str());
 
         append_transport_info(result, "", t->devpath, false);
         append_transport_info(result, "product:", t->product, false);
@@ -1138,7 +1132,7 @@
         if (x->type != y->type) {
             return x->type < y->type;
         }
-        return strcmp(x->serial, y->serial) < 0;
+        return x->serial < y->serial;
     });
 
     std::string result;
@@ -1181,7 +1175,7 @@
 
     std::unique_lock<std::recursive_mutex> lock(transport_lock);
     for (const auto& transport : pending_list) {
-        if (transport->serial && strcmp(serial, transport->serial) == 0) {
+        if (strcmp(serial, transport->serial.c_str()) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
                             << " is already in pending_list and fails to register";
             delete t;
@@ -1190,7 +1184,7 @@
     }
 
     for (const auto& transport : transport_list) {
-        if (transport->serial && strcmp(serial, transport->serial) == 0) {
+        if (strcmp(serial, transport->serial.c_str()) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
                             << " is already in transport_list and fails to register";
             delete t;
@@ -1199,7 +1193,7 @@
     }
 
     pending_list.push_front(t);
-    t->serial = strdup(serial);
+    t->serial = serial;
 
     lock.unlock();
 
@@ -1220,7 +1214,7 @@
 
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto& t : transport_list) {
-        if (t->serial && strcmp(serial, t->serial) == 0) {
+        if (strcmp(serial, t->serial.c_str()) == 0) {
             result = t;
             break;
         }
@@ -1251,11 +1245,11 @@
     D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
     init_usb_transport(t, usb);
     if (serial) {
-        t->serial = strdup(serial);
+        t->serial = serial;
     }
 
     if (devpath) {
-        t->devpath = strdup(devpath);
+        t->devpath = devpath;
     }
 
     {
diff --git a/adb/transport.h b/adb/transport.h
index ae9cc02..e9c9d37 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -92,6 +92,8 @@
     std::string transport_name_;
     ReadCallback read_callback_;
     ErrorCallback error_callback_;
+
+    static std::unique_ptr<Connection> FromFd(unique_fd fd);
 };
 
 // Abstraction for a blocking packet transport.
@@ -236,11 +238,11 @@
     TransportType type = kTransportAny;
 
     // Used to identify transports for clients.
-    char* serial = nullptr;
-    char* product = nullptr;
-    char* model = nullptr;
-    char* device = nullptr;
-    char* devpath = nullptr;
+    std::string serial;
+    std::string product;
+    std::string model;
+    std::string device;
+    std::string devpath;
 
     bool IsTcpDevice() const { return type == kTransportLocal; }
 
@@ -251,7 +253,7 @@
     char token[TOKEN_SIZE] = {};
     size_t failed_auth_attempts = 0;
 
-    std::string serial_name() const { return serial ? serial : "<unknown>"; }
+    std::string serial_name() const { return !serial.empty() ? serial : "<unknown>"; }
     std::string connection_state_name() const;
 
     void update_version(int version, size_t payload);
diff --git a/adb/transport_benchmark.cpp b/adb/transport_benchmark.cpp
index da24aa7..022808f 100644
--- a/adb/transport_benchmark.cpp
+++ b/adb/transport_benchmark.cpp
@@ -24,13 +24,19 @@
 #include "sysdeps.h"
 #include "transport.h"
 
-#define ADB_CONNECTION_BENCHMARK(benchmark_name, ...)               \
-    BENCHMARK_TEMPLATE(benchmark_name, FdConnection, ##__VA_ARGS__) \
-        ->Arg(1)                                                    \
-        ->Arg(16384)                                                \
-        ->Arg(MAX_PAYLOAD)                                          \
+#define ADB_CONNECTION_BENCHMARK(benchmark_name, ...)                          \
+    BENCHMARK_TEMPLATE(benchmark_name, FdConnection, ##__VA_ARGS__)            \
+        ->Arg(1)                                                               \
+        ->Arg(16384)                                                           \
+        ->Arg(MAX_PAYLOAD)                                                     \
+        ->UseRealTime();                                                       \
+    BENCHMARK_TEMPLATE(benchmark_name, NonblockingFdConnection, ##__VA_ARGS__) \
+        ->Arg(1)                                                               \
+        ->Arg(16384)                                                           \
+        ->Arg(MAX_PAYLOAD)                                                     \
         ->UseRealTime()
 
+struct NonblockingFdConnection;
 template <typename ConnectionType>
 std::unique_ptr<Connection> MakeConnection(unique_fd fd);
 
@@ -40,6 +46,11 @@
     return std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection));
 }
 
+template <>
+std::unique_ptr<Connection> MakeConnection<NonblockingFdConnection>(unique_fd fd) {
+    return Connection::FromFd(std::move(fd));
+}
+
 template <typename ConnectionType>
 void BM_Connection_Unidirectional(benchmark::State& state) {
     int fds[2];
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
new file mode 100644
index 0000000..85f3c52
--- /dev/null
+++ b/adb/transport_fd.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <deque>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+#include "sysdeps/memory.h"
+#include "transport.h"
+#include "types.h"
+
+static void CreateWakeFds(unique_fd* read, unique_fd* write) {
+    // TODO: eventfd on linux?
+    int wake_fds[2];
+    int rc = adb_socketpair(wake_fds);
+    set_file_block_mode(wake_fds[0], false);
+    set_file_block_mode(wake_fds[1], false);
+    CHECK_EQ(0, rc);
+    *read = unique_fd(wake_fds[0]);
+    *write = unique_fd(wake_fds[1]);
+}
+
+struct NonblockingFdConnection : public Connection {
+    NonblockingFdConnection(unique_fd fd) : started_(false), fd_(std::move(fd)) {
+        set_file_block_mode(fd_.get(), false);
+        CreateWakeFds(&wake_fd_read_, &wake_fd_write_);
+    }
+
+    void SetRunning(bool value) {
+        std::lock_guard<std::mutex> lock(run_mutex_);
+        running_ = value;
+    }
+
+    bool IsRunning() {
+        std::lock_guard<std::mutex> lock(run_mutex_);
+        return running_;
+    }
+
+    void Run(std::string* error) {
+        SetRunning(true);
+        while (IsRunning()) {
+            adb_pollfd pfds[2] = {
+                {.fd = fd_.get(), .events = POLLIN},
+                {.fd = wake_fd_read_.get(), .events = POLLIN},
+            };
+
+            {
+                std::lock_guard<std::mutex> lock(this->write_mutex_);
+                if (!writable_) {
+                    pfds[0].events |= POLLOUT;
+                }
+            }
+
+            int rc = adb_poll(pfds, 2, -1);
+            if (rc == -1) {
+                *error = android::base::StringPrintf("poll failed: %s", strerror(errno));
+                return;
+            } else if (rc == 0) {
+                LOG(FATAL) << "poll timed out with an infinite timeout?";
+            }
+
+            if (pfds[0].revents) {
+                if ((pfds[0].revents & POLLOUT)) {
+                    std::lock_guard<std::mutex> lock(this->write_mutex_);
+                    WriteResult result = DispatchWrites();
+                    switch (result) {
+                        case WriteResult::Error:
+                            *error = "write failed";
+                            return;
+
+                        case WriteResult::Completed:
+                            writable_ = true;
+                            break;
+
+                        case WriteResult::TryAgain:
+                            break;
+                    }
+                }
+
+                if (pfds[0].revents & POLLIN) {
+                    // TODO: Should we be getting blocks from a free list?
+                    auto block = std::make_unique<IOVector::block_type>(MAX_PAYLOAD);
+                    rc = adb_read(fd_.get(), &(*block)[0], block->size());
+                    if (rc == -1) {
+                        *error = std::string("read failed: ") + strerror(errno);
+                        return;
+                    } else if (rc == 0) {
+                        *error = "read failed: EOF";
+                        return;
+                    }
+                    block->resize(rc);
+                    read_buffer_.append(std::move(block));
+
+                    if (!read_header_ && read_buffer_.size() >= sizeof(amessage)) {
+                        auto header_buf = read_buffer_.take_front(sizeof(amessage)).coalesce();
+                        CHECK_EQ(sizeof(amessage), header_buf.size());
+                        read_header_ = std::make_unique<amessage>();
+                        memcpy(read_header_.get(), header_buf.data(), sizeof(amessage));
+                    }
+
+                    if (read_header_ && read_buffer_.size() >= read_header_->data_length) {
+                        auto data_chain = read_buffer_.take_front(read_header_->data_length);
+
+                        // TODO: Make apacket carry around a IOVector instead of coalescing.
+                        auto payload = data_chain.coalesce<apacket::payload_type>();
+                        auto packet = std::make_unique<apacket>();
+                        packet->msg = *read_header_;
+                        packet->payload = std::move(payload);
+                        read_header_ = nullptr;
+                        read_callback_(this, std::move(packet));
+                    }
+                }
+            }
+
+            if (pfds[1].revents) {
+                uint64_t buf;
+                rc = adb_read(wake_fd_read_.get(), &buf, sizeof(buf));
+                CHECK_EQ(static_cast<int>(sizeof(buf)), rc);
+
+                // We were woken up either to add POLLOUT to our events, or to exit.
+                // Do nothing.
+            }
+        }
+    }
+
+    void Start() override final {
+        if (started_.exchange(true)) {
+            LOG(FATAL) << "Connection started multiple times?";
+        }
+
+        thread_ = std::thread([this]() {
+            std::string error = "connection closed";
+            Run(&error);
+            this->error_callback_(this, error);
+        });
+    }
+
+    void Stop() override final {
+        SetRunning(false);
+        WakeThread();
+        thread_.join();
+    }
+
+    void WakeThread() {
+        uint64_t buf = 0;
+        if (TEMP_FAILURE_RETRY(adb_write(wake_fd_write_.get(), &buf, sizeof(buf))) != sizeof(buf)) {
+            LOG(FATAL) << "failed to wake up thread";
+        }
+    }
+
+    enum class WriteResult {
+        Error,
+        Completed,
+        TryAgain,
+    };
+
+    WriteResult DispatchWrites() REQUIRES(write_mutex_) {
+        CHECK(!write_buffer_.empty());
+        if (!writable_) {
+            return WriteResult::TryAgain;
+        }
+
+        auto iovs = write_buffer_.iovecs();
+        ssize_t rc = adb_writev(fd_.get(), iovs.data(), iovs.size());
+        if (rc == -1) {
+            return WriteResult::Error;
+        } else if (rc == 0) {
+            errno = 0;
+            return WriteResult::Error;
+        }
+
+        // TODO: Implement a more efficient drop_front?
+        write_buffer_.take_front(rc);
+        if (write_buffer_.empty()) {
+            return WriteResult::Completed;
+        }
+
+        // There's data left in the range, which means our write returned early.
+        return WriteResult::TryAgain;
+    }
+
+    bool Write(std::unique_ptr<apacket> packet) final {
+        std::lock_guard<std::mutex> lock(write_mutex_);
+        const char* header_begin = reinterpret_cast<const char*>(&packet->msg);
+        const char* header_end = header_begin + sizeof(packet->msg);
+        auto header_block = std::make_unique<IOVector::block_type>(header_begin, header_end);
+        write_buffer_.append(std::move(header_block));
+        if (!packet->payload.empty()) {
+            write_buffer_.append(std::make_unique<IOVector::block_type>(std::move(packet->payload)));
+        }
+        return DispatchWrites() != WriteResult::Error;
+    }
+
+    std::thread thread_;
+
+    std::atomic<bool> started_;
+    std::mutex run_mutex_;
+    bool running_ GUARDED_BY(run_mutex_);
+
+    std::unique_ptr<amessage> read_header_;
+    IOVector read_buffer_;
+
+    unique_fd fd_;
+    unique_fd wake_fd_read_;
+    unique_fd wake_fd_write_;
+
+    std::mutex write_mutex_;
+    bool writable_ GUARDED_BY(write_mutex_) = true;
+    IOVector write_buffer_ GUARDED_BY(write_mutex_);
+
+    IOVector incoming_queue_;
+};
+
+std::unique_ptr<Connection> Connection::FromFd(unique_fd fd) {
+    return std::make_unique<NonblockingFdConnection>(std::move(fd));
+}
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 181d666..fa38238 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -490,7 +490,7 @@
             std::make_unique<BlockingConnectionAdapter>(std::move(emulator_connection)));
         std::lock_guard<std::mutex> lock(local_transports_lock);
         atransport* existing_transport = find_emulator_transport_by_adb_port_locked(adb_port);
-        if (existing_transport != NULL) {
+        if (existing_transport != nullptr) {
             D("local transport for port %d already registered (%p)?", adb_port, existing_transport);
             fail = -1;
         } else if (local_transports.size() >= ADB_LOCAL_TRANSPORT_MAX) {
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index d987d4f..8c628d8 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -86,9 +86,9 @@
     ASSERT_EQ(0U, t.features().size());
     ASSERT_EQ(kCsHost, t.GetConnectionState());
 
-    ASSERT_EQ(nullptr, t.product);
-    ASSERT_EQ(nullptr, t.model);
-    ASSERT_EQ(nullptr, t.device);
+    ASSERT_EQ(std::string(), t.product);
+    ASSERT_EQ(std::string(), t.model);
+    ASSERT_EQ(std::string(), t.device);
 }
 
 TEST(transport, parse_banner_product_features) {
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 94b2e37..602970c 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -122,7 +122,7 @@
 // On Android devices, we rely on the kernel to provide buffered read.
 // So we can recover automatically from EOVERFLOW.
 static int remote_read(apacket* p, usb_handle* usb) {
-    if (usb_read(usb, &p->msg, sizeof(amessage))) {
+    if (usb_read(usb, &p->msg, sizeof(amessage)) != sizeof(amessage)) {
         PLOG(ERROR) << "remote usb: read terminated (message)";
         return -1;
     }
@@ -134,7 +134,8 @@
         }
 
         p->payload.resize(p->msg.data_length);
-        if (usb_read(usb, &p->payload[0], p->payload.size())) {
+        if (usb_read(usb, &p->payload[0], p->payload.size())
+                != static_cast<int>(p->payload.size())) {
             PLOG(ERROR) << "remote usb: terminated (data)";
             return -1;
         }
@@ -154,14 +155,14 @@
 }
 
 bool UsbConnection::Write(apacket* packet) {
-    unsigned size = packet->msg.data_length;
+    int size = packet->msg.data_length;
 
-    if (usb_write(handle_, &packet->msg, sizeof(packet->msg)) != 0) {
+    if (usb_write(handle_, &packet->msg, sizeof(packet->msg)) != sizeof(packet->msg)) {
         PLOG(ERROR) << "remote usb: 1 - write terminated";
         return false;
     }
 
-    if (packet->msg.data_length != 0 && usb_write(handle_, packet->payload.data(), size) != 0) {
+    if (packet->msg.data_length != 0 && usb_write(handle_, packet->payload.data(), size) != size) {
         PLOG(ERROR) << "remote usb: 2 - write terminated";
         return false;
     }
diff --git a/adb/types.h b/adb/types.h
index c6b3f07..a3e5d48 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 #include <deque>
+#include <memory>
 #include <type_traits>
 #include <utility>
 #include <vector>
@@ -37,7 +38,7 @@
 
     template <typename Iterator>
     Block(Iterator begin, Iterator end) : Block(end - begin) {
-        std::copy(begin, end, data_);
+        std::copy(begin, end, data_.get());
     }
 
     Block(const Block& copy) = delete;
@@ -73,11 +74,11 @@
     void assign(InputIt begin, InputIt end) {
         clear();
         allocate(end - begin);
-        std::copy(begin, end, data_);
+        std::copy(begin, end, data_.get());
     }
 
     void clear() {
-        free(data_);
+        data_.reset();
         capacity_ = 0;
         size_ = 0;
     }
@@ -86,11 +87,11 @@
     size_t size() const { return size_; }
     bool empty() const { return size() == 0; }
 
-    char* data() { return data_; }
-    const char* data() const { return data_; }
+    char* data() { return data_.get(); }
+    const char* data() const { return data_.get(); }
 
-    char* begin() { return data_; }
-    const char* begin() const { return data_; }
+    char* begin() { return data_.get(); }
+    const char* begin() const { return data_.get(); }
 
     char* end() { return data() + size_; }
     const char* end() const { return data() + size_; }
@@ -108,13 +109,13 @@
         CHECK_EQ(0ULL, capacity_);
         CHECK_EQ(0ULL, size_);
         if (size != 0) {
-            data_ = static_cast<char*>(malloc(size));
+            data_ = std::make_unique<char[]>(size);
             capacity_ = size;
             size_ = size;
         }
     }
 
-    char* data_ = nullptr;
+    std::unique_ptr<char[]> data_;
     size_t capacity_ = 0;
     size_t size_ = 0;
 };
diff --git a/base/Android.bp b/base/Android.bp
index 47b29c6..3d80d97 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -48,6 +48,7 @@
         "file.cpp",
         "logging.cpp",
         "parsenetaddress.cpp",
+        "properties.cpp",
         "quick_exit.cpp",
         "stringprintf.cpp",
         "strings.cpp",
@@ -58,9 +59,6 @@
     shared_libs: ["liblog"],
     target: {
         android: {
-            srcs: [
-                "properties.cpp",
-            ],
             sanitize: {
                 misc_undefined: ["integer"],
             },
@@ -95,6 +93,7 @@
     name: "libbase",
     defaults: ["libbase_defaults"],
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
     vndk: {
         enabled: true,
@@ -129,6 +128,7 @@
         "parsedouble_test.cpp",
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
+        "properties_test.cpp",
         "quick_exit_test.cpp",
         "scopeguard_test.cpp",
         "stringprintf_test.cpp",
@@ -138,7 +138,6 @@
     ],
     target: {
         android: {
-            srcs: ["properties_test.cpp"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
diff --git a/base/file.cpp b/base/file.cpp
index 2f697a1..d6fe753 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -199,17 +199,23 @@
 bool RemoveFileIfExists(const std::string& path, std::string* err) {
   struct stat st;
 #if defined(_WIN32)
-  //TODO: Windows version can't handle symbol link correctly.
+  // TODO: Windows version can't handle symbolic links correctly.
   int result = stat(path.c_str(), &st);
   bool file_type_removable = (result == 0 && S_ISREG(st.st_mode));
 #else
   int result = lstat(path.c_str(), &st);
   bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
 #endif
+  if (result == -1) {
+    if (errno == ENOENT || errno == ENOTDIR) return true;
+    if (err != nullptr) *err = strerror(errno);
+    return false;
+  }
+
   if (result == 0) {
     if (!file_type_removable) {
       if (err != nullptr) {
-        *err = "is not a regular or symbol link file";
+        *err = "is not a regular file or symbolic link";
       }
       return false;
     }
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 02b431d..6794652 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -26,6 +26,10 @@
 
 #include "android-base/test_utils.h"
 
+#if !defined(_WIN32)
+#include <pwd.h>
+#endif
+
 TEST(file, ReadFileToString_ENOENT) {
   std::string s("hello");
   errno = 0;
@@ -115,7 +119,7 @@
   ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
 }
 
-TEST(file, RemoveFileIfExist) {
+TEST(file, RemoveFileIfExists) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
   close(tf.fd);
@@ -126,9 +130,43 @@
   TemporaryDir td;
   ASSERT_FALSE(android::base::RemoveFileIfExists(td.path));
   ASSERT_FALSE(android::base::RemoveFileIfExists(td.path, &err));
-  ASSERT_EQ("is not a regular or symbol link file", err);
+  ASSERT_EQ("is not a regular file or symbolic link", err);
 }
 
+TEST(file, RemoveFileIfExists_ENOTDIR) {
+  TemporaryFile tf;
+  close(tf.fd);
+  tf.fd = -1;
+  std::string err{"xxx"};
+  ASSERT_TRUE(android::base::RemoveFileIfExists(std::string{tf.path} + "/abc", &err));
+  ASSERT_EQ("xxx", err);
+}
+
+#if !defined(_WIN32)
+TEST(file, RemoveFileIfExists_EACCES) {
+  // EACCES -- one of the directories in the path has no search permission
+  // root can bypass permission restrictions, so drop root.
+  if (getuid() == 0) {
+    passwd* shell = getpwnam("shell");
+    setgid(shell->pw_gid);
+    setuid(shell->pw_uid);
+  }
+
+  TemporaryDir td;
+  TemporaryFile tf(td.path);
+  close(tf.fd);
+  tf.fd = -1;
+  std::string err{"xxx"};
+  // Remove dir's search permission.
+  ASSERT_TRUE(chmod(td.path, S_IRUSR | S_IWUSR) == 0);
+  ASSERT_FALSE(android::base::RemoveFileIfExists(tf.path, &err));
+  ASSERT_EQ("Permission denied", err);
+  // Set dir's search permission again.
+  ASSERT_TRUE(chmod(td.path, S_IRWXU) == 0);
+  ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err));
+}
+#endif
+
 TEST(file, Readlink) {
 #if !defined(_WIN32)
   // Linux doesn't allow empty symbolic links.
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
index c3396ee..11fcf71 100644
--- a/base/include/android-base/chrono_utils.h
+++ b/base/include/android-base/chrono_utils.h
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_CHRONO_UTILS_H
-#define ANDROID_BASE_CHRONO_UTILS_H
+#pragma once
 
 #include <chrono>
 #include <sstream>
 
-#if __cplusplus > 201103L  // C++14
+#if __cplusplus > 201103L && !defined(__WIN32)  // C++14
 using namespace std::chrono_literals;
 #endif
 
@@ -52,5 +51,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_CHRONO_UTILS_H
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
index 6eb677c..cbbd8c9 100644
--- a/base/include/android-base/endian.h
+++ b/base/include/android-base/endian.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_ENDIAN_H
-#define ANDROID_BASE_ENDIAN_H
+#pragma once
 
 /* A cross-platform equivalent of bionic's <sys/endian.h>. */
 
@@ -86,5 +85,3 @@
 #define le64toh(x) (x)
 
 #endif
-
-#endif  // ANDROID_BASE_ENDIAN_H
diff --git a/base/include/android-base/errors.h b/base/include/android-base/errors.h
index 04c299c..06f29fc 100644
--- a/base/include/android-base/errors.h
+++ b/base/include/android-base/errors.h
@@ -27,8 +27,7 @@
 // special handling to get the error string. Refer to Microsoft documentation
 // to determine which error code to check for each function.
 
-#ifndef ANDROID_BASE_ERRORS_H
-#define ANDROID_BASE_ERRORS_H
+#pragma once
 
 #include <string>
 
@@ -42,5 +41,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_ERRORS_H
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 667d6fb..908690b 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_FILE_H
-#define ANDROID_BASE_FILE_H
+#pragma once
 
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -78,5 +77,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif // ANDROID_BASE_FILE_H
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index 05a12e7..aea6ce6 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_LOGGING_H
-#define ANDROID_BASE_LOGGING_H
+#pragma once
 
 //
 // Google-style C++ logging.
@@ -488,23 +487,14 @@
 // Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)" instead.
 // Note: a not-recommended alternative is to let Clang ignore the warning by adding
 //       -Wno-user-defined-warnings to CPPFLAGS.
-#ifdef __clang__
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wgcc-compat"
 #define OSTREAM_STRING_POINTER_USAGE_WARNING \
     __attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
-#else
-#define OSTREAM_STRING_POINTER_USAGE_WARNING /* empty */
-#endif
 inline std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer)
     OSTREAM_STRING_POINTER_USAGE_WARNING {
   return stream << static_cast<const void*>(string_pointer);
 }
-#ifdef __clang__
 #pragma clang diagnostic pop
-#endif
-#undef OSTREAM_STRING_POINTER_USAGE_WARNING
 
 }  // namespace std
-
-#endif  // ANDROID_BASE_LOGGING_H
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index fd6efb2..49cc0c9 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_MACROS_H
-#define ANDROID_BASE_MACROS_H
+#pragma once
 
 #include <stddef.h>  // for size_t
 #include <unistd.h>  // for TEMP_FAILURE_RETRY
@@ -171,17 +170,7 @@
 //
 //  In either case this macro has no effect on runtime behavior and performance
 //  of code.
-#if defined(__clang__) && defined(__has_warning)
-#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
 #define FALLTHROUGH_INTENDED [[clang::fallthrough]]  // NOLINT
-#endif
-#endif
-
-#ifndef FALLTHROUGH_INTENDED
-#define FALLTHROUGH_INTENDED \
-  do {                       \
-  } while (0)
-#endif
 
 // Current ABI string
 #if defined(__arm__)
@@ -197,5 +186,3 @@
 #elif defined(__mips__) && defined(__LP64__)
 #define ABI_STRING "mips64"
 #endif
-
-#endif  // ANDROID_BASE_MACROS_H
diff --git a/base/include/android-base/memory.h b/base/include/android-base/memory.h
index 9971226..0277a03 100644
--- a/base/include/android-base/memory.h
+++ b/base/include/android-base/memory.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_MEMORY_H
-#define ANDROID_BASE_MEMORY_H
+#pragma once
 
 namespace android {
 namespace base {
@@ -37,5 +36,3 @@
 
 } // namespace base
 } // namespace android
-
-#endif  // ANDROID_BASE_MEMORY_H
diff --git a/base/include/android-base/parsedouble.h b/base/include/android-base/parsedouble.h
index daa6902..c273c61 100644
--- a/base/include/android-base/parsedouble.h
+++ b/base/include/android-base/parsedouble.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PARSEDOUBLE_H
-#define ANDROID_BASE_PARSEDOUBLE_H
+#pragma once
 
 #include <errno.h>
 #include <stdlib.h>
@@ -46,5 +45,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_PARSEDOUBLE_H
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index 1b7cc5f..fc68d56 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PARSEINT_H
-#define ANDROID_BASE_PARSEINT_H
+#pragma once
 
 #include <errno.h>
 #include <stdlib.h>
@@ -27,9 +26,9 @@
 namespace android {
 namespace base {
 
-// Parses the unsigned decimal integer in the string 's' and sets 'out' to
-// that value. Optionally allows the caller to define a 'max' beyond which
-// otherwise valid values will be rejected. Returns boolean success; 'out'
+// Parses the unsigned decimal or hexadecimal integer in the string 's' and sets
+// 'out' to that value. Optionally allows the caller to define a 'max' beyond
+// which otherwise valid values will be rejected. Returns boolean success; 'out'
 // is untouched if parsing fails.
 template <typename T>
 bool ParseUint(const char* s, T* out, T max = std::numeric_limits<T>::max(),
@@ -43,9 +42,7 @@
     const char* suffixes = "bkmgtpe";
     const char* suffix;
     if (!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) return false;
-#if __clang__  // TODO: win32 still builds with GCC :-(
     if (__builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) return false;
-#endif
   }
   if (max < result) {
     return false;
@@ -72,8 +69,8 @@
   return ParseByteCount(s.c_str(), out, max);
 }
 
-// Parses the signed decimal integer in the string 's' and sets 'out' to
-// that value. Optionally allows the caller to define a 'min' and 'max
+// Parses the signed decimal or hexadecimal integer in the string 's' and sets
+// 'out' to that value. Optionally allows the caller to define a 'min' and 'max'
 // beyond which otherwise valid values will be rejected. Returns boolean
 // success; 'out' is untouched if parsing fails.
 template <typename T>
@@ -104,5 +101,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_PARSEINT_H
diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h
index b4ac025..47f8b5f 100644
--- a/base/include/android-base/parsenetaddress.h
+++ b/base/include/android-base/parsenetaddress.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PARSENETADDRESS_H
-#define ANDROID_BASE_PARSENETADDRESS_H
+#pragma once
 
 #include <string>
 
@@ -34,5 +33,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_PARSENETADDRESS_H
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 041586c..31e5273 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -14,15 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PROPERTIES_H
-#define ANDROID_BASE_PROPERTIES_H
+#pragma once
 
 #include <sys/cdefs.h>
 
-#if !defined(__BIONIC__)
-#error Only bionic supports system properties.
-#endif
-
 #include <chrono>
 #include <limits>
 #include <string>
@@ -62,16 +57,18 @@
 // Waits for the system property `key` to have the value `expected_value`.
 // Times out after `relative_timeout`.
 // Returns true on success, false on timeout.
+#if defined(__BIONIC__)
 bool WaitForProperty(const std::string& key, const std::string& expected_value,
                      std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
+#endif
 
 // Waits for the system property `key` to be created.
 // Times out after `relative_timeout`.
 // Returns true on success, false on timeout.
+#if defined(__BIONIC__)
 bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
                                                          std::chrono::milliseconds::max());
+#endif
 
 } // namespace base
 } // namespace android
-
-#endif  // ANDROID_BASE_PROPERTIES_H
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
index c314e02..e6a9d10 100644
--- a/base/include/android-base/scopeguard.h
+++ b/base/include/android-base/scopeguard.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_SCOPEGUARD_H
-#define ANDROID_BASE_SCOPEGUARD_H
+#pragma once
 
 #include <utility>  // for std::move, std::forward
 
@@ -66,5 +65,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_SCOPEGUARD_H
diff --git a/base/include/android-base/stringprintf.h b/base/include/android-base/stringprintf.h
index 1fd6297..93c56af 100644
--- a/base/include/android-base/stringprintf.h
+++ b/base/include/android-base/stringprintf.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_STRINGPRINTF_H
-#define ANDROID_BASE_STRINGPRINTF_H
+#pragma once
 
 #include <stdarg.h>
 #include <string>
@@ -24,33 +23,18 @@
 namespace base {
 
 // These printf-like functions are implemented in terms of vsnprintf, so they
-// use the same attribute for compile-time format string checking. On Windows,
-// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
-// in %zd and PRIu64 (and related) to be recognized by the compile-time
-// checking.
-#define ANDROID_BASE_FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef ANDROID_BASE_FORMAT_ARCHETYPE
-#define ANDROID_BASE_FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
+// use the same attribute for compile-time format string checking.
 
 // Returns a string corresponding to printf-like formatting of the arguments.
-std::string StringPrintf(const char* fmt, ...)
-    __attribute__((__format__(ANDROID_BASE_FORMAT_ARCHETYPE, 1, 2)));
+std::string StringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
 
 // Appends a printf-like formatting of the arguments to 'dst'.
 void StringAppendF(std::string* dst, const char* fmt, ...)
-    __attribute__((__format__(ANDROID_BASE_FORMAT_ARCHETYPE, 2, 3)));
+    __attribute__((__format__(__printf__, 2, 3)));
 
 // Appends a printf-like formatting of the arguments to 'dst'.
 void StringAppendV(std::string* dst, const char* format, va_list ap)
-    __attribute__((__format__(ANDROID_BASE_FORMAT_ARCHETYPE, 2, 0)));
-
-#undef ANDROID_BASE_FORMAT_ARCHETYPE
+    __attribute__((__format__(__printf__, 2, 0)));
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_STRINGPRINTF_H
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 4d9fa34..9c35560 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_STRINGS_H
-#define ANDROID_BASE_STRINGS_H
+#pragma once
 
 #include <sstream>
 #include <string>
@@ -75,5 +74,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_STRINGS_H
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index b29676f..9e2ea97 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_TEST_UTILS_H
-#define ANDROID_BASE_TEST_UTILS_H
+#pragma once
 
 #include <regex>
 #include <string>
@@ -114,5 +113,3 @@
       ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << (str); \
     }                                                                                             \
   } while (0)
-
-#endif  // ANDROID_BASE_TEST_UTILS_H
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 1307f0e..5c55e63 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -14,14 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_THREAD_ANNOTATIONS_H
-#define ANDROID_BASE_THREAD_ANNOTATIONS_H
+#pragma once
 
-#if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
-#define THREAD_ANNOTATION_ATTRIBUTE__(x)   __attribute__((x))
-#else
-#define THREAD_ANNOTATION_ATTRIBUTE__(x)   // no-op
-#endif
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
 
 #define CAPABILITY(x) \
       THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
@@ -109,5 +104,3 @@
 
 #define NO_THREAD_SAFETY_ANALYSIS \
       THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
-
-#endif  // ANDROID_BASE_THREAD_ANNOTATIONS_H
diff --git a/base/include/android-base/threads.h b/base/include/android-base/threads.h
index 85e65ba..dba1fc6 100644
--- a/base/include/android-base/threads.h
+++ b/base/include/android-base/threads.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_THREADS_H
-#define ANDROID_BASE_THREADS_H
+#pragma once
 
 #include <stdint.h>
 
@@ -25,4 +24,7 @@
 }
 }  // namespace android
 
+#if defined(__GLIBC__)
+// bionic has this Linux-specifix call, but glibc doesn't.
+extern "C" int tgkill(int tgid, int tid, int sig);
 #endif
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 5d89271..c733081 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_UNIQUE_FD_H
-#define ANDROID_BASE_UNIQUE_FD_H
+#pragma once
 
 #include <fcntl.h>
 
@@ -98,7 +97,8 @@
 #if !defined(_WIN32)
 
 // Inline functions, so that they can be used header-only.
-inline bool Pipe(unique_fd* read, unique_fd* write) {
+template <typename Closer>
+inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write) {
   int pipefd[2];
 
 #if defined(__linux__)
@@ -122,7 +122,9 @@
   return true;
 }
 
-inline bool Socketpair(int domain, int type, int protocol, unique_fd* left, unique_fd* right) {
+template <typename Closer>
+inline bool Socketpair(int domain, int type, int protocol, unique_fd_impl<Closer>* left,
+                       unique_fd_impl<Closer>* right) {
   int sockfd[2];
   if (socketpair(domain, type, protocol, sockfd) != 0) {
     return false;
@@ -132,7 +134,8 @@
   return true;
 }
 
-inline bool Socketpair(int type, unique_fd* left, unique_fd* right) {
+template <typename Closer>
+inline bool Socketpair(int type, unique_fd_impl<Closer>* left, unique_fd_impl<Closer>* right) {
   return Socketpair(AF_UNIX, type, 0, left, right);
 }
 
@@ -143,12 +146,4 @@
 
 template <typename T>
 int close(const android::base::unique_fd_impl<T>&)
-#if defined(__clang__)
-  __attribute__((__unavailable__(
-#else
-  __attribute__((__error__(
-#endif
-    "close called on unique_fd"
-  )));
-
-#endif  // ANDROID_BASE_UNIQUE_FD_H
+    __attribute__((__unavailable__("close called on unique_fd")));
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index c9cc1ab..4b91623 100755
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_UTF8_H
-#define ANDROID_BASE_UTF8_H
+#pragma once
 
 #ifdef _WIN32
 #include <string>
@@ -102,5 +101,3 @@
 }  // namespace utf8
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_UTF8_H
diff --git a/base/logging.cpp b/base/logging.cpp
index 978d56d..35054ac 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -58,13 +58,15 @@
 #include <android-base/strings.h>
 #include <android-base/threads.h>
 
-namespace {
+namespace android {
+namespace base {
+
+// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one.
+#if defined(__GLIBC__) || defined(_WIN32)
+static const char* getprogname() {
 #if defined(__GLIBC__)
-const char* getprogname() {
   return program_invocation_short_name;
-}
 #elif defined(_WIN32)
-const char* getprogname() {
   static bool first = true;
   static char progname[MAX_PATH] = {};
 
@@ -81,11 +83,28 @@
   }
 
   return progname;
+#endif
 }
 #endif
 
+static const char* GetFileBasename(const char* file) {
+  // We can't use basename(3) even on Unix because the Mac doesn't
+  // have a non-modifying basename.
+  const char* last_slash = strrchr(file, '/');
+  if (last_slash != nullptr) {
+    return last_slash + 1;
+  }
+#if defined(_WIN32)
+  const char* last_backslash = strrchr(file, '\\');
+  if (last_backslash != nullptr) {
+    return last_backslash + 1;
+  }
+#endif
+  return file;
+}
+
 #if defined(__linux__)
-int OpenKmsg() {
+static int OpenKmsg() {
 #if defined(__ANDROID__)
   // pick up 'file w /dev/kmsg' environment from daemon's init rc file
   const auto val = getenv("ANDROID_FILE__dev_kmsg");
@@ -100,10 +119,6 @@
   return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
 }
 #endif
-} // namespace
-
-namespace android {
-namespace base {
 
 static std::mutex& LoggingLock() {
   static auto& logging_lock = *new std::mutex();
@@ -216,7 +231,7 @@
                  unsigned int /*line*/, const char* message) {
   if (severity >= WARNING) {
     fflush(stdout);
-    fprintf(stderr, "%s: %s\n", getprogname(), message);
+    fprintf(stderr, "%s: %s\n", GetFileBasename(getprogname()), message);
   } else {
     fprintf(stdout, "%s\n", message);
   }
@@ -336,22 +351,6 @@
   Aborter() = std::move(aborter);
 }
 
-static const char* GetFileBasename(const char* file) {
-  // We can't use basename(3) even on Unix because the Mac doesn't
-  // have a non-modifying basename.
-  const char* last_slash = strrchr(file, '/');
-  if (last_slash != nullptr) {
-    return last_slash + 1;
-  }
-#if defined(_WIN32)
-  const char* last_backslash = strrchr(file, '\\');
-  if (last_backslash != nullptr) {
-    return last_backslash + 1;
-  }
-#endif
-  return file;
-}
-
 // This indirection greatly reduces the stack impact of having lots of
 // checks/logging in a function.
 class LogMessageData {
diff --git a/base/properties.cpp b/base/properties.cpp
index 6cf43f9..d5a5918 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -14,16 +14,18 @@
  * limitations under the License.
  */
 
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-
 #include "android-base/properties.h"
 
+#if defined(__BIONIC__)
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/system_properties.h>
 #include <sys/_system_properties.h>
+#endif
 
 #include <algorithm>
 #include <chrono>
 #include <limits>
+#include <map>
 #include <string>
 
 #include <android-base/parseint.h>
@@ -31,24 +33,6 @@
 namespace android {
 namespace base {
 
-std::string GetProperty(const std::string& key, const std::string& default_value) {
-  const prop_info* pi = __system_property_find(key.c_str());
-  if (pi == nullptr) return default_value;
-
-  std::string property_value;
-  __system_property_read_callback(pi,
-                                  [](void* cookie, const char*, const char* value, unsigned) {
-                                    auto property_value = reinterpret_cast<std::string*>(cookie);
-                                    *property_value = value;
-                                  },
-                                  &property_value);
-
-  // If the property exists but is empty, also return the default value.
-  // Since we can't remove system properties, "empty" is traditionally
-  // the same as "missing" (this was true for cutils' property_get).
-  return property_value.empty() ? default_value : property_value;
-}
-
 bool GetBoolProperty(const std::string& key, bool default_value) {
   std::string value = GetProperty(key, "");
   if (value == "1" || value == "y" || value == "yes" || value == "on" || value == "true") {
@@ -85,10 +69,43 @@
 template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t);
 template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t);
 
+#if !defined(__BIONIC__)
+static std::map<std::string, std::string>& g_properties = *new std::map<std::string, std::string>;
+static int __system_property_set(const char* key, const char* value) {
+  g_properties[key] = value;
+  return 0;
+}
+#endif
+
+std::string GetProperty(const std::string& key, const std::string& default_value) {
+  std::string property_value;
+#if defined(__BIONIC__)
+  const prop_info* pi = __system_property_find(key.c_str());
+  if (pi == nullptr) return default_value;
+
+  __system_property_read_callback(pi,
+                                  [](void* cookie, const char*, const char* value, unsigned) {
+                                    auto property_value = reinterpret_cast<std::string*>(cookie);
+                                    *property_value = value;
+                                  },
+                                  &property_value);
+#else
+  auto it = g_properties.find(key);
+  if (it == g_properties.end()) return default_value;
+  property_value = it->second;
+#endif
+  // If the property exists but is empty, also return the default value.
+  // Since we can't remove system properties, "empty" is traditionally
+  // the same as "missing" (this was true for cutils' property_get).
+  return property_value.empty() ? default_value : property_value;
+}
+
 bool SetProperty(const std::string& key, const std::string& value) {
   return (__system_property_set(key.c_str(), value.c_str()) == 0);
 }
 
+#if defined(__BIONIC__)
+
 struct WaitForPropertyData {
   bool done;
   const std::string* expected_value;
@@ -175,5 +192,7 @@
   return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
 }
 
+#endif
+
 }  // namespace base
 }  // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index de5f3dc..e7d4880 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -23,7 +23,9 @@
 #include <string>
 #include <thread>
 
-using namespace std::chrono_literals;
+#if !defined(_WIN32)
+using namespace std::literals;
+#endif
 
 TEST(properties, smoke) {
   android::base::SetProperty("debug.libbase.property_test", "hello");
@@ -126,6 +128,7 @@
 TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
 
 TEST(properties, WaitForProperty) {
+#if defined(__BIONIC__)
   std::atomic<bool> flag{false};
   std::thread thread([&]() {
     std::this_thread::sleep_for(100ms);
@@ -138,9 +141,13 @@
   flag = true;
   ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
   thread.join();
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForProperty_timeout) {
+#if defined(__BIONIC__)
   auto t0 = std::chrono::steady_clock::now();
   ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
                                               200ms));
@@ -149,9 +156,13 @@
   ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
   // Upper bounds on timing are inherently flaky, but let's try...
   ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForProperty_MaxTimeout) {
+#if defined(__BIONIC__)
   std::atomic<bool> flag{false};
   std::thread thread([&]() {
     android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
@@ -165,9 +176,13 @@
   // 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();
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForProperty_NegativeTimeout) {
+#if defined(__BIONIC__)
   std::atomic<bool> flag{false};
   std::thread thread([&]() {
     android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
@@ -181,9 +196,13 @@
   // Assert that this immediately returns with a negative timeout
   ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
   thread.join();
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForPropertyCreation) {
+#if defined(__BIONIC__)
   std::thread thread([&]() {
     std::this_thread::sleep_for(100ms);
     android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
@@ -192,9 +211,13 @@
   ASSERT_TRUE(android::base::WaitForPropertyCreation(
           "debug.libbase.WaitForPropertyCreation_test", 1s));
   thread.join();
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForPropertyCreation_timeout) {
+#if defined(__BIONIC__)
   auto t0 = std::chrono::steady_clock::now();
   ASSERT_FALSE(android::base::WaitForPropertyCreation(
           "debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
@@ -203,4 +226,7 @@
   ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
   // Upper bounds on timing are inherently flaky, but let's try...
   ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
diff --git a/base/threads.cpp b/base/threads.cpp
index a71382b..48f6197 100644
--- a/base/threads.cpp
+++ b/base/threads.cpp
@@ -46,3 +46,9 @@
 
 }  // namespace base
 }  // namespace android
+
+#if defined(__GLIBC__)
+int tgkill(int tgid, int tid, int sig) {
+  return syscall(__NR_tgkill, tgid, tid, sig);
+}
+#endif
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index dd9ba88..8fc2171 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -63,7 +63,6 @@
     name: "bootstat",
     defaults: ["bootstat_defaults"],
     static_libs: ["libbootstat"],
-    shared_libs: ["liblogcat"],
     init_rc: ["bootstat.rc"],
     product_variables: {
         pdk: {
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 01b8948..71d3ecb 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -24,6 +24,7 @@
 NORMAL="${ESCAPE}[0m"
 # Best guess to an average device's reboot time, refined as tests return
 DURATION_DEFAULT=45
+STOP_ON_FAILURE=false
 
 # Helper functions
 
@@ -50,11 +51,18 @@
   fi
 }
 
+[ "USAGE: get_property <prop>
+
+Returns the property value" ]
+get_property() {
+  adb shell getprop ${1} 2>&1 </dev/null
+}
+
 [ "USAGE: isDebuggable
 
 Returns: true if device is (likely) a debug build" ]
 isDebuggable() {
-  if inAdb && [ 1 -ne `adb shell getprop ro.debuggable` ]; then
+  if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then
     false
   fi
 }
@@ -93,7 +101,7 @@
     return 1
   fi
   adb shell su root setprop persist.test.boot.reason "'${1}'" 2>/dev/null
-  test_reason="`adb shell getprop persist.test.boot.reason 2>/dev/null`"
+  test_reason="`get_property persist.test.boot.reason`"
   if [ X"${test_reason}" != X"${1}" ]; then
     echo "ERROR: can not set persist.test.boot.reason to '${1}'." >&2
     return 1
@@ -188,9 +196,9 @@
       if [ 0 != ${counter} ]; then
         adb wait-for-device </dev/null >/dev/null 2>/dev/null
       fi
-      if [ -n "`adb shell getprop sys.boot.reason </dev/null 2>/dev/null`" ]
+      if [ -n "`get_property sys.boot.reason`" ]
       then
-        vals=`adb shell getprop </dev/null 2>/dev/null |
+        vals=`get_property |
               sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
         if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
         then
@@ -223,15 +231,38 @@
   rval="${2}"
   shift 2
   if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
-    echo "ERROR: expected \"${lval}\" got \"${rval}\"" >&2
-    if [ -n "${*}" ] ; then
-      echo "       ${*}" >&2
+    if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
+*}" ]; then
+      echo "ERROR: expected \"${lval}\"" >&2
+      echo "       got \"${rval}\"" |
+        sed ': again
+             N
+             s/\(\n\)\([^ ]\)/\1             \2/
+             t again' >&2
+      if [ -n "${*}" ] ; then
+        echo "       ${*}" >&2
+      fi
+    else
+      echo "ERROR: expected \"${lval}\" got \"${rval}\" ${*}" >&2
     fi
     return 1
   fi
   if [ -n "${*}" ] ; then
     if [ X"${lval}" != X"${rval}" ]; then
-      echo "INFO: ok \"${lval}\"(=\"${rval}\") ${*}" >&2
+      if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval%
+*}" ]; then
+        echo "INFO: ok \"${lval}\"" >&2
+        echo "       = \"${rval}\"" |
+          sed ': again
+               N
+               s/\(\n\)\([^ ]\)/\1          \2/
+               t again' >&2
+        if [ -n "${*}" ] ; then
+          echo "      ${*}" >&2
+        fi
+      else
+        echo "INFO: ok \"${lval}\" = \"${rval}\" ${*}" >&2
+      fi
     else
       echo "INFO: ok \"${lval}\" ${*}" >&2
     fi
@@ -250,7 +281,7 @@
   property="${1}"
   value="${2}"
   shift 2
-  val=`adb shell getprop ${property} 2>&1`
+  val=`get_property ${property}`
   EXPECT_EQ "${value}" "${val}" for Android property ${property}
   local_ret=${?}
   if [ 0 != ${local_ret} -a "ro.boot.bootreason" = "${property}" ]; then
@@ -317,6 +348,7 @@
 init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
 init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
 init    : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init    : Command 'exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc
  (/system/bin/bootstat --record_boot_complete)'...
  (/system/bin/bootstat --record_boot_complete)' (pid${SPACE}
  (/system/bin/bootstat --record_boot_reason)'...
@@ -392,6 +424,9 @@
     echo "${GREEN}[       OK ]${NORMAL} ${TEST} ${*}"
   else
     echo "${RED}[  FAILED  ]${NORMAL} ${TEST} ${*}"
+    if ${STOP_ON_FAILURE}; then
+      exit ${save_ret}
+    fi
   fi
   return ${save_ret}
 }
@@ -445,6 +480,10 @@
     *hw_reset* )                            var="hard,hw_reset" ;;
     *usb* )                                 var="cold,charger" ;;
     *rtc* )                                 var="cold,rtc" ;;
+    *2sec_reboot* )                         var="cold,rtc,2sec" ;;
+    *wdt_by_pass_pwk* )                     var="warm" ;;
+    wdt )                                   var="reboot" ;;
+    *tool_by_pass_pwk* )                    var="reboot,tool" ;;
     *bootloader* )                          var="bootloader" ;;
     * )                                     var="reboot" ;;
   esac
@@ -458,7 +497,7 @@
 
 NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
 validate_property() {
-  val="`adb shell getprop ${1} 2>&1`"
+  val=`get_property ${1}`
   ret=`validate_reason "${val}"`
   if [ "reboot" = "${ret}" ]; then
     ret=`validate_reason "reboot,${val}"`
@@ -466,6 +505,17 @@
   echo ${ret}
 }
 
+[ "USAGE: check_boilerblate_properties
+
+Check for common property values" ]
+check_boilerplate_properties() {
+  EXPECT_PROPERTY persist.sys.boot.reason ""
+  save_ret=${?}
+  reason=`validate_property sys.boot.reason`
+  ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
+  EXPECT_PROPERTY persist.sys.boot.reason.history "${reason},[1-9][0-9]*\(\|[^0-9].*\)"
+}
+
 #
 # Actual test frames
 #
@@ -476,18 +526,20 @@
 - (wait until screen is up, boot has completed)
 - adb shell getprop ro.boot.bootreason (bootloader reason)
 - adb shell getprop persist.sys.boot.reason (last reason)
+- adb shell getprop sys.boot.reason.last (last last reason)
 - adb shell getprop sys.boot.reason (system reason)
 - NB: all should have a value that is compliant with our known set." ]
 test_properties() {
   duration_test 1
   wait_for_screen
   retval=0
-  check_set="ro.boot.bootreason persist.sys.boot.reason sys.boot.reason"
+  # sys.boot.reason is last for a reason
+  check_set="ro.boot.bootreason sys.boot.reason.last sys.boot.reason"
   bootloader=""
   # NB: this test could fail if performed _after_ optional_factory_reset test
   # and will report
   #  ERROR: expected "reboot" got ""
-  #        for Android property persist.sys.boot.reason
+  #        for Android property sys.boot.reason.last
   # following is mitigation for the persist.sys.boot.reason, skip it
   if [ "reboot,factory_reset" = "`validate_property ro.boot_bootreason`" ]; then
     check_set="ro.boot.bootreason sys.boot.reason"
@@ -497,7 +549,7 @@
     reason=`validate_property ${prop}`
     EXPECT_PROPERTY ${prop} ${reason} || retval=${?}
   done
-  # sys.boot.reason is last for a reason
+  check_boilerplate_properties || retval=${?}
   report_bootstat_logs ${reason} ${bootloader}
   return ${retval}
 }
@@ -550,7 +602,8 @@
   popd >&2
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
-  EXPECT_PROPERTY persist.sys.boot.reason bootloader
+  EXPECT_PROPERTY sys.boot.reason.last bootloader
+  check_boilerplate_properties
   report_bootstat_logs reboot,ota bootloader
 }
 
@@ -564,7 +617,8 @@
   adb reboot ota
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,ota
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,ota
+  EXPECT_PROPERTY sys.boot.reason.last reboot,ota
+  check_boilerplate_properties
   report_bootstat_logs reboot,ota
 }
 
@@ -589,9 +643,15 @@
   wait_for_screen
   bootloader_reason=`validate_property ro.boot.bootreason`
   EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason}
-  EXPECT_PROPERTY sys.boot.reason ${reason}
-  EXPECT_PROPERTY persist.sys.boot.reason ${reason}
-  report_bootstat_logs ${reason}
+  # to make sys.boot.reason report user friendly
+  reasons=${reason}
+  if [ "${bootloader_reason}" != "${reason}" -a -n "${bootloader_reason}" ]; then
+    reasons="\(${reason}\|${bootloader_reason}\)"
+  fi
+  EXPECT_PROPERTY sys.boot.reason ${reasons}
+  EXPECT_PROPERTY sys.boot.reason.last ${reason}
+  check_boilerplate_properties
+  report_bootstat_logs ${reason} ${bootloader_reason}
 }
 
 [ "USAGE: test_cold
@@ -623,7 +683,8 @@
   adb reboot >&2
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
-  EXPECT_PROPERTY persist.sys.boot.reason "reboot,.*"
+  EXPECT_PROPERTY sys.boot.reason.last "reboot,.*"
+  check_boilerplate_properties
   report_bootstat_logs reboot,factory_reset reboot, reboot,adb \
     "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
     "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date"
@@ -654,7 +715,8 @@
   wait_for_screen
   ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
-  EXPECT_PROPERTY persist.sys.boot.reason ""
+  EXPECT_PROPERTY sys.boot.reason.last ""
+  check_boilerplate_properties
   report_bootstat_logs reboot,factory_reset bootloader \
     "-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
     "-bootstat: Failed to parse boot time record: /data/misc/bootstat/last_boot_time_utc" \
@@ -727,7 +789,8 @@
     )
 
   EXPECT_PROPERTY sys.boot.reason shutdown,battery
-  EXPECT_PROPERTY persist.sys.boot.reason cold
+  EXPECT_PROPERTY sys.boot.reason.last cold
+  check_boilerplate_properties
   report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
   exitPstore
 }
@@ -748,7 +811,8 @@
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
   EXPECT_PROPERTY sys.boot.reason shutdown,battery
-  EXPECT_PROPERTY persist.sys.boot.reason shutdown,battery
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,battery
+  check_boilerplate_properties
   report_bootstat_logs shutdown,battery
 }
 
@@ -768,7 +832,8 @@
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
   EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery
-  EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal,battery
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal,battery
+  check_boilerplate_properties
   report_bootstat_logs shutdown,thermal,battery
 }
 
@@ -804,7 +869,8 @@
   echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
-  EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+  EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+  check_boilerplate_properties
   report_bootstat_logs kernel_panic,sysrq
   exitPstore
 }
@@ -831,7 +897,8 @@
   echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
-  EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+  EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+  check_boilerplate_properties
   report_bootstat_logs kernel_panic,sysrq,test \
     "-bootstat: Unknown boot reason: kernel_panic,sysrq,test"
   exitPstore
@@ -861,7 +928,8 @@
   adb reboot warm
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
-  EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+  EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+  check_boilerplate_properties
   report_bootstat_logs kernel_panic,hung
   exitPstore
 }
@@ -893,7 +961,8 @@
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
   EXPECT_PROPERTY sys.boot.reason shutdown,thermal
-  EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal
+  check_boilerplate_properties
   report_bootstat_logs shutdown,thermal
 }
 
@@ -913,7 +982,8 @@
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
   EXPECT_PROPERTY sys.boot.reason shutdown,userrequested
-  EXPECT_PROPERTY persist.sys.boot.reason shutdown,userrequested
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,userrequested
+  check_boilerplate_properties
   report_bootstat_logs shutdown,userrequested
 }
 
@@ -929,7 +999,8 @@
   adb shell reboot
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,shell
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,shell
+  EXPECT_PROPERTY sys.boot.reason.last reboot,shell
+  check_boilerplate_properties
   report_bootstat_logs reboot,shell
 }
 
@@ -945,7 +1016,8 @@
   adb reboot
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,adb
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,adb
+  EXPECT_PROPERTY sys.boot.reason.last reboot,adb
+  check_boilerplate_properties
   report_bootstat_logs reboot,adb
 }
 
@@ -980,23 +1052,8 @@
   adb shell 'reboot "Its Just So Hard"'
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
-  EXPECT_PROPERTY persist.sys.boot.reason "reboot,Its Just So Hard"
-  # Do not leave this test with an illegal value in persist.sys.boot.reason
-  save_ret=${?}           # hold on to error code from above two lines
-  if isDebuggable; then   # can do this easy, or we can do this hard.
-    adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard
-    ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
-  else
-    report_bootstat_logs reboot,its_just_so_hard  # report what we have so far
-    # user build mitigation
-    adb shell reboot its_just_so_hard
-    wait_for_screen
-    ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
-    EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
-  fi
-  # Ensure persist.sys.boot.reason now valid, failure here acts as a signal
-  # that we could choke up following tests.  For example test_properties.
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,its_just_so_hard ${flag}
+  EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard
+  check_boilerplate_properties
   report_bootstat_logs reboot,its_just_so_hard
 }
 
@@ -1045,7 +1102,8 @@
   # Check values
   EXPECT_PROPERTY ro.boot.bootreason "${bootloader_expected}"
   EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
-  EXPECT_PROPERTY persist.sys.boot.reason "${last_expected}"
+  EXPECT_PROPERTY sys.boot.reason.last "${last_expected}"
+  check_boilerplate_properties
   report_bootstat_logs "${sys_expected}"
 }
 
@@ -1099,88 +1157,98 @@
   shift 2
 fi
 
-if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
-  echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
-  echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
-  exit 0
-fi
+# Helpful for debugging, allows us to import the functions.
+if [ X"--macros" != X"${1}" ]; then
 
-# Check if all conditions for the script are sane
+  if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
+    echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
+    echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+    exit 0
+  fi
 
-if [ -z "${ANDROID_SERIAL}" ]; then
-  ndev=`(
-      adb devices | grep -v 'List of devices attached'
-      fastboot devices
-    ) |
-    grep -v "^[${SPACE}${TAB}]*\$" |
-    wc -l`
-  if [ ${ndev} -gt 1 ]; then
-    echo "ERROR: no target device specified, ${ndev} connected" >&2
-    echo "${RED}[  FAILED  ]${NORMAL}"
-    exit 1
+  if [ X"--stop" = X"${1}" ]; then
+    STOP_ON_FAILURE=true
+    shift
   fi
-  echo "WARNING: no target device specified" >&2
-fi
 
-ret=0
+  # Check if all conditions for the script are sane
 
-# Test Series
-if [ X"all" = X"${*}" ]; then
-  # automagically pick up all test_<function>s.
-  eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
-  if [ X"nothing" = X"${1}" ]; then
-    shift 1
-  fi
-fi
-if [ -z "$*" ]; then
-  # automagically pick up all test_<function>, except test_optional_<function>.
-  eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null |
-                            grep -v '^optional_'`
-  if [ -z "${2}" ]; then
-    # Hard coded should shell fail to find them above (search/permission issues)
-    eval set properties ota cold factory_reset hard battery unknown \
-             kernel_panic kernel_panic_subreason kernel_panic_hung warm \
-             thermal_shutdown userrequested_shutdown shell_reboot adb_reboot \
-             Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \
-             bootloader_kernel_panic bootloader_oem_powerkey \
-             bootloader_wdog_reset bootloader_wdog_reset bootloader_wdog_reset \
-             bootloader_hard bootloader_recovery
-  fi
-  if [ X"nothing" = X"${1}" ]; then
-    shift 1
-  fi
-fi
-echo "INFO: selected test(s): ${@}" >&2
-echo
-# Prepare device
-setBootloaderBootReason 2>/dev/null
-# Start pouring through the tests.
-failures=
-successes=
-for t in "${@}"; do
-  wrap_test ${t}
-  retval=${?}
-  if [ 0 = ${retval} ]; then
-    if [ -z "${successes}" ]; then
-      successes=${t}
-    else
-      successes="${successes} ${t}"
+  if [ -z "${ANDROID_SERIAL}" ]; then
+    ndev=`(
+        adb devices | grep -v 'List of devices attached'
+        fastboot devices
+      ) |
+      grep -v "^[${SPACE}${TAB}]*\$" |
+      wc -l`
+    if [ ${ndev} -gt 1 ]; then
+      echo "ERROR: no target device specified, ${ndev} connected" >&2
+      echo "${RED}[  FAILED  ]${NORMAL}"
+      exit 1
     fi
-  else
-    ret=${retval}
-    if [ -z "${failures}" ]; then
-      failures=${t}
-    else
-      failures="${failures} ${t}"
+    echo "WARNING: no target device specified" >&2
+  fi
+
+  ret=0
+
+  # Test Series
+  if [ X"all" = X"${*}" ]; then
+    # automagically pick up all test_<function>s.
+    eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+    if [ X"nothing" = X"${1}" ]; then
+      shift 1
     fi
   fi
+  if [ -z "$*" ]; then
+    # automagically pick up all test_<function>, except test_optional_<function>.
+    eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null |
+                              grep -v '^optional_'`
+    if [ -z "${2}" ]; then
+      # Hard coded should shell fail to find them (search/permission issues)
+      eval set properties ota cold factory_reset hard battery unknown \
+               kernel_panic kernel_panic_subreason kernel_panic_hung warm \
+               thermal_shutdown userrequested_shutdown shell_reboot adb_reboot \
+               Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \
+               bootloader_kernel_panic bootloader_oem_powerkey \
+               bootloader_wdog_reset bootloader_cold bootloader_warm \
+               bootloader_hard bootloader_recovery
+    fi
+    if [ X"nothing" = X"${1}" ]; then
+      shift 1
+    fi
+  fi
+  echo "INFO: selected test(s): ${@}" >&2
   echo
-done
+  # Prepare device
+  setBootloaderBootReason 2>/dev/null
+  # Start pouring through the tests.
+  failures=
+  successes=
+  for t in "${@}"; do
+    wrap_test ${t}
+    retval=${?}
+    if [ 0 = ${retval} ]; then
+      if [ -z "${successes}" ]; then
+        successes=${t}
+      else
+        successes="${successes} ${t}"
+      fi
+    else
+      ret=${retval}
+      if [ -z "${failures}" ]; then
+        failures=${t}
+      else
+        failures="${failures} ${t}"
+      fi
+    fi
+    echo
+  done
 
-if [ -n "${successes}" ]; then
-  echo "${GREEN}[  PASSED  ]${NORMAL} ${successes}"
+  if [ -n "${successes}" ]; then
+    echo "${GREEN}[  PASSED  ]${NORMAL} ${successes}"
+  fi
+  if [ -n "${failures}" ]; then
+    echo "${RED}[  FAILED  ]${NORMAL} ${failures}"
+  fi
+  exit ${ret}
+
 fi
-if [ -n "${failures}" ]; then
-  echo "${RED}[  FAILED  ]${NORMAL} ${failures}"
-fi
-exit ${ret}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 1b13b21..4d9f1ac 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -27,6 +27,7 @@
 #include <cstddef>
 #include <cstdio>
 #include <ctime>
+#include <iterator>
 #include <map>
 #include <memory>
 #include <regex>
@@ -43,7 +44,6 @@
 #include <android/log.h>
 #include <cutils/android_reboot.h>
 #include <cutils/properties.h>
-#include <log/logcat.h>
 #include <metricslogger/metrics_logger.h>
 
 #include "boot_event_record_store.h"
@@ -124,12 +124,12 @@
   return std::string(&temp[0], len);
 }
 
-void SetProperty(const char* key, const std::string& val) {
-  property_set(key, val.c_str());
+bool SetProperty(const char* key, const std::string& val) {
+  return property_set(key, val.c_str()) == 0;
 }
 
-void SetProperty(const char* key, const char* val) {
-  property_set(key, val);
+bool SetProperty(const char* key, const char* val) {
+  return property_set(key, val) == 0;
 }
 
 constexpr int32_t kEmptyBootReason = 0;
@@ -201,7 +201,7 @@
     {"cold", 56},
     {"hard", 57},
     {"warm", 58},
-    // {"recovery", 59},  // Duplicate of enum 3 above. Immediate reuse possible.
+    {"reboot,kernel_power_off_charging__reboot_system", 59},  // Can not happen
     {"thermal-shutdown", 60},
     {"shutdown,thermal", 61},
     {"shutdown,battery", 62},
@@ -228,7 +228,7 @@
     {"2sec_reboot", 83},
     {"reboot,by_key", 84},
     {"reboot,longkey", 85},
-    {"reboot,2sec", 86},
+    {"reboot,2sec", 86},  // Deprecate in two years, replaced with cold,rtc,2sec
     {"shutdown,thermal,battery", 87},
     {"reboot,its_just_so_hard", 88},  // produced by boot_reason_test
     {"reboot,Its Just So Hard", 89},  // produced by boot_reason_test
@@ -306,6 +306,11 @@
     {"kernel_panic,sysrq,livelock,alarm", 161},   // llkd
     {"kernel_panic,sysrq,livelock,driver", 162},  // llkd
     {"kernel_panic,sysrq,livelock,zombie", 163},  // llkd
+    {"kernel_panic,modem", 164},
+    {"kernel_panic,adsp", 165},
+    {"kernel_panic,dsps", 166},
+    {"kernel_panic,wcnss", 167},
+    {"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout", 168},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -702,6 +707,10 @@
         {"Corrupt kernel stack", "stack"},
         {"low stack detected", "stack"},
         {"corrupted stack end", "stack"},
+        {"subsys-restart: Resetting the SoC - modem crashed.", "modem"},
+        {"subsys-restart: Resetting the SoC - adsp crashed.", "adsp"},
+        {"subsys-restart: Resetting the SoC - dsps crashed.", "dsps"},
+        {"subsys-restart: Resetting the SoC - wcnss crashed.", "wcnss"},
     };
 
     ret = "kernel_panic";
@@ -726,8 +735,50 @@
 
 const char system_reboot_reason_property[] = "sys.boot.reason";
 const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
+const char last_last_reboot_reason_property[] = "sys.boot.reason.last";
+constexpr size_t history_reboot_reason_size = 4;
+const char history_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY ".history";
 const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
 
+// Land system_boot_reason into system_reboot_reason_property.
+// Shift system_boot_reason into history_reboot_reason_property.
+void BootReasonAddToHistory(const std::string& system_boot_reason) {
+  if (system_boot_reason.empty()) return;
+  LOG(INFO) << "Canonical boot reason: " << system_boot_reason;
+  auto old_system_boot_reason = GetProperty(system_reboot_reason_property);
+  if (!SetProperty(system_reboot_reason_property, system_boot_reason)) {
+    SetProperty(system_reboot_reason_property, system_boot_reason.substr(0, PROPERTY_VALUE_MAX - 1));
+  }
+  auto reason_history = android::base::Split(GetProperty(history_reboot_reason_property), "\n");
+  static auto mark = time(nullptr);
+  auto mark_str = std::string(",") + std::to_string(mark);
+  auto marked_system_boot_reason = system_boot_reason + mark_str;
+  if (!reason_history.empty()) {
+    // delete any entries that we just wrote in a previous
+    // call and leveraging duplicate line handling
+    auto last = old_system_boot_reason + mark_str;
+    // trim the list to (history_reboot_reason_size - 1)
+    ssize_t max = history_reboot_reason_size;
+    for (auto it = reason_history.begin(); it != reason_history.end();) {
+      if (it->empty() || (last == *it) || (marked_system_boot_reason == *it) || (--max <= 0)) {
+        it = reason_history.erase(it);
+      } else {
+        last = *it;
+        ++it;
+      }
+    }
+  }
+  // insert at the front, concatenating mark (<epoch time>) detail to the value.
+  reason_history.insert(reason_history.begin(), marked_system_boot_reason);
+  // If the property string is too long ( > PROPERTY_VALUE_MAX)
+  // we get an error, so trim out last entry and try again.
+  while (!(SetProperty(history_reboot_reason_property, android::base::Join(reason_history, '\n')))) {
+    auto it = std::prev(reason_history.end());
+    if (it == reason_history.end()) break;
+    reason_history.erase(it);
+  }
+}
+
 // Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
 std::string BootReasonStrToReason(const std::string& boot_reason) {
   std::string ret(GetProperty(system_reboot_reason_property));
@@ -782,7 +833,12 @@
         {"hard,hw_reset", "hw_reset"},
         {"cold,charger", "usb"},
         {"cold,rtc", "rtc"},
-        {"reboot,2sec", "2sec_reboot"},
+        {"cold,rtc,2sec", "2sec_reboot"},
+        {"!warm", "wdt_by_pass_pwk"},  // change flavour of blunt
+        {"!reboot", "^wdt$"},          // change flavour of blunt
+        {"reboot,tool", "tool_by_pass_pwk"},
+        {"!reboot,longkey", "reboot_longkey"},
+        {"!reboot,longkey", "kpdpwr"},
         {"bootloader", ""},
     };
 
@@ -842,6 +898,10 @@
             ret = "reboot," + subReason;  // legitimize unknown reasons
           }
         }
+        // Some bootloaders shutdown results record in last kernel message.
+        if (!strcmp(ret.c_str(), "reboot,kernel_power_off_charging__reboot_system")) {
+          ret = "shutdown";
+        }
       }
 
       // Check for kernel panics, allowed to override reboot command.
@@ -853,103 +913,7 @@
       }
     }
 
-    // The following battery test should migrate to a default system health HAL
-
-    // Let us not worry if the reboot command was issued, for the cases of
-    // reboot -p, reboot <no reason>, reboot cold, reboot warm and reboot hard.
-    // Same for bootloader and ro.boot.bootreasons of this set, but a dead
-    // battery could conceivably lead to these, so worthy of override.
-    if (isBluntRebootReason(ret)) {
-      // Heuristic to determine if shutdown possibly because of a dead battery?
-      // Really a hail-mary pass to find it in last klog content ...
-      static const int battery_dead_threshold = 2;  // percent
-      static const char battery[] = "healthd: battery l=";
-      const pstoreConsole console(content);
-      size_t pos = console.rfind(battery);  // last one
-      std::string digits;
-      if (pos != std::string::npos) {
-        digits = content.substr(pos + strlen(battery), strlen("100 "));
-        // correct common errors
-        correctForBitError(digits, "100 ");
-        if (digits[0] == '!') digits[0] = '1';
-        if (digits[1] == '!') digits[1] = '1';
-      }
-      const char* endptr = digits.c_str();
-      unsigned level = 0;
-      while (::isdigit(*endptr)) {
-        level *= 10;
-        level += *endptr++ - '0';
-        // make sure no leading zeros, except zero itself, and range check.
-        if ((level == 0) || (level > 100)) break;
-      }
-      // example bit error rate issues for 10%
-      //   'l=10 ' no bits in error
-      //   'l=00 ' single bit error (fails above)
-      //   'l=1  ' single bit error
-      //   'l=0  ' double bit error
-      // There are others, not typically critical because of 2%
-      // battery_dead_threshold. KISS check, make sure second
-      // character after digit sequence is not a space.
-      if ((level <= 100) && (endptr != digits.c_str()) && (endptr[0] == ' ') && (endptr[1] != ' ')) {
-        LOG(INFO) << "Battery level at shutdown " << level << "%";
-        if (level <= battery_dead_threshold) {
-          ret = "shutdown,battery";
-        }
-      } else {        // Most likely
-        digits = "";  // reset digits
-
-        // Content buffer no longer will have console data. Beware if more
-        // checks added below, that depend on parsing console content.
-        content = "";
-
-        LOG(DEBUG) << "Can not find last low battery in last console messages";
-        android_logcat_context ctx = create_android_logcat();
-        FILE* fp = android_logcat_popen(&ctx, "logcat -b kernel -v brief -d");
-        if (fp != nullptr) {
-          android::base::ReadFdToString(fileno(fp), &content);
-        }
-        android_logcat_pclose(&ctx, fp);
-        static const char logcat_battery[] = "W/healthd (    0): battery l=";
-        const char* match = logcat_battery;
-
-        if (content == "") {
-          // Service logd.klog not running, go to smaller buffer in the kernel.
-          int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
-          if (rc > 0) {
-            ssize_t len = rc + 1024;  // 1K Margin should it grow between calls.
-            std::unique_ptr<char[]> buf(new char[len]);
-            rc = klogctl(KLOG_READ_ALL, buf.get(), len);
-            if (rc < len) {
-              len = rc + 1;
-            }
-            buf[--len] = '\0';
-            content = buf.get();
-          }
-          match = battery;
-        }
-
-        pos = content.find(match);  // The first one it finds.
-        if (pos != std::string::npos) {
-          digits = content.substr(pos + strlen(match), strlen("100 "));
-        }
-        endptr = digits.c_str();
-        level = 0;
-        while (::isdigit(*endptr)) {
-          level *= 10;
-          level += *endptr++ - '0';
-          // make sure no leading zeros, except zero itself, and range check.
-          if ((level == 0) || (level > 100)) break;
-        }
-        if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
-          LOG(INFO) << "Battery level at startup " << level << "%";
-          if (level <= battery_dead_threshold) {
-            ret = "shutdown,battery";
-          }
-        } else {
-          LOG(DEBUG) << "Can not find first battery level in dmesg or logcat";
-        }
-      }
-    }
+    // TODO: use the HAL to get battery level (http://b/77725702).
 
     // Is there a controlled shutdown hint in last_reboot_reason_property?
     if (isBluntRebootReason(ret)) {
@@ -986,10 +950,6 @@
   }
 
   LOG(INFO) << "Canonical boot reason: " << ret;
-  if (isKernelRebootReason(ret) && (GetProperty(last_reboot_reason_property) != "")) {
-    // Rewrite as it must be old news, kernel reasons trump user space.
-    SetProperty(last_reboot_reason_property, ret);
-  }
   return ret;
 }
 
@@ -1014,13 +974,11 @@
   if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
     boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
-    LOG(INFO) << "Canonical boot reason: reboot,factory_reset";
-    SetProperty(system_reboot_reason_property, "reboot,factory_reset");
+    BootReasonAddToHistory("reboot,factory_reset");
   } else if (build_date != record.second) {
     boot_complete_prefix = "ota_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
-    LOG(INFO) << "Canonical boot reason: reboot,ota";
-    SetProperty(system_reboot_reason_property, "reboot,ota");
+    BootReasonAddToHistory("reboot,ota");
   }
 
   return boot_complete_prefix;
@@ -1114,6 +1072,22 @@
   return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();
 }
 
+void SetSystemBootReason() {
+  const std::string bootloader_boot_reason(GetProperty(bootloader_reboot_reason_property));
+  const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));
+  // Record the scrubbed system_boot_reason to the property
+  BootReasonAddToHistory(system_boot_reason);
+  // Shift last_reboot_reason_property to last_last_reboot_reason_property
+  std::string last_boot_reason(GetProperty(last_reboot_reason_property));
+  if (last_boot_reason.empty() || isKernelRebootReason(system_boot_reason)) {
+    last_boot_reason = system_boot_reason;
+  } else {
+    transformReason(last_boot_reason);
+  }
+  SetProperty(last_last_reboot_reason_property, last_boot_reason);
+  SetProperty(last_reboot_reason_property, "");
+}
+
 // Records several metrics related to the time it takes to boot the device,
 // including disambiguating boot time on encrypted or non-encrypted devices.
 void RecordBootComplete() {
@@ -1193,12 +1167,10 @@
   boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
 
   // Log the scrubbed system_boot_reason.
-  const std::string system_reason(BootReasonStrToReason(reason));
+  const std::string system_reason(GetProperty(system_reboot_reason_property));
   int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
   boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
 
-  // Record the scrubbed system_boot_reason to the property
-  SetProperty(system_reboot_reason_property, system_reason);
   if (reason == "") {
     SetProperty(bootloader_reboot_reason_property, system_reason);
   }
@@ -1264,20 +1236,22 @@
 
   int option_index = 0;
   static const char value_str[] = "value";
+  static const char system_boot_reason_str[] = "set_system_boot_reason";
   static const char boot_complete_str[] = "record_boot_complete";
   static const char boot_reason_str[] = "record_boot_reason";
   static const char factory_reset_str[] = "record_time_since_factory_reset";
   static const struct option long_options[] = {
       // clang-format off
-      { "help",            no_argument,       NULL,   'h' },
-      { "log",             no_argument,       NULL,   'l' },
-      { "print",           no_argument,       NULL,   'p' },
-      { "record",          required_argument, NULL,   'r' },
-      { value_str,         required_argument, NULL,   0 },
-      { boot_complete_str, no_argument,       NULL,   0 },
-      { boot_reason_str,   no_argument,       NULL,   0 },
-      { factory_reset_str, no_argument,       NULL,   0 },
-      { NULL,              0,                 NULL,   0 }
+      { "help",                 no_argument,       NULL,   'h' },
+      { "log",                  no_argument,       NULL,   'l' },
+      { "print",                no_argument,       NULL,   'p' },
+      { "record",               required_argument, NULL,   'r' },
+      { value_str,              required_argument, NULL,   0 },
+      { system_boot_reason_str, no_argument,       NULL,   0 },
+      { boot_complete_str,      no_argument,       NULL,   0 },
+      { boot_reason_str,        no_argument,       NULL,   0 },
+      { factory_reset_str,      no_argument,       NULL,   0 },
+      { NULL,                   0,                 NULL,   0 }
       // clang-format on
   };
 
@@ -1293,6 +1267,8 @@
           // |optarg| is an external variable set by getopt representing
           // the option argument.
           value = optarg;
+        } else if (option_name == system_boot_reason_str) {
+          SetSystemBootReason();
         } else if (option_name == boot_complete_str) {
           RecordBootComplete();
         } else if (option_name == boot_reason_str) {
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index f06a38f..1300a27 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -68,8 +68,9 @@
 
 # Record boot complete metrics.
 on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
+    # Converts bootloader boot reason to system boot reason
     # Record boot_complete and related stats (decryption, etc).
     # Record the boot reason.
     # Record time since factory reset.
     # Log all boot events.
-    exec_background - system log -- /system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
+    exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 7c28b28..1b20157 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -17,6 +17,7 @@
 cc_library_headers {
     name: "libdebuggerd_common_headers",
     export_include_dirs: ["common/include"],
+    recovery_available: true,
 }
 
 cc_library_shared {
@@ -46,6 +47,7 @@
 cc_library_static {
     name: "libtombstoned_client_static",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
     srcs: [
         "tombstoned/tombstoned_client.cpp",
         "util.cpp",
@@ -67,6 +69,7 @@
 cc_library_static {
     name: "libdebuggerd_handler_core",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
     srcs: ["handler/debuggerd_handler.cpp"],
 
     header_libs: [
@@ -101,6 +104,7 @@
 cc_library_static {
     name: "libdebuggerd_handler_fallback",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
     srcs: [
         "handler/debuggerd_fallback.cpp",
     ],
@@ -117,6 +121,12 @@
         "liblzma",
         "libcutils",
     ],
+    target: {
+        recovery: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            exclude_static_libs: ["libdexfile"],
+        },
+    },
 
     export_include_dirs: ["include"],
 }
@@ -143,6 +153,7 @@
 cc_library_static {
     name: "libdebuggerd",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
 
     srcs: [
         "libdebuggerd/backtrace.cpp",
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 4b32b9d..f0fe1d0 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -183,6 +183,8 @@
     fprintf(stderr, "  exit                  call exit(1)\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  fortify               fail a _FORTIFY_SOURCE check\n");
+    fprintf(stderr, "  fdsan_file            close a file descriptor that's owned by a FILE*\n");
+    fprintf(stderr, "  fdsan_dir             close a file descriptor that's owned by a DIR*\n");
     fprintf(stderr, "  seccomp               fail a seccomp check\n");
 #if defined(__arm__)
     fprintf(stderr, "  kuser_helper_version  call kuser_helper_version\n");
@@ -197,6 +199,7 @@
     fprintf(stderr, "  LOG-FATAL             call libbase LOG(FATAL)\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  SIGFPE                cause a SIGFPE\n");
+    fprintf(stderr, "  SIGILL                cause a SIGILL\n");
     fprintf(stderr, "  SIGSEGV               cause a SIGSEGV at address 0x0 (synonym: crash)\n");
     fprintf(stderr, "  SIGSEGV-non-null      cause a SIGSEGV at a non-zero address\n");
     fprintf(stderr, "  SIGSEGV-unmapped      mmap/munmap a region of memory and then attempt to access it\n");
@@ -235,62 +238,78 @@
 
     // Actions.
     if (!strcasecmp(arg, "SIGSEGV-non-null")) {
-        sigsegv_non_null();
+      sigsegv_non_null();
     } else if (!strcasecmp(arg, "smash-stack")) {
-        volatile int len = 128;
-        return smash_stack(&len);
+      volatile int len = 128;
+      return smash_stack(&len);
     } else if (!strcasecmp(arg, "stack-overflow")) {
-        overflow_stack(nullptr);
+      overflow_stack(nullptr);
     } else if (!strcasecmp(arg, "nostack")) {
-        crashnostack();
+      crashnostack();
     } else if (!strcasecmp(arg, "exit")) {
-        exit(1);
+      exit(1);
     } else if (!strcasecmp(arg, "call-null")) {
       return crash_null();
     } else if (!strcasecmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
-        return crash(42);
+      return crash(42);
     } else if (!strcasecmp(arg, "abort")) {
-        maybe_abort();
+      maybe_abort();
     } else if (!strcasecmp(arg, "assert")) {
-        __assert("some_file.c", 123, "false");
+      __assert("some_file.c", 123, "false");
     } else if (!strcasecmp(arg, "assert2")) {
-        __assert2("some_file.c", 123, "some_function", "false");
+      __assert2("some_file.c", 123, "some_function", "false");
     } else if (!strcasecmp(arg, "fortify")) {
-        char buf[10];
-        __read_chk(-1, buf, 32, 10);
-        while (true) pause();
+      char buf[10];
+      __read_chk(-1, buf, 32, 10);
+      while (true) pause();
+    } else if (!strcasecmp(arg, "fdsan_file")) {
+      FILE* f = fopen("/dev/null", "r");
+      close(fileno(f));
+    } else if (!strcasecmp(arg, "fdsan_dir")) {
+      DIR* d = opendir("/dev/");
+      close(dirfd(d));
     } else if (!strcasecmp(arg, "LOG(FATAL)")) {
-        LOG(FATAL) << "hello " << 123;
+      LOG(FATAL) << "hello " << 123;
     } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL")) {
-        LOG_ALWAYS_FATAL("hello %s", "world");
+      LOG_ALWAYS_FATAL("hello %s", "world");
     } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL_IF")) {
-        LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
+      LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
     } else if (!strcasecmp(arg, "SIGFPE")) {
-        raise(SIGFPE);
-        return EXIT_SUCCESS;
+      raise(SIGFPE);
+      return EXIT_SUCCESS;
+    } else if (!strcasecmp(arg, "SIGILL")) {
+#if defined(__aarch64__)
+      __asm__ volatile(".word 0\n");
+#elif defined(__arm__)
+      __asm__ volatile(".word 0xe7f0def0\n");
+#elif defined(__i386__) || defined(__x86_64__)
+      __asm__ volatile("ud2\n");
+#else
+#error
+#endif
     } else if (!strcasecmp(arg, "SIGTRAP")) {
-        raise(SIGTRAP);
-        return EXIT_SUCCESS;
+      raise(SIGTRAP);
+      return EXIT_SUCCESS;
     } else if (!strcasecmp(arg, "fprintf-NULL")) {
-        fprintf_null();
+      fprintf_null();
     } else if (!strcasecmp(arg, "readdir-NULL")) {
-        readdir_null();
+      readdir_null();
     } else if (!strcasecmp(arg, "strlen-NULL")) {
-        return strlen_null();
+      return strlen_null();
     } else if (!strcasecmp(arg, "pthread_join-NULL")) {
-        return pthread_join(0, nullptr);
+      return pthread_join(0, nullptr);
     } else if (!strcasecmp(arg, "heap-usage")) {
-        abuse_heap();
+      abuse_heap();
     } else if (!strcasecmp(arg, "leak")) {
-        leak();
+      leak();
     } else if (!strcasecmp(arg, "SIGSEGV-unmapped")) {
-        char* map = reinterpret_cast<char*>(mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE,
-                                                 MAP_SHARED | MAP_ANONYMOUS, -1, 0));
-        munmap(map, sizeof(int));
-        map[0] = '8';
+      char* map = reinterpret_cast<char*>(
+          mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0));
+      munmap(map, sizeof(int));
+      map[0] = '8';
     } else if (!strcasecmp(arg, "seccomp")) {
-        set_system_seccomp_filter();
-        syscall(99999);
+      set_system_seccomp_filter();
+      syscall(99999);
 #if defined(__arm__)
     } else if (!strcasecmp(arg, "kuser_helper_version")) {
         return __kuser_helper_version;
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index b016e23..360ea95 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -20,6 +20,7 @@
 #include <string.h>
 
 #include <limits>
+#include <string_view>
 #include <thread>
 
 #include <android-base/file.h>
@@ -33,9 +34,10 @@
 using android::base::unique_fd;
 
 static void usage(int exit_code) {
-  fprintf(stderr, "usage: debuggerd [-b] PID\n");
+  fprintf(stderr, "usage: debuggerd [-bj] PID\n");
   fprintf(stderr, "\n");
   fprintf(stderr, "-b, --backtrace    just a backtrace rather than a full tombstone\n");
+  fprintf(stderr, "-j                 collect java traces\n");
   _exit(exit_code);
 }
 
@@ -58,8 +60,19 @@
 int main(int argc, char* argv[]) {
   if (argc <= 1) usage(0);
   if (argc > 3) usage(1);
-  if (argc == 3 && strcmp(argv[1], "-b") != 0 && strcmp(argv[1], "--backtrace") != 0) usage(1);
-  bool backtrace_only = argc == 3;
+
+  DebuggerdDumpType dump_type = kDebuggerdTombstone;
+
+  if (argc == 3) {
+    std::string_view flag = argv[1];
+    if (flag == "-b" || flag == "--backtrace") {
+      dump_type = kDebuggerdNativeBacktrace;
+    } else if (flag == "-j") {
+      dump_type = kDebuggerdJavaBacktrace;
+    } else {
+      usage(1);
+    }
+  }
 
   pid_t pid;
   if (!android::base::ParseInt(argv[argc - 1], &pid, 1, std::numeric_limits<pid_t>::max())) {
@@ -90,8 +103,7 @@
   }
 
   std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
-  if (!debuggerd_trigger_dump(pid, backtrace_only ? kDebuggerdNativeBacktrace : kDebuggerdTombstone,
-                              0, std::move(pipewrite))) {
+  if (!debuggerd_trigger_dump(pid, dump_type, 0, std::move(pipewrite))) {
     redirect_thread.join();
     errx(1, "failed to dump process %d", pid);
   }
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index c07a34a..615fb46 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -59,7 +59,16 @@
 #include "protocol.h"
 
 using android::base::Pipe;
-using android::base::unique_fd;
+
+// We muck with our fds in a 'thread' that doesn't share the same fd table.
+// Close fds in that thread with a raw close syscall instead of going through libc.
+struct FdsanBypassCloser {
+  static void Close(int fd) {
+    syscall(__NR_close, fd);
+  }
+};
+
+using unique_fd = android::base::unique_fd_impl<FdsanBypassCloser>;
 
 // see man(2) prctl, specifically the section about PR_GET_NAME
 #define MAX_TASK_NAME_LEN (16)
@@ -299,7 +308,8 @@
   debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
 
   for (int i = 0; i < 1024; ++i) {
-    close(i);
+    // Don't use close to avoid bionic's file descriptor ownership checks.
+    syscall(__NR_close, i);
   }
 
   int devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index e11be1e..0b8a936 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -102,10 +102,17 @@
   if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
 }
 
-static void dump_signal_info(log_t* log, const ThreadInfo& thread_info) {
-  char addr_desc[32]; // ", fault addr 0x1234"
+static void dump_signal_info(log_t* log, const ThreadInfo& thread_info, Memory* process_memory) {
+  char addr_desc[64];  // ", fault addr 0x1234"
   if (signal_has_si_addr(thread_info.siginfo)) {
-    snprintf(addr_desc, sizeof(addr_desc), "%p", thread_info.siginfo->si_addr);
+    void* addr = thread_info.siginfo->si_addr;
+    if (thread_info.siginfo->si_signo == SIGILL) {
+      uint32_t instruction = {};
+      process_memory->Read(reinterpret_cast<uint64_t>(addr), &instruction, sizeof(instruction));
+      snprintf(addr_desc, sizeof(addr_desc), "%p (*pc=%#08x)", addr, instruction);
+    } else {
+      snprintf(addr_desc, sizeof(addr_desc), "%p", addr);
+    }
   } else {
     snprintf(addr_desc, sizeof(addr_desc), "--------");
   }
@@ -418,7 +425,7 @@
   dump_thread_info(log, thread_info);
 
   if (thread_info.siginfo) {
-    dump_signal_info(log, thread_info);
+    dump_signal_info(log, thread_info, process_memory);
   }
 
   if (primary_thread) {
@@ -467,7 +474,7 @@
 
 static void dump_log_file(log_t* log, pid_t pid, const char* filename, unsigned int tail) {
   bool first = true;
-  struct logger_list* logger_list;
+  logger_list* logger_list;
 
   if (!log->should_retrieve_logcat) {
     return;
@@ -481,11 +488,9 @@
     return;
   }
 
-  struct log_msg log_entry;
-
   while (true) {
+    log_msg log_entry;
     ssize_t actual = android_logger_list_read(logger_list, &log_entry);
-    struct logger_entry* entry;
 
     if (actual < 0) {
       if (actual == -EINTR) {
@@ -508,8 +513,6 @@
     // high-frequency debug diagnostics should just be written to
     // the tombstone file.
 
-    entry = &log_entry.entry_v1;
-
     if (first) {
       _LOG(log, logtype::LOGS, "--------- %slog %s\n",
         tail ? "tail end of " : "", filename);
@@ -520,19 +523,8 @@
     //
     // We want to display it in the same format as "logcat -v threadtime"
     // (although in this case the pid is redundant).
-    static const char* kPrioChars = "!.VDIWEFS";
-    unsigned hdr_size = log_entry.entry.hdr_size;
-    if (!hdr_size) {
-      hdr_size = sizeof(log_entry.entry_v1);
-    }
-    if ((hdr_size < sizeof(log_entry.entry_v1)) ||
-        (hdr_size > sizeof(log_entry.entry))) {
-      continue;
-    }
-    char* msg = reinterpret_cast<char*>(log_entry.buf) + hdr_size;
-
     char timeBuf[32];
-    time_t sec = static_cast<time_t>(entry->sec);
+    time_t sec = static_cast<time_t>(log_entry.entry.sec);
     struct tm tmBuf;
     struct tm* ptm;
     ptm = localtime_r(&sec, &tmBuf);
@@ -540,17 +532,23 @@
 
     if (log_entry.id() == LOG_ID_EVENTS) {
       if (!g_eventTagMap) {
-        g_eventTagMap = android_openEventTagMap(NULL);
+        g_eventTagMap = android_openEventTagMap(nullptr);
       }
       AndroidLogEntry e;
       char buf[512];
-      android_log_processBinaryLogBuffer(entry, &e, g_eventTagMap, buf, sizeof(buf));
-      _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8.*s: %s\n",
-         timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
-         'I', (int)e.tagLen, e.tag, e.message);
+      if (android_log_processBinaryLogBuffer(&log_entry.entry_v1, &e, g_eventTagMap, buf,
+                                             sizeof(buf)) == 0) {
+        _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8.*s: %s\n", timeBuf,
+             log_entry.entry.nsec / 1000000, log_entry.entry.pid, log_entry.entry.tid, 'I',
+             (int)e.tagLen, e.tag, e.message);
+      }
       continue;
     }
 
+    char* msg = log_entry.msg();
+    if (msg == nullptr) {
+      continue;
+    }
     unsigned char prio = msg[0];
     char* tag = msg + 1;
     msg = tag + strlen(tag) + 1;
@@ -561,20 +559,21 @@
       *nl-- = '\0';
     }
 
+    static const char* kPrioChars = "!.VDIWEFS";
     char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
 
     // Look for line breaks ('\n') and display each text line
     // on a separate line, prefixed with the header, like logcat does.
     do {
       nl = strchr(msg, '\n');
-      if (nl) {
+      if (nl != nullptr) {
         *nl = '\0';
         ++nl;
       }
 
-      _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n",
-         timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
-         prioChar, tag, msg);
+      _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n", timeBuf,
+           log_entry.entry.nsec / 1000000, log_entry.entry.pid, log_entry.entry.tid, prioChar, tag,
+           msg);
     } while ((msg = nl));
   }
 
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 1f6f3c8..8bdc02f 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -287,9 +287,7 @@
     case SIGFPE: return "SIGFPE";
     case SIGILL: return "SIGILL";
     case SIGSEGV: return "SIGSEGV";
-#if defined(SIGSTKFLT)
     case SIGSTKFLT: return "SIGSTKFLT";
-#endif
     case SIGSTOP: return "SIGSTOP";
     case SIGSYS: return "SIGSYS";
     case SIGTRAP: return "SIGTRAP";
@@ -311,8 +309,14 @@
         case ILL_PRVREG: return "ILL_PRVREG";
         case ILL_COPROC: return "ILL_COPROC";
         case ILL_BADSTK: return "ILL_BADSTK";
+        case ILL_BADIADDR:
+          return "ILL_BADIADDR";
+        case __ILL_BREAK:
+          return "ILL_BREAK";
+        case __ILL_BNDMOD:
+          return "ILL_BNDMOD";
       }
-      static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
+      static_assert(NSIGILL == __ILL_BNDMOD, "missing ILL_* si_code");
       break;
     case SIGBUS:
       switch (si->si_code) {
@@ -334,36 +338,44 @@
         case FPE_FLTRES: return "FPE_FLTRES";
         case FPE_FLTINV: return "FPE_FLTINV";
         case FPE_FLTSUB: return "FPE_FLTSUB";
+        case __FPE_DECOVF:
+          return "FPE_DECOVF";
+        case __FPE_DECDIV:
+          return "FPE_DECDIV";
+        case __FPE_DECERR:
+          return "FPE_DECERR";
+        case __FPE_INVASC:
+          return "FPE_INVASC";
+        case __FPE_INVDEC:
+          return "FPE_INVDEC";
+        case FPE_FLTUNK:
+          return "FPE_FLTUNK";
+        case FPE_CONDTRAP:
+          return "FPE_CONDTRAP";
       }
-      static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
+      static_assert(NSIGFPE == FPE_CONDTRAP, "missing FPE_* si_code");
       break;
     case SIGSEGV:
       switch (si->si_code) {
         case SEGV_MAPERR: return "SEGV_MAPERR";
         case SEGV_ACCERR: return "SEGV_ACCERR";
-#if defined(SEGV_BNDERR)
         case SEGV_BNDERR: return "SEGV_BNDERR";
-#endif
-#if defined(SEGV_PKUERR)
         case SEGV_PKUERR: return "SEGV_PKUERR";
-#endif
+        case SEGV_ACCADI:
+          return "SEGV_ACCADI";
+        case SEGV_ADIDERR:
+          return "SEGV_ADIDERR";
+        case SEGV_ADIPERR:
+          return "SEGV_ADIPERR";
       }
-#if defined(SEGV_PKUERR)
-      static_assert(NSIGSEGV == SEGV_PKUERR, "missing SEGV_* si_code");
-#elif defined(SEGV_BNDERR)
-      static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code");
-#else
-      static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
-#endif
+      static_assert(NSIGSEGV == SEGV_ADIPERR, "missing SEGV_* si_code");
       break;
-#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too.
     case SIGSYS:
       switch (si->si_code) {
         case SYS_SECCOMP: return "SYS_SECCOMP";
       }
       static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
       break;
-#endif
     case SIGTRAP:
       switch (si->si_code) {
         case TRAP_BRKPT: return "TRAP_BRKPT";
diff --git a/demangle/Android.bp b/demangle/Android.bp
index cf6abfd..fd79cf8 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -36,6 +36,7 @@
     name: "libdemangle",
     defaults: ["libdemangle_defaults"],
     vendor_available: true,
+    recovery_available: true,
 
     srcs: [
         "Demangler.cpp",
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
index a7ecf37..6bee28c 100644
--- a/diagnose_usb/Android.bp
+++ b/diagnose_usb/Android.bp
@@ -2,6 +2,7 @@
     name: "libdiagnose_usb",
     cflags: ["-Wall", "-Wextra", "-Werror"],
     host_supported: true,
+    recovery_available: true,
     target: {
         windows: {
             enabled: true,
diff --git a/fastboot/constants.h b/fastboot/constants.h
new file mode 100644
index 0000000..5e7e955
--- /dev/null
+++ b/fastboot/constants.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#define FB_CMD_GETVAR "getvar"
+#define FB_CMD_DOWNLOAD "download"
+#define FB_CMD_UPLOAD "upload"
+#define FB_CMD_VERIFY "verify"
+#define FB_CMD_FLASH "flash"
+#define FB_CMD_ERASE "erase"
+#define FB_CMD_BOOT "boot"
+#define FB_CMD_SET_ACTIVE "set_active"
+#define FB_CMD_CONTINUE "continue"
+#define FB_CMD_REBOOT "reboot"
+#define FB_CMD_SHUTDOWN "shutdown"
+#define FB_CMD_REBOOT_BOOTLOADER "reboot-bootloader"
+#define FB_CMD_POWERDOWN "powerdown"
+
+#define RESPONSE_OKAY "OKAY"
+#define RESPONSE_FAIL "FAIL"
+#define RESPONSE_DATA "DATA"
+#define RESPONSE_INFO "INFO"
+
+#define FB_COMMAND_SZ 64
+#define FB_RESPONSE_SZ 64
+
+#define FB_VAR_VERSION "version"
+#define FB_VAR_VERSION_BOOTLOADER "version-bootloader"
+#define FB_VAR_VERSION_BASEBAND "version-baseband"
+#define FB_VAR_PRODUCT "product"
+#define FB_VAR_SERIALNO "serialno"
+#define FB_VAR_SECURE "secure"
+#define FB_VAR_UNLOCKED "unlocked"
+#define FB_VAR_CURRENT_SLOT "current-slot"
+#define FB_VAR_MAX_DOWNLOAD_SIZE "max-download-size"
+#define FB_VAR_HAS_SLOT "has-slot"
+#define FB_VAR_SLOT_COUNT "slot-count"
+#define FB_VAR_PARTITION_SIZE "partition-size"
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index f271d09..1087573 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -42,6 +42,7 @@
 
 #include <android-base/stringprintf.h>
 
+#include "constants.h"
 #include "transport.h"
 
 enum Op {
@@ -79,7 +80,7 @@
 static std::vector<std::unique_ptr<Action>> action_list;
 
 bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
-    std::string cmd = "getvar:" + key;
+    std::string cmd = FB_CMD_GETVAR ":" + key;
 
     char buf[FB_RESPONSE_SZ + 1];
     memset(buf, 0, sizeof(buf));
@@ -110,12 +111,12 @@
 }
 
 void fb_set_active(const std::string& slot) {
-    Action& a = queue_action(OP_COMMAND, "set_active:" + slot);
+    Action& a = queue_action(OP_COMMAND, FB_CMD_SET_ACTIVE ":" + slot);
     a.msg = "Setting current slot to '" + slot + "'";
 }
 
 void fb_queue_erase(const std::string& partition) {
-    Action& a = queue_action(OP_COMMAND, "erase:" + partition);
+    Action& a = queue_action(OP_COMMAND, FB_CMD_ERASE ":" + partition);
     a.msg = "Erasing '" + partition + "'";
 }
 
@@ -125,7 +126,7 @@
     a.size = sz;
     a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024);
 
-    Action& b = queue_action(OP_COMMAND, "flash:" + partition);
+    Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
     b.msg = "Writing '" + partition + "'";
 }
 
@@ -135,7 +136,7 @@
     a.size = sz;
     a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024);
 
-    Action& b = queue_action(OP_COMMAND, "flash:" + partition);
+    Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
     b.msg = "Writing '" + partition + "'";
 }
 
@@ -147,7 +148,7 @@
     a.msg = android::base::StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(),
                                         current, total, sz / 1024);
 
-    Action& b = queue_action(OP_COMMAND, "flash:" + partition);
+    Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
     b.msg = android::base::StringPrintf("Writing sparse '%s' %zu/%zu", partition.c_str(), current,
                                         total);
 }
@@ -223,7 +224,7 @@
 
 void fb_queue_require(const std::string& product, const std::string& var, bool invert,
                       size_t nvalues, const char** values) {
-    Action& a = queue_action(OP_QUERY, "getvar:" + var);
+    Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
     a.product = product;
     a.data = values;
     a.size = nvalues;
@@ -243,7 +244,7 @@
 }
 
 void fb_queue_display(const std::string& label, const std::string& var) {
-    Action& a = queue_action(OP_QUERY, "getvar:" + var);
+    Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
     a.data = xstrdup(label.c_str());
     a.func = cb_display;
 }
@@ -258,7 +259,7 @@
 }
 
 void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size) {
-    Action& a = queue_action(OP_QUERY, "getvar:" + var);
+    Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
     a.data = dest;
     a.size = dest_size;
     a.func = cb_save;
@@ -270,7 +271,7 @@
 }
 
 void fb_queue_reboot() {
-    Action& a = queue_action(OP_COMMAND, "reboot");
+    Action& a = queue_action(OP_COMMAND, FB_CMD_REBOOT);
     a.func = cb_do_nothing;
     a.msg = "Rebooting";
 }
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index dca3b4e..cb1d354 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -16,11 +16,11 @@
 #
 
 _fastboot() {
-    if ! type -t "$1" >/dev/null; then
+    if ! check_type "$1" >/dev/null; then
         return
     fi
 
-    if type -t _init_completion >/dev/null; then
+    if check_type _init_completion >/dev/null; then
         _init_completion || return
     fi
 
@@ -135,7 +135,7 @@
     xspec=$2
 
     # Since we're probably doing file completion here, don't add a space after.
-    if [[ $(type -t compopt) = "builtin" ]]; then
+    if [[ $(check_type compopt) == "builtin" ]]; then
         compopt -o plusdirs
         if [[ "${xspec}" == "" ]]; then
             COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -- "${cur}") )
@@ -175,7 +175,7 @@
     fi
 }
 
-if [[ $(type -t compopt) = "builtin" ]]; then
+if [[ $(check_type compopt) == "builtin" ]]; then
     complete -F _fastboot fastboot
 else
     complete -o nospace -F _fastboot fastboot
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 6493262..5aa87d9 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -316,7 +316,8 @@
 static int show_help() {
     // clang-format off
     fprintf(stdout,
-/*           1234567890123456789012345678901234567890123456789012345678901234567890123456 */
+//                    1         2         3         4         5         6         7         8
+//           12345678901234567890123456789012345678901234567890123456789012345678901234567890
             "usage: fastboot [OPTION...] COMMAND...\n"
             "\n"
             "flashing:\n"
@@ -324,8 +325,8 @@
             " flashall                   Flash all partitions from $ANDROID_PRODUCT_OUT.\n"
             "                            On A/B devices, flashed slot is set as active.\n"
             "                            Secondary images may be flashed to inactive slot.\n"
-            " flash PARTITION [FILENAME]\n"
-            "                            Flash given partition only.\n"
+            " flash PARTITION [FILENAME] Flash given partition, using the image from\n"
+            "                            $ANDROID_PRODUCT_OUT if no filename is given.\n"
             "\n"
             "basics:\n"
             " devices [-l]               List devices in bootloader (-l: with device paths).\n"
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 2935eb5..a93c0ac 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -35,6 +35,8 @@
 
 #include <bootimg.h>
 
+#include "constants.h"
+
 class Transport;
 struct sparse_file;
 
@@ -47,9 +49,6 @@
 int64_t fb_upload_data(Transport* transport, const char* outfile);
 const std::string fb_get_error();
 
-#define FB_COMMAND_SZ 64
-#define FB_RESPONSE_SZ 64
-
 /* engine.c - high level command queue engine */
 bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
 void fb_queue_flash(const std::string& partition, void* data, uint32_t sz);
@@ -78,21 +77,10 @@
 void set_verbose();
 
 // These printf-like functions are implemented in terms of vsnprintf, so they
-// use the same attribute for compile-time format string checking. On Windows,
-// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
-// in %zd and PRIu64 (and related) to be recognized by the compile-time
-// checking.
-#define FASTBOOT_FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef FASTBOOT_FORMAT_ARCHETYPE
-#define FASTBOOT_FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
+// use the same attribute for compile-time format string checking.
 void die(const char* fmt, ...) __attribute__((__noreturn__))
-__attribute__((__format__(FASTBOOT_FORMAT_ARCHETYPE, 1, 2)));
-void verbose(const char* fmt, ...) __attribute__((__format__(FASTBOOT_FORMAT_ARCHETYPE, 1, 2)));
-#undef FASTBOOT_FORMAT_ARCHETYPE
+__attribute__((__format__(__printf__, 1, 2)));
+void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
 
 /* Current product */
 extern char cur_product[FB_RESPONSE_SZ + 1];
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
index fda6f5d..e625095 100644
--- a/fastboot/protocol.cpp
+++ b/fastboot/protocol.cpp
@@ -44,6 +44,7 @@
 #include <sparse/sparse.h>
 #include <utils/FileMap.h>
 
+#include "constants.h"
 #include "fastboot.h"
 #include "transport.h"
 
@@ -68,39 +69,39 @@
         }
         status[r] = 0;
 
-        if (r < 4) {
+        if (static_cast<size_t>(r) < strlen(RESPONSE_OKAY)) {
             g_error = android::base::StringPrintf("status malformed (%d bytes)", r);
             transport->Close();
             return -1;
         }
 
-        if (!memcmp(status, "INFO", 4)) {
-            verbose("received INFO \"%s\"", status + 4);
-            fprintf(stderr, "(bootloader) %s\n", status + 4);
+        if (!memcmp(status, RESPONSE_INFO, strlen(RESPONSE_INFO))) {
+            verbose("received INFO \"%s\"", status + strlen(RESPONSE_INFO));
+            fprintf(stderr, "(bootloader) %s\n", status + strlen(RESPONSE_INFO));
             continue;
         }
 
-        if (!memcmp(status, "OKAY", 4)) {
-            verbose("received OKAY \"%s\"", status + 4);
+        if (!memcmp(status, RESPONSE_OKAY, strlen(RESPONSE_OKAY))) {
+            verbose("received OKAY \"%s\"", status + strlen(RESPONSE_OKAY));
             if (response) {
-                strcpy(response, status + 4);
+                strcpy(response, status + strlen(RESPONSE_OKAY));
             }
             return 0;
         }
 
-        if (!memcmp(status, "FAIL", 4)) {
-            verbose("received FAIL \"%s\"", status + 4);
-            if (r > 4) {
-                g_error = android::base::StringPrintf("remote: %s", status + 4);
+        if (!memcmp(status, RESPONSE_FAIL, strlen(RESPONSE_FAIL))) {
+            verbose("received FAIL \"%s\"", status + strlen(RESPONSE_FAIL));
+            if (static_cast<size_t>(r) > strlen(RESPONSE_FAIL)) {
+                g_error = android::base::StringPrintf("remote: %s", status + strlen(RESPONSE_FAIL));
             } else {
                 g_error = "remote failure";
             }
             return -1;
         }
 
-        if (!memcmp(status, "DATA", 4) && size > 0){
-            verbose("received DATA %s", status + 4);
-            uint32_t dsize = strtol(status + 4, 0, 16);
+        if (!memcmp(status, RESPONSE_DATA, strlen(RESPONSE_DATA)) && size > 0){
+            verbose("received DATA %s", status + strlen(RESPONSE_DATA));
+            uint32_t dsize = strtol(status + strlen(RESPONSE_DATA), 0, 16);
             if (dsize > size) {
                 g_error = android::base::StringPrintf("data size too large (%d)", dsize);
                 transport->Close();
@@ -247,18 +248,21 @@
 }
 
 int64_t fb_download_data(Transport* transport, const void* data, uint32_t size) {
-    std::string cmd(android::base::StringPrintf("download:%08x", size));
+    std::string cmd(android::base::StringPrintf(
+                FB_CMD_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));
+    std::string cmd(android::base::StringPrintf(
+                FB_CMD_DOWNLOAD ":" "%08x", size));
     return _command_send_fd(transport, cmd.c_str(), fd, size, 0) < 0 ? -1 : 0;
 }
 
 int64_t fb_upload_data(Transport* transport, const char* outfile) {
     // positive return value is the upload size sent by the device
-    int64_t r = _command_start(transport, "upload", std::numeric_limits<int32_t>::max(), nullptr);
+    int64_t r = _command_start(transport, FB_CMD_UPLOAD,
+            std::numeric_limits<int32_t>::max(), nullptr);
     if (r <= 0) {
         g_error = android::base::StringPrintf("command start failed (%s)", strerror(errno));
         return r;
@@ -345,7 +349,8 @@
         return -1;
     }
 
-    std::string cmd(android::base::StringPrintf("download:%08" PRIx64, size));
+    std::string cmd(android::base::StringPrintf(
+                FB_CMD_DOWNLOAD ":" "%08" PRIx64, size));
     int r = _command_start(transport, cmd, size, 0);
     if (r < 0) {
         return -1;
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 05dba15..a3ce879 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -33,11 +33,11 @@
 cc_library_static {
     name: "libfs_mgr",
     defaults: ["fs_mgr_defaults"],
+    recovery_available: true,
     export_include_dirs: ["include"],
     include_dirs: ["system/vold"],
     srcs: [
         "fs_mgr.cpp",
-        "fs_mgr_dm_ioctl.cpp",
         "fs_mgr_format.cpp",
         "fs_mgr_verity.cpp",
         "fs_mgr_avb.cpp",
@@ -55,12 +55,18 @@
         "libselinux",
         "libavb",
         "libfstab",
+        "libdm",
+        "liblp",
     ],
     export_static_lib_headers: [
         "libfstab",
+        "libdm",
+        "liblp",
     ],
     whole_static_libs: [
+        "liblp",
         "liblogwrap",
+        "libdm",
         "libfstab",
     ],
     cppflags: [
@@ -79,6 +85,7 @@
 cc_library_static {
     name: "libfstab",
     vendor_available: true,
+    recovery_available: true,
     defaults: ["fs_mgr_defaults"],
     srcs: [
         "fs_mgr_fstab.cpp",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6e9ffba..b3df811 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -31,6 +31,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <functional>
 #include <memory>
 #include <string>
 #include <thread>
@@ -50,6 +51,7 @@
 #include <ext4_utils/ext4_sb.h>
 #include <ext4_utils/ext4_utils.h>
 #include <ext4_utils/wipe.h>
+#include <libdm/dm.h>
 #include <linux/fs.h>
 #include <linux/loop.h>
 #include <linux/magic.h>
@@ -59,7 +61,6 @@
 #include "fs_mgr.h"
 #include "fs_mgr_avb.h"
 #include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
 
 #define KEY_LOC_PROP   "ro.crypto.keyfile.userdata"
 #define KEY_IN_FOOTER  "footer"
@@ -76,6 +77,8 @@
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
+using DeviceMapper = android::dm::DeviceMapper;
+
 // record fs stat
 enum FsStatFlags {
     FS_STAT_IS_EXT4 = 0x0001,
@@ -802,14 +805,9 @@
         return true;
     }
 
-    android::base::unique_fd dm_fd(open("/dev/device-mapper", O_RDONLY));
-    if (dm_fd < 0) {
-        PLOG(ERROR) << "open /dev/device-mapper failed";
-        return false;
-    }
-    struct dm_ioctl io;
+    DeviceMapper& dm = DeviceMapper::Instance();
     std::string device_name;
-    if (!fs_mgr_dm_get_device_name(&io, rec->blk_device, dm_fd, &device_name)) {
+    if (!dm.GetDmDevicePathByName(rec->blk_device, &device_name)) {
         return false;
     }
     free(rec->blk_device);
@@ -1190,27 +1188,6 @@
     return 0;
 }
 
-int fs_mgr_unmount_all(struct fstab *fstab)
-{
-    int i = 0;
-    int ret = 0;
-
-    if (!fstab) {
-        return -1;
-    }
-
-    while (fstab->recs[i].blk_device) {
-        if (umount(fstab->recs[i].mount_point)) {
-            LERROR << "Cannot unmount filesystem at "
-                   << fstab->recs[i].mount_point;
-            ret = -1;
-        }
-        i++;
-    }
-
-    return ret;
-}
-
 /* This must be called after mount_all, because the mkswap command needs to be
  * available.
  */
@@ -1380,7 +1357,7 @@
     return true;
 }
 
-bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback) {
+bool fs_mgr_update_verity_state(std::function<fs_mgr_verity_state_callback> callback) {
     if (!callback) {
         return false;
     }
@@ -1390,12 +1367,6 @@
         return false;
     }
 
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC)));
-    if (fd == -1) {
-        PERROR << "Error opening device mapper";
-        return false;
-    }
-
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
                                                                fs_mgr_free_fstab);
     if (!fstab) {
@@ -1403,8 +1374,8 @@
         return false;
     }
 
-    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
-    struct dm_ioctl* io = (struct dm_ioctl*)buffer;
+    DeviceMapper& dm = DeviceMapper::Instance();
+
     bool system_root = android::base::GetProperty("ro.build.system_root_image", "") == "true";
 
     for (int i = 0; i < fstab->num_entries; i++) {
@@ -1420,20 +1391,20 @@
             mount_point = basename(fstab->recs[i].mount_point);
         }
 
-        fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, mount_point);
+        const char* status = nullptr;
 
-        const char* status;
-        if (ioctl(fd, DM_TABLE_STATUS, io)) {
+        std::vector<DeviceMapper::TargetInfo> table;
+        if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
             if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
                 status = "V";
             } else {
                 PERROR << "Failed to query DM_TABLE_STATUS for " << mount_point.c_str();
                 continue;
             }
+        } else {
+            status = table[0].data.c_str();
         }
 
-        status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
-
         // To be consistent in vboot 1.0 and vboot 2.0 (AVB), change the mount_point
         // back to 'system' for the callback. So it has property [partition.system.verified]
         // instead of [partition.vroot.verified].
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 5a9cb65..7c6093e 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -33,11 +33,11 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <libavb/libavb.h>
+#include <libdm/dm.h>
 
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 #include "fs_mgr_priv_avb_ops.h"
-#include "fs_mgr_priv_dm_ioctl.h"
 #include "fs_mgr_priv_sha.h"
 
 static inline bool nibble_value(const char& c, uint8_t* value) {
@@ -139,38 +139,23 @@
 };
 
 std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
-    std::string cmdline;
-    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
-        PERROR << "Failed to read /proc/cmdline";
-        return nullptr;
-    }
-
     std::unique_ptr<FsManagerAvbVerifier> avb_verifier(new FsManagerAvbVerifier());
     if (!avb_verifier) {
         LERROR << "Failed to create unique_ptr<FsManagerAvbVerifier>";
         return nullptr;
     }
 
-    std::string digest;
-    std::string hash_alg;
-    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
-        std::vector<std::string> pieces = android::base::Split(entry, "=");
-        const std::string& key = pieces[0];
-        const std::string& value = pieces[1];
-
-        if (key == "androidboot.vbmeta.hash_alg") {
-            hash_alg = value;
-        } else if (key == "androidboot.vbmeta.size") {
-            if (!android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
-                return nullptr;
-            }
-        } else if (key == "androidboot.vbmeta.digest") {
-            digest = value;
-        }
+    std::string value;
+    if (!fs_mgr_get_boot_config("vbmeta.size", &value) ||
+        !android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
+        LERROR << "Invalid hash size: " << value.c_str();
+        return nullptr;
     }
 
     // Reads hash algorithm.
     size_t expected_digest_size = 0;
+    std::string hash_alg;
+    fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg);
     if (hash_alg == "sha256") {
         expected_digest_size = SHA256_DIGEST_LENGTH * 2;
         avb_verifier->hash_alg_ = kSHA256;
@@ -183,6 +168,8 @@
     }
 
     // Reads digest.
+    std::string digest;
+    fs_mgr_get_boot_config("vbmeta.digest", &digest);
     if (digest.size() != expected_digest_size) {
         LERROR << "Unexpected digest size: " << digest.size()
                << " (expected: " << expected_digest_size << ")";
@@ -231,9 +218,9 @@
 // Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
 // See the following link for more details:
 // https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
-static std::string construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
-                                          const std::string& salt, const std::string& root_digest,
-                                          const std::string& blk_device) {
+static bool construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
+                                   const std::string& salt, const std::string& root_digest,
+                                   const std::string& blk_device, android::dm::DmTable* table) {
     // Loads androidboot.veritymode from kernel cmdline.
     std::string verity_mode;
     if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
@@ -248,145 +235,56 @@
         dm_verity_mode = "ignore_corruption";
     } else if (verity_mode != "eio") {  // Default dm_verity_mode is eio.
         LERROR << "Unknown androidboot.veritymode: " << verity_mode;
-        return "";
+        return false;
     }
 
-    // dm-verity construction parameters:
-    //   <version> <dev> <hash_dev>
-    //   <data_block_size> <hash_block_size>
-    //   <num_data_blocks> <hash_start_block>
-    //   <algorithm> <digest> <salt>
-    //   [<#opt_params> <opt_params>]
-    std::ostringstream verity_table;
-    verity_table << hashtree_desc.dm_verity_version << " " << blk_device << " " << blk_device << " "
-                 << hashtree_desc.data_block_size << " " << hashtree_desc.hash_block_size << " "
-                 << hashtree_desc.image_size / hashtree_desc.data_block_size << " "
-                 << hashtree_desc.tree_offset / hashtree_desc.hash_block_size << " "
-                 << hashtree_desc.hash_algorithm << " " << root_digest << " " << salt;
+    std::ostringstream hash_algorithm;
+    hash_algorithm << hashtree_desc.hash_algorithm;
 
-    // Continued from the above optional parameters:
-    //   [<#opt_params> <opt_params>]
-    int optional_argc = 0;
-    std::ostringstream optional_args;
-
-    // dm-verity optional parameters for FEC (forward error correction):
-    //   use_fec_from_device <fec_dev>
-    //   fec_roots <num>
-    //   fec_blocks <num>
-    //   fec_start <offset>
+    android::dm::DmTargetVerity target(0, hashtree_desc.image_size / 512,
+                                       hashtree_desc.dm_verity_version, blk_device, blk_device,
+                                       hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
+                                       hashtree_desc.image_size / hashtree_desc.data_block_size,
+                                       hashtree_desc.tree_offset / hashtree_desc.hash_block_size,
+                                       hash_algorithm.str(), root_digest, salt);
     if (hashtree_desc.fec_size > 0) {
-        // Note that fec_blocks is the size that FEC covers, *NOT* the
-        // size of the FEC data. Since we use FEC for everything up until
-        // the FEC data, it's the same as the offset (fec_start).
-        optional_argc += 8;
-        // clang-format off
-        optional_args << "use_fec_from_device " << blk_device
-                      << " fec_roots " << hashtree_desc.fec_num_roots
-                      << " fec_blocks " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
-                      << " fec_start " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
-                      << " ";
-        // clang-format on
+        target.UseFec(blk_device, hashtree_desc.fec_num_roots,
+                      hashtree_desc.fec_offset / hashtree_desc.data_block_size,
+                      hashtree_desc.fec_offset / hashtree_desc.data_block_size);
     }
-
     if (!dm_verity_mode.empty()) {
-        optional_argc += 1;
-        optional_args << dm_verity_mode << " ";
+        target.SetVerityMode(dm_verity_mode);
     }
-
     // Always use ignore_zero_blocks.
-    optional_argc += 1;
-    optional_args << "ignore_zero_blocks";
+    target.IgnoreZeroBlocks();
 
-    verity_table << " " << optional_argc << " " << optional_args.str();
-    return verity_table.str();
-}
+    LINFO << "Built verity table: '" << target.GetParameterString() << "'";
 
-static bool load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name, int fd,
-                              uint64_t image_size, const std::string& verity_table) {
-    fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, dm_device_name);
-
-    // The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
-    char* buffer = (char*)io;
-
-    // Builds the dm_target_spec arguments.
-    struct dm_target_spec* dm_target = (struct dm_target_spec*)&buffer[sizeof(struct dm_ioctl)];
-    io->flags = DM_READONLY_FLAG;
-    io->target_count = 1;
-    dm_target->status = 0;
-    dm_target->sector_start = 0;
-    dm_target->length = image_size / 512;
-    strcpy(dm_target->target_type, "verity");
-
-    // Builds the verity params.
-    char* verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
-    size_t bufsize = DM_BUF_SIZE - (verity_params - buffer);
-
-    LINFO << "Loading verity table: '" << verity_table << "'";
-
-    // Copies verity_table to verity_params (including the terminating null byte).
-    if (verity_table.size() > bufsize - 1) {
-        LERROR << "Verity table size too large: " << verity_table.size()
-               << " (max allowable size: " << bufsize - 1 << ")";
-        return false;
-    }
-    memcpy(verity_params, verity_table.c_str(), verity_table.size() + 1);
-
-    // Sets ext target boundary.
-    verity_params += verity_table.size() + 1;
-    verity_params = (char*)(((unsigned long)verity_params + 7) & ~7);
-    dm_target->next = verity_params - buffer;
-
-    // Sends the ioctl to load the verity table.
-    if (ioctl(fd, DM_TABLE_LOAD, io)) {
-        PERROR << "Error loading verity table";
-        return false;
-    }
-
-    return true;
+    return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
 }
 
 static bool hashtree_dm_verity_setup(struct fstab_rec* fstab_entry,
                                      const AvbHashtreeDescriptor& hashtree_desc,
                                      const std::string& salt, const std::string& root_digest,
                                      bool wait_for_verity_dev) {
-    // Gets the device mapper fd.
-    android::base::unique_fd fd(open("/dev/device-mapper", O_RDWR));
-    if (fd < 0) {
-        PERROR << "Error opening device mapper";
+    android::dm::DmTable table;
+    if (!construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device, &table) ||
+        !table.valid()) {
+        LERROR << "Failed to construct verity table.";
         return false;
     }
+    table.set_readonly(true);
 
-    // Creates the device.
-    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
-    struct dm_ioctl* io = (struct dm_ioctl*)buffer;
     const std::string mount_point(basename(fstab_entry->mount_point));
-    if (!fs_mgr_dm_create_device(io, mount_point, fd)) {
+    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+    if (!dm.CreateDevice(mount_point, table)) {
         LERROR << "Couldn't create verity device!";
         return false;
     }
 
-    // Gets the name of the device file.
-    std::string verity_blk_name;
-    if (!fs_mgr_dm_get_device_name(io, mount_point, fd, &verity_blk_name)) {
-        LERROR << "Couldn't get verity device number!";
-        return false;
-    }
-
-    std::string verity_table =
-        construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device);
-    if (verity_table.empty()) {
-        LERROR << "Failed to construct verity table.";
-        return false;
-    }
-
-    // Loads the verity mapping table.
-    if (!load_verity_table(io, mount_point, fd, hashtree_desc.image_size, verity_table)) {
-        LERROR << "Couldn't load verity table!";
-        return false;
-    }
-
-    // Activates the device.
-    if (!fs_mgr_dm_resume_table(io, mount_point, fd)) {
+    std::string dev_path;
+    if (!dm.GetDmDevicePathByName(mount_point, &dev_path)) {
+        LERROR << "Couldn't get verity device path!";
         return false;
     }
 
@@ -395,10 +293,10 @@
 
     // Updates fstab_rec->blk_device to verity device name.
     free(fstab_entry->blk_device);
-    fstab_entry->blk_device = strdup(verity_blk_name.c_str());
+    fstab_entry->blk_device = strdup(dev_path.c_str());
 
     // Makes sure we've set everything up properly.
-    if (wait_for_verity_dev && !fs_mgr_wait_for_file(verity_blk_name, 1s)) {
+    if (wait_for_verity_dev && !fs_mgr_wait_for_file(dev_path, 1s)) {
         return false;
     }
 
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 9c5d3f3..733ad55 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
+#include <algorithm>
+#include <iterator>
 #include <string>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
@@ -23,46 +26,71 @@
 
 #include "fs_mgr_priv.h"
 
-// Tries to get the given boot config value from kernel cmdline.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline) {
+    static constexpr char quote = '"';
+
+    std::vector<std::pair<std::string, std::string>> result;
+    size_t base = 0;
+    while (true) {
+        // skip quoted spans
+        auto found = base;
+        while (((found = cmdline.find_first_of(" \"", found)) != cmdline.npos) &&
+               (cmdline[found] == quote)) {
+            // unbalanced quote is ok
+            if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;
+            ++found;
+        }
+        std::string piece;
+        auto source = cmdline.substr(base, found - base);
+        std::remove_copy(source.begin(), source.end(),
+                         std::back_insert_iterator<std::string>(piece), quote);
+        auto equal_sign = piece.find('=');
+        if (equal_sign == piece.npos) {
+            if (!piece.empty()) {
+                // no difference between <key> and <key>=
+                result.emplace_back(std::move(piece), "");
+            }
+        } else {
+            result.emplace_back(piece.substr(0, equal_sign), piece.substr(equal_sign + 1));
+        }
+        if (found == cmdline.npos) break;
+        base = found + 1;
+    }
+
+    return result;
+}
+
+bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& android_key,
+                                        std::string* out_val) {
     FS_MGR_CHECK(out_val != nullptr);
 
-    std::string cmdline;
-    std::string cmdline_key("androidboot." + key);
-    if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
-        for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
-            std::vector<std::string> pieces = android::base::Split(entry, "=");
-            if (pieces.size() == 2) {
-                if (pieces[0] == cmdline_key) {
-                    *out_val = pieces[1];
-                    return true;
-                }
-            }
+    const std::string cmdline_key("androidboot." + android_key);
+    for (const auto& [key, value] : fs_mgr_parse_boot_config(cmdline)) {
+        if (key == cmdline_key) {
+            *out_val = value;
+            return true;
         }
     }
 
+    *out_val = "";
     return false;
 }
 
-// Tries to get the boot config value in properties, kernel cmdline and
-// device tree (in that order).  returns 'true' if successfully found, 'false'
-// otherwise
+// Tries to get the given boot config value from kernel cmdline.
+// Returns true if successfully found, false otherwise.
+bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
+    std::string cmdline;
+    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) return false;
+    return fs_mgr_get_boot_config_from_kernel(cmdline, key, out_val);
+}
+
+// Tries to get the boot config value in device tree, properties and
+// kernel cmdline (in that order).  Returns 'true' if successfully
+// found, 'false' otherwise.
 bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
     FS_MGR_CHECK(out_val != nullptr);
 
-    // first check if we have "ro.boot" property already
-    *out_val = android::base::GetProperty("ro.boot." + key, "");
-    if (!out_val->empty()) {
-        return true;
-    }
-
-    // fallback to kernel cmdline, properties may not be ready yet
-    if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
-        return true;
-    }
-
-    // lastly, check the device tree
+    // firstly, check the device tree
     if (is_dt_compatible()) {
         std::string file_name = get_android_dt_dir() + "/" + key;
         if (android::base::ReadFileToString(file_name, out_val)) {
@@ -73,5 +101,16 @@
         }
     }
 
+    // next, check if we have "ro.boot" property already
+    *out_val = android::base::GetProperty("ro.boot." + key, "");
+    if (!out_val->empty()) {
+        return true;
+    }
+
+    // finally, fallback to kernel cmdline, properties may not be ready yet
+    if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
+        return true;
+    }
+
     return false;
 }
diff --git a/fs_mgr/fs_mgr_dm_ioctl.cpp b/fs_mgr/fs_mgr_dm_ioctl.cpp
deleted file mode 100644
index 3a7fae4..0000000
--- a/fs_mgr/fs_mgr_dm_ioctl.cpp
+++ /dev/null
@@ -1,79 +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 <errno.h>
-#include <string.h>
-
-#include <android-base/logging.h>
-#include <sys/ioctl.h>
-
-#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
-
-void fs_mgr_dm_ioctl_init(struct dm_ioctl* io, size_t size, const std::string& name) {
-    memset(io, 0, size);
-    io->data_size = size;
-    io->data_start = sizeof(struct dm_ioctl);
-    io->version[0] = 4;
-    io->version[1] = 0;
-    io->version[2] = 0;
-    if (!name.empty()) {
-        strlcpy(io->name, name.c_str(), sizeof(io->name));
-    }
-}
-
-bool fs_mgr_dm_create_device(struct dm_ioctl* io, const std::string& name, int fd) {
-    fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
-    if (ioctl(fd, DM_DEV_CREATE, io)) {
-        PERROR << "Error creating device mapping";
-        return false;
-    }
-    return true;
-}
-
-bool fs_mgr_dm_destroy_device(struct dm_ioctl* io, const std::string& name, int fd) {
-    fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
-    if (ioctl(fd, DM_DEV_REMOVE, io)) {
-        PERROR << "Error removing device mapping";
-        return false;
-    }
-    return true;
-}
-
-bool fs_mgr_dm_get_device_name(struct dm_ioctl* io, const std::string& name, int fd,
-                               std::string* out_dev_name) {
-    FS_MGR_CHECK(out_dev_name != nullptr);
-
-    fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
-    if (ioctl(fd, DM_DEV_STATUS, io)) {
-        PERROR << "Error fetching device-mapper device number";
-        return false;
-    }
-
-    int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
-    *out_dev_name = "/dev/block/dm-" + std::to_string(dev_num);
-
-    return true;
-}
-
-bool fs_mgr_dm_resume_table(struct dm_ioctl* io, const std::string& name, int fd) {
-    fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
-    if (ioctl(fd, DM_DEV_SUSPEND, io)) {
-        PERROR << "Error activating device table";
-        return false;
-    }
-    return true;
-}
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index b2f3a68..28e910b 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -37,104 +37,71 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <liblp/reader.h>
 
 #include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
 
 namespace android {
 namespace fs_mgr {
 
-std::string LogicalPartitionExtent::Serialize() const {
-    // Note: we need to include an explicit null-terminator.
-    std::string argv =
-        android::base::StringPrintf("%s %" PRIu64, block_device_.c_str(), first_sector_);
-    argv.push_back(0);
+using DeviceMapper = android::dm::DeviceMapper;
+using DmTable = android::dm::DmTable;
+using DmTarget = android::dm::DmTarget;
+using DmTargetZero = android::dm::DmTargetZero;
+using DmTargetLinear = android::dm::DmTargetLinear;
 
-    // The kernel expects each target to be aligned.
-    size_t spec_bytes = sizeof(struct dm_target_spec) + argv.size();
-    size_t padding = ((spec_bytes + 7) & ~7) - spec_bytes;
-    for (size_t i = 0; i < padding; i++) {
-        argv.push_back(0);
-    }
-
-    struct dm_target_spec spec;
-    spec.sector_start = logical_sector_;
-    spec.length = num_sectors_;
-    spec.status = 0;
-    strcpy(spec.target_type, "linear");
-    spec.next = sizeof(struct dm_target_spec) + argv.size();
-
-    return std::string((char*)&spec, sizeof(spec)) + argv;
-}
-
-static bool LoadDmTable(int dm_fd, const LogicalPartition& partition) {
-    // Combine all dm_target_spec buffers together.
-    std::string target_string;
-    for (const auto& extent : partition.extents) {
-        target_string += extent.Serialize();
-    }
-
-    // Allocate the ioctl buffer.
-    size_t buffer_size = sizeof(struct dm_ioctl) + target_string.size();
-    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(buffer_size);
-
-    // Initialize the ioctl buffer header, then copy our target specs in.
-    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
-    fs_mgr_dm_ioctl_init(io, buffer_size, partition.name);
-    io->target_count = partition.extents.size();
-    if (partition.attributes & kPartitionReadonly) {
-        io->flags |= DM_READONLY_FLAG;
-    }
-    memcpy(io + 1, target_string.c_str(), target_string.size());
-
-    if (ioctl(dm_fd, DM_TABLE_LOAD, io)) {
-        PERROR << "Failed ioctl() on DM_TABLE_LOAD, partition " << partition.name;
-        return false;
-    }
-    return true;
-}
-
-static bool LoadTablesAndActivate(int dm_fd, const LogicalPartition& partition) {
-    if (!LoadDmTable(dm_fd, partition)) {
-        return false;
-    }
-
-    struct dm_ioctl io;
-    return fs_mgr_dm_resume_table(&io, partition.name, dm_fd);
-}
-
-static bool CreateDmDeviceForPartition(int dm_fd, const LogicalPartition& partition) {
-    struct dm_ioctl io;
-    if (!fs_mgr_dm_create_device(&io, partition.name, dm_fd)) {
-        return false;
-    }
-    if (!LoadTablesAndActivate(dm_fd, partition)) {
-        // Remove the device rather than leave it in an inactive state.
-        fs_mgr_dm_destroy_device(&io, partition.name, dm_fd);
-        return false;
-    }
-
-    LINFO << "Created device-mapper device: " << partition.name;
-    return true;
-}
-
-bool CreateLogicalPartitions(const LogicalPartitionTable& table) {
-    android::base::unique_fd dm_fd(open("/dev/device-mapper", O_RDWR));
-    if (dm_fd < 0) {
-        PLOG(ERROR) << "failed to open /dev/device-mapper";
-        return false;
-    }
-    for (const auto& partition : table.partitions) {
-        if (!CreateDmDeviceForPartition(dm_fd, partition)) {
-            LOG(ERROR) << "could not create dm-linear device for partition: " << partition.name;
+static bool CreateDmTable(const std::string& block_device, const LpMetadata& metadata,
+                          const LpMetadataPartition& partition, DmTable* table) {
+    uint64_t sector = 0;
+    for (size_t i = 0; i < partition.num_extents; i++) {
+        const auto& extent = metadata.extents[partition.first_extent_index + i];
+        std::unique_ptr<DmTarget> target;
+        switch (extent.target_type) {
+            case LP_TARGET_TYPE_ZERO:
+                target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
+                break;
+            case LP_TARGET_TYPE_LINEAR:
+                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, block_device,
+                                                          extent.target_data);
+                break;
+            default:
+                LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
+                return false;
+        }
+        if (!table->AddTarget(std::move(target))) {
             return false;
         }
+        sector += extent.num_sectors;
+    }
+    if (partition.attributes & LP_PARTITION_ATTR_READONLY) {
+        table->set_readonly(true);
     }
     return true;
 }
 
-std::unique_ptr<LogicalPartitionTable> LoadPartitionsFromDeviceTree() {
-    return nullptr;
+bool CreateLogicalPartitions(const std::string& block_device) {
+    uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+    auto metadata = ReadMetadata(block_device.c_str(), slot);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read partition table.";
+        return true;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    for (const auto& partition : metadata->partitions) {
+        DmTable table;
+        if (!CreateDmTable(block_device, *metadata.get(), partition, &table)) {
+            return false;
+        }
+        std::string name = GetPartitionName(partition);
+        if (!dm.CreateDevice(name, table)) {
+            return false;
+        }
+        std::string path;
+        dm.GetDmDevicePathByName(partition.name, &path);
+        LINFO << "Created logical partition " << name << " on device " << path;
+    }
+    return true;
 }
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index af4d6c1..a5b3fe8 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -235,41 +235,46 @@
          * If not found, the loop exits with fl[i].name being null.
          */
         for (i = 0; fl[i].name; i++) {
-            if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
+            auto name = fl[i].name;
+            auto len = strlen(name);
+            auto end = len;
+            if (name[end - 1] == '=') --end;
+            if (!strncmp(p, name, len) && (p[end] == name[end])) {
                 f |= fl[i].flag;
-                if ((fl[i].flag == MF_CRYPT) && flag_vals) {
+                if (!flag_vals) break;
+                if (p[end] != '=') break;
+                char* arg = p + end + 1;
+                auto flag = fl[i].flag;
+                if (flag == MF_CRYPT) {
                     /* The encryptable flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
+                    flag_vals->key_loc = strdup(arg);
+                } else if (flag == MF_VERIFY) {
                     /* If the verify flag is followed by an = and the
                      * location for the verity state,  get it and return it.
                      */
-                    char *start = strchr(p, '=');
-                    if (start) {
-                        flag_vals->verity_loc = strdup(start + 1);
-                    }
-                } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
+                    flag_vals->verity_loc = strdup(arg);
+                } else if (flag == MF_FORCECRYPT) {
                     /* The forceencrypt flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_FORCEFDEORFBE) && flag_vals) {
+                    flag_vals->key_loc = strdup(arg);
+                } else if (flag == MF_FORCEFDEORFBE) {
                     /* The forcefdeorfbe flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+                    flag_vals->key_loc = strdup(arg);
                     flag_vals->file_contents_mode = EM_AES_256_XTS;
                     flag_vals->file_names_mode = EM_AES_256_CTS;
-                } else if ((fl[i].flag == MF_FILEENCRYPTION) && flag_vals) {
+                } else if (flag == MF_FILEENCRYPTION) {
                     /* The fileencryption flag is followed by an = and
                      * the mode of contents encryption, then optionally a
                      * : and the mode of filenames encryption (defaults
                      * to aes-256-cts).  Get it and return it.
                      */
-                    char *mode = strchr(p, '=') + 1;
-                    char *colon = strchr(mode, ':');
+                    auto mode = arg;
+                    auto colon = strchr(mode, ':');
                     if (colon) {
                         *colon = '\0';
                     }
@@ -283,33 +288,30 @@
                     } else {
                         flag_vals->file_names_mode = EM_AES_256_CTS;
                     }
-                } else if ((fl[i].flag == MF_KEYDIRECTORY) && flag_vals) {
+                } else if (flag == MF_KEYDIRECTORY) {
                     /* The metadata flag is followed by an = and the
                      * directory for the keys.  Get it and return it.
                      */
-                    flag_vals->key_dir = strdup(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
+                    flag_vals->key_dir = strdup(arg);
+                } else if (flag == MF_LENGTH) {
                     /* The length flag is followed by an = and the
                      * size of the partition.  Get it and return it.
                      */
-                    flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
+                    flag_vals->part_length = strtoll(arg, NULL, 0);
+                } else if (flag == MF_VOLDMANAGED) {
                     /* The voldmanaged flag is followed by an = and the
                      * label, a colon and the partition number or the
                      * word "auto", e.g.
                      *   voldmanaged=sdcard:3
                      * Get and return them.
                      */
-                    char *label_start;
-                    char *label_end;
-                    char *part_start;
+                    auto label_start = arg;
+                    auto label_end = strchr(label_start, ':');
 
-                    label_start = strchr(p, '=') + 1;
-                    label_end = strchr(p, ':');
                     if (label_end) {
                         flag_vals->label = strndup(label_start,
                                                    (int) (label_end - label_start));
-                        part_start = strchr(p, ':') + 1;
+                        auto part_start = label_end + 1;
                         if (!strcmp(part_start, "auto")) {
                             flag_vals->partnum = -1;
                         } else {
@@ -318,41 +320,41 @@
                     } else {
                         LERROR << "Warning: voldmanaged= flag malformed";
                     }
-                } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
-                    flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_MAX_COMP_STREAMS) && flag_vals) {
-                    flag_vals->max_comp_streams = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
-                    int is_percent = !!strrchr(p, '%');
-                    unsigned int val = strtoll(strchr(p, '=') + 1, NULL, 0);
+                } else if (flag == MF_SWAPPRIO) {
+                    flag_vals->swap_prio = strtoll(arg, NULL, 0);
+                } else if (flag == MF_MAX_COMP_STREAMS) {
+                    flag_vals->max_comp_streams = strtoll(arg, NULL, 0);
+                } else if (flag == MF_ZRAMSIZE) {
+                    auto is_percent = !!strrchr(arg, '%');
+                    auto val = strtoll(arg, NULL, 0);
                     if (is_percent)
                         flag_vals->zram_size = calculate_zram_size(val);
                     else
                         flag_vals->zram_size = val;
-                } else if ((fl[i].flag == MF_RESERVEDSIZE) && flag_vals) {
+                } else if (flag == MF_RESERVEDSIZE) {
                     /* The reserved flag is followed by an = and the
                      * reserved size of the partition.  Get it and return it.
                      */
-                    flag_vals->reserved_size = parse_size(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_ERASEBLKSIZE) && flag_vals) {
+                    flag_vals->reserved_size = parse_size(arg);
+                } else if (flag == MF_ERASEBLKSIZE) {
                     /* The erase block size flag is followed by an = and the flash
                      * erase block size. Get it, check that it is a power of 2 and
                      * at least 4096, and return it.
                      */
-                    unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+                    auto val = strtoul(arg, NULL, 0);
                     if (val >= 4096 && (val & (val - 1)) == 0)
                         flag_vals->erase_blk_size = val;
-                } else if ((fl[i].flag == MF_LOGICALBLKSIZE) && flag_vals) {
+                } else if (flag == MF_LOGICALBLKSIZE) {
                     /* The logical block size flag is followed by an = and the flash
                      * logical block size. Get it, check that it is a power of 2 and
                      * at least 4096, and return it.
                      */
-                    unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+                    auto val = strtoul(arg, NULL, 0);
                     if (val >= 4096 && (val & (val - 1)) == 0)
                         flag_vals->logical_blk_size = val;
-                } else if ((fl[i].flag == MF_SYSFS) && flag_vals) {
+                } else if (flag == MF_SYSFS) {
                     /* The path to trigger device gc by idle-maint of vold. */
-                    flag_vals->sysfs_path = strdup(strchr(p, '=') + 1);
+                    flag_vals->sysfs_path = strdup(arg);
                 }
                 break;
             }
@@ -506,8 +508,7 @@
     return false;
 }
 
-static struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file)
-{
+static struct fstab* fs_mgr_read_fstab_file(FILE* fstab_file, bool proc_mounts) {
     int cnt, entries;
     ssize_t len;
     size_t alloc_len = 0;
@@ -607,7 +608,10 @@
             fstab->recs[cnt].fs_options = NULL;
         }
 
-        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+        // For /proc/mounts, ignore everything after mnt_freq and mnt_passno
+        if (proc_mounts) {
+            p += strlen(p);
+        } else if (!(p = strtok_r(NULL, delim, &save_ptr))) {
             LERROR << "Error parsing fs_mgr_options";
             goto err;
         }
@@ -679,7 +683,6 @@
     // We can't call fs_mgr_free_fstab because a->recs still references the
     // memory allocated by strdup.
     free(b->recs);
-    free(b->fstab_filename);
     free(b);
 
     a->num_entries = total_entries;
@@ -740,10 +743,8 @@
         return nullptr;
     }
 
-    fstab = fs_mgr_read_fstab_file(fstab_file);
-    if (fstab) {
-        fstab->fstab_filename = strdup(fstab_path);
-    } else {
+    fstab = fs_mgr_read_fstab_file(fstab_file, !strcmp("/proc/mounts", fstab_path));
+    if (!fstab) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
     }
 
@@ -770,7 +771,7 @@
         return nullptr;
     }
 
-    struct fstab *fstab = fs_mgr_read_fstab_file(fstab_file.get());
+    struct fstab* fstab = fs_mgr_read_fstab_file(fstab_file.get(), false);
     if (!fstab) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:"
                << std::endl << fstab_buf;
@@ -854,9 +855,6 @@
     /* Free the fstab_recs array created by calloc(3) */
     free(fstab->recs);
 
-    /* Free the fstab filename */
-    free(fstab->fstab_filename);
-
     /* Free fstab */
     free(fstab);
 }
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
index d98dc02..417fb38 100644
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -19,7 +19,13 @@
 
 #include <sys/cdefs.h>
 #include <string>
+#include <utility>
+#include <vector>
 
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline);
+
+bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& key,
+                                        std::string* out_val);
 bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
 bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
 
diff --git a/fs_mgr/fs_mgr_priv_dm_ioctl.h b/fs_mgr/fs_mgr_priv_dm_ioctl.h
deleted file mode 100644
index 792475d..0000000
--- a/fs_mgr/fs_mgr_priv_dm_ioctl.h
+++ /dev/null
@@ -1,34 +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.
- */
-
-#ifndef __CORE_FS_MGR_PRIV_DM_IOCTL_H
-#define __CORE_FS_MGR_PRIV_DM_IOCTL_H
-
-#include <linux/dm-ioctl.h>
-#include <string>
-
-void fs_mgr_dm_ioctl_init(struct dm_ioctl* io, size_t size, const std::string& name);
-
-bool fs_mgr_dm_create_device(struct dm_ioctl* io, const std::string& name, int fd);
-
-bool fs_mgr_dm_destroy_device(struct dm_ioctl* io, const std::string& name, int fd);
-
-bool fs_mgr_dm_get_device_name(struct dm_ioctl* io, const std::string& name, int fd,
-                               std::string* out_dev_name);
-
-bool fs_mgr_dm_resume_table(struct dm_ioctl* io, const std::string& name, int fd);
-
-#endif /* __CORE_FS_MGR_PRIV_DM_IOCTL_H */
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index fe41f8a..5fb4ebb 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -35,6 +35,7 @@
 #include <android-base/unique_fd.h>
 #include <crypto_utils/android_pubkey.h>
 #include <cutils/properties.h>
+#include <libdm/dm.h>
 #include <logwrap/logwrap.h>
 #include <openssl/obj_mac.h>
 #include <openssl/rsa.h>
@@ -44,7 +45,6 @@
 
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
 
 #define VERITY_TABLE_RSA_KEY "/verity_key"
 #define VERITY_TABLE_HASH_IDX 8
@@ -250,48 +250,27 @@
     return true;
 }
 
-static int load_verity_table(struct dm_ioctl *io, const std::string &name,
-                             uint64_t device_size, int fd,
-        const struct verity_table_params *params, format_verity_table_func format)
-{
-    char *verity_params;
-    char *buffer = (char*) io;
-    size_t bufsize;
+static int load_verity_table(android::dm::DeviceMapper& dm, const std::string& name,
+                             uint64_t device_size, const struct verity_table_params* params,
+                             format_verity_table_func format) {
+    android::dm::DmTable table;
+    table.set_readonly(true);
 
-    fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, name);
-
-    struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
-
-    // set tgt arguments
-    io->target_count = 1;
-    io->flags = DM_READONLY_FLAG;
-    tgt->status = 0;
-    tgt->sector_start = 0;
-    tgt->length = device_size / 512;
-    strcpy(tgt->target_type, "verity");
-
-    // build the verity params
-    verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
-    bufsize = DM_BUF_SIZE - (verity_params - buffer);
-
-    if (!format(verity_params, bufsize, params)) {
+    char buffer[DM_BUF_SIZE];
+    if (!format(buffer, sizeof(buffer), params)) {
         LERROR << "Failed to format verity parameters";
         return -1;
     }
 
-    LINFO << "loading verity table: '" << verity_params << "'";
-
-    // set next target boundary
-    verity_params += strlen(verity_params) + 1;
-    verity_params = (char*)(((uintptr_t)verity_params + 7) & ~7);
-    tgt->next = verity_params - buffer;
-
-    // send the ioctl to load the verity table
-    if (ioctl(fd, DM_TABLE_LOAD, io)) {
-        PERROR << "Error loading verity table";
+    android::dm::DmTargetVerityString target(0, device_size / 512, buffer);
+    if (!table.AddTarget(std::make_unique<decltype(target)>(target))) {
+        LERROR << "Failed to add verity target";
         return -1;
     }
-
+    if (!dm.CreateDevice(name, table)) {
+        LERROR << "Failed to create verity device \"" << name << "\"";
+        return -1;
+    }
     return 0;
 }
 
@@ -761,11 +740,11 @@
     struct fec_verity_metadata verity;
     struct verity_table_params params = { .table = NULL };
 
-    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
-    struct dm_ioctl *io = (struct dm_ioctl *) buffer;
     const std::string mount_point(basename(fstab->mount_point));
     bool verified_at_boot = false;
 
+    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+
     if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
             FEC_DEFAULT_ROOTS) < 0) {
         PERROR << "Failed to open '" << fstab->blk_device << "'";
@@ -798,24 +777,6 @@
 
     params.ecc_dev = fstab->blk_device;
 
-    // get the device mapper fd
-    if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
-        PERROR << "Error opening device mapper";
-        goto out;
-    }
-
-    // create the device
-    if (!fs_mgr_dm_create_device(io, mount_point, fd)) {
-        LERROR << "Couldn't create verity device!";
-        goto out;
-    }
-
-    // get the name of the device file
-    if (!fs_mgr_dm_get_device_name(io, mount_point, fd, &verity_blk_name)) {
-        LERROR << "Couldn't get verity device number!";
-        goto out;
-    }
-
     if (load_verity_state(fstab, &params.mode) < 0) {
         /* if accessing or updating the state failed, switch to the default
          * safe mode. This makes sure the device won't end up in an endless
@@ -861,8 +822,7 @@
                                    fstab->fs_mgr_flags & MF_SLOTSELECT);
 
     // load the verity mapping table
-    if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
-            format_verity_table) == 0) {
+    if (load_verity_table(dm, mount_point, verity.data_size, &params, format_verity_table) == 0) {
         goto loaded;
     }
 
@@ -871,15 +831,14 @@
         LINFO << "Disabling error correction for " << mount_point.c_str();
         params.ecc.valid = false;
 
-        if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
-                format_verity_table) == 0) {
+        if (load_verity_table(dm, mount_point, verity.data_size, &params, format_verity_table) == 0) {
             goto loaded;
         }
     }
 
     // try the legacy format for backwards compatibility
-    if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
-            format_legacy_verity_table) == 0) {
+    if (load_verity_table(dm, mount_point, verity.data_size, &params, format_legacy_verity_table) ==
+        0) {
         goto loaded;
     }
 
@@ -888,8 +847,8 @@
         LINFO << "Falling back to EIO mode for " << mount_point.c_str();
         params.mode = VERITY_MODE_EIO;
 
-        if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
-                format_legacy_verity_table) == 0) {
+        if (load_verity_table(dm, mount_point, verity.data_size, &params,
+                              format_legacy_verity_table) == 0) {
             goto loaded;
         }
     }
@@ -898,9 +857,8 @@
     goto out;
 
 loaded:
-
-    // activate the device
-    if (!fs_mgr_dm_resume_table(io, mount_point, fd)) {
+    if (!dm.GetDmDevicePathByName(mount_point, &verity_blk_name)) {
+        LERROR << "Couldn't get verity device number!";
         goto out;
     }
 
@@ -923,7 +881,7 @@
     if (!verified_at_boot) {
         free(fstab->blk_device);
         fstab->blk_device = strdup(verity_blk_name.c_str());
-    } else if (!fs_mgr_dm_destroy_device(io, mount_point, fd)) {
+    } else if (!dm.DeleteDevice(mount_point)) {
         LERROR << "Failed to remove verity device " << mount_point.c_str();
         goto out;
     }
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 72f019e..1049fb6 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -22,6 +22,8 @@
 #include <stdbool.h>
 #include <linux/dm-ioctl.h>
 
+#include <functional>
+
 #include <fstab/fstab.h>
 
 // Magic number at start of verity metadata
@@ -48,8 +50,8 @@
 };
 
 // Callback function for verity status
-typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab,
-        const char *mount_point, int mode, int status);
+typedef void fs_mgr_verity_state_callback(struct fstab_rec* fstab, const char* mount_point,
+                                          int mode, int status);
 
 #define FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED 7
 #define FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION 6
@@ -70,11 +72,10 @@
                     char *tmp_mount_point);
 int fs_mgr_do_mount_one(struct fstab_rec *rec);
 int fs_mgr_do_tmpfs_mount(const char *n_name);
-int fs_mgr_unmount_all(struct fstab *fstab);
 struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
 void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
 bool fs_mgr_load_verity_state(int* mode);
-bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
+bool fs_mgr_update_verity_state(std::function<fs_mgr_verity_state_callback> callback);
 int fs_mgr_swapon_all(struct fstab *fstab);
 bool fs_mgr_update_logical_partition(struct fstab_rec* rec);
 
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 7f928e5..42af9d0 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -26,72 +26,18 @@
 #define __CORE_FS_MGR_DM_LINEAR_H
 
 #include <stdint.h>
+
 #include <memory>
 #include <string>
 #include <vector>
 
+#include <libdm/dm.h>
+#include <liblp/metadata_format.h>
+
 namespace android {
 namespace fs_mgr {
 
-static const uint32_t kPartitionReadonly = 0x1;
-
-class LogicalPartitionExtent {
-  public:
-    LogicalPartitionExtent() : logical_sector_(0), first_sector_(0), num_sectors_(0) {}
-    LogicalPartitionExtent(uint64_t logical_sector, uint64_t first_sector, uint64_t num_sectors,
-                           const std::string& block_device)
-        : logical_sector_(logical_sector),
-          first_sector_(first_sector),
-          num_sectors_(num_sectors),
-          block_device_(block_device) {}
-
-    // Return a string containing the dm_target_spec buffer needed to use this
-    // extent in a device-mapper table.
-    std::string Serialize() const;
-
-    const std::string& block_device() const { return block_device_; }
-
-  private:
-    // Logical sector this extent represents in the presented block device.
-    // This is equal to the previous extent's logical sector plus the number
-    // of sectors in that extent. The first extent always starts at 0.
-    uint64_t logical_sector_;
-    // First 512-byte sector of this extent, on the source block device.
-    uint64_t first_sector_;
-    // Number of 512-byte sectors.
-    uint64_t num_sectors_;
-    // Target block device.
-    std::string block_device_;
-};
-
-struct LogicalPartition {
-    LogicalPartition() : attributes(0), num_sectors(0) {}
-
-    std::string name;
-    uint32_t attributes;
-    // Number of 512-byte sectors total.
-    uint64_t num_sectors;
-    // List of extents.
-    std::vector<LogicalPartitionExtent> extents;
-};
-
-struct LogicalPartitionTable {
-    // List of partitions in the partition table.
-    std::vector<LogicalPartition> partitions;
-};
-
-// Load a dm-linear table from the device tree if one is available; otherwise,
-// return null.
-std::unique_ptr<LogicalPartitionTable> LoadPartitionsFromDeviceTree();
-
-// Create device-mapper devices for the given partition table.
-//
-// On success, two devices nodes will be created for each partition, both
-// pointing to the same device:
-//   /dev/block/dm-<N> where N is a sequential ID assigned by device-mapper.
-//   /dev/block/dm-<name> where |name| is the partition name.
-//
-bool CreateLogicalPartitions(const LogicalPartitionTable& table);
+bool CreateLogicalPartitions(const std::string& block_device);
 
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index d232cca..b1ee328 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -33,7 +33,6 @@
 struct fstab {
     int num_entries;
     struct fstab_rec* recs;
-    char* fstab_filename;
 };
 
 struct fstab_rec {
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
new file mode 100644
index 0000000..22af123
--- /dev/null
+++ b/fs_mgr/libdm/Android.bp
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+    name: "libdm",
+    defaults: ["fs_mgr_defaults"],
+    recovery_available: true,
+
+    export_include_dirs: ["include"],
+    cflags: [
+        // TODO(b/110035986): Allows us to create a skeleton of required classes
+        "-Wno-unused-private-field",
+    ],
+
+    srcs: [
+        "dm_table.cpp",
+        "dm_target.cpp",
+        "dm.cpp",
+        "loop_control.cpp",
+    ],
+
+    header_libs: [
+        "libbase_headers",
+        "liblog_headers",
+    ],
+}
+
+cc_test {
+    name: "libdm_test",
+    defaults: ["fs_mgr_defaults"],
+    static_libs: [
+        "libdm",
+        "libbase",
+        "liblog",
+    ],
+    srcs: [
+        "dm_test.cpp",
+        "loop_control_test.cpp",
+        "test_util.cpp",
+    ]
+}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
new file mode 100644
index 0000000..ad3b6f4
--- /dev/null
+++ b/fs_mgr/libdm/dm.cpp
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libdm/dm.h"
+
+#include <sys/ioctl.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace dm {
+
+DeviceMapper& DeviceMapper::Instance() {
+    static DeviceMapper instance;
+    return instance;
+}
+// Creates a new device mapper device
+bool DeviceMapper::CreateDevice(const std::string& name) {
+    if (name.empty()) {
+        LOG(ERROR) << "Unnamed device mapper device creation is not supported";
+        return false;
+    }
+
+    if (name.size() >= DM_NAME_LEN) {
+        LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
+        return false;
+    }
+
+    struct dm_ioctl io;
+    InitIo(&io, name);
+
+    if (ioctl(fd_, DM_DEV_CREATE, &io)) {
+        PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
+        return false;
+    }
+
+    // Check to make sure the newly created device doesn't already have targets
+    // added or opened by someone
+    CHECK(io.target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
+    CHECK(io.open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
+
+    // Creates a new device mapper device with the name passed in
+    return true;
+}
+
+bool DeviceMapper::DeleteDevice(const std::string& name) {
+    if (name.empty()) {
+        LOG(ERROR) << "Unnamed device mapper device creation is not supported";
+        return false;
+    }
+
+    if (name.size() >= DM_NAME_LEN) {
+        LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
+        return false;
+    }
+
+    struct dm_ioctl io;
+    InitIo(&io, name);
+
+    if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
+        PLOG(ERROR) << "DM_DEV_REMOVE failed for [" << name << "]";
+        return false;
+    }
+
+    // Check to make sure appropriate uevent is generated so ueventd will
+    // do the right thing and remove the corresponding device node and symlinks.
+    CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
+            << "Didn't generate uevent for [" << name << "] removal";
+
+    return true;
+}
+
+const std::unique_ptr<DmTable> DeviceMapper::table(const std::string& /* name */) const {
+    // TODO(b/110035986): Return the table, as read from the kernel instead
+    return nullptr;
+}
+
+DmDeviceState DeviceMapper::state(const std::string& /* name */) const {
+    // TODO(b/110035986): Return the state, as read from the kernel instead
+    return DmDeviceState::INVALID;
+}
+
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
+    if (!CreateDevice(name)) {
+        return false;
+    }
+    if (!LoadTableAndActivate(name, table)) {
+        DeleteDevice(name);
+        return false;
+    }
+    return true;
+}
+
+bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
+    std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
+    ioctl_buffer += table.Serialize();
+
+    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
+    InitIo(io, name);
+    io->data_size = ioctl_buffer.size();
+    io->data_start = sizeof(struct dm_ioctl);
+    io->target_count = static_cast<uint32_t>(table.num_targets());
+    if (table.readonly()) {
+        io->flags |= DM_READONLY_FLAG;
+    }
+    if (ioctl(fd_, DM_TABLE_LOAD, io)) {
+        PLOG(ERROR) << "DM_TABLE_LOAD failed";
+        return false;
+    }
+
+    InitIo(io, name);
+    if (ioctl(fd_, DM_DEV_SUSPEND, io)) {
+        PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
+        return false;
+    }
+    return true;
+}
+
+// Reads all the available device mapper targets and their corresponding
+// versions from the kernel and returns in a vector
+bool DeviceMapper::GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets) {
+    targets->clear();
+
+    // calculate the space needed to read a maximum of kMaxPossibleDmTargets
+    uint32_t payload_size = sizeof(struct dm_target_versions);
+    payload_size += DM_MAX_TYPE_NAME;
+    // device mapper wants every target spec to be aligned at 8-byte boundary
+    payload_size = DM_ALIGN(payload_size);
+    payload_size *= kMaxPossibleDmTargets;
+
+    uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
+    auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
+    if (buffer == nullptr) {
+        LOG(ERROR) << "failed to allocate memory";
+        return false;
+    }
+
+    // Sets appropriate data size and data_start to make sure we tell kernel
+    // about the total size of the buffer we are passing and where to start
+    // writing the list of targets.
+    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+    InitIo(io);
+    io->data_size = data_size;
+    io->data_start = sizeof(*io);
+
+    if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
+        PLOG(ERROR) << "DM_LIST_VERSIONS failed";
+        return false;
+    }
+
+    // If the provided buffer wasn't enough to list all targets, note that
+    // any data beyond sizeof(*io) must not be read in this case
+    if (io->flags & DM_BUFFER_FULL_FLAG) {
+        LOG(INFO) << data_size << " is not enough memory to list all dm targets";
+        return false;
+    }
+
+    // if there are no targets registered, return success with empty vector
+    if (io->data_size == sizeof(*io)) {
+        return true;
+    }
+
+    // Parse each target and list the name and version
+    // TODO(b/110035986): Templatize this
+    uint32_t next = sizeof(*io);
+    data_size = io->data_size - next;
+    struct dm_target_versions* vers =
+            reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+    while (next && data_size) {
+        targets->emplace_back(vers);
+        if (vers->next == 0) {
+            break;
+        }
+        next += vers->next;
+        data_size -= vers->next;
+        vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+    }
+
+    return true;
+}
+
+bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
+    devices->clear();
+
+    // calculate the space needed to read a maximum of 256 targets, each with
+    // name with maximum length of 16 bytes
+    uint32_t payload_size = sizeof(struct dm_name_list);
+    // 128-bytes for the name
+    payload_size += DM_NAME_LEN;
+    // dm wants every device spec to be aligned at 8-byte boundary
+    payload_size = DM_ALIGN(payload_size);
+    payload_size *= kMaxPossibleDmDevices;
+    uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
+    auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
+    if (buffer == nullptr) {
+        LOG(ERROR) << "failed to allocate memory";
+        return false;
+    }
+
+    // Sets appropriate data size and data_start to make sure we tell kernel
+    // about the total size of the buffer we are passing and where to start
+    // writing the list of targets.
+    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+    InitIo(io);
+    io->data_size = data_size;
+    io->data_start = sizeof(*io);
+
+    if (ioctl(fd_, DM_LIST_DEVICES, io)) {
+        PLOG(ERROR) << "DM_LIST_DEVICES failed";
+        return false;
+    }
+
+    // If the provided buffer wasn't enough to list all devices any data
+    // beyond sizeof(*io) must not be read.
+    if (io->flags & DM_BUFFER_FULL_FLAG) {
+        LOG(INFO) << data_size << " is not enough memory to list all dm devices";
+        return false;
+    }
+
+    // if there are no devices created yet, return success with empty vector
+    if (io->data_size == sizeof(*io)) {
+        return true;
+    }
+
+    // Parse each device and add a new DmBlockDevice to the vector
+    // created from the kernel data.
+    uint32_t next = sizeof(*io);
+    data_size = io->data_size - next;
+    struct dm_name_list* dm_dev =
+            reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
+
+    while (next && data_size) {
+        devices->emplace_back((dm_dev));
+        if (dm_dev->next == 0) {
+            break;
+        }
+        next += dm_dev->next;
+        data_size -= dm_dev->next;
+        dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
+    }
+
+    return true;
+}
+
+// Accepts a device mapper device name (like system_a, vendor_b etc) and
+// returns the path to it's device node (or symlink to the device node)
+bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {
+    struct dm_ioctl io;
+    InitIo(&io, name);
+    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+        PLOG(ERROR) << "DM_DEV_STATUS failed for " << name;
+        return false;
+    }
+
+    uint32_t dev_num = minor(io.dev);
+    *path = "/dev/block/dm-" + std::to_string(dev_num);
+    return true;
+}
+
+bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
+    char buffer[4096];
+    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer);
+
+    InitIo(io, name);
+    io->data_size = sizeof(buffer);
+    io->data_start = sizeof(*io);
+    if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
+        PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
+        return false;
+    }
+    if (io->flags & DM_BUFFER_FULL_FLAG) {
+        PLOG(ERROR) << "DM_TABLE_STATUS result for " << name << " was too large";
+        return false;
+    }
+
+    uint32_t cursor = io->data_start;
+    uint32_t data_end = std::min(io->data_size, uint32_t(sizeof(buffer)));
+    for (uint32_t i = 0; i < io->target_count; i++) {
+        if (cursor + sizeof(struct dm_target_spec) > data_end) {
+            break;
+        }
+        // After each dm_target_spec is a status string. spec->next is an
+        // offset from |io->data_start|, and we clamp it to the size of our
+        // buffer.
+        struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(buffer + cursor);
+        uint32_t data_offset = cursor + sizeof(dm_target_spec);
+        uint32_t next_cursor = std::min(io->data_start + spec->next, data_end);
+
+        std::string data;
+        if (next_cursor > data_offset) {
+            // Note: we use c_str() to eliminate any extra trailing 0s.
+            data = std::string(buffer + data_offset, next_cursor - data_offset).c_str();
+        }
+        table->emplace_back(*spec, data);
+        cursor = next_cursor;
+    }
+    return true;
+}
+
+// private methods of DeviceMapper
+void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
+    CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
+    memset(io, 0, sizeof(*io));
+
+    io->version[0] = DM_VERSION0;
+    io->version[1] = DM_VERSION1;
+    io->version[2] = DM_VERSION2;
+    io->data_size = sizeof(*io);
+    io->data_start = 0;
+    if (!name.empty()) {
+        strlcpy(io->name, name.c_str(), sizeof(io->name));
+    }
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
new file mode 100644
index 0000000..15c7ce1
--- /dev/null
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libdm/dm_table.h"
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+
+namespace android {
+namespace dm {
+
+bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& target) {
+    if (!target->Valid()) {
+        return false;
+    }
+    targets_.push_back(std::move(target));
+    return true;
+}
+
+bool DmTable::RemoveTarget(std::unique_ptr<DmTarget>&& /* target */) {
+    return true;
+}
+
+bool DmTable::valid() const {
+    if (targets_.empty()) {
+        LOG(ERROR) << "Device-mapper table must have at least one target.";
+        return "";
+    }
+    if (targets_[0]->start() != 0) {
+        LOG(ERROR) << "Device-mapper table must start at logical sector 0.";
+        return "";
+    }
+    return true;
+}
+
+uint64_t DmTable::num_sectors() const {
+    return valid() ? num_sectors_ : 0;
+}
+
+// Returns a string representation of the table that is ready to be passed
+// down to the kernel for loading.
+//
+// Implementation must verify there are no gaps in the table, table starts
+// with sector == 0, and iterate over each target to get its table
+// serialized.
+std::string DmTable::Serialize() const {
+    if (!valid()) {
+        return "";
+    }
+
+    std::string table;
+    for (const auto& target : targets_) {
+        table += target->Serialize();
+    }
+    return table;
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
new file mode 100644
index 0000000..20b26df
--- /dev/null
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libdm/dm_target.h"
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+
+#include <libdm/dm.h>
+
+namespace android {
+namespace dm {
+
+std::string DmTarget::Serialize() const {
+    // Create a string containing a dm_target_spec, parameter data, and an
+    // explicit null terminator.
+    std::string data(sizeof(dm_target_spec), '\0');
+    data += GetParameterString();
+    data.push_back('\0');
+
+    // The kernel expects each target to be 8-byte aligned.
+    size_t padding = DM_ALIGN(data.size()) - data.size();
+    for (size_t i = 0; i < padding; i++) {
+        data.push_back('\0');
+    }
+
+    // Finally fill in the dm_target_spec.
+    struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&data[0]);
+    spec->sector_start = start();
+    spec->length = size();
+    strlcpy(spec->target_type, name().c_str(), sizeof(spec->target_type));
+    spec->next = (uint32_t)data.size();
+    return data;
+}
+
+std::string DmTargetZero::GetParameterString() const {
+    // The zero target type has no additional parameters.
+    return "";
+}
+
+std::string DmTargetLinear::GetParameterString() const {
+    return block_device_ + " " + std::to_string(physical_sector_);
+}
+
+DmTargetVerity::DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
+                               const std::string& block_device, const std::string& hash_device,
+                               uint32_t data_block_size, uint32_t hash_block_size,
+                               uint32_t num_data_blocks, uint32_t hash_start_block,
+                               const std::string& hash_algorithm, const std::string& root_digest,
+                               const std::string& salt)
+    : DmTarget(start, length), valid_(true) {
+    base_args_ = {
+            std::to_string(version),
+            block_device,
+            hash_device,
+            std::to_string(data_block_size),
+            std::to_string(hash_block_size),
+            std::to_string(num_data_blocks),
+            std::to_string(hash_start_block),
+            hash_algorithm,
+            root_digest,
+            salt,
+    };
+}
+
+void DmTargetVerity::UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks,
+                            uint32_t start) {
+    optional_args_.emplace_back("use_fec_from_device");
+    optional_args_.emplace_back(device);
+    optional_args_.emplace_back("fec_roots");
+    optional_args_.emplace_back(std::to_string(num_roots));
+    optional_args_.emplace_back("fec_blocks");
+    optional_args_.emplace_back(std::to_string(num_blocks));
+    optional_args_.emplace_back("fec_start");
+    optional_args_.emplace_back(std::to_string(start));
+}
+
+void DmTargetVerity::SetVerityMode(const std::string& mode) {
+    if (mode != "restart_on_corruption" && mode != "ignore_corruption") {
+        LOG(ERROR) << "Unknown verity mode: " << mode;
+        valid_ = false;
+        return;
+    }
+    optional_args_.emplace_back(mode);
+}
+
+void DmTargetVerity::IgnoreZeroBlocks() {
+    optional_args_.emplace_back("ignore_zero_blocks");
+}
+
+std::string DmTargetVerity::GetParameterString() const {
+    std::string base = android::base::Join(base_args_, " ");
+    if (optional_args_.empty()) {
+        return base;
+    }
+    std::string optional = android::base::Join(optional_args_, " ");
+    return base + " " + std::to_string(optional_args_.size()) + " " + optional;
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
new file mode 100644
index 0000000..cc61917
--- /dev/null
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <ctime>
+#include <map>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <libdm/dm.h>
+#include <libdm/loop_control.h>
+#include "test_util.h"
+
+using namespace std;
+using namespace android::dm;
+using unique_fd = android::base::unique_fd;
+
+TEST(libdm, HasMinimumTargets) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+    vector<DmTargetTypeInfo> targets;
+    ASSERT_TRUE(dm.GetAvailableTargets(&targets));
+
+    map<string, DmTargetTypeInfo> by_name;
+    for (const auto& target : targets) {
+        by_name[target.name()] = target;
+    }
+
+    auto iter = by_name.find("linear");
+    EXPECT_NE(iter, by_name.end());
+}
+
+// Helper to ensure that device mapper devices are released.
+class TempDevice {
+  public:
+    TempDevice(const std::string& name, const DmTable& table)
+        : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
+        valid_ = dm_.CreateDevice(name, table);
+    }
+    TempDevice(TempDevice&& other) : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
+        other.valid_ = false;
+    }
+    ~TempDevice() {
+        if (valid_) {
+            dm_.DeleteDevice(name_);
+        }
+    }
+    bool Destroy() {
+        if (!valid_) {
+            return false;
+        }
+        valid_ = false;
+        return dm_.DeleteDevice(name_);
+    }
+    bool WaitForUdev() const {
+        auto start_time = std::chrono::steady_clock::now();
+        while (true) {
+            if (!access(path().c_str(), F_OK)) {
+                return true;
+            }
+            if (errno != ENOENT) {
+                return false;
+            }
+            std::this_thread::sleep_for(50ms);
+            std::chrono::duration elapsed = std::chrono::steady_clock::now() - start_time;
+            if (elapsed >= 5s) {
+                return false;
+            }
+        }
+    }
+    std::string path() const {
+        std::string device_path;
+        if (!dm_.GetDmDevicePathByName(name_, &device_path)) {
+            return "";
+        }
+        return device_path;
+    }
+    const std::string& name() const { return name_; }
+    bool valid() const { return valid_; }
+
+    TempDevice(const TempDevice&) = delete;
+    TempDevice& operator=(const TempDevice&) = delete;
+
+    TempDevice& operator=(TempDevice&& other) {
+        name_ = other.name_;
+        valid_ = other.valid_;
+        other.valid_ = false;
+        return *this;
+    }
+
+  private:
+    DeviceMapper& dm_;
+    std::string name_;
+    bool valid_;
+};
+
+TEST(libdm, DmLinear) {
+    unique_fd tmp1(CreateTempFile("file_1", 4096));
+    ASSERT_GE(tmp1, 0);
+    unique_fd tmp2(CreateTempFile("file_2", 4096));
+    ASSERT_GE(tmp2, 0);
+
+    // Create two different files. These will back two separate loop devices.
+    const char message1[] = "Hello! This is sector 1.";
+    const char message2[] = "Goodbye. This is sector 2.";
+    ASSERT_TRUE(android::base::WriteFully(tmp1, message1, sizeof(message1)));
+    ASSERT_TRUE(android::base::WriteFully(tmp2, message2, sizeof(message2)));
+
+    LoopDevice loop_a(tmp1);
+    ASSERT_TRUE(loop_a.valid());
+    LoopDevice loop_b(tmp2);
+    ASSERT_TRUE(loop_b.valid());
+
+    // Define a 2-sector device, with each sector mapping to the first sector
+    // of one of our loop devices.
+    DmTable table;
+    ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(0, 1, loop_a.device(), 0)));
+    ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(1, 1, loop_b.device(), 0)));
+    ASSERT_TRUE(table.valid());
+
+    TempDevice dev("libdm-test-dm-linear", table);
+    ASSERT_TRUE(dev.valid());
+    ASSERT_FALSE(dev.path().empty());
+    ASSERT_TRUE(dev.WaitForUdev());
+
+    // Note: a scope is needed to ensure that there are no open descriptors
+    // when we go to close the device.
+    {
+        unique_fd dev_fd(open(dev.path().c_str(), O_RDWR));
+        ASSERT_GE(dev_fd, 0);
+
+        // Test that each sector of our device is correctly mapped to each loop
+        // device.
+        char sector[512];
+        ASSERT_TRUE(android::base::ReadFully(dev_fd, sector, sizeof(sector)));
+        ASSERT_EQ(strncmp(sector, message1, sizeof(message1)), 0);
+        ASSERT_TRUE(android::base::ReadFully(dev_fd, sector, sizeof(sector)));
+        ASSERT_EQ(strncmp(sector, message2, sizeof(message2)), 0);
+    }
+
+    // Test GetTableStatus.
+    DeviceMapper& dm = DeviceMapper::Instance();
+    vector<DeviceMapper::TargetInfo> targets;
+    ASSERT_TRUE(dm.GetTableStatus(dev.name(), &targets));
+    ASSERT_EQ(targets.size(), 2);
+    EXPECT_EQ(strcmp(targets[0].spec.target_type, "linear"), 0);
+    EXPECT_TRUE(targets[0].data.empty());
+    EXPECT_EQ(targets[0].spec.sector_start, 0);
+    EXPECT_EQ(targets[0].spec.length, 1);
+    EXPECT_EQ(strcmp(targets[1].spec.target_type, "linear"), 0);
+    EXPECT_TRUE(targets[1].data.empty());
+    EXPECT_EQ(targets[1].spec.sector_start, 1);
+    EXPECT_EQ(targets[1].spec.length, 1);
+
+    // Normally the TestDevice destructor would delete this, but at least one
+    // test should ensure that device deletion works.
+    ASSERT_TRUE(dev.Destroy());
+}
+
+TEST(libdm, DmVerityArgsAvb2) {
+    std::string device = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a";
+    std::string algorithm = "sha1";
+    std::string digest = "4be7e823b8c40f7bd5c8ccd5123f0722c5baca21";
+    std::string salt = "cc99f81ecb9484220a003b0719ee59dcf9be7e5d";
+
+    DmTargetVerity target(0, 10000, 1, device, device, 4096, 4096, 125961, 125961, algorithm,
+                          digest, salt);
+    target.UseFec(device, 2, 126955, 126955);
+    target.SetVerityMode("restart_on_corruption");
+    target.IgnoreZeroBlocks();
+
+    // Verity table from a walleye build.
+    std::string expected =
+            "1 /dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a "
+            "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a 4096 4096 125961 125961 sha1 "
+            "4be7e823b8c40f7bd5c8ccd5123f0722c5baca21 cc99f81ecb9484220a003b0719ee59dcf9be7e5d 10 "
+            "use_fec_from_device /dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a fec_roots "
+            "2 fec_blocks 126955 fec_start 126955 restart_on_corruption ignore_zero_blocks";
+    EXPECT_EQ(target.GetParameterString(), expected);
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
new file mode 100644
index 0000000..e2bc729
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -0,0 +1,171 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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 _LIBDM_DM_H_
+#define _LIBDM_DM_H_
+
+#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <linux/kdev_t.h>
+#include <stdint.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "dm_table.h"
+
+// The minimum expected device mapper major.minor version
+#define DM_VERSION0 (4)
+#define DM_VERSION1 (0)
+#define DM_VERSION2 (0)
+
+#define DM_ALIGN_MASK (7)
+#define DM_ALIGN(x) ((x + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
+
+namespace android {
+namespace dm {
+
+enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE };
+
+class DeviceMapper final {
+  public:
+    class DmBlockDevice final {
+      public:
+        // only allow creating this with dm_name_list
+        DmBlockDevice() = delete;
+
+        explicit DmBlockDevice(struct dm_name_list* d) : name_(d->name), dev_(d->dev){};
+
+        // Returs device mapper name associated with the block device
+        const std::string& name() const { return name_; }
+
+        // Return major number for the block device
+        uint32_t Major() const { return major(dev_); }
+
+        // Return minor number for the block device
+        uint32_t Minor() const { return minor(dev_); }
+        ~DmBlockDevice() = default;
+
+      private:
+        std::string name_;
+        uint64_t dev_;
+    };
+
+    // Removes a device mapper device with the given name.
+    // Returns 'true' on success, false otherwise.
+    bool DeleteDevice(const std::string& name);
+
+    // Reads the device mapper table from the device with given anme and
+    // returns it in a DmTable object.
+    const std::unique_ptr<DmTable> table(const std::string& name) const;
+
+    // Returns the current state of the underlying device mapper device
+    // with given name.
+    // One of INVALID, SUSPENDED or ACTIVE.
+    DmDeviceState state(const std::string& name) const;
+
+    // Creates a device, loads the given table, and activates it. If the device
+    // is not able to be activated, it is destroyed, and false is returned.
+    bool CreateDevice(const std::string& name, const DmTable& table);
+
+    // Loads the device mapper table from parameter into the underlying device
+    // mapper device with given name and activate / resumes the device in the
+    // process. A device with the given name must already exist.
+    //
+    // Returns 'true' on success, false otherwise.
+    bool LoadTableAndActivate(const std::string& name, const DmTable& table);
+
+    // Returns true if a list of available device mapper targets registered in the kernel was
+    // successfully read and stored in 'targets'. Returns 'false' otherwise.
+    bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets);
+
+    // Return 'true' if it can successfully read the list of device mapper block devices
+    // currently created. 'devices' will be empty if the kernel interactions
+    // were successful and there are no block devices at the moment. Returns
+    // 'false' in case of any failure along the way.
+    bool GetAvailableDevices(std::vector<DmBlockDevice>* devices);
+
+    // Returns the path to the device mapper device node in '/dev' corresponding to
+    // 'name'. If the device does not exist, false is returned, and the path
+    // parameter is not set.
+    bool GetDmDevicePathByName(const std::string& name, std::string* path);
+
+    // The only way to create a DeviceMapper object.
+    static DeviceMapper& Instance();
+
+    ~DeviceMapper() {
+        if (fd_ != -1) {
+            ::close(fd_);
+        }
+    }
+
+    // Query the status of a table, given a device name. The output vector will
+    // contain one TargetInfo for each target in the table. If the device does
+    // not exist, or there were too many targets, the call will fail and return
+    // false.
+    struct TargetInfo {
+        struct dm_target_spec spec;
+        std::string data;
+        TargetInfo(const struct dm_target_spec& spec, const std::string& data)
+            : spec(spec), data(data) {}
+    };
+    bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);
+
+  private:
+    // Maximum possible device mapper targets registered in the kernel.
+    // This is only used to read the list of targets from kernel so we allocate
+    // a finite amount of memory. This limit is in no way enforced by the kernel.
+    static constexpr uint32_t kMaxPossibleDmTargets = 256;
+
+    // Maximum possible device mapper created block devices. Note that this is restricted by
+    // the minor numbers (that used to be 8 bits) that can be range from 0 to 2^20-1 in newer
+    // kernels. In Android systems however, we never expect these to grow beyond the artificial
+    // limit we are imposing here of 256.
+    static constexpr uint32_t kMaxPossibleDmDevices = 256;
+
+    void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
+
+    DeviceMapper() : fd_(-1) {
+        fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
+        if (fd_ < 0) {
+            PLOG(ERROR) << "Failed to open device-mapper";
+        }
+    }
+
+    // Creates a device mapper device with given name.
+    // Return 'true' on success and 'false' on failure to
+    // create OR if a device mapper device with the same name already
+    // exists.
+    bool CreateDevice(const std::string& name);
+
+    int fd_;
+    // Non-copyable & Non-movable
+    DeviceMapper(const DeviceMapper&) = delete;
+    DeviceMapper& operator=(const DeviceMapper&) = delete;
+    DeviceMapper& operator=(DeviceMapper&&) = delete;
+    DeviceMapper(DeviceMapper&&) = delete;
+};
+
+}  // namespace dm
+}  // namespace android
+
+#endif /* _LIBDM_DM_H_ */
diff --git a/fs_mgr/libdm/include/libdm/dm_table.h b/fs_mgr/libdm/include/libdm/dm_table.h
new file mode 100644
index 0000000..5c639be
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm_table.h
@@ -0,0 +1,84 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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 _LIBDM_DMTABLE_H_
+#define _LIBDM_DMTABLE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "dm_target.h"
+
+namespace android {
+namespace dm {
+
+class DmTable {
+  public:
+    DmTable() : num_sectors_(0), readonly_(false) {}
+
+    // Adds a target to the device mapper table for a range specified in the target object.
+    // The function will return 'true' if the target was successfully added and doesn't overlap with
+    // any of the existing targets in the table. Gaps are allowed. The final check, including
+    // overlaps and gaps are done before loading the table. Returns 'false' on failure.
+    bool AddTarget(std::unique_ptr<DmTarget>&& target);
+
+    // Removes a target from the table for the range specified in the target object. Returns 'false'
+    // if the target name doesn't match with the one in the table. Returns 'true' if target is
+    // successfully removed.
+    bool RemoveTarget(std::unique_ptr<DmTarget>&& target);
+
+    // Checks the table to make sure it is valid. i.e. Checks for range overlaps, range gaps
+    // and returns 'true' if the table is ready to be loaded into kernel. Returns 'false' if the
+    // table is malformed.
+    bool valid() const;
+
+    // Returns the toatl number of targets.
+    size_t num_targets() const { return targets_.size(); }
+
+    // Returns the total size represented by the table in terms of number of 512-byte sectors.
+    // NOTE: This function will overlook if there are any gaps in the targets added in the table.
+    uint64_t num_sectors() const;
+
+    // Returns the string represntation of the table that is ready to be passed into the kernel
+    // as part of the DM_TABLE_LOAD ioctl.
+    std::string Serialize() const;
+
+    void set_readonly(bool readonly) { readonly_ = readonly; }
+    bool readonly() const { return readonly_; }
+
+    ~DmTable() = default;
+
+  private:
+    // list of targets defined in this table sorted by
+    // their start and end sectors.
+    // Note: Overlapping targets MUST never be added in this list.
+    std::vector<std::unique_ptr<DmTarget>> targets_;
+
+    // Total size in terms of # of sectors, as calculated by looking at the last and the first
+    // target in 'target_'.
+    uint64_t num_sectors_;
+
+    // True if the device should be read-only; false otherwise.
+    bool readonly_;
+};
+
+}  // namespace dm
+}  // namespace android
+
+#endif /* _LIBDM_DMTABLE_H_ */
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
new file mode 100644
index 0000000..d5974f4
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -0,0 +1,150 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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 _LIBDM_DMTARGET_H_
+#define _LIBDM_DMTARGET_H_
+
+#include <linux/dm-ioctl.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace dm {
+
+class DmTargetTypeInfo {
+  public:
+    DmTargetTypeInfo() : major_(0), minor_(0), patch_(0) {}
+    DmTargetTypeInfo(const struct dm_target_versions* info)
+        : name_(info->name),
+          major_(info->version[0]),
+          minor_(info->version[1]),
+          patch_(info->version[2]) {}
+
+    const std::string& name() const { return name_; }
+    std::string version() const {
+        return std::to_string(major_) + "." + std::to_string(minor_) + "." + std::to_string(patch_);
+    }
+
+  private:
+    std::string name_;
+    uint32_t major_;
+    uint32_t minor_;
+    uint32_t patch_;
+};
+
+class DmTarget {
+  public:
+    DmTarget(uint64_t start, uint64_t length) : start_(start), length_(length) {}
+
+    virtual ~DmTarget() = default;
+
+    // Returns name of the target.
+    virtual std::string name() const = 0;
+
+    // Return the first logical sector represented by this target.
+    uint64_t start() const { return start_; }
+
+    // Returns size in number of sectors when this target is part of
+    // a DmTable, return 0 otherwise.
+    uint64_t size() const { return length_; }
+
+    // Function that converts this object to a string of arguments that can
+    // be passed to the kernel for adding this target in a table. Each target (e.g. verity, linear)
+    // must implement this, for it to be used on a device.
+    std::string Serialize() const;
+
+    virtual bool Valid() const { return true; }
+
+  protected:
+    // Get the parameter string that is passed to the end of the dm_target_spec
+    // for this target type.
+    virtual std::string GetParameterString() const = 0;
+
+  private:
+    // logical sector number start and total length (in terms of 512-byte sectors) represented
+    // by this target within a DmTable.
+    uint64_t start_, length_;
+};
+
+class DmTargetZero final : public DmTarget {
+  public:
+    DmTargetZero(uint64_t start, uint64_t length) : DmTarget(start, length) {}
+
+    std::string name() const override { return "zero"; }
+    std::string GetParameterString() const override;
+};
+
+class DmTargetLinear final : public DmTarget {
+  public:
+    DmTargetLinear(uint64_t start, uint64_t length, const std::string& block_device,
+                   uint64_t physical_sector)
+        : DmTarget(start, length), block_device_(block_device), physical_sector_(physical_sector) {}
+
+    std::string name() const override { return "linear"; }
+    std::string GetParameterString() const override;
+    const std::string& block_device() const { return block_device_; }
+
+  private:
+    std::string block_device_;
+    uint64_t physical_sector_;
+};
+
+class DmTargetVerity final : public DmTarget {
+  public:
+    DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
+                   const std::string& block_device, const std::string& hash_device,
+                   uint32_t data_block_size, uint32_t hash_block_size, uint32_t num_data_blocks,
+                   uint32_t hash_start_block, const std::string& hash_algorithm,
+                   const std::string& root_digest, const std::string& salt);
+
+    void UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks, uint32_t start);
+    void SetVerityMode(const std::string& mode);
+    void IgnoreZeroBlocks();
+
+    std::string name() const override { return "verity"; }
+    std::string GetParameterString() const override;
+    bool Valid() const override { return valid_; }
+
+  private:
+    std::vector<std::string> base_args_;
+    std::vector<std::string> optional_args_;
+    bool valid_;
+};
+
+// This is the same as DmTargetVerity, but the table may be specified as a raw
+// string. This code exists only for fs_mgr_verity and should be avoided. Use
+// DmTargetVerity for new code instead.
+class DmTargetVerityString final : public DmTarget {
+  public:
+    DmTargetVerityString(uint64_t start, uint64_t length, const std::string& target_string)
+        : DmTarget(start, length), target_string_(target_string) {}
+
+    std::string name() const override { return "verity"; }
+    std::string GetParameterString() const override { return target_string_; }
+    bool Valid() const override { return true; }
+
+  private:
+    std::string target_string_;
+};
+
+}  // namespace dm
+}  // namespace android
+
+#endif /* _LIBDM_DMTARGET_H_ */
diff --git a/fs_mgr/libdm/include/libdm/loop_control.h b/fs_mgr/libdm/include/libdm/loop_control.h
new file mode 100644
index 0000000..e6e83f4
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -0,0 +1,82 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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 _LIBDM_LOOP_CONTROL_H_
+#define _LIBDM_LOOP_CONTROL_H_
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace dm {
+
+class LoopControl final {
+  public:
+    LoopControl();
+
+    // Attaches the file specified by 'file_fd' to the loop device specified
+    // by 'loopdev'
+    bool Attach(int file_fd, std::string* loopdev) const;
+
+    // Detach the loop device given by 'loopdev' from the attached backing file.
+    bool Detach(const std::string& loopdev) const;
+
+    LoopControl(const LoopControl&) = delete;
+    LoopControl& operator=(const LoopControl&) = delete;
+    LoopControl& operator=(LoopControl&&) = default;
+    LoopControl(LoopControl&&) = default;
+
+  private:
+    bool FindFreeLoopDevice(std::string* loopdev) const;
+
+    static constexpr const char* kLoopControlDevice = "/dev/loop-control";
+
+    android::base::unique_fd control_fd_;
+};
+
+// Create a temporary loop device around a file descriptor or path.
+class LoopDevice {
+  public:
+    // Create a loop device for the given file descriptor. It is closed when
+    // LoopDevice is destroyed only if auto_close is true.
+    LoopDevice(int fd, bool auto_close = false);
+    // Create a loop device for the given file path. It will be opened for
+    // reading and writing and closed when the loop device is detached.
+    explicit LoopDevice(const std::string& path);
+    ~LoopDevice();
+
+    bool valid() const { return fd_ != -1 && !device_.empty(); }
+    const std::string& device() const { return device_; }
+
+    LoopDevice(const LoopDevice&) = delete;
+    LoopDevice& operator=(const LoopDevice&) = delete;
+    LoopDevice& operator=(LoopDevice&&) = default;
+    LoopDevice(LoopDevice&&) = default;
+
+  private:
+    void Init();
+
+    android::base::unique_fd fd_;
+    bool owns_fd_;
+    std::string device_;
+    LoopControl control_;
+};
+
+}  // namespace dm
+}  // namespace android
+
+#endif /* _LIBDM_LOOP_CONTROL_H_ */
diff --git a/fs_mgr/libdm/loop_control.cpp b/fs_mgr/libdm/loop_control.cpp
new file mode 100644
index 0000000..0beb1a6
--- /dev/null
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libdm/loop_control.h"
+
+#include <fcntl.h>
+#include <linux/loop.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace dm {
+
+LoopControl::LoopControl() : control_fd_(-1) {
+    control_fd_.reset(TEMP_FAILURE_RETRY(open(kLoopControlDevice, O_RDWR | O_CLOEXEC)));
+    if (control_fd_ < 0) {
+        PLOG(ERROR) << "Failed to open loop-control";
+    }
+}
+
+bool LoopControl::Attach(int file_fd, std::string* loopdev) const {
+    if (!FindFreeLoopDevice(loopdev)) {
+        LOG(ERROR) << "Failed to attach, no free loop devices";
+        return false;
+    }
+
+    android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC)));
+    if (loop_fd < 0) {
+        PLOG(ERROR) << "Failed to open: " << *loopdev;
+        return false;
+    }
+
+    int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd);
+    if (rc < 0) {
+        PLOG(ERROR) << "Failed LOOP_SET_FD";
+        return false;
+    }
+    return true;
+}
+
+bool LoopControl::Detach(const std::string& loopdev) const {
+    if (loopdev.empty()) {
+        LOG(ERROR) << "Must provide a loop device";
+        return false;
+    }
+
+    android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev.c_str(), O_RDWR | O_CLOEXEC)));
+    if (loop_fd < 0) {
+        PLOG(ERROR) << "Failed to open: " << loopdev;
+        return false;
+    }
+
+    int rc = ioctl(loop_fd, LOOP_CLR_FD, 0);
+    if (rc) {
+        PLOG(ERROR) << "Failed LOOP_CLR_FD for '" << loopdev << "'";
+        return false;
+    }
+    return true;
+}
+
+bool LoopControl::FindFreeLoopDevice(std::string* loopdev) const {
+    int rc = ioctl(control_fd_, LOOP_CTL_GET_FREE);
+    if (rc < 0) {
+        PLOG(ERROR) << "Failed to get free loop device";
+        return false;
+    }
+
+    // Ueventd on android creates all loop devices as /dev/block/loopX
+    // The total number of available devices is determined by 'loop.max_part'
+    // kernel command line argument.
+    *loopdev = ::android::base::StringPrintf("/dev/block/loop%d", rc);
+    return true;
+}
+
+LoopDevice::LoopDevice(int fd, bool auto_close) : fd_(fd), owns_fd_(auto_close) {
+    Init();
+}
+
+LoopDevice::LoopDevice(const std::string& path) : fd_(-1), owns_fd_(true) {
+    fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
+    if (fd_ < -1) {
+        PLOG(ERROR) << "open failed for " << path;
+        return;
+    }
+    Init();
+}
+
+LoopDevice::~LoopDevice() {
+    if (valid()) {
+        control_.Detach(device_);
+    }
+    if (!owns_fd_) {
+        (void)fd_.release();
+    }
+}
+
+void LoopDevice::Init() {
+    control_.Attach(fd_, &device_);
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/loop_control_test.cpp b/fs_mgr/libdm/loop_control_test.cpp
new file mode 100644
index 0000000..08bdc00
--- /dev/null
+++ b/fs_mgr/libdm/loop_control_test.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libdm/loop_control.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include "test_util.h"
+
+using namespace std;
+using namespace android::dm;
+using unique_fd = android::base::unique_fd;
+
+static unique_fd TempFile() {
+    // A loop device needs to be at least one sector to actually work, so fill
+    // up the file with a message.
+    unique_fd fd(CreateTempFile("temp", 0));
+    if (fd < 0) {
+        return {};
+    }
+    char buffer[] = "Hello";
+    for (size_t i = 0; i < 1000; i++) {
+        if (!android::base::WriteFully(fd, buffer, sizeof(buffer))) {
+            perror("write");
+            return {};
+        }
+    }
+    return fd;
+}
+
+TEST(libdm, LoopControl) {
+    unique_fd fd = TempFile();
+    ASSERT_GE(fd, 0);
+
+    LoopDevice loop(fd);
+    ASSERT_TRUE(loop.valid());
+
+    char buffer[6];
+    unique_fd loop_fd(open(loop.device().c_str(), O_RDWR));
+    ASSERT_GE(loop_fd, 0);
+    ASSERT_TRUE(android::base::ReadFully(loop_fd, buffer, sizeof(buffer)));
+    ASSERT_EQ(memcmp(buffer, "Hello", 6), 0);
+}
diff --git a/fs_mgr/libdm/test_util.cpp b/fs_mgr/libdm/test_util.cpp
new file mode 100644
index 0000000..307251c
--- /dev/null
+++ b/fs_mgr/libdm/test_util.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "test_util.h"
+
+namespace android {
+namespace dm {
+
+using unique_fd = android::base::unique_fd;
+
+// Create a temporary in-memory file. If size is non-zero, the file will be
+// created with a fixed size.
+unique_fd CreateTempFile(const std::string& name, size_t size) {
+    unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));
+    if (fd < 0) {
+        return {};
+    }
+    if (size) {
+        if (ftruncate(fd, size) < 0) {
+            perror("ftruncate");
+            return {};
+        }
+        if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+            perror("fcntl");
+            return {};
+        }
+    }
+    return fd;
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/test_util.h b/fs_mgr/libdm/test_util.h
new file mode 100644
index 0000000..96b051c
--- /dev/null
+++ b/fs_mgr/libdm/test_util.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBDM_TEST_UTILS_H_
+#define _LIBDM_TEST_UTILS_H_
+
+#include <android-base/unique_fd.h>
+#include <stddef.h>
+
+#include <string>
+
+namespace android {
+namespace dm {
+
+// Create a temporary in-memory file. If size is non-zero, the file will be
+// created with a fixed size.
+android::base::unique_fd CreateTempFile(const std::string& name, size_t size);
+
+}  // namespace dm
+}  // namespace android
+
+#endif  // _LIBDM_TEST_UTILS_H_
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
new file mode 100644
index 0000000..1434b21
--- /dev/null
+++ b/fs_mgr/liblp/Android.bp
@@ -0,0 +1,67 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+    name: "liblp",
+    host_supported: true,
+    recovery_available: true,
+    defaults: ["fs_mgr_defaults"],
+    cppflags: [
+        "-D_FILE_OFFSET_BITS=64",
+    ],
+    srcs: [
+        "builder.cpp",
+        "images.cpp",
+        "reader.cpp",
+        "utility.cpp",
+        "writer.cpp",
+    ],
+    static_libs: [
+        "libbase",
+        "liblog",
+        "libcrypto",
+        "libcrypto_utils",
+        "libsparse",
+    ],
+    whole_static_libs: [
+        "libext2_uuid",
+        "libext4_utils",
+        "libsparse",
+        "libz",
+    ],
+    export_include_dirs: ["include"],
+}
+
+cc_test {
+    name: "liblp_test",
+    defaults: ["fs_mgr_defaults"],
+    cppflags: [
+        "-Wno-unused-parameter",
+    ],
+    static_libs: [
+        "libbase",
+        "liblog",
+        "libcrypto",
+        "libcrypto_utils",
+        "liblp",
+        "libfs_mgr",
+    ],
+    srcs: [
+        "builder_test.cpp",
+        "io_test.cpp",
+        "utility_test.cpp",
+    ],
+}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
new file mode 100644
index 0000000..9d710f9
--- /dev/null
+++ b/fs_mgr/liblp/builder.cpp
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "liblp/builder.h"
+
+#if defined(__linux__)
+#include <linux/fs.h>
+#endif
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <algorithm>
+
+#include <android-base/unique_fd.h>
+#include <uuid/uuid.h>
+
+#include "liblp/liblp.h"
+#include "reader.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
+#if defined(__linux__)
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
+        return false;
+    }
+    if (!GetDescriptorSize(fd, &device_info->size)) {
+        return false;
+    }
+    if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+        return false;
+    }
+    if (ioctl(fd, BLKALIGNOFF, &device_info->alignment_offset) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+        return false;
+    }
+    return true;
+#else
+    (void)block_device;
+    (void)device_info;
+    LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
+    return false;
+#endif
+}
+
+void LinearExtent::AddTo(LpMetadata* out) const {
+    out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
+}
+
+void ZeroExtent::AddTo(LpMetadata* out) const {
+    out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
+}
+
+Partition::Partition(const std::string& name, const std::string& guid, uint32_t attributes)
+    : name_(name), guid_(guid), attributes_(attributes), size_(0) {}
+
+void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
+    size_ += extent->num_sectors() * LP_SECTOR_SIZE;
+    extents_.push_back(std::move(extent));
+}
+
+void Partition::RemoveExtents() {
+    size_ = 0;
+    extents_.clear();
+}
+
+void Partition::ShrinkTo(uint64_t requested_size) {
+    uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
+    if (size_ <= aligned_size) {
+        return;
+    }
+    if (aligned_size == 0) {
+        RemoveExtents();
+        return;
+    }
+
+    // Remove or shrink extents of any kind until the total partition size is
+    // equal to the requested size.
+    uint64_t sectors_to_remove = (size_ - aligned_size) / LP_SECTOR_SIZE;
+    while (sectors_to_remove) {
+        Extent* extent = extents_.back().get();
+        if (extent->num_sectors() > sectors_to_remove) {
+            size_ -= sectors_to_remove * LP_SECTOR_SIZE;
+            extent->set_num_sectors(extent->num_sectors() - sectors_to_remove);
+            break;
+        }
+        size_ -= (extent->num_sectors() * LP_SECTOR_SIZE);
+        sectors_to_remove -= extent->num_sectors();
+        extents_.pop_back();
+    }
+    DCHECK(size_ == requested_size);
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
+                                                      uint32_t slot_number) {
+    std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
+    if (!metadata) {
+        return nullptr;
+    }
+    std::unique_ptr<MetadataBuilder> builder = New(*metadata.get());
+    if (!builder) {
+        return nullptr;
+    }
+    BlockDeviceInfo device_info;
+    if (fs_mgr::GetBlockDeviceInfo(block_device, &device_info)) {
+        builder->set_block_device_info(device_info);
+    }
+    return builder;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const BlockDeviceInfo& device_info,
+                                                      uint32_t metadata_max_size,
+                                                      uint32_t metadata_slot_count) {
+    std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+    if (!builder->Init(device_info, metadata_max_size, metadata_slot_count)) {
+        return nullptr;
+    }
+    return builder;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata) {
+    std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+    if (!builder->Init(metadata)) {
+        return nullptr;
+    }
+    return builder;
+}
+
+MetadataBuilder::MetadataBuilder() {
+    memset(&geometry_, 0, sizeof(geometry_));
+    geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
+    geometry_.struct_size = sizeof(geometry_);
+
+    memset(&header_, 0, sizeof(header_));
+    header_.magic = LP_METADATA_HEADER_MAGIC;
+    header_.major_version = LP_METADATA_MAJOR_VERSION;
+    header_.minor_version = LP_METADATA_MINOR_VERSION;
+    header_.header_size = sizeof(header_);
+    header_.partitions.entry_size = sizeof(LpMetadataPartition);
+    header_.extents.entry_size = sizeof(LpMetadataExtent);
+}
+
+bool MetadataBuilder::Init(const LpMetadata& metadata) {
+    geometry_ = metadata.geometry;
+
+    for (const auto& partition : metadata.partitions) {
+        Partition* builder = AddPartition(GetPartitionName(partition), GetPartitionGuid(partition),
+                                          partition.attributes);
+        if (!builder) {
+            return false;
+        }
+
+        for (size_t i = 0; i < partition.num_extents; i++) {
+            const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
+            if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+                auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_data);
+                builder->AddExtent(std::move(copy));
+            } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
+                auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
+                builder->AddExtent(std::move(copy));
+            }
+        }
+    }
+
+    device_info_.alignment = geometry_.alignment;
+    device_info_.alignment_offset = geometry_.alignment_offset;
+    return true;
+}
+
+bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata_max_size,
+                           uint32_t metadata_slot_count) {
+    if (metadata_max_size < sizeof(LpMetadataHeader)) {
+        LERROR << "Invalid metadata maximum size.";
+        return false;
+    }
+    if (metadata_slot_count == 0) {
+        LERROR << "Invalid metadata slot count.";
+        return false;
+    }
+
+    // Align the metadata size up to the nearest sector.
+    metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
+
+    // Check that device properties are sane.
+    device_info_ = device_info;
+    if (device_info_.size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device size must be a multiple of 512.";
+        return false;
+    }
+    if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) {
+        LERROR << "Alignment offset is not sector-aligned.";
+        return false;
+    }
+    if (device_info_.alignment % LP_SECTOR_SIZE != 0) {
+        LERROR << "Partition alignment is not sector-aligned.";
+        return false;
+    }
+    if (device_info_.alignment_offset > device_info_.alignment) {
+        LERROR << "Partition alignment offset is greater than its alignment.";
+        return false;
+    }
+
+    // We reserve a geometry block (4KB) plus space for each copy of the
+    // maximum size of a metadata blob. Then, we double that space since
+    // we store a backup copy of everything.
+    uint64_t reserved =
+            LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
+    uint64_t total_reserved = reserved * 2;
+    if (device_info_.size < total_reserved) {
+        LERROR << "Attempting to create metadata on a block device that is too small.";
+        return false;
+    }
+
+    // Compute the first free sector, factoring in alignment.
+    uint64_t free_area = AlignTo(reserved, device_info_.alignment, device_info_.alignment_offset);
+    uint64_t first_sector = free_area / LP_SECTOR_SIZE;
+
+    // Compute the last free sector, which is inclusive. We subtract 1 to make
+    // sure that logical partitions won't overlap with the same sector as the
+    // backup metadata, which could happen if the block device was not aligned
+    // to LP_SECTOR_SIZE.
+    uint64_t last_sector = ((device_info_.size - reserved) / LP_SECTOR_SIZE) - 1;
+
+    // If this check fails, it means either (1) we did not have free space to
+    // allocate a single sector, or (2) we did, but the alignment was high
+    // enough to bump the first sector out of range. Either way, we cannot
+    // continue.
+    if (first_sector > last_sector) {
+        LERROR << "Not enough space to allocate any partition tables.";
+        return false;
+    }
+
+    geometry_.first_logical_sector = first_sector;
+    geometry_.last_logical_sector = last_sector;
+    geometry_.metadata_max_size = metadata_max_size;
+    geometry_.metadata_slot_count = metadata_slot_count;
+    geometry_.alignment = device_info_.alignment;
+    geometry_.alignment_offset = device_info_.alignment_offset;
+    geometry_.block_device_size = device_info_.size;
+    return true;
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& guid,
+                                         uint32_t attributes) {
+    if (name.empty()) {
+        LERROR << "Partition must have a non-empty name.";
+        return nullptr;
+    }
+    if (FindPartition(name)) {
+        LERROR << "Attempting to create duplication partition with name: " << name;
+        return nullptr;
+    }
+    partitions_.push_back(std::make_unique<Partition>(name, guid, attributes));
+    return partitions_.back().get();
+}
+
+Partition* MetadataBuilder::FindPartition(const std::string& name) {
+    for (const auto& partition : partitions_) {
+        if (partition->name() == name) {
+            return partition.get();
+        }
+    }
+    return nullptr;
+}
+
+void MetadataBuilder::RemovePartition(const std::string& name) {
+    for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
+        if ((*iter)->name() == name) {
+            partitions_.erase(iter);
+            return;
+        }
+    }
+}
+
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_size) {
+    // Align the space needed up to the nearest sector.
+    uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
+    if (partition->size() >= aligned_size) {
+        return true;
+    }
+
+    // Figure out how much we need to allocate.
+    uint64_t space_needed = aligned_size - partition->size();
+    uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
+    DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
+
+    struct Interval {
+        uint64_t start;
+        uint64_t end;
+
+        Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+        bool operator<(const Interval& other) const { return start < other.start; }
+    };
+    std::vector<Interval> intervals;
+
+    // Collect all extents in the partition table.
+    for (const auto& partition : partitions_) {
+        for (const auto& extent : partition->extents()) {
+            LinearExtent* linear = extent->AsLinearExtent();
+            if (!linear) {
+                continue;
+            }
+            intervals.emplace_back(linear->physical_sector(),
+                                   linear->physical_sector() + extent->num_sectors());
+        }
+    }
+
+    // Sort extents by starting sector.
+    std::sort(intervals.begin(), intervals.end());
+
+    // Find gaps that we can use for new extents. Note we store new extents in a
+    // temporary vector, and only commit them if we are guaranteed enough free
+    // space.
+    std::vector<std::unique_ptr<LinearExtent>> new_extents;
+    for (size_t i = 1; i < intervals.size(); i++) {
+        const Interval& previous = intervals[i - 1];
+        const Interval& current = intervals[i];
+
+        if (previous.end >= current.start) {
+            // There is no gap between these two extents, try the next one. Note that
+            // extents may never overlap, but just for safety, we ignore them if they
+            // do.
+            DCHECK(previous.end == current.start);
+            continue;
+        }
+
+        uint64_t aligned = AlignSector(previous.end);
+        if (aligned >= current.start) {
+            // After alignment, this extent is not usable.
+            continue;
+        }
+
+        // This gap is enough to hold the remainder of the space requested, so we
+        // can allocate what we need and return.
+        if (current.start - aligned >= sectors_needed) {
+            auto extent = std::make_unique<LinearExtent>(sectors_needed, aligned);
+            sectors_needed -= extent->num_sectors();
+            new_extents.push_back(std::move(extent));
+            break;
+        }
+
+        // This gap is not big enough to fit the remainder of the space requested,
+        // so consume the whole thing and keep looking for more.
+        auto extent = std::make_unique<LinearExtent>(current.start - aligned, aligned);
+        sectors_needed -= extent->num_sectors();
+        new_extents.push_back(std::move(extent));
+    }
+
+    // If we still have more to allocate, take it from the remaining free space
+    // in the allocatable region.
+    if (sectors_needed) {
+        uint64_t first_sector;
+        if (intervals.empty()) {
+            first_sector = geometry_.first_logical_sector;
+        } else {
+            first_sector = intervals.back().end;
+        }
+        DCHECK(first_sector <= geometry_.last_logical_sector);
+
+        // Note: After alignment, |first_sector| may be > the last usable sector.
+        first_sector = AlignSector(first_sector);
+
+        // Note: the last usable sector is inclusive.
+        if (first_sector > geometry_.last_logical_sector ||
+            geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
+            LERROR << "Not enough free space to expand partition: " << partition->name();
+            return false;
+        }
+        auto extent = std::make_unique<LinearExtent>(sectors_needed, first_sector);
+        new_extents.push_back(std::move(extent));
+    }
+
+    for (auto& extent : new_extents) {
+        partition->AddExtent(std::move(extent));
+    }
+    return true;
+}
+
+void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t requested_size) {
+    partition->ShrinkTo(requested_size);
+}
+
+std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
+    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+    metadata->header = header_;
+    metadata->geometry = geometry_;
+
+    // Flatten the partition and extent structures into an LpMetadata, which
+    // makes it very easy to validate, serialize, or pass on to device-mapper.
+    for (const auto& partition : partitions_) {
+        LpMetadataPartition part;
+        memset(&part, 0, sizeof(part));
+
+        if (partition->name().size() > sizeof(part.name)) {
+            LERROR << "Partition name is too long: " << partition->name();
+            return nullptr;
+        }
+        if (partition->attributes() & ~(LP_PARTITION_ATTRIBUTE_MASK)) {
+            LERROR << "Partition " << partition->name() << " has unsupported attribute.";
+            return nullptr;
+        }
+
+        strncpy(part.name, partition->name().c_str(), sizeof(part.name));
+        if (uuid_parse(partition->guid().c_str(), part.guid) != 0) {
+            LERROR << "Could not parse guid " << partition->guid() << " for partition "
+                   << partition->name();
+            return nullptr;
+        }
+
+        part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
+        part.num_extents = static_cast<uint32_t>(partition->extents().size());
+        part.attributes = partition->attributes();
+
+        for (const auto& extent : partition->extents()) {
+            extent->AddTo(metadata.get());
+        }
+        metadata->partitions.push_back(part);
+    }
+
+    metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
+    metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
+    return metadata;
+}
+
+uint64_t MetadataBuilder::AllocatableSpace() const {
+    return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
+}
+
+uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
+    // Note: when reading alignment info from the Kernel, we don't assume it
+    // is aligned to the sector size, so we round up to the nearest sector.
+    uint64_t lba = sector * LP_SECTOR_SIZE;
+    uint64_t aligned = AlignTo(lba, device_info_.alignment, device_info_.alignment_offset);
+    return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
+}
+
+void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) {
+    device_info_.size = device_info.size;
+
+    // The kernel does not guarantee these values are present, so we only
+    // replace existing values if the new values are non-zero.
+    if (device_info.alignment) {
+        device_info_.alignment = device_info.alignment;
+    }
+    if (device_info.alignment_offset) {
+        device_info_.alignment_offset = device_info.alignment_offset;
+    }
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
new file mode 100644
index 0000000..b610fd4
--- /dev/null
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+#include "fs_mgr.h"
+#include "utility.h"
+
+using namespace std;
+using namespace android::fs_mgr;
+
+static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
+static const char* TEST_GUID2 = "A799D1D6-669F-41D8-A3F0-EBB7572D8303";
+
+TEST(liblp, BuildBasic) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    EXPECT_EQ(partition->name(), "system");
+    EXPECT_EQ(partition->guid(), TEST_GUID);
+    EXPECT_EQ(partition->attributes(), LP_PARTITION_ATTR_READONLY);
+    EXPECT_EQ(partition->size(), 0);
+    EXPECT_EQ(builder->FindPartition("system"), partition);
+
+    builder->RemovePartition("system");
+    EXPECT_EQ(builder->FindPartition("system"), nullptr);
+}
+
+TEST(liblp, ResizePartition) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+    EXPECT_EQ(system->size(), 65536);
+    ASSERT_EQ(system->extents().size(), 1);
+
+    LinearExtent* extent = system->extents()[0]->AsLinearExtent();
+    ASSERT_NE(extent, nullptr);
+    EXPECT_EQ(extent->num_sectors(), 65536 / LP_SECTOR_SIZE);
+    // The first logical sector will be (4096+1024*2)/512 = 12.
+    EXPECT_EQ(extent->physical_sector(), 12);
+
+    // Test growing to the same size.
+    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+    EXPECT_EQ(system->size(), 65536);
+    EXPECT_EQ(system->extents().size(), 1);
+    EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+    // Test growing to a smaller size.
+    EXPECT_EQ(builder->GrowPartition(system, 0), true);
+    EXPECT_EQ(system->size(), 65536);
+    EXPECT_EQ(system->extents().size(), 1);
+    EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+    // Test shrinking to a greater size.
+    builder->ShrinkPartition(system, 131072);
+    EXPECT_EQ(system->size(), 65536);
+    EXPECT_EQ(system->extents().size(), 1);
+    EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+
+    // Test shrinking within the same extent.
+    builder->ShrinkPartition(system, 32768);
+    EXPECT_EQ(system->size(), 32768);
+    EXPECT_EQ(system->extents().size(), 1);
+    extent = system->extents()[0]->AsLinearExtent();
+    ASSERT_NE(extent, nullptr);
+    EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE);
+    EXPECT_EQ(extent->physical_sector(), 12);
+
+    // Test shrinking to 0.
+    builder->ShrinkPartition(system, 0);
+    EXPECT_EQ(system->size(), 0);
+    EXPECT_EQ(system->extents().size(), 0);
+}
+
+TEST(liblp, PartitionAlignment) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    // Test that we align up to one sector.
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    EXPECT_EQ(builder->GrowPartition(system, 10000), true);
+    EXPECT_EQ(system->size(), 10240);
+    EXPECT_EQ(system->extents().size(), 1);
+
+    builder->ShrinkPartition(system, 9000);
+    EXPECT_EQ(system->size(), 9216);
+    EXPECT_EQ(system->extents().size(), 1);
+}
+
+TEST(liblp, DiskAlignment) {
+    static const uint64_t kDiskSize = 1000000;
+    static const uint32_t kMetadataSize = 1024;
+    static const uint32_t kMetadataSlots = 2;
+
+    unique_ptr<MetadataBuilder> builder =
+            MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+    ASSERT_EQ(builder, nullptr);
+}
+
+TEST(liblp, MetadataAlignment) {
+    // Make sure metadata sizes get aligned up.
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
+}
+
+TEST(liblp, InternalAlignment) {
+    // Test the metadata fitting within alignment.
+    BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+
+    // Test a large alignment offset thrown in.
+    device_info.alignment_offset = 753664;
+    builder = MetadataBuilder::New(device_info, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+
+    // Alignment offset without alignment doesn't mean anything.
+    device_info.alignment = 0;
+    builder = MetadataBuilder::New(device_info, 1024, 2);
+    ASSERT_EQ(builder, nullptr);
+
+    // Test a small alignment with an alignment offset.
+    device_info.alignment = 12 * 1024;
+    device_info.alignment_offset = 3 * 1024;
+    builder = MetadataBuilder::New(device_info, 16 * 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 78);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+
+    // Test a small alignment with no alignment offset.
+    device_info.alignment = 11 * 1024;
+    builder = MetadataBuilder::New(device_info, 16 * 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 72);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+}
+
+TEST(liblp, InternalPartitionAlignment) {
+    BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
+
+    Partition* a = builder->AddPartition("a", TEST_GUID, 0);
+    ASSERT_NE(a, nullptr);
+    Partition* b = builder->AddPartition("b", TEST_GUID2, 0);
+    ASSERT_NE(b, nullptr);
+
+    // Add a bunch of small extents to each, interleaving.
+    for (size_t i = 0; i < 10; i++) {
+        ASSERT_TRUE(builder->GrowPartition(a, a->size() + 4096));
+        ASSERT_TRUE(builder->GrowPartition(b, b->size() + 4096));
+    }
+    EXPECT_EQ(a->size(), 40960);
+    EXPECT_EQ(b->size(), 40960);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // Check that each starting sector is aligned.
+    for (const auto& extent : exported->extents) {
+        ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
+        EXPECT_EQ(extent.num_sectors, 8);
+
+        uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
+        uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
+        EXPECT_EQ(lba, aligned_lba);
+    }
+
+    // Sanity check one extent.
+    EXPECT_EQ(exported->extents.back().target_data, 30656);
+}
+
+TEST(liblp, UseAllDiskSpace) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    EXPECT_EQ(builder->AllocatableSpace(), 1036288);
+
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    EXPECT_EQ(builder->GrowPartition(system, 1036288), true);
+    EXPECT_EQ(system->size(), 1036288);
+    EXPECT_EQ(builder->GrowPartition(system, 1036289), false);
+}
+
+TEST(liblp, BuildComplex) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+    EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+    EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+    EXPECT_EQ(system->size(), 98304);
+    EXPECT_EQ(vendor->size(), 32768);
+
+    // We now expect to have 3 extents total: 2 for system, 1 for vendor, since
+    // our allocation strategy is greedy/first-fit.
+    ASSERT_EQ(system->extents().size(), 2);
+    ASSERT_EQ(vendor->extents().size(), 1);
+
+    LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
+    LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
+    LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
+    ASSERT_NE(system1, nullptr);
+    ASSERT_NE(system2, nullptr);
+    ASSERT_NE(vendor1, nullptr);
+    EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
+    EXPECT_EQ(system1->physical_sector(), 12);
+    EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
+    EXPECT_EQ(system2->physical_sector(), 204);
+    EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
+    EXPECT_EQ(vendor1->physical_sector(), 140);
+    EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector());
+    EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
+}
+
+TEST(liblp, AddInvalidPartition) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+
+    // Duplicate name.
+    partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    EXPECT_EQ(partition, nullptr);
+
+    // Empty name.
+    partition = builder->AddPartition("", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    EXPECT_EQ(partition, nullptr);
+}
+
+TEST(liblp, BuilderExport) {
+    static const uint64_t kDiskSize = 1024 * 1024;
+    static const uint32_t kMetadataSize = 1024;
+    static const uint32_t kMetadataSlots = 2;
+    unique_ptr<MetadataBuilder> builder =
+            MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+    EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+    EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    EXPECT_NE(exported, nullptr);
+
+    // Verify geometry. Some details of this may change if we change the
+    // metadata structures. So in addition to checking the exact values, we
+    // also check that they are internally consistent after.
+    const LpMetadataGeometry& geometry = exported->geometry;
+    EXPECT_EQ(geometry.magic, LP_METADATA_GEOMETRY_MAGIC);
+    EXPECT_EQ(geometry.struct_size, sizeof(geometry));
+    EXPECT_EQ(geometry.metadata_max_size, 1024);
+    EXPECT_EQ(geometry.metadata_slot_count, 2);
+    EXPECT_EQ(geometry.first_logical_sector, 12);
+    EXPECT_EQ(geometry.last_logical_sector, 2035);
+
+    static const size_t kMetadataSpace =
+            (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE;
+    uint64_t space_at_end = kDiskSize - (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE;
+    EXPECT_GE(space_at_end, kMetadataSpace);
+    EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
+
+    // Verify header.
+    const LpMetadataHeader& header = exported->header;
+    EXPECT_EQ(header.magic, LP_METADATA_HEADER_MAGIC);
+    EXPECT_EQ(header.major_version, LP_METADATA_MAJOR_VERSION);
+    EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION);
+
+    ASSERT_EQ(exported->partitions.size(), 2);
+    ASSERT_EQ(exported->extents.size(), 3);
+
+    for (const auto& partition : exported->partitions) {
+        Partition* original = builder->FindPartition(GetPartitionName(partition));
+        ASSERT_NE(original, nullptr);
+        EXPECT_EQ(original->guid(), GetPartitionGuid(partition));
+        for (size_t i = 0; i < partition.num_extents; i++) {
+            const auto& extent = exported->extents[partition.first_extent_index + i];
+            LinearExtent* original_extent = original->extents()[i]->AsLinearExtent();
+            EXPECT_EQ(extent.num_sectors, original_extent->num_sectors());
+            EXPECT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
+            EXPECT_EQ(extent.target_data, original_extent->physical_sector());
+        }
+        EXPECT_EQ(partition.attributes, original->attributes());
+    }
+}
+
+TEST(liblp, BuilderImport) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+    EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+    EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    builder = MetadataBuilder::New(*exported.get());
+    ASSERT_NE(builder, nullptr);
+    system = builder->FindPartition("system");
+    ASSERT_NE(system, nullptr);
+    vendor = builder->FindPartition("vendor");
+    ASSERT_NE(vendor, nullptr);
+
+    EXPECT_EQ(system->size(), 98304);
+    ASSERT_EQ(system->extents().size(), 2);
+    EXPECT_EQ(system->guid(), TEST_GUID);
+    EXPECT_EQ(system->attributes(), LP_PARTITION_ATTR_READONLY);
+    EXPECT_EQ(vendor->size(), 32768);
+    ASSERT_EQ(vendor->extents().size(), 1);
+    EXPECT_EQ(vendor->guid(), TEST_GUID2);
+    EXPECT_EQ(vendor->attributes(), LP_PARTITION_ATTR_READONLY);
+
+    LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
+    LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
+    LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
+    EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
+    EXPECT_EQ(system1->physical_sector(), 12);
+    EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
+    EXPECT_EQ(system2->physical_sector(), 204);
+    EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
+}
+
+TEST(liblp, ExportNameTooLong) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
+    Partition* system = builder->AddPartition(name + name, TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    EXPECT_NE(system, nullptr);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    EXPECT_EQ(exported, nullptr);
+}
+
+TEST(liblp, ExportInvalidGuid) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* system = builder->AddPartition("system", "bad", LP_PARTITION_ATTR_READONLY);
+    EXPECT_NE(system, nullptr);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    EXPECT_EQ(exported, nullptr);
+}
+
+TEST(liblp, MetadataTooLarge) {
+    static const size_t kDiskSize = 128 * 1024;
+    static const size_t kMetadataSize = 64 * 1024;
+
+    // No space to store metadata + geometry.
+    BlockDeviceInfo device_info(kDiskSize, 0, 0);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_EQ(builder, nullptr);
+
+    // No space to store metadata + geometry + one free sector.
+    device_info.size += LP_METADATA_GEOMETRY_SIZE * 2;
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_EQ(builder, nullptr);
+
+    // Space for metadata + geometry + one free sector.
+    device_info.size += LP_SECTOR_SIZE;
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_NE(builder, nullptr);
+
+    // Test with alignment.
+    device_info.alignment = 131072;
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_EQ(builder, nullptr);
+
+    device_info.alignment = 0;
+    device_info.alignment_offset = 32768 - LP_SECTOR_SIZE;
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_EQ(builder, nullptr);
+}
+
+TEST(liblp, block_device_info) {
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                               fs_mgr_free_fstab);
+    ASSERT_NE(fstab, nullptr);
+
+    // This should read from the "super" partition once we have a well-defined
+    // way to access it.
+    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), "/data");
+    ASSERT_NE(rec, nullptr);
+
+    BlockDeviceInfo device_info;
+    ASSERT_TRUE(GetBlockDeviceInfo(rec->blk_device, &device_info));
+
+    // Sanity check that the device doesn't give us some weird inefficient
+    // alignment.
+    ASSERT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
+    ASSERT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
+    ASSERT_LE(device_info.alignment_offset, INT_MAX);
+
+    // Having an alignment offset > alignment doesn't really make sense.
+    ASSERT_LT(device_info.alignment_offset, device_info.alignment);
+}
+
+TEST(liblp, UpdateBlockDeviceInfo) {
+    BlockDeviceInfo device_info(1024 * 1024, 4096, 1024);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    EXPECT_EQ(builder->block_device_info().size, device_info.size);
+    EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
+    EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+
+    device_info.alignment = 0;
+    device_info.alignment_offset = 2048;
+    builder->set_block_device_info(device_info);
+    EXPECT_EQ(builder->block_device_info().alignment, 4096);
+    EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+
+    device_info.alignment = 8192;
+    device_info.alignment_offset = 0;
+    builder->set_block_device_info(device_info);
+    EXPECT_EQ(builder->block_device_info().alignment, 8192);
+    EXPECT_EQ(builder->block_device_info().alignment_offset, 2048);
+}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
new file mode 100644
index 0000000..a361a5d
--- /dev/null
+++ b/fs_mgr/liblp/images.cpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "images.h"
+
+#include <limits.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <sparse/sparse.h>
+
+#include "reader.h"
+#include "utility.h"
+#include "writer.h"
+
+namespace android {
+namespace fs_mgr {
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
+    LpMetadataGeometry geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+        return nullptr;
+    }
+    if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
+        return nullptr;
+    }
+    std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
+    if (!metadata) {
+        return nullptr;
+    }
+    metadata->geometry = geometry;
+    return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
+    android::base::unique_fd fd(open(file, O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+        return nullptr;
+    }
+    return ReadFromImageFile(fd);
+}
+
+bool WriteToImageFile(int fd, const LpMetadata& input) {
+    std::string geometry = SerializeGeometry(input.geometry);
+    std::string metadata = SerializeMetadata(input);
+
+    std::string everything = geometry + metadata;
+
+    if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
+        return false;
+    }
+    return true;
+}
+
+bool WriteToImageFile(const char* file, const LpMetadata& input) {
+    android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+        return false;
+    }
+    return WriteToImageFile(fd, input);
+}
+
+// We use an object to build the sparse file since it requires that data
+// pointers be held alive until the sparse file is destroyed. It's easier
+// to do this when the data pointers are all in one place.
+class SparseBuilder {
+  public:
+    SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
+                  const std::map<std::string, std::string>& images);
+
+    bool Build();
+    bool Export(const char* file);
+    bool IsValid() const { return file_ != nullptr; }
+
+  private:
+    bool AddData(const std::string& blob, uint64_t sector);
+    bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);
+    int OpenImageFile(const std::string& file);
+    bool SectorToBlock(uint64_t sector, uint32_t* block);
+
+    const LpMetadata& metadata_;
+    const LpMetadataGeometry& geometry_;
+    uint32_t block_size_;
+    std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
+    std::string primary_blob_;
+    std::string backup_blob_;
+    std::map<std::string, std::string> images_;
+    std::vector<android::base::unique_fd> temp_fds_;
+};
+
+SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
+                             const std::map<std::string, std::string>& images)
+    : metadata_(metadata),
+      geometry_(metadata.geometry),
+      block_size_(block_size),
+      file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy),
+      images_(images) {}
+
+bool SparseBuilder::Export(const char* file) {
+    android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+    if (fd < 0) {
+        PERROR << "open failed: " << file;
+        return false;
+    }
+    // No gzip compression; sparseify; no checksum.
+    int ret = sparse_file_write(file_.get(), fd, false, true, false);
+    if (ret != 0) {
+        LERROR << "sparse_file_write failed (error code " << ret << ")";
+        return false;
+    }
+    return true;
+}
+
+bool SparseBuilder::AddData(const std::string& blob, uint64_t sector) {
+    uint32_t block;
+    if (!SectorToBlock(sector, &block)) {
+        return false;
+    }
+    void* data = const_cast<char*>(blob.data());
+    int ret = sparse_file_add_data(file_.get(), data, blob.size(), block);
+    if (ret != 0) {
+        LERROR << "sparse_file_add_data failed (error code " << ret << ")";
+        return false;
+    }
+    return true;
+}
+
+bool SparseBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
+    // The caller must ensure that the metadata has an alignment that is a
+    // multiple of the block size. liblp will take care of the rest, ensuring
+    // that all partitions are on an aligned boundary. Therefore all writes
+    // should be block-aligned, and if they are not, the table was misconfigured.
+    // Note that the default alignment is 1MiB, which is a multiple of the
+    // default block size (4096).
+    if ((sector * LP_SECTOR_SIZE) % block_size_ != 0) {
+        LERROR << "sector " << sector << " is not aligned to block size " << block_size_;
+        return false;
+    }
+    *block = (sector * LP_SECTOR_SIZE) / block_size_;
+    return true;
+}
+
+bool SparseBuilder::Build() {
+    std::string geometry_blob = SerializeGeometry(geometry_);
+    std::string metadata_blob = SerializeMetadata(metadata_);
+    metadata_blob.resize(geometry_.metadata_max_size);
+
+    std::string all_metadata;
+    for (size_t i = 0; i < geometry_.metadata_slot_count; i++) {
+        all_metadata += metadata_blob;
+    }
+
+    // Metadata immediately follows geometry, and we write the same metadata
+    // to all slots. Note that we don't bother trying to write skip chunks
+    // here since it's a small amount of data.
+    primary_blob_ = geometry_blob + all_metadata;
+    if (!AddData(primary_blob_, 0)) {
+        return false;
+    }
+
+    for (const auto& partition : metadata_.partitions) {
+        auto iter = images_.find(GetPartitionName(partition));
+        if (iter == images_.end()) {
+            continue;
+        }
+        if (!AddPartitionImage(partition, iter->second)) {
+            return false;
+        }
+        images_.erase(iter);
+    }
+
+    if (!images_.empty()) {
+        LERROR << "Partition image was specified but no partition was found.";
+        return false;
+    }
+
+    // The backup area contains all metadata slots, and then geometry. Similar
+    // to before we write the metadata to every slot.
+    int64_t backup_offset = GetBackupMetadataOffset(geometry_, 0);
+    uint64_t backups_start = geometry_.block_device_size + backup_offset;
+    uint64_t backup_sector = backups_start / LP_SECTOR_SIZE;
+
+    backup_blob_ = all_metadata + geometry_blob;
+    if (!AddData(backup_blob_, backup_sector)) {
+        return false;
+    }
+    return true;
+}
+
+static inline bool HasFillValue(uint32_t* buffer, size_t count) {
+    uint32_t fill_value = buffer[0];
+    for (size_t i = 1; i < count; i++) {
+        if (fill_value != buffer[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool SparseBuilder::AddPartitionImage(const LpMetadataPartition& partition,
+                                      const std::string& file) {
+    if (partition.num_extents != 1) {
+        LERROR << "Partition for new tables should not have more than one extent: "
+               << GetPartitionName(partition);
+        return false;
+    }
+
+    const LpMetadataExtent& extent = metadata_.extents[partition.first_extent_index];
+    if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
+        LERROR << "Partition should only have linear extents: " << GetPartitionName(partition);
+        return false;
+    }
+
+    int fd = OpenImageFile(file);
+    if (fd < 0) {
+        LERROR << "Could not open image for partition: " << GetPartitionName(partition);
+        return false;
+    }
+
+    // Make sure the image does not exceed the partition size.
+    uint64_t file_length;
+    if (!GetDescriptorSize(fd, &file_length)) {
+        LERROR << "Could not compute image size";
+        return false;
+    }
+    if (file_length > extent.num_sectors * LP_SECTOR_SIZE) {
+        LERROR << "Image for partition '" << GetPartitionName(partition)
+               << "' is greater than its size";
+        return false;
+    }
+    if (SeekFile64(fd, 0, SEEK_SET)) {
+        PERROR << "lseek failed";
+        return false;
+    }
+
+    uint32_t output_block;
+    if (!SectorToBlock(extent.target_data, &output_block)) {
+        return false;
+    }
+
+    uint64_t pos = 0;
+    uint64_t remaining = file_length;
+    while (remaining) {
+        uint32_t buffer[block_size_ / sizeof(uint32_t)];
+        size_t read_size = remaining >= sizeof(buffer) ? sizeof(buffer) : size_t(remaining);
+        if (!android::base::ReadFully(fd, buffer, sizeof(buffer))) {
+            PERROR << "read failed";
+            return false;
+        }
+        if (read_size != sizeof(buffer) || !HasFillValue(buffer, read_size / sizeof(uint32_t))) {
+            int rv = sparse_file_add_fd(file_.get(), fd, pos, read_size, output_block);
+            if (rv) {
+                LERROR << "sparse_file_add_fd failed with code: " << rv;
+                return false;
+            }
+        } else {
+            int rv = sparse_file_add_fill(file_.get(), buffer[0], read_size, output_block);
+            if (rv) {
+                LERROR << "sparse_file_add_fill failed with code: " << rv;
+                return false;
+            }
+        }
+        pos += read_size;
+        remaining -= read_size;
+        output_block++;
+    }
+
+    return true;
+}
+
+int SparseBuilder::OpenImageFile(const std::string& file) {
+    android::base::unique_fd source_fd(open(file.c_str(), O_RDONLY));
+    if (source_fd < 0) {
+        PERROR << "open image file failed: " << file;
+        return -1;
+    }
+
+    std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> source(
+            sparse_file_import(source_fd, true, true), sparse_file_destroy);
+    if (!source) {
+        int fd = source_fd.get();
+        temp_fds_.push_back(std::move(source_fd));
+        return fd;
+    }
+
+    char temp_file[PATH_MAX];
+    snprintf(temp_file, sizeof(temp_file), "%s/imageXXXXXX", P_tmpdir);
+    android::base::unique_fd temp_fd(mkstemp(temp_file));
+    if (temp_fd < 0) {
+        PERROR << "mkstemp failed";
+        return -1;
+    }
+    if (unlink(temp_file) < 0) {
+        PERROR << "unlink failed";
+        return -1;
+    }
+
+    // We temporarily unsparse the file, rather than try to merge its chunks.
+    int rv = sparse_file_write(source.get(), temp_fd, false, false, false);
+    if (rv) {
+        LERROR << "sparse_file_write failed with code: " << rv;
+        return -1;
+    }
+    temp_fds_.push_back(std::move(temp_fd));
+    return temp_fds_.back().get();
+}
+
+bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+                       const std::map<std::string, std::string>& images) {
+    if (block_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
+        return false;
+    }
+    if (metadata.geometry.block_device_size % block_size != 0) {
+        LERROR << "Device size must be a multiple of the block size, " << block_size;
+        return false;
+    }
+    uint64_t num_blocks = metadata.geometry.block_device_size % block_size;
+    if (num_blocks >= UINT_MAX) {
+        // libsparse counts blocks in unsigned 32-bit integers, so we check to
+        // make sure we're not going to overflow.
+        LERROR << "Block device is too large to encode with libsparse.";
+        return false;
+    }
+
+    SparseBuilder builder(metadata, block_size, images);
+    if (!builder.IsValid()) {
+        LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size;
+        return false;
+    }
+    if (!builder.Build()) {
+        return false;
+    }
+    return builder.Export(file);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
new file mode 100644
index 0000000..3a999b8
--- /dev/null
+++ b/fs_mgr/liblp/images.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Helper function to serialize geometry and metadata to a normal file, for
+// flashing or debugging.
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+bool WriteToImageFile(int fd, const LpMetadata& metadata);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
new file mode 100644
index 0000000..8bde313
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -0,0 +1,217 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef LIBLP_METADATA_BUILDER_H
+#define LIBLP_METADATA_BUILDER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+
+#include "liblp.h"
+
+namespace android {
+namespace fs_mgr {
+
+class LinearExtent;
+
+// By default, partitions are aligned on a 1MiB boundary.
+static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
+
+struct BlockDeviceInfo {
+    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0) {}
+    BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset)
+        : size(size), alignment(alignment), alignment_offset(alignment_offset) {}
+    // Size of the block device, in bytes.
+    uint64_t size;
+    // Optimal target alignment, in bytes. Partition extents will be aligned to
+    // this value by default. This value must be 0 or a multiple of 512.
+    uint32_t alignment;
+    // Alignment offset to parent device (if any), in bytes. The sector at
+    // |alignment_offset| on the target device is correctly aligned on its
+    // parent device. This value must be 0 or a multiple of 512.
+    uint32_t alignment_offset;
+};
+
+// Abstraction around dm-targets that can be encoded into logical partition tables.
+class Extent {
+  public:
+    explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
+    virtual ~Extent() {}
+
+    virtual void AddTo(LpMetadata* out) const = 0;
+    virtual LinearExtent* AsLinearExtent() { return nullptr; }
+
+    uint64_t num_sectors() const { return num_sectors_; }
+    void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }
+
+  protected:
+    uint64_t num_sectors_;
+};
+
+// This corresponds to a dm-linear target.
+class LinearExtent final : public Extent {
+  public:
+    LinearExtent(uint64_t num_sectors, uint64_t physical_sector)
+        : Extent(num_sectors), physical_sector_(physical_sector) {}
+
+    void AddTo(LpMetadata* metadata) const override;
+    LinearExtent* AsLinearExtent() override { return this; }
+
+    uint64_t physical_sector() const { return physical_sector_; }
+
+  private:
+    uint64_t physical_sector_;
+};
+
+// This corresponds to a dm-zero target.
+class ZeroExtent final : public Extent {
+  public:
+    explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
+
+    void AddTo(LpMetadata* out) const override;
+};
+
+class Partition final {
+  public:
+    Partition(const std::string& name, const std::string& guid, uint32_t attributes);
+
+    // Add a raw extent.
+    void AddExtent(std::unique_ptr<Extent>&& extent);
+
+    // Remove all extents from this partition.
+    void RemoveExtents();
+
+    // Remove and/or shrink extents until the partition is the requested size.
+    // See MetadataBuilder::ShrinkPartition for more information.
+    void ShrinkTo(uint64_t requested_size);
+
+    const std::string& name() const { return name_; }
+    uint32_t attributes() const { return attributes_; }
+    const std::string& guid() const { return guid_; }
+    const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
+    uint64_t size() const { return size_; }
+
+  private:
+    std::string name_;
+    std::string guid_;
+    std::vector<std::unique_ptr<Extent>> extents_;
+    uint32_t attributes_;
+    uint64_t size_;
+};
+
+class MetadataBuilder {
+  public:
+    // Construct an empty logical partition table builder. The block device size
+    // and maximum metadata size must be specified, as this will determine which
+    // areas of the physical partition can be flashed for metadata vs for logical
+    // partitions.
+    //
+    // If the parameters would yield invalid metadata, nullptr is returned. This
+    // could happen if the block device size is too small to store the metadata
+    // and backup copies.
+    static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+                                                uint32_t metadata_max_size,
+                                                uint32_t metadata_slot_count);
+
+    // Import an existing table for modification. This reads metadata off the
+    // given block device and imports it. It also adjusts alignment information
+    // based on run-time values in the operating system.
+    static std::unique_ptr<MetadataBuilder> New(const std::string& block_device,
+                                                uint32_t slot_number);
+
+    // Import an existing table for modification. If the table is not valid, for
+    // example it contains duplicate partition names, then nullptr is returned.
+    // This method is for testing or changing off-line tables.
+    static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+
+    // Wrapper around New() with a BlockDeviceInfo that only specifies a device
+    // size. This is a convenience method for tests.
+    static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
+                                                uint32_t metadata_slot_count) {
+        BlockDeviceInfo device_info(blockdev_size, 0, 0);
+        return New(device_info, metadata_max_size, metadata_slot_count);
+    }
+
+    // Export metadata so it can be serialized to an image, to disk, or mounted
+    // via device-mapper.
+    std::unique_ptr<LpMetadata> Export();
+
+    // Add a partition, returning a handle so it can be sized as needed. If a
+    // partition with the given name already exists, nullptr is returned.
+    Partition* AddPartition(const std::string& name, const std::string& guid, uint32_t attributes);
+
+    // Delete a partition by name if it exists.
+    void RemovePartition(const std::string& name);
+
+    // Find a partition by name. If no partition is found, nullptr is returned.
+    Partition* FindPartition(const std::string& name);
+
+    // Grow a partition to the requested size. If the partition's size is already
+    // greater or equal to the requested size, this will return true and the
+    // partition table will not be changed. Otherwise, a greedy algorithm is
+    // used to find free gaps in the partition table and allocate them for this
+    // partition. If not enough space can be allocated, false is returned, and
+    // the parition table will not be modified.
+    //
+    // The size will be rounded UP to the nearest sector.
+    //
+    // Note, this is an in-memory operation, and it does not alter the
+    // underlying filesystem or contents of the partition on disk.
+    bool GrowPartition(Partition* partition, uint64_t requested_size);
+
+    // Shrink a partition to the requested size. If the partition is already
+    // smaller than the given size, this will return and the partition table
+    // will not be changed. Otherwise, extents will be removed and/or shrunk
+    // from the end of the partition until it is the requested size.
+    //
+    // The size will be rounded UP to the nearest sector.
+    //
+    // Note, this is an in-memory operation, and it does not alter the
+    // underlying filesystem or contents of the partition on disk.
+    void ShrinkPartition(Partition* partition, uint64_t requested_size);
+
+    // Amount of space that can be allocated to logical partitions.
+    uint64_t AllocatableSpace() const;
+
+    // Merge new block device information into previous values. Alignment values
+    // are only overwritten if the new values are non-zero.
+    void set_block_device_info(const BlockDeviceInfo& device_info);
+    const BlockDeviceInfo& block_device_info() const { return device_info_; }
+
+  private:
+    MetadataBuilder();
+    bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+    bool Init(const LpMetadata& metadata);
+
+    uint64_t AlignSector(uint64_t sector);
+
+    LpMetadataGeometry geometry_;
+    LpMetadataHeader header_;
+    std::vector<std::unique_ptr<Partition>> partitions_;
+    BlockDeviceInfo device_info_;
+};
+
+// Read BlockDeviceInfo for a given block device. This always returns false
+// for non-Linux operating systems.
+bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_METADATA_BUILDER_H */
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
new file mode 100644
index 0000000..627aa8c
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -0,0 +1,78 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef LIBLP_LIBLP_H
+#define LIBLP_LIBLP_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Helper structure for easily interpreting deserialized metadata, or
+// re-serializing metadata.
+struct LpMetadata {
+    LpMetadataGeometry geometry;
+    LpMetadataHeader header;
+    std::vector<LpMetadataPartition> partitions;
+    std::vector<LpMetadataExtent> extents;
+};
+
+// Place an initial partition table on the device. This will overwrite the
+// existing geometry, and should not be used for normal partition table
+// updates. False can be returned if the geometry is incompatible with the
+// block device or an I/O error occurs.
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                         uint32_t slot_number);
+
+// Update the partition table for a given metadata slot number. False is
+// returned if an error occurs, which can include:
+//  - Invalid slot number.
+//  - I/O error.
+//  - Corrupt or missing metadata geometry on disk.
+//  - Incompatible geometry.
+bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                          uint32_t slot_number);
+
+// Read logical partition metadata from its predetermined location on a block
+// device. If readback fails, we also attempt to load from a backup copy.
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+
+// Read/Write logical partition metadata to an image file, for diagnostics or
+// flashing.
+bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+                       const std::map<std::string, std::string>& images);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+
+// Helper to extract safe C++ strings from partition info.
+std::string GetPartitionName(const LpMetadataPartition& partition);
+std::string GetPartitionGuid(const LpMetadataPartition& partition);
+
+// Helper to return a slot number for a slot suffix.
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif  // LIBLP_LIBLP_H
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
new file mode 100644
index 0000000..e1323e1
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOGICAL_PARTITION_METADATA_FORMAT_H_
+#define LOGICAL_PARTITION_METADATA_FORMAT_H_
+
+#ifdef __cplusplus
+#include <string>
+#include <vector>
+#endif
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Magic signature for LpMetadataGeometry. */
+#define LP_METADATA_GEOMETRY_MAGIC 0x616c4467
+
+/* Space reserved for geometry information. */
+#define LP_METADATA_GEOMETRY_SIZE 4096
+
+/* Magic signature for LpMetadataHeader. */
+#define LP_METADATA_HEADER_MAGIC 0x414C5030
+
+/* Current metadata version. */
+#define LP_METADATA_MAJOR_VERSION 1
+#define LP_METADATA_MINOR_VERSION 0
+
+/* Attributes for the LpMetadataPartition::attributes field.
+ *
+ * READONLY - The partition should not be considered writable. When used with
+ * device mapper, the block device will be created as read-only.
+ */
+#define LP_PARTITION_ATTR_NONE 0x0
+#define LP_PARTITION_ATTR_READONLY 0x1
+
+/* Mask that defines all valid attributes. */
+#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY)
+
+/* Default name of the physical partition that holds logical partition entries.
+ * The layout of this partition will look like:
+ *
+ *     +--------------------+
+ *     | Disk Geometry      |
+ *     +--------------------+
+ *     | Metadata           |
+ *     +--------------------+
+ *     | Logical Partitions |
+ *     +--------------------+
+ *     | Backup Metadata    |
+ *     +--------------------+
+ *     | Geometry Backup    |
+ *     +--------------------+
+ */
+#define LP_METADATA_PARTITION_NAME "super"
+
+/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
+#define LP_SECTOR_SIZE 512
+
+/* This structure is stored at sector 0 in the first 4096 bytes of the
+ * partition, and again in the very last 4096 bytes. It is never modified and
+ * describes how logical partition information can be located.
+ */
+typedef struct LpMetadataGeometry {
+    /*  0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */
+    uint32_t magic;
+
+    /*  4: Size of the LpMetadataGeometry struct. */
+    uint32_t struct_size;
+
+    /*  8: SHA256 checksum of this struct, with this field set to 0. */
+    uint8_t checksum[32];
+
+    /* 40: Maximum amount of space a single copy of the metadata can use. This
+     * must be a multiple of LP_SECTOR_SIZE.
+     */
+    uint32_t metadata_max_size;
+
+    /* 44: Number of copies of the metadata to keep. For A/B devices, this
+     * will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B
+     * it will be 1. A backup copy of each slot is kept, so if this is "2",
+     * there will be four copies total.
+     */
+    uint32_t metadata_slot_count;
+
+    /* 48: First usable sector for allocating logical partitions. this will be
+     * the first sector after the initial 4096 geometry block, followed by the
+     * space consumed by metadata_max_size*metadata_slot_count.
+     */
+    uint64_t first_logical_sector;
+
+    /* 56: Last usable sector, inclusive, for allocating logical partitions.
+     * At the end of this sector will follow backup metadata slots and the
+     * backup geometry block at the very end.
+     */
+    uint64_t last_logical_sector;
+
+    /* 64: Alignment for defining partitions or partition extents. For example,
+     * an alignment of 1MiB will require that all partitions have a size evenly
+     * divisible by 1MiB, and that the smallest unit the partition can grow by
+     * is 1MiB.
+     *
+     * Alignment is normally determined at runtime when growing or adding
+     * partitions. If for some reason the alignment cannot be determined, then
+     * this predefined alignment in the geometry is used instead. By default
+     * it is set to 1MiB.
+     */
+    uint32_t alignment;
+
+    /* 68: Alignment offset for "stacked" devices. For example, if the "super"
+     * partition itself is not aligned within the parent block device's
+     * partition table, then we adjust for this in deciding where to place
+     * |first_logical_sector|.
+     *
+     * Similar to |alignment|, this will be derived from the operating system.
+     * If it cannot be determined, it is assumed to be 0.
+     */
+    uint32_t alignment_offset;
+
+    /* 72: Block device size, as specified when the metadata was created. This
+     * can be used to verify the geometry against a target device.
+     */
+    uint64_t block_device_size;
+} __attribute__((packed)) LpMetadataGeometry;
+
+/* The logical partition metadata has a number of tables; they are described
+ * in the header via the following structure.
+ *
+ * The size of the table can be computed by multiplying entry_size by
+ * num_entries, and the result must not overflow a 32-bit signed integer.
+ */
+typedef struct LpMetadataTableDescriptor {
+    /*  0: Location of the table, relative to the metadata header. */
+    uint32_t offset;
+    /*  4: Number of entries in the table. */
+    uint32_t num_entries;
+    /*  8: Size of each entry in the table, in bytes. */
+    uint32_t entry_size;
+} __attribute__((packed)) LpMetadataTableDescriptor;
+
+/* Binary format for the header of the logical partition metadata format.
+ *
+ * The format has three sections. The header must occur first, and the
+ * proceeding tables may be placed in any order after.
+ *
+ *  +-----------------------------------------+
+ *  | Header data - fixed size                |
+ *  +-----------------------------------------+
+ *  | Partition table - variable size         |
+ *  +-----------------------------------------+
+ *  | Partition table extents - variable size |
+ *  +-----------------------------------------+
+ *
+ * The "Header" portion is described by LpMetadataHeader. It will always
+ * precede the other three blocks.
+ *
+ * All fields are stored in little-endian byte order when serialized.
+ *
+ * This struct is versioned; see the |major_version| and |minor_version|
+ * fields.
+ */
+typedef struct LpMetadataHeader {
+    /*  0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
+    uint32_t magic;
+
+    /*  4: Version number required to read this metadata. If the version is not
+     * equal to the library version, the metadata should be considered
+     * incompatible.
+     */
+    uint16_t major_version;
+
+    /*  6: Minor version. A library supporting newer features should be able to
+     * read metadata with an older minor version. However, an older library
+     * should not support reading metadata if its minor version is higher.
+     */
+    uint16_t minor_version;
+
+    /*  8: The size of this header struct. */
+    uint32_t header_size;
+
+    /* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
+     * if this field were set to 0.
+     */
+    uint8_t header_checksum[32];
+
+    /* 44: The total size of all tables. This size is contiguous; tables may not
+     * have gaps in between, and they immediately follow the header.
+     */
+    uint32_t tables_size;
+
+    /* 48: SHA256 checksum of all table contents. */
+    uint8_t tables_checksum[32];
+
+    /* 80: Partition table descriptor. */
+    LpMetadataTableDescriptor partitions;
+    /* 92: Extent table descriptor. */
+    LpMetadataTableDescriptor extents;
+} __attribute__((packed)) LpMetadataHeader;
+
+/* This struct defines a logical partition entry, similar to what would be
+ * present in a GUID Partition Table.
+ */
+typedef struct LpMetadataPartition {
+    /*  0: Name of this partition in ASCII characters. Any unused characters in
+     * the buffer must be set to 0. Characters may only be alphanumeric or _.
+     * The name must include at least one ASCII character, and it must be unique
+     * across all partition names. The length (36) is the same as the maximum
+     * length of a GPT partition name.
+     */
+    char name[36];
+
+    /* 36: Globally unique identifier (GUID) of this partition. */
+    uint8_t guid[16];
+
+    /* 52: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
+    uint32_t attributes;
+
+    /* 56: Index of the first extent owned by this partition. The extent will
+     * start at logical sector 0. Gaps between extents are not allowed.
+     */
+    uint32_t first_extent_index;
+
+    /* 60: Number of extents in the partition. Every partition must have at
+     * least one extent.
+     */
+    uint32_t num_extents;
+} __attribute__((packed)) LpMetadataPartition;
+
+/* This extent is a dm-linear target, and the index is an index into the
+ * LinearExtent table.
+ */
+#define LP_TARGET_TYPE_LINEAR 0
+
+/* This extent is a dm-zero target. The index is ignored and must be 0. */
+#define LP_TARGET_TYPE_ZERO 1
+
+/* This struct defines an extent entry in the extent table block. */
+typedef struct LpMetadataExtent {
+    /*  0: Length of this extent, in 512-byte sectors. */
+    uint64_t num_sectors;
+
+    /*  8: Target type for device-mapper (see LP_TARGET_TYPE_* values). */
+    uint32_t target_type;
+
+    /* 12: Contents depends on target_type.
+     *
+     * LINEAR: The sector on the physical partition that this extent maps onto.
+     * ZERO: This field must be 0.
+     */
+    uint64_t target_data;
+} __attribute__((packed)) LpMetadataExtent;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
new file mode 100644
index 0000000..bbbedc7
--- /dev/null
+++ b/fs_mgr/liblp/io_test.cpp
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+
+#include "images.h"
+#include "reader.h"
+#include "utility.h"
+#include "writer.h"
+
+using namespace std;
+using namespace android::fs_mgr;
+using unique_fd = android::base::unique_fd;
+
+// Our tests assume a 128KiB disk with two 512 byte metadata slots.
+static const size_t kDiskSize = 131072;
+static const size_t kMetadataSize = 512;
+static const size_t kMetadataSlots = 2;
+static const char* TEST_GUID_BASE = "A799D1D6-669F-41D8-A3F0-EBB7572D830";
+static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
+
+// Helper function for creating an in-memory file descriptor. This lets us
+// simulate read/writing logical partition metadata as if we had a block device
+// for a physical partition.
+static unique_fd CreateFakeDisk(off_t size) {
+    unique_fd fd(syscall(__NR_memfd_create, "fake_disk", MFD_ALLOW_SEALING));
+    if (fd < 0) {
+        perror("memfd_create");
+        return {};
+    }
+    if (ftruncate(fd, size) < 0) {
+        perror("ftruncate");
+        return {};
+    }
+    // Prevent anything from accidentally growing/shrinking the file, as it
+    // would not be allowed on an actual partition.
+    if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+        perror("fcntl");
+        return {};
+    }
+    // Write garbage to the "disk" so we can tell what has been zeroed or not.
+    unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(size);
+    memset(buffer.get(), 0xcc, size);
+    if (!android::base::WriteFully(fd, buffer.get(), size)) {
+        return {};
+    }
+    return fd;
+}
+
+// Create a disk of the default size.
+static unique_fd CreateFakeDisk() {
+    return CreateFakeDisk(kDiskSize);
+}
+
+// Create a MetadataBuilder around some default sizes.
+static unique_ptr<MetadataBuilder> CreateDefaultBuilder() {
+    unique_ptr<MetadataBuilder> builder =
+            MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+    return builder;
+}
+
+static bool AddDefaultPartitions(MetadataBuilder* builder) {
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_NONE);
+    if (!system) {
+        return false;
+    }
+    return builder->GrowPartition(system, 24 * 1024);
+}
+
+// Create a temporary disk and flash it with the default partition setup.
+static unique_fd CreateFlashedDisk() {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    if (!builder || !AddDefaultPartitions(builder.get())) {
+        return {};
+    }
+    unique_fd fd = CreateFakeDisk();
+    if (fd < 0) {
+        return {};
+    }
+    // Export and flash.
+    unique_ptr<LpMetadata> exported = builder->Export();
+    if (!exported) {
+        return {};
+    }
+    if (!FlashPartitionTable(fd, *exported.get(), 0)) {
+        return {};
+    }
+    return fd;
+}
+
+// Test that our CreateFakeDisk() function works.
+TEST(liblp, CreateFakeDisk) {
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    uint64_t size;
+    ASSERT_TRUE(GetDescriptorSize(fd, &size));
+    ASSERT_EQ(size, kDiskSize);
+}
+
+// Flashing metadata should not work if the metadata was created for a larger
+// disk than the destination disk.
+TEST(liblp, ExportDiskTooSmall) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2);
+    ASSERT_NE(builder, nullptr);
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // A larger geometry should fail to flash, since there won't be enough
+    // space to store the logical partition range that was specified.
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    EXPECT_FALSE(FlashPartitionTable(fd, *exported.get(), 0));
+}
+
+// Test the basics of flashing a partition and reading it back.
+TEST(liblp, FlashAndReadback) {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    // Export and flash.
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
+
+    // Read back. Note that some fields are only filled in during
+    // serialization, so exported and imported will not be identical. For
+    // example, table sizes and checksums are computed in WritePartitionTable.
+    // Therefore we check on a field-by-field basis.
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+
+    // Check geometry and header.
+    EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size);
+    EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count);
+    EXPECT_EQ(exported->geometry.first_logical_sector, imported->geometry.first_logical_sector);
+    EXPECT_EQ(exported->geometry.last_logical_sector, imported->geometry.last_logical_sector);
+    EXPECT_EQ(exported->header.major_version, imported->header.major_version);
+    EXPECT_EQ(exported->header.minor_version, imported->header.minor_version);
+    EXPECT_EQ(exported->header.header_size, imported->header.header_size);
+
+    // Check partition tables.
+    ASSERT_EQ(exported->partitions.size(), imported->partitions.size());
+    EXPECT_EQ(GetPartitionName(exported->partitions[0]), GetPartitionName(imported->partitions[0]));
+    EXPECT_EQ(GetPartitionGuid(exported->partitions[0]), GetPartitionGuid(imported->partitions[0]));
+    EXPECT_EQ(exported->partitions[0].attributes, imported->partitions[0].attributes);
+    EXPECT_EQ(exported->partitions[0].first_extent_index,
+              imported->partitions[0].first_extent_index);
+    EXPECT_EQ(exported->partitions[0].num_extents, imported->partitions[0].num_extents);
+
+    // Check extent tables.
+    ASSERT_EQ(exported->extents.size(), imported->extents.size());
+    EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors);
+    EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type);
+    EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data);
+}
+
+// Test that we can update metadata slots without disturbing others.
+TEST(liblp, UpdateAnyMetadataSlot) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_EQ(imported->partitions.size(), 1);
+    EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
+
+    // Verify that we can't read unwritten metadata.
+    ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
+
+    // Change the name before writing to the next slot.
+    strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
+
+    // Read back the original slot, make sure it hasn't changed.
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_EQ(imported->partitions.size(), 1);
+    EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
+
+    // Now read back the new slot, and verify that it has a different name.
+    imported = ReadMetadata(fd, 1);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_EQ(imported->partitions.size(), 1);
+    EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor");
+
+    // Verify that we didn't overwrite anything in the logical paritition area.
+    // We expect the disk to be filled with 0xcc on creation so we can read
+    // this back and compare it.
+    char expected[LP_SECTOR_SIZE];
+    memset(expected, 0xcc, sizeof(expected));
+    for (uint64_t i = imported->geometry.first_logical_sector;
+         i <= imported->geometry.last_logical_sector; i++) {
+        char buffer[LP_SECTOR_SIZE];
+        ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0);
+        ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+        ASSERT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+    }
+}
+
+TEST(liblp, InvalidMetadataSlot) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    // Make sure all slots are filled.
+    unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+    ASSERT_NE(metadata, nullptr);
+    for (uint32_t i = 1; i < kMetadataSlots; i++) {
+        ASSERT_TRUE(UpdatePartitionTable(fd, *metadata.get(), i));
+    }
+
+    // Verify that we can't read unavailable slots.
+    EXPECT_EQ(ReadMetadata(fd, kMetadataSlots), nullptr);
+}
+
+// Test that updating a metadata slot does not allow it to be computed based
+// on mismatching geometry.
+TEST(liblp, NoChangingGeometry) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
+
+    imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    imported->geometry.metadata_slot_count++;
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    imported->geometry.first_logical_sector++;
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    imported->geometry.last_logical_sector--;
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+}
+
+// Test that changing one bit of metadata is enough to break the checksum.
+TEST(liblp, BitFlipGeometry) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    LpMetadataGeometry geometry;
+    ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::ReadFully(fd, &geometry, sizeof(geometry)));
+
+    LpMetadataGeometry bad_geometry = geometry;
+    bad_geometry.metadata_slot_count++;
+    ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry)));
+
+    unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+    ASSERT_NE(metadata, nullptr);
+    EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
+}
+
+TEST(liblp, ReadBackupGeometry) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    char corruption[LP_METADATA_GEOMETRY_SIZE];
+    memset(corruption, 0xff, sizeof(corruption));
+
+    // Corrupt the first 4096 bytes of the disk.
+    ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+
+    // Corrupt the last 4096 bytes too.
+    ASSERT_GE(lseek(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+}
+
+TEST(liblp, ReadBackupMetadata) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+
+    char corruption[kMetadataSize];
+    memset(corruption, 0xff, sizeof(corruption));
+
+    ASSERT_GE(lseek(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+
+    off_t offset = LP_METADATA_GEOMETRY_SIZE + kMetadataSize * 2;
+
+    // Corrupt the backup metadata.
+    ASSERT_GE(lseek(fd, -offset, SEEK_END), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+}
+
+// Test that we don't attempt to write metadata if it would overflow its
+// reserved space.
+TEST(liblp, TooManyPartitions) {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+
+    // Compute the maximum number of partitions we can fit in 1024 bytes of metadata.
+    size_t max_partitions = (kMetadataSize - sizeof(LpMetadataHeader)) / sizeof(LpMetadataPartition);
+    EXPECT_LT(max_partitions, 10);
+
+    // Add this number of partitions.
+    Partition* partition = nullptr;
+    for (size_t i = 0; i < max_partitions; i++) {
+        std::string guid = std::string(TEST_GUID) + to_string(i);
+        partition = builder->AddPartition(to_string(i), TEST_GUID, LP_PARTITION_ATTR_NONE);
+        ASSERT_NE(partition, nullptr);
+    }
+    ASSERT_NE(partition, nullptr);
+    // Add one extent to any partition to fill up more space - we're at 508
+    // bytes after this, out of 512.
+    ASSERT_TRUE(builder->GrowPartition(partition, 1024));
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    // Check that we are able to write our table.
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *exported.get(), 1));
+
+    // Check that adding one more partition overflows the metadata allotment.
+    partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(partition, nullptr);
+
+    exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // The new table should be too large to be written.
+    ASSERT_FALSE(UpdatePartitionTable(fd, *exported.get(), 1));
+
+    // Check that the first and last logical sectors weren't touched when we
+    // wrote this almost-full metadata.
+    char expected[LP_SECTOR_SIZE];
+    memset(expected, 0xcc, sizeof(expected));
+    char buffer[LP_SECTOR_SIZE];
+    ASSERT_GE(lseek(fd, exported->geometry.first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+    EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+    ASSERT_GE(lseek(fd, exported->geometry.last_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+    EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+}
+
+// Test that we can read and write image files.
+TEST(liblp, ImageFiles) {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+    unique_ptr<LpMetadata> exported = builder->Export();
+
+    unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
+    ASSERT_GE(fd, 0);
+    ASSERT_TRUE(WriteToImageFile(fd, *exported.get()));
+
+    unique_ptr<LpMetadata> imported = ReadFromImageFile(fd);
+    ASSERT_NE(imported, nullptr);
+}
+
+class BadWriter {
+  public:
+    // When requested, write garbage instead of the requested bytes, then
+    // return false.
+    bool operator()(int fd, const std::string& blob) {
+        write_count_++;
+        if (write_count_ == fail_on_write_) {
+            std::unique_ptr<char[]> new_data = std::make_unique<char[]>(blob.size());
+            memset(new_data.get(), 0xe5, blob.size());
+            EXPECT_TRUE(android::base::WriteFully(fd, new_data.get(), blob.size()));
+            return false;
+        } else {
+            if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+                return false;
+            }
+            return fail_after_write_ != write_count_;
+        }
+    }
+    void Reset() {
+        fail_on_write_ = 0;
+        fail_after_write_ = 0;
+        write_count_ = 0;
+    }
+    void FailOnWrite(int number) {
+        Reset();
+        fail_on_write_ = number;
+    }
+    void FailAfterWrite(int number) {
+        Reset();
+        fail_after_write_ = number;
+    }
+
+  private:
+    int fail_on_write_ = 0;
+    int fail_after_write_ = 0;
+    int write_count_ = 0;
+};
+
+// Test that an interrupted flash operation on the "primary" copy of metadata
+// is not fatal.
+TEST(liblp, UpdatePrimaryMetadataFailure) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    BadWriter writer;
+
+    // Read and write it back.
+    writer.FailOnWrite(1);
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+
+    // We should still be able to read the backup copy.
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+
+    // Flash again, this time fail the backup copy. We should still be able
+    // to read the primary.
+    writer.FailOnWrite(3);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+}
+
+// Test that an interrupted flash operation on the "backup" copy of metadata
+// is not fatal.
+TEST(liblp, UpdateBackupMetadataFailure) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    BadWriter writer;
+
+    // Read and write it back.
+    writer.FailOnWrite(2);
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+
+    // We should still be able to read the primary copy.
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+
+    // Flash again, this time fail the primary copy. We should still be able
+    // to read the primary.
+    writer.FailOnWrite(2);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+}
+
+// Test that an interrupted write *in between* writing metadata will read
+// the correct metadata copy. The primary is always considered newer than
+// the backup.
+TEST(liblp, UpdateMetadataCleanFailure) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    BadWriter writer;
+
+    // Change the name of the existing partition.
+    unique_ptr<LpMetadata> new_table = ReadMetadata(fd, 0);
+    ASSERT_NE(new_table, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    new_table->partitions[0].name[0]++;
+
+    // Flash it, but fail to write the backup copy.
+    writer.FailAfterWrite(2);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+
+    // When we read back, we should get the updated primary copy.
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
+
+    // Flash again. After, the backup and primary copy should be coherent.
+    // Note that the sync step should have used the primary to sync, not
+    // the backup.
+    writer.Reset();
+    ASSERT_TRUE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
+}
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
new file mode 100644
index 0000000..117da59
--- /dev/null
+++ b/fs_mgr/liblp/reader.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "reader.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
+// LP_METADATA_GEOMETRY_SIZE bytes in size.
+static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
+    static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE);
+    memcpy(geometry, buffer, sizeof(*geometry));
+
+    // Check the magic signature.
+    if (geometry->magic != LP_METADATA_GEOMETRY_MAGIC) {
+        LERROR << "Logical partition metadata has invalid geometry magic signature.";
+        return false;
+    }
+    // Reject if the struct size is larger than what we compiled. This is so we
+    // can compute a checksum with the |struct_size| field rather than using
+    // sizeof.
+    if (geometry->struct_size > sizeof(LpMetadataGeometry)) {
+        LERROR << "Logical partition metadata has unrecognized fields.";
+        return false;
+    }
+    // Recompute and check the CRC32.
+    {
+        LpMetadataGeometry temp = *geometry;
+        memset(&temp.checksum, 0, sizeof(temp.checksum));
+        SHA256(&temp, temp.struct_size, temp.checksum);
+        if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {
+            LERROR << "Logical partition metadata has invalid geometry checksum.";
+            return false;
+        }
+    }
+    // Check that the struct size is equal (this will have to change if we ever
+    // change the struct size in a release).
+    if (geometry->struct_size != sizeof(LpMetadataGeometry)) {
+        LERROR << "Logical partition metadata has invalid struct size.";
+        return false;
+    }
+    if (geometry->metadata_slot_count == 0) {
+        LERROR << "Logical partition metadata has invalid slot count.";
+        return false;
+    }
+    if (geometry->metadata_max_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Metadata max size is not sector-aligned.";
+        return false;
+    }
+
+    // Check that the metadata area and logical partition areas don't overlap.
+    int64_t end_of_metadata =
+            GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) +
+            geometry->metadata_max_size;
+    if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << "Logical partition metadata overlaps with logical partition contents.";
+        return false;
+    }
+    return true;
+}
+
+// Read and validate geometry information from a block device that holds
+// logical partitions. If the information is corrupted, this will attempt
+// to read it from a secondary backup location.
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
+    // Read the first 4096 bytes.
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+        return false;
+    }
+    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
+        return false;
+    }
+    if (ParseGeometry(buffer.get(), geometry)) {
+        return true;
+    }
+
+    // Try the backup copy in the last 4096 bytes.
+    if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE;
+        return false;
+    }
+    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << "backup read " << LP_METADATA_GEOMETRY_SIZE
+               << " bytes failed";
+        return false;
+    }
+    return ParseGeometry(buffer.get(), geometry);
+}
+
+static bool ValidateTableBounds(const LpMetadataHeader& header,
+                                const LpMetadataTableDescriptor& table) {
+    if (table.offset > header.tables_size) {
+        return false;
+    }
+    uint64_t table_size = uint64_t(table.num_entries) * table.entry_size;
+    if (header.tables_size - table.offset < table_size) {
+        return false;
+    }
+    return true;
+}
+
+static bool ValidateMetadataHeader(const LpMetadataHeader& header) {
+    // To compute the header's checksum, we have to temporarily set its checksum
+    // field to 0.
+    {
+        LpMetadataHeader temp = header;
+        memset(&temp.header_checksum, 0, sizeof(temp.header_checksum));
+        SHA256(&temp, sizeof(temp), temp.header_checksum);
+        if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) != 0) {
+            LERROR << "Logical partition metadata has invalid checksum.";
+            return false;
+        }
+    }
+
+    // Do basic validation of key metadata bits.
+    if (header.magic != LP_METADATA_HEADER_MAGIC) {
+        LERROR << "Logical partition metadata has invalid magic value.";
+        return false;
+    }
+    // Check that the version is compatible.
+    if (header.major_version != LP_METADATA_MAJOR_VERSION ||
+        header.minor_version > LP_METADATA_MINOR_VERSION) {
+        LERROR << "Logical partition metadata has incompatible version.";
+        return false;
+    }
+    if (!ValidateTableBounds(header, header.partitions) ||
+        !ValidateTableBounds(header, header.extents)) {
+        LERROR << "Logical partition metadata has invalid table bounds.";
+        return false;
+    }
+    // Check that table entry sizes can accomodate their respective structs. If
+    // table sizes change, these checks will have to be adjusted.
+    if (header.partitions.entry_size != sizeof(LpMetadataPartition)) {
+        LERROR << "Logical partition metadata has invalid partition table entry size.";
+        return false;
+    }
+    if (header.extents.entry_size != sizeof(LpMetadataExtent)) {
+        LERROR << "Logical partition metadata has invalid extent table entry size.";
+        return false;
+    }
+    return true;
+}
+
+// Parse and validate all metadata at the current position in the given file
+// descriptor.
+std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
+    // First read and validate the header.
+    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+    if (!android::base::ReadFully(fd, &metadata->header, sizeof(metadata->header))) {
+        PERROR << __PRETTY_FUNCTION__ << "read " << sizeof(metadata->header) << "bytes failed";
+        return nullptr;
+    }
+    if (!ValidateMetadataHeader(metadata->header)) {
+        return nullptr;
+    }
+
+    LpMetadataHeader& header = metadata->header;
+
+    // Read the metadata payload. Allocation is fallible in case the metadata is
+    // corrupt and has some huge value.
+    std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[header.tables_size]);
+    if (!buffer) {
+        LERROR << "Out of memory reading logical partition tables.";
+        return nullptr;
+    }
+    if (!android::base::ReadFully(fd, buffer.get(), header.tables_size)) {
+        PERROR << __PRETTY_FUNCTION__ << "read " << header.tables_size << "bytes failed";
+        return nullptr;
+    }
+
+    uint8_t checksum[32];
+    SHA256(buffer.get(), header.tables_size, checksum);
+    if (memcmp(checksum, header.tables_checksum, sizeof(checksum)) != 0) {
+        LERROR << "Logical partition metadata has invalid table checksum.";
+        return nullptr;
+    }
+
+    // ValidateTableSize ensured that |cursor| is valid for the number of
+    // entries in the table.
+    uint8_t* cursor = buffer.get() + header.partitions.offset;
+    for (size_t i = 0; i < header.partitions.num_entries; i++) {
+        LpMetadataPartition partition;
+        memcpy(&partition, cursor, sizeof(partition));
+        cursor += header.partitions.entry_size;
+
+        if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
+            LERROR << "Logical partition has invalid attribute set.";
+            return nullptr;
+        }
+        if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
+            LERROR << "Logical partition has invalid extent list.";
+            return nullptr;
+        }
+
+        metadata->partitions.push_back(partition);
+    }
+
+    cursor = buffer.get() + header.extents.offset;
+    for (size_t i = 0; i < header.extents.num_entries; i++) {
+        LpMetadataExtent extent;
+        memcpy(&extent, cursor, sizeof(extent));
+        cursor += header.extents.entry_size;
+
+        metadata->extents.push_back(extent);
+    }
+
+    return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
+                                                uint32_t slot_number) {
+    int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        return nullptr;
+    }
+    return ParseMetadata(fd);
+}
+
+std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
+                                               uint32_t slot_number) {
+    int64_t offset = GetBackupMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, offset, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        return nullptr;
+    }
+    return ParseMetadata(fd);
+}
+
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
+    LpMetadataGeometry geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+        return nullptr;
+    }
+
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << __PRETTY_FUNCTION__ << "invalid metadata slot number";
+        return nullptr;
+    }
+
+    // Read the priamry copy, and if that fails, try the backup.
+    std::unique_ptr<LpMetadata> metadata = ReadPrimaryMetadata(fd, geometry, slot_number);
+    if (!metadata) {
+        metadata = ReadBackupMetadata(fd, geometry, slot_number);
+    }
+    if (metadata) {
+        metadata->geometry = geometry;
+    }
+    return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
+    android::base::unique_fd fd(open(block_device, O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return nullptr;
+    }
+    return ReadMetadata(fd, slot_number);
+}
+
+static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
+    // If the end of the buffer has a null character, it's safe to assume the
+    // buffer is null terminated. Otherwise, we cap the string to the input
+    // buffer size.
+    if (name[buffer_size - 1] == '\0') {
+        return std::string(name);
+    }
+    return std::string(name, buffer_size);
+}
+
+std::string GetPartitionName(const LpMetadataPartition& partition) {
+    return NameFromFixedArray(partition.name, sizeof(partition.name));
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
new file mode 100644
index 0000000..843b2f2
--- /dev/null
+++ b/fs_mgr/liblp/reader.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBLP_READER_H_
+#define LIBLP_READER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
+
+// Helper functions for manually reading geometry and metadata.
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
+std::unique_ptr<LpMetadata> ParseMetadata(int fd);
+
+// These functions assume a valid geometry and slot number.
+std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
+                                                uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
+                                               uint32_t slot_number);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_READER_H_ */
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
new file mode 100644
index 0000000..a590037
--- /dev/null
+++ b/fs_mgr/liblp/utility.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <ext4_utils/ext4_utils.h>
+#include <openssl/sha.h>
+#include <uuid/uuid.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+bool GetDescriptorSize(int fd, uint64_t* size) {
+    struct stat s;
+    if (fstat(fd, &s) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "fstat failed";
+        return false;
+    }
+
+    if (S_ISBLK(s.st_mode)) {
+        *size = get_block_device_size(fd);
+        return *size != 0;
+    }
+
+    int64_t result = SeekFile64(fd, 0, SEEK_END);
+    if (result == -1) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+        return false;
+    }
+
+    *size = result;
+    return true;
+}
+
+int64_t SeekFile64(int fd, int64_t offset, int whence) {
+    static_assert(sizeof(off_t) == sizeof(int64_t), "Need 64-bit lseek");
+    return lseek(fd, offset, whence);
+}
+
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+    CHECK(slot_number < geometry.metadata_slot_count);
+
+    int64_t offset = LP_METADATA_GEOMETRY_SIZE + geometry.metadata_max_size * slot_number;
+    CHECK(offset + geometry.metadata_max_size <=
+          int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
+    return offset;
+}
+
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+    CHECK(slot_number < geometry.metadata_slot_count);
+    int64_t start = int64_t(-LP_METADATA_GEOMETRY_SIZE) -
+                    int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+    return start + int64_t(geometry.metadata_max_size * slot_number);
+}
+
+void SHA256(const void* data, size_t length, uint8_t out[32]) {
+    SHA256_CTX c;
+    SHA256_Init(&c);
+    SHA256_Update(&c, data, length);
+    SHA256_Final(out, &c);
+}
+
+std::string GetPartitionGuid(const LpMetadataPartition& partition) {
+    // 32 hex characters, four hyphens. Unfortunately libext2_uuid provides no
+    // macro to assist with buffer sizing.
+    static const size_t kGuidLen = 36;
+    char buffer[kGuidLen + 1];
+    uuid_unparse_upper(partition.guid, buffer);
+    return buffer;
+}
+
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
+    if (suffix.empty()) {
+        return 0;
+    }
+    if (suffix.size() != 2 || suffix[0] != '_' || suffix[1] < 'a') {
+        LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
+               << "' does not have a recognized format.";
+        return 0;
+    }
+    return suffix[1] - 'a';
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
new file mode 100644
index 0000000..4522275
--- /dev/null
+++ b/fs_mgr/liblp/utility.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBLP_UTILITY_H
+#define LIBLP_UTILITY_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+
+#include "liblp/metadata_format.h"
+
+#define LP_TAG "[liblp]"
+#define LERROR LOG(ERROR) << LP_TAG
+#define PERROR PLOG(ERROR) << LP_TAG
+
+namespace android {
+namespace fs_mgr {
+
+// Determine the size of a block device (or file). Logs and returns false on
+// error. After calling this, the position of |fd| may have changed.
+bool GetDescriptorSize(int fd, uint64_t* size);
+
+// Return the offset of a primary metadata slot, relative to the start of the
+// device.
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Return the offset of a backup metadata slot, relative to the end of the
+// device.
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Cross-platform helper for lseek64().
+int64_t SeekFile64(int fd, int64_t offset, int whence);
+
+// Compute a SHA256 hash.
+void SHA256(const void* data, size_t length, uint8_t out[32]);
+
+// Align |base| such that it is evenly divisible by |alignment|, which does not
+// have to be a power of two.
+constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) {
+    if (!alignment) {
+        return base;
+    }
+    uint64_t remainder = base % alignment;
+    if (remainder == 0) {
+        return base;
+    }
+    return base + (alignment - remainder);
+}
+
+// Same as the above |AlignTo|, except that |base| is only aligned when added to
+// |alignment_offset|.
+constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment, uint32_t alignment_offset) {
+    uint64_t aligned = AlignTo(base, alignment) + alignment_offset;
+    if (aligned - alignment >= base) {
+        // We overaligned (base < alignment_offset).
+        return aligned - alignment;
+    }
+    return aligned;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif  // LIBLP_UTILITY_H
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
new file mode 100644
index 0000000..092dbf1
--- /dev/null
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <liblp/liblp.h>
+
+#include "utility.h"
+
+using namespace android;
+using namespace android::fs_mgr;
+
+TEST(liblp, SlotNumberForSlotSuffix) {
+    EXPECT_EQ(SlotNumberForSlotSuffix(""), 0);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_a"), 0);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_b"), 1);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 2);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 3);
+}
+
+TEST(liblp, GetMetadataOffset) {
+    LpMetadataGeometry geometry = {LP_METADATA_GEOMETRY_MAGIC,
+                                   sizeof(geometry),
+                                   {0},
+                                   16384,
+                                   4,
+                                   10000,
+                                   80000,
+                                   0,
+                                   0,
+                                   1024 * 1024};
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 4096 + 16384 * 3);
+
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), -4096 - 16384 * 1);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), -4096 - 16384 * 2);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4);
+}
+
+TEST(liblp, AlignTo) {
+    EXPECT_EQ(AlignTo(37, 0), 37);
+    EXPECT_EQ(AlignTo(1024, 1024), 1024);
+    EXPECT_EQ(AlignTo(555, 1024), 1024);
+    EXPECT_EQ(AlignTo(555, 1000), 1000);
+    EXPECT_EQ(AlignTo(0, 1024), 0);
+    EXPECT_EQ(AlignTo(54, 32, 30), 62);
+    EXPECT_EQ(AlignTo(32, 32, 30), 62);
+    EXPECT_EQ(AlignTo(17, 32, 30), 30);
+}
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
new file mode 100644
index 0000000..156319b
--- /dev/null
+++ b/fs_mgr/liblp/writer.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "writer.h"
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "reader.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+std::string SerializeGeometry(const LpMetadataGeometry& input) {
+    LpMetadataGeometry geometry = input;
+    memset(geometry.checksum, 0, sizeof(geometry.checksum));
+    SHA256(&geometry, sizeof(geometry), geometry.checksum);
+
+    std::string blob(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
+    blob.resize(LP_METADATA_GEOMETRY_SIZE);
+    return blob;
+}
+
+static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
+    return g1.metadata_max_size == g2.metadata_max_size &&
+           g1.metadata_slot_count == g2.metadata_slot_count &&
+           g1.first_logical_sector == g2.first_logical_sector &&
+           g1.last_logical_sector == g2.last_logical_sector;
+}
+
+std::string SerializeMetadata(const LpMetadata& input) {
+    LpMetadata metadata = input;
+    LpMetadataHeader& header = metadata.header;
+
+    // Serialize individual tables.
+    std::string partitions(reinterpret_cast<const char*>(metadata.partitions.data()),
+                           metadata.partitions.size() * sizeof(LpMetadataPartition));
+    std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
+                        metadata.extents.size() * sizeof(LpMetadataExtent));
+
+    // Compute positions of tables.
+    header.partitions.offset = 0;
+    header.extents.offset = header.partitions.offset + partitions.size();
+    header.tables_size = header.extents.offset + extents.size();
+
+    // Compute payload checksum.
+    std::string tables = partitions + extents;
+    SHA256(tables.data(), tables.size(), header.tables_checksum);
+
+    // Compute header checksum.
+    memset(header.header_checksum, 0, sizeof(header.header_checksum));
+    SHA256(&header, sizeof(header), header.header_checksum);
+
+    std::string header_blob =
+            std::string(reinterpret_cast<const char*>(&metadata.header), sizeof(metadata.header));
+    return header_blob + tables;
+}
+
+// Perform sanity checks so we don't accidentally overwrite valid metadata
+// with potentially invalid metadata, or random partition data with metadata.
+static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std::string* blob) {
+    uint64_t blockdevice_size;
+    if (!GetDescriptorSize(fd, &blockdevice_size)) {
+        return false;
+    }
+
+    *blob = SerializeMetadata(metadata);
+
+    const LpMetadataHeader& header = metadata.header;
+    const LpMetadataGeometry& geometry = metadata.geometry;
+    // Validate the usable sector range.
+    if (geometry.first_logical_sector > geometry.last_logical_sector) {
+        LERROR << "Logical partition metadata has invalid sector range.";
+        return false;
+    }
+    // Make sure we're writing within the space reserved.
+    if (blob->size() > geometry.metadata_max_size) {
+        LERROR << "Logical partition metadata is too large.";
+        return false;
+    }
+
+    // Make sure the device has enough space to store two backup copies of the
+    // metadata.
+    uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
+                             uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+    if (reserved_size > blockdevice_size ||
+        reserved_size > geometry.first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << "Not enough space to store all logical partition metadata slots.";
+        return false;
+    }
+    if (blockdevice_size - reserved_size < (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE) {
+        LERROR << "Not enough space to backup all logical partition metadata slots.";
+        return false;
+    }
+    if (blockdevice_size != metadata.geometry.block_device_size) {
+        LERROR << "Block device size " << blockdevice_size
+               << " does not match metadata requested size " << metadata.geometry.block_device_size;
+        return false;
+    }
+
+    // Make sure all partition entries reference valid extents.
+    for (const auto& partition : metadata.partitions) {
+        if (partition.first_extent_index + partition.num_extents > metadata.extents.size()) {
+            LERROR << "Partition references invalid extent.";
+            return false;
+        }
+    }
+
+    // Make sure all linear extents have a valid range.
+    for (const auto& extent : metadata.extents) {
+        if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+            uint64_t physical_sector = extent.target_data;
+            if (physical_sector < geometry.first_logical_sector ||
+                physical_sector + extent.num_sectors > geometry.last_logical_sector) {
+                LERROR << "Extent table entry is out of bounds.";
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                                 const std::string& blob,
+                                 const std::function<bool(int, const std::string&)>& writer) {
+    int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset;
+        return false;
+    }
+    if (!writer(fd, blob)) {
+        PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+        return false;
+    }
+    return true;
+}
+
+static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                                const std::string& blob,
+                                const std::function<bool(int, const std::string&)>& writer) {
+    int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
+    int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_END);
+    if (abs_offset == (int64_t)-1) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << backup_offset;
+        return false;
+    }
+    if (abs_offset < int64_t((geometry.last_logical_sector + 1) * LP_SECTOR_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << "backup offset " << abs_offset
+               << " is within logical partition bounds, sector " << geometry.last_logical_sector;
+        return false;
+    }
+    if (!writer(fd, blob)) {
+        PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+        return false;
+    }
+    return true;
+}
+
+static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                          const std::string& blob,
+                          const std::function<bool(int, const std::string&)>& writer) {
+    // Make sure we're writing to a valid metadata slot.
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << "Invalid logical partition metadata slot number.";
+        return false;
+    }
+    if (!WritePrimaryMetadata(fd, geometry, slot_number, blob, writer)) {
+        return false;
+    }
+    if (!WriteBackupMetadata(fd, geometry, slot_number, blob, writer)) {
+        return false;
+    }
+    return true;
+}
+
+static bool DefaultWriter(int fd, const std::string& blob) {
+    return android::base::WriteFully(fd, blob.data(), blob.size());
+}
+
+bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
+    // Before writing geometry and/or logical partition tables, perform some
+    // basic checks that the geometry and tables are coherent, and will fit
+    // on the given block device.
+    std::string metadata_blob;
+    if (!ValidateAndSerializeMetadata(fd, metadata, &metadata_blob)) {
+        return false;
+    }
+
+    // Write geometry to the first and last 4096 bytes of the device.
+    std::string blob = SerializeGeometry(metadata.geometry);
+    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+        return false;
+    }
+    if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+        return false;
+    }
+
+    // Write metadata to the correct slot, now that geometry is in place.
+    return WriteMetadata(fd, metadata.geometry, slot_number, metadata_blob, DefaultWriter);
+}
+
+static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
+    return !memcmp(a.header.header_checksum, b.header.header_checksum,
+                   sizeof(a.header.header_checksum));
+}
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+                          const std::function<bool(int, const std::string&)>& writer) {
+    // Before writing geometry and/or logical partition tables, perform some
+    // basic checks that the geometry and tables are coherent, and will fit
+    // on the given block device.
+    std::string blob;
+    if (!ValidateAndSerializeMetadata(fd, metadata, &blob)) {
+        return false;
+    }
+
+    // Verify that the old geometry is identical. If it's not, then we might be
+    // writing a table that was built for a different device, so we must reject
+    // it.
+    const LpMetadataGeometry& geometry = metadata.geometry;
+    LpMetadataGeometry old_geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
+        return false;
+    }
+    if (!CompareGeometry(geometry, old_geometry)) {
+        LERROR << "Incompatible geometry in new logical partition metadata";
+        return false;
+    }
+
+    // Validate the slot number now, before we call Read*Metadata.
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << "Invalid logical partition metadata slot number.";
+        return false;
+    }
+
+    // Try to read both existing copies of the metadata, if any.
+    std::unique_ptr<LpMetadata> primary = ReadPrimaryMetadata(fd, geometry, slot_number);
+    std::unique_ptr<LpMetadata> backup = ReadBackupMetadata(fd, geometry, slot_number);
+
+    if (primary && (!backup || !CompareMetadata(*primary.get(), *backup.get()))) {
+        // If the backup copy does not match the primary copy, we first
+        // synchronize the backup copy. This guarantees that a partial write
+        // still leaves one copy intact.
+        std::string old_blob;
+        if (!ValidateAndSerializeMetadata(fd, *primary.get(), &old_blob)) {
+            LERROR << "Error serializing primary metadata to repair corrupted backup";
+            return false;
+        }
+        if (!WriteBackupMetadata(fd, geometry, slot_number, old_blob, writer)) {
+            LERROR << "Error writing primary metadata to repair corrupted backup";
+            return false;
+        }
+    } else if (backup && !primary) {
+        // The backup copy is coherent, and the primary is not. Sync it for
+        // safety.
+        std::string old_blob;
+        if (!ValidateAndSerializeMetadata(fd, *backup.get(), &old_blob)) {
+            LERROR << "Error serializing primary metadata to repair corrupted backup";
+            return false;
+        }
+        if (!WritePrimaryMetadata(fd, geometry, slot_number, old_blob, writer)) {
+            LERROR << "Error writing primary metadata to repair corrupted backup";
+            return false;
+        }
+    }
+
+    // Both copies should now be in sync, so we can continue the update.
+    return WriteMetadata(fd, geometry, slot_number, blob, writer);
+}
+
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                         uint32_t slot_number) {
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return false;
+    }
+    return FlashPartitionTable(fd, metadata, slot_number);
+}
+
+bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                          uint32_t slot_number) {
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return false;
+    }
+    return UpdatePartitionTable(fd, metadata, slot_number);
+}
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
+    return UpdatePartitionTable(fd, metadata, slot_number, DefaultWriter);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/writer.h b/fs_mgr/liblp/writer.h
new file mode 100644
index 0000000..adbbebf
--- /dev/null
+++ b/fs_mgr/liblp/writer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBLP_WRITER_H
+#define LIBLP_WRITER_H
+
+#include <functional>
+#include <string>
+
+#include <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+std::string SerializeGeometry(const LpMetadataGeometry& input);
+std::string SerializeMetadata(const LpMetadata& input);
+
+// These variants are for testing only. The path-based functions should be used
+// for actual operation, so that open() is called with the correct flags.
+bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+                          const std::function<bool(int, const std::string&)>& writer);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_WRITER_H */
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
new file mode 100644
index 0000000..5497223
--- /dev/null
+++ b/fs_mgr/tests/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "fs_mgr_unit_test",
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libfs_mgr",
+        "libfstab",
+    ],
+
+    srcs: [
+        "fs_mgr_test.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
new file mode 100644
index 0000000..8b1c55a
--- /dev/null
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <linux/fs.h>
+#include <mntent.h>
+
+#include <algorithm>
+#include <iterator>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+#include <gtest/gtest.h>
+
+#include "../fs_mgr_priv_boot_config.h"
+
+namespace {
+
+const std::string cmdline =
+        "rcupdate.rcu_expedited=1 rootwait ro "
+        "init=/init androidboot.bootdevice=1d84000.ufshc "
+        "androidboot.baseband=sdy androidboot.keymaster=1  skip_initramfs "
+        "androidboot.serialno=BLAHBLAHBLAH androidboot.slot_suffix=_a "
+        "androidboot.hardware.platform=sdw813 androidboot.hardware=foo "
+        "androidboot.revision=EVT1.0 androidboot.bootloader=burp-0.1-7521 "
+        "androidboot.hardware.sku=mary androidboot.hardware.radio.subtype=0 "
+        "androidboot.dtbo_idx=2 androidboot.mode=normal "
+        "androidboot.hardware.ddr=1GB,combuchi,LPDDR4X "
+        "androidboot.ddr_info=combuchiandroidboot.ddr_size=2GB "
+        "androidboot.hardware.ufs=2GB,combushi "
+        "androidboot.boottime=0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123 "
+        "androidboot.ramdump=disabled "
+        "dm=\"1 vroot none ro 1,0 10416 verity 1 624684 fec_start 624684\" "
+        "root=/dev/dm-0 "
+        "androidboot.vbmeta.device=PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb "
+        "androidboot.vbmeta.avb_version=\"1.1\" "
+        "androidboot.vbmeta.device_state=unlocked "
+        "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=5248 "
+        "androidboot.vbmeta.digest="
+        "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860 "
+        "androidboot.vbmeta.invalidate_on_error=yes "
+        "androidboot.veritymode=enforcing androidboot.verifiedbootstate=orange "
+        "androidboot.space=\"sha256 5248 androidboot.nospace=nope\" "
+        "printk.devkmsg=on msm_rtb.filter=0x237 ehci-hcd.park=3 "
+        "\"string =\"\"string '\" "
+        "service_locator.enable=1 firmware_class.path=/vendor/firmware "
+        "cgroup.memory=nokmem lpm_levels.sleep_disabled=1 "
+        "buildvariant=userdebug  console=null "
+        "terminator=\"truncated";
+
+const std::vector<std::pair<std::string, std::string>> result_space = {
+        {"rcupdate.rcu_expedited", "1"},
+        {"rootwait", ""},
+        {"ro", ""},
+        {"init", "/init"},
+        {"androidboot.bootdevice", "1d84000.ufshc"},
+        {"androidboot.baseband", "sdy"},
+        {"androidboot.keymaster", "1"},
+        {"skip_initramfs", ""},
+        {"androidboot.serialno", "BLAHBLAHBLAH"},
+        {"androidboot.slot_suffix", "_a"},
+        {"androidboot.hardware.platform", "sdw813"},
+        {"androidboot.hardware", "foo"},
+        {"androidboot.revision", "EVT1.0"},
+        {"androidboot.bootloader", "burp-0.1-7521"},
+        {"androidboot.hardware.sku", "mary"},
+        {"androidboot.hardware.radio.subtype", "0"},
+        {"androidboot.dtbo_idx", "2"},
+        {"androidboot.mode", "normal"},
+        {"androidboot.hardware.ddr", "1GB,combuchi,LPDDR4X"},
+        {"androidboot.ddr_info", "combuchiandroidboot.ddr_size=2GB"},
+        {"androidboot.hardware.ufs", "2GB,combushi"},
+        {"androidboot.boottime", "0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123"},
+        {"androidboot.ramdump", "disabled"},
+        {"dm", "1 vroot none ro 1,0 10416 verity 1 624684 fec_start 624684"},
+        {"root", "/dev/dm-0"},
+        {"androidboot.vbmeta.device", "PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb"},
+        {"androidboot.vbmeta.avb_version", "1.1"},
+        {"androidboot.vbmeta.device_state", "unlocked"},
+        {"androidboot.vbmeta.hash_alg", "sha256"},
+        {"androidboot.vbmeta.size", "5248"},
+        {"androidboot.vbmeta.digest",
+         "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860"},
+        {"androidboot.vbmeta.invalidate_on_error", "yes"},
+        {"androidboot.veritymode", "enforcing"},
+        {"androidboot.verifiedbootstate", "orange"},
+        {"androidboot.space", "sha256 5248 androidboot.nospace=nope"},
+        {"printk.devkmsg", "on"},
+        {"msm_rtb.filter", "0x237"},
+        {"ehci-hcd.park", "3"},
+        {"string ", "string '"},
+        {"service_locator.enable", "1"},
+        {"firmware_class.path", "/vendor/firmware"},
+        {"cgroup.memory", "nokmem"},
+        {"lpm_levels.sleep_disabled", "1"},
+        {"buildvariant", "userdebug"},
+        {"console", "null"},
+        {"terminator", "truncated"},
+};
+
+}  // namespace
+
+TEST(fs_mgr, fs_mgr_parse_boot_config) {
+    EXPECT_EQ(result_space, fs_mgr_parse_boot_config(cmdline));
+}
+
+TEST(fs_mgr, fs_mgr_get_boot_config_from_kernel_cmdline) {
+    std::string content;
+    for (const auto& entry : result_space) {
+        static constexpr char androidboot[] = "androidboot.";
+        if (!android::base::StartsWith(entry.first, androidboot)) continue;
+        auto key = entry.first.substr(strlen(androidboot));
+        EXPECT_TRUE(fs_mgr_get_boot_config_from_kernel(cmdline, key, &content)) << " for " << key;
+        EXPECT_EQ(entry.second, content);
+    }
+    EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "vbmeta.avb_versio", &content));
+    EXPECT_TRUE(content.empty()) << content;
+    EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "nospace", &content));
+    EXPECT_TRUE(content.empty()) << content;
+}
+
+TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
+    auto fstab = fs_mgr_read_fstab("/proc/mounts");
+    ASSERT_NE(fstab, nullptr);
+
+    std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "r"),
+                                                           endmntent);
+    ASSERT_NE(mounts, nullptr);
+
+    mntent* mentry;
+    int i = 0;
+    while ((mentry = getmntent(mounts.get())) != nullptr) {
+        ASSERT_LT(i, fstab->num_entries);
+        auto fsrec = &fstab->recs[i];
+
+        std::string mnt_fsname(mentry->mnt_fsname ?: "nullptr");
+        std::string blk_device(fsrec->blk_device ?: "nullptr");
+        EXPECT_EQ(mnt_fsname, blk_device);
+
+        std::string mnt_dir(mentry->mnt_dir ?: "nullptr");
+        std::string mount_point(fsrec->mount_point ?: "nullptr");
+        EXPECT_EQ(mnt_dir, mount_point);
+
+        std::string mnt_type(mentry->mnt_type ?: "nullptr");
+        std::string fs_type(fsrec->fs_type ?: "nullptr");
+        EXPECT_EQ(mnt_type, fs_type);
+
+        std::set<std::string> mnt_opts;
+        for (auto& s : android::base::Split(mentry->mnt_opts ?: "nullptr", ",")) {
+            mnt_opts.emplace(s);
+        }
+        std::set<std::string> fs_options;
+        for (auto& s : android::base::Split(fsrec->fs_options ?: "nullptr", ",")) {
+            fs_options.emplace(s);
+        }
+        // matches private content in fs_mgr_fstab.c
+        static struct flag_list {
+            const char* name;
+            unsigned int flag;
+        } mount_flags[] = {
+                {"noatime", MS_NOATIME},
+                {"noexec", MS_NOEXEC},
+                {"nosuid", MS_NOSUID},
+                {"nodev", MS_NODEV},
+                {"nodiratime", MS_NODIRATIME},
+                {"ro", MS_RDONLY},
+                {"rw", 0},
+                {"remount", MS_REMOUNT},
+                {"bind", MS_BIND},
+                {"rec", MS_REC},
+                {"unbindable", MS_UNBINDABLE},
+                {"private", MS_PRIVATE},
+                {"slave", MS_SLAVE},
+                {"shared", MS_SHARED},
+                {"defaults", 0},
+                {0, 0},
+        };
+        for (auto f = 0; mount_flags[f].name; ++f) {
+            if (mount_flags[f].flag & fsrec->flags) {
+                fs_options.emplace(mount_flags[f].name);
+            }
+        }
+        if (!(fsrec->flags & MS_RDONLY)) fs_options.emplace("rw");
+        EXPECT_EQ(mnt_opts, fs_options);
+        ++i;
+    }
+}
diff --git a/fs_mgr/tools/Android.bp b/fs_mgr/tools/Android.bp
new file mode 100644
index 0000000..4d4aae4
--- /dev/null
+++ b/fs_mgr/tools/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+    name: "dmctl",
+    srcs: ["dmctl.cpp"],
+
+    static_libs: [
+        "libfs_mgr",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+
+    cflags: ["-Werror"],
+}
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
new file mode 100644
index 0000000..5e11c84
--- /dev/null
+++ b/fs_mgr/tools/dmctl.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/dm-ioctl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+#include <libdm/dm.h>
+
+#include <functional>
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using DeviceMapper = ::android::dm::DeviceMapper;
+using DmTable = ::android::dm::DmTable;
+using DmTarget = ::android::dm::DmTarget;
+using DmTargetLinear = ::android::dm::DmTargetLinear;
+using DmTargetZero = ::android::dm::DmTargetZero;
+using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
+using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
+
+static int Usage(void) {
+    std::cerr << "usage: dmctl <command> [command options]" << std::endl;
+    std::cerr << "commands:" << std::endl;
+    std::cerr << "  create <dm-name> [-ro] <targets...>" << std::endl;
+    std::cerr << "  delete <dm-name>" << std::endl;
+    std::cerr << "  list <devices | targets>" << std::endl;
+    std::cerr << "  getpath <dm-name>" << std::endl;
+    std::cerr << "  table <dm-name>" << std::endl;
+    std::cerr << "  help" << std::endl;
+    std::cerr << std::endl;
+    std::cerr << "Target syntax:" << std::endl;
+    std::cerr << "  <target_type> <start_sector> <num_sectors> [target_data]" << std::endl;
+    return -EINVAL;
+}
+
+class TargetParser final {
+  public:
+    TargetParser(int argc, char** argv) : arg_index_(0), argc_(argc), argv_(argv) {}
+
+    bool More() const { return arg_index_ < argc_; }
+    std::unique_ptr<DmTarget> Next() {
+        if (!HasArgs(3)) {
+            std::cerr << "Expected <target_type> <start_sector> <num_sectors>" << std::endl;
+            return nullptr;
+        }
+
+        std::string target_type = NextArg();
+        uint64_t start_sector, num_sectors;
+        if (!android::base::ParseUint(NextArg(), &start_sector)) {
+            std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
+            return nullptr;
+        }
+        if (!android::base::ParseUint(NextArg(), &num_sectors) || !num_sectors) {
+            std::cerr << "Expected non-zero sector count, got: " << PreviousArg() << std::endl;
+            return nullptr;
+        }
+
+        if (target_type == "zero") {
+            return std::make_unique<DmTargetZero>(start_sector, num_sectors);
+        } else if (target_type == "linear") {
+            if (!HasArgs(2)) {
+                std::cerr << "Expected \"linear\" <block_device> <sector>" << std::endl;
+                return nullptr;
+            }
+
+            std::string block_device = NextArg();
+            uint64_t physical_sector;
+            if (!android::base::ParseUint(NextArg(), &physical_sector)) {
+                std::cerr << "Expected sector, got: \"" << PreviousArg() << "\"" << std::endl;
+                return nullptr;
+            }
+            return std::make_unique<DmTargetLinear>(start_sector, num_sectors, block_device,
+                                                    physical_sector);
+        } else {
+            std::cerr << "Unrecognized target type: " << target_type << std::endl;
+            return nullptr;
+        }
+    }
+
+  private:
+    bool HasArgs(int count) { return arg_index_ + count <= argc_; }
+    const char* NextArg() {
+        CHECK(arg_index_ < argc_);
+        return argv_[arg_index_++];
+    }
+    const char* PreviousArg() {
+        CHECK(arg_index_ >= 0);
+        return argv_[arg_index_ - 1];
+    }
+
+  private:
+    int arg_index_;
+    int argc_;
+    char** argv_;
+};
+
+static int DmCreateCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
+        return -EINVAL;
+    }
+    std::string name = argv[0];
+
+    // Parse extended options first.
+    DmTable table;
+    int arg_index = 1;
+    while (arg_index < argc && argv[arg_index][0] == '-') {
+        if (strcmp(argv[arg_index], "-ro") == 0) {
+            table.set_readonly(true);
+        } else {
+            std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
+            return -EINVAL;
+        }
+        arg_index++;
+    }
+
+    // Parse everything else as target information.
+    TargetParser parser(argc - arg_index, argv + arg_index);
+    while (parser.More()) {
+        std::unique_ptr<DmTarget> target = parser.Next();
+        if (!target || !table.AddTarget(std::move(target))) {
+            return -EINVAL;
+        }
+    }
+
+    if (table.num_targets() == 0) {
+        std::cerr << "Must define at least one target." << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.CreateDevice(name, table)) {
+        std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
+        return -EIO;
+    }
+    return 0;
+}
+
+static int DmDeleteCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "Usage: dmctl delete <name>" << std::endl;
+        return -EINVAL;
+    }
+
+    std::string name = argv[0];
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.DeleteDevice(name)) {
+        std::cerr << "Failed to delete [" << name << "]" << std::endl;
+        return -EIO;
+    }
+
+    return 0;
+}
+
+static int DmListTargets(DeviceMapper& dm) {
+    std::vector<DmTargetTypeInfo> targets;
+    if (!dm.GetAvailableTargets(&targets)) {
+        std::cerr << "Failed to read available device mapper targets" << std::endl;
+        return -errno;
+    }
+
+    std::cout << "Available Device Mapper Targets:" << std::endl;
+    if (targets.empty()) {
+        std::cout << "  <empty>" << std::endl;
+        return 0;
+    }
+
+    for (const auto& target : targets) {
+        std::cout << std::left << std::setw(20) << target.name() << " : " << target.version()
+                  << std::endl;
+    }
+
+    return 0;
+}
+
+static int DmListDevices(DeviceMapper& dm) {
+    std::vector<DmBlockDevice> devices;
+    if (!dm.GetAvailableDevices(&devices)) {
+        std::cerr << "Failed to read available device mapper devices" << std::endl;
+        return -errno;
+    }
+    std::cout << "Available Device Mapper Devices:" << std::endl;
+    if (devices.empty()) {
+        std::cout << "  <empty>" << std::endl;
+        return 0;
+    }
+
+    for (const auto& dev : devices) {
+        std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
+                  << dev.Minor() << std::endl;
+    }
+
+    return 0;
+}
+
+static const std::map<std::string, std::function<int(DeviceMapper&)>> listmap = {
+        {"targets", DmListTargets},
+        {"devices", DmListDevices},
+};
+
+static int DmListCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    for (const auto& l : listmap) {
+        if (l.first == argv[0]) return l.second(dm);
+    }
+
+    std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
+    return -EINVAL;
+}
+
+static int HelpCmdHandler(int /* argc */, char** /* argv */) {
+    Usage();
+    return 0;
+}
+
+static int GetPathCmdHandler(int argc, char** argv) {
+    if (argc != 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    std::string path;
+    if (!dm.GetDmDevicePathByName(argv[0], &path)) {
+        std::cerr << "Could not query path of device \"" << argv[0] << "\"." << std::endl;
+        return -EINVAL;
+    }
+    std::cout << path << std::endl;
+    return 0;
+}
+
+static int TableCmdHandler(int argc, char** argv) {
+    if (argc != 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    std::vector<DeviceMapper::TargetInfo> table;
+    if (!dm.GetTableStatus(argv[0], &table)) {
+        std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
+        return -EINVAL;
+    }
+    std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
+    for (const auto& target : table) {
+        std::cout << target.spec.sector_start << "-"
+                  << (target.spec.sector_start + target.spec.length) << ": "
+                  << target.spec.target_type;
+        if (!target.data.empty()) {
+            std::cout << ", " << target.data;
+        }
+        std::cout << std::endl;
+    }
+    return 0;
+}
+
+static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
+        // clang-format off
+        {"create", DmCreateCmdHandler},
+        {"delete", DmDeleteCmdHandler},
+        {"list", DmListCmdHandler},
+        {"help", HelpCmdHandler},
+        {"getpath", GetPathCmdHandler},
+        {"table", TableCmdHandler},
+        // clang-format on
+};
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::StderrLogger);
+    if (argc < 2) {
+        return Usage();
+    }
+
+    for (const auto& cmd : cmdmap) {
+        if (cmd.first == argv[1]) {
+            return cmd.second(argc - 2, argv + 2);
+        }
+    }
+
+    return Usage();
+}
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 61c8804..abb387c 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -234,11 +234,13 @@
     virtual int verify(uint32_t uid,
             const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
             const uint8_t *provided_password, uint32_t provided_password_length, bool *request_reenroll) {
-        uint8_t *auth_token;
+        uint8_t *auth_token = nullptr;
         uint32_t auth_token_length;
-        return verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
+        int ret = verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
                 provided_password, provided_password_length,
                 &auth_token, &auth_token_length, request_reenroll);
+        delete [] auth_token;
+        return ret;
     }
 
     virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 56f5148..7269b62 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -5,3 +5,16 @@
     header_libs: ["libbatteryservice_headers"],
     export_header_lib_headers: ["libbatteryservice_headers"],
 }
+
+cc_library_static {
+    name: "libbatterymonitor",
+    srcs: ["BatteryMonitor.cpp"],
+    vendor_available: true,
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libutils",
+        "libbase",
+    ],
+    header_libs: ["libhealthd_headers"],
+    export_header_lib_headers: ["libhealthd_headers"],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 6c8fecf..1244903 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -3,14 +3,6 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := BatteryMonitor.cpp
-LOCAL_MODULE := libbatterymonitor
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libutils libbase libbinder
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
 LOCAL_SRC_FILES := \
     healthd_mode_android.cpp \
     BatteryPropertiesRegistrar.cpp
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 194e667..97435c7 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -18,7 +18,6 @@
 #define HEALTHD_BATTERYMONITOR_H
 
 #include <batteryservice/BatteryService.h>
-#include <binder/IInterface.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
diff --git a/init/Android.bp b/init/Android.bp
index 63f3fca..5bbb7a0 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -67,18 +67,9 @@
         "libsquashfs_utils",
         "liblogwrap",
         "libext4_utils",
-        "libcutils",
-        "libbase",
-        "libc",
         "libseccomp_policy",
-        "libselinux",
-        "liblog",
         "libcrypto_utils",
-        "libcrypto",
-        "libc++_static",
-        "libdl",
         "libsparse",
-        "libz",
         "libprocessgroup",
         "libavb",
         "libkeyutils",
@@ -86,6 +77,17 @@
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
     ],
+    shared_libs: [
+        "libcutils",
+        "libbase",
+        "libc",
+        "liblog",
+        "libcrypto",
+        "libc++",
+        "libdl",
+        "libz",
+        "libselinux",
+    ],
 }
 
 cc_library_static {
@@ -166,10 +168,10 @@
 cc_test {
     name: "init_tests",
     defaults: ["init_defaults"],
-    static_executable: true,
     srcs: [
         "devices_test.cpp",
         "init_test.cpp",
+        "keychords_test.cpp",
         "persistent_properties_test.cpp",
         "property_service_test.cpp",
         "property_type_test.cpp",
@@ -177,6 +179,8 @@
         "rlimit_parser_test.cpp",
         "service_test.cpp",
         "subcontext_test.cpp",
+        "tokenizer_test.cpp",
+        "ueventd_parser_test.cpp",
         "ueventd_test.cpp",
         "util_test.cpp",
     ],
@@ -186,7 +190,6 @@
 
 cc_benchmark {
     name: "init_benchmarks",
-    static_executable: true,
     defaults: ["init_defaults"],
     srcs: [
         "subcontext_benchmark.cpp",
@@ -232,8 +235,11 @@
         "action_parser.cpp",
         "capabilities.cpp",
         "descriptors.cpp",
+        "epoll.cpp",
+        "keychords.cpp",
         "import_parser.cpp",
-        "host_init_parser.cpp",
+        "host_import_parser.cpp",
+        "host_init_verifier.cpp",
         "host_init_stubs.cpp",
         "parser.cpp",
         "rlimit_parser.cpp",
@@ -246,7 +252,10 @@
     proto: {
         type: "lite",
     },
-    generated_headers: ["generated_stub_builtin_function_map"],
+    generated_headers: [
+        "generated_stub_builtin_function_map",
+        "generated_android_ids"
+    ],
     target: {
         android: {
             enabled: false,
diff --git a/init/Android.mk b/init/Android.mk
index c4a6a50..a81a0f6 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -45,7 +45,6 @@
 
 LOCAL_MODULE:= init
 
-LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
 LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
 
@@ -59,18 +58,9 @@
     libsquashfs_utils \
     liblogwrap \
     libext4_utils \
-    libcutils \
-    libbase \
-    libc \
     libseccomp_policy \
-    libselinux \
-    liblog \
     libcrypto_utils \
-    libcrypto \
-    libc++_static \
-    libdl \
     libsparse \
-    libz \
     libprocessgroup \
     libavb \
     libkeyutils \
@@ -78,6 +68,26 @@
     libpropertyinfoserializer \
     libpropertyinfoparser \
 
+shared_libs := \
+    libcutils \
+    libbase \
+    liblog \
+    libcrypto \
+    libdl \
+    libz \
+    libselinux \
+
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+# init is static executable for non-system-as-root devices, because the dynamic linker
+# and shared libs are not available before /system is mounted, but init has to run
+# before the partition is mounted.
+LOCAL_STATIC_LIBRARIES += $(shared_libs) libc++_static
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+else
+LOCAL_SHARED_LIBRARIES := $(shared_libs) libc++
+endif
+shared_libs :=
+
 LOCAL_REQUIRED_MODULES := \
     e2fsdroid \
     mke2fs \
diff --git a/init/README.md b/init/README.md
index 550ef05..b0a73b9 100644
--- a/init/README.md
+++ b/init/README.md
@@ -752,3 +752,22 @@
     kill -SIGCONT 4343
 
     > strace runs
+
+Host Init Script Verification
+-----------------------------
+
+Init scripts are checked for correctness during build time. Specifically the below is checked.
+
+1) Well formatted action, service and import sections, e.g. no actions without a preceding 'on'
+line, and no extraneous lines after an 'import' statement.
+2) All commands map to a valid keyword and the argument count is within the correct range.
+3) All service options are valid. This is stricter than how commands are checked as the service
+options' arguments are fully parsed, e.g. UIDs and GIDs must resolve.
+
+There are other parts of init scripts that are only parsed at runtime and therefore not checked
+during build time, among them are the below.
+
+1) The validity of the arguments of commands, e.g. no checking if file paths actually exist, if
+SELinux would permit the operation, or if the UIDs and GIDs resolve.
+2) No checking if a service exists or has a valid SELinux domain defined
+3) No checking if a service has not been previously defined in a different init script.
diff --git a/init/action.cpp b/init/action.cpp
index f782b51..11335ca 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,16 +18,11 @@
 
 #include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 
 #include "util.h"
 
-#if defined(__ANDROID__)
-#include <android-base/properties.h>
-#else
-#include "host_init_stubs.h"
-#endif
-
 using android::base::Join;
 
 namespace android {
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index a2c9671..8a4b518 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -16,16 +16,11 @@
 
 #include "action_parser.h"
 
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 
 #include "stable_properties.h"
 
-#if defined(__ANDROID__)
-#include <android-base/properties.h>
-#else
-#include "host_init_stubs.h"
-#endif
-
 using android::base::GetBoolProperty;
 using android::base::StartsWith;
 
diff --git a/init/devices.h b/init/devices.h
index f9035da..0be660f 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -35,6 +35,8 @@
 
 class Permissions {
   public:
+    friend void TestPermissions(const Permissions& expected, const Permissions& test);
+
     Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
 
     bool Match(const std::string& path) const;
@@ -57,6 +59,8 @@
 
 class SysfsPermissions : public Permissions {
   public:
+    friend void TestSysfsPermissions(const SysfsPermissions& expected, const SysfsPermissions& test);
+
     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) {}
@@ -71,16 +75,24 @@
 class Subsystem {
   public:
     friend class SubsystemParser;
+    friend void TestSubsystems(const Subsystem& expected, const Subsystem& test);
+
+    enum DevnameSource {
+        DEVNAME_UEVENT_DEVNAME,
+        DEVNAME_UEVENT_DEVPATH,
+    };
 
     Subsystem() {}
-    Subsystem(std::string name) : name_(std::move(name)) {}
+    Subsystem(const std::string& name) : name_(name) {}
+    Subsystem(const std::string& name, DevnameSource source, const std::string& dir_name)
+        : name_(name), devname_source_(source), dir_name_(dir_name) {}
 
     // Returns the full path for a uevent of a device that is a member of this subsystem,
     // according to the rules parsed from ueventd.rc
     std::string ParseDevPath(const Uevent& uevent) const {
-        std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
-                                  ? uevent.device_name
-                                  : android::base::Basename(uevent.path);
+        std::string devname = devname_source_ == DEVNAME_UEVENT_DEVNAME
+                                      ? uevent.device_name
+                                      : android::base::Basename(uevent.path);
 
         return dir_name_ + "/" + devname;
     }
@@ -88,14 +100,9 @@
     bool operator==(const std::string& string_name) const { return name_ == string_name; }
 
   private:
-    enum class DevnameSource {
-        DEVNAME_UEVENT_DEVNAME,
-        DEVNAME_UEVENT_DEVPATH,
-    };
-
     std::string name_;
+    DevnameSource devname_source_ = DEVNAME_UEVENT_DEVNAME;
     std::string dir_name_ = "/dev";
-    DevnameSource devname_source_;
 };
 
 class DeviceHandler {
@@ -106,7 +113,6 @@
     DeviceHandler(std::vector<Permissions> dev_permissions,
                   std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
                   std::set<std::string> boot_devices, bool skip_restorecon);
-    ~DeviceHandler(){};
 
     void HandleDeviceEvent(const Uevent& uevent);
 
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 8c8d9f2..28bda34 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -21,7 +21,6 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <string>
 #include <thread>
 
 #include <android-base/chrono_utils.h>
@@ -36,6 +35,8 @@
 namespace android {
 namespace init {
 
+std::vector<std::string> firmware_directories;
+
 static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
                          int loading_fd, int data_fd) {
     // Start transfer.
@@ -78,12 +79,9 @@
         return;
     }
 
-    static const char* firmware_dirs[] = {"/etc/firmware/", "/odm/firmware/",
-                                          "/vendor/firmware/", "/firmware/image/"};
-
 try_loading_again:
-    for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
-        std::string file = firmware_dirs[i] + uevent.firmware;
+    for (const auto& firmware_directory : firmware_directories) {
+        std::string file = firmware_directory + uevent.firmware;
         unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
         struct stat sb;
         if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index e456ac4..6081511 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -17,11 +17,16 @@
 #ifndef _INIT_FIRMWARE_HANDLER_H
 #define _INIT_FIRMWARE_HANDLER_H
 
+#include <string>
+#include <vector>
+
 #include "uevent.h"
 
 namespace android {
 namespace init {
 
+extern std::vector<std::string> firmware_directories;
+
 void HandleFirmwareEvent(const Uevent& uevent);
 
 }  // namespace init
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
new file mode 100644
index 0000000..93e363f
--- /dev/null
+++ b/init/host_import_parser.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host_import_parser.h"
+
+#include <android-base/strings.h>
+
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,
+                                               int) {
+    if (args.size() != 2) {
+        return Error() << "single argument needed for import\n";
+    }
+
+    return Success();
+}
+
+Result<Success> HostImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+    return Error() << "Unexpected line found after import statement";
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/host_import_parser.h b/init/host_import_parser.h
new file mode 100644
index 0000000..52b8891
--- /dev/null
+++ b/init/host_import_parser.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "parser.h"
+
+namespace android {
+namespace init {
+
+class HostImportParser : public SectionParser {
+  public:
+    HostImportParser() {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string&, int) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/host_init_parser.cpp b/init/host_init_parser.cpp
deleted file mode 100644
index df497ea..0000000
--- a/init/host_init_parser.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <pwd.h>
-
-#include <android-base/logging.h>
-
-#include "action.h"
-#include "action_manager.h"
-#include "action_parser.h"
-#include "parser.h"
-#include "result.h"
-#include "service.h"
-
-// The host passwd file won't have the Android entries, so we fake success here.
-passwd* getpwnam(const char* login) {  // NOLINT: implementing bad function.
-    char dummy_buf[] = "dummy";
-    static passwd dummy_passwd = {
-        .pw_name = dummy_buf,
-        .pw_dir = dummy_buf,
-        .pw_shell = dummy_buf,
-        .pw_uid = 123,
-        .pw_gid = 123,
-    };
-    return &dummy_passwd;
-}
-
-namespace android {
-namespace init {
-
-static Result<Success> do_stub(const BuiltinArguments& args) {
-    return Success();
-}
-
-#include "generated_stub_builtin_function_map.h"
-
-int main(int argc, char** argv) {
-    android::base::InitLogging(argv, &android::base::StdioLogger);
-    if (argc != 2) {
-        LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
-        return -1;
-    }
-    const BuiltinFunctionMap function_map;
-    Action::set_function_map(&function_map);
-    ActionManager& am = ActionManager::GetInstance();
-    ServiceList& sl = ServiceList::GetInstance();
-    Parser parser;
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
-
-    size_t num_errors = 0;
-    if (!parser.ParseConfig(argv[1], &num_errors)) {
-        LOG(ERROR) << "Failed to find script";
-        return -1;
-    }
-    if (num_errors > 0) {
-        LOG(ERROR) << "Parse failed with " << num_errors << " errors";
-        return -1;
-    }
-    LOG(INFO) << "Parse success!";
-    return 0;
-}
-
-}  // namespace init
-}  // namespace android
-
-int main(int argc, char** argv) {
-    android::init::main(argc, argv);
-}
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
index 4451ac8..2352fc7 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -16,33 +16,25 @@
 
 #include "host_init_stubs.h"
 
+#include <android-base/properties.h>
+
 // unistd.h
 int setgroups(size_t __size, const gid_t* __list) {
     return 0;
 }
 
 namespace android {
-namespace base {
-
-std::string GetProperty(const std::string&, const std::string& default_value) {
-    return default_value;
-}
-
-bool GetBoolProperty(const std::string&, bool default_value) {
-    return default_value;
-}
-
-}  // namespace base
-}  // namespace android
-
-namespace android {
 namespace init {
 
 // init.h
 std::string default_console = "/dev/console";
 
 // property_service.h
-uint32_t (*property_set)(const std::string& name, const std::string& value) = nullptr;
+uint32_t SetProperty(const std::string& key, const std::string& value) {
+    android::base::SetProperty(key, value);
+    return 0;
+}
+uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
 uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
                            std::string*) {
     return 0;
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index bb241af..f0e1f07 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -29,16 +29,6 @@
 // unistd.h
 int setgroups(size_t __size, const gid_t* __list);
 
-// android-base/properties.h
-namespace android {
-namespace base {
-
-std::string GetProperty(const std::string& key, const std::string& default_value);
-bool GetBoolProperty(const std::string& key, bool default_value);
-
-}  // namespace base
-}  // namespace android
-
 namespace android {
 namespace init {
 
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
new file mode 100644
index 0000000..8407729
--- /dev/null
+++ b/init/host_init_verifier.cpp
@@ -0,0 +1,166 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
+#include "host_import_parser.h"
+#include "host_init_stubs.h"
+#include "parser.h"
+#include "result.h"
+#include "service.h"
+
+#define EXCLUDE_FS_CONFIG_STRUCTURES
+#include "generated_android_ids.h"
+
+using namespace std::literals;
+
+using android::base::ParseInt;
+using android::base::ReadFileToString;
+using android::base::Split;
+
+static std::string passwd_file;
+
+static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
+    std::string passwd;
+    if (!ReadFileToString(passwd_file, &passwd)) {
+        return {};
+    }
+
+    std::vector<std::pair<std::string, int>> result;
+    auto passwd_lines = Split(passwd, "\n");
+    for (const auto& line : passwd_lines) {
+        auto split_line = Split(line, ":");
+        if (split_line.size() < 3) {
+            continue;
+        }
+        int uid = 0;
+        if (!ParseInt(split_line[2], &uid)) {
+            continue;
+        }
+        result.emplace_back(split_line[0], uid);
+    }
+    return result;
+}
+
+passwd* getpwnam(const char* login) {  // NOLINT: implementing bad function.
+    // This isn't thread safe, but that's okay for our purposes.
+    static char static_name[32] = "";
+    static char static_dir[32] = "/";
+    static char static_shell[32] = "/system/bin/sh";
+    static passwd static_passwd = {
+        .pw_name = static_name,
+        .pw_dir = static_dir,
+        .pw_shell = static_shell,
+        .pw_uid = 0,
+        .pw_gid = 0,
+    };
+
+    for (size_t n = 0; n < android_id_count; ++n) {
+        if (!strcmp(android_ids[n].name, login)) {
+            snprintf(static_name, sizeof(static_name), "%s", android_ids[n].name);
+            static_passwd.pw_uid = android_ids[n].aid;
+            static_passwd.pw_gid = android_ids[n].aid;
+            return &static_passwd;
+        }
+    }
+
+    static const auto vendor_passwd = GetVendorPasswd();
+
+    for (const auto& [name, uid] : vendor_passwd) {
+        if (name == login) {
+            snprintf(static_name, sizeof(static_name), "%s", name.c_str());
+            static_passwd.pw_uid = uid;
+            static_passwd.pw_gid = uid;
+            return &static_passwd;
+        }
+    }
+
+    unsigned int oem_uid;
+    if (sscanf(login, "oem_%u", &oem_uid) == 1) {
+        snprintf(static_name, sizeof(static_name), "%s", login);
+        static_passwd.pw_uid = oem_uid;
+        static_passwd.pw_gid = oem_uid;
+        return &static_passwd;
+    }
+
+    errno = ENOENT;
+    return nullptr;
+}
+
+namespace android {
+namespace init {
+
+static Result<Success> do_stub(const BuiltinArguments& args) {
+    return Success();
+}
+
+#include "generated_stub_builtin_function_map.h"
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::StdioLogger);
+    android::base::SetMinimumLogSeverity(android::base::ERROR);
+
+    if (argc != 2 && argc != 3) {
+        LOG(ERROR) << "Usage: " << argv[0] << " <init rc file> [passwd file]";
+        return EXIT_FAILURE;
+    }
+
+    if (argc == 3) {
+        passwd_file = argv[2];
+    }
+
+    const BuiltinFunctionMap function_map;
+    Action::set_function_map(&function_map);
+    ActionManager& am = ActionManager::GetInstance();
+    ServiceList& sl = ServiceList::GetInstance();
+    Parser parser;
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+    parser.AddSectionParser("import", std::make_unique<HostImportParser>());
+
+    if (!parser.ParseConfigFileInsecure(argv[1])) {
+        LOG(ERROR) << "Failed to open init rc script '" << argv[1] << "'";
+        return EXIT_FAILURE;
+    }
+    if (parser.parse_error_count() > 0) {
+        LOG(ERROR) << "Failed to parse init script '" << argv[1] << "' with "
+                   << parser.parse_error_count() << " errors";
+        return EXIT_FAILURE;
+    }
+    return EXIT_SUCCESS;
+}
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    return android::init::main(argc, argv);
+}
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index e335fd1..fb3185e 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -41,14 +41,15 @@
     return Success();
 }
 
+Result<Success> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+    return Error() << "Unexpected line found after import statement";
+}
+
 void ImportParser::EndFile() {
     auto current_imports = std::move(imports_);
     imports_.clear();
     for (const auto& [import, line_num] : current_imports) {
-        if (!parser_->ParseConfig(import)) {
-            PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
-                        << "'";
-        }
+        parser_->ParseConfig(import);
     }
 }
 
diff --git a/init/import_parser.h b/init/import_parser.h
index 5a2f894..7bc72e6 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -30,6 +30,7 @@
     ImportParser(Parser* parser) : parser_(parser) {}
     Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
     void EndFile() override;
 
   private:
diff --git a/init/init.cpp b/init/init.cpp
index fd9a90c..686cd6e 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -352,21 +352,23 @@
 }
 
 static void export_kernel_boot_props() {
+    constexpr const char* UNSET = "";
     struct {
         const char *src_prop;
         const char *dst_prop;
         const char *default_value;
     } prop_map[] = {
-        { "ro.boot.serialno",   "ro.serialno",   "", },
+        { "ro.boot.serialno",   "ro.serialno",   UNSET, },
         { "ro.boot.mode",       "ro.bootmode",   "unknown", },
         { "ro.boot.baseband",   "ro.baseband",   "unknown", },
         { "ro.boot.bootloader", "ro.bootloader", "unknown", },
         { "ro.boot.hardware",   "ro.hardware",   "unknown", },
         { "ro.boot.revision",   "ro.revision",   "0", },
     };
-    for (size_t i = 0; i < arraysize(prop_map); i++) {
-        std::string value = GetProperty(prop_map[i].src_prop, "");
-        property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);
+    for (const auto& prop : prop_map) {
+        std::string value = GetProperty(prop.src_prop, prop.default_value);
+        if (value != UNSET)
+            property_set(prop.dst_prop, value);
     }
 }
 
@@ -553,6 +555,33 @@
     }
 }
 
+void HandleKeychord(const std::vector<int>& keycodes) {
+    // Only handle keychords if adb is enabled.
+    std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
+    if (adb_enabled != "running") {
+        LOG(WARNING) << "Not starting service for keychord " << android::base::Join(keycodes, ' ')
+                     << " because ADB is disabled";
+        return;
+    }
+
+    auto found = false;
+    for (const auto& service : ServiceList::GetInstance()) {
+        auto svc = service.get();
+        if (svc->keycodes() == keycodes) {
+            found = true;
+            LOG(INFO) << "Starting service '" << svc->name() << "' from keychord "
+                      << android::base::Join(keycodes, ' ');
+            if (auto result = svc->Start(); !result) {
+                LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord "
+                           << android::base::Join(keycodes, ' ') << ": " << result.error();
+            }
+        }
+    }
+    if (!found) {
+        LOG(ERROR) << "Service for keychord " << android::base::Join(keycodes, ' ') << " not found";
+    }
+}
+
 int main(int argc, char** argv) {
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
@@ -577,47 +606,64 @@
     if (is_first_stage) {
         boot_clock::time_point start_time = boot_clock::now();
 
+        std::vector<std::pair<std::string, int>> errors;
+#define CHECKCALL(x) \
+    if (x != 0) errors.emplace_back(#x " failed", errno);
+
         // Clear the umask.
         umask(0);
 
-        clearenv();
-        setenv("PATH", _PATH_DEFPATH, 1);
+        CHECKCALL(clearenv());
+        CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
         // Get the basic filesystem setup we need put together in the initramdisk
         // on / and then we'll let the rc file figure out the rest.
-        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
-        mkdir("/dev/pts", 0755);
-        mkdir("/dev/socket", 0755);
-        mount("devpts", "/dev/pts", "devpts", 0, NULL);
-        #define MAKE_STR(x) __STRING(x)
-        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
+        CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
+        CHECKCALL(mkdir("/dev/pts", 0755));
+        CHECKCALL(mkdir("/dev/socket", 0755));
+        CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
+#define MAKE_STR(x) __STRING(x)
+        CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
+#undef MAKE_STR
         // Don't expose the raw commandline to unprivileged processes.
-        chmod("/proc/cmdline", 0440);
+        CHECKCALL(chmod("/proc/cmdline", 0440));
         gid_t groups[] = { AID_READPROC };
-        setgroups(arraysize(groups), groups);
-        mount("sysfs", "/sys", "sysfs", 0, NULL);
-        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
+        CHECKCALL(setgroups(arraysize(groups), groups));
+        CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
+        CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
 
-        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
+        CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
 
         if constexpr (WORLD_WRITABLE_KMSG) {
-            mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
+            CHECKCALL(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));
+        CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
+        CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
 
         // Mount staging areas for devices managed by vold
         // See storage config details at http://source.android.com/devices/storage/
-        mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
-              "mode=0755,uid=0,gid=1000");
+        CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                        "mode=0755,uid=0,gid=1000"));
         // /mnt/vendor is used to mount vendor-specific partitions that can not be
         // part of the vendor partition, e.g. because they are mounted read-write.
-        mkdir("/mnt/vendor", 0755);
+        CHECKCALL(mkdir("/mnt/vendor", 0755));
+        // /mnt/product is used to mount product-specific partitions that can not be
+        // part of the product partition, e.g. because they are mounted read-write.
+        CHECKCALL(mkdir("/mnt/product", 0755));
+
+#undef CHECKCALL
 
         // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
         // talk to the outside world...
         InitKernelLogging(argv);
 
+        if (!errors.empty()) {
+            for (const auto& [error_string, error_errno] : errors) {
+                LOG(ERROR) << error_string << " " << strerror(error_errno);
+            }
+            LOG(FATAL) << "Init encountered errors starting first stage, aborting";
+        }
+
         LOG(INFO) << "init first stage started!";
 
         if (!DoFirstStageMount()) {
@@ -730,9 +776,13 @@
     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
     am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
     am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
+    Keychords keychords;
     am.QueueBuiltinAction(
-        [&epoll](const BuiltinArguments& args) -> Result<Success> {
-            KeychordInit(&epoll);
+        [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
+            for (const auto& svc : ServiceList::GetInstance()) {
+                keychords.Register(svc->keycodes());
+            }
+            keychords.Start(&epoll, HandleKeychord);
             return Success();
         },
         "KeychordInit");
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 34de640..0ee0203 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -29,6 +29,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
+#include <liblp/metadata_format.h>
 
 #include "devices.h"
 #include "fs_mgr.h"
@@ -39,7 +40,6 @@
 #include "util.h"
 
 using android::base::Timer;
-using android::fs_mgr::LogicalPartitionTable;
 
 namespace android {
 namespace init {
@@ -74,7 +74,7 @@
     bool need_dm_verity_;
 
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
-    std::unique_ptr<LogicalPartitionTable> dm_linear_table_;
+    std::string lp_metadata_partition_;
     std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
     std::unique_ptr<DeviceHandler> device_handler_;
@@ -120,13 +120,17 @@
 }
 
 static inline bool IsDmLinearEnabled() {
-    bool enabled = false;
-    import_kernel_cmdline(
-        false, [&enabled](const std::string& key, const std::string& value, bool in_qemu) {
-            if (key == "androidboot.lrap" && value == "1") {
-                enabled = true;
-            }
-        });
+    static bool checked = false;
+    static bool enabled = false;
+    if (checked) {
+        return enabled;
+    }
+    import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
+        if (key == "androidboot.logical_partitions" && value == "1") {
+            enabled = true;
+        }
+    });
+    checked = true;
     return enabled;
 }
 
@@ -144,10 +148,6 @@
         LOG(INFO) << "Failed to read fstab from device tree";
     }
 
-    if (IsDmLinearEnabled()) {
-        dm_linear_table_ = android::fs_mgr::LoadPartitionsFromDeviceTree();
-    }
-
     auto boot_devices = fs_mgr_get_boot_devices();
     device_handler_ =
         std::make_unique<DeviceHandler>(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
@@ -163,7 +163,7 @@
 }
 
 bool FirstStageMount::DoFirstStageMount() {
-    if (!dm_linear_table_ && mount_fstab_recs_.empty()) {
+    if (!IsDmLinearEnabled() && mount_fstab_recs_.empty()) {
         // Nothing to mount.
         LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
         return true;
@@ -184,16 +184,11 @@
 
 bool FirstStageMount::GetBackingDmLinearDevices() {
     // Add any additional devices required for dm-linear mappings.
-    if (!dm_linear_table_) {
+    if (!IsDmLinearEnabled()) {
         return true;
     }
 
-    for (const auto& partition : dm_linear_table_->partitions) {
-        for (const auto& extent : partition.extents) {
-            const std::string& partition_name = android::base::Basename(extent.block_device());
-            required_devices_partition_names_.emplace(partition_name);
-        }
-    }
+    required_devices_partition_names_.emplace(LP_METADATA_PARTITION_NAME);
     return true;
 }
 
@@ -205,7 +200,7 @@
         return true;
     }
 
-    if (dm_linear_table_ || need_dm_verity_) {
+    if (IsDmLinearEnabled() || need_dm_verity_) {
         const std::string dm_path = "/devices/virtual/misc/device-mapper";
         bool found = false;
         auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
@@ -253,10 +248,16 @@
 }
 
 bool FirstStageMount::CreateLogicalPartitions() {
-    if (!dm_linear_table_) {
+    if (!IsDmLinearEnabled()) {
         return true;
     }
-    return android::fs_mgr::CreateLogicalPartitions(*dm_linear_table_.get());
+
+    if (lp_metadata_partition_.empty()) {
+        LOG(ERROR) << "Could not locate logical partition tables in partition "
+                   << LP_METADATA_PARTITION_NAME;
+        return false;
+    }
+    return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
 }
 
 ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
@@ -266,6 +267,10 @@
     auto iter = required_devices_partition_names_.find(name);
     if (iter != required_devices_partition_names_.end()) {
         LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
+        if (IsDmLinearEnabled() && name == LP_METADATA_PARTITION_NAME) {
+            std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
+            lp_metadata_partition_ = links[0];
+        }
         required_devices_partition_names_.erase(iter);
         device_handler_->HandleDeviceEvent(uevent);
         if (required_devices_partition_names_.empty()) {
@@ -431,12 +436,19 @@
 bool FirstStageMountVBootV2::GetDmVerityDevices() {
     need_dm_verity_ = false;
 
+    std::set<std::string> logical_partitions;
+
     // fstab_rec->blk_device has A/B suffix.
     for (auto fstab_rec : mount_fstab_recs_) {
         if (fs_mgr_is_avb(fstab_rec)) {
             need_dm_verity_ = true;
         }
-        required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+        if (fs_mgr_is_logical(fstab_rec)) {
+            // Don't try to find logical partitions via uevent regeneration.
+            logical_partitions.emplace(basename(fstab_rec->blk_device));
+        } else {
+            required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+        }
     }
 
     // libavb verifies AVB metadata on all verified partitions at once.
@@ -451,11 +463,15 @@
         std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
         std::string ab_suffix = fs_mgr_get_slot_suffix();
         for (const auto& partition : partitions) {
+            std::string partition_name = partition + ab_suffix;
+            if (logical_partitions.count(partition_name)) {
+                continue;
+            }
             // required_devices_partition_names_ is of type std::set so it's not an issue
             // to emplace a partition twice. e.g., /vendor might be in both places:
             //   - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor"
             //   - mount_fstab_recs_: /vendor_a
-            required_devices_partition_names_.emplace(partition + ab_suffix);
+            required_devices_partition_names_.emplace(partition_name);
         }
     }
     return true;
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 418cdeb..1af06dd 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -33,150 +33,117 @@
 #include <vector>
 
 #include <android-base/logging.h>
-#include <android-base/properties.h>
-
-#include "init.h"
-#include "service.h"
 
 namespace android {
 namespace init {
 
-namespace {
+Keychords::Keychords() : epoll_(nullptr), inotify_fd_(-1) {}
 
-int keychords_count;
-Epoll* epoll;
-
-struct KeychordEntry {
-    const std::vector<int> keycodes;
-    bool notified;
-    int id;
-
-    KeychordEntry(const std::vector<int>& keycodes, int id)
-        : keycodes(keycodes), notified(false), id(id) {}
-};
-
-std::vector<KeychordEntry> keychord_entries;
-
-// Bit management
-class KeychordMask {
-  private:
-    typedef unsigned int mask_t;
-    std::vector<mask_t> bits;
-    static constexpr size_t bits_per_byte = 8;
-
-  public:
-    explicit KeychordMask(size_t bit = 0) : bits((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}
-
-    void SetBit(size_t bit, bool value = true) {
-        auto idx = bit / (bits_per_byte * sizeof(mask_t));
-        if (idx >= bits.size()) return;
-        if (value) {
-            bits[idx] |= mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t)));
-        } else {
-            bits[idx] &= ~(mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t))));
-        }
+Keychords::~Keychords() noexcept {
+    if (inotify_fd_ >= 0) {
+        epoll_->UnregisterHandler(inotify_fd_);
+        ::close(inotify_fd_);
     }
+    while (!registration_.empty()) GeteventCloseDevice(registration_.begin()->first);
+}
 
-    bool GetBit(size_t bit) const {
-        auto idx = bit / (bits_per_byte * sizeof(mask_t));
-        return bits[idx] & (mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t))));
-    }
+Keychords::Mask::Mask(size_t bit) : bits_((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}
 
-    size_t bytesize() const { return bits.size() * sizeof(mask_t); }
-    void* data() { return bits.data(); }
-    size_t size() const { return bits.size() * sizeof(mask_t) * bits_per_byte; }
-    void resize(size_t bit) {
-        auto idx = bit / (bits_per_byte * sizeof(mask_t));
-        if (idx >= bits.size()) {
-            bits.resize(idx + 1, 0);
-        }
-    }
-
-    operator bool() const {
-        for (size_t i = 0; i < bits.size(); ++i) {
-            if (bits[i]) return true;
-        }
-        return false;
-    }
-
-    KeychordMask operator&(const KeychordMask& rval) const {
-        auto len = std::min(bits.size(), rval.bits.size());
-        KeychordMask ret;
-        ret.bits.resize(len);
-        for (size_t i = 0; i < len; ++i) {
-            ret.bits[i] = bits[i] & rval.bits[i];
-        }
-        return ret;
-    }
-
-    void operator|=(const KeychordMask& rval) {
-        size_t len = rval.bits.size();
-        bits.resize(len);
-        for (size_t i = 0; i < len; ++i) {
-            bits[i] |= rval.bits[i];
-        }
-    }
-};
-
-KeychordMask keychord_current;
-
-constexpr char kDevicePath[] = "/dev/input";
-
-std::map<std::string, int> keychord_registration;
-
-void HandleKeychord(int id) {
-    // Only handle keychords if adb is enabled.
-    std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
-    if (adb_enabled == "running") {
-        Service* svc = ServiceList::GetInstance().FindService(id, &Service::keychord_id);
-        if (svc) {
-            LOG(INFO) << "Starting service '" << svc->name() << "' from keychord " << id;
-            if (auto result = svc->Start(); !result) {
-                LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord " << id
-                           << ": " << result.error();
-            }
-        } else {
-            LOG(ERROR) << "Service for keychord " << id << " not found";
-        }
+void Keychords::Mask::SetBit(size_t bit, bool value) {
+    auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+    if (idx >= bits_.size()) return;
+    if (value) {
+        bits_[idx] |= mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t)));
     } else {
-        LOG(WARNING) << "Not starting service for keychord " << id << " because ADB is disabled";
+        bits_[idx] &= ~(mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));
     }
 }
 
-void KeychordLambdaCheck() {
-    for (auto& e : keychord_entries) {
-        bool found = true;
-        for (auto& code : e.keycodes) {
-            if (!keychord_current.GetBit(code)) {
-                e.notified = false;
+bool Keychords::Mask::GetBit(size_t bit) const {
+    auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+    return bits_[idx] & (mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));
+}
+
+size_t Keychords::Mask::bytesize() const {
+    return bits_.size() * sizeof(mask_t);
+}
+
+void* Keychords::Mask::data() {
+    return bits_.data();
+}
+
+size_t Keychords::Mask::size() const {
+    return bits_.size() * sizeof(mask_t) * kBitsPerByte;
+}
+
+void Keychords::Mask::resize(size_t bit) {
+    auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+    if (idx >= bits_.size()) {
+        bits_.resize(idx + 1, 0);
+    }
+}
+
+Keychords::Mask::operator bool() const {
+    for (size_t i = 0; i < bits_.size(); ++i) {
+        if (bits_[i]) return true;
+    }
+    return false;
+}
+
+Keychords::Mask Keychords::Mask::operator&(const Keychords::Mask& rval) const {
+    auto len = std::min(bits_.size(), rval.bits_.size());
+    Keychords::Mask ret;
+    ret.bits_.resize(len);
+    for (size_t i = 0; i < len; ++i) {
+        ret.bits_[i] = bits_[i] & rval.bits_[i];
+    }
+    return ret;
+}
+
+void Keychords::Mask::operator|=(const Keychords::Mask& rval) {
+    auto len = rval.bits_.size();
+    bits_.resize(len);
+    for (size_t i = 0; i < len; ++i) {
+        bits_[i] |= rval.bits_[i];
+    }
+}
+
+Keychords::Entry::Entry() : notified(false) {}
+
+void Keychords::LambdaCheck() {
+    for (auto& [keycodes, entry] : entries_) {
+        auto found = true;
+        for (auto& code : keycodes) {
+            if (!current_.GetBit(code)) {
+                entry.notified = false;
                 found = false;
                 break;
             }
         }
         if (!found) continue;
-        if (e.notified) continue;
-        e.notified = true;
-        HandleKeychord(e.id);
+        if (entry.notified) continue;
+        entry.notified = true;
+        handler_(keycodes);
     }
 }
 
-void KeychordLambdaHandler(int fd) {
+void Keychords::LambdaHandler(int fd) {
     input_event event;
     auto res = TEMP_FAILURE_RETRY(::read(fd, &event, sizeof(event)));
     if ((res != sizeof(event)) || (event.type != EV_KEY)) return;
-    keychord_current.SetBit(event.code, event.value);
-    KeychordLambdaCheck();
+    current_.SetBit(event.code, event.value);
+    LambdaCheck();
 }
 
-bool KeychordGeteventEnable(int fd) {
-    static bool EviocsmaskSupported = true;
-
+bool Keychords::GeteventEnable(int fd) {
     // Make sure it is an event channel, should pass this ioctl call
     int version;
     if (::ioctl(fd, EVIOCGVERSION, &version)) return false;
 
+#ifdef EVIOCSMASK
+    static auto EviocsmaskSupported = true;
     if (EviocsmaskSupported) {
-        KeychordMask mask(EV_KEY);
+        Keychords::Mask mask(EV_KEY);
         mask.SetBit(EV_KEY);
         input_mask msg = {};
         msg.type = EV_SYN;
@@ -187,21 +154,23 @@
             EviocsmaskSupported = false;
         }
     }
+#endif
 
-    KeychordMask mask;
-    for (auto& e : keychord_entries) {
-        for (auto& code : e.keycodes) {
+    Keychords::Mask mask;
+    for (auto& [keycodes, entry] : entries_) {
+        for (auto& code : keycodes) {
             mask.resize(code);
             mask.SetBit(code);
         }
     }
 
-    keychord_current.resize(mask.size());
-    KeychordMask available(mask.size());
+    current_.resize(mask.size());
+    Keychords::Mask available(mask.size());
     auto res = ::ioctl(fd, EVIOCGBIT(EV_KEY, available.bytesize()), available.data());
     if (res == -1) return false;
     if (!(available & mask)) return false;
 
+#ifdef EVIOCSMASK
     if (EviocsmaskSupported) {
         input_mask msg = {};
         msg.type = EV_KEY;
@@ -209,46 +178,45 @@
         msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());
         ::ioctl(fd, EVIOCSMASK, &msg);
     }
+#endif
 
-    KeychordMask set(mask.size());
+    Keychords::Mask set(mask.size());
     res = ::ioctl(fd, EVIOCGKEY(res), set.data());
     if (res > 0) {
-        keychord_current |= mask & available & set;
-        KeychordLambdaCheck();
+        current_ |= mask & available & set;
+        LambdaCheck();
     }
-    epoll->RegisterHandler(fd, [fd]() { KeychordLambdaHandler(fd); });
+    epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });
     return true;
 }
 
-void GeteventOpenDevice(const std::string& device) {
-    if (keychord_registration.count(device)) return;
+void Keychords::GeteventOpenDevice(const std::string& device) {
+    if (registration_.count(device)) return;
     auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDWR | O_CLOEXEC));
     if (fd == -1) {
         PLOG(ERROR) << "Can not open " << device;
         return;
     }
-    if (!KeychordGeteventEnable(fd)) {
+    if (!GeteventEnable(fd)) {
         ::close(fd);
     } else {
-        keychord_registration.emplace(device, fd);
+        registration_.emplace(device, fd);
     }
 }
 
-void GeteventCloseDevice(const std::string& device) {
-    auto it = keychord_registration.find(device);
-    if (it == keychord_registration.end()) return;
+void Keychords::GeteventCloseDevice(const std::string& device) {
+    auto it = registration_.find(device);
+    if (it == registration_.end()) return;
     auto fd = (*it).second;
-    epoll->UnregisterHandler(fd);
-    keychord_registration.erase(it);
+    epoll_->UnregisterHandler(fd);
+    registration_.erase(it);
     ::close(fd);
 }
 
-int inotify_fd = -1;
+void Keychords::InotifyHandler() {
+    unsigned char buf[512];  // History shows 32-64 bytes typical
 
-void InotifyHandler() {
-    unsigned char buf[512];
-
-    auto res = TEMP_FAILURE_RETRY(::read(inotify_fd, buf, sizeof(buf)));
+    auto res = TEMP_FAILURE_RETRY(::read(inotify_fd_, buf, sizeof(buf)));
     if (res < 0) {
         PLOG(WARNING) << "could not get event";
         return;
@@ -274,14 +242,15 @@
     }
 }
 
-void GeteventOpenDevice() {
-    inotify_fd = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
-    if (inotify_fd < 0) {
+void Keychords::GeteventOpenDevice() {
+    inotify_fd_ = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+    if (inotify_fd_ < 0) {
         PLOG(WARNING) << "Could not instantiate inotify for " << kDevicePath;
-    } else if (::inotify_add_watch(inotify_fd, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) < 0) {
+    } else if (::inotify_add_watch(inotify_fd_, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) <
+               0) {
         PLOG(WARNING) << "Could not add watch for " << kDevicePath;
-        ::close(inotify_fd);
-        inotify_fd = -1;
+        ::close(inotify_fd_);
+        inotify_fd_ = -1;
     }
 
     std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);
@@ -296,27 +265,20 @@
         }
     }
 
-    if (inotify_fd >= 0) epoll->RegisterHandler(inotify_fd, InotifyHandler);
+    if (inotify_fd_ >= 0) {
+        epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });
+    }
 }
 
-void AddServiceKeycodes(Service* svc) {
-    if (svc->keycodes().empty()) return;
-    for (auto& code : svc->keycodes()) {
-        if ((code < 0) || (code >= KEY_MAX)) return;
-    }
-    ++keychords_count;
-    keychord_entries.emplace_back(KeychordEntry(svc->keycodes(), keychords_count));
-    svc->set_keychord_id(keychords_count);
+void Keychords::Register(const std::vector<int>& keycodes) {
+    if (keycodes.empty()) return;
+    entries_.try_emplace(keycodes, Entry());
 }
 
-}  // namespace
-
-void KeychordInit(Epoll* init_epoll) {
-    epoll = init_epoll;
-    for (const auto& service : ServiceList::GetInstance()) {
-        AddServiceKeycodes(service.get());
-    }
-    if (keychords_count) GeteventOpenDevice();
+void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
+    epoll_ = epoll;
+    handler_ = handler;
+    if (entries_.size()) GeteventOpenDevice();
 }
 
 }  // namespace init
diff --git a/init/keychords.h b/init/keychords.h
index f3aecbb..00ed205 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -17,12 +17,81 @@
 #ifndef _INIT_KEYCHORDS_H_
 #define _INIT_KEYCHORDS_H_
 
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
 #include "epoll.h"
 
 namespace android {
 namespace init {
 
-void KeychordInit(Epoll* init_epoll);
+class Keychords {
+  public:
+    Keychords();
+    Keychords(const Keychords&) = delete;
+    Keychords(Keychords&&) = delete;
+    Keychords& operator=(const Keychords&) = delete;
+    Keychords& operator=(Keychords&&) = delete;
+    ~Keychords() noexcept;
+
+    void Register(const std::vector<int>& keycodes);
+    void Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler);
+
+  private:
+    // Bit management
+    class Mask {
+      public:
+        explicit Mask(size_t bit = 0);
+
+        void SetBit(size_t bit, bool value = true);
+        bool GetBit(size_t bit) const;
+
+        size_t bytesize() const;
+        void* data();
+        size_t size() const;
+        void resize(size_t bit);
+
+        operator bool() const;
+        Mask operator&(const Mask& rval) const;
+        void operator|=(const Mask& rval);
+
+      private:
+        typedef unsigned int mask_t;
+        static constexpr size_t kBitsPerByte = 8;
+
+        std::vector<mask_t> bits_;
+    };
+
+    struct Entry {
+        Entry();
+
+        bool notified;
+    };
+
+    static constexpr char kDevicePath[] = "/dev/input";
+
+    void LambdaCheck();
+    void LambdaHandler(int fd);
+    void InotifyHandler();
+
+    bool GeteventEnable(int fd);
+    void GeteventOpenDevice(const std::string& device);
+    void GeteventOpenDevice();
+    void GeteventCloseDevice(const std::string& device);
+
+    Epoll* epoll_;
+    std::function<void(const std::vector<int>&)> handler_;
+
+    std::map<std::string, int> registration_;
+
+    std::map<const std::vector<int>, Entry> entries_;
+
+    Mask current_;
+
+    int inotify_fd_;
+};
 
 }  // namespace init
 }  // namespace android
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
new file mode 100644
index 0000000..c8c47a8
--- /dev/null
+++ b/init/keychords_test.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "keychords.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/uinput.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "epoll.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace init {
+
+namespace {
+
+// This class is used to inject keys.
+class EventHandler {
+  public:
+    EventHandler();
+    EventHandler(const EventHandler&) = delete;
+    EventHandler(EventHandler&&);
+    EventHandler& operator=(const EventHandler&) = delete;
+    EventHandler& operator=(EventHandler&&);
+    ~EventHandler() noexcept;
+
+    bool init();
+
+    bool send(struct input_event& e);
+    bool send(uint16_t type, uint16_t code, uint16_t value);
+    bool send(uint16_t code, bool value);
+
+  private:
+    int fd_;
+};
+
+EventHandler::EventHandler() : fd_(-1) {}
+
+EventHandler::EventHandler(EventHandler&& rval) : fd_(rval.fd_) {
+    rval.fd_ = -1;
+}
+
+EventHandler& EventHandler::operator=(EventHandler&& rval) {
+    fd_ = rval.fd_;
+    rval.fd_ = -1;
+    return *this;
+}
+
+EventHandler::~EventHandler() {
+    if (fd_ == -1) return;
+    ::ioctl(fd_, UI_DEV_DESTROY);
+    ::close(fd_);
+}
+
+bool EventHandler::init() {
+    if (fd_ != -1) return true;
+    auto fd = TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK | O_CLOEXEC));
+    if (fd == -1) return false;
+    if (::ioctl(fd, UI_SET_EVBIT, EV_KEY) == -1) {
+        ::close(fd);
+        return false;
+    }
+
+    static const struct uinput_user_dev u = {
+        .name = "com.google.android.init.test",
+        .id.bustype = BUS_VIRTUAL,
+        .id.vendor = 0x1AE0,   // Google
+        .id.product = 0x494E,  // IN
+        .id.version = 1,
+    };
+    if (TEMP_FAILURE_RETRY(::write(fd, &u, sizeof(u))) != sizeof(u)) {
+        ::close(fd);
+        return false;
+    }
+
+    // all keys
+    for (uint16_t i = 0; i < KEY_MAX; ++i) {
+        if (::ioctl(fd, UI_SET_KEYBIT, i) == -1) {
+            ::close(fd);
+            return false;
+        }
+    }
+    if (::ioctl(fd, UI_DEV_CREATE) == -1) {
+        ::close(fd);
+        return false;
+    }
+    fd_ = fd;
+    return true;
+}
+
+bool EventHandler::send(struct input_event& e) {
+    gettimeofday(&e.time, nullptr);
+    return TEMP_FAILURE_RETRY(::write(fd_, &e, sizeof(e))) == sizeof(e);
+}
+
+bool EventHandler::send(uint16_t type, uint16_t code, uint16_t value) {
+    struct input_event e = {.type = type, .code = code, .value = value};
+    return send(e);
+}
+
+bool EventHandler::send(uint16_t code, bool value) {
+    return (code < KEY_MAX) && init() && send(EV_KEY, code, value) && send(EV_SYN, SYN_REPORT, 0);
+}
+
+std::string InitFds(const char* prefix, pid_t pid = getpid()) {
+    std::string ret;
+
+    std::string init_fds("/proc/");
+    init_fds += std::to_string(pid) + "/fd";
+    std::unique_ptr<DIR, decltype(&closedir)> fds(opendir(init_fds.c_str()), closedir);
+    if (!fds) return ret;
+
+    dirent* entry;
+    while ((entry = readdir(fds.get()))) {
+        if (entry->d_name[0] == '.') continue;
+        std::string devname = init_fds + '/' + entry->d_name;
+        char buf[256];
+        auto retval = readlink(devname.c_str(), buf, sizeof(buf) - 1);
+        if ((retval < 0) || (size_t(retval) >= (sizeof(buf) - 1))) continue;
+        buf[retval] = '\0';
+        if (!android::base::StartsWith(buf, prefix)) continue;
+        if (ret.size() != 0) ret += ",";
+        ret += buf;
+    }
+    return ret;
+}
+
+std::string InitInputFds() {
+    return InitFds("/dev/input/");
+}
+
+std::string InitInotifyFds() {
+    return InitFds("anon_inode:inotify");
+}
+
+// NB: caller (this series of tests, or conversely the service parser in init)
+// is responsible for validation, sorting and uniqueness of the chords, so no
+// fuzzing is advised.
+
+const std::vector<int> escape_chord = {KEY_ESC};
+const std::vector<int> triple1_chord = {KEY_BACKSPACE, KEY_VOLUMEDOWN, KEY_VOLUMEUP};
+const std::vector<int> triple2_chord = {KEY_VOLUMEDOWN, KEY_VOLUMEUP, KEY_BACK};
+
+const std::vector<const std::vector<int>> empty_chords;
+const std::vector<const std::vector<int>> chords = {
+    escape_chord,
+    triple1_chord,
+    triple2_chord,
+};
+
+class TestFrame {
+  public:
+    TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev = nullptr);
+
+    void RelaxForMs(std::chrono::milliseconds wait = 1ms);
+
+    void SetChord(int key, bool value = true);
+    void SetChords(const std::vector<int>& chord, bool value = true);
+    void ClrChord(int key);
+    void ClrChords(const std::vector<int>& chord);
+
+    bool IsOnlyChord(const std::vector<int>& chord) const;
+    bool IsNoChord() const;
+    bool IsChord(const std::vector<int>& chord) const;
+    void WaitForChord(const std::vector<int>& chord);
+
+    std::string Format() const;
+
+  private:
+    static std::string Format(const std::vector<const std::vector<int>>& chords);
+
+    Epoll epoll_;
+    Keychords keychords_;
+    std::vector<const std::vector<int>> keycodes_;
+    EventHandler* ev_;
+};
+
+TestFrame::TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev)
+    : ev_(ev) {
+    if (!epoll_.Open()) return;
+    for (const auto& keycodes : chords) keychords_.Register(keycodes);
+    keychords_.Start(&epoll_, [this](const std::vector<int>& keycodes) {
+        this->keycodes_.emplace_back(keycodes);
+    });
+}
+
+void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
+    epoll_.Wait(wait);
+}
+
+void TestFrame::SetChord(int key, bool value) {
+    ASSERT_TRUE(!!ev_);
+    RelaxForMs();
+    EXPECT_TRUE(ev_->send(key, value));
+}
+
+void TestFrame::SetChords(const std::vector<int>& chord, bool value) {
+    ASSERT_TRUE(!!ev_);
+    for (auto& key : chord) SetChord(key, value);
+    RelaxForMs();
+}
+
+void TestFrame::ClrChord(int key) {
+    ASSERT_TRUE(!!ev_);
+    SetChord(key, false);
+}
+
+void TestFrame::ClrChords(const std::vector<int>& chord) {
+    ASSERT_TRUE(!!ev_);
+    SetChords(chord, false);
+}
+
+bool TestFrame::IsOnlyChord(const std::vector<int>& chord) const {
+    auto ret = false;
+    for (const auto& keycode : keycodes_) {
+        if (keycode != chord) return false;
+        ret = true;
+    }
+    return ret;
+}
+
+bool TestFrame::IsNoChord() const {
+    return keycodes_.empty();
+}
+
+bool TestFrame::IsChord(const std::vector<int>& chord) const {
+    for (const auto& keycode : keycodes_) {
+        if (keycode == chord) return true;
+    }
+    return false;
+}
+
+void TestFrame::WaitForChord(const std::vector<int>& chord) {
+    for (int retry = 1000; retry && !IsChord(chord); --retry) RelaxForMs();
+}
+
+std::string TestFrame::Format(const std::vector<const std::vector<int>>& chords) {
+    std::string ret("{");
+    if (!chords.empty()) {
+        ret += android::base::Join(chords.front(), ' ');
+        for (auto it = std::next(chords.begin()); it != chords.end(); ++it) {
+            ret += ',';
+            ret += android::base::Join(*it, ' ');
+        }
+    }
+    return ret + '}';
+}
+
+std::string TestFrame::Format() const {
+    return Format(keycodes_);
+}
+
+}  // namespace
+
+TEST(keychords, not_instantiated) {
+    TestFrame test_frame(empty_chords);
+    EXPECT_TRUE(InitInotifyFds().size() == 0);
+}
+
+TEST(keychords, instantiated) {
+    // Test if a valid set of chords results in proper instantiation of the
+    // underlying mechanisms for /dev/input/ attachment.
+    TestFrame test_frame(chords);
+    EXPECT_TRUE(InitInotifyFds().size() != 0);
+}
+
+TEST(keychords, init_inotify) {
+    std::string before(InitInputFds());
+
+    TestFrame test_frame(chords);
+
+    EventHandler ev;
+    EXPECT_TRUE(ev.init());
+
+    for (int retry = 1000; retry && before == InitInputFds(); --retry) test_frame.RelaxForMs();
+    std::string after(InitInputFds());
+    EXPECT_NE(before, after);
+}
+
+TEST(keychords, key) {
+    EventHandler ev;
+    EXPECT_TRUE(ev.init());
+    TestFrame test_frame(chords, &ev);
+
+    test_frame.SetChords(escape_chord);
+    test_frame.WaitForChord(escape_chord);
+    test_frame.ClrChords(escape_chord);
+    EXPECT_TRUE(test_frame.IsOnlyChord(escape_chord))
+        << "expected only " << android::base::Join(escape_chord, ' ') << " got "
+        << test_frame.Format();
+}
+
+TEST(keychords, keys_in_series) {
+    EventHandler ev;
+    EXPECT_TRUE(ev.init());
+    TestFrame test_frame(chords, &ev);
+
+    for (auto& key : triple1_chord) {
+        test_frame.SetChord(key);
+        test_frame.ClrChord(key);
+    }
+    test_frame.WaitForChord(triple1_chord);
+    EXPECT_TRUE(test_frame.IsNoChord()) << "expected nothing got " << test_frame.Format();
+}
+
+TEST(keychords, keys_in_parallel) {
+    EventHandler ev;
+    EXPECT_TRUE(ev.init());
+    TestFrame test_frame(chords, &ev);
+
+    test_frame.SetChords(triple2_chord);
+    test_frame.WaitForChord(triple2_chord);
+    test_frame.ClrChords(triple2_chord);
+    EXPECT_TRUE(test_frame.IsOnlyChord(triple2_chord))
+        << "expected only " << android::base::Join(triple2_chord, ' ') << " got "
+        << test_frame.Format();
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/parser.cpp b/init/parser.cpp
index 4453aaa..fa0fd11 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 
 #include <android-base/chrono_utils.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -39,14 +40,13 @@
     line_callbacks_.emplace_back(prefix, callback);
 }
 
-void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
-    // TODO: Use a parser with const input and remove this copy
-    std::vector<char> data_copy(data.begin(), data.end());
-    data_copy.push_back('\0');
+void Parser::ParseData(const std::string& filename, std::string* data) {
+    data->push_back('\n');  // TODO: fix tokenizer
+    data->push_back('\0');
 
     parse_state state;
     state.line = 0;
-    state.ptr = &data_copy[0];
+    state.ptr = data->data();
     state.nexttoken = 0;
 
     SectionParser* section_parser = nullptr;
@@ -57,7 +57,7 @@
         if (section_parser == nullptr) return;
 
         if (auto result = section_parser->EndSection(); !result) {
-            (*parse_errors)++;
+            parse_error_count_++;
             LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
         }
 
@@ -69,44 +69,53 @@
         switch (next_token(&state)) {
             case T_EOF:
                 end_section();
+
+                for (const auto& [section_name, section_parser] : section_parsers_) {
+                    section_parser->EndFile();
+                }
+
                 return;
-            case T_NEWLINE:
+            case T_NEWLINE: {
                 state.line++;
                 if (args.empty()) break;
                 // If we have a line matching a prefix we recognize, call its callback and unset any
                 // current section parsers.  This is meant for /sys/ and /dev/ line entries for
                 // uevent.
-                for (const auto& [prefix, callback] : line_callbacks_) {
-                    if (android::base::StartsWith(args[0], prefix)) {
-                        end_section();
+                auto line_callback = std::find_if(
+                    line_callbacks_.begin(), line_callbacks_.end(),
+                    [&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });
+                if (line_callback != line_callbacks_.end()) {
+                    end_section();
 
-                        if (auto result = callback(std::move(args)); !result) {
-                            (*parse_errors)++;
-                            LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
-                        }
-                        break;
+                    if (auto result = line_callback->second(std::move(args)); !result) {
+                        parse_error_count_++;
+                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
-                }
-                if (section_parsers_.count(args[0])) {
+                } else if (section_parsers_.count(args[0])) {
                     end_section();
                     section_parser = section_parsers_[args[0]].get();
                     section_start_line = state.line;
                     if (auto result =
                             section_parser->ParseSection(std::move(args), filename, state.line);
                         !result) {
-                        (*parse_errors)++;
+                        parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         section_parser = nullptr;
                     }
                 } else if (section_parser) {
                     if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
                         !result) {
-                        (*parse_errors)++;
+                        parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
+                } else {
+                    parse_error_count_++;
+                    LOG(ERROR) << filename << ": " << state.line
+                               << ": Invalid section keyword found";
                 }
                 args.clear();
                 break;
+            }
             case T_TEXT:
                 args.emplace_back(state.text);
                 break;
@@ -114,30 +123,36 @@
     }
 }
 
-bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
+bool Parser::ParseConfigFileInsecure(const std::string& path) {
+    std::string config_contents;
+    if (!android::base::ReadFileToString(path, &config_contents)) {
+        return false;
+    }
+
+    ParseData(path, &config_contents);
+    return true;
+}
+
+bool Parser::ParseConfigFile(const std::string& path) {
     LOG(INFO) << "Parsing file " << path << "...";
     android::base::Timer t;
     auto config_contents = ReadFile(path);
     if (!config_contents) {
-        LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
+        LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
         return false;
     }
 
-    config_contents->push_back('\n');  // TODO: fix parse_config.
-    ParseData(path, *config_contents, parse_errors);
-    for (const auto& [section_name, section_parser] : section_parsers_) {
-        section_parser->EndFile();
-    }
+    ParseData(path, &config_contents.value());
 
     LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
     return true;
 }
 
-bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
+bool Parser::ParseConfigDir(const std::string& path) {
     LOG(INFO) << "Parsing directory " << path << "...";
     std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
     if (!config_dir) {
-        PLOG(ERROR) << "Could not import directory '" << path << "'";
+        PLOG(INFO) << "Could not import directory '" << path << "'";
         return false;
     }
     dirent* current_file;
@@ -153,7 +168,7 @@
     // Sort first so we load files in a consistent order (bug 31996208)
     std::sort(files.begin(), files.end());
     for (const auto& file : files) {
-        if (!ParseConfigFile(file, parse_errors)) {
+        if (!ParseConfigFile(file)) {
             LOG(ERROR) << "could not import file '" << file << "'";
         }
     }
@@ -161,16 +176,10 @@
 }
 
 bool Parser::ParseConfig(const std::string& path) {
-    size_t parse_errors;
-    return ParseConfig(path, &parse_errors);
-}
-
-bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
-    *parse_errors = 0;
     if (is_dir(path.c_str())) {
-        return ParseConfigDir(path, parse_errors);
+        return ParseConfigDir(path);
     }
-    return ParseConfigFile(path, parse_errors);
+    return ParseConfigFile(path);
 }
 
 }  // namespace init
diff --git a/init/parser.h b/init/parser.h
index f6e237f..2454b6a 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -72,17 +72,22 @@
     Parser();
 
     bool ParseConfig(const std::string& path);
-    bool ParseConfig(const std::string& path, size_t* parse_errors);
     void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
     void AddSingleLineParser(const std::string& prefix, LineCallback callback);
 
+    // Host init verifier check file permissions.
+    bool ParseConfigFileInsecure(const std::string& path);
+
+    size_t parse_error_count() const { return parse_error_count_; }
+
   private:
-    void ParseData(const std::string& filename, const std::string& data, size_t* parse_errors);
-    bool ParseConfigFile(const std::string& path, size_t* parse_errors);
-    bool ParseConfigDir(const std::string& path, size_t* parse_errors);
+    void ParseData(const std::string& filename, std::string* data);
+    bool ParseConfigFile(const std::string& path);
+    bool ParseConfigDir(const std::string& path);
 
     std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
     std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
+    size_t parse_error_count_ = 0;
 };
 
 }  // namespace init
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 741fde0..d1c427d 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -95,6 +95,11 @@
 
 void CreateSerializedPropertyInfo();
 
+struct PropertyAuditData {
+    const ucred* cr;
+    const char* name;
+};
+
 void property_init() {
     mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
     CreateSerializedPropertyInfo();
@@ -111,7 +116,7 @@
         return false;
     }
 
-    property_audit_data audit_data;
+    PropertyAuditData audit_data;
 
     audit_data.name = name.c_str();
     audit_data.cr = &cr;
@@ -393,6 +398,35 @@
     DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
 };
 
+bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
+                               const std::string& source_context, const ucred& cr) {
+    // We check the legacy method first but these properties are dontaudit, so we only log an audit
+    // if the newer method fails as well.  We only do this with the legacy ctl. properties.
+    if (name == "ctl.start" || name == "ctl.stop" || name == "ctl.restart") {
+        // The legacy permissions model is that ctl. properties have their name ctl.<action> and
+        // their value is the name of the service to apply that action to.  Permissions for these
+        // actions are based on the service, so we must create a fake name of ctl.<service> to
+        // check permissions.
+        auto control_string_legacy = "ctl." + value;
+        const char* target_context_legacy = nullptr;
+        const char* type_legacy = nullptr;
+        property_info_area->GetPropertyInfo(control_string_legacy.c_str(), &target_context_legacy,
+                                            &type_legacy);
+
+        if (CheckMacPerms(control_string_legacy, target_context_legacy, source_context.c_str(), cr)) {
+            return true;
+        }
+    }
+
+    auto control_string_full = name + "$" + value;
+    const char* target_context_full = nullptr;
+    const char* type_full = nullptr;
+    property_info_area->GetPropertyInfo(control_string_full.c_str(), &target_context_full,
+                                        &type_full);
+
+    return CheckMacPerms(control_string_full, target_context_full, source_context.c_str(), cr);
+}
+
 // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                            const std::string& source_context, const ucred& cr, std::string* error) {
@@ -402,15 +436,9 @@
     }
 
     if (StartsWith(name, "ctl.")) {
-        // ctl. properties have their name ctl.<action> and their value is the name of the service
-        // to apply that action to.  Permissions for these actions are based on the service, so we
-        // must create a fake name of ctl.<service> to check permissions.
-        auto control_string = "ctl." + value;
-        const char* target_context = nullptr;
-        const char* type = nullptr;
-        property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
-        if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) {
-            *error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str());
+        if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
+            *error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
+                                  value.c_str());
             return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
         }
 
@@ -742,7 +770,7 @@
 }
 
 static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
-    property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
+    auto* d = reinterpret_cast<PropertyAuditData*>(data);
 
     if (!d || !d->name || !d->cr) {
         LOG(ERROR) << "AuditCallback invoked with null data arguments!";
diff --git a/init/property_service.h b/init/property_service.h
index 4a354c2..cacd987 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -26,11 +26,6 @@
 namespace android {
 namespace init {
 
-struct property_audit_data {
-    const ucred* cr;
-    const char* name;
-};
-
 extern uint32_t (*property_set)(const std::string& name, const std::string& value);
 
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
diff --git a/init/service.cpp b/init/service.cpp
index 0e08d9b..4c2747e 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -18,6 +18,7 @@
 
 #include <fcntl.h>
 #include <inttypes.h>
+#include <linux/input.h>
 #include <linux/securebits.h>
 #include <sched.h>
 #include <sys/mount.h>
@@ -32,6 +33,7 @@
 #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 <android-base/unique_fd.h>
@@ -46,8 +48,6 @@
 #if defined(__ANDROID__)
 #include <sys/system_properties.h>
 
-#include <android-base/properties.h>
-
 #include "init.h"
 #include "property_service.h"
 #else
@@ -130,7 +130,7 @@
         if (umount2("/sys", MNT_DETACH) == -1) {
             return ErrnoError() << "Could not umount(/sys)";
         }
-        if (mount("", "/sys", "sys", kSafeFlags, "") == -1) {
+        if (mount("", "/sys", "sysfs", kSafeFlags, "") == -1) {
             return ErrnoError() << "Could not mount(/sys)";
         }
     }
@@ -228,7 +228,6 @@
       seclabel_(seclabel),
       onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
                  "onrestart", {}),
-      keychord_id_(0),
       ioprio_class_(IoSchedClass_NONE),
       ioprio_pri_(0),
       priority_(0),
@@ -544,10 +543,13 @@
 Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
     for (std::size_t i = 1; i < args.size(); i++) {
         int code;
-        if (ParseInt(args[i], &code)) {
-            keycodes_.emplace_back(code);
+        if (ParseInt(args[i], &code, 0, KEY_MAX)) {
+            for (auto& key : keycodes_) {
+                if (key == code) return Error() << "duplicate keycode: " << args[i];
+            }
+            keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
         } else {
-            LOG(WARNING) << "ignoring invalid keycode: " << args[i];
+            return Error() << "invalid keycode: " << args[i];
         }
     }
     return Success();
@@ -785,9 +787,9 @@
     flags_ |= SVC_EXEC;
     is_exec_service_running_ = true;
 
-    LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
-              << supp_gids_.size() << " context " << (!seclabel_.empty() ? seclabel_ : "default")
-              << ") started; waiting...";
+    LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << uid_ << " gid "
+              << gid_ << "+" << supp_gids_.size() << " context "
+              << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
 
     return Success();
 }
diff --git a/init/service.h b/init/service.h
index cbfd52f..ea79a07 100644
--- a/init/service.h
+++ b/init/service.h
@@ -108,8 +108,6 @@
     const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
     const std::string& seclabel() const { return seclabel_; }
     const std::vector<int>& keycodes() const { return keycodes_; }
-    int keychord_id() const { return keychord_id_; }
-    void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
     IoSchedClass ioprio_class() const { return ioprio_class_; }
     int ioprio_pri() const { return ioprio_pri_; }
     const std::set<std::string>& interfaces() const { return interfaces_; }
@@ -199,9 +197,8 @@
 
     std::set<std::string> interfaces_;  // e.g. some.package.foo@1.0::IBaz/instance-name
 
-    // keycodes for triggering this service via /dev/keychord
+    // keycodes for triggering this service via /dev/input/input*
     std::vector<int> keycodes_;
-    int keychord_id_;
 
     IoSchedClass ioprio_class_;
     int ioprio_pri_;
diff --git a/init/service_test.cpp b/init/service_test.cpp
index b43c2e9..194aa2b 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -46,7 +46,6 @@
     EXPECT_EQ(0U, service_in_old_memory->uid());
     EXPECT_EQ(0U, service_in_old_memory->gid());
     EXPECT_EQ(0U, service_in_old_memory->namespace_flags());
-    EXPECT_EQ(0, service_in_old_memory->keychord_id());
     EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory->ioprio_class());
     EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory->priority());
@@ -66,7 +65,6 @@
     EXPECT_EQ(0U, service_in_old_memory2->uid());
     EXPECT_EQ(0U, service_in_old_memory2->gid());
     EXPECT_EQ(0U, service_in_old_memory2->namespace_flags());
-    EXPECT_EQ(0, service_in_old_memory2->keychord_id());
     EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory2->ioprio_class());
     EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory2->priority());
diff --git a/init/tokenizer.cpp b/init/tokenizer.cpp
index f8d9b6b..bb143f1 100644
--- a/init/tokenizer.cpp
+++ b/init/tokenizer.cpp
@@ -85,15 +85,19 @@
                 goto textdone;
             case 'n':
                 *s++ = '\n';
+                x++;
                 break;
             case 'r':
                 *s++ = '\r';
+                x++;
                 break;
             case 't':
                 *s++ = '\t';
+                x++;
                 break;
             case '\\':
                 *s++ = '\\';
+                x++;
                 break;
             case '\r':
                     /* \ <cr> <lf> -> line continuation */
@@ -101,6 +105,7 @@
                     x++;
                     continue;
                 }
+                x++;
             case '\n':
                     /* \ <lf> -> line continuation */
                 state->line++;
diff --git a/init/tokenizer_test.cpp b/init/tokenizer_test.cpp
new file mode 100644
index 0000000..acfc7c7
--- /dev/null
+++ b/init/tokenizer_test.cpp
@@ -0,0 +1,163 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "tokenizer.h"
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace init {
+
+namespace {
+
+void RunTest(const std::string& data, const std::vector<std::vector<std::string>>& expected_tokens) {
+    auto data_copy = std::string{data};
+    data_copy.push_back('\n');  // TODO: fix tokenizer
+    data_copy.push_back('\0');
+
+    parse_state state;
+    state.line = 0;
+    state.ptr = data_copy.data();
+    state.nexttoken = 0;
+
+    std::vector<std::string> current_line;
+    std::vector<std::vector<std::string>> tokens;
+
+    while (true) {
+        switch (next_token(&state)) {
+            case T_EOF:
+                EXPECT_EQ(expected_tokens, tokens) << data;
+                return;
+            case T_NEWLINE:
+                tokens.emplace_back(std::move(current_line));
+                break;
+            case T_TEXT:
+                current_line.emplace_back(state.text);
+                break;
+        }
+    }
+}
+
+}  // namespace
+
+TEST(tokenizer, null) {
+    RunTest("", {{}});
+}
+
+TEST(tokenizer, simple_oneline) {
+    RunTest("one two\tthree\rfour", {{"one", "two", "three", "four"}});
+}
+
+TEST(tokenizer, simple_multiline) {
+    RunTest("1 2 3\n4 5 6\n7 8 9", {{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9"}});
+}
+
+TEST(tokenizer, preceding_space) {
+    // Preceding spaces are ignored.
+    RunTest("    1 2 3\n\t\t\t\t4 5 6\n\r\r\r\r7 8 9",
+            {{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9"}});
+}
+
+TEST(tokenizer, comments) {
+    // Entirely commented lines still produce a T_NEWLINE token for tracking line count.
+    RunTest("1 2 3\n#4 5 6\n7 8 9", {{"1", "2", "3"}, {}, {"7", "8", "9"}});
+
+    RunTest("#1 2 3\n4 5 6\n7 8 9", {{}, {"4", "5", "6"}, {"7", "8", "9"}});
+
+    RunTest("1 2 3\n4 5 6\n#7 8 9", {{"1", "2", "3"}, {"4", "5", "6"}, {}});
+
+    RunTest("1 2 #3\n4 #5 6\n#7 8 9", {{"1", "2"}, {"4"}, {}});
+}
+
+TEST(tokenizer, control_chars) {
+    // Literal \n, \r, \t, and \\ produce the control characters \n, \r, \t, and \\ respectively.
+    // Literal \? produces ? for all other character '?'
+
+    RunTest(R"(1 token\ntoken 2)", {{"1", "token\ntoken", "2"}});
+    RunTest(R"(1 token\rtoken 2)", {{"1", "token\rtoken", "2"}});
+    RunTest(R"(1 token\ttoken 2)", {{"1", "token\ttoken", "2"}});
+    RunTest(R"(1 token\\token 2)", {{"1", "token\\token", "2"}});
+    RunTest(R"(1 token\btoken 2)", {{"1", "tokenbtoken", "2"}});
+
+    RunTest(R"(1 token\n 2)", {{"1", "token\n", "2"}});
+    RunTest(R"(1 token\r 2)", {{"1", "token\r", "2"}});
+    RunTest(R"(1 token\t 2)", {{"1", "token\t", "2"}});
+    RunTest(R"(1 token\\ 2)", {{"1", "token\\", "2"}});
+    RunTest(R"(1 token\b 2)", {{"1", "tokenb", "2"}});
+
+    RunTest(R"(1 \ntoken 2)", {{"1", "\ntoken", "2"}});
+    RunTest(R"(1 \rtoken 2)", {{"1", "\rtoken", "2"}});
+    RunTest(R"(1 \ttoken 2)", {{"1", "\ttoken", "2"}});
+    RunTest(R"(1 \\token 2)", {{"1", "\\token", "2"}});
+    RunTest(R"(1 \btoken 2)", {{"1", "btoken", "2"}});
+
+    RunTest(R"(1 \n 2)", {{"1", "\n", "2"}});
+    RunTest(R"(1 \r 2)", {{"1", "\r", "2"}});
+    RunTest(R"(1 \t 2)", {{"1", "\t", "2"}});
+    RunTest(R"(1 \\ 2)", {{"1", "\\", "2"}});
+    RunTest(R"(1 \b 2)", {{"1", "b", "2"}});
+}
+
+TEST(tokenizer, cr_lf) {
+    // \ before \n, \r, or \r\n is interpreted as a line continuation
+    // Extra whitespace on the next line is eaten, except \r unlike in the above tests.
+
+    RunTest("lf\\\ncont", {{"lfcont"}});
+    RunTest("lf\\\n    \t\t\t\tcont", {{"lfcont"}});
+
+    RunTest("crlf\\\r\ncont", {{"crlfcont"}});
+    RunTest("crlf\\\r\n    \t\t\t\tcont", {{"crlfcont"}});
+
+    RunTest("cr\\\rcont", {{"crcont"}});
+
+    RunTest("lfspace \\\ncont", {{"lfspace", "cont"}});
+    RunTest("lfspace \\\n    \t\t\t\tcont", {{"lfspace", "cont"}});
+
+    RunTest("crlfspace \\\r\ncont", {{"crlfspace", "cont"}});
+    RunTest("crlfspace \\\r\n    \t\t\t\tcont", {{"crlfspace", "cont"}});
+
+    RunTest("crspace \\\rcont", {{"crspace", "cont"}});
+}
+
+TEST(tokenizer, quoted) {
+    RunTest("\"quoted simple string\"", {{"quoted simple string"}});
+
+    // Unterminated quotes just return T_EOF without any T_NEWLINE.
+    RunTest("\"unterminated quoted string", {});
+
+    RunTest("\"1 2 3\"\n \"unterminated quoted string", {{"1 2 3"}});
+
+    // Escaping quotes is not allowed and are treated as an unterminated quoted string.
+    RunTest("\"quoted escaped quote\\\"\"", {});
+    RunTest("\"quoted escaped\\\" quote\"", {});
+    RunTest("\"\\\"quoted escaped quote\"", {});
+
+    RunTest("\"quoted control characters \\n \\r \\t \\\\ \\b \\\r \\\n \r \n\"",
+            {{"quoted control characters \\n \\r \\t \\\\ \\b \\\r \\\n \r \n"}});
+
+    RunTest("\"quoted simple string\" \"second quoted string\"",
+            {{"quoted simple string", "second quoted string"}});
+
+    RunTest("\"# comment quoted string\"", {{"# comment quoted string"}});
+
+    RunTest("\"Adjacent \"\"quoted strings\"", {{"Adjacent quoted strings"}});
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index a284203..b42a4c6 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -215,39 +215,6 @@
     LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
 }
 
-DeviceHandler CreateDeviceHandler() {
-    Parser parser;
-
-    std::vector<Subsystem> subsystems;
-    parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>(&subsystems));
-
-    using namespace std::placeholders;
-    std::vector<SysfsPermissions> sysfs_permissions;
-    std::vector<Permissions> dev_permissions;
-    parser.AddSingleLineParser("/sys/",
-                               std::bind(ParsePermissionsLine, _1, &sysfs_permissions, nullptr));
-    parser.AddSingleLineParser("/dev/",
-                               std::bind(ParsePermissionsLine, _1, nullptr, &dev_permissions));
-
-    parser.ParseConfig("/ueventd.rc");
-    parser.ParseConfig("/vendor/ueventd.rc");
-    parser.ParseConfig("/odm/ueventd.rc");
-
-    /*
-     * keep the current product name base configuration so
-     * we remain backwards compatible and allow it to override
-     * everything
-     * TODO: cleanup platform ueventd.rc to remove vendor specific
-     * device node entries (b/34968103)
-     */
-    std::string hardware = android::base::GetProperty("ro.hardware", "");
-    parser.ParseConfig("/ueventd." + hardware + ".rc");
-
-    auto boot_devices = fs_mgr_get_boot_devices();
-    return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
-                         std::move(subsystems), std::move(boot_devices), true);
-}
-
 int ueventd_main(int argc, char** argv) {
     /*
      * init sets the umask to 077 for forked processes. We need to
@@ -263,9 +230,27 @@
     SelinuxSetupKernelLogging();
     SelabelInitialize();
 
-    DeviceHandler device_handler = CreateDeviceHandler();
+    DeviceHandler device_handler;
     UeventListener uevent_listener;
 
+    {
+        // Keep the current product name base configuration so we remain backwards compatible and
+        // allow it to override everything.
+        // TODO: cleanup platform ueventd.rc to remove vendor specific device node entries (b/34968103)
+        auto hardware = android::base::GetProperty("ro.hardware", "");
+
+        auto ueventd_configuration =
+                ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
+                             "/ueventd." + hardware + ".rc"});
+
+        device_handler = DeviceHandler{std::move(ueventd_configuration.dev_permissions),
+                                       std::move(ueventd_configuration.sysfs_permissions),
+                                       std::move(ueventd_configuration.subsystems),
+                                       fs_mgr_get_boot_devices(), true};
+
+        firmware_directories = ueventd_configuration.firmware_directories;
+    }
+
     if (access(COLDBOOT_DONE, F_OK) != 0) {
         ColdBoot cold_boot(uevent_listener, device_handler);
         cold_boot.Run();
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index f74c878..54b0d16 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -20,6 +20,7 @@
 #include <pwd.h>
 
 #include "keyword_map.h"
+#include "parser.h"
 
 namespace android {
 namespace init {
@@ -72,6 +73,33 @@
     return Success();
 }
 
+Result<Success> ParseFirmwareDirectoriesLine(std::vector<std::string>&& args,
+                                             std::vector<std::string>* firmware_directories) {
+    if (args.size() < 2) {
+        return Error() << "firmware_directories must have at least 1 entry";
+    }
+
+    std::move(std::next(args.begin()), args.end(), std::back_inserter(*firmware_directories));
+
+    return Success();
+}
+
+class SubsystemParser : public SectionParser {
+  public:
+    SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
+    Result<Success> EndSection() override;
+
+  private:
+    Result<Success> ParseDevName(std::vector<std::string>&& args);
+    Result<Success> ParseDirName(std::vector<std::string>&& args);
+
+    Subsystem subsystem_;
+    std::vector<Subsystem>* subsystems_;
+};
+
 Result<Success> SubsystemParser::ParseSection(std::vector<std::string>&& args,
                                               const std::string& filename, int line) {
     if (args.size() != 2) {
@@ -89,11 +117,11 @@
 
 Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
     if (args[1] == "uevent_devname") {
-        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
+        subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVNAME;
         return Success();
     }
     if (args[1] == "uevent_devpath") {
-        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
+        subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVPATH;
         return Success();
     }
 
@@ -138,5 +166,29 @@
     return Success();
 }
 
+UeventdConfiguration ParseConfig(const std::vector<std::string>& configs) {
+    Parser parser;
+    UeventdConfiguration ueventd_configuration;
+
+    parser.AddSectionParser("subsystem",
+                            std::make_unique<SubsystemParser>(&ueventd_configuration.subsystems));
+
+    using namespace std::placeholders;
+    parser.AddSingleLineParser(
+            "/sys/",
+            std::bind(ParsePermissionsLine, _1, &ueventd_configuration.sysfs_permissions, nullptr));
+    parser.AddSingleLineParser("/dev/", std::bind(ParsePermissionsLine, _1, nullptr,
+                                                  &ueventd_configuration.dev_permissions));
+    parser.AddSingleLineParser("firmware_directories",
+                               std::bind(ParseFirmwareDirectoriesLine, _1,
+                                         &ueventd_configuration.firmware_directories));
+
+    for (const auto& config : configs) {
+        parser.ParseConfig(config);
+    }
+
+    return ueventd_configuration;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 83684f3..343d58b 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -21,30 +21,18 @@
 #include <vector>
 
 #include "devices.h"
-#include "parser.h"
 
 namespace android {
 namespace init {
 
-class SubsystemParser : public SectionParser {
-  public:
-    SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
-    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                 int line) override;
-    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
-    Result<Success> EndSection() override;
-
-  private:
-    Result<Success> ParseDevName(std::vector<std::string>&& args);
-    Result<Success> ParseDirName(std::vector<std::string>&& args);
-
-    Subsystem subsystem_;
-    std::vector<Subsystem>* subsystems_;
+struct UeventdConfiguration {
+    std::vector<Subsystem> subsystems;
+    std::vector<SysfsPermissions> sysfs_permissions;
+    std::vector<Permissions> dev_permissions;
+    std::vector<std::string> firmware_directories;
 };
 
-Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
-                                     std::vector<SysfsPermissions>* out_sysfs_permissions,
-                                     std::vector<Permissions>* out_dev_permissions);
+UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
new file mode 100644
index 0000000..31208b9
--- /dev/null
+++ b/init/ueventd_parser_test.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ueventd_parser.h"
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+
+namespace android {
+namespace init {
+
+void TestSubsystems(const Subsystem& expected, const Subsystem& test) {
+    EXPECT_EQ(expected.name_, test.name_);
+    EXPECT_EQ(expected.devname_source_, test.devname_source_) << expected.name_;
+    EXPECT_EQ(expected.dir_name_, test.dir_name_) << expected.name_;
+}
+
+void TestPermissions(const Permissions& expected, const Permissions& test) {
+    EXPECT_EQ(expected.name_, test.name_);
+    EXPECT_EQ(expected.perm_, test.perm_) << expected.name_;
+    EXPECT_EQ(expected.uid_, test.uid_) << expected.name_;
+    EXPECT_EQ(expected.gid_, test.gid_) << expected.name_;
+    EXPECT_EQ(expected.prefix_, test.prefix_) << expected.name_;
+    EXPECT_EQ(expected.wildcard_, test.wildcard_) << expected.name_;
+}
+
+void TestSysfsPermissions(const SysfsPermissions& expected, const SysfsPermissions& test) {
+    TestPermissions(expected, test);
+    EXPECT_EQ(expected.attribute_, test.attribute_);
+}
+
+template <typename T, typename F>
+void TestVector(const T& expected, const T& test, F function) {
+    ASSERT_EQ(expected.size(), test.size());
+    auto expected_it = expected.begin();
+    auto test_it = test.begin();
+
+    for (; expected_it != expected.end(); ++expected_it, ++test_it) {
+        function(*expected_it, *test_it);
+    }
+}
+
+void TestUeventdFile(const std::string& content, const UeventdConfiguration& expected) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd(content, tf.fd));
+
+    auto result = ParseConfig({tf.path});
+
+    TestVector(expected.subsystems, result.subsystems, TestSubsystems);
+    TestVector(expected.sysfs_permissions, result.sysfs_permissions, TestSysfsPermissions);
+    TestVector(expected.dev_permissions, result.dev_permissions, TestPermissions);
+    EXPECT_EQ(expected.firmware_directories, result.firmware_directories);
+}
+
+TEST(ueventd_parser, EmptyFile) {
+    TestUeventdFile("", {});
+}
+
+TEST(ueventd_parser, Subsystems) {
+    auto ueventd_file = R"(
+subsystem test_devname
+    devname uevent_devname
+
+subsystem test_devpath_no_dirname
+    devname uevent_devpath
+
+subsystem test_devname2
+    devname uevent_devname
+
+subsystem test_devpath_dirname
+    devname uevent_devpath
+    dirname /dev/graphics
+)";
+
+    auto subsystems = std::vector<Subsystem>{
+            {"test_devname", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_no_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev"},
+            {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
+
+    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}});
+}
+
+TEST(ueventd_parser, Permissions) {
+    auto ueventd_file = R"(
+/dev/rtc0                 0640   system     system
+/dev/graphics/*           0660   root       graphics
+/dev/*/test               0660   root       system
+
+/sys/devices/platform/trusty.*      trusty_version        0440  root   log
+/sys/devices/virtual/input/input   enable      0660  root   input
+/sys/devices/virtual/*/input   poll_delay  0660  root   input
+)";
+
+    auto permissions = std::vector<Permissions>{
+            {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM},
+            {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS},
+            {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM},
+    };
+
+    auto sysfs_permissions = std::vector<SysfsPermissions>{
+            {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG},
+            {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT},
+            {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
+    };
+
+    TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}});
+}
+
+TEST(ueventd_parser, FirmwareDirectories) {
+    auto ueventd_file = R"(
+firmware_directories /first/ /second /third
+firmware_directories /more
+)";
+
+    auto firmware_directories = std::vector<std::string>{
+            "/first/",
+            "/second",
+            "/third",
+            "/more",
+    };
+
+    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories});
+}
+
+TEST(ueventd_parser, AllTogether) {
+    auto ueventd_file = R"(
+
+/dev/rtc0                 0640   system     system
+firmware_directories /first/ /second /third
+/sys/devices/platform/trusty.*      trusty_version        0440  root   log
+
+subsystem test_devname
+    devname uevent_devname
+
+/dev/graphics/*           0660   root       graphics
+
+subsystem test_devpath_no_dirname
+    devname uevent_devpath
+
+/sys/devices/virtual/input/input   enable      0660  root   input
+
+## this is a comment
+
+subsystem test_devname2
+## another comment
+    devname uevent_devname
+
+subsystem test_devpath_dirname
+    devname uevent_devpath
+    dirname /dev/graphics
+
+/dev/*/test               0660   root       system
+/sys/devices/virtual/*/input   poll_delay  0660  root   input
+firmware_directories /more
+
+#ending comment
+)";
+
+    auto subsystems = std::vector<Subsystem>{
+            {"test_devname", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_no_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev"},
+            {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
+
+    auto permissions = std::vector<Permissions>{
+            {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM},
+            {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS},
+            {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM},
+    };
+
+    auto sysfs_permissions = std::vector<SysfsPermissions>{
+            {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG},
+            {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT},
+            {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
+    };
+
+    auto firmware_directories = std::vector<std::string>{
+            "/first/",
+            "/second",
+            "/third",
+            "/more",
+    };
+
+    TestUeventdFile(ueventd_file,
+                    {subsystems, sysfs_permissions, permissions, firmware_directories});
+}
+
+// All of these lines are ill-formed, so test that there is 0 output.
+TEST(ueventd_parser, ParseErrors) {
+    auto ueventd_file = R"(
+
+/dev/rtc0                 badmode   baduidbad     system
+/dev/rtc0                 0640   baduidbad     system
+/dev/rtc0                 0640   system     baduidbad
+firmware_directories #no directory listed
+/sys/devices/platform/trusty.*      trusty_version        badmode  root   log
+/sys/devices/platform/trusty.*      trusty_version        0440  baduidbad   log
+/sys/devices/platform/trusty.*      trusty_version        0440  root   baduidbad
+
+subsystem #no name
+
+)";
+
+    TestUeventdFile(ueventd_file, {});
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/util.cpp b/init/util.cpp
index 4455b2e..5f2b87d 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -33,6 +33,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -43,8 +44,6 @@
 #include "reboot.h"
 
 #if defined(__ANDROID__)
-#include <android-base/properties.h>
-
 #include "selinux.h"
 #else
 #include "host_init_stubs.h"
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 8a2afea..4ab439d 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -27,6 +27,7 @@
     name: "libasyncio",
     defaults: ["libasyncio_defaults"],
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
     srcs: [
         "AsyncIO.cpp",
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 0f93dd0..a10e636 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -42,7 +42,6 @@
     "Backtrace.cpp",
     "BacktraceCurrent.cpp",
     "BacktracePtrace.cpp",
-    "thread_utils.c",
     "ThreadEntry.cpp",
     "UnwindStack.cpp",
     "UnwindStackMap.cpp",
@@ -58,6 +57,7 @@
 cc_library {
     name: "libbacktrace",
     vendor_available: false,
+    recovery_available: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -93,7 +93,6 @@
             ],
 
             static_libs: [
-                "libcutils",
                 "libprocinfo",
             ],
 
@@ -102,8 +101,6 @@
             include_dirs: [
                 "art/runtime",
             ],
-
-            header_libs: ["jni_headers"],
         },
         android: {
             static_libs: ["libasync_safe"],
@@ -112,6 +109,10 @@
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
             exclude_shared_libs: ["libdexfile"],
         },
+        recovery: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            exclude_shared_libs: ["libdexfile"],
+        },
     },
     whole_static_libs: ["libdemangle"],
 }
@@ -142,7 +143,6 @@
         "backtrace_offline_test.cpp",
         "backtrace_test.cpp",
         "GetPss.cpp",
-        "thread_utils.c",
     ],
 
     cflags: [
@@ -156,7 +156,6 @@
         "libbacktrace",
         "libdexfile",
         "libbase",
-        "libcutils",
         "liblog",
         "libunwindstack",
     ],
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 6445a7c..6bec63c 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -23,6 +23,7 @@
 #include <string>
 
 #include <android-base/stringprintf.h>
+#include <android-base/threads.h>
 
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
@@ -31,7 +32,6 @@
 
 #include "BacktraceLog.h"
 #include "UnwindStack.h"
-#include "thread_utils.h"
 
 using android::base::StringPrintf;
 
@@ -124,7 +124,7 @@
   if (pid == BACKTRACE_CURRENT_PROCESS) {
     pid = getpid();
     if (tid == BACKTRACE_CURRENT_THREAD) {
-      tid = gettid();
+      tid = android::base::GetThreadId();
     }
   } else if (tid == BACKTRACE_CURRENT_THREAD) {
     tid = pid;
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index f6f4423..39cb995 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -28,13 +28,13 @@
 
 #include <string>
 
+#include <android-base/threads.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
 #include "BacktraceAsyncSafeLog.h"
 #include "BacktraceCurrent.h"
 #include "ThreadEntry.h"
-#include "thread_utils.h"
 
 bool BacktraceCurrent::ReadWord(uint64_t ptr, word_t* out_value) {
   if (!VerifyReadWordArgs(ptr, out_value)) {
@@ -76,7 +76,7 @@
     return UnwindFromContext(num_ignore_frames, ucontext);
   }
 
-  if (Tid() != gettid()) {
+  if (Tid() != android::base::GetThreadId()) {
     return UnwindThread(num_ignore_frames);
   }
 
@@ -114,16 +114,17 @@
 static void SignalLogOnly(int, siginfo_t*, void*) {
   ErrnoRestorer restore;
 
-  BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(),
-                       THREAD_SIGNAL);
+  BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(),
+                       static_cast<int>(android::base::GetThreadId()), THREAD_SIGNAL);
 }
 
 static void SignalHandler(int, siginfo_t*, void* sigcontext) {
   ErrnoRestorer restore;
 
-  ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
+  ThreadEntry* entry = ThreadEntry::Get(getpid(), android::base::GetThreadId(), false);
   if (!entry) {
-    BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
+    BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(),
+                         static_cast<int>(android::base::GetThreadId()));
     return;
   }
 
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 399721d..6a967f7 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -32,8 +32,6 @@
 #include <procinfo/process_map.h>
 #endif
 
-#include "thread_utils.h"
-
 using android::base::StringPrintf;
 
 std::string backtrace_map_t::Name() const {
diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp
index bf6b16f..9da457d 100644
--- a/libbacktrace/BacktracePtrace.cpp
+++ b/libbacktrace/BacktracePtrace.cpp
@@ -28,7 +28,6 @@
 
 #include "BacktraceLog.h"
 #include "BacktracePtrace.h"
-#include "thread_utils.h"
 
 #if !defined(__APPLE__)
 static bool PtraceRead(pid_t tid, uint64_t addr, word_t* out_value) {
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index e087b2e..4e7f761 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -23,10 +23,6 @@
 #include <set>
 #include <string>
 
-#if !defined(__ANDROID__)
-#include <cutils/threads.h>
-#endif
-
 #include <backtrace/Backtrace.h>
 #include <demangle.h>
 #include <unwindstack/Elf.h>
diff --git a/libbacktrace/backtrace_benchmarks.cpp b/libbacktrace/backtrace_benchmarks.cpp
index a23e3b4..099ac60 100644
--- a/libbacktrace/backtrace_benchmarks.cpp
+++ b/libbacktrace/backtrace_benchmarks.cpp
@@ -27,6 +27,7 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/threads.h>
 
 #include <benchmark/benchmark.h>
 
@@ -154,7 +155,7 @@
 
 static void CreateBacktrace(benchmark::State& state, BacktraceMap* map, BacktraceCreateFn fn) {
   while (state.KeepRunning()) {
-    std::unique_ptr<Backtrace> backtrace(fn(getpid(), gettid(), map));
+    std::unique_ptr<Backtrace> backtrace(fn(getpid(), android::base::GetThreadId(), map));
     backtrace->Unwind(0);
   }
 }
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 9877f29..7d1027e 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -31,9 +31,9 @@
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/threads.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
-#include <cutils/threads.h>
 
 #include <gtest/gtest.h>
 
@@ -99,7 +99,7 @@
 
 static void* OfflineThreadFunc(void* arg) {
   OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
-  fn_arg->tid = gettid();
+  fn_arg->tid = android::base::GetThreadId();
   test_get_context_and_wait(&fn_arg->ucontext, &fn_arg->exit_flag);
   return nullptr;
 }
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 1e3d379..06a32c7 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -46,16 +46,16 @@
 
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+#include <android-base/threads.h>
 #include <android-base/unique_fd.h>
 #include <cutils/atomic.h>
-#include <cutils/threads.h>
 
 #include <gtest/gtest.h>
 
 // For the THREAD_SIGNAL definition.
 #include "BacktraceCurrent.h"
 #include "backtrace_testlib.h"
-#include "thread_utils.h"
 
 // Number of microseconds per milliseconds.
 #define US_PER_MSEC             1000
@@ -524,7 +524,7 @@
 }
 
 void VerifyLevelThread(void*) {
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), android::base::GetThreadId()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
   VERIFY_NO_ERROR(backtrace->GetError().error_code);
@@ -537,7 +537,7 @@
 }
 
 static void VerifyMaxThread(void*) {
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), android::base::GetThreadId()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
   ASSERT_EQ(BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
@@ -552,7 +552,7 @@
 static void* ThreadLevelRun(void* data) {
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
-  thread->tid = gettid();
+  thread->tid = android::base::GetThreadId();
   EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
   return nullptr;
 }
@@ -643,7 +643,7 @@
 static void* ThreadMaxRun(void* data) {
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
-  thread->tid = gettid();
+  thread->tid = android::base::GetThreadId();
   EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
   return nullptr;
 }
@@ -993,7 +993,7 @@
 static void* ThreadReadTest(void* data) {
   thread_t* thread_data = reinterpret_cast<thread_t*>(data);
 
-  thread_data->tid = gettid();
+  thread_data->tid = android::base::GetThreadId();
 
   // Create two map pages.
   // Mark the second page as not-readable.
@@ -1186,49 +1186,45 @@
   ASSERT_TRUE(expected_functions.empty()) << "Not all functions found in shared library.";
 }
 
-static const char* CopySharedLibrary() {
-#if defined(__LP64__)
-  const char* lib_name = "lib64";
-#else
-  const char* lib_name = "lib";
-#endif
+static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+  std::string system_dir;
 
 #if defined(__BIONIC__)
-  const char* tmp_so_name = "/data/local/tmp/libbacktrace_test.so";
-  std::string cp_cmd = android::base::StringPrintf("cp /system/%s/libbacktrace_test.so %s",
-                                                   lib_name, tmp_so_name);
+  system_dir = "/system/lib";
 #else
-  const char* tmp_so_name = "/tmp/libbacktrace_test.so";
-  if (getenv("ANDROID_HOST_OUT") == NULL) {
-    fprintf(stderr, "ANDROID_HOST_OUT not set, make sure you run lunch.");
-    return nullptr;
-  }
-  std::string cp_cmd = android::base::StringPrintf("cp %s/%s/libbacktrace_test.so %s",
-                                                   getenv("ANDROID_HOST_OUT"), lib_name,
-                                                   tmp_so_name);
+  const char* host_out_env = getenv("ANDROID_HOST_OUT");
+  ASSERT_TRUE(host_out_env != nullptr);
+  system_dir = std::string(host_out_env) + "/lib";
 #endif
 
-  // Copy the shared so to a tempory directory.
-  system(cp_cmd.c_str());
+#if defined(__LP64__)
+  system_dir += "64";
+#endif
 
-  return tmp_so_name;
+  *tmp_so_name = std::string(tmp_dir) + "/libbacktrace_test.so";
+  std::string cp_cmd =
+      android::base::StringPrintf("cp %s/libbacktrace_test.so %s", system_dir.c_str(), tmp_dir);
+
+  // Copy the shared so to a tempory directory.
+  ASSERT_EQ(0, system(cp_cmd.c_str()));
 }
 
 TEST(libbacktrace, check_unreadable_elf_local) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
+  TemporaryDir td;
+  std::string tmp_so_name;
+  ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
 
   struct stat buf;
-  ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+  ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
   uint64_t map_size = buf.st_size;
 
-  int fd = open(tmp_so_name, O_RDONLY);
+  int fd = open(tmp_so_name.c_str(), O_RDONLY);
   ASSERT_TRUE(fd != -1);
 
   void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
   ASSERT_TRUE(map != MAP_FAILED);
   close(fd);
-  ASSERT_TRUE(unlink(tmp_so_name) != -1);
+  ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
 
   std::vector<std::string> found_functions;
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
@@ -1256,32 +1252,33 @@
 }
 
 TEST(libbacktrace, check_unreadable_elf_remote) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
+  TemporaryDir td;
+  std::string tmp_so_name;
+  ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
 
   g_ready = 0;
 
   struct stat buf;
-  ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+  ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
   uint64_t map_size = buf.st_size;
 
   pid_t pid;
   if ((pid = fork()) == 0) {
-    int fd = open(tmp_so_name, O_RDONLY);
+    int fd = open(tmp_so_name.c_str(), O_RDONLY);
     if (fd == -1) {
-      fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name, strerror(errno));
-      unlink(tmp_so_name);
+      fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name.c_str(), strerror(errno));
+      unlink(tmp_so_name.c_str());
       exit(0);
     }
 
     void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
     if (map == MAP_FAILED) {
       fprintf(stderr, "Failed to map in memory: %s\n", strerror(errno));
-      unlink(tmp_so_name);
+      unlink(tmp_so_name.c_str());
       exit(0);
     }
     close(fd);
-    if (unlink(tmp_so_name) == -1) {
+    if (unlink(tmp_so_name.c_str()) == -1) {
       fprintf(stderr, "Failed to unlink: %s\n", strerror(errno));
       exit(0);
     }
@@ -1394,11 +1391,13 @@
 typedef int (*test_func_t)(int, int, int, int, void (*)(void*), void*);
 
 TEST(libbacktrace, unwind_through_unreadable_elf_local) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
-  void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+  TemporaryDir td;
+  std::string tmp_so_name;
+  ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+  void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
   ASSERT_TRUE(lib_handle != nullptr);
-  ASSERT_TRUE(unlink(tmp_so_name) != -1);
+  ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
 
   test_func_t test_func;
   test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1411,11 +1410,13 @@
 }
 
 TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
-  void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+  TemporaryDir td;
+  std::string tmp_so_name;
+  ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+  void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
   ASSERT_TRUE(lib_handle != nullptr);
-  ASSERT_TRUE(unlink(tmp_so_name) != -1);
+  ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
 
   test_func_t test_func;
   test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1444,7 +1445,8 @@
 
     size_t frame_num;
     if (FindFuncFrameInBacktrace(backtrace.get(), reinterpret_cast<uint64_t>(test_func),
-                                 &frame_num)) {
+                                 &frame_num) &&
+        frame_num != 0) {
       VerifyUnreadableElfFrame(backtrace.get(), reinterpret_cast<uint64_t>(test_func), frame_num);
       done = true;
     }
@@ -1813,7 +1815,8 @@
 
 static void TestFrameSkipNumbering(create_func_t create_func, map_create_func_t map_create_func) {
   std::unique_ptr<BacktraceMap> map(map_create_func(getpid(), false));
-  std::unique_ptr<Backtrace> backtrace(create_func(getpid(), gettid(), map.get()));
+  std::unique_ptr<Backtrace> backtrace(
+      create_func(getpid(), android::base::GetThreadId(), map.get()));
   backtrace->Unwind(1);
   ASSERT_NE(0U, backtrace->NumFrames());
   ASSERT_EQ(0U, backtrace->GetFrame(0)->num);
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 735a2f3..10e790b 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -122,7 +122,7 @@
   // Tracing a thread in a different process is not supported.
   // If map is NULL, then create the map and manage it internally.
   // If map is not NULL, the map is still owned by the caller.
-  static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
+  static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = nullptr);
 
   // Create an offline Backtrace object that can be used to do an unwind without a process
   // that is still running. By default, information is only cached in the map
@@ -145,7 +145,7 @@
   virtual ~Backtrace();
 
   // Get the current stack trace and store in the backtrace_ structure.
-  virtual bool Unwind(size_t num_ignore_frames, void* context = NULL) = 0;
+  virtual bool Unwind(size_t num_ignore_frames, void* context = nullptr) = 0;
 
   static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
                      std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
@@ -160,7 +160,7 @@
   // If the string is empty, then no valid function name was found,
   // or the pc is not in any valid map.
   virtual std::string GetFunctionName(uint64_t pc, uint64_t* offset,
-                                      const backtrace_map_t* map = NULL);
+                                      const backtrace_map_t* map = nullptr);
 
   // Fill in the map data associated with the given pc.
   virtual void FillInMap(uint64_t pc, backtrace_map_t* map);
@@ -185,7 +185,7 @@
 
   const backtrace_frame_data_t* GetFrame(size_t frame_num) {
     if (frame_num >= frames_.size()) {
-      return NULL;
+      return nullptr;
     }
     return &frames_[frame_num];
   }
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index 47de12a..e47560f 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -17,6 +17,7 @@
 cc_library {
     name: "libcrypto_utils",
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
     },
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index cdbb65f..37afb98 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -20,6 +20,7 @@
 libcutils_nonwindows_sources = [
     "android_get_control_file.cpp",
     "fs.cpp",
+    "hashmap.cpp",
     "multiuser.cpp",
     "socket_inaddr_any_server_unix.cpp",
     "socket_local_client_unix.cpp",
@@ -61,11 +62,9 @@
         "config_utils.cpp",
         "fs_config.cpp",
         "canned_fs_config.cpp",
-        "hashmap.cpp",
         "iosched_policy.cpp",
         "load_file.cpp",
         "native_handle.cpp",
-        "open_memstream.c",
         "record_stream.cpp",
         "sched_policy.cpp",
         "sockets.cpp",
diff --git a/libcutils/OWNERS b/libcutils/OWNERS
new file mode 100644
index 0000000..c18ed51
--- /dev/null
+++ b/libcutils/OWNERS
@@ -0,0 +1,4 @@
+cferris@google.com
+enh@google.com
+jmgao@google.com
+tomcherry@google.com
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 15ace0e..0cc4fc0 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -90,7 +90,7 @@
     dev_t rdev;
     struct stat st;
 
-    if (TEMP_FAILURE_RETRY(fstat(fd, &st)) < 0) {
+    if (fstat(fd, &st) < 0) {
         return -1;
     }
 
@@ -135,6 +135,12 @@
     return -1;
 }
 
+static int __ashmem_check_failure(int fd, int result)
+{
+    if (result == -1 && errno == ENOTTY) __ashmem_is_ashmem(fd, 1);
+    return result;
+}
+
 int ashmem_valid(int fd)
 {
     return __ashmem_is_ashmem(fd, 0) >= 0;
@@ -182,12 +188,7 @@
 
 int ashmem_set_prot_region(int fd, int prot)
 {
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
-    }
-
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot)));
 }
 
 int ashmem_pin_region(int fd, size_t offset, size_t len)
@@ -195,12 +196,7 @@
     // TODO: should LP64 reject too-large offset/len?
     ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
 
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
-    }
-
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin));
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin)));
 }
 
 int ashmem_unpin_region(int fd, size_t offset, size_t len)
@@ -208,20 +204,10 @@
     // TODO: should LP64 reject too-large offset/len?
     ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
 
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
-    }
-
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin));
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin)));
 }
 
 int ashmem_get_size_region(int fd)
 {
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
-    }
-
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
 }
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 0f2b460..267b7b3 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -80,6 +80,7 @@
     { 00775, AID_ROOT,         AID_ROOT,         0, "data/preloads" },
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data" },
     { 00755, AID_ROOT,         AID_SYSTEM,       0, "mnt" },
+    { 00755, AID_ROOT,         AID_SHELL,        0, "product/bin" },
     { 00750, AID_ROOT,         AID_SHELL,        0, "sbin" },
     { 00777, AID_ROOT,         AID_ROOT,         0, "sdcard" },
     { 00751, AID_ROOT,         AID_SDCARD_R,     0, "storage" },
@@ -153,6 +154,7 @@
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump64" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/debuggerd" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/install-recovery.sh" },
+    { 00550, AID_LOGD,      AID_LOGD,      0, "system/bin/logd" },
     { 00700, AID_ROOT,      AID_ROOT,      0, "system/bin/secilc" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/uncrypt" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "system/build.prop" },
@@ -178,12 +180,6 @@
     // in user builds.
     { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
                                               "system/bin/inputflinger" },
-    { 00550, AID_LOGD,      AID_LOGD,      CAP_MASK_LONG(CAP_SYSLOG) |
-                                           CAP_MASK_LONG(CAP_AUDIT_CONTROL) |
-                                           CAP_MASK_LONG(CAP_SETGID),
-                                              "system/bin/logd" },
-    { 00550, AID_SYSTEM,    AID_LOG,      CAP_MASK_LONG(CAP_SYSLOG),
-                                              "system/bin/bootstat" },
     { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
                                            CAP_MASK_LONG(CAP_SETGID),
                                               "system/bin/run-as" },
@@ -195,6 +191,7 @@
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "init*" },
+    { 00755, AID_ROOT,      AID_SHELL,     0, "product/bin/*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib/valgrind/*" },
@@ -237,9 +234,10 @@
     return fd;
 }
 
-// if path is "vendor/<stuff>", "oem/<stuff>" or "odm/<stuff>"
+// if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>" or
+// "vendor/<stuff>"
 static bool is_partition(const char* path, size_t len) {
-    static const char* partitions[] = {"vendor/", "oem/", "odm/"};
+    static const char* partitions[] = {"odm/", "oem/", "product/", "vendor/"};
     for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
         size_t plen = strlen(partitions[i]);
         if (len <= plen) continue;
diff --git a/libcutils/hashmap.cpp b/libcutils/hashmap.cpp
index 10e3b25..57d6006 100644
--- a/libcutils/hashmap.cpp
+++ b/libcutils/hashmap.cpp
@@ -18,7 +18,7 @@
 
 #include <assert.h>
 #include <errno.h>
-#include <cutils/threads.h>
+#include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
@@ -36,7 +36,7 @@
     size_t bucketCount;
     int (*hash)(void* key);
     bool (*equals)(void* keyA, void* keyB);
-    mutex_t lock; 
+    pthread_mutex_t lock;
     size_t size;
 };
 
@@ -44,18 +44,18 @@
         int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
     assert(hash != NULL);
     assert(equals != NULL);
-    
+
     Hashmap* map = static_cast<Hashmap*>(malloc(sizeof(Hashmap)));
     if (map == NULL) {
         return NULL;
     }
-    
+
     // 0.75 load factor.
     size_t minimumBucketCount = initialCapacity * 4 / 3;
     map->bucketCount = 1;
     while (map->bucketCount <= minimumBucketCount) {
         // Bucket count must be power of 2.
-        map->bucketCount <<= 1; 
+        map->bucketCount <<= 1;
     }
 
     map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));
@@ -63,14 +63,14 @@
         free(map);
         return NULL;
     }
-    
+
     map->size = 0;
 
     map->hash = hash;
     map->equals = equals;
-    
-    mutex_init(&map->lock);
-    
+
+    pthread_mutex_init(&map->lock, nullptr);
+
     return map;
 }
 
@@ -89,12 +89,8 @@
     h ^= (((unsigned int) h) >> 14);
     h += (h << 4);
     h ^= (((unsigned int) h) >> 10);
-       
-    return h;
-}
 
-size_t hashmapSize(Hashmap* map) {
-    return map->size;
+    return h;
 }
 
 static inline size_t calculateIndex(size_t bucketCount, int hash) {
@@ -111,7 +107,7 @@
             // Abort expansion.
             return;
         }
-        
+
         // Move over existing entries.
         size_t i;
         for (i = 0; i < map->bucketCount; i++) {
@@ -133,11 +129,11 @@
 }
 
 void hashmapLock(Hashmap* map) {
-    mutex_lock(&map->lock);
+    pthread_mutex_lock(&map->lock);
 }
 
 void hashmapUnlock(Hashmap* map) {
-    mutex_unlock(&map->lock);
+    pthread_mutex_unlock(&map->lock);
 }
 
 void hashmapFree(Hashmap* map) {
@@ -151,7 +147,7 @@
         }
     }
     free(map->buckets);
-    mutex_destroy(&map->lock);
+    pthread_mutex_destroy(&map->lock);
     free(map);
 }
 
@@ -240,54 +236,6 @@
     return NULL;
 }
 
-bool hashmapContainsKey(Hashmap* map, void* key) {
-    int hash = hashKey(map, key);
-    size_t index = calculateIndex(map->bucketCount, hash);
-
-    Entry* entry = map->buckets[index];
-    while (entry != NULL) {
-        if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
-            return true;
-        }
-        entry = entry->next;
-    }
-
-    return false;
-}
-
-void* hashmapMemoize(Hashmap* map, void* key, 
-        void* (*initialValue)(void* key, void* context), void* context) {
-    int hash = hashKey(map, key);
-    size_t index = calculateIndex(map->bucketCount, hash);
-
-    Entry** p = &(map->buckets[index]);
-    while (true) {
-        Entry* current = *p;
-
-        // Add a new entry.
-        if (current == NULL) {
-            *p = createEntry(key, hash, NULL);
-            if (*p == NULL) {
-                errno = ENOMEM;
-                return NULL;
-            }
-            void* value = initialValue(key, context);
-            (*p)->value = value;
-            map->size++;
-            expandIfNecessary(map);
-            return value;
-        }
-
-        // Return existing value.
-        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
-            return current->value;
-        }
-
-        // Move to next entry.
-        p = &current->next;
-    }
-}
-
 void* hashmapRemove(Hashmap* map, void* key) {
     int hash = hashKey(map, key);
     size_t index = calculateIndex(map->bucketCount, hash);
@@ -310,9 +258,8 @@
     return NULL;
 }
 
-void hashmapForEach(Hashmap* map, 
-        bool (*callback)(void* key, void* value, void* context),
-        void* context) {
+void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
+                    void* context) {
     size_t i;
     for (i = 0; i < map->bucketCount; i++) {
         Entry* entry = map->buckets[i];
@@ -325,34 +272,3 @@
         }
     }
 }
-
-size_t hashmapCurrentCapacity(Hashmap* map) {
-    size_t bucketCount = map->bucketCount;
-    return bucketCount * 3 / 4;
-}
-
-size_t hashmapCountCollisions(Hashmap* map) {
-    size_t collisions = 0;
-    size_t i;
-    for (i = 0; i < map->bucketCount; i++) {
-        Entry* entry = map->buckets[i];
-        while (entry != NULL) {
-            if (entry->next != NULL) {
-                collisions++;
-            }
-            entry = entry->next;
-        }
-    }
-    return collisions;
-}
-
-int hashmapIntHash(void* key) {
-    // Return the key value itself.
-    return *((int*) key);
-}
-
-bool hashmapIntEquals(void* keyA, void* keyB) {
-    int a = *((int*) keyA);
-    int b = *((int*) keyB);
-    return a == b;
-}
diff --git a/libcutils/include/cutils/hashmap.h b/libcutils/include/cutils/hashmap.h
index 5cb344c..9cfd669 100644
--- a/libcutils/include/cutils/hashmap.h
+++ b/libcutils/include/cutils/hashmap.h
@@ -16,6 +16,9 @@
 
 /**
  * Hash map.
+ *
+ * Use std::map or std::unordered_map instead.
+ * https://en.cppreference.com/w/cpp/container
  */
 
 #ifndef __HASHMAP_H
@@ -68,38 +71,17 @@
 void* hashmapGet(Hashmap* map, void* key);
 
 /**
- * Returns true if the map contains an entry for the given key.
- */
-bool hashmapContainsKey(Hashmap* map, void* key);
-
-/**
- * Gets the value for a key. If a value is not found, this function gets a 
- * value and creates an entry using the given callback.
- *
- * If memory allocation fails, the callback is not called, this function
- * returns NULL, and errno is set to ENOMEM.
- */
-void* hashmapMemoize(Hashmap* map, void* key, 
-        void* (*initialValue)(void* key, void* context), void* context);
-
-/**
  * Removes an entry from the map. Returns the removed value or NULL if no
  * entry was present.
  */
 void* hashmapRemove(Hashmap* map, void* key);
 
 /**
- * Gets the number of entries in this map.
- */
-size_t hashmapSize(Hashmap* map);
-
-/**
  * Invokes the given callback on each entry in the map. Stops iterating if
  * the callback returns false.
  */
-void hashmapForEach(Hashmap* map, 
-        bool (*callback)(void* key, void* value, void* context),
-        void* context);
+void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
+                    void* context);
 
 /**
  * Concurrency support.
@@ -115,36 +97,8 @@
  */
 void hashmapUnlock(Hashmap* map);
 
-/**
- * Key utilities.
- */
-
-/**
- * Hashes int keys. 'key' is a pointer to int.
- */
-int hashmapIntHash(void* key);
-
-/**
- * Compares two int keys for equality.
- */
-bool hashmapIntEquals(void* keyA, void* keyB);
-
-/**
- * For debugging.
- */
-
-/**
- * Gets current capacity.
- */
-size_t hashmapCurrentCapacity(Hashmap* map);
-
-/**
- * Counts the number of entry collisions.
- */
-size_t hashmapCountCollisions(Hashmap* map);
-
 #ifdef __cplusplus
 }
 #endif
 
-#endif /* __HASHMAP_H */ 
+#endif /* __HASHMAP_H */
diff --git a/libcutils/include/cutils/open_memstream.h b/libcutils/include/cutils/open_memstream.h
deleted file mode 100644
index c1a81eb..0000000
--- a/libcutils/include/cutils/open_memstream.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2010 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 __CUTILS_OPEN_MEMSTREAM_H__
-#define __CUTILS_OPEN_MEMSTREAM_H__
-
-#include <stdio.h>
-
-#if defined(__APPLE__)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-FILE* open_memstream(char** bufp, size_t* sizep);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __APPLE__ */
-
-#endif /*__CUTILS_OPEN_MEMSTREAM_H__*/
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
index 5727494..ba4846e 100644
--- a/libcutils/include/cutils/threads.h
+++ b/libcutils/include/cutils/threads.h
@@ -29,16 +29,16 @@
 extern "C" {
 #endif
 
-/***********************************************************************/
-/***********************************************************************/
-/*****                                                             *****/
-/*****         local thread storage                                *****/
-/*****                                                             *****/
-/***********************************************************************/
-/***********************************************************************/
+//
+// Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
+//
 
 extern pid_t gettid();
 
+//
+// Deprecated: use `_Thread_local` in C or `thread_local` in C++.
+//
+
 #if !defined(_WIN32)
 
 typedef struct {
@@ -70,77 +70,6 @@
                                void*                    value,
                                thread_store_destruct_t  destroy);
 
-/***********************************************************************/
-/***********************************************************************/
-/*****                                                             *****/
-/*****         mutexes                                             *****/
-/*****                                                             *****/
-/***********************************************************************/
-/***********************************************************************/
-
-#if !defined(_WIN32)
-
-typedef pthread_mutex_t   mutex_t;
-
-#define  MUTEX_INITIALIZER  PTHREAD_MUTEX_INITIALIZER
-
-static __inline__ void  mutex_lock(mutex_t*  lock)
-{
-    pthread_mutex_lock(lock);
-}
-static __inline__ void  mutex_unlock(mutex_t*  lock)
-{
-    pthread_mutex_unlock(lock);
-}
-static __inline__ int  mutex_init(mutex_t*  lock)
-{
-    return pthread_mutex_init(lock, NULL);
-}
-static __inline__ void mutex_destroy(mutex_t*  lock)
-{
-    pthread_mutex_destroy(lock);
-}
-
-#else // !defined(_WIN32)
-
-typedef struct {
-    int                init;
-    CRITICAL_SECTION   lock[1];
-} mutex_t;
-
-#define  MUTEX_INITIALIZER  { 0, {{ NULL, 0, 0, NULL, NULL, 0 }} }
-
-static __inline__ void  mutex_lock(mutex_t*  lock)
-{
-    if (!lock->init) {
-        lock->init = 1;
-        InitializeCriticalSection( lock->lock );
-        lock->init = 2;
-    } else while (lock->init != 2)
-        Sleep(10);
-
-    EnterCriticalSection(lock->lock);
-}
-
-static __inline__ void  mutex_unlock(mutex_t*  lock)
-{
-    LeaveCriticalSection(lock->lock);
-}
-static __inline__ int  mutex_init(mutex_t*  lock)
-{
-    InitializeCriticalSection(lock->lock);
-    lock->init = 2;
-    return 0;
-}
-static __inline__ void  mutex_destroy(mutex_t*  lock)
-{
-    if (lock->init) {
-        lock->init = 0;
-        DeleteCriticalSection(lock->lock);
-    }
-}
-#endif // !defined(_WIN32)
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/libcutils/include_vndk/cutils/open_memstream.h b/libcutils/include_vndk/cutils/open_memstream.h
deleted file mode 120000
index c894084..0000000
--- a/libcutils/include_vndk/cutils/open_memstream.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/cutils/open_memstream.h
\ No newline at end of file
diff --git a/libcutils/open_memstream.c b/libcutils/open_memstream.c
deleted file mode 100644
index 9183266..0000000
--- a/libcutils/open_memstream.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#if defined(__APPLE__)
-
-/*
- * Implementation of the POSIX open_memstream() function, which Linux has
- * but BSD lacks.
- *
- * Summary:
- * - Works like a file-backed FILE* opened with fopen(name, "w"), but the
- *   backing is a chunk of memory rather than a file.
- * - The buffer expands as you write more data.  Seeking past the end
- *   of the file and then writing to it zero-fills the gap.
- * - The values at "*bufp" and "*sizep" should be considered read-only,
- *   and are only valid immediately after an fflush() or fclose().
- * - A '\0' is maintained just past the end of the file. This is not included
- *   in "*sizep".  (The behavior w.r.t. fseek() is not clearly defined.
- *   The spec says the null byte is written when a write() advances EOF,
- *   but it looks like glibc ensures the null byte is always found at EOF,
- *   even if you just seeked backwards.  The example on the opengroup.org
- *   page suggests that this is the expected behavior.  The null must be
- *   present after a no-op fflush(), which we can't see, so we have to save
- *   and restore it.  Annoying, but allows file truncation.)
- * - After fclose(), the caller must eventually free(*bufp).
- *
- * This is built out of funopen(), which BSD has but Linux lacks.  There is
- * no flush() operator, so we need to keep the user pointers up to date
- * after each operation.
- *
- * I don't think Windows has any of the above, but we don't need to use
- * them there, so we just supply a stub.
- */
-#include <cutils/open_memstream.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-#if 0
-# define DBUG(x) printf x
-#else
-# define DBUG(x) ((void)0)
-#endif
-
-/*
- * Definition of a seekable, write-only memory stream.
- */
-typedef struct {
-    char**      bufp;       /* pointer to buffer pointer */
-    size_t*     sizep;      /* pointer to eof */
-
-    size_t      allocSize;  /* size of buffer */
-    size_t      eof;        /* furthest point we've written to */
-    size_t      offset;     /* current write offset */
-    char        saved;      /* required by NUL handling */
-} MemStream;
-
-#define kInitialSize    1024
-
-/*
- * Ensure that we have enough storage to write "size" bytes at the
- * current offset.  We also have to take into account the extra '\0'
- * that we maintain just past EOF.
- *
- * Returns 0 on success.
- */
-static int ensureCapacity(MemStream* stream, int writeSize)
-{
-    DBUG(("+++ ensureCap off=%d size=%d\n", stream->offset, writeSize));
-
-    size_t neededSize = stream->offset + writeSize + 1;
-    if (neededSize <= stream->allocSize)
-        return 0;
-
-    size_t newSize;
-
-    if (stream->allocSize == 0) {
-        newSize = kInitialSize;
-    } else {
-        newSize = stream->allocSize;
-        newSize += newSize / 2;             /* expand by 3/2 */
-    }
-
-    if (newSize < neededSize)
-        newSize = neededSize;
-    DBUG(("+++ realloc %p->%p to size=%d\n",
-        stream->bufp, *stream->bufp, newSize));
-    char* newBuf = (char*) realloc(*stream->bufp, newSize);
-    if (newBuf == NULL)
-        return -1;
-
-    *stream->bufp = newBuf;
-    stream->allocSize = newSize;
-    return 0;
-}
-
-/*
- * Write data to a memstream, expanding the buffer if necessary.
- *
- * If we previously seeked beyond EOF, zero-fill the gap.
- *
- * Returns the number of bytes written.
- */
-static int write_memstream(void* cookie, const char* buf, int size)
-{
-    MemStream* stream = (MemStream*) cookie;
-
-    if (ensureCapacity(stream, size) < 0)
-        return -1;
-
-    /* seeked past EOF earlier? */
-    if (stream->eof < stream->offset) {
-        DBUG(("+++ zero-fill gap from %d to %d\n",
-            stream->eof, stream->offset-1));
-        memset(*stream->bufp + stream->eof, '\0',
-            stream->offset - stream->eof);
-    }
-
-    /* copy data, advance write pointer */
-    memcpy(*stream->bufp + stream->offset, buf, size);
-    stream->offset += size;
-
-    if (stream->offset > stream->eof) {
-        /* EOF has advanced, update it and append null byte */
-        DBUG(("+++ EOF advanced to %d, appending nul\n", stream->offset));
-        assert(stream->offset < stream->allocSize);
-        stream->eof = stream->offset;
-    } else {
-        /* within previously-written area; save char we're about to stomp */
-        DBUG(("+++ within written area, saving '%c' at %d\n",
-            *(*stream->bufp + stream->offset), stream->offset));
-        stream->saved = *(*stream->bufp + stream->offset);
-    }
-    *(*stream->bufp + stream->offset) = '\0';
-    *stream->sizep = stream->offset;
-
-    return size;
-}
-
-/*
- * Seek within a memstream.
- *
- * Returns the new offset, or -1 on failure.
- */
-static fpos_t seek_memstream(void* cookie, fpos_t offset, int whence)
-{
-    MemStream* stream = (MemStream*) cookie;
-    off_t newPosn = (off_t) offset;
-
-    if (whence == SEEK_CUR) {
-        newPosn += stream->offset;
-    } else if (whence == SEEK_END) {
-        newPosn += stream->eof;
-    }
-
-    if (newPosn < 0 || ((fpos_t)((size_t) newPosn)) != newPosn) {
-        /* bad offset - negative or huge */
-        DBUG(("+++ bogus seek offset %ld\n", (long) newPosn));
-        errno = EINVAL;
-        return (fpos_t) -1;
-    }
-
-    if (stream->offset < stream->eof) {
-        /*
-         * We were pointing to an area we'd already written to, which means
-         * we stomped on a character and must now restore it.
-         */
-        DBUG(("+++ restoring char '%c' at %d\n",
-            stream->saved, stream->offset));
-        *(*stream->bufp + stream->offset) = stream->saved;
-    }
-
-    stream->offset = (size_t) newPosn;
-
-    if (stream->offset < stream->eof) {
-        /*
-         * We're seeked backward into the stream.  Preserve the character
-         * at EOF and stomp it with a NUL.
-         */
-        stream->saved = *(*stream->bufp + stream->offset);
-        *(*stream->bufp + stream->offset) = '\0';
-        *stream->sizep = stream->offset;
-    } else {
-        /*
-         * We're positioned at, or possibly beyond, the EOF.  We want to
-         * publish the current EOF, not the current position.
-         */
-        *stream->sizep = stream->eof;
-    }
-
-    return newPosn;
-}
-
-/*
- * Close the memstream.  We free everything but the data buffer.
- */
-static int close_memstream(void* cookie)
-{
-    free(cookie);
-    return 0;
-}
-
-/*
- * Prepare a memstream.
- */
-FILE* open_memstream(char** bufp, size_t* sizep)
-{
-    FILE* fp;
-    MemStream* stream;
-
-    if (bufp == NULL || sizep == NULL) {
-        errno = EINVAL;
-        return NULL;
-    }
-
-    stream = (MemStream*) calloc(1, sizeof(MemStream));
-    if (stream == NULL)
-        return NULL;
-
-    fp = funopen(stream,
-        NULL, write_memstream, seek_memstream, close_memstream);
-    if (fp == NULL) {
-        free(stream);
-        return NULL;
-    }
-
-    *sizep = 0;
-    *bufp = NULL;
-    stream->bufp = bufp;
-    stream->sizep = sizep;
-
-    return fp;
-}
-
-
-
-
-#if 0
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/*
- * Simple regression test.
- *
- * To test on desktop Linux with valgrind, it's possible to make a simple
- * change to open_memstream() to use fopencookie instead:
- *
- *  cookie_io_functions_t iofuncs =
- *      { NULL, write_memstream, seek_memstream, close_memstream };
- *  fp = fopencookie(stream, "w", iofuncs);
- *
- * (Some tweaks to seek_memstream are also required, as that takes a
- * pointer to an offset rather than an offset, and returns 0 or -1.)
- */
-int testMemStream(void)
-{
-    FILE *stream;
-    char *buf;
-    size_t len;
-    off_t eob;
-
-    printf("Test1\n");
-
-    /* std example */
-    stream = open_memstream(&buf, &len);
-    fprintf(stream, "hello my world");
-    fflush(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    eob = ftello(stream);
-    fseeko(stream, 0, SEEK_SET);
-    fprintf(stream, "good-bye");
-    fseeko(stream, eob, SEEK_SET);
-    fclose(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    free(buf);
-
-    printf("Test2\n");
-
-    /* std example without final seek-to-end */
-    stream = open_memstream(&buf, &len);
-    fprintf(stream, "hello my world");
-    fflush(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    eob = ftello(stream);
-    fseeko(stream, 0, SEEK_SET);
-    fprintf(stream, "good-bye");
-    //fseeko(stream, eob, SEEK_SET);
-    fclose(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    free(buf);
-
-    printf("Test3\n");
-
-    /* fancy example; should expand buffer with writes */
-    static const int kCmpLen = 1024 + 128;
-    char* cmp = malloc(kCmpLen);
-    memset(cmp, 0, 1024);
-    memset(cmp+1024, 0xff, kCmpLen-1024);
-    sprintf(cmp, "This-is-a-tes1234");
-    sprintf(cmp + 1022, "abcdef");
-
-    stream = open_memstream (&buf, &len);
-    setvbuf(stream, NULL, _IONBF, 0);   /* note: crashes in glibc with this */
-    fprintf(stream, "This-is-a-test");
-    fseek(stream, -1, SEEK_CUR);    /* broken in glibc; can use {13,SEEK_SET} */
-    fprintf(stream, "1234");
-    fseek(stream, 1022, SEEK_SET);
-    fputc('a', stream);
-    fputc('b', stream);
-    fputc('c', stream);
-    fputc('d', stream);
-    fputc('e', stream);
-    fputc('f', stream);
-    fflush(stream);
-
-    if (memcmp(buf, cmp, len+1) != 0) {
-        printf("mismatch\n");
-    } else {
-        printf("match\n");
-    }
-
-    printf("Test4\n");
-    stream = open_memstream (&buf, &len);
-    fseek(stream, 5000, SEEK_SET);
-    fseek(stream, 4096, SEEK_SET);
-    fseek(stream, -1, SEEK_SET);        /* should have no effect */
-    fputc('x', stream);
-    if (ftell(stream) == 4097)
-        printf("good\n");
-    else
-        printf("BAD: offset is %ld\n", ftell(stream));
-
-    printf("DONE\n");
-
-    return 0;
-}
-
-/* expected output:
-Test1
-buf=hello my world, len=14
-buf=good-bye world, len=14
-Test2
-buf=hello my world, len=14
-buf=good-bye, len=8
-Test3
-match
-Test4
-good
-DONE
-*/
-
-#endif
-
-#endif /* __APPLE__ */
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 2849aa8..0cb8a4d 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -32,10 +32,6 @@
 
 #include "android_get_control_env.h"
 
-#ifndef TEMP_FAILURE_RETRY
-#define TEMP_FAILURE_RETRY(exp) (exp) // KISS implementation
-#endif
-
 #if defined(__ANDROID__)
 /* For the socket trust (credentials) check */
 #include <private/android_filesystem_config.h>
@@ -102,15 +98,15 @@
     // Compare to UNIX domain socket name, must match!
     struct sockaddr_un addr;
     socklen_t addrlen = sizeof(addr);
-    int ret = TEMP_FAILURE_RETRY(getsockname(fd, (struct sockaddr *)&addr, &addrlen));
+    int ret = getsockname(fd, (struct sockaddr*)&addr, &addrlen);
     if (ret < 0) return -1;
-    char *path = NULL;
-    if (asprintf(&path, ANDROID_SOCKET_DIR "/%s", name) < 0) return -1;
-    if (!path) return -1;
-    int cmp = strcmp(addr.sun_path, path);
-    free(path);
-    if (cmp != 0) return -1;
 
-    // It is what we think it is
-    return fd;
+    constexpr char prefix[] = ANDROID_SOCKET_DIR "/";
+    constexpr size_t prefix_size = sizeof(prefix) - sizeof('\0');
+    if ((strncmp(addr.sun_path, prefix, prefix_size) == 0) &&
+        (strcmp(addr.sun_path + prefix_size, name) == 0)) {
+        // It is what we think it is
+        return fd;
+    }
+    return -1;
 }
diff --git a/libcutils/str_parms.cpp b/libcutils/str_parms.cpp
index f5a52a7..d818c51 100644
--- a/libcutils/str_parms.cpp
+++ b/libcutils/str_parms.cpp
@@ -354,12 +354,8 @@
 char *str_parms_to_str(struct str_parms *str_parms)
 {
     char *str = NULL;
-
-    if (hashmapSize(str_parms->map) > 0)
-        hashmapForEach(str_parms->map, combine_strings, &str);
-    else
-        str = strdup("");
-    return str;
+    hashmapForEach(str_parms->map, combine_strings, &str);
+    return (str != NULL) ? str : strdup("");
 }
 
 static bool dump_entry(void* key, void* value, void* /*context*/) {
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index f3593ff..b388e95 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -2,6 +2,7 @@
     name: "libkeyutils",
     cflags: ["-Werror"],
     defaults: ["linux_bionic_supported"],
+    recovery_available: true,
     export_include_dirs: ["include/"],
     local_include_dirs: ["include/"],
     srcs: ["keyutils.cpp"],
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 1bd796a..4a165a0 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -83,8 +83,8 @@
         },
         android_arm: {
             // TODO: This is to work around b/24465209. Remove after root cause is fixed
+            pack_relocations: false,
             ldflags: ["-Wl,--hash-style=both"],
-            use_clang_lld: false,
         },
         windows: {
             srcs: ["uio.c"],
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 52cbe8b..ee9220d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -115,16 +115,8 @@
  */
 int __android_log_print(int prio, const char* tag, const char* fmt, ...)
 #if defined(__GNUC__)
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-    __attribute__((__format__(gnu_printf, 3, 4)))
-#else
     __attribute__((__format__(printf, 3, 4)))
 #endif
-#else
-    __attribute__((__format__(printf, 3, 4)))
-#endif
-#endif
     ;
 
 /**
@@ -133,16 +125,8 @@
  */
 int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap)
 #if defined(__GNUC__)
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-    __attribute__((__format__(gnu_printf, 3, 0)))
-#else
     __attribute__((__format__(printf, 3, 0)))
 #endif
-#else
-    __attribute__((__format__(printf, 3, 0)))
-#endif
-#endif
     ;
 
 /**
@@ -164,16 +148,8 @@
                           ...)
 #if defined(__GNUC__)
     __attribute__((__noreturn__))
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-    __attribute__((__format__(gnu_printf, 3, 4)))
-#else
     __attribute__((__format__(printf, 3, 4)))
 #endif
-#else
-    __attribute__((__format__(printf, 3, 4)))
-#endif
-#endif
     ;
 
 #ifndef log_id_t_defined
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index 21fc7cc..f1ff31a 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -40,6 +40,17 @@
 #endif
 #endif
 
+/*
+ * Use __VA_ARGS__ if running a static analyzer,
+ * to avoid warnings of unused variables in __VA_ARGS__.
+ */
+
+#ifdef __clang_analyzer__
+#define __FAKE_USE_VA_ARGS(...) ((void)(__VA_ARGS__))
+#else
+#define __FAKE_USE_VA_ARGS(...) ((void)(0))
+#endif
+
 /* --------------------------------------------------------------------- */
 
 /*
@@ -112,7 +123,7 @@
 #define LOG_ALWAYS_FATAL_IF(cond, ...)                              \
   ((__predict_false(cond))                                          \
        ? ((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__)) \
-       : (void)0)
+       : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 #ifndef LOG_ALWAYS_FATAL
@@ -128,10 +139,10 @@
 #if LOG_NDEBUG
 
 #ifndef LOG_FATAL_IF
-#define LOG_FATAL_IF(cond, ...) ((void)0)
+#define LOG_FATAL_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
 #endif
 #ifndef LOG_FATAL
-#define LOG_FATAL(...) ((void)0)
+#define LOG_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
 #endif
 
 #else
@@ -175,11 +186,12 @@
 #ifndef ALOGV
 #define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
 #if LOG_NDEBUG
-#define ALOGV(...)          \
-  do {                      \
-    if (false) {            \
-      __ALOGV(__VA_ARGS__); \
-    }                       \
+#define ALOGV(...)                   \
+  do {                               \
+    __FAKE_USE_VA_ARGS(__VA_ARGS__); \
+    if (false) {                     \
+      __ALOGV(__VA_ARGS__);          \
+    }                                \
   } while (false)
 #else
 #define ALOGV(...) __ALOGV(__VA_ARGS__)
@@ -188,11 +200,11 @@
 
 #ifndef ALOGV_IF
 #if LOG_NDEBUG
-#define ALOGV_IF(cond, ...) ((void)0)
+#define ALOGV_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
 #else
 #define ALOGV_IF(cond, ...)                                                  \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 #endif
 
@@ -206,7 +218,7 @@
 #ifndef ALOGD_IF
 #define ALOGD_IF(cond, ...)                                                \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 /*
@@ -219,7 +231,7 @@
 #ifndef ALOGI_IF
 #define ALOGI_IF(cond, ...)                                               \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 /*
@@ -232,7 +244,7 @@
 #ifndef ALOGW_IF
 #define ALOGW_IF(cond, ...)                                               \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 /*
@@ -245,7 +257,7 @@
 #ifndef ALOGE_IF
 #define ALOGE_IF(cond, ...)                                                \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 /* --------------------------------------------------------------------- */
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index d118563..93b9d4e 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -184,7 +184,7 @@
       hdr_size = sizeof(entry_v1);
     }
     if ((hdr_size < sizeof(entry_v1)) || (hdr_size > sizeof(entry))) {
-      return NULL;
+      return nullptr;
     }
     return reinterpret_cast<char*>(buf) + hdr_size;
   }
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index 965de37..b927b46 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -173,7 +173,7 @@
 #if defined(_USING_LIBCXX)
   operator std::string() {
     if (ret) return std::string("");
-    const char* cp = NULL;
+    const char* cp = nullptr;
     ssize_t len = android_log_write_list_buffer(ctx, &cp);
     if (len < 0) ret = len;
     if (!cp || (len <= 0)) return std::string("");
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 329ba85..6d78ed6 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -21,6 +21,7 @@
 
 TEST(libc, __pstore_append) {
 #ifdef __ANDROID__
+#ifndef NO_PSTORE
   FILE* fp;
   ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
   static const char message[] = "libc.__pstore_append\n";
@@ -43,6 +44,9 @@
             "Reboot, ensure string libc.__pstore_append is in "
             "/sys/fs/pstore/pmsg-ramoops-0\n");
   }
+#else  /* NO_PSTORE */
+  GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 597a6bb..a8a9a12 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -3180,113 +3180,6 @@
 }
 #endif  // USING_LOGGER_DEFAULT
 
-#ifdef USING_LOGGER_DEFAULT  // Do not retest event mapping functionality
-#ifdef __ANDROID__
-// must be: '<needle:> 0 kB'
-static bool isZero(const std::string& content, std::string::size_type pos,
-                   const char* needle) {
-  std::string::size_type offset = content.find(needle, pos);
-  return (offset != std::string::npos) &&
-         ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
-          std::string::npos) &&
-         (content.find_first_not_of('0', offset) != offset);
-}
-
-// must not be: '<needle:> 0 kB'
-static bool isNotZero(const std::string& content, std::string::size_type pos,
-                      const char* needle) {
-  std::string::size_type offset = content.find(needle, pos);
-  return (offset != std::string::npos) &&
-         ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
-          std::string::npos) &&
-         (content.find_first_not_of("123456789", offset) != offset);
-}
-
-static void event_log_tags_test_smap(pid_t pid) {
-  std::string filename = android::base::StringPrintf("/proc/%d/smaps", pid);
-
-  std::string content;
-  if (!android::base::ReadFileToString(filename, &content)) return;
-
-  bool shared_ok = false;
-  bool private_ok = false;
-  bool anonymous_ok = false;
-  bool pass_ok = false;
-
-  static const char event_log_tags[] = "event-log-tags";
-  std::string::size_type pos = 0;
-  while ((pos = content.find(event_log_tags, pos)) != std::string::npos) {
-    pos += strlen(event_log_tags);
-
-    // must not be: 'Shared_Clean: 0 kB'
-    bool ok =
-        isNotZero(content, pos, "Shared_Clean:") ||
-        // If not /etc/event-log-tags, thus r/w, then half points
-        // back for not 'Shared_Dirty: 0 kB'
-        ((content.substr(pos - 5 - strlen(event_log_tags), 5) != "/etc/") &&
-         isNotZero(content, pos, "Shared_Dirty:"));
-    if (ok && !pass_ok) {
-      shared_ok = true;
-    } else if (!ok) {
-      shared_ok = false;
-    }
-
-    // must be: 'Private_Dirty: 0 kB' and 'Private_Clean: 0 kB'
-    ok = isZero(content, pos, "Private_Dirty:") ||
-         isZero(content, pos, "Private_Clean:");
-    if (ok && !pass_ok) {
-      private_ok = true;
-    } else if (!ok) {
-      private_ok = false;
-    }
-
-    // must be: 'Anonymous: 0 kB'
-    ok = isZero(content, pos, "Anonymous:");
-    if (ok && !pass_ok) {
-      anonymous_ok = true;
-    } else if (!ok) {
-      anonymous_ok = false;
-    }
-
-    pass_ok = true;
-  }
-  content = "";
-
-  if (!pass_ok) return;
-  if (shared_ok && anonymous_ok && private_ok) return;
-
-  filename = android::base::StringPrintf("/proc/%d/comm", pid);
-  android::base::ReadFileToString(filename, &content);
-  content = android::base::StringPrintf(
-      "%d:%s", pid, content.substr(0, content.find('\n')).c_str());
-
-  EXPECT_TRUE(IsOk(shared_ok, content));
-  EXPECT_TRUE(IsOk(private_ok, content));
-  EXPECT_TRUE(IsOk(anonymous_ok, content));
-}
-#endif  // __ANDROID__
-
-TEST(liblog, event_log_tags) {
-#ifdef __ANDROID__
-  std::unique_ptr<DIR, int (*)(DIR*)> proc_dir(opendir("/proc"), closedir);
-  ASSERT_FALSE(!proc_dir);
-
-  dirent* e;
-  while ((e = readdir(proc_dir.get()))) {
-    if (e->d_type != DT_DIR) continue;
-    if (!isdigit(e->d_name[0])) continue;
-    long long id = atoll(e->d_name);
-    if (id <= 0) continue;
-    pid_t pid = id;
-    if (id != pid) continue;
-    event_log_tags_test_smap(pid);
-  }
-#else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-#endif  // USING_LOGGER_DEFAULT
-
 #ifdef USING_LOGGER_DEFAULT  // Do not retest ratelimit
 TEST(liblog, __android_log_ratelimit) {
   time_t state = 0;
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index 444a5ac..443c3ea 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -27,6 +27,7 @@
 // Test the APIs in this standalone include file
 #include <log/log_read.h>
 // Do not use anything in log/log_time.h despite side effects of the above.
+#include <private/android_logger.h>
 
 TEST(liblog, __android_log_write__android_logger_list_read) {
 #ifdef __ANDROID__
@@ -105,7 +106,10 @@
       // framework (b/68266385).
       EXPECT_LE(  // boolean 1 or 0 depending on expected content or empty
           !!((strcmp("crash", name) != 0) &&
-             ((strcmp("kernel", name) != 0) || __android_log_is_debuggable()) &&
+             ((strcmp("kernel", name) != 0) ||
+              __android_logger_property_get_bool(
+                  "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG |
+                                        BOOL_DEFAULT_FLAG_SVELTE)) &&
              (strcmp("stats", name) != 0)),
           android_logger_get_log_readable_size(logger));
     } else {
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
index 6d9141a..9cc0c9b 100644
--- a/libmemunreachable/README.md
+++ b/libmemunreachable/README.md
@@ -44,7 +44,7 @@
 
 ### C++ interface ###
 
-####`bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)`####
+#### `bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)` ####
 Updates an `UnreachableMemoryInfo` object with information on leaks, including details on up to `limit` leaks.  Returns true if leak detection succeeded.
 
 #### `std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100)` ####
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
index 4fbf729..933d65a 100644
--- a/libmemunreachable/tests/ThreadCapture_test.cpp
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -32,6 +32,8 @@
 #include "ScopedDisableMalloc.h"
 #include "ScopedPipe.h"
 
+#include <android-base/threads.h>
+
 using namespace std::chrono_literals;
 
 namespace android {
@@ -260,7 +262,7 @@
 
       ThreadCapture thread_capture(ret, heap);
       thread_capture.InjectTestFunc([&](pid_t tid) {
-        syscall(SYS_tgkill, ret, tid, SIGKILL);
+        tgkill(ret, tid, SIGKILL);
         usleep(10000);
       });
       auto list_tids = allocator::vector<pid_t>(heap);
@@ -319,7 +321,7 @@
 
           ThreadCapture thread_capture(child, heap);
           thread_capture.InjectTestFunc([&](pid_t tid) {
-            syscall(SYS_tgkill, child, tid, sig);
+            tgkill(child, tid, sig);
             usleep(10000);
           });
           auto list_tids = allocator::vector<pid_t>(heap);
diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS
index f2cc942..5c28a9f 100644
--- a/libnativeloader/OWNERS
+++ b/libnativeloader/OWNERS
@@ -1 +1,2 @@
 dimitry@google.com
+jiyong@google.com
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 3563fc1..19a1783 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -53,8 +53,21 @@
 #if defined(__ANDROID__)
 // Look up linker namespace by class_loader. Returns nullptr if
 // there is no namespace associated with the class_loader.
+// TODO(b/79940628): move users to FindNativeLoaderNamespaceByClassLoader and remove this function.
 __attribute__((visibility("default")))
 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+// That version works with native bridge namespaces, but requires use of OpenNativeLibrary.
+class NativeLoaderNamespace;
+__attribute__((visibility("default")))
+NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(
+    JNIEnv* env, jobject class_loader);
+// Load library.  Unlinke OpenNativeLibrary above couldn't create namespace on demand, but does
+// not require access to JNIEnv either.
+__attribute__((visibility("default")))
+void* OpenNativeLibrary(NativeLoaderNamespace* ns,
+                        const char* path,
+                        bool* needs_native_bridge,
+                        std::string* error_msg);
 #endif
 
 __attribute__((visibility("default")))
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 7fef106..67c1c10 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -29,6 +29,7 @@
 #include "nativebridge/native_bridge.h"
 
 #include <algorithm>
+#include <list>
 #include <memory>
 #include <mutex>
 #include <string>
@@ -150,15 +151,14 @@
  public:
   LibraryNamespaces() : initialized_(false) { }
 
-  bool Create(JNIEnv* env,
-              uint32_t target_sdk_version,
-              jobject class_loader,
-              bool is_shared,
-              bool is_for_vendor,
-              jstring java_library_path,
-              jstring java_permitted_path,
-              NativeLoaderNamespace* ns,
-              std::string* error_msg) {
+  NativeLoaderNamespace* Create(JNIEnv* env,
+                                uint32_t target_sdk_version,
+                                jobject class_loader,
+                                bool is_shared,
+                                bool is_for_vendor,
+                                jstring java_library_path,
+                                jstring java_permitted_path,
+                                std::string* error_msg) {
     std::string library_path; // empty string by default.
 
     if (java_library_path != nullptr) {
@@ -182,10 +182,10 @@
     }
 
     if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) {
-      return false;
+      return nullptr;
     }
 
-    bool found = FindNamespaceByClassLoader(env, class_loader, nullptr);
+    bool found = FindNamespaceByClassLoader(env, class_loader);
 
     LOG_ALWAYS_FATAL_IF(found,
                         "There is already a namespace associated with this classloader");
@@ -199,13 +199,12 @@
       namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
     }
 
-    NativeLoaderNamespace parent_ns;
-    bool found_parent_namespace = FindParentNamespaceByClassLoader(env, class_loader, &parent_ns);
+    NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
 
     bool is_native_bridge = false;
 
-    if (found_parent_namespace) {
-      is_native_bridge = !parent_ns.is_android_namespace();
+    if (parent_ns != nullptr) {
+      is_native_bridge = !parent_ns->is_android_namespace();
     } else if (!library_path.empty()) {
       is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
     }
@@ -251,15 +250,17 @@
 
     NativeLoaderNamespace native_loader_ns;
     if (!is_native_bridge) {
+      android_namespace_t* android_parent_ns =
+          parent_ns == nullptr ? nullptr : parent_ns->get_android_ns();
       android_namespace_t* ns = android_create_namespace(namespace_name,
                                                          nullptr,
                                                          library_path.c_str(),
                                                          namespace_type,
                                                          permitted_path.c_str(),
-                                                         parent_ns.get_android_ns());
+                                                         android_parent_ns);
       if (ns == nullptr) {
         *error_msg = dlerror();
-        return false;
+        return nullptr;
       }
 
       // Note that when vendor_ns is not configured this function will return nullptr
@@ -269,49 +270,50 @@
 
       if (!android_link_namespaces(ns, nullptr, system_exposed_libraries.c_str())) {
         *error_msg = dlerror();
-        return false;
+        return nullptr;
       }
 
       if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) {
         // vendor apks are allowed to use VNDK-SP libraries.
         if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) {
           *error_msg = dlerror();
-          return false;
+          return nullptr;
         }
       }
 
       if (!vendor_public_libraries_.empty()) {
         if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
           *error_msg = dlerror();
-          return false;
+          return nullptr;
         }
       }
 
       native_loader_ns = NativeLoaderNamespace(ns);
     } else {
+      native_bridge_namespace_t* native_bridge_parent_namespace =
+          parent_ns == nullptr ? nullptr : parent_ns->get_native_bridge_ns();
       native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name,
                                                                   nullptr,
                                                                   library_path.c_str(),
                                                                   namespace_type,
                                                                   permitted_path.c_str(),
-                                                                  parent_ns.get_native_bridge_ns());
-
+                                                                  native_bridge_parent_namespace);
       if (ns == nullptr) {
         *error_msg = NativeBridgeGetError();
-        return false;
+        return nullptr;
       }
 
       native_bridge_namespace_t* vendor_ns = NativeBridgeGetVendorNamespace();
 
       if (!NativeBridgeLinkNamespaces(ns, nullptr, system_exposed_libraries.c_str())) {
         *error_msg = NativeBridgeGetError();
-        return false;
+        return nullptr;
       }
 
       if (!vendor_public_libraries_.empty()) {
         if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
           *error_msg = NativeBridgeGetError();
-          return false;
+          return nullptr;
         }
       }
 
@@ -320,24 +322,19 @@
 
     namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
 
-    *ns = native_loader_ns;
-    return true;
+    return &(namespaces_.back().second);
   }
 
-  bool FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader, NativeLoaderNamespace* ns) {
+  NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
     auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
                 [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
                   return env->IsSameObject(value.first, class_loader);
                 });
     if (it != namespaces_.end()) {
-      if (ns != nullptr) {
-        *ns = it->second;
-      }
-
-      return true;
+      return &it->second;
     }
 
-    return false;
+    return nullptr;
   }
 
   void Initialize() {
@@ -557,24 +554,23 @@
     return env->CallObjectMethod(class_loader, get_parent);
   }
 
-  bool FindParentNamespaceByClassLoader(JNIEnv* env,
-                                        jobject class_loader,
-                                        NativeLoaderNamespace* ns) {
+  NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
     jobject parent_class_loader = GetParentClassLoader(env, class_loader);
 
     while (parent_class_loader != nullptr) {
-      if (FindNamespaceByClassLoader(env, parent_class_loader, ns)) {
-        return true;
+      NativeLoaderNamespace* ns;
+      if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
+        return ns;
       }
 
       parent_class_loader = GetParentClassLoader(env, parent_class_loader);
     }
 
-    return false;
+    return nullptr;
   }
 
   bool initialized_;
-  std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
+  std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
   std::string system_public_libraries_;
   std::string vendor_public_libraries_;
   std::string oem_public_libraries_;
@@ -614,7 +610,6 @@
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
 
   std::string error_msg;
-  NativeLoaderNamespace ns;
   bool success = g_namespaces->Create(env,
                                       target_sdk_version,
                                       class_loader,
@@ -622,8 +617,7 @@
                                       is_for_vendor,
                                       library_path,
                                       permitted_path,
-                                      &ns,
-                                      &error_msg);
+                                      &error_msg) != nullptr;
   if (!success) {
     return env->NewStringUTF(error_msg.c_str());
   }
@@ -649,43 +643,24 @@
   }
 
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  NativeLoaderNamespace ns;
+  NativeLoaderNamespace* ns;
 
-  if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
+  if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
     // This is the case where the classloader was not created by ApplicationLoaders
     // In this case we create an isolated not-shared namespace for it.
-    if (!g_namespaces->Create(env,
-                              target_sdk_version,
-                              class_loader,
-                              false /* is_shared */,
-                              false /* is_for_vendor */,
-                              library_path,
-                              nullptr,
-                              &ns,
-                              error_msg)) {
+    if ((ns = g_namespaces->Create(env,
+                                   target_sdk_version,
+                                   class_loader,
+                                   false /* is_shared */,
+                                   false /* is_for_vendor */,
+                                   library_path,
+                                   nullptr,
+                                   error_msg)) == nullptr) {
       return nullptr;
     }
   }
 
-  if (ns.is_android_namespace()) {
-    android_dlextinfo extinfo;
-    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
-    extinfo.library_namespace = ns.get_android_ns();
-
-    void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
-    if (handle == nullptr) {
-      *error_msg = dlerror();
-    }
-    *needs_native_bridge = false;
-    return handle;
-  } else {
-    void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns.get_native_bridge_ns());
-    if (handle == nullptr) {
-      *error_msg = NativeBridgeGetError();
-    }
-    *needs_native_bridge = true;
-    return handle;
-  }
+  return OpenNativeLibrary(ns, path, needs_native_bridge, error_msg);
 #else
   UNUSED(env, target_sdk_version, class_loader);
 
@@ -741,18 +716,45 @@
 }
 
 #if defined(__ANDROID__)
+void* OpenNativeLibrary(NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge,
+                        std::string* error_msg) {
+  if (ns->is_android_namespace()) {
+    android_dlextinfo extinfo;
+    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+    extinfo.library_namespace = ns->get_android_ns();
+
+    void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
+    if (handle == nullptr) {
+      *error_msg = dlerror();
+    }
+    *needs_native_bridge = false;
+    return handle;
+  } else {
+    void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns->get_native_bridge_ns());
+    if (handle == nullptr) {
+      *error_msg = NativeBridgeGetError();
+    }
+    *needs_native_bridge = true;
+    return handle;
+  }
+}
+
 // native_bridge_namespaces are not supported for callers of this function.
 // This function will return nullptr in the case when application is running
 // on native bridge.
 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  NativeLoaderNamespace ns;
-  if (g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
-    return ns.is_android_namespace() ? ns.get_android_ns() : nullptr;
+  NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+  if (ns != nullptr) {
+    return ns->is_android_namespace() ? ns->get_android_ns() : nullptr;
   }
 
   return nullptr;
 }
+NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+  return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+}
 #endif
 
 }; //  android namespace
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 6dfa697..1cebb5d 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -39,12 +39,14 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <private/android_filesystem_config.h>
 
 #include <processgroup/processgroup.h>
 
+using android::base::GetBoolProperty;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
@@ -62,12 +64,20 @@
 static const std::string& GetCgroupRootPath() {
     static std::string cgroup_root_path;
     std::call_once(init_path_flag, [&]() {
-            // Check if mem cgroup is mounted, only then check for write-access to avoid
-            // SELinux denials
+        // low-ram devices use per-app memcg by default, unlike high-end ones
+        bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
+        bool per_app_memcg =
+            GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
+        if (per_app_memcg) {
+            // Check if mem cgroup is mounted, only then check for
+            // write-access to avoid SELinux denials
             cgroup_root_path =
-                (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ? ACCT_CGROUP_PATH
-                                                                                 : MEM_CGROUP_PATH);
-            });
+                (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
+                ACCT_CGROUP_PATH : MEM_CGROUP_PATH);
+        } else {
+            cgroup_root_path = ACCT_CGROUP_PATH;
+        }
+    });
     return cgroup_root_path;
 }
 
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index d776b3d..15f03d0 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -27,6 +27,7 @@
     name: "libprocinfo",
     defaults: ["libprocinfo_defaults"],
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
     },
diff --git a/libsparse/.clang-format b/libsparse/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libsparse/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index b894656..8ad339f 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -3,13 +3,14 @@
 cc_library {
     name: "libsparse",
     host_supported: true,
+    recovery_available: true,
     unique_host_soname: true,
     srcs: [
-        "backed_block.c",
-        "output_file.c",
-        "sparse.c",
-        "sparse_crc32.c",
-        "sparse_err.c",
+        "backed_block.cpp",
+        "output_file.cpp",
+        "sparse.cpp",
+        "sparse_crc32.cpp",
+        "sparse_err.cpp",
         "sparse_read.cpp",
     ],
     cflags: ["-Werror"],
@@ -30,8 +31,8 @@
     name: "simg2img",
     host_supported: true,
     srcs: [
-        "simg2img.c",
-        "sparse_crc32.c",
+        "simg2img.cpp",
+        "sparse_crc32.cpp",
     ],
     static_libs: [
         "libsparse",
@@ -45,7 +46,7 @@
 cc_binary {
     name: "img2simg",
     host_supported: true,
-    srcs: ["img2simg.c"],
+    srcs: ["img2simg.cpp"],
     static_libs: [
         "libsparse",
         "libz",
@@ -57,7 +58,7 @@
 
 cc_binary_host {
     name: "append2simg",
-    srcs: ["append2simg.c"],
+    srcs: ["append2simg.cpp"],
     static_libs: [
         "libsparse",
         "libz",
diff --git a/libsparse/OWNERS b/libsparse/OWNERS
new file mode 100644
index 0000000..70b375f
--- /dev/null
+++ b/libsparse/OWNERS
@@ -0,0 +1 @@
+ccross@google.com
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
deleted file mode 100644
index eef8764..0000000
--- a/libsparse/append2simg.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-#define _GNU_SOURCE
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-#include "sparse_file.h"
-#include "backed_block.h"
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#endif
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define off64_t off_t
-#endif
-
-void usage()
-{
-    fprintf(stderr, "Usage: append2simg <output> <input>\n");
-}
-
-int main(int argc, char *argv[])
-{
-    int output;
-    int output_block;
-    char *output_path;
-    struct sparse_file *sparse_output;
-
-    int input;
-    char *input_path;
-    off64_t input_len;
-
-    int tmp_fd;
-    char *tmp_path;
-
-    int ret;
-
-    if (argc == 3) {
-        output_path = argv[1];
-        input_path = argv[2];
-    } else {
-        usage();
-        exit(-1);
-    }
-
-    ret = asprintf(&tmp_path, "%s.append2simg", output_path);
-    if (ret < 0) {
-        fprintf(stderr, "Couldn't allocate filename\n");
-        exit(-1);
-    }
-
-    output = open(output_path, O_RDWR | O_BINARY);
-    if (output < 0) {
-        fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
-        exit(-1);
-    }
-
-    sparse_output = sparse_file_import_auto(output, false, true);
-    if (!sparse_output) {
-        fprintf(stderr, "Couldn't import output file\n");
-        exit(-1);
-    }
-
-    input = open(input_path, O_RDONLY | O_BINARY);
-    if (input < 0) {
-        fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
-        exit(-1);
-    }
-
-    input_len = lseek64(input, 0, SEEK_END);
-    if (input_len < 0) {
-        fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
-        exit(-1);
-    } else if (input_len % sparse_output->block_size) {
-        fprintf(stderr, "Input file is not a multiple of the output file's block size");
-        exit(-1);
-    }
-    lseek64(input, 0, SEEK_SET);
-
-    output_block = sparse_output->len / sparse_output->block_size;
-    if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
-        fprintf(stderr, "Couldn't add input file\n");
-        exit(-1);
-    }
-    sparse_output->len += input_len;
-
-    tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
-    if (tmp_fd < 0) {
-        fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
-        exit(-1);
-    }
-
-    lseek64(output, 0, SEEK_SET);
-    if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
-        fprintf(stderr, "Failed to write sparse file\n");
-        exit(-1);
-    }
-
-    sparse_file_destroy(sparse_output);
-    close(tmp_fd);
-    close(output);
-    close(input);
-
-    ret = rename(tmp_path, output_path);
-    if (ret < 0) {
-        fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
-        exit(-1);
-    }
-
-    free(tmp_path);
-
-    exit(0);
-}
diff --git a/libsparse/append2simg.cpp b/libsparse/append2simg.cpp
new file mode 100644
index 0000000..99f4339
--- /dev/null
+++ b/libsparse/append2simg.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+#include "backed_block.h"
+#include "sparse_file.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#endif
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage() {
+  fprintf(stderr, "Usage: append2simg <output> <input>\n");
+}
+
+int main(int argc, char* argv[]) {
+  int output;
+  int output_block;
+  char* output_path;
+  struct sparse_file* sparse_output;
+
+  int input;
+  char* input_path;
+  off64_t input_len;
+
+  int tmp_fd;
+  char* tmp_path;
+
+  int ret;
+
+  if (argc == 3) {
+    output_path = argv[1];
+    input_path = argv[2];
+  } else {
+    usage();
+    exit(-1);
+  }
+
+  ret = asprintf(&tmp_path, "%s.append2simg", output_path);
+  if (ret < 0) {
+    fprintf(stderr, "Couldn't allocate filename\n");
+    exit(-1);
+  }
+
+  output = open(output_path, O_RDWR | O_BINARY);
+  if (output < 0) {
+    fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
+    exit(-1);
+  }
+
+  sparse_output = sparse_file_import_auto(output, false, true);
+  if (!sparse_output) {
+    fprintf(stderr, "Couldn't import output file\n");
+    exit(-1);
+  }
+
+  input = open(input_path, O_RDONLY | O_BINARY);
+  if (input < 0) {
+    fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
+    exit(-1);
+  }
+
+  input_len = lseek64(input, 0, SEEK_END);
+  if (input_len < 0) {
+    fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
+    exit(-1);
+  } else if (input_len % sparse_output->block_size) {
+    fprintf(stderr, "Input file is not a multiple of the output file's block size");
+    exit(-1);
+  }
+  lseek64(input, 0, SEEK_SET);
+
+  output_block = sparse_output->len / sparse_output->block_size;
+  if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
+    fprintf(stderr, "Couldn't add input file\n");
+    exit(-1);
+  }
+  sparse_output->len += input_len;
+
+  tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
+  if (tmp_fd < 0) {
+    fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
+    exit(-1);
+  }
+
+  lseek64(output, 0, SEEK_SET);
+  if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
+    fprintf(stderr, "Failed to write sparse file\n");
+    exit(-1);
+  }
+
+  sparse_file_destroy(sparse_output);
+  close(tmp_fd);
+  close(output);
+  close(input);
+
+  ret = rename(tmp_path, output_path);
+  if (ret < 0) {
+    fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
+    exit(-1);
+  }
+
+  free(tmp_path);
+
+  exit(0);
+}
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c
deleted file mode 100644
index 794cd6b..0000000
--- a/libsparse/backed_block.c
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright (C) 2010 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 <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "backed_block.h"
-#include "sparse_defs.h"
-
-struct backed_block {
-	unsigned int block;
-	unsigned int len;
-	enum backed_block_type type;
-	union {
-		struct {
-			void *data;
-		} data;
-		struct {
-			char *filename;
-			int64_t offset;
-		} file;
-		struct {
-			int fd;
-			int64_t offset;
-		} fd;
-		struct {
-			uint32_t val;
-		} fill;
-	};
-	struct backed_block *next;
-};
-
-struct backed_block_list {
-	struct backed_block *data_blocks;
-	struct backed_block *last_used;
-	unsigned int block_size;
-};
-
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
-{
-	return bbl->data_blocks;
-}
-
-struct backed_block *backed_block_iter_next(struct backed_block *bb)
-{
-	return bb->next;
-}
-
-unsigned int backed_block_len(struct backed_block *bb)
-{
-	return bb->len;
-}
-
-unsigned int backed_block_block(struct backed_block *bb)
-{
-	return bb->block;
-}
-
-void *backed_block_data(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_DATA);
-	return bb->data.data;
-}
-
-const char *backed_block_filename(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_FILE);
-	return bb->file.filename;
-}
-
-int backed_block_fd(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_FD);
-	return bb->fd.fd;
-}
-
-int64_t backed_block_file_offset(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
-	if (bb->type == BACKED_BLOCK_FILE) {
-		return bb->file.offset;
-	} else { /* bb->type == BACKED_BLOCK_FD */
-		return bb->fd.offset;
-	}
-}
-
-uint32_t backed_block_fill_val(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_FILL);
-	return bb->fill.val;
-}
-
-enum backed_block_type backed_block_type(struct backed_block *bb)
-{
-	return bb->type;
-}
-
-void backed_block_destroy(struct backed_block *bb)
-{
-	if (bb->type == BACKED_BLOCK_FILE) {
-		free(bb->file.filename);
-	}
-
-	free(bb);
-}
-
-struct backed_block_list *backed_block_list_new(unsigned int block_size)
-{
-	struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
-	b->block_size = block_size;
-	return b;
-}
-
-void backed_block_list_destroy(struct backed_block_list *bbl)
-{
-	if (bbl->data_blocks) {
-		struct backed_block *bb = bbl->data_blocks;
-		while (bb) {
-			struct backed_block *next = bb->next;
-			backed_block_destroy(bb);
-			bb = next;
-		}
-	}
-
-	free(bbl);
-}
-
-void backed_block_list_move(struct backed_block_list *from,
-		struct backed_block_list *to, struct backed_block *start,
-		struct backed_block *end)
-{
-	struct backed_block *bb;
-
-	if (start == NULL) {
-		start = from->data_blocks;
-	}
-
-	if (!end) {
-		for (end = start; end && end->next; end = end->next)
-			;
-	}
-
-	if (start == NULL || end == NULL) {
-		return;
-	}
-
-	from->last_used = NULL;
-	to->last_used = NULL;
-	if (from->data_blocks == start) {
-		from->data_blocks = end->next;
-	} else {
-		for (bb = from->data_blocks; bb; bb = bb->next) {
-			if (bb->next == start) {
-				bb->next = end->next;
-				break;
-			}
-		}
-	}
-
-	if (!to->data_blocks) {
-		to->data_blocks = start;
-		end->next = NULL;
-	} else {
-		for (bb = to->data_blocks; bb; bb = bb->next) {
-			if (!bb->next || bb->next->block > start->block) {
-				end->next = bb->next;
-				bb->next = start;
-				break;
-			}
-		}
-	}
-}
-
-/* may free b */
-static int merge_bb(struct backed_block_list *bbl,
-		struct backed_block *a, struct backed_block *b)
-{
-	unsigned int block_len;
-
-	/* Block doesn't exist (possible if one block is the last block) */
-	if (!a || !b) {
-		return -EINVAL;
-	}
-
-	assert(a->block < b->block);
-
-	/* Blocks are of different types */
-	if (a->type != b->type) {
-		return -EINVAL;
-	}
-
-	/* Blocks are not adjacent */
-	block_len = a->len / bbl->block_size; /* rounds down */
-	if (a->block + block_len != b->block) {
-		return -EINVAL;
-	}
-
-	switch (a->type) {
-	case BACKED_BLOCK_DATA:
-		/* Don't support merging data for now */
-		return -EINVAL;
-	case BACKED_BLOCK_FILL:
-		if (a->fill.val != b->fill.val) {
-			return -EINVAL;
-		}
-		break;
-	case BACKED_BLOCK_FILE:
-		/* Already make sure b->type is BACKED_BLOCK_FILE */
-		if (strcmp(a->file.filename, b->file.filename) ||
-				a->file.offset + a->len != b->file.offset) {
-			return -EINVAL;
-		}
-		break;
-	case BACKED_BLOCK_FD:
-		if (a->fd.fd != b->fd.fd ||
-				a->fd.offset + a->len != b->fd.offset) {
-			return -EINVAL;
-		}
-		break;
-	}
-
-	/* Blocks are compatible and adjacent, with a before b.  Merge b into a,
-	 * and free b */
-	a->len += b->len;
-	a->next = b->next;
-
-	backed_block_destroy(b);
-
-	return 0;
-}
-
-static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
-{
-	struct backed_block *bb;
-
-	if (bbl->data_blocks == NULL) {
-		bbl->data_blocks = new_bb;
-		return 0;
-	}
-
-	if (bbl->data_blocks->block > new_bb->block) {
-		new_bb->next = bbl->data_blocks;
-		bbl->data_blocks = new_bb;
-		return 0;
-	}
-
-	/* Optimization: blocks are mostly queued in sequence, so save the
-	   pointer to the last bb that was added, and start searching from
-	   there if the next block number is higher */
-	if (bbl->last_used && new_bb->block > bbl->last_used->block)
-		bb = bbl->last_used;
-	else
-		bb = bbl->data_blocks;
-	bbl->last_used = new_bb;
-
-	for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
-		;
-
-	if (bb->next == NULL) {
-		bb->next = new_bb;
-	} else {
-		new_bb->next = bb->next;
-		bb->next = new_bb;
-	}
-
-	merge_bb(bbl, new_bb, new_bb->next);
-	if (!merge_bb(bbl, bb, new_bb)) {
-		/* new_bb destroyed, point to retained as last_used */
-		bbl->last_used = bb;
-	}
-
-	return 0;
-}
-
-/* Queues a fill block of memory to be written to the specified data blocks */
-int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
-		unsigned int len, unsigned int block)
-{
-	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
-	if (bb == NULL) {
-		return -ENOMEM;
-	}
-
-	bb->block = block;
-	bb->len = len;
-	bb->type = BACKED_BLOCK_FILL;
-	bb->fill.val = fill_val;
-	bb->next = NULL;
-
-	return queue_bb(bbl, bb);
-}
-
-/* Queues a block of memory to be written to the specified data blocks */
-int backed_block_add_data(struct backed_block_list *bbl, void *data,
-		unsigned int len, unsigned int block)
-{
-	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
-	if (bb == NULL) {
-		return -ENOMEM;
-	}
-
-	bb->block = block;
-	bb->len = len;
-	bb->type = BACKED_BLOCK_DATA;
-	bb->data.data = data;
-	bb->next = NULL;
-
-	return queue_bb(bbl, bb);
-}
-
-/* Queues a chunk of a file on disk to be written to the specified data blocks */
-int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
-		int64_t offset, unsigned int len, unsigned int block)
-{
-	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
-	if (bb == NULL) {
-		return -ENOMEM;
-	}
-
-	bb->block = block;
-	bb->len = len;
-	bb->type = BACKED_BLOCK_FILE;
-	bb->file.filename = strdup(filename);
-	bb->file.offset = offset;
-	bb->next = NULL;
-
-	return queue_bb(bbl, bb);
-}
-
-/* Queues a chunk of a fd to be written to the specified data blocks */
-int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
-		unsigned int len, unsigned int block)
-{
-	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
-	if (bb == NULL) {
-		return -ENOMEM;
-	}
-
-	bb->block = block;
-	bb->len = len;
-	bb->type = BACKED_BLOCK_FD;
-	bb->fd.fd = fd;
-	bb->fd.offset = offset;
-	bb->next = NULL;
-
-	return queue_bb(bbl, bb);
-}
-
-int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
-		unsigned int max_len)
-{
-	struct backed_block *new_bb;
-
-	max_len = ALIGN_DOWN(max_len, bbl->block_size);
-
-	if (bb->len <= max_len) {
-		return 0;
-	}
-
-	new_bb = malloc(sizeof(struct backed_block));
-	if (new_bb == NULL) {
-		return -ENOMEM;
-	}
-
-	*new_bb = *bb;
-
-	new_bb->len = bb->len - max_len;
-	new_bb->block = bb->block + max_len / bbl->block_size;
-	new_bb->next = bb->next;
-	bb->next = new_bb;
-	bb->len = max_len;
-
-	switch (bb->type) {
-	case BACKED_BLOCK_DATA:
-		new_bb->data.data = (char *)bb->data.data + max_len;
-		break;
-	case BACKED_BLOCK_FILE:
-		new_bb->file.offset += max_len;
-		break;
-	case BACKED_BLOCK_FD:
-		new_bb->fd.offset += max_len;
-		break;
-	case BACKED_BLOCK_FILL:
-		break;
-	}
-
-	return 0;
-}
diff --git a/libsparse/backed_block.cpp b/libsparse/backed_block.cpp
new file mode 100644
index 0000000..7f5632e
--- /dev/null
+++ b/libsparse/backed_block.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2010 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 <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "backed_block.h"
+#include "sparse_defs.h"
+
+struct backed_block {
+  unsigned int block;
+  unsigned int len;
+  enum backed_block_type type;
+  union {
+    struct {
+      void* data;
+    } data;
+    struct {
+      char* filename;
+      int64_t offset;
+    } file;
+    struct {
+      int fd;
+      int64_t offset;
+    } fd;
+    struct {
+      uint32_t val;
+    } fill;
+  };
+  struct backed_block* next;
+};
+
+struct backed_block_list {
+  struct backed_block* data_blocks;
+  struct backed_block* last_used;
+  unsigned int block_size;
+};
+
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl) {
+  return bbl->data_blocks;
+}
+
+struct backed_block* backed_block_iter_next(struct backed_block* bb) {
+  return bb->next;
+}
+
+unsigned int backed_block_len(struct backed_block* bb) {
+  return bb->len;
+}
+
+unsigned int backed_block_block(struct backed_block* bb) {
+  return bb->block;
+}
+
+void* backed_block_data(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_DATA);
+  return bb->data.data;
+}
+
+const char* backed_block_filename(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_FILE);
+  return bb->file.filename;
+}
+
+int backed_block_fd(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_FD);
+  return bb->fd.fd;
+}
+
+int64_t backed_block_file_offset(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
+  if (bb->type == BACKED_BLOCK_FILE) {
+    return bb->file.offset;
+  } else { /* bb->type == BACKED_BLOCK_FD */
+    return bb->fd.offset;
+  }
+}
+
+uint32_t backed_block_fill_val(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_FILL);
+  return bb->fill.val;
+}
+
+enum backed_block_type backed_block_type(struct backed_block* bb) {
+  return bb->type;
+}
+
+void backed_block_destroy(struct backed_block* bb) {
+  if (bb->type == BACKED_BLOCK_FILE) {
+    free(bb->file.filename);
+  }
+
+  free(bb);
+}
+
+struct backed_block_list* backed_block_list_new(unsigned int block_size) {
+  struct backed_block_list* b =
+      reinterpret_cast<backed_block_list*>(calloc(sizeof(struct backed_block_list), 1));
+  b->block_size = block_size;
+  return b;
+}
+
+void backed_block_list_destroy(struct backed_block_list* bbl) {
+  if (bbl->data_blocks) {
+    struct backed_block* bb = bbl->data_blocks;
+    while (bb) {
+      struct backed_block* next = bb->next;
+      backed_block_destroy(bb);
+      bb = next;
+    }
+  }
+
+  free(bbl);
+}
+
+void backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,
+                            struct backed_block* start, struct backed_block* end) {
+  struct backed_block* bb;
+
+  if (start == NULL) {
+    start = from->data_blocks;
+  }
+
+  if (!end) {
+    for (end = start; end && end->next; end = end->next)
+      ;
+  }
+
+  if (start == NULL || end == NULL) {
+    return;
+  }
+
+  from->last_used = NULL;
+  to->last_used = NULL;
+  if (from->data_blocks == start) {
+    from->data_blocks = end->next;
+  } else {
+    for (bb = from->data_blocks; bb; bb = bb->next) {
+      if (bb->next == start) {
+        bb->next = end->next;
+        break;
+      }
+    }
+  }
+
+  if (!to->data_blocks) {
+    to->data_blocks = start;
+    end->next = NULL;
+  } else {
+    for (bb = to->data_blocks; bb; bb = bb->next) {
+      if (!bb->next || bb->next->block > start->block) {
+        end->next = bb->next;
+        bb->next = start;
+        break;
+      }
+    }
+  }
+}
+
+/* may free b */
+static int merge_bb(struct backed_block_list* bbl, struct backed_block* a, struct backed_block* b) {
+  unsigned int block_len;
+
+  /* Block doesn't exist (possible if one block is the last block) */
+  if (!a || !b) {
+    return -EINVAL;
+  }
+
+  assert(a->block < b->block);
+
+  /* Blocks are of different types */
+  if (a->type != b->type) {
+    return -EINVAL;
+  }
+
+  /* Blocks are not adjacent */
+  block_len = a->len / bbl->block_size; /* rounds down */
+  if (a->block + block_len != b->block) {
+    return -EINVAL;
+  }
+
+  switch (a->type) {
+    case BACKED_BLOCK_DATA:
+      /* Don't support merging data for now */
+      return -EINVAL;
+    case BACKED_BLOCK_FILL:
+      if (a->fill.val != b->fill.val) {
+        return -EINVAL;
+      }
+      break;
+    case BACKED_BLOCK_FILE:
+      /* Already make sure b->type is BACKED_BLOCK_FILE */
+      if (strcmp(a->file.filename, b->file.filename) || a->file.offset + a->len != b->file.offset) {
+        return -EINVAL;
+      }
+      break;
+    case BACKED_BLOCK_FD:
+      if (a->fd.fd != b->fd.fd || a->fd.offset + a->len != b->fd.offset) {
+        return -EINVAL;
+      }
+      break;
+  }
+
+  /* Blocks are compatible and adjacent, with a before b.  Merge b into a,
+   * and free b */
+  a->len += b->len;
+  a->next = b->next;
+
+  backed_block_destroy(b);
+
+  return 0;
+}
+
+static int queue_bb(struct backed_block_list* bbl, struct backed_block* new_bb) {
+  struct backed_block* bb;
+
+  if (bbl->data_blocks == NULL) {
+    bbl->data_blocks = new_bb;
+    return 0;
+  }
+
+  if (bbl->data_blocks->block > new_bb->block) {
+    new_bb->next = bbl->data_blocks;
+    bbl->data_blocks = new_bb;
+    return 0;
+  }
+
+  /* Optimization: blocks are mostly queued in sequence, so save the
+     pointer to the last bb that was added, and start searching from
+     there if the next block number is higher */
+  if (bbl->last_used && new_bb->block > bbl->last_used->block)
+    bb = bbl->last_used;
+  else
+    bb = bbl->data_blocks;
+  bbl->last_used = new_bb;
+
+  for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
+    ;
+
+  if (bb->next == NULL) {
+    bb->next = new_bb;
+  } else {
+    new_bb->next = bb->next;
+    bb->next = new_bb;
+  }
+
+  merge_bb(bbl, new_bb, new_bb->next);
+  if (!merge_bb(bbl, bb, new_bb)) {
+    /* new_bb destroyed, point to retained as last_used */
+    bbl->last_used = bb;
+  }
+
+  return 0;
+}
+
+/* Queues a fill block of memory to be written to the specified data blocks */
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+                          unsigned int block) {
+  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+  if (bb == NULL) {
+    return -ENOMEM;
+  }
+
+  bb->block = block;
+  bb->len = len;
+  bb->type = BACKED_BLOCK_FILL;
+  bb->fill.val = fill_val;
+  bb->next = NULL;
+
+  return queue_bb(bbl, bb);
+}
+
+/* Queues a block of memory to be written to the specified data blocks */
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+                          unsigned int block) {
+  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+  if (bb == NULL) {
+    return -ENOMEM;
+  }
+
+  bb->block = block;
+  bb->len = len;
+  bb->type = BACKED_BLOCK_DATA;
+  bb->data.data = data;
+  bb->next = NULL;
+
+  return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a file on disk to be written to the specified data blocks */
+int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
+                          unsigned int len, unsigned int block) {
+  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+  if (bb == NULL) {
+    return -ENOMEM;
+  }
+
+  bb->block = block;
+  bb->len = len;
+  bb->type = BACKED_BLOCK_FILE;
+  bb->file.filename = strdup(filename);
+  bb->file.offset = offset;
+  bb->next = NULL;
+
+  return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a fd to be written to the specified data blocks */
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+                        unsigned int block) {
+  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+  if (bb == NULL) {
+    return -ENOMEM;
+  }
+
+  bb->block = block;
+  bb->len = len;
+  bb->type = BACKED_BLOCK_FD;
+  bb->fd.fd = fd;
+  bb->fd.offset = offset;
+  bb->next = NULL;
+
+  return queue_bb(bbl, bb);
+}
+
+int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb,
+                       unsigned int max_len) {
+  struct backed_block* new_bb;
+
+  max_len = ALIGN_DOWN(max_len, bbl->block_size);
+
+  if (bb->len <= max_len) {
+    return 0;
+  }
+
+  new_bb = reinterpret_cast<backed_block*>(malloc(sizeof(struct backed_block)));
+  if (new_bb == NULL) {
+    return -ENOMEM;
+  }
+
+  *new_bb = *bb;
+
+  new_bb->len = bb->len - max_len;
+  new_bb->block = bb->block + max_len / bbl->block_size;
+  new_bb->next = bb->next;
+  bb->next = new_bb;
+  bb->len = max_len;
+
+  switch (bb->type) {
+    case BACKED_BLOCK_DATA:
+      new_bb->data.data = (char*)bb->data.data + max_len;
+      break;
+    case BACKED_BLOCK_FILE:
+      new_bb->file.offset += max_len;
+      break;
+    case BACKED_BLOCK_FD:
+      new_bb->fd.offset += max_len;
+      break;
+    case BACKED_BLOCK_FILL:
+      break;
+  }
+
+  return 0;
+}
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index 1a159be..3a75460 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -23,42 +23,40 @@
 struct backed_block;
 
 enum backed_block_type {
-	BACKED_BLOCK_DATA,
-	BACKED_BLOCK_FILE,
-	BACKED_BLOCK_FD,
-	BACKED_BLOCK_FILL,
+  BACKED_BLOCK_DATA,
+  BACKED_BLOCK_FILE,
+  BACKED_BLOCK_FD,
+  BACKED_BLOCK_FILL,
 };
 
-int backed_block_add_data(struct backed_block_list *bbl, void *data,
-		unsigned int len, unsigned int block);
-int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
-		unsigned int len, unsigned int block);
-int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
-		int64_t offset, unsigned int len, unsigned int block);
-int backed_block_add_fd(struct backed_block_list *bbl, int fd,
-		int64_t offset, unsigned int len, unsigned int block);
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+                          unsigned int block);
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+                          unsigned int block);
+int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
+                          unsigned int len, unsigned int block);
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+                        unsigned int block);
 
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
-struct backed_block *backed_block_iter_next(struct backed_block *bb);
-unsigned int backed_block_len(struct backed_block *bb);
-unsigned int backed_block_block(struct backed_block *bb);
-void *backed_block_data(struct backed_block *bb);
-const char *backed_block_filename(struct backed_block *bb);
-int backed_block_fd(struct backed_block *bb);
-int64_t backed_block_file_offset(struct backed_block *bb);
-uint32_t backed_block_fill_val(struct backed_block *bb);
-enum backed_block_type backed_block_type(struct backed_block *bb);
-int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
-		unsigned int max_len);
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
+struct backed_block* backed_block_iter_next(struct backed_block* bb);
+unsigned int backed_block_len(struct backed_block* bb);
+unsigned int backed_block_block(struct backed_block* bb);
+void* backed_block_data(struct backed_block* bb);
+const char* backed_block_filename(struct backed_block* bb);
+int backed_block_fd(struct backed_block* bb);
+int64_t backed_block_file_offset(struct backed_block* bb);
+uint32_t backed_block_fill_val(struct backed_block* bb);
+enum backed_block_type backed_block_type(struct backed_block* bb);
+int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb, unsigned int max_len);
 
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
-struct backed_block *backed_block_iter_next(struct backed_block *bb);
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
+struct backed_block* backed_block_iter_next(struct backed_block* bb);
 
-struct backed_block_list *backed_block_list_new(unsigned int block_size);
-void backed_block_list_destroy(struct backed_block_list *bbl);
+struct backed_block_list* backed_block_list_new(unsigned int block_size);
+void backed_block_list_destroy(struct backed_block_list* bbl);
 
-void backed_block_list_move(struct backed_block_list *from,
-		struct backed_block_list *to, struct backed_block *start,
-		struct backed_block *end);
+void backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,
+                            struct backed_block* start, struct backed_block* end);
 
 #endif
diff --git a/libsparse/defs.h b/libsparse/defs.h
index 34e63c5..28e5cab 100644
--- a/libsparse/defs.h
+++ b/libsparse/defs.h
@@ -17,7 +17,7 @@
 #ifndef _LIBSPARSE_DEFS_H_
 
 #ifndef __unused
-#define __unused        __attribute__((__unused__))
+#define __unused __attribute__((__unused__))
 #endif
 
 #endif /* _LIBSPARSE_DEFS_H_ */
diff --git a/libsparse/img2simg.c b/libsparse/img2simg.c
deleted file mode 100644
index a0db36f..0000000
--- a/libsparse/img2simg.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define off64_t off_t
-#endif
-
-void usage()
-{
-    fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
-}
-
-int main(int argc, char *argv[])
-{
-	int in;
-	int out;
-	int ret;
-	struct sparse_file *s;
-	unsigned int block_size = 4096;
-	off64_t len;
-
-	if (argc < 3 || argc > 4) {
-		usage();
-		exit(-1);
-	}
-
-	if (argc == 4) {
-		block_size = atoi(argv[3]);
-	}
-
-	if (block_size < 1024 || block_size % 4 != 0) {
-		usage();
-		exit(-1);
-	}
-
-	if (strcmp(argv[1], "-") == 0) {
-		in = STDIN_FILENO;
-	} else {
-		in = open(argv[1], O_RDONLY | O_BINARY);
-		if (in < 0) {
-			fprintf(stderr, "Cannot open input file %s\n", argv[1]);
-			exit(-1);
-		}
-	}
-
-	if (strcmp(argv[2], "-") == 0) {
-		out = STDOUT_FILENO;
-	} else {
-		out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
-		if (out < 0) {
-			fprintf(stderr, "Cannot open output file %s\n", argv[2]);
-			exit(-1);
-		}
-	}
-
-	len = lseek64(in, 0, SEEK_END);
-	lseek64(in, 0, SEEK_SET);
-
-	s = sparse_file_new(block_size, len);
-	if (!s) {
-		fprintf(stderr, "Failed to create sparse file\n");
-		exit(-1);
-	}
-
-	sparse_file_verbose(s);
-	ret = sparse_file_read(s, in, false, false);
-	if (ret) {
-		fprintf(stderr, "Failed to read file\n");
-		exit(-1);
-	}
-
-	ret = sparse_file_write(s, out, false, true, false);
-	if (ret) {
-		fprintf(stderr, "Failed to write sparse file\n");
-		exit(-1);
-	}
-
-	close(in);
-	close(out);
-
-	exit(0);
-}
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
new file mode 100644
index 0000000..4c2c6ca
--- /dev/null
+++ b/libsparse/img2simg.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage() {
+  fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
+}
+
+int main(int argc, char* argv[]) {
+  int in;
+  int out;
+  int ret;
+  struct sparse_file* s;
+  unsigned int block_size = 4096;
+  off64_t len;
+
+  if (argc < 3 || argc > 4) {
+    usage();
+    exit(-1);
+  }
+
+  if (argc == 4) {
+    block_size = atoi(argv[3]);
+  }
+
+  if (block_size < 1024 || block_size % 4 != 0) {
+    usage();
+    exit(-1);
+  }
+
+  if (strcmp(argv[1], "-") == 0) {
+    in = STDIN_FILENO;
+  } else {
+    in = open(argv[1], O_RDONLY | O_BINARY);
+    if (in < 0) {
+      fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+      exit(-1);
+    }
+  }
+
+  if (strcmp(argv[2], "-") == 0) {
+    out = STDOUT_FILENO;
+  } else {
+    out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+    if (out < 0) {
+      fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+      exit(-1);
+    }
+  }
+
+  len = lseek64(in, 0, SEEK_END);
+  lseek64(in, 0, SEEK_SET);
+
+  s = sparse_file_new(block_size, len);
+  if (!s) {
+    fprintf(stderr, "Failed to create sparse file\n");
+    exit(-1);
+  }
+
+  sparse_file_verbose(s);
+  ret = sparse_file_read(s, in, false, false);
+  if (ret) {
+    fprintf(stderr, "Failed to read file\n");
+    exit(-1);
+  }
+
+  ret = sparse_file_write(s, out, false, true, false);
+  if (ret) {
+    fprintf(stderr, "Failed to write sparse file\n");
+    exit(-1);
+  }
+
+  close(in);
+  close(out);
+
+  exit(0);
+}
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 1b91ead..3d5fb0c 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -246,9 +246,24 @@
 int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc);
 
 /**
- * sparse_file_import - import an existing sparse file
+ * sparse_file_read_buf - read a buffer into a sparse file cookie
  *
  * @s - sparse file cookie
+ * @buf - buffer to read from
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads a buffer into a sparse file cookie. The buffer must remain
+ * valid until the sparse file cookie is freed. If crc is true, the
+ * crc of the sparse file will be verified.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_read_buf(struct sparse_file *s, char *buf, bool crc);
+
+/**
+ * sparse_file_import - import an existing sparse file
+ *
+ * @fd - file descriptor to read from
  * @verbose - print verbose errors while reading the sparse file
  * @crc - verify the crc of a file in the Android sparse file format
  *
@@ -261,6 +276,21 @@
 struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc);
 
 /**
+ * sparse_file_import_buf - import an existing sparse file from a buffer
+ *
+ * @buf - buffer to read from
+ * @verbose - print verbose errors while reading the sparse file
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads existing sparse file data into a sparse file cookie, recreating the same
+ * sparse cookie that was used to write it.  If verbose is true, prints verbose
+ * errors when the sparse file is formatted incorrectly.
+ *
+ * Returns a new sparse file cookie on success, NULL on error.
+ */
+struct sparse_file *sparse_file_import_buf(char* buf, bool verbose, bool crc);
+
+/**
  * sparse_file_import_auto - import an existing sparse or normal file
  *
  * @fd - file descriptor to read from
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
deleted file mode 100644
index 002ad27..0000000
--- a/libsparse/output_file.c
+++ /dev/null
@@ -1,776 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <zlib.h>
-
-#include "defs.h"
-#include "output_file.h"
-#include "sparse_crc32.h"
-#include "sparse_format.h"
-
-#ifndef _WIN32
-#include <sys/mman.h>
-#define O_BINARY 0
-#else
-#define ftruncate64 ftruncate
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define ftruncate64 ftruncate
-#define mmap64 mmap
-#define off64_t off_t
-#endif
-
-#define min(a, b) \
-	({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
-
-#define SPARSE_HEADER_MAJOR_VER 1
-#define SPARSE_HEADER_MINOR_VER 0
-#define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
-#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
-
-#define container_of(inner, outer_t, elem) \
-	((outer_t *)((char *)(inner) - offsetof(outer_t, elem)))
-
-struct output_file_ops {
-	int (*open)(struct output_file *, int fd);
-	int (*skip)(struct output_file *, int64_t);
-	int (*pad)(struct output_file *, int64_t);
-	int (*write)(struct output_file *, void *, size_t);
-	void (*close)(struct output_file *);
-};
-
-struct sparse_file_ops {
-	int (*write_data_chunk)(struct output_file *out, unsigned int len,
-			void *data);
-	int (*write_fill_chunk)(struct output_file *out, unsigned int len,
-			uint32_t fill_val);
-	int (*write_skip_chunk)(struct output_file *out, int64_t len);
-	int (*write_end_chunk)(struct output_file *out);
-};
-
-struct output_file {
-	int64_t cur_out_ptr;
-	unsigned int chunk_cnt;
-	uint32_t crc32;
-	struct output_file_ops *ops;
-	struct sparse_file_ops *sparse_ops;
-	int use_crc;
-	unsigned int block_size;
-	int64_t len;
-	char *zero_buf;
-	uint32_t *fill_buf;
-	char *buf;
-};
-
-struct output_file_gz {
-	struct output_file out;
-	gzFile gz_fd;
-};
-
-#define to_output_file_gz(_o) \
-	container_of((_o), struct output_file_gz, out)
-
-struct output_file_normal {
-	struct output_file out;
-	int fd;
-};
-
-#define to_output_file_normal(_o) \
-	container_of((_o), struct output_file_normal, out)
-
-struct output_file_callback {
-	struct output_file out;
-	void *priv;
-	int (*write)(void *priv, const void *buf, size_t len);
-};
-
-#define to_output_file_callback(_o) \
-	container_of((_o), struct output_file_callback, out)
-
-static int file_open(struct output_file *out, int fd)
-{
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	outn->fd = fd;
-	return 0;
-}
-
-static int file_skip(struct output_file *out, int64_t cnt)
-{
-	off64_t ret;
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	ret = lseek64(outn->fd, cnt, SEEK_CUR);
-	if (ret < 0) {
-		error_errno("lseek64");
-		return -1;
-	}
-	return 0;
-}
-
-static int file_pad(struct output_file *out, int64_t len)
-{
-	int ret;
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	ret = ftruncate64(outn->fd, len);
-	if (ret < 0) {
-		return -errno;
-	}
-
-	return 0;
-}
-
-static int file_write(struct output_file *out, void *data, size_t len)
-{
-	ssize_t ret;
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	while (len > 0) {
-		ret = write(outn->fd, data, len);
-		if (ret < 0) {
-			if (errno == EINTR) {
-				continue;
-			}
-			error_errno("write");
-			return -1;
-		}
-
-		data = (char *)data + ret;
-		len -= ret;
-	}
-
-	return 0;
-}
-
-static void file_close(struct output_file *out)
-{
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	free(outn);
-}
-
-static struct output_file_ops file_ops = {
-	.open = file_open,
-	.skip = file_skip,
-	.pad = file_pad,
-	.write = file_write,
-	.close = file_close,
-};
-
-static int gz_file_open(struct output_file *out, int fd)
-{
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	outgz->gz_fd = gzdopen(fd, "wb9");
-	if (!outgz->gz_fd) {
-		error_errno("gzopen");
-		return -errno;
-	}
-
-	return 0;
-}
-
-
-static int gz_file_skip(struct output_file *out, int64_t cnt)
-{
-	off64_t ret;
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
-	if (ret < 0) {
-		error_errno("gzseek");
-		return -1;
-	}
-	return 0;
-}
-
-static int gz_file_pad(struct output_file *out, int64_t len)
-{
-	off64_t ret;
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	ret = gztell(outgz->gz_fd);
-	if (ret < 0) {
-		return -1;
-	}
-
-	if (ret >= len) {
-		return 0;
-	}
-
-	ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
-	if (ret < 0) {
-		return -1;
-	}
-
-	gzwrite(outgz->gz_fd, "", 1);
-
-	return 0;
-}
-
-static int gz_file_write(struct output_file *out, void *data, size_t len)
-{
-	int ret;
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	while (len > 0) {
-		ret = gzwrite(outgz->gz_fd, data,
-			      min(len, (unsigned int)INT_MAX));
-		if (ret == 0) {
-			error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
-			return -1;
-		}
-		len -= ret;
-		data = (char *)data + ret;
-	}
-
-	return 0;
-}
-
-static void gz_file_close(struct output_file *out)
-{
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	gzclose(outgz->gz_fd);
-	free(outgz);
-}
-
-static struct output_file_ops gz_file_ops = {
-	.open = gz_file_open,
-	.skip = gz_file_skip,
-	.pad = gz_file_pad,
-	.write = gz_file_write,
-	.close = gz_file_close,
-};
-
-static int callback_file_open(struct output_file *out __unused, int fd __unused)
-{
-	return 0;
-}
-
-static int callback_file_skip(struct output_file *out, int64_t off)
-{
-	struct output_file_callback *outc = to_output_file_callback(out);
-	int to_write;
-	int ret;
-
-	while (off > 0) {
-		to_write = min(off, (int64_t)INT_MAX);
-		ret = outc->write(outc->priv, NULL, to_write);
-		if (ret < 0) {
-			return ret;
-		}
-		off -= to_write;
-	}
-
-	return 0;
-}
-
-static int callback_file_pad(struct output_file *out __unused, int64_t len __unused)
-{
-	return -1;
-}
-
-static int callback_file_write(struct output_file *out, void *data, size_t len)
-{
-	struct output_file_callback *outc = to_output_file_callback(out);
-
-	return outc->write(outc->priv, data, len);
-}
-
-static void callback_file_close(struct output_file *out)
-{
-	struct output_file_callback *outc = to_output_file_callback(out);
-
-	free(outc);
-}
-
-static struct output_file_ops callback_file_ops = {
-	.open = callback_file_open,
-	.skip = callback_file_skip,
-	.pad = callback_file_pad,
-	.write = callback_file_write,
-	.close = callback_file_close,
-};
-
-int read_all(int fd, void *buf, size_t len)
-{
-	size_t total = 0;
-	int ret;
-	char *ptr = buf;
-
-	while (total < len) {
-		ret = read(fd, ptr, len - total);
-
-		if (ret < 0)
-			return -errno;
-
-		if (ret == 0)
-			return -EINVAL;
-
-		ptr += ret;
-		total += ret;
-	}
-
-	return 0;
-}
-
-static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len)
-{
-	chunk_header_t chunk_header;
-	int ret;
-
-	if (skip_len % out->block_size) {
-		error("don't care size %"PRIi64" is not a multiple of the block size %u",
-				skip_len, out->block_size);
-		return -1;
-	}
-
-	/* We are skipping data, so emit a don't care chunk. */
-	chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
-	chunk_header.reserved1 = 0;
-	chunk_header.chunk_sz = skip_len / out->block_size;
-	chunk_header.total_sz = CHUNK_HEADER_LEN;
-	ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-	if (ret < 0)
-		return -1;
-
-	out->cur_out_ptr += skip_len;
-	out->chunk_cnt++;
-
-	return 0;
-}
-
-static int write_sparse_fill_chunk(struct output_file *out, unsigned int len,
-		uint32_t fill_val)
-{
-	chunk_header_t chunk_header;
-	int rnd_up_len, count;
-	int ret;
-
-	/* Round up the fill length to a multiple of the block size */
-	rnd_up_len = ALIGN(len, out->block_size);
-
-	/* Finally we can safely emit a chunk of data */
-	chunk_header.chunk_type = CHUNK_TYPE_FILL;
-	chunk_header.reserved1 = 0;
-	chunk_header.chunk_sz = rnd_up_len / out->block_size;
-	chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
-	ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-
-	if (ret < 0)
-		return -1;
-	ret = out->ops->write(out, &fill_val, sizeof(fill_val));
-	if (ret < 0)
-		return -1;
-
-	if (out->use_crc) {
-		count = out->block_size / sizeof(uint32_t);
-		while (count--)
-			out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
-	}
-
-	out->cur_out_ptr += rnd_up_len;
-	out->chunk_cnt++;
-
-	return 0;
-}
-
-static int write_sparse_data_chunk(struct output_file *out, unsigned int len,
-		void *data)
-{
-	chunk_header_t chunk_header;
-	int rnd_up_len, zero_len;
-	int ret;
-
-	/* Round up the data length to a multiple of the block size */
-	rnd_up_len = ALIGN(len, out->block_size);
-	zero_len = rnd_up_len - len;
-
-	/* Finally we can safely emit a chunk of data */
-	chunk_header.chunk_type = CHUNK_TYPE_RAW;
-	chunk_header.reserved1 = 0;
-	chunk_header.chunk_sz = rnd_up_len / out->block_size;
-	chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
-	ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-
-	if (ret < 0)
-		return -1;
-	ret = out->ops->write(out, data, len);
-	if (ret < 0)
-		return -1;
-	if (zero_len) {
-		ret = out->ops->write(out, out->zero_buf, zero_len);
-		if (ret < 0)
-			return -1;
-	}
-
-	if (out->use_crc) {
-		out->crc32 = sparse_crc32(out->crc32, data, len);
-		if (zero_len)
-			out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
-	}
-
-	out->cur_out_ptr += rnd_up_len;
-	out->chunk_cnt++;
-
-	return 0;
-}
-
-int write_sparse_end_chunk(struct output_file *out)
-{
-	chunk_header_t chunk_header;
-	int ret;
-
-	if (out->use_crc) {
-		chunk_header.chunk_type = CHUNK_TYPE_CRC32;
-		chunk_header.reserved1 = 0;
-		chunk_header.chunk_sz = 0;
-		chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
-
-		ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-		if (ret < 0) {
-			return ret;
-		}
-		out->ops->write(out, &out->crc32, 4);
-		if (ret < 0) {
-			return ret;
-		}
-
-		out->chunk_cnt++;
-	}
-
-	return 0;
-}
-
-static struct sparse_file_ops sparse_file_ops = {
-		.write_data_chunk = write_sparse_data_chunk,
-		.write_fill_chunk = write_sparse_fill_chunk,
-		.write_skip_chunk = write_sparse_skip_chunk,
-		.write_end_chunk = write_sparse_end_chunk,
-};
-
-static int write_normal_data_chunk(struct output_file *out, unsigned int len,
-		void *data)
-{
-	int ret;
-	unsigned int rnd_up_len = ALIGN(len, out->block_size);
-
-	ret = out->ops->write(out, data, len);
-	if (ret < 0) {
-		return ret;
-	}
-
-	if (rnd_up_len > len) {
-		ret = out->ops->skip(out, rnd_up_len - len);
-	}
-
-	return ret;
-}
-
-static int write_normal_fill_chunk(struct output_file *out, unsigned int len,
-		uint32_t fill_val)
-{
-	int ret;
-	unsigned int i;
-	unsigned int write_len;
-
-	/* Initialize fill_buf with the fill_val */
-	for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
-		out->fill_buf[i] = fill_val;
-	}
-
-	while (len) {
-		write_len = min(len, out->block_size);
-		ret = out->ops->write(out, out->fill_buf, write_len);
-		if (ret < 0) {
-			return ret;
-		}
-
-		len -= write_len;
-	}
-
-	return 0;
-}
-
-static int write_normal_skip_chunk(struct output_file *out, int64_t len)
-{
-	return out->ops->skip(out, len);
-}
-
-int write_normal_end_chunk(struct output_file *out)
-{
-	return out->ops->pad(out, out->len);
-}
-
-static struct sparse_file_ops normal_file_ops = {
-		.write_data_chunk = write_normal_data_chunk,
-		.write_fill_chunk = write_normal_fill_chunk,
-		.write_skip_chunk = write_normal_skip_chunk,
-		.write_end_chunk = write_normal_end_chunk,
-};
-
-void output_file_close(struct output_file *out)
-{
-	out->sparse_ops->write_end_chunk(out);
-	out->ops->close(out);
-}
-
-static int output_file_init(struct output_file *out, int block_size,
-		int64_t len, bool sparse, int chunks, bool crc)
-{
-	int ret;
-
-	out->len = len;
-	out->block_size = block_size;
-	out->cur_out_ptr = 0ll;
-	out->chunk_cnt = 0;
-	out->crc32 = 0;
-	out->use_crc = crc;
-
-	out->zero_buf = calloc(block_size, 1);
-	if (!out->zero_buf) {
-		error_errno("malloc zero_buf");
-		return -ENOMEM;
-	}
-
-	out->fill_buf = calloc(block_size, 1);
-	if (!out->fill_buf) {
-		error_errno("malloc fill_buf");
-		ret = -ENOMEM;
-		goto err_fill_buf;
-	}
-
-	if (sparse) {
-		out->sparse_ops = &sparse_file_ops;
-	} else {
-		out->sparse_ops = &normal_file_ops;
-	}
-
-	if (sparse) {
-		sparse_header_t sparse_header = {
-				.magic = SPARSE_HEADER_MAGIC,
-				.major_version = SPARSE_HEADER_MAJOR_VER,
-				.minor_version = SPARSE_HEADER_MINOR_VER,
-				.file_hdr_sz = SPARSE_HEADER_LEN,
-				.chunk_hdr_sz = CHUNK_HEADER_LEN,
-				.blk_sz = out->block_size,
-				.total_blks = DIV_ROUND_UP(out->len, out->block_size),
-				.total_chunks = chunks,
-				.image_checksum = 0
-		};
-
-		if (out->use_crc) {
-			sparse_header.total_chunks++;
-		}
-
-		ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
-		if (ret < 0) {
-			goto err_write;
-		}
-	}
-
-	return 0;
-
-err_write:
-	free(out->fill_buf);
-err_fill_buf:
-	free(out->zero_buf);
-	return ret;
-}
-
-static struct output_file *output_file_new_gz(void)
-{
-	struct output_file_gz *outgz = calloc(1, sizeof(struct output_file_gz));
-	if (!outgz) {
-		error_errno("malloc struct outgz");
-		return NULL;
-	}
-
-	outgz->out.ops = &gz_file_ops;
-
-	return &outgz->out;
-}
-
-static struct output_file *output_file_new_normal(void)
-{
-	struct output_file_normal *outn = calloc(1, sizeof(struct output_file_normal));
-	if (!outn) {
-		error_errno("malloc struct outn");
-		return NULL;
-	}
-
-	outn->out.ops = &file_ops;
-
-	return &outn->out;
-}
-
-struct output_file *output_file_open_callback(
-		int (*write)(void *, const void *, size_t),
-		void *priv, unsigned int block_size, int64_t len,
-		int gz __unused, int sparse, int chunks, int crc)
-{
-	int ret;
-	struct output_file_callback *outc;
-
-	outc = calloc(1, sizeof(struct output_file_callback));
-	if (!outc) {
-		error_errno("malloc struct outc");
-		return NULL;
-	}
-
-	outc->out.ops = &callback_file_ops;
-	outc->priv = priv;
-	outc->write = write;
-
-	ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
-	if (ret < 0) {
-		free(outc);
-		return NULL;
-	}
-
-	return &outc->out;
-}
-
-struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
-		int gz, int sparse, int chunks, int crc)
-{
-	int ret;
-	struct output_file *out;
-
-	if (gz) {
-		out = output_file_new_gz();
-	} else {
-		out = output_file_new_normal();
-	}
-	if (!out) {
-		return NULL;
-	}
-
-	out->ops->open(out, fd);
-
-	ret = output_file_init(out, block_size, len, sparse, chunks, crc);
-	if (ret < 0) {
-		free(out);
-		return NULL;
-	}
-
-	return out;
-}
-
-/* Write a contiguous region of data blocks from a memory buffer */
-int write_data_chunk(struct output_file *out, unsigned int len, void *data)
-{
-	return out->sparse_ops->write_data_chunk(out, len, data);
-}
-
-/* Write a contiguous region of data blocks with a fill value */
-int write_fill_chunk(struct output_file *out, unsigned int len,
-		uint32_t fill_val)
-{
-	return out->sparse_ops->write_fill_chunk(out, len, fill_val);
-}
-
-int write_fd_chunk(struct output_file *out, unsigned int len,
-		int fd, int64_t offset)
-{
-	int ret;
-	int64_t aligned_offset;
-	int aligned_diff;
-	uint64_t buffer_size;
-	char *ptr;
-
-	aligned_offset = offset & ~(4096 - 1);
-	aligned_diff = offset - aligned_offset;
-	buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
-
-#ifndef _WIN32
-	if (buffer_size > SIZE_MAX)
-		return -E2BIG;
-	char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd,
-			aligned_offset);
-	if (data == MAP_FAILED) {
-		return -errno;
-	}
-	ptr = data + aligned_diff;
-#else
-	off64_t pos;
-	char *data = malloc(len);
-	if (!data) {
-		return -errno;
-	}
-	pos = lseek64(fd, offset, SEEK_SET);
-	if (pos < 0) {
-                free(data);
-		return -errno;
-	}
-	ret = read_all(fd, data, len);
-	if (ret < 0) {
-                free(data);
-		return ret;
-	}
-	ptr = data;
-#endif
-
-	ret = out->sparse_ops->write_data_chunk(out, len, ptr);
-
-#ifndef _WIN32
-	munmap(data, buffer_size);
-#else
-	free(data);
-#endif
-
-	return ret;
-}
-
-/* Write a contiguous region of data blocks from a file */
-int write_file_chunk(struct output_file *out, unsigned int len,
-		const char *file, int64_t offset)
-{
-	int ret;
-
-	int file_fd = open(file, O_RDONLY | O_BINARY);
-	if (file_fd < 0) {
-		return -errno;
-	}
-
-	ret = write_fd_chunk(out, len, file_fd, offset);
-
-	close(file_fd);
-
-	return ret;
-}
-
-int write_skip_chunk(struct output_file *out, int64_t len)
-{
-	return out->sparse_ops->write_skip_chunk(out, len);
-}
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
new file mode 100644
index 0000000..5388e77
--- /dev/null
+++ b/libsparse/output_file.cpp
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <zlib.h>
+
+#include "defs.h"
+#include "output_file.h"
+#include "sparse_crc32.h"
+#include "sparse_format.h"
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#define O_BINARY 0
+#else
+#define ftruncate64 ftruncate
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define ftruncate64 ftruncate
+#define mmap64 mmap
+#define off64_t off_t
+#endif
+
+#define min(a, b)        \
+  ({                     \
+    typeof(a) _a = (a);  \
+    typeof(b) _b = (b);  \
+    (_a < _b) ? _a : _b; \
+  })
+
+#define SPARSE_HEADER_MAJOR_VER 1
+#define SPARSE_HEADER_MINOR_VER 0
+#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
+#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
+
+#define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))
+
+struct output_file_ops {
+  int (*open)(struct output_file*, int fd);
+  int (*skip)(struct output_file*, int64_t);
+  int (*pad)(struct output_file*, int64_t);
+  int (*write)(struct output_file*, void*, size_t);
+  void (*close)(struct output_file*);
+};
+
+struct sparse_file_ops {
+  int (*write_data_chunk)(struct output_file* out, unsigned int len, void* data);
+  int (*write_fill_chunk)(struct output_file* out, unsigned int len, uint32_t fill_val);
+  int (*write_skip_chunk)(struct output_file* out, int64_t len);
+  int (*write_end_chunk)(struct output_file* out);
+};
+
+struct output_file {
+  int64_t cur_out_ptr;
+  unsigned int chunk_cnt;
+  uint32_t crc32;
+  struct output_file_ops* ops;
+  struct sparse_file_ops* sparse_ops;
+  int use_crc;
+  unsigned int block_size;
+  int64_t len;
+  char* zero_buf;
+  uint32_t* fill_buf;
+  char* buf;
+};
+
+struct output_file_gz {
+  struct output_file out;
+  gzFile gz_fd;
+};
+
+#define to_output_file_gz(_o) container_of((_o), struct output_file_gz, out)
+
+struct output_file_normal {
+  struct output_file out;
+  int fd;
+};
+
+#define to_output_file_normal(_o) container_of((_o), struct output_file_normal, out)
+
+struct output_file_callback {
+  struct output_file out;
+  void* priv;
+  int (*write)(void* priv, const void* buf, size_t len);
+};
+
+#define to_output_file_callback(_o) container_of((_o), struct output_file_callback, out)
+
+static int file_open(struct output_file* out, int fd) {
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  outn->fd = fd;
+  return 0;
+}
+
+static int file_skip(struct output_file* out, int64_t cnt) {
+  off64_t ret;
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  ret = lseek64(outn->fd, cnt, SEEK_CUR);
+  if (ret < 0) {
+    error_errno("lseek64");
+    return -1;
+  }
+  return 0;
+}
+
+static int file_pad(struct output_file* out, int64_t len) {
+  int ret;
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  ret = ftruncate64(outn->fd, len);
+  if (ret < 0) {
+    return -errno;
+  }
+
+  return 0;
+}
+
+static int file_write(struct output_file* out, void* data, size_t len) {
+  ssize_t ret;
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  while (len > 0) {
+    ret = write(outn->fd, data, len);
+    if (ret < 0) {
+      if (errno == EINTR) {
+        continue;
+      }
+      error_errno("write");
+      return -1;
+    }
+
+    data = (char*)data + ret;
+    len -= ret;
+  }
+
+  return 0;
+}
+
+static void file_close(struct output_file* out) {
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  free(outn);
+}
+
+static struct output_file_ops file_ops = {
+    .open = file_open,
+    .skip = file_skip,
+    .pad = file_pad,
+    .write = file_write,
+    .close = file_close,
+};
+
+static int gz_file_open(struct output_file* out, int fd) {
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  outgz->gz_fd = gzdopen(fd, "wb9");
+  if (!outgz->gz_fd) {
+    error_errno("gzopen");
+    return -errno;
+  }
+
+  return 0;
+}
+
+static int gz_file_skip(struct output_file* out, int64_t cnt) {
+  off64_t ret;
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
+  if (ret < 0) {
+    error_errno("gzseek");
+    return -1;
+  }
+  return 0;
+}
+
+static int gz_file_pad(struct output_file* out, int64_t len) {
+  off64_t ret;
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  ret = gztell(outgz->gz_fd);
+  if (ret < 0) {
+    return -1;
+  }
+
+  if (ret >= len) {
+    return 0;
+  }
+
+  ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
+  if (ret < 0) {
+    return -1;
+  }
+
+  gzwrite(outgz->gz_fd, "", 1);
+
+  return 0;
+}
+
+static int gz_file_write(struct output_file* out, void* data, size_t len) {
+  int ret;
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  while (len > 0) {
+    ret = gzwrite(outgz->gz_fd, data, min(len, (unsigned int)INT_MAX));
+    if (ret == 0) {
+      error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
+      return -1;
+    }
+    len -= ret;
+    data = (char*)data + ret;
+  }
+
+  return 0;
+}
+
+static void gz_file_close(struct output_file* out) {
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  gzclose(outgz->gz_fd);
+  free(outgz);
+}
+
+static struct output_file_ops gz_file_ops = {
+    .open = gz_file_open,
+    .skip = gz_file_skip,
+    .pad = gz_file_pad,
+    .write = gz_file_write,
+    .close = gz_file_close,
+};
+
+static int callback_file_open(struct output_file* out __unused, int fd __unused) {
+  return 0;
+}
+
+static int callback_file_skip(struct output_file* out, int64_t off) {
+  struct output_file_callback* outc = to_output_file_callback(out);
+  int to_write;
+  int ret;
+
+  while (off > 0) {
+    to_write = min(off, (int64_t)INT_MAX);
+    ret = outc->write(outc->priv, NULL, to_write);
+    if (ret < 0) {
+      return ret;
+    }
+    off -= to_write;
+  }
+
+  return 0;
+}
+
+static int callback_file_pad(struct output_file* out __unused, int64_t len __unused) {
+  return -1;
+}
+
+static int callback_file_write(struct output_file* out, void* data, size_t len) {
+  struct output_file_callback* outc = to_output_file_callback(out);
+
+  return outc->write(outc->priv, data, len);
+}
+
+static void callback_file_close(struct output_file* out) {
+  struct output_file_callback* outc = to_output_file_callback(out);
+
+  free(outc);
+}
+
+static struct output_file_ops callback_file_ops = {
+    .open = callback_file_open,
+    .skip = callback_file_skip,
+    .pad = callback_file_pad,
+    .write = callback_file_write,
+    .close = callback_file_close,
+};
+
+int read_all(int fd, void* buf, size_t len) {
+  size_t total = 0;
+  int ret;
+  char* ptr = reinterpret_cast<char*>(buf);
+
+  while (total < len) {
+    ret = read(fd, ptr, len - total);
+
+    if (ret < 0) return -errno;
+
+    if (ret == 0) return -EINVAL;
+
+    ptr += ret;
+    total += ret;
+  }
+
+  return 0;
+}
+
+static int write_sparse_skip_chunk(struct output_file* out, int64_t skip_len) {
+  chunk_header_t chunk_header;
+  int ret;
+
+  if (skip_len % out->block_size) {
+    error("don't care size %" PRIi64 " is not a multiple of the block size %u", skip_len,
+          out->block_size);
+    return -1;
+  }
+
+  /* We are skipping data, so emit a don't care chunk. */
+  chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
+  chunk_header.reserved1 = 0;
+  chunk_header.chunk_sz = skip_len / out->block_size;
+  chunk_header.total_sz = CHUNK_HEADER_LEN;
+  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+  if (ret < 0) return -1;
+
+  out->cur_out_ptr += skip_len;
+  out->chunk_cnt++;
+
+  return 0;
+}
+
+static int write_sparse_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+  chunk_header_t chunk_header;
+  int rnd_up_len, count;
+  int ret;
+
+  /* Round up the fill length to a multiple of the block size */
+  rnd_up_len = ALIGN(len, out->block_size);
+
+  /* Finally we can safely emit a chunk of data */
+  chunk_header.chunk_type = CHUNK_TYPE_FILL;
+  chunk_header.reserved1 = 0;
+  chunk_header.chunk_sz = rnd_up_len / out->block_size;
+  chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
+  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+  if (ret < 0) return -1;
+  ret = out->ops->write(out, &fill_val, sizeof(fill_val));
+  if (ret < 0) return -1;
+
+  if (out->use_crc) {
+    count = out->block_size / sizeof(uint32_t);
+    while (count--) out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
+  }
+
+  out->cur_out_ptr += rnd_up_len;
+  out->chunk_cnt++;
+
+  return 0;
+}
+
+static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data) {
+  chunk_header_t chunk_header;
+  int rnd_up_len, zero_len;
+  int ret;
+
+  /* Round up the data length to a multiple of the block size */
+  rnd_up_len = ALIGN(len, out->block_size);
+  zero_len = rnd_up_len - len;
+
+  /* Finally we can safely emit a chunk of data */
+  chunk_header.chunk_type = CHUNK_TYPE_RAW;
+  chunk_header.reserved1 = 0;
+  chunk_header.chunk_sz = rnd_up_len / out->block_size;
+  chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
+  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+  if (ret < 0) return -1;
+  ret = out->ops->write(out, data, len);
+  if (ret < 0) return -1;
+  if (zero_len) {
+    ret = out->ops->write(out, out->zero_buf, zero_len);
+    if (ret < 0) return -1;
+  }
+
+  if (out->use_crc) {
+    out->crc32 = sparse_crc32(out->crc32, data, len);
+    if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
+  }
+
+  out->cur_out_ptr += rnd_up_len;
+  out->chunk_cnt++;
+
+  return 0;
+}
+
+int write_sparse_end_chunk(struct output_file* out) {
+  chunk_header_t chunk_header;
+  int ret;
+
+  if (out->use_crc) {
+    chunk_header.chunk_type = CHUNK_TYPE_CRC32;
+    chunk_header.reserved1 = 0;
+    chunk_header.chunk_sz = 0;
+    chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
+
+    ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+    if (ret < 0) {
+      return ret;
+    }
+    out->ops->write(out, &out->crc32, 4);
+    if (ret < 0) {
+      return ret;
+    }
+
+    out->chunk_cnt++;
+  }
+
+  return 0;
+}
+
+static struct sparse_file_ops sparse_file_ops = {
+    .write_data_chunk = write_sparse_data_chunk,
+    .write_fill_chunk = write_sparse_fill_chunk,
+    .write_skip_chunk = write_sparse_skip_chunk,
+    .write_end_chunk = write_sparse_end_chunk,
+};
+
+static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data) {
+  int ret;
+  unsigned int rnd_up_len = ALIGN(len, out->block_size);
+
+  ret = out->ops->write(out, data, len);
+  if (ret < 0) {
+    return ret;
+  }
+
+  if (rnd_up_len > len) {
+    ret = out->ops->skip(out, rnd_up_len - len);
+  }
+
+  return ret;
+}
+
+static int write_normal_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+  int ret;
+  unsigned int i;
+  unsigned int write_len;
+
+  /* Initialize fill_buf with the fill_val */
+  for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
+    out->fill_buf[i] = fill_val;
+  }
+
+  while (len) {
+    write_len = min(len, out->block_size);
+    ret = out->ops->write(out, out->fill_buf, write_len);
+    if (ret < 0) {
+      return ret;
+    }
+
+    len -= write_len;
+  }
+
+  return 0;
+}
+
+static int write_normal_skip_chunk(struct output_file* out, int64_t len) {
+  return out->ops->skip(out, len);
+}
+
+int write_normal_end_chunk(struct output_file* out) {
+  return out->ops->pad(out, out->len);
+}
+
+static struct sparse_file_ops normal_file_ops = {
+    .write_data_chunk = write_normal_data_chunk,
+    .write_fill_chunk = write_normal_fill_chunk,
+    .write_skip_chunk = write_normal_skip_chunk,
+    .write_end_chunk = write_normal_end_chunk,
+};
+
+void output_file_close(struct output_file* out) {
+  out->sparse_ops->write_end_chunk(out);
+  out->ops->close(out);
+}
+
+static int output_file_init(struct output_file* out, int block_size, int64_t len, bool sparse,
+                            int chunks, bool crc) {
+  int ret;
+
+  out->len = len;
+  out->block_size = block_size;
+  out->cur_out_ptr = 0ll;
+  out->chunk_cnt = 0;
+  out->crc32 = 0;
+  out->use_crc = crc;
+
+  out->zero_buf = reinterpret_cast<char*>(calloc(block_size, 1));
+  if (!out->zero_buf) {
+    error_errno("malloc zero_buf");
+    return -ENOMEM;
+  }
+
+  out->fill_buf = reinterpret_cast<uint32_t*>(calloc(block_size, 1));
+  if (!out->fill_buf) {
+    error_errno("malloc fill_buf");
+    ret = -ENOMEM;
+    goto err_fill_buf;
+  }
+
+  if (sparse) {
+    out->sparse_ops = &sparse_file_ops;
+  } else {
+    out->sparse_ops = &normal_file_ops;
+  }
+
+  if (sparse) {
+    sparse_header_t sparse_header = {
+        .magic = SPARSE_HEADER_MAGIC,
+        .major_version = SPARSE_HEADER_MAJOR_VER,
+        .minor_version = SPARSE_HEADER_MINOR_VER,
+        .file_hdr_sz = SPARSE_HEADER_LEN,
+        .chunk_hdr_sz = CHUNK_HEADER_LEN,
+        .blk_sz = out->block_size,
+        .total_blks = static_cast<unsigned>(DIV_ROUND_UP(out->len, out->block_size)),
+        .total_chunks = static_cast<unsigned>(chunks),
+        .image_checksum = 0};
+
+    if (out->use_crc) {
+      sparse_header.total_chunks++;
+    }
+
+    ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
+    if (ret < 0) {
+      goto err_write;
+    }
+  }
+
+  return 0;
+
+err_write:
+  free(out->fill_buf);
+err_fill_buf:
+  free(out->zero_buf);
+  return ret;
+}
+
+static struct output_file* output_file_new_gz(void) {
+  struct output_file_gz* outgz =
+      reinterpret_cast<struct output_file_gz*>(calloc(1, sizeof(struct output_file_gz)));
+  if (!outgz) {
+    error_errno("malloc struct outgz");
+    return NULL;
+  }
+
+  outgz->out.ops = &gz_file_ops;
+
+  return &outgz->out;
+}
+
+static struct output_file* output_file_new_normal(void) {
+  struct output_file_normal* outn =
+      reinterpret_cast<struct output_file_normal*>(calloc(1, sizeof(struct output_file_normal)));
+  if (!outn) {
+    error_errno("malloc struct outn");
+    return NULL;
+  }
+
+  outn->out.ops = &file_ops;
+
+  return &outn->out;
+}
+
+struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
+                                              unsigned int block_size, int64_t len, int gz __unused,
+                                              int sparse, int chunks, int crc) {
+  int ret;
+  struct output_file_callback* outc;
+
+  outc =
+      reinterpret_cast<struct output_file_callback*>(calloc(1, sizeof(struct output_file_callback)));
+  if (!outc) {
+    error_errno("malloc struct outc");
+    return NULL;
+  }
+
+  outc->out.ops = &callback_file_ops;
+  outc->priv = priv;
+  outc->write = write;
+
+  ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
+  if (ret < 0) {
+    free(outc);
+    return NULL;
+  }
+
+  return &outc->out;
+}
+
+struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
+                                        int sparse, int chunks, int crc) {
+  int ret;
+  struct output_file* out;
+
+  if (gz) {
+    out = output_file_new_gz();
+  } else {
+    out = output_file_new_normal();
+  }
+  if (!out) {
+    return NULL;
+  }
+
+  out->ops->open(out, fd);
+
+  ret = output_file_init(out, block_size, len, sparse, chunks, crc);
+  if (ret < 0) {
+    free(out);
+    return NULL;
+  }
+
+  return out;
+}
+
+/* Write a contiguous region of data blocks from a memory buffer */
+int write_data_chunk(struct output_file* out, unsigned int len, void* data) {
+  return out->sparse_ops->write_data_chunk(out, len, data);
+}
+
+/* Write a contiguous region of data blocks with a fill value */
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+  return out->sparse_ops->write_fill_chunk(out, len, fill_val);
+}
+
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
+  int ret;
+  int64_t aligned_offset;
+  int aligned_diff;
+  uint64_t buffer_size;
+  char* ptr;
+
+  aligned_offset = offset & ~(4096 - 1);
+  aligned_diff = offset - aligned_offset;
+  buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
+
+#ifndef _WIN32
+  if (buffer_size > SIZE_MAX) return -E2BIG;
+  char* data =
+      reinterpret_cast<char*>(mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
+  if (data == MAP_FAILED) {
+    return -errno;
+  }
+  ptr = data + aligned_diff;
+#else
+  off64_t pos;
+  char* data = reinterpret_cast<char*>(malloc(len));
+  if (!data) {
+    return -errno;
+  }
+  pos = lseek64(fd, offset, SEEK_SET);
+  if (pos < 0) {
+    free(data);
+    return -errno;
+  }
+  ret = read_all(fd, data, len);
+  if (ret < 0) {
+    free(data);
+    return ret;
+  }
+  ptr = data;
+#endif
+
+  ret = out->sparse_ops->write_data_chunk(out, len, ptr);
+
+#ifndef _WIN32
+  munmap(data, buffer_size);
+#else
+  free(data);
+#endif
+
+  return ret;
+}
+
+/* Write a contiguous region of data blocks from a file */
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset) {
+  int ret;
+
+  int file_fd = open(file, O_RDONLY | O_BINARY);
+  if (file_fd < 0) {
+    return -errno;
+  }
+
+  ret = write_fd_chunk(out, len, file_fd, offset);
+
+  close(file_fd);
+
+  return ret;
+}
+
+int write_skip_chunk(struct output_file* out, int64_t len) {
+  return out->sparse_ops->write_skip_chunk(out, len);
+}
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 690f610..278430b 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -25,23 +25,19 @@
 
 struct output_file;
 
-struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
-		int gz, int sparse, int chunks, int crc);
-struct output_file *output_file_open_callback(
-		int (*write)(void *, const void *, size_t),
-		void *priv, unsigned int block_size, int64_t len, int gz, int sparse,
-		int chunks, int crc);
-int write_data_chunk(struct output_file *out, unsigned int len, void *data);
-int write_fill_chunk(struct output_file *out, unsigned int len,
-		uint32_t fill_val);
-int write_file_chunk(struct output_file *out, unsigned int len,
-		const char *file, int64_t offset);
-int write_fd_chunk(struct output_file *out, unsigned int len,
-		int fd, int64_t offset);
-int write_skip_chunk(struct output_file *out, int64_t len);
-void output_file_close(struct output_file *out);
+struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
+                                        int sparse, int chunks, int crc);
+struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
+                                              unsigned int block_size, int64_t len, int gz,
+                                              int sparse, int chunks, int crc);
+int write_data_chunk(struct output_file* out, unsigned int len, void* data);
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val);
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset);
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset);
+int write_skip_chunk(struct output_file* out, int64_t len);
+void output_file_close(struct output_file* out);
 
-int read_all(int fd, void *buf, size_t len);
+int read_all(int fd, void* buf, size_t len);
 
 #ifdef __cplusplus
 }
diff --git a/libsparse/simg2img.c b/libsparse/simg2img.c
deleted file mode 100644
index b9b438e..0000000
--- a/libsparse/simg2img.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2010 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 <sparse/sparse.h>
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage()
-{
-  fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
-}
-
-int main(int argc, char *argv[])
-{
-	int in;
-	int out;
-	int i;
-	struct sparse_file *s;
-
-	if (argc < 3) {
-		usage();
-		exit(-1);
-	}
-
-	out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
-	if (out < 0) {
-		fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
-		exit(-1);
-	}
-
-	for (i = 1; i < argc - 1; i++) {
-		if (strcmp(argv[i], "-") == 0) {
-			in = STDIN_FILENO;
-		} else {
-			in = open(argv[i], O_RDONLY | O_BINARY);
-			if (in < 0) {
-				fprintf(stderr, "Cannot open input file %s\n", argv[i]);
-				exit(-1);
-			}
-		}
-
-		s = sparse_file_import(in, true, false);
-		if (!s) {
-			fprintf(stderr, "Failed to read sparse file\n");
-			exit(-1);
-		}
-
-		if (lseek(out, 0, SEEK_SET) == -1) {
-			perror("lseek failed");
-			exit(EXIT_FAILURE);
-		}
-
-		if (sparse_file_write(s, out, false, false, false) < 0) {
-			fprintf(stderr, "Cannot write output file\n");
-			exit(-1);
-		}
-		sparse_file_destroy(s);
-		close(in);
-	}
-
-	close(out);
-
-	exit(0);
-}
-
diff --git a/libsparse/simg2img.cpp b/libsparse/simg2img.cpp
new file mode 100644
index 0000000..8ba5f69
--- /dev/null
+++ b/libsparse/simg2img.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 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 <sparse/sparse.h>
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage() {
+  fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
+}
+
+int main(int argc, char* argv[]) {
+  int in;
+  int out;
+  int i;
+  struct sparse_file* s;
+
+  if (argc < 3) {
+    usage();
+    exit(-1);
+  }
+
+  out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+  if (out < 0) {
+    fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
+    exit(-1);
+  }
+
+  for (i = 1; i < argc - 1; i++) {
+    if (strcmp(argv[i], "-") == 0) {
+      in = STDIN_FILENO;
+    } else {
+      in = open(argv[i], O_RDONLY | O_BINARY);
+      if (in < 0) {
+        fprintf(stderr, "Cannot open input file %s\n", argv[i]);
+        exit(-1);
+      }
+    }
+
+    s = sparse_file_import(in, true, false);
+    if (!s) {
+      fprintf(stderr, "Failed to read sparse file\n");
+      exit(-1);
+    }
+
+    if (lseek(out, 0, SEEK_SET) == -1) {
+      perror("lseek failed");
+      exit(EXIT_FAILURE);
+    }
+
+    if (sparse_file_write(s, out, false, false, false) < 0) {
+      fprintf(stderr, "Cannot write output file\n");
+      exit(-1);
+    }
+    sparse_file_destroy(s);
+    close(in);
+  }
+
+  close(out);
+
+  exit(0);
+}
diff --git a/libsparse/simg2simg.c b/libsparse/simg2simg.c
deleted file mode 100644
index 5f9ccf6..0000000
--- a/libsparse/simg2simg.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-#define _GNU_SOURCE
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage()
-{
-  fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
-}
-
-int main(int argc, char *argv[])
-{
-	int in;
-	int out;
-	int i;
-	int ret;
-	struct sparse_file *s;
-	int64_t max_size;
-	struct sparse_file **out_s;
-	int files;
-	char filename[4096];
-
-	if (argc != 4) {
-		usage();
-		exit(-1);
-	}
-
-	max_size = atoll(argv[3]);
-
-	in = open(argv[1], O_RDONLY | O_BINARY);
-	if (in < 0) {
-		fprintf(stderr, "Cannot open input file %s\n", argv[1]);
-		exit(-1);
-	}
-
-	s = sparse_file_import(in, true, false);
-	if (!s) {
-		fprintf(stderr, "Failed to import sparse file\n");
-		exit(-1);
-	}
-
-	files = sparse_file_resparse(s, max_size, NULL, 0);
-	if (files < 0) {
-		fprintf(stderr, "Failed to resparse\n");
-		exit(-1);
-	}
-
-	out_s = calloc(sizeof(struct sparse_file *), files);
-	if (!out_s) {
-		fprintf(stderr, "Failed to allocate sparse file array\n");
-		exit(-1);
-	}
-
-	files = sparse_file_resparse(s, max_size, out_s, files);
-	if (files < 0) {
-		fprintf(stderr, "Failed to resparse\n");
-		exit(-1);
-	}
-
-	for (i = 0; i < files; i++) {
-		ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
-		if (ret >= (int)sizeof(filename)) {
-			fprintf(stderr, "Filename too long\n");
-			exit(-1);
-		}
-
-		out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
-		if (out < 0) {
-			fprintf(stderr, "Cannot open output file %s\n", argv[2]);
-			exit(-1);
-		}
-
-		ret = sparse_file_write(out_s[i], out, false, true, false);
-		if (ret) {
-			fprintf(stderr, "Failed to write sparse file\n");
-			exit(-1);
-		}
-		close(out);
-	}
-
-	close(in);
-
-	exit(0);
-}
diff --git a/libsparse/simg2simg.cpp b/libsparse/simg2simg.cpp
new file mode 100644
index 0000000..7e65701
--- /dev/null
+++ b/libsparse/simg2simg.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage() {
+  fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
+}
+
+int main(int argc, char* argv[]) {
+  int in;
+  int out;
+  int i;
+  int ret;
+  struct sparse_file* s;
+  int64_t max_size;
+  struct sparse_file** out_s;
+  int files;
+  char filename[4096];
+
+  if (argc != 4) {
+    usage();
+    exit(-1);
+  }
+
+  max_size = atoll(argv[3]);
+
+  in = open(argv[1], O_RDONLY | O_BINARY);
+  if (in < 0) {
+    fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+    exit(-1);
+  }
+
+  s = sparse_file_import(in, true, false);
+  if (!s) {
+    fprintf(stderr, "Failed to import sparse file\n");
+    exit(-1);
+  }
+
+  files = sparse_file_resparse(s, max_size, NULL, 0);
+  if (files < 0) {
+    fprintf(stderr, "Failed to resparse\n");
+    exit(-1);
+  }
+
+  out_s = calloc(sizeof(struct sparse_file*), files);
+  if (!out_s) {
+    fprintf(stderr, "Failed to allocate sparse file array\n");
+    exit(-1);
+  }
+
+  files = sparse_file_resparse(s, max_size, out_s, files);
+  if (files < 0) {
+    fprintf(stderr, "Failed to resparse\n");
+    exit(-1);
+  }
+
+  for (i = 0; i < files; i++) {
+    ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
+    if (ret >= (int)sizeof(filename)) {
+      fprintf(stderr, "Filename too long\n");
+      exit(-1);
+    }
+
+    out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+    if (out < 0) {
+      fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+      exit(-1);
+    }
+
+    ret = sparse_file_write(out_s[i], out, false, true, false);
+    if (ret) {
+      fprintf(stderr, "Failed to write sparse file\n");
+      exit(-1);
+    }
+    close(out);
+  }
+
+  close(in);
+
+  exit(0);
+}
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
deleted file mode 100644
index 466435f..0000000
--- a/libsparse/sparse.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2012 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 <stdlib.h>
-
-#include <sparse/sparse.h>
-
-#include "defs.h"
-#include "sparse_file.h"
-
-#include "output_file.h"
-#include "backed_block.h"
-#include "sparse_defs.h"
-#include "sparse_format.h"
-
-struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
-{
-	struct sparse_file *s = calloc(sizeof(struct sparse_file), 1);
-	if (!s) {
-		return NULL;
-	}
-
-	s->backed_block_list = backed_block_list_new(block_size);
-	if (!s->backed_block_list) {
-		free(s);
-		return NULL;
-	}
-
-	s->block_size = block_size;
-	s->len = len;
-
-	return s;
-}
-
-void sparse_file_destroy(struct sparse_file *s)
-{
-	backed_block_list_destroy(s->backed_block_list);
-	free(s);
-}
-
-int sparse_file_add_data(struct sparse_file *s,
-		void *data, unsigned int len, unsigned int block)
-{
-	return backed_block_add_data(s->backed_block_list, data, len, block);
-}
-
-int sparse_file_add_fill(struct sparse_file *s,
-		uint32_t fill_val, unsigned int len, unsigned int block)
-{
-	return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
-}
-
-int sparse_file_add_file(struct sparse_file *s,
-		const char *filename, int64_t file_offset, unsigned int len,
-		unsigned int block)
-{
-	return backed_block_add_file(s->backed_block_list, filename, file_offset,
-			len, block);
-}
-
-int sparse_file_add_fd(struct sparse_file *s,
-		int fd, int64_t file_offset, unsigned int len, unsigned int block)
-{
-	return backed_block_add_fd(s->backed_block_list, fd, file_offset,
-			len, block);
-}
-unsigned int sparse_count_chunks(struct sparse_file *s)
-{
-	struct backed_block *bb;
-	unsigned int last_block = 0;
-	unsigned int chunks = 0;
-
-	for (bb = backed_block_iter_new(s->backed_block_list); bb;
-			bb = backed_block_iter_next(bb)) {
-		if (backed_block_block(bb) > last_block) {
-			/* If there is a gap between chunks, add a skip chunk */
-			chunks++;
-		}
-		chunks++;
-		last_block = backed_block_block(bb) +
-				DIV_ROUND_UP(backed_block_len(bb), s->block_size);
-	}
-	if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
-		chunks++;
-	}
-
-	return chunks;
-}
-
-static int sparse_file_write_block(struct output_file *out,
-		struct backed_block *bb)
-{
-	int ret = -EINVAL;
-
-	switch (backed_block_type(bb)) {
-	case BACKED_BLOCK_DATA:
-		ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
-		break;
-	case BACKED_BLOCK_FILE:
-		ret = write_file_chunk(out, backed_block_len(bb),
-				       backed_block_filename(bb),
-				       backed_block_file_offset(bb));
-		break;
-	case BACKED_BLOCK_FD:
-		ret = write_fd_chunk(out, backed_block_len(bb),
-				     backed_block_fd(bb),
-				     backed_block_file_offset(bb));
-		break;
-	case BACKED_BLOCK_FILL:
-		ret = write_fill_chunk(out, backed_block_len(bb),
-				       backed_block_fill_val(bb));
-		break;
-	}
-
-	return ret;
-}
-
-static int write_all_blocks(struct sparse_file *s, struct output_file *out)
-{
-	struct backed_block *bb;
-	unsigned int last_block = 0;
-	int64_t pad;
-	int ret = 0;
-
-	for (bb = backed_block_iter_new(s->backed_block_list); bb;
-			bb = backed_block_iter_next(bb)) {
-		if (backed_block_block(bb) > last_block) {
-			unsigned int blocks = backed_block_block(bb) - last_block;
-			write_skip_chunk(out, (int64_t)blocks * s->block_size);
-		}
-		ret = sparse_file_write_block(out, bb);
-		if (ret)
-			return ret;
-		last_block = backed_block_block(bb) +
-				DIV_ROUND_UP(backed_block_len(bb), s->block_size);
-	}
-
-	pad = s->len - (int64_t)last_block * s->block_size;
-	assert(pad >= 0);
-	if (pad > 0) {
-		write_skip_chunk(out, pad);
-	}
-
-	return 0;
-}
-
-int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
-		bool crc)
-{
-	int ret;
-	int chunks;
-	struct output_file *out;
-
-	chunks = sparse_count_chunks(s);
-	out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
-
-	if (!out)
-		return -ENOMEM;
-
-	ret = write_all_blocks(s, out);
-
-	output_file_close(out);
-
-	return ret;
-}
-
-int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
-		int (*write)(void *priv, const void *data, size_t len), void *priv)
-{
-	int ret;
-	int chunks;
-	struct output_file *out;
-
-	chunks = sparse_count_chunks(s);
-	out = output_file_open_callback(write, priv, s->block_size, s->len, false,
-			sparse, chunks, crc);
-
-	if (!out)
-		return -ENOMEM;
-
-	ret = write_all_blocks(s, out);
-
-	output_file_close(out);
-
-	return ret;
-}
-
-struct chunk_data {
-	void		*priv;
-	unsigned int	block;
-	unsigned int	nr_blocks;
-	int (*write)(void *priv, const void *data, size_t len,
-		     unsigned int block, unsigned int nr_blocks);
-};
-
-static int foreach_chunk_write(void *priv, const void *data, size_t len)
-{
-	struct chunk_data *chk = priv;
-
-	return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
-}
-
-int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,
-	int (*write)(void *priv, const void *data, size_t len, unsigned int block,
-		     unsigned int nr_blocks),
-	void *priv)
-{
-	int ret;
-	int chunks;
-	struct chunk_data chk;
-	struct output_file *out;
-	struct backed_block *bb;
-
-	chk.priv = priv;
-	chk.write = write;
-	chk.block = chk.nr_blocks = 0;
-	chunks = sparse_count_chunks(s);
-	out = output_file_open_callback(foreach_chunk_write, &chk,
-					s->block_size, s->len, false, sparse,
-					chunks, crc);
-
-	if (!out)
-		return -ENOMEM;
-
-	for (bb = backed_block_iter_new(s->backed_block_list); bb;
-			bb = backed_block_iter_next(bb)) {
-		chk.block = backed_block_block(bb);
-		chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
-		ret = sparse_file_write_block(out, bb);
-		if (ret)
-			return ret;
-	}
-
-	output_file_close(out);
-
-	return ret;
-}
-
-static int out_counter_write(void *priv, const void *data __unused, size_t len)
-{
-	int64_t *count = priv;
-	*count += len;
-	return 0;
-}
-
-int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc)
-{
-	int ret;
-	int chunks = sparse_count_chunks(s);
-	int64_t count = 0;
-	struct output_file *out;
-
-	out = output_file_open_callback(out_counter_write, &count,
-			s->block_size, s->len, false, sparse, chunks, crc);
-	if (!out) {
-		return -1;
-	}
-
-	ret = write_all_blocks(s, out);
-
-	output_file_close(out);
-
-	if (ret < 0) {
-		return -1;
-	}
-
-	return count;
-}
-
-unsigned int sparse_file_block_size(struct sparse_file *s)
-{
-	return s->block_size;
-}
-
-static struct backed_block *move_chunks_up_to_len(struct sparse_file *from,
-		struct sparse_file *to, unsigned int len)
-{
-	int64_t count = 0;
-	struct output_file *out_counter;
-	struct backed_block *last_bb = NULL;
-	struct backed_block *bb;
-	struct backed_block *start;
-	unsigned int last_block = 0;
-	int64_t file_len = 0;
-	int ret;
-
-	/*
-	 * overhead is sparse file header, the potential end skip
-	 * chunk and crc chunk.
-	 */
-	int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) +
-			sizeof(uint32_t);
-	len -= overhead;
-
-	start = backed_block_iter_new(from->backed_block_list);
-	out_counter = output_file_open_callback(out_counter_write, &count,
-			to->block_size, to->len, false, true, 0, false);
-	if (!out_counter) {
-		return NULL;
-	}
-
-	for (bb = start; bb; bb = backed_block_iter_next(bb)) {
-		count = 0;
-		if (backed_block_block(bb) > last_block)
-			count += sizeof(chunk_header_t);
-		last_block = backed_block_block(bb) +
-				DIV_ROUND_UP(backed_block_len(bb), to->block_size);
-
-		/* will call out_counter_write to update count */
-		ret = sparse_file_write_block(out_counter, bb);
-		if (ret) {
-			bb = NULL;
-			goto out;
-		}
-		if (file_len + count > len) {
-			/*
-			 * If the remaining available size is more than 1/8th of the
-			 * requested size, split the chunk.  Results in sparse files that
-			 * are at least 7/8ths of the requested size
-			 */
-			file_len += sizeof(chunk_header_t);
-			if (!last_bb || (len - file_len > (len / 8))) {
-				backed_block_split(from->backed_block_list, bb, len - file_len);
-				last_bb = bb;
-			}
-			goto move;
-		}
-		file_len += count;
-		last_bb = bb;
-	}
-
-move:
-	backed_block_list_move(from->backed_block_list,
-		to->backed_block_list, start, last_bb);
-
-out:
-	output_file_close(out_counter);
-
-	return bb;
-}
-
-int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len,
-		struct sparse_file **out_s, int out_s_count)
-{
-	struct backed_block *bb;
-	struct sparse_file *s;
-	struct sparse_file *tmp;
-	int c = 0;
-
-	tmp = sparse_file_new(in_s->block_size, in_s->len);
-	if (!tmp) {
-		return -ENOMEM;
-	}
-
-	do {
-		s = sparse_file_new(in_s->block_size, in_s->len);
-
-		bb = move_chunks_up_to_len(in_s, s, max_len);
-
-		if (c < out_s_count) {
-			out_s[c] = s;
-		} else {
-			backed_block_list_move(s->backed_block_list, tmp->backed_block_list,
-					NULL, NULL);
-			sparse_file_destroy(s);
-		}
-		c++;
-	} while (bb);
-
-	backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list,
-			NULL, NULL);
-
-	sparse_file_destroy(tmp);
-
-	return c;
-}
-
-void sparse_file_verbose(struct sparse_file *s)
-{
-	s->verbose = true;
-}
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
new file mode 100644
index 0000000..6ff97b6
--- /dev/null
+++ b/libsparse/sparse.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2012 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 <stdlib.h>
+
+#include <sparse/sparse.h>
+
+#include "defs.h"
+#include "sparse_file.h"
+
+#include "backed_block.h"
+#include "output_file.h"
+#include "sparse_defs.h"
+#include "sparse_format.h"
+
+struct sparse_file* sparse_file_new(unsigned int block_size, int64_t len) {
+  struct sparse_file* s = reinterpret_cast<sparse_file*>(calloc(sizeof(struct sparse_file), 1));
+  if (!s) {
+    return NULL;
+  }
+
+  s->backed_block_list = backed_block_list_new(block_size);
+  if (!s->backed_block_list) {
+    free(s);
+    return NULL;
+  }
+
+  s->block_size = block_size;
+  s->len = len;
+
+  return s;
+}
+
+void sparse_file_destroy(struct sparse_file* s) {
+  backed_block_list_destroy(s->backed_block_list);
+  free(s);
+}
+
+int sparse_file_add_data(struct sparse_file* s, void* data, unsigned int len, unsigned int block) {
+  return backed_block_add_data(s->backed_block_list, data, len, block);
+}
+
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, unsigned int len,
+                         unsigned int block) {
+  return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
+}
+
+int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
+                         unsigned int len, unsigned int block) {
+  return backed_block_add_file(s->backed_block_list, filename, file_offset, len, block);
+}
+
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, unsigned int len,
+                       unsigned int block) {
+  return backed_block_add_fd(s->backed_block_list, fd, file_offset, len, block);
+}
+unsigned int sparse_count_chunks(struct sparse_file* s) {
+  struct backed_block* bb;
+  unsigned int last_block = 0;
+  unsigned int chunks = 0;
+
+  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+    if (backed_block_block(bb) > last_block) {
+      /* If there is a gap between chunks, add a skip chunk */
+      chunks++;
+    }
+    chunks++;
+    last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+  }
+  if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
+    chunks++;
+  }
+
+  return chunks;
+}
+
+static int sparse_file_write_block(struct output_file* out, struct backed_block* bb) {
+  int ret = -EINVAL;
+
+  switch (backed_block_type(bb)) {
+    case BACKED_BLOCK_DATA:
+      ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
+      break;
+    case BACKED_BLOCK_FILE:
+      ret = write_file_chunk(out, backed_block_len(bb), backed_block_filename(bb),
+                             backed_block_file_offset(bb));
+      break;
+    case BACKED_BLOCK_FD:
+      ret = write_fd_chunk(out, backed_block_len(bb), backed_block_fd(bb),
+                           backed_block_file_offset(bb));
+      break;
+    case BACKED_BLOCK_FILL:
+      ret = write_fill_chunk(out, backed_block_len(bb), backed_block_fill_val(bb));
+      break;
+  }
+
+  return ret;
+}
+
+static int write_all_blocks(struct sparse_file* s, struct output_file* out) {
+  struct backed_block* bb;
+  unsigned int last_block = 0;
+  int64_t pad;
+  int ret = 0;
+
+  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+    if (backed_block_block(bb) > last_block) {
+      unsigned int blocks = backed_block_block(bb) - last_block;
+      write_skip_chunk(out, (int64_t)blocks * s->block_size);
+    }
+    ret = sparse_file_write_block(out, bb);
+    if (ret) return ret;
+    last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+  }
+
+  pad = s->len - (int64_t)last_block * s->block_size;
+  assert(pad >= 0);
+  if (pad > 0) {
+    write_skip_chunk(out, pad);
+  }
+
+  return 0;
+}
+
+int sparse_file_write(struct sparse_file* s, int fd, bool gz, bool sparse, bool crc) {
+  int ret;
+  int chunks;
+  struct output_file* out;
+
+  chunks = sparse_count_chunks(s);
+  out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
+
+  if (!out) return -ENOMEM;
+
+  ret = write_all_blocks(s, out);
+
+  output_file_close(out);
+
+  return ret;
+}
+
+int sparse_file_callback(struct sparse_file* s, bool sparse, bool crc,
+                         int (*write)(void* priv, const void* data, size_t len), void* priv) {
+  int ret;
+  int chunks;
+  struct output_file* out;
+
+  chunks = sparse_count_chunks(s);
+  out = output_file_open_callback(write, priv, s->block_size, s->len, false, sparse, chunks, crc);
+
+  if (!out) return -ENOMEM;
+
+  ret = write_all_blocks(s, out);
+
+  output_file_close(out);
+
+  return ret;
+}
+
+struct chunk_data {
+  void* priv;
+  unsigned int block;
+  unsigned int nr_blocks;
+  int (*write)(void* priv, const void* data, size_t len, unsigned int block, unsigned int nr_blocks);
+};
+
+static int foreach_chunk_write(void* priv, const void* data, size_t len) {
+  struct chunk_data* chk = reinterpret_cast<chunk_data*>(priv);
+
+  return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
+}
+
+int sparse_file_foreach_chunk(struct sparse_file* s, bool sparse, bool crc,
+                              int (*write)(void* priv, const void* data, size_t len,
+                                           unsigned int block, unsigned int nr_blocks),
+                              void* priv) {
+  int ret;
+  int chunks;
+  struct chunk_data chk;
+  struct output_file* out;
+  struct backed_block* bb;
+
+  chk.priv = priv;
+  chk.write = write;
+  chk.block = chk.nr_blocks = 0;
+  chunks = sparse_count_chunks(s);
+  out = output_file_open_callback(foreach_chunk_write, &chk, s->block_size, s->len, false, sparse,
+                                  chunks, crc);
+
+  if (!out) return -ENOMEM;
+
+  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+    chk.block = backed_block_block(bb);
+    chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
+    ret = sparse_file_write_block(out, bb);
+    if (ret) return ret;
+  }
+
+  output_file_close(out);
+
+  return ret;
+}
+
+static int out_counter_write(void* priv, const void* data __unused, size_t len) {
+  int64_t* count = reinterpret_cast<int64_t*>(priv);
+  *count += len;
+  return 0;
+}
+
+int64_t sparse_file_len(struct sparse_file* s, bool sparse, bool crc) {
+  int ret;
+  int chunks = sparse_count_chunks(s);
+  int64_t count = 0;
+  struct output_file* out;
+
+  out = output_file_open_callback(out_counter_write, &count, s->block_size, s->len, false, sparse,
+                                  chunks, crc);
+  if (!out) {
+    return -1;
+  }
+
+  ret = write_all_blocks(s, out);
+
+  output_file_close(out);
+
+  if (ret < 0) {
+    return -1;
+  }
+
+  return count;
+}
+
+unsigned int sparse_file_block_size(struct sparse_file* s) {
+  return s->block_size;
+}
+
+static struct backed_block* move_chunks_up_to_len(struct sparse_file* from, struct sparse_file* to,
+                                                  unsigned int len) {
+  int64_t count = 0;
+  struct output_file* out_counter;
+  struct backed_block* last_bb = NULL;
+  struct backed_block* bb;
+  struct backed_block* start;
+  unsigned int last_block = 0;
+  int64_t file_len = 0;
+  int ret;
+
+  /*
+   * overhead is sparse file header, the potential end skip
+   * chunk and crc chunk.
+   */
+  int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) + sizeof(uint32_t);
+  len -= overhead;
+
+  start = backed_block_iter_new(from->backed_block_list);
+  out_counter = output_file_open_callback(out_counter_write, &count, to->block_size, to->len, false,
+                                          true, 0, false);
+  if (!out_counter) {
+    return NULL;
+  }
+
+  for (bb = start; bb; bb = backed_block_iter_next(bb)) {
+    count = 0;
+    if (backed_block_block(bb) > last_block) count += sizeof(chunk_header_t);
+    last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), to->block_size);
+
+    /* will call out_counter_write to update count */
+    ret = sparse_file_write_block(out_counter, bb);
+    if (ret) {
+      bb = NULL;
+      goto out;
+    }
+    if (file_len + count > len) {
+      /*
+       * If the remaining available size is more than 1/8th of the
+       * requested size, split the chunk.  Results in sparse files that
+       * are at least 7/8ths of the requested size
+       */
+      file_len += sizeof(chunk_header_t);
+      if (!last_bb || (len - file_len > (len / 8))) {
+        backed_block_split(from->backed_block_list, bb, len - file_len);
+        last_bb = bb;
+      }
+      goto move;
+    }
+    file_len += count;
+    last_bb = bb;
+  }
+
+move:
+  backed_block_list_move(from->backed_block_list, to->backed_block_list, start, last_bb);
+
+out:
+  output_file_close(out_counter);
+
+  return bb;
+}
+
+int sparse_file_resparse(struct sparse_file* in_s, unsigned int max_len, struct sparse_file** out_s,
+                         int out_s_count) {
+  struct backed_block* bb;
+  struct sparse_file* s;
+  struct sparse_file* tmp;
+  int c = 0;
+
+  tmp = sparse_file_new(in_s->block_size, in_s->len);
+  if (!tmp) {
+    return -ENOMEM;
+  }
+
+  do {
+    s = sparse_file_new(in_s->block_size, in_s->len);
+
+    bb = move_chunks_up_to_len(in_s, s, max_len);
+
+    if (c < out_s_count) {
+      out_s[c] = s;
+    } else {
+      backed_block_list_move(s->backed_block_list, tmp->backed_block_list, NULL, NULL);
+      sparse_file_destroy(s);
+    }
+    c++;
+  } while (bb);
+
+  backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, NULL, NULL);
+
+  sparse_file_destroy(tmp);
+
+  return c;
+}
+
+void sparse_file_verbose(struct sparse_file* s) {
+  s->verbose = true;
+}
diff --git a/libsparse/sparse_crc32.c b/libsparse/sparse_crc32.c
deleted file mode 100644
index 38bfe4a..0000000
--- a/libsparse/sparse_crc32.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-
- *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
- *  code or tables extracted from it, as desired without restriction.
- */
-
-/*
- *  First, the polynomial itself and its table of feedback terms.  The
- *  polynomial is
- *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
- *
- *  Note that we take it "backwards" and put the highest-order term in
- *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
- *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
- *  the MSB being 1
- *
- *  Note that the usual hardware shift register implementation, which
- *  is what we're using (we're merely optimizing it by doing eight-bit
- *  chunks at a time) shifts bits into the lowest-order term.  In our
- *  implementation, that means shifting towards the right.  Why do we
- *  do it this way?  Because the calculated CRC must be transmitted in
- *  order from highest-order term to lowest-order term.  UARTs transmit
- *  characters in order from LSB to MSB.  By storing the CRC this way
- *  we hand it to the UART in the order low-byte to high-byte; the UART
- *  sends each low-bit to hight-bit; and the result is transmission bit
- *  by bit from highest- to lowest-order term without requiring any bit
- *  shuffling on our part.  Reception works similarly
- *
- *  The feedback terms table consists of 256, 32-bit entries.  Notes
- *
- *      The table can be generated at runtime if desired; code to do so
- *      is shown later.  It might not be obvious, but the feedback
- *      terms simply represent the results of eight shift/xor opera
- *      tions for all combinations of data and CRC register values
- *
- *      The values must be right-shifted by eight bits by the "updcrc
- *      logic; the shift must be unsigned (bring in zeroes).  On some
- *      hardware you could probably optimize the shift in assembler by
- *      using byte-swap instructions
- *      polynomial $edb88320
- *
- *
- * CRC32 code derived from work by Gary S. Brown.
- */
-
-/* Code taken from FreeBSD 8 */
-#include <stdint.h>
-
-static uint32_t crc32_tab[] = {
-        0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
-        0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
-        0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
-        0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
-        0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
-        0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
-        0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
-        0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
-        0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
-        0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
-        0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
-        0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
-        0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
-        0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
-        0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
-        0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
-        0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
-        0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
-        0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
-        0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
-        0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
-        0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
-        0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
-        0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
-        0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
-        0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
-        0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
-        0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
-        0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
-        0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
-        0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
-        0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
-        0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
-        0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
-        0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
-        0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
-        0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
-        0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
-        0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
-        0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
-        0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
-        0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
-        0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
-};
-
-/*
- * A function that calculates the CRC-32 based on the table above is
- * given below for documentation purposes. An equivalent implementation
- * of this function that's actually used in the kernel can be found
- * in sys/libkern.h, where it can be inlined.
- */
-
-uint32_t sparse_crc32(uint32_t crc_in, const void *buf, int size)
-{
-        const uint8_t *p = buf;
-        uint32_t crc;
-
-        crc = crc_in ^ ~0U;
-        while (size--)
-                crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
-        return crc ^ ~0U;
-}
-
diff --git a/libsparse/sparse_crc32.cpp b/libsparse/sparse_crc32.cpp
new file mode 100644
index 0000000..267322c
--- /dev/null
+++ b/libsparse/sparse_crc32.cpp
@@ -0,0 +1,97 @@
+/*-
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ */
+
+/*
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera
+ *      tions for all combinations of data and CRC register values
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions
+ *      polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+/* Code taken from FreeBSD 8 */
+#include <stdint.h>
+#include <stdio.h>
+
+static uint32_t crc32_tab[] = {
+    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
+
+/*
+ * A function that calculates the CRC-32 based on the table above is
+ * given below for documentation purposes. An equivalent implementation
+ * of this function that's actually used in the kernel can be found
+ * in sys/libkern.h, where it can be inlined.
+ */
+
+uint32_t sparse_crc32(uint32_t crc_in, const void* buf, size_t size) {
+  const uint8_t* p = reinterpret_cast<const uint8_t*>(buf);
+  uint32_t crc;
+
+  crc = crc_in ^ ~0U;
+  while (size--) crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+  return crc ^ ~0U;
+}
diff --git a/libsparse/sparse_crc32.h b/libsparse/sparse_crc32.h
index 50cd9e9..2702c4f 100644
--- a/libsparse/sparse_crc32.h
+++ b/libsparse/sparse_crc32.h
@@ -19,14 +19,6 @@
 
 #include <stdint.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size);
-
-#ifdef __cplusplus
-}
-#endif
+uint32_t sparse_crc32(uint32_t crc, const void* buf, size_t size);
 
 #endif
diff --git a/libsparse/sparse_defs.h b/libsparse/sparse_defs.h
index b99cfd5..9137805 100644
--- a/libsparse/sparse_defs.h
+++ b/libsparse/sparse_defs.h
@@ -39,11 +39,14 @@
 typedef unsigned short int u16;
 typedef unsigned char u8;
 
-#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
-#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+#define DIV_ROUND_UP(x, y) (((x) + (y)-1) / (y))
+#define ALIGN(x, y) ((y)*DIV_ROUND_UP((x), (y)))
 #define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
 
-#define error(fmt, args...) do { fprintf(stderr, "error: %s: " fmt "\n", __func__, ## args); } while (0)
+#define error(fmt, args...)                                    \
+  do {                                                         \
+    fprintf(stderr, "error: %s: " fmt "\n", __func__, ##args); \
+  } while (0)
 #define error_errno(s, args...) error(s ": %s", ##args, strerror(errno))
 
 #endif
diff --git a/libsparse/sparse_err.c b/libsparse/sparse_err.cpp
similarity index 73%
rename from libsparse/sparse_err.c
rename to libsparse/sparse_err.cpp
index 0f392ad..6886d31 100644
--- a/libsparse/sparse_err.c
+++ b/libsparse/sparse_err.cpp
@@ -20,14 +20,13 @@
 #include <stdio.h>
 #include <unistd.h>
 
-void sparse_default_print(const char *fmt, ...)
-{
-	va_list argp;
+void sparse_default_print(const char* fmt, ...) {
+  va_list argp;
 
-	va_start(argp, fmt);
-	vfprintf(stderr, fmt, argp);
-	va_end(argp);
+  va_start(argp, fmt);
+  vfprintf(stderr, fmt, argp);
+  va_end(argp);
 }
 
-void (*sparse_print_error)(const char *fmt, ...) = sparse_default_print;
-void (*sparse_print_verbose)(const char *fmt, ...) = sparse_default_print;
+void (*sparse_print_error)(const char* fmt, ...) = sparse_default_print;
+void (*sparse_print_verbose)(const char* fmt, ...) = sparse_default_print;
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 763f43f..e565f63 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -24,12 +24,12 @@
 #include <sparse/sparse.h>
 
 struct sparse_file {
-	unsigned int block_size;
-	int64_t len;
-	bool verbose;
+  unsigned int block_size;
+  int64_t len;
+  bool verbose;
 
-	struct backed_block_list *backed_block_list;
-	struct output_file *out;
+  struct backed_block_list* backed_block_list;
+  struct output_file* out;
 };
 
 #ifdef __cplusplus
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
index 779e038..a8a721e 100644
--- a/libsparse/sparse_format.h
+++ b/libsparse/sparse_format.h
@@ -23,31 +23,31 @@
 #endif
 
 typedef struct sparse_header {
-  __le32	magic;		/* 0xed26ff3a */
-  __le16	major_version;	/* (0x1) - reject images with higher major versions */
-  __le16	minor_version;	/* (0x0) - allow images with higer minor versions */
-  __le16	file_hdr_sz;	/* 28 bytes for first revision of the file format */
-  __le16	chunk_hdr_sz;	/* 12 bytes for first revision of the file format */
-  __le32	blk_sz;		/* block size in bytes, must be a multiple of 4 (4096) */
-  __le32	total_blks;	/* total blocks in the non-sparse output image */
-  __le32	total_chunks;	/* total chunks in the sparse input image */
-  __le32	image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
-				/* as 0. Standard 802.3 polynomial, use a Public Domain */
-				/* table implementation */
+  __le32 magic;          /* 0xed26ff3a */
+  __le16 major_version;  /* (0x1) - reject images with higher major versions */
+  __le16 minor_version;  /* (0x0) - allow images with higer minor versions */
+  __le16 file_hdr_sz;    /* 28 bytes for first revision of the file format */
+  __le16 chunk_hdr_sz;   /* 12 bytes for first revision of the file format */
+  __le32 blk_sz;         /* block size in bytes, must be a multiple of 4 (4096) */
+  __le32 total_blks;     /* total blocks in the non-sparse output image */
+  __le32 total_chunks;   /* total chunks in the sparse input image */
+  __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
+                         /* as 0. Standard 802.3 polynomial, use a Public Domain */
+                         /* table implementation */
 } sparse_header_t;
 
-#define SPARSE_HEADER_MAGIC	0xed26ff3a
+#define SPARSE_HEADER_MAGIC 0xed26ff3a
 
-#define CHUNK_TYPE_RAW		0xCAC1
-#define CHUNK_TYPE_FILL		0xCAC2
-#define CHUNK_TYPE_DONT_CARE	0xCAC3
-#define CHUNK_TYPE_CRC32    0xCAC4
+#define CHUNK_TYPE_RAW 0xCAC1
+#define CHUNK_TYPE_FILL 0xCAC2
+#define CHUNK_TYPE_DONT_CARE 0xCAC3
+#define CHUNK_TYPE_CRC32 0xCAC4
 
 typedef struct chunk_header {
-  __le16	chunk_type;	/* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
-  __le16	reserved1;
-  __le32	chunk_sz;	/* in blocks in output image */
-  __le32	total_sz;	/* in bytes of chunk input file including chunk header and data */
+  __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
+  __le16 reserved1;
+  __le32 chunk_sz; /* in blocks in output image */
+  __le32 total_sz; /* in bytes of chunk input file including chunk header and data */
 } chunk_header_t;
 
 /* Following a Raw or Fill or CRC32 chunk is data.
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 4379635..56e2c9a 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -17,16 +17,16 @@
 #define _FILE_OFFSET_BITS 64
 #define _LARGEFILE64_SOURCE 1
 
-#include <algorithm>
-#include <inttypes.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <string>
 #include <unistd.h>
+#include <algorithm>
+#include <string>
 
 #include <sparse/sparse.h>
 
@@ -37,447 +37,541 @@
 #include "sparse_file.h"
 #include "sparse_format.h"
 
-
 #if defined(__APPLE__) && defined(__MACH__)
 #define lseek64 lseek
 #define off64_t off_t
 #endif
 
 #define SPARSE_HEADER_MAJOR_VER 1
-#define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
+#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
 #define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
 
 static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
-static char *copybuf;
+static char* copybuf;
 
-static std::string ErrorString(int err)
-{
-	if (err == -EOVERFLOW) return "EOF while reading file";
-	if (err == -EINVAL) return "Invalid sparse file format";
-	if (err == -ENOMEM) return "Failed allocation while reading file";
-	return android::base::StringPrintf("Unknown error %d", err);
+static std::string ErrorString(int err) {
+  if (err == -EOVERFLOW) return "EOF while reading file";
+  if (err == -EINVAL) return "Invalid sparse file format";
+  if (err == -ENOMEM) return "Failed allocation while reading file";
+  return android::base::StringPrintf("Unknown error %d", err);
 }
 
-static void verbose_error(bool verbose, int err, const char *fmt, ...)
-{
-	if (!verbose) return;
+class SparseFileSource {
+ public:
+  /* Seeks the source ahead by the given offset. */
+  virtual void Seek(int64_t offset) = 0;
 
-	std::string msg = ErrorString(err);
-	if (fmt) {
-		msg += " at ";
-		va_list argp;
-		va_start(argp, fmt);
-		android::base::StringAppendV(&msg, fmt, argp);
-		va_end(argp);
-	}
-	sparse_print_verbose("%s\n", msg.c_str());
+  /* Return the current offset. */
+  virtual int64_t GetOffset() = 0;
+
+  /* Set the current offset. Return 0 if successful. */
+  virtual int SetOffset(int64_t offset) = 0;
+
+  /* Adds the given length from the current offset of the source to the file at the given block.
+   * Return 0 if successful. */
+  virtual int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) = 0;
+
+  /* Get data of fixed size from the current offset and seek len bytes. Return 0 if successful. */
+  virtual int ReadValue(void* ptr, int len) = 0;
+
+  /* Find the crc32 of the next len bytes and seek ahead len bytes. Return 0 if successful. */
+  virtual int GetCrc32(uint32_t* crc32, int64_t len) = 0;
+
+  virtual ~SparseFileSource(){};
+};
+
+class SparseFileFdSource : public SparseFileSource {
+ private:
+  int fd;
+
+ public:
+  SparseFileFdSource(int fd) : fd(fd) {}
+  ~SparseFileFdSource() override {}
+
+  void Seek(int64_t off) override { lseek64(fd, off, SEEK_CUR); }
+
+  int64_t GetOffset() override { return lseek64(fd, 0, SEEK_CUR); }
+
+  int SetOffset(int64_t offset) override {
+    return lseek64(fd, offset, SEEK_SET) == offset ? 0 : -errno;
+  }
+
+  int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+    return sparse_file_add_fd(s, fd, GetOffset(), len, block);
+  }
+
+  int ReadValue(void* ptr, int len) override { return read_all(fd, ptr, len); }
+
+  int GetCrc32(uint32_t* crc32, int64_t len) override {
+    int chunk;
+    int ret;
+    while (len) {
+      chunk = std::min(len, COPY_BUF_SIZE);
+      ret = read_all(fd, copybuf, chunk);
+      if (ret < 0) {
+        return ret;
+      }
+      *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+      len -= chunk;
+    }
+    return 0;
+  }
+};
+
+class SparseFileBufSource : public SparseFileSource {
+ private:
+  char* buf;
+  int64_t offset;
+
+ public:
+  SparseFileBufSource(char* buf) : buf(buf), offset(0) {}
+  ~SparseFileBufSource() override {}
+
+  void Seek(int64_t off) override {
+    buf += off;
+    offset += off;
+  }
+
+  int64_t GetOffset() override { return offset; }
+
+  int SetOffset(int64_t off) override {
+    buf += off - offset;
+    offset = off;
+    return 0;
+  }
+
+  int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+    return sparse_file_add_data(s, buf, len, block);
+  }
+
+  int ReadValue(void* ptr, int len) override {
+    memcpy(ptr, buf, len);
+    Seek(len);
+    return 0;
+  }
+
+  int GetCrc32(uint32_t* crc32, int64_t len) override {
+    *crc32 = sparse_crc32(*crc32, buf, len);
+    Seek(len);
+    return 0;
+  }
+};
+
+static void verbose_error(bool verbose, int err, const char* fmt, ...) {
+  if (!verbose) return;
+
+  std::string msg = ErrorString(err);
+  if (fmt) {
+    msg += " at ";
+    va_list argp;
+    va_start(argp, fmt);
+    android::base::StringAppendV(&msg, fmt, argp);
+    va_end(argp);
+  }
+  sparse_print_verbose("%s\n", msg.c_str());
 }
 
-static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
-		int fd, int64_t offset, unsigned int blocks, unsigned int block,
-		uint32_t *crc32)
-{
-	int ret;
-	int chunk;
-	int64_t len = blocks * s->block_size;
+static int process_raw_chunk(struct sparse_file* s, unsigned int chunk_size,
+                             SparseFileSource* source, unsigned int blocks, unsigned int block,
+                             uint32_t* crc32) {
+  int ret;
+  int64_t len = blocks * s->block_size;
 
-	if (chunk_size % s->block_size != 0) {
-		return -EINVAL;
-	}
+  if (chunk_size % s->block_size != 0) {
+    return -EINVAL;
+  }
 
-	if (chunk_size / s->block_size != blocks) {
-		return -EINVAL;
-	}
+  if (chunk_size / s->block_size != blocks) {
+    return -EINVAL;
+  }
 
-	ret = sparse_file_add_fd(s, fd, offset, len, block);
-	if (ret < 0) {
-		return ret;
-	}
+  ret = source->AddToSparseFile(s, len, block);
+  if (ret < 0) {
+    return ret;
+  }
 
-	if (crc32) {
-		while (len) {
-			chunk = std::min(len, COPY_BUF_SIZE);
-			ret = read_all(fd, copybuf, chunk);
-			if (ret < 0) {
-				return ret;
-			}
-			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
-			len -= chunk;
-		}
-	} else {
-		lseek64(fd, len, SEEK_CUR);
-	}
+  if (crc32) {
+    ret = source->GetCrc32(crc32, len);
+    if (ret < 0) {
+      return ret;
+    }
+  } else {
+    source->Seek(len);
+  }
 
-	return 0;
+  return 0;
 }
 
-static int process_fill_chunk(struct sparse_file *s, unsigned int chunk_size,
-		int fd, unsigned int blocks, unsigned int block, uint32_t *crc32)
-{
-	int ret;
-	int chunk;
-	int64_t len = (int64_t)blocks * s->block_size;
-	uint32_t fill_val;
-	uint32_t *fillbuf;
-	unsigned int i;
+static int process_fill_chunk(struct sparse_file* s, unsigned int chunk_size,
+                              SparseFileSource* source, unsigned int blocks, unsigned int block,
+                              uint32_t* crc32) {
+  int ret;
+  int chunk;
+  int64_t len = (int64_t)blocks * s->block_size;
+  uint32_t fill_val;
+  uint32_t* fillbuf;
+  unsigned int i;
 
-	if (chunk_size != sizeof(fill_val)) {
-		return -EINVAL;
-	}
+  if (chunk_size != sizeof(fill_val)) {
+    return -EINVAL;
+  }
 
-	ret = read_all(fd, &fill_val, sizeof(fill_val));
-	if (ret < 0) {
-		return ret;
-	}
+  ret = source->ReadValue(&fill_val, sizeof(fill_val));
+  if (ret < 0) {
+    return ret;
+  }
 
-	ret = sparse_file_add_fill(s, fill_val, len, block);
-	if (ret < 0) {
-		return ret;
-	}
+  ret = sparse_file_add_fill(s, fill_val, len, block);
+  if (ret < 0) {
+    return ret;
+  }
 
-	if (crc32) {
-		/* Fill copy_buf with the fill value */
-		fillbuf = (uint32_t *)copybuf;
-		for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
-			fillbuf[i] = fill_val;
-		}
+  if (crc32) {
+    /* Fill copy_buf with the fill value */
+    fillbuf = (uint32_t*)copybuf;
+    for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
+      fillbuf[i] = fill_val;
+    }
 
-		while (len) {
-			chunk = std::min(len, COPY_BUF_SIZE);
-			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
-			len -= chunk;
-		}
-	}
+    while (len) {
+      chunk = std::min(len, COPY_BUF_SIZE);
+      *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+      len -= chunk;
+    }
+  }
 
-	return 0;
+  return 0;
 }
 
-static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
-		int fd __unused, unsigned int blocks,
-		unsigned int block __unused, uint32_t *crc32)
-{
-	if (chunk_size != 0) {
-		return -EINVAL;
-	}
+static int process_skip_chunk(struct sparse_file* s, unsigned int chunk_size,
+                              SparseFileSource* source __unused, unsigned int blocks,
+                              unsigned int block __unused, uint32_t* crc32) {
+  if (chunk_size != 0) {
+    return -EINVAL;
+  }
 
-	if (crc32) {
-	        int64_t len = (int64_t)blocks * s->block_size;
-		memset(copybuf, 0, COPY_BUF_SIZE);
+  if (crc32) {
+    int64_t len = (int64_t)blocks * s->block_size;
+    memset(copybuf, 0, COPY_BUF_SIZE);
 
-		while (len) {
-			int chunk = std::min(len, COPY_BUF_SIZE);
-			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
-			len -= chunk;
-		}
-	}
+    while (len) {
+      int chunk = std::min(len, COPY_BUF_SIZE);
+      *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+      len -= chunk;
+    }
+  }
 
-	return 0;
+  return 0;
 }
 
-static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t *crc32)
-{
-	uint32_t file_crc32;
-	int ret;
+static int process_crc32_chunk(SparseFileSource* source, unsigned int chunk_size, uint32_t* crc32) {
+  uint32_t file_crc32;
 
-	if (chunk_size != sizeof(file_crc32)) {
-		return -EINVAL;
-	}
+  if (chunk_size != sizeof(file_crc32)) {
+    return -EINVAL;
+  }
 
-	ret = read_all(fd, &file_crc32, sizeof(file_crc32));
-	if (ret < 0) {
-		return ret;
-	}
+  int ret = source->ReadValue(&file_crc32, sizeof(file_crc32));
+  if (ret < 0) {
+    return ret;
+  }
 
-	if (crc32 != NULL && file_crc32 != *crc32) {
-		return -EINVAL;
-	}
+  if (crc32 != NULL && file_crc32 != *crc32) {
+    return -EINVAL;
+  }
 
-	return 0;
+  return 0;
 }
 
-static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
-		unsigned int chunk_hdr_sz, chunk_header_t *chunk_header,
-		unsigned int cur_block, uint32_t *crc_ptr)
-{
-	int ret;
-	unsigned int chunk_data_size;
+static int process_chunk(struct sparse_file* s, SparseFileSource* source, unsigned int chunk_hdr_sz,
+                         chunk_header_t* chunk_header, unsigned int cur_block, uint32_t* crc_ptr) {
+  int ret;
+  unsigned int chunk_data_size;
+  int64_t offset = source->GetOffset();
 
-	chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
+  chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
 
-	switch (chunk_header->chunk_type) {
-		case CHUNK_TYPE_RAW:
-			ret = process_raw_chunk(s, chunk_data_size, fd, offset,
-					chunk_header->chunk_sz, cur_block, crc_ptr);
-			if (ret < 0) {
-				verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
-				return ret;
-			}
-			return chunk_header->chunk_sz;
-		case CHUNK_TYPE_FILL:
-			ret = process_fill_chunk(s, chunk_data_size, fd,
-					chunk_header->chunk_sz, cur_block, crc_ptr);
-			if (ret < 0) {
-				verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
-				return ret;
-			}
-			return chunk_header->chunk_sz;
-		case CHUNK_TYPE_DONT_CARE:
-			ret = process_skip_chunk(s, chunk_data_size, fd,
-					chunk_header->chunk_sz, cur_block, crc_ptr);
-			if (chunk_data_size != 0) {
-				if (ret < 0) {
-					verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
-					return ret;
-				}
-			}
-			return chunk_header->chunk_sz;
-		case CHUNK_TYPE_CRC32:
-			ret = process_crc32_chunk(fd, chunk_data_size, crc_ptr);
-			if (ret < 0) {
-				verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
-						offset);
-				return ret;
-			}
-			return 0;
-		default:
-			verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
-					chunk_header->chunk_type, offset);
-	}
+  switch (chunk_header->chunk_type) {
+    case CHUNK_TYPE_RAW:
+      ret =
+          process_raw_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block, crc_ptr);
+      if (ret < 0) {
+        verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
+        return ret;
+      }
+      return chunk_header->chunk_sz;
+    case CHUNK_TYPE_FILL:
+      ret = process_fill_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
+                               crc_ptr);
+      if (ret < 0) {
+        verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
+        return ret;
+      }
+      return chunk_header->chunk_sz;
+    case CHUNK_TYPE_DONT_CARE:
+      ret = process_skip_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
+                               crc_ptr);
+      if (chunk_data_size != 0) {
+        if (ret < 0) {
+          verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
+          return ret;
+        }
+      }
+      return chunk_header->chunk_sz;
+    case CHUNK_TYPE_CRC32:
+      ret = process_crc32_chunk(source, chunk_data_size, crc_ptr);
+      if (ret < 0) {
+        verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64, offset);
+        return ret;
+      }
+      return 0;
+    default:
+      verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64, chunk_header->chunk_type,
+                    offset);
+  }
 
-	return 0;
+  return 0;
 }
 
-static int sparse_file_read_sparse(struct sparse_file *s, int fd, bool crc)
-{
-	int ret;
-	unsigned int i;
-	sparse_header_t sparse_header;
-	chunk_header_t chunk_header;
-	uint32_t crc32 = 0;
-	uint32_t *crc_ptr = 0;
-	unsigned int cur_block = 0;
-	off64_t offset;
+static int sparse_file_read_sparse(struct sparse_file* s, SparseFileSource* source, bool crc) {
+  int ret;
+  unsigned int i;
+  sparse_header_t sparse_header;
+  chunk_header_t chunk_header;
+  uint32_t crc32 = 0;
+  uint32_t* crc_ptr = 0;
+  unsigned int cur_block = 0;
 
-	if (!copybuf) {
-		copybuf = (char *)malloc(COPY_BUF_SIZE);
-	}
+  if (!copybuf) {
+    copybuf = (char*)malloc(COPY_BUF_SIZE);
+  }
 
-	if (!copybuf) {
-		return -ENOMEM;
-	}
+  if (!copybuf) {
+    return -ENOMEM;
+  }
 
-	if (crc) {
-		crc_ptr = &crc32;
-	}
+  if (crc) {
+    crc_ptr = &crc32;
+  }
 
-	ret = read_all(fd, &sparse_header, sizeof(sparse_header));
-	if (ret < 0) {
-		return ret;
-	}
+  ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
+  if (ret < 0) {
+    return ret;
+  }
 
-	if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
-		return -EINVAL;
-	}
+  if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
-		return -EINVAL;
-	}
+  if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
-		return -EINVAL;
-	}
+  if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
-		return -EINVAL;
-	}
+  if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
-		/* Skip the remaining bytes in a header that is longer than
-		 * we expected.
-		 */
-		lseek64(fd, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
-	}
+  if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
+    /* Skip the remaining bytes in a header that is longer than
+     * we expected.
+     */
+    source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);
+  }
 
-	for (i = 0; i < sparse_header.total_chunks; i++) {
-		ret = read_all(fd, &chunk_header, sizeof(chunk_header));
-		if (ret < 0) {
-			return ret;
-		}
+  for (i = 0; i < sparse_header.total_chunks; i++) {
+    ret = source->ReadValue(&chunk_header, sizeof(chunk_header));
+    if (ret < 0) {
+      return ret;
+    }
 
-		if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
-			/* Skip the remaining bytes in a header that is longer than
-			 * we expected.
-			 */
-			lseek64(fd, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
-		}
+    if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
+      /* Skip the remaining bytes in a header that is longer than
+       * we expected.
+       */
+      source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);
+    }
 
-		offset = lseek64(fd, 0, SEEK_CUR);
+    ret = process_chunk(s, source, sparse_header.chunk_hdr_sz, &chunk_header, cur_block, crc_ptr);
+    if (ret < 0) {
+      return ret;
+    }
 
-		ret = process_chunk(s, fd, offset, sparse_header.chunk_hdr_sz, &chunk_header,
-				cur_block, crc_ptr);
-		if (ret < 0) {
-			return ret;
-		}
+    cur_block += ret;
+  }
 
-		cur_block += ret;
-	}
+  if (sparse_header.total_blks != cur_block) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.total_blks != cur_block) {
-		return -EINVAL;
-	}
-
-	return 0;
+  return 0;
 }
 
-static int sparse_file_read_normal(struct sparse_file *s, int fd)
-{
-	int ret;
-	uint32_t *buf = (uint32_t *)malloc(s->block_size);
-	unsigned int block = 0;
-	int64_t remain = s->len;
-	int64_t offset = 0;
-	unsigned int to_read;
-	unsigned int i;
-	bool sparse_block;
+static int sparse_file_read_normal(struct sparse_file* s, int fd) {
+  int ret;
+  uint32_t* buf = (uint32_t*)malloc(s->block_size);
+  unsigned int block = 0;
+  int64_t remain = s->len;
+  int64_t offset = 0;
+  unsigned int to_read;
+  unsigned int i;
+  bool sparse_block;
 
-	if (!buf) {
-		return -ENOMEM;
-	}
+  if (!buf) {
+    return -ENOMEM;
+  }
 
-	while (remain > 0) {
-		to_read = std::min(remain, (int64_t)(s->block_size));
-		ret = read_all(fd, buf, to_read);
-		if (ret < 0) {
-			error("failed to read sparse file");
-			free(buf);
-			return ret;
-		}
+  while (remain > 0) {
+    to_read = std::min(remain, (int64_t)(s->block_size));
+    ret = read_all(fd, buf, to_read);
+    if (ret < 0) {
+      error("failed to read sparse file");
+      free(buf);
+      return ret;
+    }
 
-		if (to_read == s->block_size) {
-			sparse_block = true;
-			for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
-				if (buf[0] != buf[i]) {
-					sparse_block = false;
-					break;
-				}
-			}
-		} else {
-			sparse_block = false;
-		}
+    if (to_read == s->block_size) {
+      sparse_block = true;
+      for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
+        if (buf[0] != buf[i]) {
+          sparse_block = false;
+          break;
+        }
+      }
+    } else {
+      sparse_block = false;
+    }
 
-		if (sparse_block) {
-			/* TODO: add flag to use skip instead of fill for buf[0] == 0 */
-			sparse_file_add_fill(s, buf[0], to_read, block);
-		} else {
-			sparse_file_add_fd(s, fd, offset, to_read, block);
-		}
+    if (sparse_block) {
+      /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
+      sparse_file_add_fill(s, buf[0], to_read, block);
+    } else {
+      sparse_file_add_fd(s, fd, offset, to_read, block);
+    }
 
-		remain -= to_read;
-		offset += to_read;
-		block++;
-	}
+    remain -= to_read;
+    offset += to_read;
+    block++;
+  }
 
-	free(buf);
-	return 0;
+  free(buf);
+  return 0;
 }
 
-int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc)
-{
-	if (crc && !sparse) {
-		return -EINVAL;
-	}
+int sparse_file_read(struct sparse_file* s, int fd, bool sparse, bool crc) {
+  if (crc && !sparse) {
+    return -EINVAL;
+  }
 
-	if (sparse) {
-		return sparse_file_read_sparse(s, fd, crc);
-	} else {
-		return sparse_file_read_normal(s, fd);
-	}
+  if (sparse) {
+    SparseFileFdSource source(fd);
+    return sparse_file_read_sparse(s, &source, crc);
+  } else {
+    return sparse_file_read_normal(s, fd);
+  }
 }
 
-struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
-{
-	int ret;
-	sparse_header_t sparse_header;
-	int64_t len;
-	struct sparse_file *s;
-
-	ret = read_all(fd, &sparse_header, sizeof(sparse_header));
-	if (ret < 0) {
-		verbose_error(verbose, ret, "header");
-		return NULL;
-	}
-
-	if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
-		verbose_error(verbose, -EINVAL, "header magic");
-		return NULL;
-	}
-
-	if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
-		verbose_error(verbose, -EINVAL, "header major version");
-		return NULL;
-	}
-
-	if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
-		return NULL;
-	}
-
-	if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
-		return NULL;
-	}
-
-	len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
-	s = sparse_file_new(sparse_header.blk_sz, len);
-	if (!s) {
-		verbose_error(verbose, -EINVAL, NULL);
-		return NULL;
-	}
-
-	ret = lseek64(fd, 0, SEEK_SET);
-	if (ret < 0) {
-		verbose_error(verbose, ret, "seeking");
-		sparse_file_destroy(s);
-		return NULL;
-	}
-
-	s->verbose = verbose;
-
-	ret = sparse_file_read(s, fd, true, crc);
-	if (ret < 0) {
-		sparse_file_destroy(s);
-		return NULL;
-	}
-
-	return s;
+int sparse_file_read_buf(struct sparse_file* s, char* buf, bool crc) {
+  SparseFileBufSource source(buf);
+  return sparse_file_read_sparse(s, &source, crc);
 }
 
-struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose)
-{
-	struct sparse_file *s;
-	int64_t len;
-	int ret;
+static struct sparse_file* sparse_file_import_source(SparseFileSource* source, bool verbose,
+                                                     bool crc) {
+  int ret;
+  sparse_header_t sparse_header;
+  int64_t len;
+  struct sparse_file* s;
 
-	s = sparse_file_import(fd, verbose, crc);
-	if (s) {
-		return s;
-	}
+  ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
+  if (ret < 0) {
+    verbose_error(verbose, ret, "header");
+    return NULL;
+  }
 
-	len = lseek64(fd, 0, SEEK_END);
-	if (len < 0) {
-		return NULL;
-	}
+  if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+    verbose_error(verbose, -EINVAL, "header magic");
+    return NULL;
+  }
 
-	lseek64(fd, 0, SEEK_SET);
+  if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+    verbose_error(verbose, -EINVAL, "header major version");
+    return NULL;
+  }
 
-	s = sparse_file_new(4096, len);
-	if (!s) {
-		return NULL;
-	}
+  if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+    return NULL;
+  }
 
-	ret = sparse_file_read_normal(s, fd);
-	if (ret < 0) {
-		sparse_file_destroy(s);
-		return NULL;
-	}
+  if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
+    return NULL;
+  }
 
-	return s;
+  len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
+  s = sparse_file_new(sparse_header.blk_sz, len);
+  if (!s) {
+    verbose_error(verbose, -EINVAL, NULL);
+    return NULL;
+  }
+
+  ret = source->SetOffset(0);
+  if (ret < 0) {
+    verbose_error(verbose, ret, "seeking");
+    sparse_file_destroy(s);
+    return NULL;
+  }
+
+  s->verbose = verbose;
+
+  ret = sparse_file_read_sparse(s, source, crc);
+  if (ret < 0) {
+    sparse_file_destroy(s);
+    return NULL;
+  }
+
+  return s;
+}
+
+struct sparse_file* sparse_file_import(int fd, bool verbose, bool crc) {
+  SparseFileFdSource source(fd);
+  return sparse_file_import_source(&source, verbose, crc);
+}
+
+struct sparse_file* sparse_file_import_buf(char* buf, bool verbose, bool crc) {
+  SparseFileBufSource source(buf);
+  return sparse_file_import_source(&source, verbose, crc);
+}
+
+struct sparse_file* sparse_file_import_auto(int fd, bool crc, bool verbose) {
+  struct sparse_file* s;
+  int64_t len;
+  int ret;
+
+  s = sparse_file_import(fd, verbose, crc);
+  if (s) {
+    return s;
+  }
+
+  len = lseek64(fd, 0, SEEK_END);
+  if (len < 0) {
+    return NULL;
+  }
+
+  lseek64(fd, 0, SEEK_SET);
+
+  s = sparse_file_new(4096, len);
+  if (!s) {
+    return NULL;
+  }
+
+  ret = sparse_file_read_normal(s, fd);
+  if (ret < 0) {
+    sparse_file_destroy(s);
+    return NULL;
+  }
+
+  return s;
 }
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 3fae5e6..c95563d 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -40,13 +40,6 @@
 }
 
 cc_test {
-    name: "sync_test",
-    defaults: ["libsync_defaults"],
-    gtest: false,
-    srcs: ["sync_test.c"],
-}
-
-cc_test {
     name: "sync-unit-tests",
     shared_libs: ["libsync"],
     srcs: ["tests/sync_test.cpp"],
diff --git a/libsync/OWNERS b/libsync/OWNERS
new file mode 100644
index 0000000..dc61733
--- /dev/null
+++ b/libsync/OWNERS
@@ -0,0 +1,3 @@
+ghackmann@google.com
+jessehall@google.com
+marissaw@google.com
diff --git a/libsync/include/android/sync.h b/libsync/include/android/sync.h
index 68f74a0..32bb878 100644
--- a/libsync/include/android/sync.h
+++ b/libsync/include/android/sync.h
@@ -41,28 +41,8 @@
 
 __BEGIN_DECLS
 
-struct sync_fence_info_data {
- uint32_t len;
- char name[32];
- int32_t status;
- uint8_t pt_info[0];
-};
-
-struct sync_pt_info {
- uint32_t len;
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint64_t timestamp_ns;
- uint8_t driver_data[0];
-};
-
 /* timeout in msecs */
 int sync_wait(int fd, int timeout);
-struct sync_fence_info_data *sync_fence_info(int fd);
-struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
-                                  struct sync_pt_info *itr);
-void sync_fence_info_free(struct sync_fence_info_data *info);
 
 __END_DECLS
 
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
index a786d3e..49f01e1 100644
--- a/libsync/include/ndk/sync.h
+++ b/libsync/include/ndk/sync.h
@@ -32,8 +32,6 @@
 
 __BEGIN_DECLS
 
-#if __ANDROID_API__ >= __ANDROID_API_O__
-
 /* Fences indicate the status of an asynchronous task. They are initially
  * in unsignaled state (0), and make a one-time transition to either signaled
  * (1) or error (< 0) state. A sync file is a collection of one or more fences;
@@ -63,14 +61,14 @@
  * The original fences remain valid, and the caller is responsible for closing
  * them.
  */
-int32_t sync_merge(const char *name, int32_t fd1, int32_t fd2);
+int32_t sync_merge(const char* name, int32_t fd1, int32_t fd2) __INTRODUCED_IN(26);
 
 /**
  * Retrieve detailed information about a sync file and its fences.
  *
  * The returned sync_file_info must be freed by calling sync_file_info_free().
  */
-struct sync_file_info *sync_file_info(int32_t fd);
+struct sync_file_info* sync_file_info(int32_t fd) __INTRODUCED_IN(26);
 
 /**
  * Get the array of fence infos from the sync file's info.
@@ -88,9 +86,7 @@
 }
 
 /** Free a struct sync_file_info structure */
-void sync_file_info_free(struct sync_file_info *info);
-
-#endif // __ANDROID_API__ >= __ANDROID_API_O__
+void sync_file_info_free(struct sync_file_info* info) __INTRODUCED_IN(26);
 
 __END_DECLS
 
diff --git a/libsync/sync.c b/libsync/sync.c
index 6b187fa..b8c48c7 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -30,6 +30,29 @@
 
 #include <android/sync.h>
 
+/* Prototypes for deprecated functions that used to be declared in the legacy
+ * android/sync.h. They've been moved here to make sure new code does not use
+ * them, but the functions are still defined to avoid breaking existing
+ * binaries. Eventually they can be removed altogether.
+ */
+struct sync_fence_info_data {
+    uint32_t len;
+    char name[32];
+    int32_t status;
+    uint8_t pt_info[0];
+};
+struct sync_pt_info {
+    uint32_t len;
+    char obj_name[32];
+    char driver_name[32];
+    int32_t status;
+    uint64_t timestamp_ns;
+    uint8_t driver_data[0];
+};
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
 /* Legacy Sync API */
 
 struct sync_legacy_merge_data {
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
deleted file mode 100644
index f1ffdcf..0000000
--- a/libsync/sync_test.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- *  sync_test.c
- *
- *   Copyright 2012 Google, Inc
- *
- *  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 <pthread.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <android/sync.h>
-#include "sw_sync.h"
-
-pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-struct sync_thread_data {
-    int thread_no;
-    int fd[2];
-};
-
-void *sync_thread(void *data)
-{
-    struct sync_thread_data *sync_data = data;
-    struct sync_fence_info_data *info;
-    int err;
-    int i;
-
-    for (i = 0; i < 2; i++) {
-        err = sync_wait(sync_data->fd[i], 10000);
-
-        pthread_mutex_lock(&printf_mutex);
-        if (err < 0) {
-            printf("thread %d wait %d failed: %s\n", sync_data->thread_no,
-                   i, strerror(errno));
-        } else {
-            printf("thread %d wait %d done\n", sync_data->thread_no, i);
-        }
-        info = sync_fence_info(sync_data->fd[i]);
-        if (info) {
-            struct sync_pt_info *pt_info = NULL;
-            printf("  fence %s %d\n", info->name, info->status);
-
-            while ((pt_info = sync_pt_info(info, pt_info))) {
-                int ts_sec = pt_info->timestamp_ns / 1000000000LL;
-                int ts_usec = (pt_info->timestamp_ns % 1000000000LL) / 1000LL;
-                printf("    pt %s %s %d %d.%06d", pt_info->obj_name,
-                       pt_info->driver_name, pt_info->status,
-                       ts_sec, ts_usec);
-                if (!strcmp(pt_info->driver_name, "sw_sync"))
-                    printf(" val=%d\n", *(uint32_t *)pt_info->driver_data);
-                else
-                    printf("\n");
-            }
-            sync_fence_info_free(info);
-        }
-        pthread_mutex_unlock(&printf_mutex);
-    }
-
-    return NULL;
-}
-
-int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
-{
-    struct sync_thread_data sync_data[4];
-    pthread_t threads[4];
-    int sync_timeline_fd;
-    int i, j;
-    char str[256];
-
-    sync_timeline_fd = sw_sync_timeline_create();
-    if (sync_timeline_fd < 0) {
-        perror("can't create sw_sync_timeline:");
-        return 1;
-    }
-
-    for (i = 0; i < 3; i++) {
-        sync_data[i].thread_no = i;
-
-        for (j = 0; j < 2; j++) {
-            unsigned val = i + j * 3 + 1;
-            snprintf(str, sizeof(str), "test_fence%d-%d", i, j);
-            int fd = sw_sync_fence_create(sync_timeline_fd, str, val);
-            if (fd < 0) {
-                printf("can't create sync pt %d: %s", val, strerror(errno));
-                return 1;
-            }
-            sync_data[i].fd[j] = fd;
-            printf("sync_data[%d].fd[%d] = %d;\n", i, j, fd);
-
-        }
-    }
-
-    sync_data[3].thread_no = 3;
-    for (j = 0; j < 2; j++) {
-        snprintf(str, sizeof(str), "merged_fence%d", j);
-        sync_data[3].fd[j] = sync_merge(str, sync_data[0].fd[j], sync_data[1].fd[j]);
-        if (sync_data[3].fd[j] < 0) {
-            printf("can't merge sync pts %d and %d: %s\n",
-                   sync_data[0].fd[j], sync_data[1].fd[j], strerror(errno));
-            return 1;
-        }
-    }
-
-    for (i = 0; i < 4; i++)
-        pthread_create(&threads[i], NULL, sync_thread, &sync_data[i]);
-
-
-    for (i = 0; i < 3; i++) {
-        int err;
-        printf("press enter to inc to %d\n", i+1);
-        fgets(str, sizeof(str), stdin);
-        err = sw_sync_timeline_inc(sync_timeline_fd, 1);
-        if (err < 0) {
-            perror("can't increment sync obj:");
-            return 1;
-        }
-    }
-
-    printf("press enter to close sync_timeline\n");
-    fgets(str, sizeof(str), stdin);
-
-    close(sync_timeline_fd);
-
-    printf("press enter to end test\n");
-    fgets(str, sizeof(str), stdin);
-
-    for (i = 0; i < 3; i++) {
-        void *val;
-        pthread_join(threads[i], &val);
-    }
-
-    return 0;
-}
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 0fb86d6..011b09d 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -15,6 +15,35 @@
 #include <random>
 #include <unordered_map>
 
+/* These deprecated declarations were in the legacy android/sync.h. They've been removed to
+ * encourage code to move to the modern equivalents. But they are still implemented in libsync.so
+ * to avoid breaking existing binaries; as long as that's true we should keep testing them here.
+ * That means making local copies of the declarations.
+ */
+extern "C" {
+
+struct sync_fence_info_data {
+    uint32_t len;
+    char name[32];
+    int32_t status;
+    uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+    uint32_t len;
+    char obj_name[32];
+    char driver_name[32];
+    int32_t status;
+    uint64_t timestamp_ns;
+    uint8_t driver_data[0];
+};
+
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
+}  // extern "C"
+
 // TODO: better stress tests?
 // Handle more than 64 fd's simultaneously, i.e. fix sync_fence_info's 4k limit.
 // Handle wraparound in timelines like nvidia.
diff --git a/libsysutils/include/sysutils/FrameworkClient.h b/libsysutils/include/sysutils/FrameworkClient.h
deleted file mode 100644
index 4a3f0de..0000000
--- a/libsysutils/include/sysutils/FrameworkClient.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef _FRAMEWORK_CLIENT_H
-#define _FRAMEWORK_CLIENT_H
-
-#include "List.h"
-
-#include <pthread.h>
-
-class FrameworkClient {
-    int             mSocket;
-    pthread_mutex_t mWriteMutex;
-
-public:
-    FrameworkClient(int sock);
-    virtual ~FrameworkClient() {}
-
-    int sendMsg(const char *msg);
-    int sendMsg(const char *msg, const char *data);
-};
-
-typedef android::sysutils::List<FrameworkClient *> FrameworkClientCollection;
-#endif
diff --git a/libsysutils/include/sysutils/OWNERS b/libsysutils/include/sysutils/OWNERS
new file mode 100644
index 0000000..b78918e
--- /dev/null
+++ b/libsysutils/include/sysutils/OWNERS
@@ -0,0 +1,2 @@
+per-file Netlink* = ek@google.com
+per-file Netlink* = lorenzo@google.com
diff --git a/libsysutils/src/FrameworkClient.cpp b/libsysutils/src/FrameworkClient.cpp
deleted file mode 100644
index 72b3d0a..0000000
--- a/libsysutils/src/FrameworkClient.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2009-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.
- */
-
-#define LOG_TAG "FrameworkClient"
-
-#include <alloca.h>
-#include <errno.h>
-#include <pthread.h>
-#include <sys/types.h>
-
-#include <android/log.h>
-#include <sysutils/FrameworkClient.h>
-
-FrameworkClient::FrameworkClient(int socket) {
-    mSocket = socket;
-    pthread_mutex_init(&mWriteMutex, NULL);
-}
-
-int FrameworkClient::sendMsg(const char *msg) {
-    int ret;
-    if (mSocket < 0) {
-        errno = EHOSTUNREACH;
-        return -1;
-    }
-
-    pthread_mutex_lock(&mWriteMutex);
-    ret = TEMP_FAILURE_RETRY(write(mSocket, msg, strlen(msg) +1));
-    if (ret < 0) {
-        SLOGW("Unable to send msg '%s' (%s)", msg, strerror(errno));
-    }
-    pthread_mutex_unlock(&mWriteMutex);
-    return 0;
-}
-
-int FrameworkClient::sendMsg(const char *msg, const char *data) {
-    size_t bufflen = strlen(msg) + strlen(data) + 1;
-    char *buffer = (char *) alloca(bufflen);
-    if (!buffer) {
-        errno = -ENOMEM;
-        return -1;
-    }
-    snprintf(buffer, bufflen, "%s%s", msg, data);
-    return sendMsg(buffer);
-}
-
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 35a3063..f0c66ec 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -239,12 +239,13 @@
     asprintf(&mParams[1], "INTERFACE=%s", ifname);
     asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
     asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
+    asprintf(&mParams[4], "IFINDEX=%u", ifaddr->ifa_index);
 
     if (cacheinfo) {
-        asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered);
-        asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid);
-        asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp);
-        asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp);
+        asprintf(&mParams[5], "PREFERRED=%u", cacheinfo->ifa_prefered);
+        asprintf(&mParams[6], "VALID=%u", cacheinfo->ifa_valid);
+        asprintf(&mParams[7], "CSTAMP=%u", cacheinfo->cstamp);
+        asprintf(&mParams[8], "TSTAMP=%u", cacheinfo->tstamp);
     }
 
     return true;
diff --git a/libsysutils/src/OWNERS b/libsysutils/src/OWNERS
new file mode 100644
index 0000000..b78918e
--- /dev/null
+++ b/libsysutils/src/OWNERS
@@ -0,0 +1,2 @@
+per-file Netlink* = ek@google.com
+per-file Netlink* = lorenzo@google.com
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index a6bf730..df5da65 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -38,6 +38,7 @@
 cc_library {
     name: "libunwindstack",
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -62,6 +63,7 @@
         "MapInfo.cpp",
         "Maps.cpp",
         "Memory.cpp",
+        "LocalUnwinder.cpp",
         "Regs.cpp",
         "RegsArm.cpp",
         "RegsArm64.cpp",
@@ -93,6 +95,14 @@
             ],
             exclude_shared_libs: ["libdexfile"],
         },
+        recovery: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            exclude_srcs: [
+                "DexFile.cpp",
+                "DexFiles.cpp",
+            ],
+            exclude_shared_libs: ["libdexfile"],
+        },
     },
 
     arch: {
@@ -125,6 +135,21 @@
 //-------------------------------------------------------------------------
 // Unit Tests
 //-------------------------------------------------------------------------
+cc_test_library {
+    name: "libunwindstack_local",
+    defaults: ["libunwindstack_flags"],
+    srcs: ["tests/TestLocal.cpp"],
+
+    cflags: [
+        "-O0",
+        "-g",
+    ],
+
+    shared_libs: [
+        "libunwindstack",
+    ],
+}
+
 cc_test {
     name: "libunwindstack_test",
     defaults: ["libunwindstack_flags"],
@@ -151,6 +176,7 @@
         "tests/ElfTest.cpp",
         "tests/ElfTestUtils.cpp",
         "tests/JitDebugTest.cpp",
+        "tests/LocalUnwinderTest.cpp",
         "tests/LogFake.cpp",
         "tests/MapInfoGetElfTest.cpp",
         "tests/MapInfoGetLoadBiasTest.cpp",
@@ -197,6 +223,7 @@
         "tests/files/offline/art_quick_osr_stub_arm/*",
         "tests/files/offline/bad_eh_frame_hdr_arm64/*",
         "tests/files/offline/debug_frame_first_x86/*",
+        "tests/files/offline/debug_frame_load_bias_arm/*",
         "tests/files/offline/eh_frame_hdr_begin_x86_64/*",
         "tests/files/offline/jit_debug_arm/*",
         "tests/files/offline/jit_debug_x86/*",
@@ -206,6 +233,9 @@
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
+    required: [
+        "libunwindstack_local",
+    ],
 }
 
 //-------------------------------------------------------------------------
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 6e397e3..818f5d1 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -31,6 +31,8 @@
 
 namespace unwindstack {
 
+static constexpr uint8_t LOG_CFA_REG = 64;
+
 void ArmExidx::LogRawData() {
   std::string log_str("Raw Data:");
   for (const uint8_t data : data_) {
@@ -63,8 +65,10 @@
   if (data == 1) {
     // This is a CANT UNWIND entry.
     status_ = ARM_STATUS_NO_UNWIND;
-    if (log_) {
-      log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+      }
       log(log_indent_, "[cantunwind]");
     }
     return false;
@@ -86,7 +90,7 @@
       // If this didn't end with a finish op, add one.
       data_.push_back(ARM_OP_FINISH);
     }
-    if (log_) {
+    if (log_type_ == ARM_LOG_FULL) {
       LogRawData();
     }
     return true;
@@ -163,7 +167,7 @@
     data_.push_back(ARM_OP_FINISH);
   }
 
-  if (log_) {
+  if (log_type_ == ARM_LOG_FULL) {
     LogRawData();
   }
   return true;
@@ -190,32 +194,45 @@
   registers |= byte;
   if (registers == 0) {
     // 10000000 00000000: Refuse to unwind
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Refuse to unwind");
     }
     status_ = ARM_STATUS_NO_UNWIND;
     return false;
   }
   // 1000iiii iiiiiiii: Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
-  if (log_) {
-    bool add_comma = false;
-    std::string msg = "pop {";
-    for (size_t i = 0; i < 12; i++) {
-      if (registers & (1 << i)) {
-        if (add_comma) {
-          msg += ", ";
+  registers <<= 4;
+
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      bool add_comma = false;
+      std::string msg = "pop {";
+      for (size_t reg = 4; reg < 16; reg++) {
+        if (registers & (1 << reg)) {
+          if (add_comma) {
+            msg += ", ";
+          }
+          msg += android::base::StringPrintf("r%zu", reg);
+          add_comma = true;
         }
-        msg += android::base::StringPrintf("r%zu", i + 4);
-        add_comma = true;
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      uint32_t cfa_offset = __builtin_popcount(registers) * 4;
+      log_cfa_offset_ += cfa_offset;
+      for (size_t reg = 4; reg < 16; reg++) {
+        if (registers & (1 << reg)) {
+          log_regs_[reg] = cfa_offset;
+          cfa_offset -= 4;
+        }
       }
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
   }
 
-  registers <<= 4;
   for (size_t reg = 4; reg < 16; reg++) {
     if (registers & (1 << reg)) {
       if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
@@ -246,15 +263,20 @@
   if (bits == 13 || bits == 15) {
     // 10011101: Reserved as prefix for ARM register to register moves
     // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "[Reserved]");
     }
     status_ = ARM_STATUS_RESERVED;
     return false;
   }
   // 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
-  if (log_) {
-    log(log_indent_, "vsp = r%d", bits);
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      log(log_indent_, "vsp = r%d", bits);
+    } else {
+      log_regs_[LOG_CFA_REG] = bits;
+    }
+
     if (log_skip_execution_) {
       return true;
     }
@@ -270,17 +292,36 @@
 
   // 10100nnn: Pop r4-r[4+nnn]
   // 10101nnn: Pop r4-r[4+nnn], r14
-  if (log_) {
-    std::string msg = "pop {r4";
+  if (log_type_ != ARM_LOG_NONE) {
     uint8_t end_reg = byte & 0x7;
-    if (end_reg) {
-      msg += android::base::StringPrintf("-r%d", 4 + end_reg);
-    }
-    if (byte & 0x8) {
-      log(log_indent_, "%s, r14}", msg.c_str());
+    if (log_type_ == ARM_LOG_FULL) {
+      std::string msg = "pop {r4";
+      if (end_reg) {
+        msg += android::base::StringPrintf("-r%d", 4 + end_reg);
+      }
+      if (byte & 0x8) {
+        log(log_indent_, "%s, r14}", msg.c_str());
+      } else {
+        log(log_indent_, "%s}", msg.c_str());
+      }
     } else {
-      log(log_indent_, "%s}", msg.c_str());
+      end_reg += 4;
+      uint32_t cfa_offset = (end_reg - 3) * 4;
+      if (byte & 0x8) {
+        cfa_offset += 4;
+      }
+      log_cfa_offset_ += cfa_offset;
+
+      for (uint8_t reg = 4; reg <= end_reg; reg++) {
+        log_regs_[reg] = cfa_offset;
+        cfa_offset -= 4;
+      }
+
+      if (byte & 0x8) {
+        log_regs_[14] = cfa_offset;
+      }
     }
+
     if (log_skip_execution_) {
       return true;
     }
@@ -307,8 +348,11 @@
 
 inline bool ArmExidx::DecodePrefix_10_11_0000() {
   // 10110000: Finish
-  if (log_) {
-    log(log_indent_, "finish");
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      log(log_indent_, "finish");
+    }
+
     if (log_skip_execution_) {
       status_ = ARM_STATUS_FINISH;
       return false;
@@ -326,7 +370,7 @@
 
   if (byte == 0) {
     // 10110001 00000000: Spare
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
@@ -334,7 +378,7 @@
   }
   if (byte >> 4) {
     // 10110001 xxxxyyyy: Spare (xxxx != 0000)
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
@@ -342,19 +386,32 @@
   }
 
   // 10110001 0000iiii: Pop integer registers under mask {r3, r2, r1, r0}
-  if (log_) {
-    bool add_comma = false;
-    std::string msg = "pop {";
-    for (size_t i = 0; i < 4; i++) {
-      if (byte & (1 << i)) {
-        if (add_comma) {
-          msg += ", ";
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      bool add_comma = false;
+      std::string msg = "pop {";
+      for (size_t i = 0; i < 4; i++) {
+        if (byte & (1 << i)) {
+          if (add_comma) {
+            msg += ", ";
+          }
+          msg += android::base::StringPrintf("r%zu", i);
+          add_comma = true;
         }
-        msg += android::base::StringPrintf("r%zu", i);
-        add_comma = true;
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      byte &= 0xf;
+      uint32_t cfa_offset = __builtin_popcount(byte) * 4;
+      log_cfa_offset_ += cfa_offset;
+      for (size_t reg = 0; reg < 4; reg++) {
+        if (byte & (1 << reg)) {
+          log_regs_[reg] = cfa_offset;
+          cfa_offset -= 4;
+        }
       }
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
@@ -373,6 +430,15 @@
   return true;
 }
 
+inline void ArmExidx::AdjustRegisters(int32_t offset) {
+  for (auto& entry : log_regs_) {
+    if (entry.first >= LOG_CFA_REG) {
+      break;
+    }
+    entry.second += offset;
+  }
+}
+
 inline bool ArmExidx::DecodePrefix_10_11_0010() {
   // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
   uint32_t result = 0;
@@ -387,8 +453,15 @@
     shift += 7;
   } while (byte & 0x80);
   result <<= 2;
-  if (log_) {
-    log(log_indent_, "vsp = vsp + %d", 0x204 + result);
+  if (log_type_ != ARM_LOG_NONE) {
+    int32_t cfa_offset = 0x204 + result;
+    if (log_type_ == ARM_LOG_FULL) {
+      log(log_indent_, "vsp = vsp + %d", cfa_offset);
+    } else {
+      log_cfa_offset_ += cfa_offset;
+    }
+    AdjustRegisters(cfa_offset);
+
     if (log_skip_execution_) {
       return true;
     }
@@ -404,14 +477,20 @@
     return false;
   }
 
-  if (log_) {
+  if (log_type_ != ARM_LOG_NONE) {
     uint8_t start_reg = byte >> 4;
-    std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
     uint8_t end_reg = start_reg + (byte & 0xf);
-    if (end_reg) {
-      msg += android::base::StringPrintf("-d%d", end_reg);
+
+    if (log_type_ == ARM_LOG_FULL) {
+      std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+      if (end_reg) {
+        msg += android::base::StringPrintf("-d%d", end_reg);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      log(log_indent_, "Unsupported DX register display");
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
@@ -422,7 +501,7 @@
 
 inline bool ArmExidx::DecodePrefix_10_11_01nn() {
   // 101101nn: Spare
-  if (log_) {
+  if (log_type_ != ARM_LOG_NONE) {
     log(log_indent_, "Spare");
   }
   status_ = ARM_STATUS_SPARE;
@@ -433,13 +512,18 @@
   CHECK((byte & ~0x07) == 0xb8);
 
   // 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
-  if (log_) {
-    std::string msg = "pop {d8";
-    uint8_t last_reg = (byte & 0x7);
-    if (last_reg) {
-      msg += android::base::StringPrintf("-d%d", last_reg + 8);
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      uint8_t last_reg = (byte & 0x7);
+      std::string msg = "pop {d8";
+      if (last_reg) {
+        msg += android::base::StringPrintf("-d%d", last_reg + 8);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      log(log_indent_, "Unsupported DX register display");
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
@@ -489,14 +573,19 @@
     }
 
     // 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
-    if (log_) {
-      uint8_t start_reg = byte >> 4;
-      std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
-      uint8_t end_reg = byte & 0xf;
-      if (end_reg) {
-        msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        uint8_t start_reg = byte >> 4;
+        std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
+        uint8_t end_reg = byte & 0xf;
+        if (end_reg) {
+          msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      } else {
+        log(log_indent_, "Unsupported wRX register display");
       }
-      log(log_indent_, "%s}", msg.c_str());
+
       if (log_skip_execution_) {
         return true;
       }
@@ -510,32 +599,40 @@
 
     if (byte == 0) {
       // 11000111 00000000: Spare
-      if (log_) {
+      if (log_type_ != ARM_LOG_NONE) {
         log(log_indent_, "Spare");
       }
       status_ = ARM_STATUS_SPARE;
       return false;
     } else if ((byte >> 4) == 0) {
       // 11000111 0000iiii: Intel Wireless MMX pop wCGR registers {wCGR0,1,2,3}
-      if (log_) {
-        bool add_comma = false;
-        std::string msg = "pop {";
-        for (size_t i = 0; i < 4; i++) {
-          if (byte & (1 << i)) {
-            if (add_comma) {
-              msg += ", ";
+      if (log_type_ != ARM_LOG_NONE) {
+        if (log_type_ == ARM_LOG_FULL) {
+          bool add_comma = false;
+          std::string msg = "pop {";
+          for (size_t i = 0; i < 4; i++) {
+            if (byte & (1 << i)) {
+              if (add_comma) {
+                msg += ", ";
+              }
+              msg += android::base::StringPrintf("wCGR%zu", i);
+              add_comma = true;
             }
-            msg += android::base::StringPrintf("wCGR%zu", i);
-            add_comma = true;
           }
+          log(log_indent_, "%s}", msg.c_str());
+        } else {
+          log(log_indent_, "Unsupported wCGR register display");
         }
-        log(log_indent_, "%s}", msg.c_str());
+
+        if (log_skip_execution_) {
+          return true;
+        }
       }
       // Only update the cfa.
       cfa_ += __builtin_popcount(byte) * 4;
     } else {
       // 11000111 xxxxyyyy: Spare (xxxx != 0000)
-      if (log_) {
+      if (log_type_ != ARM_LOG_NONE) {
         log(log_indent_, "Spare");
       }
       status_ = ARM_STATUS_SPARE;
@@ -543,13 +640,18 @@
     }
   } else {
     // 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
-    if (log_) {
-      std::string msg = "pop {wR10";
-      uint8_t nnn = byte & 0x7;
-      if (nnn) {
-        msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        std::string msg = "pop {wR10";
+        uint8_t nnn = byte & 0x7;
+        if (nnn) {
+          msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      } else {
+        log(log_indent_, "Unsupported wRX register display");
       }
-      log(log_indent_, "%s}", msg.c_str());
+
       if (log_skip_execution_) {
         return true;
       }
@@ -570,14 +672,19 @@
       return false;
     }
 
-    if (log_) {
-      uint8_t start_reg = byte >> 4;
-      std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
-      uint8_t end_reg = byte & 0xf;
-      if (end_reg) {
-        msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        uint8_t start_reg = byte >> 4;
+        std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
+        uint8_t end_reg = byte & 0xf;
+        if (end_reg) {
+          msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      } else {
+        log(log_indent_, "Unsupported DX register display");
       }
-      log(log_indent_, "%s}", msg.c_str());
+
       if (log_skip_execution_) {
         return true;
       }
@@ -590,14 +697,19 @@
       return false;
     }
 
-    if (log_) {
-      uint8_t start_reg = byte >> 4;
-      std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
-      uint8_t end_reg = byte & 0xf;
-      if (end_reg) {
-        msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        uint8_t start_reg = byte >> 4;
+        std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+        uint8_t end_reg = byte & 0xf;
+        if (end_reg) {
+          msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      } else {
+        log(log_indent_, "Unsupported DX register display");
       }
-      log(log_indent_, "%s}", msg.c_str());
+
       if (log_skip_execution_) {
         return true;
       }
@@ -606,7 +718,7 @@
     cfa_ += (byte & 0xf) * 8 + 8;
   } else {
     // 11001yyy: Spare (yyy != 000, 001)
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
@@ -619,13 +731,18 @@
   CHECK((byte & ~0x07) == 0xd0);
 
   // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
-  if (log_) {
-    std::string msg = "pop {d8";
-    uint8_t end_reg = byte & 0x7;
-    if (end_reg) {
-      msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      std::string msg = "pop {d8";
+      uint8_t end_reg = byte & 0x7;
+      if (end_reg) {
+        msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      log(log_indent_, "Unsupported DX register display");
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
@@ -646,7 +763,7 @@
     return DecodePrefix_11_010(byte);
   default:
     // 11xxxyyy: Spare (xxx != 000, 001, 010)
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
@@ -664,8 +781,15 @@
   switch (byte >> 6) {
   case 0:
     // 00xxxxxx: vsp = vsp + (xxxxxxx << 2) + 4
-    if (log_) {
-      log(log_indent_, "vsp = vsp + %d", ((byte & 0x3f) << 2) + 4);
+    if (log_type_ != ARM_LOG_NONE) {
+      int32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
+      if (log_type_ == ARM_LOG_FULL) {
+        log(log_indent_, "vsp = vsp + %d", cfa_offset);
+      } else {
+        log_cfa_offset_ += cfa_offset;
+      }
+      AdjustRegisters(cfa_offset);
+
       if (log_skip_execution_) {
         break;
       }
@@ -674,8 +798,15 @@
     break;
   case 1:
     // 01xxxxxx: vsp = vsp - (xxxxxxx << 2) + 4
-    if (log_) {
-      log(log_indent_, "vsp = vsp - %d", ((byte & 0x3f) << 2) + 4);
+    if (log_type_ != ARM_LOG_NONE) {
+      uint32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
+      if (log_type_ == ARM_LOG_FULL) {
+        log(log_indent_, "vsp = vsp - %d", cfa_offset);
+      } else {
+        log_cfa_offset_ -= cfa_offset;
+      }
+      AdjustRegisters(-cfa_offset);
+
       if (log_skip_execution_) {
         break;
       }
@@ -696,4 +827,36 @@
   return status_ == ARM_STATUS_FINISH;
 }
 
+void ArmExidx::LogByReg() {
+  if (log_type_ != ARM_LOG_BY_REG) {
+    return;
+  }
+
+  uint8_t cfa_reg;
+  if (log_regs_.count(LOG_CFA_REG) == 0) {
+    cfa_reg = 13;
+  } else {
+    cfa_reg = log_regs_[LOG_CFA_REG];
+  }
+
+  if (log_cfa_offset_ != 0) {
+    char sign = (log_cfa_offset_ > 0) ? '+' : '-';
+    log(log_indent_, "cfa = r%zu %c %d", cfa_reg, sign, abs(log_cfa_offset_));
+  } else {
+    log(log_indent_, "cfa = r%zu", cfa_reg);
+  }
+
+  for (const auto& entry : log_regs_) {
+    if (entry.first >= LOG_CFA_REG) {
+      break;
+    }
+    if (entry.second == 0) {
+      log(log_indent_, "r%zu = [cfa]", entry.first);
+    } else {
+      char sign = (entry.second > 0) ? '-' : '+';
+      log(log_indent_, "r%zu = [cfa %c %d]", entry.first, sign, abs(entry.second));
+    }
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
index 96756a0..d9fc371 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include <deque>
+#include <map>
 
 namespace unwindstack {
 
@@ -44,6 +45,12 @@
   ARM_OP_FINISH = 0xb0,
 };
 
+enum ArmLogType : uint8_t {
+  ARM_LOG_NONE,
+  ARM_LOG_FULL,
+  ARM_LOG_BY_REG,
+};
+
 class ArmExidx {
  public:
   ArmExidx(RegsArm* regs, Memory* elf_memory, Memory* process_memory)
@@ -52,6 +59,8 @@
 
   void LogRawData();
 
+  void LogByReg();
+
   bool ExtractEntryData(uint32_t entry_offset);
 
   bool Eval();
@@ -71,12 +80,13 @@
   bool pc_set() { return pc_set_; }
   void set_pc_set(bool pc_set) { pc_set_ = pc_set; }
 
-  void set_log(bool log) { log_ = log; }
+  void set_log(ArmLogType log_type) { log_type_ = log_type; }
   void set_log_skip_execution(bool skip_execution) { log_skip_execution_ = skip_execution; }
   void set_log_indent(uint8_t indent) { log_indent_ = indent; }
 
  private:
   bool GetByte(uint8_t* byte);
+  void AdjustRegisters(int32_t offset);
 
   bool DecodePrefix_10_00(uint8_t byte);
   bool DecodePrefix_10_01(uint8_t byte);
@@ -103,10 +113,12 @@
   Memory* elf_memory_;
   Memory* process_memory_;
 
-  bool log_ = false;
+  ArmLogType log_type_ = ARM_LOG_NONE;
   uint8_t log_indent_ = 0;
   bool log_skip_execution_ = false;
   bool pc_set_ = false;
+  int32_t log_cfa_offset_ = 0;
+  std::map<uint8_t, int32_t> log_regs_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 6ecedce..cd9ef61 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -264,8 +264,8 @@
 }
 
 template <typename AddressType>
-bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t load_bias,
-                                uint64_t start_offset, uint64_t end_offset) {
+bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t start_offset,
+                                uint64_t end_offset) {
   memory_->set_cur_offset(start_offset);
   uint64_t cfa_offset;
   uint64_t cur_pc = fde_->pc_start;
@@ -301,8 +301,8 @@
         break;
     }
     if (cur_pc != old_pc) {
-      log(indent, "");
-      log(indent, "PC 0x%" PRIx64, cur_pc + load_bias);
+      log(0, "");
+      log(indent, "PC 0x%" PRIx64, cur_pc);
     }
     old_pc = cur_pc;
   }
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index 16c66e2..c5ffb8e 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -71,8 +71,7 @@
   bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
                        dwarf_loc_regs_t* loc_regs);
 
-  bool Log(uint32_t indent, uint64_t pc, uint64_t load_bias, uint64_t start_offset,
-           uint64_t end_offset);
+  bool Log(uint32_t indent, uint64_t pc, uint64_t start_offset, uint64_t end_offset);
 
   const DwarfErrorData& last_error() { return last_error_; }
   DwarfErrorCode LastErrorCode() { return last_error_.code; }
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
index 635cefd..388ab0a 100644
--- a/libunwindstack/DwarfDebugFrame.h
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -26,9 +26,9 @@
 namespace unwindstack {
 
 template <typename AddressType>
-class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
+class DwarfDebugFrame : public DwarfSectionImplNoHdr<AddressType> {
  public:
-  DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {
+  DwarfDebugFrame(Memory* memory) : DwarfSectionImplNoHdr<AddressType>(memory) {
     this->cie32_value_ = static_cast<uint32_t>(-1);
     this->cie64_value_ = static_cast<uint64_t>(-1);
   }
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
index 7a41e45..df441fb 100644
--- a/libunwindstack/DwarfEhFrame.h
+++ b/libunwindstack/DwarfEhFrame.h
@@ -25,9 +25,9 @@
 namespace unwindstack {
 
 template <typename AddressType>
-class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
+class DwarfEhFrame : public DwarfSectionImplNoHdr<AddressType> {
  public:
-  DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  DwarfEhFrame(Memory* memory) : DwarfSectionImplNoHdr<AddressType>(memory) {}
   virtual ~DwarfEhFrame() = default;
 
   uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index 9a49013..668527a 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -22,19 +22,27 @@
 
 #include "Check.h"
 #include "DwarfEhFrameWithHdr.h"
+#include "DwarfEncoding.h"
 
 namespace unwindstack {
 
+static inline bool IsEncodingRelative(uint8_t encoding) {
+  encoding >>= 4;
+  return encoding > 0 && encoding <= DW_EH_PE_funcrel;
+}
+
 template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size) {
-  uint8_t data[4];
+bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
+  load_bias_ = load_bias;
 
   memory_.clear_func_offset();
   memory_.clear_text_offset();
   memory_.set_data_offset(offset);
   memory_.set_cur_offset(offset);
+  pc_offset_ = offset;
 
   // Read the first four bytes all at once.
+  uint8_t data[4];
   if (!memory_.ReadBytes(data, 4)) {
     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
     last_error_.address = memory_.cur_offset();
@@ -81,12 +89,22 @@
 }
 
 template <typename AddressType>
-const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromIndex(size_t index) {
-  const FdeInfo* info = GetFdeInfoFromIndex(index);
-  if (info == nullptr) {
+const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromPc(uint64_t pc) {
+  uint64_t fde_offset;
+  if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
     return nullptr;
   }
-  return this->GetFdeFromOffset(info->offset);
+  const DwarfFde* fde = this->GetFdeFromOffset(fde_offset);
+  if (fde == nullptr) {
+    return nullptr;
+  }
+
+  // Guaranteed pc >= pc_start, need to check pc in the fde range.
+  if (pc < fde->pc_end) {
+    return fde;
+  }
+  last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+  return nullptr;
 }
 
 template <typename AddressType>
@@ -100,7 +118,7 @@
 
   memory_.set_data_offset(entries_data_offset_);
   memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
-  memory_.set_pc_offset(memory_.cur_offset());
+  memory_.set_pc_offset(0);
   uint64_t value;
   if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
       !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
@@ -109,6 +127,11 @@
     fde_info_.erase(index);
     return nullptr;
   }
+
+  // Relative encodings require adding in the load bias.
+  if (IsEncodingRelative(table_encoding_)) {
+    value += load_bias_;
+  }
   info->pc = value;
   return info;
 }
@@ -174,27 +197,27 @@
 
   memory_.set_data_offset(entries_data_offset_);
   memory_.set_cur_offset(cur_entries_offset_);
+  memory_.set_pc_offset(0);
   cur_entries_offset_ = 0;
 
   FdeInfo* prev_info = nullptr;
   for (size_t current = fde_info_.size();
        current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
-    memory_.set_pc_offset(memory_.cur_offset());
-    uint64_t value;
-    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-
     FdeInfo* info = &fde_info_[current];
-    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+    uint64_t value;
+    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+        !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
       fde_info_.erase(current);
       last_error_.code = DWARF_ERROR_MEMORY_INVALID;
       last_error_.address = memory_.cur_offset();
       return false;
     }
-    info->pc = value + 4;
+
+    // Relative encodings require adding in the load bias.
+    if (IsEncodingRelative(table_encoding_)) {
+      value += load_bias_;
+    }
+    info->pc = value;
 
     if (pc < info->pc) {
       if (prev_info == nullptr) {
@@ -229,6 +252,21 @@
   }
 }
 
+template <typename AddressType>
+void DwarfEhFrameWithHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
+  for (size_t i = 0; i < fde_count_; i++) {
+    const FdeInfo* info = GetFdeInfoFromIndex(i);
+    if (info == nullptr) {
+      break;
+    }
+    const DwarfFde* fde = this->GetFdeFromOffset(info->offset);
+    if (fde == nullptr) {
+      break;
+    }
+    fdes->push_back(fde);
+  }
+}
+
 // Explicitly instantiate DwarfEhFrameWithHdr
 template class DwarfEhFrameWithHdr<uint32_t>;
 template class DwarfEhFrameWithHdr<uint64_t>;
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index 3571166..e3e9ca8 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -21,7 +21,7 @@
 
 #include <unordered_map>
 
-#include "DwarfEhFrame.h"
+#include <unwindstack/DwarfSection.h>
 
 namespace unwindstack {
 
@@ -29,29 +29,43 @@
 class Memory;
 
 template <typename AddressType>
-class DwarfEhFrameWithHdr : public DwarfEhFrame<AddressType> {
+class DwarfEhFrameWithHdr : public DwarfSectionImpl<AddressType> {
  public:
   // Add these so that the protected members of DwarfSectionImpl
   // can be accessed without needing a this->.
   using DwarfSectionImpl<AddressType>::memory_;
-  using DwarfSectionImpl<AddressType>::fde_count_;
+  using DwarfSectionImpl<AddressType>::pc_offset_;
   using DwarfSectionImpl<AddressType>::entries_offset_;
   using DwarfSectionImpl<AddressType>::entries_end_;
   using DwarfSectionImpl<AddressType>::last_error_;
+  using DwarfSectionImpl<AddressType>::load_bias_;
 
   struct FdeInfo {
     AddressType pc;
     uint64_t offset;
   };
 
-  DwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrame<AddressType>(memory) {}
+  DwarfEhFrameWithHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
   virtual ~DwarfEhFrameWithHdr() = default;
 
-  bool Init(uint64_t offset, uint64_t size) override;
+  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+    return this->memory_.cur_offset() - pointer - 4;
+  }
 
-  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+    return this->memory_.cur_offset() - pointer - 8;
+  }
 
-  const DwarfFde* GetFdeFromIndex(size_t index) override;
+  uint64_t AdjustPcFromFde(uint64_t pc) override {
+    // The eh_frame uses relative pcs.
+    return pc + this->memory_.cur_offset() - 4;
+  }
+
+  bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
+
+  const DwarfFde* GetFdeFromPc(uint64_t pc) override;
+
+  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset);
 
   const FdeInfo* GetFdeInfoFromIndex(size_t index);
 
@@ -59,6 +73,8 @@
 
   bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
 
+  void GetFdes(std::vector<const DwarfFde*>* fdes) override;
+
  protected:
   uint8_t version_;
   uint8_t ptr_encoding_;
@@ -70,6 +86,7 @@
   uint64_t entries_data_offset_;
   uint64_t cur_entries_offset_ = 0;
 
+  uint64_t fde_count_;
   std::unordered_map<uint64_t, FdeInfo> fde_info_;
 };
 
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 65eec65..6061f61 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -36,24 +36,6 @@
 
 DwarfSection::DwarfSection(Memory* memory) : memory_(memory) {}
 
-const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
-  uint64_t fde_offset;
-  if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
-    return nullptr;
-  }
-  const DwarfFde* fde = GetFdeFromOffset(fde_offset);
-  if (fde == nullptr) {
-    return nullptr;
-  }
-
-  // Guaranteed pc >= pc_start, need to check pc in the fde range.
-  if (pc < fde->pc_end) {
-    return fde;
-  }
-  last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
-  return nullptr;
-}
-
 bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
   // Lookup the pc in the cache.
   auto it = loc_regs_.upper_bound(pc);
@@ -81,6 +63,314 @@
 }
 
 template <typename AddressType>
+const DwarfCie* DwarfSectionImpl<AddressType>::GetCieFromOffset(uint64_t offset) {
+  auto cie_entry = cie_entries_.find(offset);
+  if (cie_entry != cie_entries_.end()) {
+    return &cie_entry->second;
+  }
+  DwarfCie* cie = &cie_entries_[offset];
+  memory_.set_cur_offset(offset);
+  if (!FillInCieHeader(cie) || !FillInCie(cie)) {
+    // Erase the cached entry.
+    cie_entries_.erase(offset);
+    return nullptr;
+  }
+  return cie;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInCieHeader(DwarfCie* cie) {
+  cie->lsda_encoding = DW_EH_PE_omit;
+  uint32_t length32;
+  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+  if (length32 == static_cast<uint32_t>(-1)) {
+    // 64 bit Cie
+    uint64_t length64;
+    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    cie->cfa_instructions_end = memory_.cur_offset() + length64;
+    cie->fde_address_encoding = DW_EH_PE_sdata8;
+
+    uint64_t cie_id;
+    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    if (cie_id != cie64_value_) {
+      // This is not a Cie, something has gone horribly wrong.
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+  } else {
+    // 32 bit Cie
+    cie->cfa_instructions_end = memory_.cur_offset() + length32;
+    cie->fde_address_encoding = DW_EH_PE_sdata4;
+
+    uint32_t cie_id;
+    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    if (cie_id != cie32_value_) {
+      // This is not a Cie, something has gone horribly wrong.
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
+  if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
+    // Unrecognized version.
+    last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
+    return false;
+  }
+
+  // Read the augmentation string.
+  char aug_value;
+  do {
+    if (!memory_.ReadBytes(&aug_value, 1)) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    cie->augmentation_string.push_back(aug_value);
+  } while (aug_value != '\0');
+
+  if (cie->version == 4) {
+    // Skip the Address Size field since we only use it for validation.
+    memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+    // Segment Size
+    if (!memory_.ReadBytes(&cie->segment_size, 1)) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+  }
+
+  // Code Alignment Factor
+  if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  // Data Alignment Factor
+  if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (cie->version == 1) {
+    // Return Address is a single byte.
+    uint8_t return_address_register;
+    if (!memory_.ReadBytes(&return_address_register, 1)) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    cie->return_address_register = return_address_register;
+  } else if (!memory_.ReadULEB128(&cie->return_address_register)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (cie->augmentation_string[0] != 'z') {
+    cie->cfa_instructions_offset = memory_.cur_offset();
+    return true;
+  }
+
+  uint64_t aug_length;
+  if (!memory_.ReadULEB128(&aug_length)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+  cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
+
+  for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
+    switch (cie->augmentation_string[i]) {
+      case 'L':
+        if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
+          return false;
+        }
+        break;
+      case 'P': {
+        uint8_t encoding;
+        if (!memory_.ReadBytes(&encoding, 1)) {
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
+          return false;
+        }
+        memory_.set_pc_offset(pc_offset_);
+        if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
+          return false;
+        }
+      } break;
+      case 'R':
+        if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
+          return false;
+        }
+        break;
+    }
+  }
+  return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
+  auto fde_entry = fde_entries_.find(offset);
+  if (fde_entry != fde_entries_.end()) {
+    return &fde_entry->second;
+  }
+  DwarfFde* fde = &fde_entries_[offset];
+  memory_.set_cur_offset(offset);
+  if (!FillInFdeHeader(fde) || !FillInFde(fde)) {
+    fde_entries_.erase(offset);
+    return nullptr;
+  }
+  return fde;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInFdeHeader(DwarfFde* fde) {
+  uint32_t length32;
+  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (length32 == static_cast<uint32_t>(-1)) {
+    // 64 bit Fde.
+    uint64_t length64;
+    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    fde->cfa_instructions_end = memory_.cur_offset() + length64;
+
+    uint64_t value64;
+    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    if (value64 == cie64_value_) {
+      // This is a Cie, this means something has gone wrong.
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+
+    // Get the Cie pointer, which is necessary to properly read the rest of
+    // of the Fde information.
+    fde->cie_offset = GetCieOffsetFromFde64(value64);
+  } else {
+    // 32 bit Fde.
+    fde->cfa_instructions_end = memory_.cur_offset() + length32;
+
+    uint32_t value32;
+    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    if (value32 == cie32_value_) {
+      // This is a Cie, this means something has gone wrong.
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+
+    // Get the Cie pointer, which is necessary to properly read the rest of
+    // of the Fde information.
+    fde->cie_offset = GetCieOffsetFromFde32(value32);
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
+  uint64_t cur_offset = memory_.cur_offset();
+
+  const DwarfCie* cie = GetCieFromOffset(fde->cie_offset);
+  if (cie == nullptr) {
+    return false;
+  }
+  fde->cie = cie;
+
+  if (cie->segment_size != 0) {
+    // Skip over the segment selector for now.
+    cur_offset += cie->segment_size;
+  }
+  memory_.set_cur_offset(cur_offset);
+
+  // The load bias only applies to the start.
+  memory_.set_pc_offset(load_bias_);
+  bool valid = memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_start);
+  fde->pc_start = AdjustPcFromFde(fde->pc_start);
+
+  memory_.set_pc_offset(0);
+  if (!valid || !memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_end)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+  fde->pc_end += fde->pc_start;
+
+  if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
+    // Augmentation Size
+    uint64_t aug_length;
+    if (!memory_.ReadULEB128(&aug_length)) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+    uint64_t cur_offset = memory_.cur_offset();
+
+    memory_.set_pc_offset(pc_offset_);
+    if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    // Set our position to after all of the augmentation data.
+    memory_.set_cur_offset(cur_offset + aug_length);
+  }
+  fde->cfa_instructions_offset = memory_.cur_offset();
+
+  return true;
+}
+
+template <typename AddressType>
 bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, Memory* regular_memory,
                                                    AddressType* value,
                                                    RegsInfo<AddressType>* regs_info,
@@ -260,305 +550,6 @@
 }
 
 template <typename AddressType>
-const DwarfCie* DwarfSectionImpl<AddressType>::GetCie(uint64_t offset) {
-  auto cie_entry = cie_entries_.find(offset);
-  if (cie_entry != cie_entries_.end()) {
-    return &cie_entry->second;
-  }
-  DwarfCie* cie = &cie_entries_[offset];
-  memory_.set_cur_offset(offset);
-  if (!FillInCie(cie)) {
-    // Erase the cached entry.
-    cie_entries_.erase(offset);
-    return nullptr;
-  }
-  return cie;
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
-  uint32_t length32;
-  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  // Set the default for the lsda encoding.
-  cie->lsda_encoding = DW_EH_PE_omit;
-
-  if (length32 == static_cast<uint32_t>(-1)) {
-    // 64 bit Cie
-    uint64_t length64;
-    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-
-    cie->cfa_instructions_end = memory_.cur_offset() + length64;
-    cie->fde_address_encoding = DW_EH_PE_sdata8;
-
-    uint64_t cie_id;
-    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (cie_id != cie64_value_) {
-      // This is not a Cie, something has gone horribly wrong.
-      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-  } else {
-    // 32 bit Cie
-    cie->cfa_instructions_end = memory_.cur_offset() + length32;
-    cie->fde_address_encoding = DW_EH_PE_sdata4;
-
-    uint32_t cie_id;
-    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (cie_id != cie32_value_) {
-      // This is not a Cie, something has gone horribly wrong.
-      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-  }
-
-  if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
-    // Unrecognized version.
-    last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
-    return false;
-  }
-
-  // Read the augmentation string.
-  char aug_value;
-  do {
-    if (!memory_.ReadBytes(&aug_value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    cie->augmentation_string.push_back(aug_value);
-  } while (aug_value != '\0');
-
-  if (cie->version == 4) {
-    // Skip the Address Size field since we only use it for validation.
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-
-    // Segment Size
-    if (!memory_.ReadBytes(&cie->segment_size, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-  }
-
-  // Code Alignment Factor
-  if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  // Data Alignment Factor
-  if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  if (cie->version == 1) {
-    // Return Address is a single byte.
-    uint8_t return_address_register;
-    if (!memory_.ReadBytes(&return_address_register, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    cie->return_address_register = return_address_register;
-  } else if (!memory_.ReadULEB128(&cie->return_address_register)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  if (cie->augmentation_string[0] != 'z') {
-    cie->cfa_instructions_offset = memory_.cur_offset();
-    return true;
-  }
-
-  uint64_t aug_length;
-  if (!memory_.ReadULEB128(&aug_length)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
-
-  for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
-    switch (cie->augmentation_string[i]) {
-      case 'L':
-        if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
-          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-          last_error_.address = memory_.cur_offset();
-          return false;
-        }
-        break;
-      case 'P': {
-        uint8_t encoding;
-        if (!memory_.ReadBytes(&encoding, 1)) {
-          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-          last_error_.address = memory_.cur_offset();
-          return false;
-        }
-        if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
-          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-          last_error_.address = memory_.cur_offset();
-          return false;
-        }
-      } break;
-      case 'R':
-        if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
-          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-          last_error_.address = memory_.cur_offset();
-          return false;
-        }
-        break;
-    }
-  }
-  return true;
-}
-
-template <typename AddressType>
-const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
-  auto fde_entry = fde_entries_.find(offset);
-  if (fde_entry != fde_entries_.end()) {
-    return &fde_entry->second;
-  }
-  DwarfFde* fde = &fde_entries_[offset];
-  memory_.set_cur_offset(offset);
-  if (!FillInFde(fde)) {
-    fde_entries_.erase(offset);
-    return nullptr;
-  }
-  return fde;
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
-  uint32_t length32;
-  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-
-  if (length32 == static_cast<uint32_t>(-1)) {
-    // 64 bit Fde.
-    uint64_t length64;
-    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    fde->cfa_instructions_end = memory_.cur_offset() + length64;
-
-    uint64_t value64;
-    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (value64 == cie64_value_) {
-      // This is a Cie, this means something has gone wrong.
-      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-
-    // Get the Cie pointer, which is necessary to properly read the rest of
-    // of the Fde information.
-    fde->cie_offset = GetCieOffsetFromFde64(value64);
-  } else {
-    // 32 bit Fde.
-    fde->cfa_instructions_end = memory_.cur_offset() + length32;
-
-    uint32_t value32;
-    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (value32 == cie32_value_) {
-      // This is a Cie, this means something has gone wrong.
-      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-
-    // Get the Cie pointer, which is necessary to properly read the rest of
-    // of the Fde information.
-    fde->cie_offset = GetCieOffsetFromFde32(value32);
-  }
-  uint64_t cur_offset = memory_.cur_offset();
-
-  const DwarfCie* cie = GetCie(fde->cie_offset);
-  if (cie == nullptr) {
-    return false;
-  }
-  fde->cie = cie;
-
-  if (cie->segment_size != 0) {
-    // Skip over the segment selector for now.
-    cur_offset += cie->segment_size;
-  }
-  memory_.set_cur_offset(cur_offset);
-
-  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  fde->pc_start = AdjustPcFromFde(fde->pc_start);
-
-  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_end)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  fde->pc_end += fde->pc_start;
-  if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
-    // Augmentation Size
-    uint64_t aug_length;
-    if (!memory_.ReadULEB128(&aug_length)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    uint64_t cur_offset = memory_.cur_offset();
-
-    if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-
-    // Set our position to after all of the augmentation data.
-    memory_.set_cur_offset(cur_offset + aug_length);
-  }
-  fde->cfa_instructions_offset = memory_.cur_offset();
-
-  return true;
-}
-
-template <typename AddressType>
 bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
                                                        dwarf_loc_regs_t* loc_regs) {
   DwarfCfa<AddressType> cfa(&memory_, fde);
@@ -582,17 +573,16 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, uint64_t load_bias,
-                                        const DwarfFde* fde) {
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) {
   DwarfCfa<AddressType> cfa(&memory_, fde);
 
   // Always print the cie information.
   const DwarfCie* cie = fde->cie;
-  if (!cfa.Log(indent, pc, load_bias, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
+  if (!cfa.Log(indent, pc, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
     last_error_ = cfa.last_error();
     return false;
   }
-  if (!cfa.Log(indent, pc, load_bias, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
+  if (!cfa.Log(indent, pc, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
     last_error_ = cfa.last_error();
     return false;
   }
@@ -600,309 +590,225 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size) {
+bool DwarfSectionImplNoHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
+  load_bias_ = load_bias;
   entries_offset_ = offset;
+  next_entries_offset_ = offset;
   entries_end_ = offset + size;
 
   memory_.clear_func_offset();
   memory_.clear_text_offset();
-  memory_.set_data_offset(offset);
   memory_.set_cur_offset(offset);
-  memory_.set_pc_offset(offset);
-
-  return CreateSortedFdeList();
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
-  uint8_t version;
-  if (!memory_.ReadBytes(&version, 1)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  // Read the augmentation string.
-  std::vector<char> aug_string;
-  char aug_value;
-  bool get_encoding = false;
-  do {
-    if (!memory_.ReadBytes(&aug_value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-    if (aug_value == 'R') {
-      get_encoding = true;
-    }
-    aug_string.push_back(aug_value);
-  } while (aug_value != '\0');
-
-  if (version == 4) {
-    // Skip the Address Size field.
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-
-    // Read the segment size.
-    if (!memory_.ReadBytes(segment_size, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-  } else {
-    *segment_size = 0;
-  }
-
-  if (aug_string[0] != 'z' || !get_encoding) {
-    // No encoding
-    return true;
-  }
-
-  // Skip code alignment factor
-  uint8_t value;
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-  } while (value & 0x80);
-
-  // Skip data alignment factor
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-  } while (value & 0x80);
-
-  if (version == 1) {
-    // Skip return address register.
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-  } else {
-    // Skip return address register.
-    do {
-      if (!memory_.ReadBytes(&value, 1)) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-    } while (value & 0x80);
-  }
-
-  // Skip the augmentation length.
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-  } while (value & 0x80);
-
-  for (size_t i = 1; i < aug_string.size(); i++) {
-    if (aug_string[i] == 'R') {
-      if (!memory_.ReadBytes(encoding, 1)) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-      // Got the encoding, that's all we are looking for.
-      return true;
-    } else if (aug_string[i] == 'L') {
-      memory_.set_cur_offset(memory_.cur_offset() + 1);
-    } else if (aug_string[i] == 'P') {
-      uint8_t encoding;
-      if (!memory_.ReadBytes(&encoding, 1)) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-      uint64_t value;
-      if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-    }
-  }
-
-  // It should be impossible to get here.
-  abort();
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size,
-                                               uint8_t encoding) {
-  if (segment_size != 0) {
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-  }
-
-  uint64_t start;
-  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  start = AdjustPcFromFde(start);
-
-  uint64_t length;
-  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
-  if (length != 0) {
-    fdes_.emplace_back(entry_offset, start, length);
-  }
+  memory_.set_data_offset(offset);
+  pc_offset_ = offset;
 
   return true;
 }
 
+// Create a cached version of the fde information such that it is a std::map
+// that is indexed by end pc and contains a pair that represents the start pc
+// followed by the fde object. The fde pointers are owned by fde_entries_
+// and not by the map object.
+// It is possible for an fde to be represented by multiple entries in
+// the map. This can happen if the the start pc and end pc overlap already
+// existing entries. For example, if there is already an entry of 0x400, 0x200,
+// and an fde has a start pc of 0x100 and end pc of 0x500, two new entries
+// will be added: 0x200, 0x100 and 0x500, 0x400.
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::CreateSortedFdeList() {
-  memory_.set_cur_offset(entries_offset_);
+void DwarfSectionImplNoHdr<AddressType>::InsertFde(const DwarfFde* fde) {
+  uint64_t start = fde->pc_start;
+  uint64_t end = fde->pc_end;
+  auto it = fdes_.upper_bound(start);
+  bool add_element = false;
+  while (it != fdes_.end() && start < end) {
+    if (add_element) {
+      add_element = false;
+      if (end < it->second.first) {
+        if (it->first == end) {
+          return;
+        }
+        fdes_[end] = std::make_pair(start, fde);
+        return;
+      }
+      if (start != it->second.first) {
+        fdes_[it->second.first] = std::make_pair(start, fde);
+      }
+    }
+    if (start < it->first) {
+      if (end < it->second.first) {
+        if (it->first != end) {
+          fdes_[end] = std::make_pair(start, fde);
+        }
+        return;
+      }
+      add_element = true;
+    }
+    start = it->first;
+    ++it;
+  }
+  if (start < end) {
+    fdes_[end] = std::make_pair(start, fde);
+  }
+}
 
-  // Loop through all of the entries and read just enough to create
-  // a sorted list of pcs.
-  // This code assumes that first comes the cie, then the fdes that
-  // it applies to.
-  uint64_t cie_offset = 0;
-  uint8_t address_encoding;
-  uint8_t segment_size;
-  while (memory_.cur_offset() < entries_end_) {
-    uint64_t cur_entry_offset = memory_.cur_offset();
+template <typename AddressType>
+bool DwarfSectionImplNoHdr<AddressType>::GetNextCieOrFde(DwarfFde** fde_entry) {
+  uint64_t start_offset = next_entries_offset_;
 
-    // Figure out the entry length and type.
-    uint32_t value32;
+  memory_.set_cur_offset(next_entries_offset_);
+  uint32_t value32;
+  if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  uint64_t cie_offset;
+  uint8_t cie_fde_encoding;
+  bool entry_is_cie = false;
+  if (value32 == static_cast<uint32_t>(-1)) {
+    // 64 bit entry.
+    uint64_t value64;
+    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    next_entries_offset_ = memory_.cur_offset() + value64;
+    // Read the Cie Id of a Cie or the pointer of the Fde.
+    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    if (value64 == cie64_value_) {
+      entry_is_cie = true;
+      cie_fde_encoding = DW_EH_PE_sdata8;
+    } else {
+      cie_offset = this->GetCieOffsetFromFde64(value64);
+    }
+  } else {
+    next_entries_offset_ = memory_.cur_offset() + value32;
+
+    // 32 bit Cie
     if (!memory_.ReadBytes(&value32, sizeof(value32))) {
       last_error_.code = DWARF_ERROR_MEMORY_INVALID;
       last_error_.address = memory_.cur_offset();
       return false;
     }
 
-    uint64_t next_entry_offset;
-    if (value32 == static_cast<uint32_t>(-1)) {
-      uint64_t value64;
-      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-      next_entry_offset = memory_.cur_offset() + value64;
-
-      // Read the Cie Id of a Cie or the pointer of the Fde.
-      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-
-      if (value64 == cie64_value_) {
-        // Cie 64 bit
-        address_encoding = DW_EH_PE_sdata8;
-        if (!GetCieInfo(&segment_size, &address_encoding)) {
-          return false;
-        }
-        cie_offset = cur_entry_offset;
-      } else {
-        uint64_t last_cie_offset = GetCieOffsetFromFde64(value64);
-        if (last_cie_offset != cie_offset) {
-          // This means that this Fde is not following the Cie.
-          last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-          return false;
-        }
-
-        // Fde 64 bit
-        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
-          return false;
-        }
-      }
+    if (value32 == cie32_value_) {
+      entry_is_cie = true;
+      cie_fde_encoding = DW_EH_PE_sdata4;
     } else {
-      next_entry_offset = memory_.cur_offset() + value32;
-
-      // Read the Cie Id of a Cie or the pointer of the Fde.
-      if (!memory_.ReadBytes(&value32, sizeof(value32))) {
-        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-        last_error_.address = memory_.cur_offset();
-        return false;
-      }
-
-      if (value32 == cie32_value_) {
-        // Cie 32 bit
-        address_encoding = DW_EH_PE_sdata4;
-        if (!GetCieInfo(&segment_size, &address_encoding)) {
-          return false;
-        }
-        cie_offset = cur_entry_offset;
-      } else {
-        uint64_t last_cie_offset = GetCieOffsetFromFde32(value32);
-        if (last_cie_offset != cie_offset) {
-          // This means that this Fde is not following the Cie.
-          last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-          return false;
-        }
-
-        // Fde 32 bit
-        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
-          return false;
-        }
-      }
+      cie_offset = this->GetCieOffsetFromFde32(value32);
     }
-
-    if (next_entry_offset < memory_.cur_offset()) {
-      // Simply consider the processing done in this case.
-      break;
-    }
-    memory_.set_cur_offset(next_entry_offset);
   }
 
-  // Sort the entries.
-  std::sort(fdes_.begin(), fdes_.end(), [](const FdeInfo& a, const FdeInfo& b) {
-    if (a.start == b.start) return a.end < b.end;
-    return a.start < b.start;
-  });
+  if (entry_is_cie) {
+    DwarfCie* cie = &cie_entries_[start_offset];
+    cie->lsda_encoding = DW_EH_PE_omit;
+    cie->cfa_instructions_end = next_entries_offset_;
+    cie->fde_address_encoding = cie_fde_encoding;
 
-  fde_count_ = fdes_.size();
+    if (!this->FillInCie(cie)) {
+      cie_entries_.erase(start_offset);
+      return false;
+    }
+    *fde_entry = nullptr;
+  } else {
+    DwarfFde* fde = &fde_entries_[start_offset];
+    fde->cfa_instructions_end = next_entries_offset_;
+    fde->cie_offset = cie_offset;
 
+    if (!this->FillInFde(fde)) {
+      fde_entries_.erase(start_offset);
+      return false;
+    }
+    *fde_entry = fde;
+  }
   return true;
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
-  if (fde_count_ == 0) {
-    return false;
-  }
-
-  size_t first = 0;
-  size_t last = fde_count_;
-  while (first < last) {
-    size_t current = (first + last) / 2;
-    const FdeInfo* info = &fdes_[current];
-    if (pc >= info->start && pc <= info->end) {
-      *fde_offset = info->offset;
-      return true;
-    }
-
-    if (pc < info->start) {
-      last = current;
+void DwarfSectionImplNoHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
+  // Loop through the already cached entries.
+  uint64_t entry_offset = entries_offset_;
+  while (entry_offset < next_entries_offset_) {
+    auto cie_it = cie_entries_.find(entry_offset);
+    if (cie_it != cie_entries_.end()) {
+      entry_offset = cie_it->second.cfa_instructions_end;
     } else {
-      first = current + 1;
+      auto fde_it = fde_entries_.find(entry_offset);
+      if (fde_it == fde_entries_.end()) {
+        // No fde or cie at this entry, should not be possible.
+        return;
+      }
+      entry_offset = fde_it->second.cfa_instructions_end;
+      fdes->push_back(&fde_it->second);
     }
   }
-  return false;
+
+  while (next_entries_offset_ < entries_end_) {
+    DwarfFde* fde;
+    if (!GetNextCieOrFde(&fde)) {
+      break;
+    }
+    if (fde != nullptr) {
+      InsertFde(fde);
+      fdes->push_back(fde);
+    }
+
+    if (next_entries_offset_ < memory_.cur_offset()) {
+      // Simply consider the processing done in this case.
+      break;
+    }
+  }
 }
 
 template <typename AddressType>
-const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromIndex(size_t index) {
-  if (index >= fdes_.size()) {
-    return nullptr;
+const DwarfFde* DwarfSectionImplNoHdr<AddressType>::GetFdeFromPc(uint64_t pc) {
+  // Search in the list of fdes we already have.
+  auto it = fdes_.upper_bound(pc);
+  if (it != fdes_.end()) {
+    if (pc >= it->second.first) {
+      return it->second.second;
+    }
   }
-  return this->GetFdeFromOffset(fdes_[index].offset);
+
+  // The section might have overlapping pcs in fdes, so it is necessary
+  // to do a linear search of the fdes by pc. As fdes are read, a cached
+  // search map is created.
+  while (next_entries_offset_ < entries_end_) {
+    DwarfFde* fde;
+    if (!GetNextCieOrFde(&fde)) {
+      return nullptr;
+    }
+    if (fde != nullptr) {
+      InsertFde(fde);
+      if (pc >= fde->pc_start && pc < fde->pc_end) {
+        return fde;
+      }
+    }
+
+    if (next_entries_offset_ < memory_.cur_offset()) {
+      // Simply consider the processing done in this case.
+      break;
+    }
+  }
+  return nullptr;
 }
 
 // Explicitly instantiate DwarfSectionImpl
 template class DwarfSectionImpl<uint32_t>;
 template class DwarfSectionImpl<uint64_t>;
 
+// Explicitly instantiate DwarfSectionImplNoHdr
+template class DwarfSectionImplNoHdr<uint32_t>;
+template class DwarfSectionImplNoHdr<uint64_t>;
+
 // Explicitly instantiate DwarfDebugFrame
 template class DwarfDebugFrame<uint32_t>;
 template class DwarfDebugFrame<uint64_t>;
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 3762107..4723606 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -53,7 +53,7 @@
 
   valid_ = interface_->Init(&load_bias_);
   if (valid_) {
-    interface_->InitHeaders();
+    interface_->InitHeaders(load_bias_);
     if (init_gnu_debugdata) {
       InitGnuDebugdata();
     } else {
@@ -83,7 +83,7 @@
   // is in the uncompressed data.
   uint64_t load_bias;
   if (gnu->Init(&load_bias)) {
-    gnu->InitHeaders();
+    gnu->InitHeaders(load_bias);
     interface_->SetGnuDebugdataInterface(gnu);
   } else {
     // Free all of the memory associated with the gnu_debugdata section.
@@ -103,9 +103,9 @@
 
 bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
   std::lock_guard<std::mutex> guard(lock_);
-  return valid_ && (interface_->GetFunctionName(addr, load_bias_, name, func_offset) ||
-                    (gnu_debugdata_interface_ && gnu_debugdata_interface_->GetFunctionName(
-                                                     addr, load_bias_, name, func_offset)));
+  return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
+                    (gnu_debugdata_interface_ &&
+                     gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
 }
 
 bool Elf::GetGlobalVariable(const std::string& name, uint64_t* memory_address) {
@@ -174,7 +174,7 @@
 
   // Lock during the step which can update information in the object.
   std::lock_guard<std::mutex> guard(lock_);
-  return interface_->Step(adjusted_rel_pc, load_bias_, regs, process_memory, finished);
+  return interface_->Step(adjusted_rel_pc, regs, process_memory, finished);
 }
 
 bool Elf::IsValidElf(Memory* memory) {
@@ -220,7 +220,6 @@
   if (!valid_ || pc < load_bias_) {
     return false;
   }
-  pc -= load_bias_;
 
   if (interface_->IsValidPc(pc)) {
     return true;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 4c05a1b..2c00456 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -124,10 +124,10 @@
 }
 
 template <typename AddressType>
-void ElfInterface::InitHeadersWithTemplate() {
+void ElfInterface::InitHeadersWithTemplate(uint64_t load_bias) {
   if (eh_frame_hdr_offset_ != 0) {
     eh_frame_.reset(new DwarfEhFrameWithHdr<AddressType>(memory_));
-    if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_)) {
+    if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, load_bias)) {
       eh_frame_.reset(nullptr);
     }
   }
@@ -136,7 +136,7 @@
     // If there is an eh_frame section without an eh_frame_hdr section,
     // or using the frame hdr object failed to init.
     eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
-    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) {
+    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, load_bias)) {
       eh_frame_.reset(nullptr);
     }
   }
@@ -150,7 +150,7 @@
 
   if (debug_frame_offset_ != 0) {
     debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
-    if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) {
+    if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_, load_bias)) {
       debug_frame_.reset(nullptr);
       debug_frame_offset_ = 0;
       debug_frame_size_ = static_cast<uint64_t>(-1);
@@ -204,49 +204,19 @@
   uint64_t offset = ehdr.e_phoff;
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
     PhdrType phdr;
-    if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
+    if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
       last_error_.code = ERROR_MEMORY_INVALID;
-      last_error_.address =
-          offset + reinterpret_cast<uintptr_t>(&phdr.p_type) - reinterpret_cast<uintptr_t>(&phdr);
+      last_error_.address = offset;
       return false;
     }
 
-    if (HandleType(offset, phdr.p_type, *load_bias)) {
-      continue;
-    }
-
     switch (phdr.p_type) {
     case PT_LOAD:
     {
-      // Get the flags first, if this isn't an executable header, ignore it.
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_flags) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       if ((phdr.p_flags & PF_X) == 0) {
         continue;
       }
 
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_vaddr) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
                                           static_cast<size_t>(phdr.p_memsz)};
       if (phdr.p_offset == 0) {
@@ -256,46 +226,20 @@
     }
 
     case PT_GNU_EH_FRAME:
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       // This is really the pointer to the .eh_frame_hdr section.
       eh_frame_hdr_offset_ = phdr.p_offset;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       eh_frame_hdr_size_ = phdr.p_memsz;
       break;
 
     case PT_DYNAMIC:
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       dynamic_offset_ = phdr.p_offset;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_vaddr) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       dynamic_vaddr_ = phdr.p_vaddr;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       dynamic_size_ = phdr.p_memsz;
       break;
+
+    default:
+      HandleUnknownType(phdr.p_type, phdr.p_offset, phdr.p_filesz);
+      break;
     }
   }
   return true;
@@ -313,8 +257,7 @@
   ShdrType shdr;
   if (ehdr.e_shstrndx < ehdr.e_shnum) {
     uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
-    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))) {
+    if (memory_->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
       sec_offset = shdr.sh_offset;
       sec_size = shdr.sh_size;
     }
@@ -441,14 +384,14 @@
 }
 
 template <typename SymType>
-bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
+bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
                                                uint64_t* func_offset) {
   if (symbols_.empty()) {
     return false;
   }
 
   for (const auto symbol : symbols_) {
-    if (symbol->GetName<SymType>(addr, load_bias, memory_, name, func_offset)) {
+    if (symbol->GetName<SymType>(addr, memory_, name, func_offset)) {
       return true;
     }
   }
@@ -469,34 +412,25 @@
   return false;
 }
 
-bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                        bool* finished) {
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
   last_error_.code = ERROR_NONE;
   last_error_.address = 0;
 
-  // Adjust the load bias to get the real relative pc.
-  if (pc < load_bias) {
-    last_error_.code = ERROR_UNWIND_INFO;
-    return false;
-  }
-  uint64_t adjusted_pc = pc - load_bias;
-
   // Try the debug_frame first since it contains the most specific unwind
   // information.
   DwarfSection* debug_frame = debug_frame_.get();
-  if (debug_frame != nullptr && debug_frame->Step(adjusted_pc, regs, process_memory, finished)) {
+  if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) {
     return true;
   }
 
   // Try the eh_frame next.
   DwarfSection* eh_frame = eh_frame_.get();
-  if (eh_frame != nullptr && eh_frame->Step(adjusted_pc, regs, process_memory, finished)) {
+  if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
     return true;
   }
 
-  // Finally try the gnu_debugdata interface, but always use a zero load bias.
   if (gnu_debugdata_interface_ != nullptr &&
-      gnu_debugdata_interface_->Step(pc, 0, regs, process_memory, finished)) {
+      gnu_debugdata_interface_->Step(pc, regs, process_memory, finished)) {
     return true;
   }
 
@@ -559,8 +493,8 @@
 }
 
 // Instantiate all of the needed template functions.
-template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
-template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>(uint64_t);
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>(uint64_t);
 
 template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(uint64_t*);
 template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(uint64_t*);
@@ -574,9 +508,9 @@
 template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
 template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
 
-template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, uint64_t, std::string*,
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
                                                                    uint64_t*);
-template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, uint64_t, std::string*,
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
                                                                    uint64_t*);
 
 template bool ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(const std::string&, uint64_t*);
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index f93baeb..3dd5d54 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -26,6 +26,14 @@
 
 namespace unwindstack {
 
+bool ElfInterfaceArm::Init(uint64_t* load_bias) {
+  if (!ElfInterface32::Init(load_bias)) {
+    return false;
+  }
+  load_bias_ = *load_bias;
+  return true;
+}
+
 bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
   if (start_offset_ == 0 || total_entries_ == 0) {
     last_error_.code = ERROR_UNWIND_INFO;
@@ -79,41 +87,35 @@
 #define PT_ARM_EXIDX 0x70000001
 #endif
 
-bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) {
+void ElfInterfaceArm::HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) {
   if (type != PT_ARM_EXIDX) {
-    return false;
+    return;
   }
 
-  Elf32_Phdr phdr;
-  if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
-    return true;
-  }
-  if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-    return true;
-  }
-  start_offset_ = phdr.p_vaddr - load_bias;
-  total_entries_ = phdr.p_memsz / 8;
-  return true;
+  // The offset already takes into account the load bias.
+  start_offset_ = ph_offset;
+
+  // Always use filesz instead of memsz. In most cases they are the same,
+  // but some shared libraries wind up setting one correctly and not the other.
+  total_entries_ = ph_filesz / 8;
 }
 
-bool ElfInterfaceArm::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                           bool* finished) {
+bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
   // 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, load_bias, regs, process_memory, finished) ||
-         StepExidx(pc, load_bias, regs, process_memory, finished);
+  return ElfInterface32::Step(pc, regs, process_memory, finished) ||
+         StepExidx(pc, regs, process_memory, finished);
 }
 
-bool ElfInterfaceArm::StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                                bool* finished) {
+bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
   // Adjust the load bias to get the real relative pc.
-  if (pc < load_bias) {
+  if (pc < load_bias_) {
     last_error_.code = ERROR_UNWIND_INFO;
     return false;
   }
-  pc -= load_bias;
+  pc -= load_bias_;
 
   RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
   uint64_t entry_offset;
@@ -167,13 +169,12 @@
   return return_value;
 }
 
-bool ElfInterfaceArm::GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                                      uint64_t* offset) {
+bool ElfInterfaceArm::GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) {
   // For ARM, thumb function symbols have bit 0 set, but the address passed
   // in here might not have this bit set and result in a failure to find
   // the thumb function names. Adjust the address and offset to account
   // for this possible case.
-  if (ElfInterface32::GetFunctionName(addr | 1, load_bias, name, offset)) {
+  if (ElfInterface32::GetFunctionName(addr | 1, name, offset)) {
     *offset &= ~1;
     return true;
   }
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index c1597ce..4c3a0c3 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -64,28 +64,30 @@
   iterator begin() { return iterator(this, 0); }
   iterator end() { return iterator(this, total_entries_); }
 
+  bool Init(uint64_t* load_bias) override;
+
   bool GetPrel31Addr(uint32_t offset, uint32_t* addr);
 
   bool FindEntry(uint32_t pc, uint64_t* entry_offset);
 
-  bool HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) override;
+  void HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) override;
 
-  bool Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-            bool* finished) override;
+  bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
 
-  bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                 bool* finished);
+  bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
 
-  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                       uint64_t* offset) override;
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) override;
 
   uint64_t start_offset() { return start_offset_; }
 
   size_t total_entries() { return total_entries_; }
 
+  void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
+
  protected:
   uint64_t start_offset_ = 0;
   size_t total_entries_ = 0;
+  uint64_t load_bias_ = 0;
 
   std::unordered_map<size_t, uint32_t> addrs_;
 };
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
new file mode 100644
index 0000000..952b332
--- /dev/null
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <pthread.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/LocalUnwinder.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+namespace unwindstack {
+
+bool LocalUnwinder::Init() {
+  pthread_rwlock_init(&maps_rwlock_, nullptr);
+
+  // Create the maps.
+  maps_.reset(new unwindstack::LocalUpdatableMaps());
+  if (!maps_->Parse()) {
+    maps_.reset();
+    return false;
+  }
+
+  process_memory_ = unwindstack::Memory::CreateProcessMemory(getpid());
+
+  return true;
+}
+
+bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) {
+  for (const std::string& skip_library : skip_libraries_) {
+    if (skip_library == map_name) {
+      return true;
+    }
+  }
+  return false;
+}
+
+MapInfo* LocalUnwinder::GetMapInfo(uint64_t pc) {
+  pthread_rwlock_rdlock(&maps_rwlock_);
+  MapInfo* map_info = maps_->Find(pc);
+  pthread_rwlock_unlock(&maps_rwlock_);
+
+  if (map_info == nullptr) {
+    pthread_rwlock_wrlock(&maps_rwlock_);
+    // This is guaranteed not to invalidate any previous MapInfo objects so
+    // we don't need to worry about any MapInfo* values already in use.
+    if (maps_->Reparse()) {
+      map_info = maps_->Find(pc);
+    }
+    pthread_rwlock_unlock(&maps_rwlock_);
+  }
+
+  return map_info;
+}
+
+bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
+  std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+  unwindstack::RegsGetLocal(regs.get());
+
+  size_t num_frames = 0;
+  bool adjust_pc = false;
+  while (true) {
+    uint64_t cur_pc = regs->pc();
+    uint64_t cur_sp = regs->sp();
+
+    MapInfo* map_info = GetMapInfo(cur_pc);
+    if (map_info == nullptr) {
+      break;
+    }
+
+    Elf* elf = map_info->GetElf(process_memory_, true);
+    uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
+    uint64_t step_pc = rel_pc;
+    uint64_t pc_adjustment;
+    if (adjust_pc) {
+      pc_adjustment = regs->GetPcAdjustment(rel_pc, elf);
+    } else {
+      pc_adjustment = 0;
+    }
+    step_pc -= pc_adjustment;
+    // Skip any locations that are within this library.
+    if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) {
+      // Add frame information.
+      std::string func_name;
+      uint64_t func_offset;
+      if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) {
+        frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment,
+                                 func_name, func_offset);
+      } else {
+        frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0);
+      }
+      num_frames++;
+    }
+    if (!elf->valid()) {
+      break;
+    }
+    if (frame_info->size() == max_frames) {
+      break;
+    }
+
+    adjust_pc = true;
+    bool finished;
+    if (!elf->Step(rel_pc, step_pc, regs.get(), process_memory_.get(), &finished) || finished) {
+      break;
+    }
+    // pc and sp are the same, terminate the unwind.
+    if (cur_pc == regs->pc() && cur_sp == regs->sp()) {
+      break;
+    }
+  }
+  return num_frames != 0;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index bb682ea..e676a5a 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -105,4 +105,83 @@
   return "/proc/" + std::to_string(pid_) + "/maps";
 }
 
+const std::string LocalUpdatableMaps::GetMapsFile() const {
+  return "/proc/self/maps";
+}
+
+bool LocalUpdatableMaps::Reparse() {
+  // New maps will be added at the end without deleting the old ones.
+  size_t last_map_idx = maps_.size();
+  if (!Parse()) {
+    // Delete any maps added by the Parse call.
+    for (size_t i = last_map_idx; i < maps_.size(); i++) {
+      delete maps_[i];
+    }
+    maps_.resize(last_map_idx);
+    return false;
+  }
+
+  size_t total_entries = maps_.size();
+  size_t search_map_idx = 0;
+  for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
+    MapInfo* new_map_info = maps_[new_map_idx];
+    uint64_t start = new_map_info->start;
+    uint64_t end = new_map_info->end;
+    uint64_t flags = new_map_info->flags;
+    std::string* name = &new_map_info->name;
+    for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
+      MapInfo* info = maps_[old_map_idx];
+      if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
+        // No need to check
+        search_map_idx = old_map_idx + 1;
+        delete new_map_info;
+        maps_[new_map_idx] = nullptr;
+        total_entries--;
+        break;
+      } else if (info->start > start) {
+        // Stop, there isn't going to be a match.
+        search_map_idx = old_map_idx;
+        break;
+      }
+
+      // Never delete these maps, they may be in use. The assumption is
+      // that there will only every be a handfull of these so waiting
+      // to destroy them is not too expensive.
+      saved_maps_.push_back(info);
+      maps_[old_map_idx] = nullptr;
+      total_entries--;
+    }
+    if (search_map_idx >= last_map_idx) {
+      break;
+    }
+  }
+
+  // Now move out any of the maps that never were found.
+  for (size_t i = search_map_idx; i < last_map_idx; i++) {
+    saved_maps_.push_back(maps_[i]);
+    maps_[i] = nullptr;
+    total_entries--;
+  }
+
+  // Sort all of the values such that the nullptrs wind up at the end, then
+  // resize them away.
+  std::sort(maps_.begin(), maps_.end(), [](const auto* a, const auto* b) {
+    if (a == nullptr) {
+      return false;
+    } else if (b == nullptr) {
+      return true;
+    }
+    return a->start < b->start;
+  });
+  maps_.resize(total_entries);
+
+  return true;
+}
+
+LocalUpdatableMaps::~LocalUpdatableMaps() {
+  for (auto map_info : saved_maps_) {
+    delete map_info;
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index 25def40..14ebdbb 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -54,10 +54,7 @@
 }
 
 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;
-
+bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
   if (symbols_.size() != 0) {
     const Info* info = GetInfoFromCache(addr);
     if (info) {
@@ -81,9 +78,6 @@
     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.
@@ -134,8 +128,8 @@
 }
 
 // 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*);
+template bool Symbols::GetName<Elf32_Sym>(uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf64_Sym>(uint64_t, Memory*, std::string*, uint64_t*);
 
 template bool Symbols::GetGlobal<Elf32_Sym>(Memory*, const std::string&, uint64_t*);
 template bool Symbols::GetGlobal<Elf64_Sym>(Memory*, const std::string&, uint64_t*);
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 7d239c1..7fcd067 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -44,8 +44,7 @@
   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);
+  bool GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset);
 
   template <typename SymType>
   bool GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address);
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 209c54a..e9942de 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -43,7 +43,12 @@
 
   class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
    public:
-    iterator(DwarfSection* section, size_t index) : section_(section), index_(index) {}
+    iterator(DwarfSection* section, size_t index) : index_(index) {
+      section->GetFdes(&fdes_);
+      if (index_ == static_cast<size_t>(-1)) {
+        index_ = fdes_.size();
+      }
+    }
 
     iterator& operator++() {
       index_++;
@@ -65,32 +70,31 @@
     bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
     bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
 
-    const DwarfFde* operator*() { return section_->GetFdeFromIndex(index_); }
+    const DwarfFde* operator*() {
+      if (index_ > fdes_.size()) return nullptr;
+      return fdes_[index_];
+    }
 
    private:
-    DwarfSection* section_ = nullptr;
+    std::vector<const DwarfFde*> fdes_;
     size_t index_ = 0;
   };
 
   iterator begin() { return iterator(this, 0); }
-  iterator end() { return iterator(this, fde_count_); }
+  iterator end() { return iterator(this, static_cast<size_t>(-1)); }
 
   DwarfErrorCode LastErrorCode() { return last_error_.code; }
   uint64_t LastErrorAddress() { return last_error_.address; }
 
-  virtual bool Init(uint64_t offset, uint64_t size) = 0;
+  virtual bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) = 0;
 
   virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
 
-  virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
+  virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) = 0;
 
-  virtual bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) = 0;
+  virtual void GetFdes(std::vector<const DwarfFde*>* fdes) = 0;
 
-  virtual const DwarfFde* GetFdeFromIndex(size_t index) = 0;
-
-  const DwarfFde* GetFdeFromPc(uint64_t pc);
-
-  virtual const DwarfFde* GetFdeFromOffset(uint64_t fde_offset) = 0;
+  virtual const DwarfFde* GetFdeFromPc(uint64_t pc) = 0;
 
   virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
 
@@ -109,7 +113,6 @@
   uint32_t cie32_value_ = 0;
   uint64_t cie64_value_ = 0;
 
-  uint64_t fde_count_ = 0;
   std::unordered_map<uint64_t, DwarfFde> fde_entries_;
   std::unordered_map<uint64_t, DwarfCie> cie_entries_;
   std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
@@ -119,52 +122,73 @@
 template <typename AddressType>
 class DwarfSectionImpl : public DwarfSection {
  public:
-  struct FdeInfo {
-    FdeInfo(uint64_t offset, uint64_t start, uint64_t length)
-        : offset(offset), start(start), end(start + length) {}
-
-    uint64_t offset;
-    AddressType start;
-    AddressType end;
-  };
-
   DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
   virtual ~DwarfSectionImpl() = default;
 
-  bool Init(uint64_t offset, uint64_t size) override;
+  const DwarfCie* GetCieFromOffset(uint64_t offset);
 
-  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
-
-  const DwarfFde* GetFdeFromIndex(size_t index) override;
+  const DwarfFde* GetFdeFromOffset(uint64_t offset);
 
   bool EvalRegister(const DwarfLocation* loc, uint32_t reg, AddressType* reg_ptr, void* info);
 
   bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
             Regs* regs, bool* finished) override;
 
-  const DwarfCie* GetCie(uint64_t offset);
-  bool FillInCie(DwarfCie* cie);
-
-  const DwarfFde* GetFdeFromOffset(uint64_t offset) override;
-  bool FillInFde(DwarfFde* fde);
-
   bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
 
-  bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) override;
+  bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
 
  protected:
+  bool FillInCieHeader(DwarfCie* cie);
+
+  bool FillInCie(DwarfCie* cie);
+
+  bool FillInFdeHeader(DwarfFde* fde);
+
+  bool FillInFde(DwarfFde* fde);
+
   bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
                       RegsInfo<AddressType>* regs_info, bool* is_dex_pc);
 
-  bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
+  uint64_t load_bias_ = 0;
+  uint64_t entries_offset_ = 0;
+  uint64_t entries_end_ = 0;
+  uint64_t pc_offset_ = 0;
+};
 
-  bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding);
+template <typename AddressType>
+class DwarfSectionImplNoHdr : public DwarfSectionImpl<AddressType> {
+ public:
+  // Add these so that the protected members of DwarfSectionImpl
+  // can be accessed without needing a this->.
+  using DwarfSectionImpl<AddressType>::memory_;
+  using DwarfSectionImpl<AddressType>::pc_offset_;
+  using DwarfSectionImpl<AddressType>::entries_offset_;
+  using DwarfSectionImpl<AddressType>::entries_end_;
+  using DwarfSectionImpl<AddressType>::last_error_;
+  using DwarfSectionImpl<AddressType>::load_bias_;
+  using DwarfSectionImpl<AddressType>::cie_entries_;
+  using DwarfSectionImpl<AddressType>::fde_entries_;
+  using DwarfSectionImpl<AddressType>::cie32_value_;
+  using DwarfSectionImpl<AddressType>::cie64_value_;
 
-  bool CreateSortedFdeList();
+  DwarfSectionImplNoHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  virtual ~DwarfSectionImplNoHdr() = default;
 
-  std::vector<FdeInfo> fdes_;
-  uint64_t entries_offset_;
-  uint64_t entries_end_;
+  bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
+
+  const DwarfFde* GetFdeFromPc(uint64_t pc) override;
+
+  void GetFdes(std::vector<const DwarfFde*>* fdes) override;
+
+ protected:
+  bool GetNextCieOrFde(DwarfFde** fde_entry);
+
+  void InsertFde(const DwarfFde* fde);
+
+  uint64_t next_entries_offset_ = 0;
+
+  std::map<uint64_t, std::pair<uint64_t, const DwarfFde*>> fdes_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 3a221bc..5c1210d 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -54,17 +54,15 @@
 
   virtual bool Init(uint64_t* load_bias) = 0;
 
-  virtual void InitHeaders() = 0;
+  virtual void InitHeaders(uint64_t load_bias) = 0;
 
   virtual bool GetSoname(std::string* name) = 0;
 
-  virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                               uint64_t* offset) = 0;
+  virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
 
   virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
 
-  virtual bool Step(uint64_t rel_pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                    bool* finished);
+  virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
 
   virtual bool IsValidPc(uint64_t pc);
 
@@ -100,7 +98,7 @@
 
  protected:
   template <typename AddressType>
-  void InitHeadersWithTemplate();
+  void InitHeadersWithTemplate(uint64_t load_bias);
 
   template <typename EhdrType, typename PhdrType, typename ShdrType>
   bool ReadAllHeaders(uint64_t* load_bias);
@@ -115,13 +113,12 @@
   bool GetSonameWithTemplate(std::string* soname);
 
   template <typename SymType>
-  bool GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
-                                   uint64_t* func_offset);
+  bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
 
   template <typename SymType>
   bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
 
-  virtual bool HandleType(uint64_t, uint32_t, uint64_t) { return false; }
+  virtual void HandleUnknownType(uint32_t, uint64_t, uint64_t) {}
 
   template <typename EhdrType>
   static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
@@ -169,15 +166,16 @@
     return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(load_bias);
   }
 
-  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
+  void InitHeaders(uint64_t load_bias) override {
+    ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
+  }
 
   bool GetSoname(std::string* soname) override {
     return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
   }
 
-  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                       uint64_t* func_offset) override {
-    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, load_bias, name, func_offset);
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
   }
 
   bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
@@ -198,15 +196,16 @@
     return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(load_bias);
   }
 
-  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
+  void InitHeaders(uint64_t load_bias) override {
+    ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
+  }
 
   bool GetSoname(std::string* soname) override {
     return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
   }
 
-  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                       uint64_t* func_offset) override {
-    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, load_bias, name, func_offset);
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
   }
 
   bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
diff --git a/libunwindstack/include/unwindstack/LocalUnwinder.h b/libunwindstack/include/unwindstack/LocalUnwinder.h
new file mode 100644
index 0000000..80bb53e
--- /dev/null
+++ b/libunwindstack/include/unwindstack/LocalUnwinder.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+#define _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+struct MapInfo;
+
+struct LocalFrameData {
+  LocalFrameData(MapInfo* map_info, uint64_t pc, uint64_t rel_pc, const std::string& function_name,
+                 uint64_t function_offset)
+      : map_info(map_info),
+        pc(pc),
+        rel_pc(rel_pc),
+        function_name(function_name),
+        function_offset(function_offset) {}
+
+  MapInfo* map_info;
+  uint64_t pc;
+  uint64_t rel_pc;
+  std::string function_name;
+  uint64_t function_offset;
+};
+
+// This is a specialized class that should only be used for doing local unwinds.
+// The Unwind call can be made as multiple times on the same object, and it can
+// be called by multiple threads at the same time.
+// It is designed to be used in debugging circumstances to get a stack trace
+// as fast as possible.
+class LocalUnwinder {
+ public:
+  LocalUnwinder() = default;
+  LocalUnwinder(const std::vector<std::string>& skip_libraries) : skip_libraries_(skip_libraries) {}
+  ~LocalUnwinder() = default;
+
+  bool Init();
+
+  bool Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames);
+
+  bool ShouldSkipLibrary(const std::string& map_name);
+
+  MapInfo* GetMapInfo(uint64_t pc);
+
+  ErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
+
+ private:
+  pthread_rwlock_t maps_rwlock_;
+  std::unique_ptr<LocalUpdatableMaps> maps_ = nullptr;
+  std::shared_ptr<Memory> process_memory_;
+  std::vector<std::string> skip_libraries_;
+  ErrorData last_error_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_LOCAL_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 74e5c47..67fbed2 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -87,6 +87,19 @@
   virtual ~LocalMaps() = default;
 };
 
+class LocalUpdatableMaps : public Maps {
+ public:
+  LocalUpdatableMaps() : Maps() {}
+  virtual ~LocalUpdatableMaps();
+
+  bool Reparse();
+
+  const std::string GetMapsFile() const override;
+
+ private:
+  std::vector<MapInfo*> saved_maps_;
+};
+
 class BufferMaps : public Maps {
  public:
   BufferMaps(const char* buffer) : buffer_(buffer) {}
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index c0c07f4..dee5e98 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -41,18 +41,6 @@
 
   bool ReadFully(uint64_t addr, void* dst, size_t 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 ReadFully(offset, field, size);
-  }
-
   inline bool Read32(uint64_t addr, uint32_t* dst) {
     return ReadFully(addr, dst, sizeof(uint32_t));
   }
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 8d6d00d..5f3d1ea 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -36,8 +36,6 @@
 class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
  protected:
   void Init(Memory* process_memory = nullptr) {
-    TearDown();
-
     if (process_memory == nullptr) {
       process_memory = &process_memory_;
     }
@@ -50,8 +48,8 @@
     regs_arm_->set_sp(0);
 
     exidx_.reset(new ArmExidx(regs_arm_.get(), &elf_memory_, process_memory));
-    if (log_) {
-      exidx_->set_log(true);
+    if (log_ != ARM_LOG_NONE) {
+      exidx_->set_log(log_);
       exidx_->set_log_indent(0);
       exidx_->set_log_skip_execution(false);
     }
@@ -60,14 +58,20 @@
   }
 
   void SetUp() override {
-    if (GetParam() != "no_logging") {
-      log_ = false;
+    if (GetParam() == "no_logging") {
+      log_ = ARM_LOG_NONE;
+    } else if (GetParam() == "register_logging") {
+      log_ = ARM_LOG_BY_REG;
     } else {
-      log_ = true;
+      log_ = ARM_LOG_FULL;
     }
-    ResetLogs();
     elf_memory_.Clear();
     process_memory_.Clear();
+    ResetExidx();
+  }
+
+  void ResetExidx() {
+    ResetLogs();
     Init();
   }
 
@@ -77,7 +81,7 @@
 
   MemoryFake elf_memory_;
   MemoryFake process_memory_;
-  bool log_;
+  ArmLogType log_;
 };
 
 TEST_P(ArmExidxDecodeTest, vsp_incr) {
@@ -86,38 +90,59 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 4\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->clear();
   data_->push_back(0x01);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 8\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1000cU, exidx_->cfa());
+  ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->clear();
   data_->push_back(0x3f);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 256\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1010cU, exidx_->cfa());
+  ASSERT_EQ(0x10100U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, vsp_decr) {
@@ -126,38 +151,59 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 - 4\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0xfffcU, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->clear();
   data_->push_back(0x41);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 - 8\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0xfff4U, exidx_->cfa());
+  ASSERT_EQ(0xfff8U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->clear();
   data_->push_back(0x7f);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 - 256\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0xfef4U, exidx_->cfa());
+  ASSERT_EQ(0xff00U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, refuse_unwind) {
@@ -166,10 +212,14 @@
   data_->push_back(0x00);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
 }
@@ -182,29 +232,60 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_TRUE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r15}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r15}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 4\n"
+          "4 unwind r15 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
   ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0x8f);
   data_->push_back(0xff);
   for (size_t i = 0; i < 12; i++) {
-    process_memory_.SetData32(0x10004 + i * 4, i + 0x20);
+    process_memory_.SetData32(0x10000 + i * 4, i + 0x20);
   }
   exidx_->set_pc_set(false);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_TRUE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
-              GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
+                GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 48\n"
+          "4 unwind r4 = [cfa - 48]\n"
+          "4 unwind r5 = [cfa - 44]\n"
+          "4 unwind r6 = [cfa - 40]\n"
+          "4 unwind r7 = [cfa - 36]\n"
+          "4 unwind r8 = [cfa - 32]\n"
+          "4 unwind r9 = [cfa - 28]\n"
+          "4 unwind r10 = [cfa - 24]\n"
+          "4 unwind r11 = [cfa - 20]\n"
+          "4 unwind r12 = [cfa - 16]\n"
+          "4 unwind r13 = [cfa - 12]\n"
+          "4 unwind r14 = [cfa - 8]\n"
+          "4 unwind r15 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   // Popping r13 results in a modified cfa.
   ASSERT_EQ(0x29U, exidx_->cfa());
@@ -222,7 +303,7 @@
   ASSERT_EQ(0x2aU, (*exidx_->regs())[14]);
   ASSERT_EQ(0x2bU, (*exidx_->regs())[15]);
 
-  ResetLogs();
+  ResetExidx();
   exidx_->set_cfa(0x10034);
   data_->push_back(0x81);
   data_->push_back(0x28);
@@ -233,10 +314,22 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 12\n"
+          "4 unwind r7 = [cfa - 12]\n"
+          "4 unwind r9 = [cfa - 8]\n"
+          "4 unwind r12 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10040U, exidx_->cfa());
   ASSERT_EQ(0x11U, (*exidx_->regs())[7]);
@@ -255,34 +348,63 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r0\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(1U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
+  exidx_->set_cfa(0x100);
+  for (size_t i = 0; i < 15; i++) {
+    (*regs_arm_)[i] = i + 1;
+  }
   data_->push_back(0x93);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r3\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(4U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
+  exidx_->set_cfa(0x100);
+  for (size_t i = 0; i < 15; i++) {
+    (*regs_arm_)[i] = i + 1;
+  }
   data_->push_back(0x9e);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r14\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(15U, exidx_->cfa());
 }
@@ -292,22 +414,30 @@
   data_->push_back(0x9d);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
 
   // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0x9f);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
 }
@@ -319,53 +449,93 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 4\n"
+          "4 unwind r4 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
   ASSERT_EQ(0x14U, (*exidx_->regs())[4]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xa3);
-  process_memory_.SetData32(0x10004, 0x20);
-  process_memory_.SetData32(0x10008, 0x30);
-  process_memory_.SetData32(0x1000c, 0x40);
-  process_memory_.SetData32(0x10010, 0x50);
+  process_memory_.SetData32(0x10000, 0x20);
+  process_memory_.SetData32(0x10004, 0x30);
+  process_memory_.SetData32(0x10008, 0x40);
+  process_memory_.SetData32(0x1000c, 0x50);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 16\n"
+          "4 unwind r4 = [cfa - 16]\n"
+          "4 unwind r5 = [cfa - 12]\n"
+          "4 unwind r6 = [cfa - 8]\n"
+          "4 unwind r7 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10014U, exidx_->cfa());
+  ASSERT_EQ(0x10010U, exidx_->cfa());
   ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
   ASSERT_EQ(0x30U, (*exidx_->regs())[5]);
   ASSERT_EQ(0x40U, (*exidx_->regs())[6]);
   ASSERT_EQ(0x50U, (*exidx_->regs())[7]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xa7);
-  process_memory_.SetData32(0x10014, 0x41);
-  process_memory_.SetData32(0x10018, 0x51);
-  process_memory_.SetData32(0x1001c, 0x61);
-  process_memory_.SetData32(0x10020, 0x71);
-  process_memory_.SetData32(0x10024, 0x81);
-  process_memory_.SetData32(0x10028, 0x91);
-  process_memory_.SetData32(0x1002c, 0xa1);
-  process_memory_.SetData32(0x10030, 0xb1);
+  process_memory_.SetData32(0x10000, 0x41);
+  process_memory_.SetData32(0x10004, 0x51);
+  process_memory_.SetData32(0x10008, 0x61);
+  process_memory_.SetData32(0x1000c, 0x71);
+  process_memory_.SetData32(0x10010, 0x81);
+  process_memory_.SetData32(0x10014, 0x91);
+  process_memory_.SetData32(0x10018, 0xa1);
+  process_memory_.SetData32(0x1001c, 0xb1);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 32\n"
+          "4 unwind r4 = [cfa - 32]\n"
+          "4 unwind r5 = [cfa - 28]\n"
+          "4 unwind r6 = [cfa - 24]\n"
+          "4 unwind r7 = [cfa - 20]\n"
+          "4 unwind r8 = [cfa - 16]\n"
+          "4 unwind r9 = [cfa - 12]\n"
+          "4 unwind r10 = [cfa - 8]\n"
+          "4 unwind r11 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10034U, exidx_->cfa());
+  ASSERT_EQ(0x10020U, exidx_->cfa());
   ASSERT_EQ(0x41U, (*exidx_->regs())[4]);
   ASSERT_EQ(0x51U, (*exidx_->regs())[5]);
   ASSERT_EQ(0x61U, (*exidx_->regs())[6]);
@@ -384,57 +554,100 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 8\n"
+          "4 unwind r4 = [cfa - 8]\n"
+          "4 unwind r14 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
   ASSERT_EQ(0x12U, (*exidx_->regs())[4]);
   ASSERT_EQ(0x22U, (*exidx_->regs())[14]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xab);
-  process_memory_.SetData32(0x10008, 0x1);
-  process_memory_.SetData32(0x1000c, 0x2);
-  process_memory_.SetData32(0x10010, 0x3);
-  process_memory_.SetData32(0x10014, 0x4);
-  process_memory_.SetData32(0x10018, 0x5);
+  process_memory_.SetData32(0x10000, 0x1);
+  process_memory_.SetData32(0x10004, 0x2);
+  process_memory_.SetData32(0x10008, 0x3);
+  process_memory_.SetData32(0x1000c, 0x4);
+  process_memory_.SetData32(0x10010, 0x5);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 20\n"
+          "4 unwind r4 = [cfa - 20]\n"
+          "4 unwind r5 = [cfa - 16]\n"
+          "4 unwind r6 = [cfa - 12]\n"
+          "4 unwind r7 = [cfa - 8]\n"
+          "4 unwind r14 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x10014U, exidx_->cfa());
   ASSERT_EQ(0x1U, (*exidx_->regs())[4]);
   ASSERT_EQ(0x2U, (*exidx_->regs())[5]);
   ASSERT_EQ(0x3U, (*exidx_->regs())[6]);
   ASSERT_EQ(0x4U, (*exidx_->regs())[7]);
   ASSERT_EQ(0x5U, (*exidx_->regs())[14]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xaf);
-  process_memory_.SetData32(0x1001c, 0x1a);
-  process_memory_.SetData32(0x10020, 0x2a);
-  process_memory_.SetData32(0x10024, 0x3a);
-  process_memory_.SetData32(0x10028, 0x4a);
-  process_memory_.SetData32(0x1002c, 0x5a);
-  process_memory_.SetData32(0x10030, 0x6a);
-  process_memory_.SetData32(0x10034, 0x7a);
-  process_memory_.SetData32(0x10038, 0x8a);
-  process_memory_.SetData32(0x1003c, 0x9a);
+  process_memory_.SetData32(0x10000, 0x1a);
+  process_memory_.SetData32(0x10004, 0x2a);
+  process_memory_.SetData32(0x10008, 0x3a);
+  process_memory_.SetData32(0x1000c, 0x4a);
+  process_memory_.SetData32(0x10010, 0x5a);
+  process_memory_.SetData32(0x10014, 0x6a);
+  process_memory_.SetData32(0x10018, 0x7a);
+  process_memory_.SetData32(0x1001c, 0x8a);
+  process_memory_.SetData32(0x10020, 0x9a);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 36\n"
+          "4 unwind r4 = [cfa - 36]\n"
+          "4 unwind r5 = [cfa - 32]\n"
+          "4 unwind r6 = [cfa - 28]\n"
+          "4 unwind r7 = [cfa - 24]\n"
+          "4 unwind r8 = [cfa - 20]\n"
+          "4 unwind r9 = [cfa - 16]\n"
+          "4 unwind r10 = [cfa - 12]\n"
+          "4 unwind r11 = [cfa - 8]\n"
+          "4 unwind r14 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10040U, exidx_->cfa());
+  ASSERT_EQ(0x10024U, exidx_->cfa());
   ASSERT_EQ(0x1aU, (*exidx_->regs())[4]);
   ASSERT_EQ(0x2aU, (*exidx_->regs())[5]);
   ASSERT_EQ(0x3aU, (*exidx_->regs())[6]);
@@ -451,10 +664,17 @@
   data_->push_back(0xb0);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10000U, exidx_->cfa());
   ASSERT_EQ(ARM_STATUS_FINISH, exidx_->status());
@@ -466,10 +686,14 @@
   data_->push_back(0x00);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10000U, exidx_->cfa());
   ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -477,15 +701,19 @@
   // 10110001 xxxxyyyy: Spare (xxxx != 0000)
   for (size_t x = 1; x < 16; x++) {
     for (size_t y = 0; y < 16; y++) {
-      ResetLogs();
+      ResetExidx();
       data_->push_back(0xb1);
       data_->push_back((x << 4) | y);
       ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
       ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
-      if (log_) {
-        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
-      } else {
-        ASSERT_EQ("", GetFakeLogPrint());
+      switch (log_) {
+        case ARM_LOG_NONE:
+          ASSERT_EQ("", GetFakeLogPrint());
+          break;
+        case ARM_LOG_FULL:
+        case ARM_LOG_BY_REG:
+          ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+          break;
       }
       ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
       ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -494,29 +722,37 @@
 
   // 101101nn: Spare
   for (size_t n = 0; n < 4; n++) {
-    ResetLogs();
+    ResetExidx();
     data_->push_back(0xb4 | n);
     ASSERT_FALSE(exidx_->Decode()) << "n = " << n;
     ASSERT_EQ("", GetFakeLogBuf()) << "n = " << n;
-    if (log_) {
-      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
-    } else {
-      ASSERT_EQ("", GetFakeLogPrint());
+    switch (log_) {
+      case ARM_LOG_NONE:
+        ASSERT_EQ("", GetFakeLogPrint());
+        break;
+      case ARM_LOG_FULL:
+      case ARM_LOG_BY_REG:
+        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
+        break;
     }
     ASSERT_EQ(0x10000U, exidx_->cfa()) << "n = " << n;
     ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
   }
 
   // 11000111 00000000: Spare
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc7);
   data_->push_back(0x00);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10000U, exidx_->cfa());
   ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -524,15 +760,19 @@
   // 11000111 xxxxyyyy: Spare (xxxx != 0000)
   for (size_t x = 1; x < 16; x++) {
     for (size_t y = 0; y < 16; y++) {
-      ResetLogs();
+      ResetExidx();
       data_->push_back(0xc7);
       data_->push_back(0x10);
       ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
       ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
-      if (log_) {
-        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
-      } else {
-        ASSERT_EQ("", GetFakeLogPrint());
+      switch (log_) {
+        case ARM_LOG_NONE:
+          ASSERT_EQ("", GetFakeLogPrint());
+          break;
+        case ARM_LOG_FULL:
+        case ARM_LOG_BY_REG:
+          ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+          break;
       }
       ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
       ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -541,14 +781,18 @@
 
   // 11001yyy: Spare (yyy != 000, 001)
   for (size_t y = 2; y < 8; y++) {
-    ResetLogs();
+    ResetExidx();
     data_->push_back(0xc8 | y);
     ASSERT_FALSE(exidx_->Decode()) << "y = " << y;
     ASSERT_EQ("", GetFakeLogBuf()) << "y = " << y;
-    if (log_) {
-      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
-    } else {
-      ASSERT_EQ("", GetFakeLogPrint());
+    switch (log_) {
+      case ARM_LOG_NONE:
+        ASSERT_EQ("", GetFakeLogPrint());
+        break;
+      case ARM_LOG_FULL:
+      case ARM_LOG_BY_REG:
+        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
+        break;
     }
     ASSERT_EQ(0x10000U, exidx_->cfa()) << "y = " << y;
     ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -557,14 +801,18 @@
   // 11xxxyyy: Spare (xxx != 000, 001, 010)
   for (size_t x = 3; x < 8; x++) {
     for (size_t y = 0; y < 8; y++) {
-      ResetLogs();
+      ResetExidx();
       data_->push_back(0xc0 | (x << 3) | y);
       ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
       ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
-      if (log_) {
-        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
-      } else {
-        ASSERT_EQ("", GetFakeLogPrint());
+      switch (log_) {
+        case ARM_LOG_NONE:
+          ASSERT_EQ("", GetFakeLogPrint());
+          break;
+        case ARM_LOG_FULL:
+        case ARM_LOG_BY_REG:
+          ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+          break;
       }
       ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
       ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -580,47 +828,81 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 4\n"
+          "4 unwind r0 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
   ASSERT_EQ(0x45U, (*exidx_->regs())[0]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb1);
   data_->push_back(0x0a);
-  process_memory_.SetData32(0x10004, 0x23);
-  process_memory_.SetData32(0x10008, 0x24);
+  process_memory_.SetData32(0x10000, 0x23);
+  process_memory_.SetData32(0x10004, 0x24);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 8\n"
+          "4 unwind r1 = [cfa - 8]\n"
+          "4 unwind r3 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1000cU, exidx_->cfa());
+  ASSERT_EQ(0x10008U, exidx_->cfa());
   ASSERT_EQ(0x23U, (*exidx_->regs())[1]);
   ASSERT_EQ(0x24U, (*exidx_->regs())[3]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb1);
   data_->push_back(0x0f);
-  process_memory_.SetData32(0x1000c, 0x65);
-  process_memory_.SetData32(0x10010, 0x54);
-  process_memory_.SetData32(0x10014, 0x43);
-  process_memory_.SetData32(0x10018, 0x32);
+  process_memory_.SetData32(0x10000, 0x65);
+  process_memory_.SetData32(0x10004, 0x54);
+  process_memory_.SetData32(0x10008, 0x43);
+  process_memory_.SetData32(0x1000c, 0x32);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 16\n"
+          "4 unwind r0 = [cfa - 16]\n"
+          "4 unwind r1 = [cfa - 12]\n"
+          "4 unwind r2 = [cfa - 8]\n"
+          "4 unwind r3 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x10010U, exidx_->cfa());
   ASSERT_EQ(0x65U, (*exidx_->regs())[0]);
   ASSERT_EQ(0x54U, (*exidx_->regs())[1]);
   ASSERT_EQ(0x43U, (*exidx_->regs())[2]);
@@ -634,28 +916,42 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 1024\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10400U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb2);
   data_->push_back(0xff);
   data_->push_back(0x02);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 2048\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10c00U, exidx_->cfa());
+  ASSERT_EQ(0x10800U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb2);
   data_->push_back(0xff);
   data_->push_back(0x82);
@@ -663,12 +959,19 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 3147776\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x311400U, exidx_->cfa());
+  ASSERT_EQ(0x310800U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp_fstmfdx) {
@@ -678,25 +981,37 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x1000cU, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb3);
   data_->push_back(0x48);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10058U, exidx_->cfa());
+  ASSERT_EQ(0x1004cU, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) {
@@ -705,36 +1020,54 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x1000cU, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xbb);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10030U, exidx_->cfa());
+  ASSERT_EQ(0x10024U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xbf);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10074U, exidx_->cfa());
+  ASSERT_EQ(0x10044U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) {
@@ -743,36 +1076,54 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc2);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10020U, exidx_->cfa());
+  ASSERT_EQ(0x10018U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc5);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10050U, exidx_->cfa());
+  ASSERT_EQ(0x10030U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_mmx_wr) {
@@ -782,38 +1133,56 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc6);
   data_->push_back(0x25);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10038U, exidx_->cfa());
+  ASSERT_EQ(0x10030U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc6);
   data_->push_back(0xff);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x100b8U, exidx_->cfa());
+  ASSERT_EQ(0x10080U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) {
@@ -823,38 +1192,56 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc7);
   data_->push_back(0x0a);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1000cU, exidx_->cfa());
+  ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc7);
   data_->push_back(0x0f);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x10010U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) {
@@ -864,38 +1251,56 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc8);
   data_->push_back(0x14);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10030U, exidx_->cfa());
+  ASSERT_EQ(0x10028U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc8);
   data_->push_back(0xff);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x100b0U, exidx_->cfa());
+  ASSERT_EQ(0x10080U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) {
@@ -905,38 +1310,56 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc9);
   data_->push_back(0x23);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10028U, exidx_->cfa());
+  ASSERT_EQ(0x10020U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc9);
   data_->push_back(0xff);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x100a8U, exidx_->cfa());
+  ASSERT_EQ(0x10080U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) {
@@ -945,36 +1368,54 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xd2);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10020U, exidx_->cfa());
+  ASSERT_EQ(0x10018U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xd7);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10060U, exidx_->cfa());
+  ASSERT_EQ(0x10040U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, expect_truncated) {
@@ -1047,32 +1488,147 @@
 TEST_P(ArmExidxDecodeTest, eval_multiple_decodes) {
   // vsp = vsp + 4
   data_->push_back(0x00);
-  // vsp = vsp + 8
+  // vsp = vsp + 12
   data_->push_back(0x02);
   // Finish
   data_->push_back(0xb0);
 
   ASSERT_TRUE(exidx_->Eval());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 4\n"
-              "4 unwind vsp = vsp + 12\n"
-              "4 unwind finish\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind vsp = vsp + 4\n"
+          "4 unwind vsp = vsp + 12\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 16\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10010U, exidx_->cfa());
   ASSERT_FALSE(exidx_->pc_set());
 }
 
+TEST_P(ArmExidxDecodeTest, eval_vsp_add_after_pop) {
+  // Pop {r15}
+  data_->push_back(0x88);
+  data_->push_back(0x00);
+  // vsp = vsp + 12
+  data_->push_back(0x02);
+  // Finish
+  data_->push_back(0xb0);
+  process_memory_.SetData32(0x10000, 0x10);
+
+  ASSERT_TRUE(exidx_->Eval());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind pop {r15}\n"
+          "4 unwind vsp = vsp + 12\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 16\n"
+          "4 unwind r15 = [cfa - 16]\n",
+          GetFakeLogPrint());
+      break;
+  }
+  ASSERT_EQ(0x10010U, exidx_->cfa());
+  ASSERT_TRUE(exidx_->pc_set());
+  ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_add_large_after_pop) {
+  // Pop {r15}
+  data_->push_back(0x88);
+  data_->push_back(0x00);
+  // vsp = vsp + 1024
+  data_->push_back(0xb2);
+  data_->push_back(0x7f);
+  // Finish
+  data_->push_back(0xb0);
+  process_memory_.SetData32(0x10000, 0x10);
+
+  ASSERT_TRUE(exidx_->Eval());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind pop {r15}\n"
+          "4 unwind vsp = vsp + 1024\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 1028\n"
+          "4 unwind r15 = [cfa - 1028]\n",
+          GetFakeLogPrint());
+      break;
+  }
+  ASSERT_EQ(0x10404U, exidx_->cfa());
+  ASSERT_TRUE(exidx_->pc_set());
+  ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_sub_after_pop) {
+  // Pop {r15}
+  data_->push_back(0x88);
+  data_->push_back(0x00);
+  // vsp = vsp - 4
+  data_->push_back(0x41);
+  // Finish
+  data_->push_back(0xb0);
+  process_memory_.SetData32(0x10000, 0x10);
+
+  ASSERT_TRUE(exidx_->Eval());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind pop {r15}\n"
+          "4 unwind vsp = vsp - 8\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 - 4\n"
+          "4 unwind r15 = [cfa + 4]\n",
+          GetFakeLogPrint());
+      break;
+  }
+  ASSERT_EQ(0xfffcU, exidx_->cfa());
+  ASSERT_TRUE(exidx_->pc_set());
+  ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
 TEST_P(ArmExidxDecodeTest, eval_pc_set) {
   // vsp = vsp + 4
   data_->push_back(0x00);
-  // vsp = vsp + 8
+  // vsp = vsp + 12
   data_->push_back(0x02);
   // Pop {r15}
   data_->push_back(0x88);
   data_->push_back(0x00);
-  // vsp = vsp + 8
+  // vsp = vsp + 12
   data_->push_back(0x02);
   // Finish
   data_->push_back(0xb0);
@@ -1080,20 +1636,33 @@
   process_memory_.SetData32(0x10010, 0x10);
 
   ASSERT_TRUE(exidx_->Eval());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 4\n"
-              "4 unwind vsp = vsp + 12\n"
-              "4 unwind pop {r15}\n"
-              "4 unwind vsp = vsp + 12\n"
-              "4 unwind finish\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind vsp = vsp + 4\n"
+          "4 unwind vsp = vsp + 12\n"
+          "4 unwind pop {r15}\n"
+          "4 unwind vsp = vsp + 12\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 32\n"
+          "4 unwind r15 = [cfa - 16]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10020U, exidx_->cfa());
   ASSERT_TRUE(exidx_->pc_set());
   ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
 }
 
-INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
+INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest,
+                        ::testing::Values("logging", "register_logging", "no_logging"));
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index 8d0f0e5..79c799c 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -301,7 +301,7 @@
   elf_memory_.SetData32(0x1000, 0x7fff2340);
   elf_memory_.SetData32(0x1004, 1);
 
-  exidx_->set_log(true);
+  exidx_->set_log(ARM_LOG_FULL);
   exidx_->set_log_indent(0);
   exidx_->set_log_skip_execution(false);
 
@@ -316,7 +316,7 @@
   elf_memory_.SetData32(0x4000, 0x7ffa3000);
   elf_memory_.SetData32(0x4004, 0x80a8b0b0);
 
-  exidx_->set_log(true);
+  exidx_->set_log(ARM_LOG_FULL);
   exidx_->set_log_indent(0);
   exidx_->set_log_skip_execution(false);
 
@@ -330,7 +330,7 @@
   elf_memory_.SetData32(0x6234, 0x2);
   elf_memory_.SetData32(0x6238, 0x00112233);
 
-  exidx_->set_log(true);
+  exidx_->set_log(ARM_LOG_FULL);
   exidx_->set_log_indent(0);
   exidx_->set_log_skip_execution(false);
 
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index b17ca33..bb2e8f0 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -79,7 +79,7 @@
     this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
 
     ResetLogs();
-    ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+    ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
     std::string expected = "4 unwind Illegal\n";
     expected += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", i);
     ASSERT_EQ(expected, GetFakeLogPrint());
@@ -90,7 +90,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_nop) {
   this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
   std::string expected =
       "4 unwind DW_CFA_nop\n"
       "4 unwind Raw Data: 0x00\n";
@@ -101,7 +101,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_offset) {
   this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2002));
   std::string expected =
       "4 unwind DW_CFA_offset register(3) 4\n"
       "4 unwind Raw Data: 0x83 0x04\n";
@@ -111,7 +111,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2100, 0x2103));
   expected =
       "4 unwind DW_CFA_offset register(3) 132\n"
       "4 unwind Raw Data: 0x83 0x84 0x01\n";
@@ -122,7 +122,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended) {
   this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
   std::string expected =
       "4 unwind DW_CFA_offset_extended register(3) 2\n"
       "4 unwind Raw Data: 0x05 0x03 0x02\n";
@@ -132,7 +132,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
   expected =
       "4 unwind DW_CFA_offset_extended register(129) 2306\n"
       "4 unwind Raw Data: 0x05 0x81 0x01 0x82 0x12\n";
@@ -143,7 +143,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended_sf) {
   this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
   std::string expected =
       "4 unwind DW_CFA_offset_extended_sf register(5) 16\n"
       "4 unwind Raw Data: 0x11 0x05 0x10\n";
@@ -154,7 +154,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
   expected =
       "4 unwind DW_CFA_offset_extended_sf register(134) -1\n"
       "4 unwind Raw Data: 0x11 0x86 0x01 0xff 0x7f\n";
@@ -165,7 +165,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_restore) {
   this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
   std::string expected =
       "4 unwind DW_CFA_restore register(2)\n"
       "4 unwind Raw Data: 0xc2\n";
@@ -175,7 +175,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3003));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x3000, 0x3003));
   expected =
       "4 unwind DW_CFA_offset register(2) 4\n"
       "4 unwind Raw Data: 0x82 0x04\n"
@@ -188,7 +188,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_restore_extended) {
   this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4000, 0x4002));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4000, 0x4002));
   std::string expected =
       "4 unwind DW_CFA_restore_extended register(8)\n"
       "4 unwind Raw Data: 0x06 0x08\n";
@@ -198,7 +198,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5007));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x5000, 0x5007));
   expected =
       "4 unwind DW_CFA_offset_extended register(258) 4\n"
       "4 unwind Raw Data: 0x05 0x82 0x02 0x04\n"
@@ -228,7 +228,7 @@
   this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
   ResetLogs();
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam)));
   std::string expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
   expected += "4 unwind " + raw_data + "\n";
   expected += "4 unwind \n";
@@ -240,7 +240,7 @@
   ResetLogs();
   this->fde_.pc_start = address + 0x10;
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam)));
   expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
   expected += "4 unwind " + raw_data + "\n";
   expected += "4 unwind \n";
@@ -252,7 +252,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc) {
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x44});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x201));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x201));
   std::string expected =
       "4 unwind DW_CFA_advance_loc 4\n"
       "4 unwind Raw Data: 0x44\n"
@@ -260,22 +260,12 @@
       "4 unwind PC 0x2010\n";
   ASSERT_EQ(expected, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
-
-  ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x200, 0x201));
-  expected =
-      "4 unwind DW_CFA_advance_loc 4\n"
-      "4 unwind Raw Data: 0x44\n"
-      "4 unwind \n"
-      "4 unwind PC 0x2110\n";
-  ASSERT_EQ(expected, GetFakeLogPrint());
-  ASSERT_EQ("", GetFakeLogBuf());
 }
 
 TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc1) {
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x202));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x202));
   std::string expected =
       "4 unwind DW_CFA_advance_loc1 4\n"
       "4 unwind Raw Data: 0x02 0x04\n"
@@ -283,22 +273,12 @@
       "4 unwind PC 0x2004\n";
   ASSERT_EQ(expected, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
-
-  ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x10, 0x200, 0x202));
-  expected =
-      "4 unwind DW_CFA_advance_loc1 4\n"
-      "4 unwind Raw Data: 0x02 0x04\n"
-      "4 unwind \n"
-      "4 unwind PC 0x2014\n";
-  ASSERT_EQ(expected, GetFakeLogPrint());
-  ASSERT_EQ("", GetFakeLogBuf());
 }
 
 TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc2) {
   this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x600, 0x603));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x600, 0x603));
   std::string expected =
       "4 unwind DW_CFA_advance_loc2 772\n"
       "4 unwind Raw Data: 0x03 0x04 0x03\n"
@@ -306,22 +286,12 @@
       "4 unwind PC 0x2304\n";
   ASSERT_EQ(expected, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
-
-  ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1000, 0x600, 0x603));
-  expected =
-      "4 unwind DW_CFA_advance_loc2 772\n"
-      "4 unwind Raw Data: 0x03 0x04 0x03\n"
-      "4 unwind \n"
-      "4 unwind PC 0x3304\n";
-  ASSERT_EQ(expected, GetFakeLogPrint());
-  ASSERT_EQ("", GetFakeLogBuf());
 }
 
 TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc4) {
   this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x505));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x505));
   std::string expected =
       "4 unwind DW_CFA_advance_loc4 16909060\n"
       "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
@@ -329,22 +299,12 @@
       "4 unwind PC 0x1022304\n";
   ASSERT_EQ(expected, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
-
-  ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x500, 0x505));
-  expected =
-      "4 unwind DW_CFA_advance_loc4 16909060\n"
-      "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
-      "4 unwind \n"
-      "4 unwind PC 0x1024304\n";
-  ASSERT_EQ(expected, GetFakeLogPrint());
-  ASSERT_EQ("", GetFakeLogBuf());
 }
 
 TYPED_TEST_P(DwarfCfaLogTest, cfa_undefined) {
   this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa02));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xa02));
   std::string expected =
       "4 unwind DW_CFA_undefined register(9)\n"
       "4 unwind Raw Data: 0x07 0x09\n";
@@ -355,7 +315,7 @@
   dwarf_loc_regs_t cie_loc_regs;
   this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1a00, 0x1a03));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1a00, 0x1a03));
   expected =
       "4 unwind DW_CFA_undefined register(129)\n"
       "4 unwind Raw Data: 0x07 0x81 0x01\n";
@@ -366,7 +326,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_same) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
   std::string expected =
       "4 unwind DW_CFA_same_value register(127)\n"
       "4 unwind Raw Data: 0x08 0x7f\n";
@@ -376,7 +336,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2100, 0x2103));
   expected =
       "4 unwind DW_CFA_same_value register(255)\n"
       "4 unwind Raw Data: 0x08 0xff 0x01\n";
@@ -387,7 +347,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_register) {
   this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x303));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x303));
   std::string expected =
       "4 unwind DW_CFA_register register(2) register(1)\n"
       "4 unwind Raw Data: 0x09 0x02 0x01\n";
@@ -397,7 +357,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4305));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4300, 0x4305));
   expected =
       "4 unwind DW_CFA_register register(255) register(511)\n"
       "4 unwind Raw Data: 0x09 0xff 0x01 0xff 0x03\n";
@@ -408,7 +368,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_state) {
   this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x301));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x301));
 
   std::string expected =
       "4 unwind DW_CFA_remember_state\n"
@@ -419,7 +379,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4301));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4300, 0x4301));
 
   expected =
       "4 unwind DW_CFA_restore_state\n"
@@ -431,7 +391,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_state_cfa_offset_restore) {
   this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3004));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x3000, 0x3004));
 
   std::string expected =
       "4 unwind DW_CFA_remember_state\n"
@@ -447,7 +407,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa register(127) 116\n"
@@ -458,7 +418,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x205));
 
   expected =
       "4 unwind DW_CFA_def_cfa register(383) 628\n"
@@ -470,7 +430,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_sf) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_sf register(48) 37\n"
@@ -482,7 +442,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x205));
 
   expected =
       "4 unwind DW_CFA_def_cfa_sf register(163) -6\n"
@@ -494,7 +454,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_register) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_register register(114)\n"
@@ -505,7 +465,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
 
   expected =
       "4 unwind DW_CFA_def_cfa_register register(4217)\n"
@@ -517,7 +477,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_offset 89\n"
@@ -526,7 +486,7 @@
   ASSERT_EQ("", GetFakeLogBuf());
 
   ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   expected =
       "4 unwind DW_CFA_def_cfa_offset 89\n"
@@ -537,7 +497,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
 
   expected =
       "4 unwind DW_CFA_def_cfa_offset 1364\n"
@@ -549,7 +509,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset_sf) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
@@ -558,7 +518,7 @@
   ASSERT_EQ("", GetFakeLogBuf());
 
   ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   expected =
       "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
@@ -570,7 +530,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
 
   expected =
       "4 unwind DW_CFA_def_cfa_offset_sf -10\n"
@@ -582,7 +542,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_expression) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x04, 0x05});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x106));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x106));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_expression 4\n"
@@ -614,7 +574,7 @@
   }
   expected += '\n';
   this->memory_.SetMemory(0x200, ops);
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x284));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x284));
 
   expected = "4 unwind DW_CFA_def_cfa_expression 129\n" + expected;
   ASSERT_EQ(expected + op_string, GetFakeLogPrint());
@@ -624,7 +584,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_expression) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0xc0, 0xc1});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x105));
 
   std::string expected =
       "4 unwind DW_CFA_expression register(4) 2\n"
@@ -652,7 +612,7 @@
   expected = "4 unwind DW_CFA_expression register(255) 130\n" + expected + "\n";
 
   this->memory_.SetMemory(0x200, ops);
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x287));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x287));
 
   ASSERT_EQ(expected + op_string, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
@@ -661,7 +621,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
 
   std::string expected =
       "4 unwind DW_CFA_val_offset register(69) 84\n"
@@ -672,7 +632,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x400, 0x405));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x400, 0x405));
 
   expected =
       "4 unwind DW_CFA_val_offset register(290) 692\n"
@@ -684,7 +644,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset_sf) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
 
   std::string expected =
       "4 unwind DW_CFA_val_offset_sf register(86) 18\n"
@@ -696,7 +656,7 @@
   ResetLogs();
   this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa05));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xa05));
 
   expected =
       "4 unwind DW_CFA_val_offset_sf register(255) -64\n"
@@ -708,7 +668,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_val_expression) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0xb0, 0xb1});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x105));
 
   std::string expected =
       "4 unwind DW_CFA_val_expression register(5) 2\n"
@@ -737,7 +697,7 @@
 
   this->memory_.SetMemory(0xa00, ops);
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xaad));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xaad));
 
   ASSERT_EQ(expected + op_string, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
@@ -746,7 +706,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_args_size) {
   this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2002));
 
   std::string expected =
       "4 unwind DW_CFA_GNU_args_size 4\n"
@@ -757,7 +717,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5004));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x5000, 0x5004));
 
   expected =
       "4 unwind DW_CFA_GNU_args_size 65572\n"
@@ -769,7 +729,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_negative_offset_extended) {
   this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
 
   std::string expected =
       "4 unwind DW_CFA_GNU_negative_offset_extended register(8) 16\n"
@@ -780,7 +740,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
 
   expected =
       "4 unwind DW_CFA_GNU_negative_offset_extended register(257) 255\n"
@@ -792,7 +752,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_register_override) {
   this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x306));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x306));
 
   std::string expected =
       "4 unwind DW_CFA_register register(2) register(1)\n"
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index c28a41e..d620934 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -16,7 +16,8 @@
 
 #include <stdint.h>
 
-#include <gmock/gmock.h>
+#include <vector>
+
 #include <gtest/gtest.h>
 
 #include <unwindstack/DwarfError.h>
@@ -30,442 +31,377 @@
 namespace unwindstack {
 
 template <typename TypeParam>
-class MockDwarfDebugFrame : public DwarfDebugFrame<TypeParam> {
- public:
-  MockDwarfDebugFrame(Memory* memory) : DwarfDebugFrame<TypeParam>(memory) {}
-  ~MockDwarfDebugFrame() = default;
-
-  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
-  void TestSetOffset(uint64_t offset) { this->entries_offset_ = offset; }
-  void TestSetEndOffset(uint64_t offset) { this->entries_end_ = offset; }
-  void TestPushFdeInfo(const typename DwarfDebugFrame<TypeParam>::FdeInfo& info) {
-    this->fdes_.push_back(info);
-  }
-
-  uint64_t TestGetFdeCount() { return this->fde_count_; }
-  uint8_t TestGetOffset() { return this->offset_; }
-  uint8_t TestGetEndOffset() { return this->end_offset_; }
-  void TestGetFdeInfo(size_t index, typename DwarfDebugFrame<TypeParam>::FdeInfo* info) {
-    *info = this->fdes_[index];
-  }
-};
-
-template <typename TypeParam>
 class DwarfDebugFrameTest : public ::testing::Test {
  protected:
   void SetUp() override {
     memory_.Clear();
-    debug_frame_ = new MockDwarfDebugFrame<TypeParam>(&memory_);
+    debug_frame_ = new DwarfDebugFrame<TypeParam>(&memory_);
     ResetLogs();
   }
 
   void TearDown() override { delete debug_frame_; }
 
   MemoryFake memory_;
-  MockDwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
+  DwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
 };
 TYPED_TEST_CASE_P(DwarfDebugFrameTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init32) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData32(0x5108, 0x1500);
-  this->memory_.SetData32(0x510c, 0x200);
-
-  this->memory_.SetData32(0x5200, 0xfc);
-  this->memory_.SetData32(0x5204, 0);
-  this->memory_.SetData32(0x5208, 0x2500);
-  this->memory_.SetData32(0x520c, 0x300);
-
-  // CIE 32 information.
-  this->memory_.SetData32(0x5300, 0xfc);
-  this->memory_.SetData32(0x5304, 0xffffffff);
-  this->memory_.SetData8(0x5308, 1);
-  this->memory_.SetData8(0x5309, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5400, 0xfc);
-  this->memory_.SetData32(0x5404, 0x300);
-  this->memory_.SetData32(0x5408, 0x3500);
-  this->memory_.SetData32(0x540c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xfc);
-  this->memory_.SetData32(0x5504, 0x300);
-  this->memory_.SetData32(0x5508, 0x4500);
-  this->memory_.SetData32(0x550c, 0x500);
-
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
-
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x1500U, info.start);
-  EXPECT_EQ(0x1700U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(1, &info);
-  EXPECT_EQ(0x5200U, info.offset);
-  EXPECT_EQ(0x2500U, info.start);
-  EXPECT_EQ(0x2800U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(2, &info);
-  EXPECT_EQ(0x5400U, info.offset);
-  EXPECT_EQ(0x3500U, info.start);
-  EXPECT_EQ(0x3900U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(3, &info);
-  EXPECT_EQ(0x5500U, info.offset);
-  EXPECT_EQ(0x4500U, info.start);
-  EXPECT_EQ(0x4a00U, info.end);
+static void SetCie32(MemoryFake* memory, uint64_t offset, uint32_t length,
+                     std::vector<uint8_t> data) {
+  memory->SetData32(offset, length);
+  offset += 4;
+  // Indicates this is a cie.
+  memory->SetData32(offset, 0xffffffff);
+  offset += 4;
+  memory->SetMemory(offset, data);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init32_fde_not_following_cie) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0x1000);
-  this->memory_.SetData32(0x5108, 0x1500);
-  this->memory_.SetData32(0x510c, 0x200);
-
-  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
+static void SetCie64(MemoryFake* memory, uint64_t offset, uint64_t length,
+                     std::vector<uint8_t> data) {
+  memory->SetData32(offset, 0xffffffff);
+  offset += 4;
+  memory->SetData64(offset, length);
+  offset += 8;
+  // Indicates this is a cie.
+  memory->SetData64(offset, 0xffffffffffffffffUL);
+  offset += 8;
+  memory->SetMemory(offset, data);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init32_do_not_fail_on_bad_next_entry) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData32(0x5108, 0x1500);
-  this->memory_.SetData32(0x510c, 0x200);
-
-  this->memory_.SetData32(0x5200, 0xfc);
-  this->memory_.SetData32(0x5204, 0);
-  this->memory_.SetData32(0x5208, 0x2500);
-  this->memory_.SetData32(0x520c, 0x300);
-
-  // CIE 32 information.
-  this->memory_.SetData32(0x5300, 0);
-  this->memory_.SetData32(0x5304, 0xffffffff);
-  this->memory_.SetData8(0x5308, 1);
-  this->memory_.SetData8(0x5309, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5400, 0xfc);
-  this->memory_.SetData32(0x5404, 0x300);
-  this->memory_.SetData32(0x5408, 0x3500);
-  this->memory_.SetData32(0x540c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xfc);
-  this->memory_.SetData32(0x5504, 0x300);
-  this->memory_.SetData32(0x5508, 0x4500);
-  this->memory_.SetData32(0x550c, 0x500);
-
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(2U, this->debug_frame_->TestGetFdeCount());
-}
-
-TYPED_TEST_P(DwarfDebugFrameTest, Init64) {
-  // CIE 64 information.
-  this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
-  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
-  this->memory_.SetData64(0x510c, 0);
-  this->memory_.SetData64(0x5114, 0x1500);
-  this->memory_.SetData64(0x511c, 0x200);
-
-  this->memory_.SetData32(0x5200, 0xffffffff);
-  this->memory_.SetData64(0x5204, 0xf4);
-  this->memory_.SetData64(0x520c, 0);
-  this->memory_.SetData64(0x5214, 0x2500);
-  this->memory_.SetData64(0x521c, 0x300);
-
-  // CIE 64 information.
-  this->memory_.SetData32(0x5300, 0xffffffff);
-  this->memory_.SetData64(0x5304, 0xf4);
-  this->memory_.SetData64(0x530c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5314, 1);
-  this->memory_.SetData8(0x5315, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5400, 0xffffffff);
-  this->memory_.SetData64(0x5404, 0xf4);
-  this->memory_.SetData64(0x540c, 0x300);
-  this->memory_.SetData64(0x5414, 0x3500);
-  this->memory_.SetData64(0x541c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xffffffff);
-  this->memory_.SetData64(0x5504, 0xf4);
-  this->memory_.SetData64(0x550c, 0x300);
-  this->memory_.SetData64(0x5514, 0x4500);
-  this->memory_.SetData64(0x551c, 0x500);
-
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
-
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x1500U, info.start);
-  EXPECT_EQ(0x1700U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(1, &info);
-  EXPECT_EQ(0x5200U, info.offset);
-  EXPECT_EQ(0x2500U, info.start);
-  EXPECT_EQ(0x2800U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(2, &info);
-  EXPECT_EQ(0x5400U, info.offset);
-  EXPECT_EQ(0x3500U, info.start);
-  EXPECT_EQ(0x3900U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(3, &info);
-  EXPECT_EQ(0x5500U, info.offset);
-  EXPECT_EQ(0x4500U, info.start);
-  EXPECT_EQ(0x4a00U, info.end);
-}
-
-TYPED_TEST_P(DwarfDebugFrameTest, Init64_fde_not_following_cie) {
-  // CIE 64 information.
-  this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
-  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
-  this->memory_.SetData64(0x510c, 0x1000);
-  this->memory_.SetData64(0x5114, 0x1500);
-  this->memory_.SetData64(0x511c, 0x200);
-
-  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
-}
-
-TYPED_TEST_P(DwarfDebugFrameTest, Init64_do_not_fail_on_bad_next_entry) {
-  // CIE 64 information.
-  this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
-  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
-  this->memory_.SetData64(0x510c, 0);
-  this->memory_.SetData64(0x5114, 0x1500);
-  this->memory_.SetData64(0x511c, 0x200);
-
-  this->memory_.SetData32(0x5200, 0xffffffff);
-  this->memory_.SetData64(0x5204, 0xf4);
-  this->memory_.SetData64(0x520c, 0);
-  this->memory_.SetData64(0x5214, 0x2500);
-  this->memory_.SetData64(0x521c, 0x300);
-
-  // CIE 64 information.
-  this->memory_.SetData32(0x5300, 0xffffffff);
-  this->memory_.SetData64(0x5304, 0);
-  this->memory_.SetData64(0x530c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5314, 1);
-  this->memory_.SetData8(0x5315, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5400, 0xffffffff);
-  this->memory_.SetData64(0x5404, 0xf4);
-  this->memory_.SetData64(0x540c, 0x300);
-  this->memory_.SetData64(0x5414, 0x3500);
-  this->memory_.SetData64(0x541c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xffffffff);
-  this->memory_.SetData64(0x5504, 0xf4);
-  this->memory_.SetData64(0x550c, 0x300);
-  this->memory_.SetData64(0x5514, 0x4500);
-  this->memory_.SetData64(0x551c, 0x500);
-
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(2U, this->debug_frame_->TestGetFdeCount());
-}
-
-TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 1);
-  // Augment string.
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'});
-  // Code alignment factor.
-  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00});
-  // Data alignment factor.
-  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
-  // Return address register
-  this->memory_.SetData8(0x5014, 0x84);
-  // Augmentation length
-  this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00});
-  // R data.
-  this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
-
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
-  ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
-
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x1500U, info.start);
-  EXPECT_EQ(0x1700U, info.end);
-}
-
-TYPED_TEST_P(DwarfDebugFrameTest, Init_version4) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 4);
-  // Augment string.
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
-  // Address size.
-  this->memory_.SetData8(0x500e, 4);
-  // Segment size.
-  this->memory_.SetData8(0x500f, 0);
-  // Code alignment factor.
-  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00});
-  // Data alignment factor.
-  this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
-  // Return address register
-  this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10});
-  // Augmentation length
-  this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00});
-  // L data.
-  this->memory_.SetData8(0x501a, 0x10);
-  // P data.
-  this->memory_.SetData8(0x501b, DW_EH_PE_udata4);
-  this->memory_.SetData32(0x501c, 0x100);
-  // R data.
-  this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
-
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
-  ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
-
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x1500U, info.start);
-  EXPECT_EQ(0x1700U, info.end);
-}
-
-TYPED_TEST_P(DwarfDebugFrameTest, GetFdeOffsetFromPc) {
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  for (size_t i = 0; i < 9; i++) {
-    info.start = 0x1000 * (i + 1);
-    info.end = 0x1000 * (i + 2) - 0x10;
-    info.offset = 0x5000 + i * 0x20;
-    this->debug_frame_->TestPushFdeInfo(info);
+static void SetFde32(MemoryFake* memory, uint64_t offset, uint32_t length, uint64_t cie_offset,
+                     uint32_t pc_start, uint32_t pc_length, uint64_t segment_length = 0,
+                     std::vector<uint8_t>* data = nullptr) {
+  memory->SetData32(offset, length);
+  offset += 4;
+  memory->SetData32(offset, cie_offset);
+  offset += 4 + segment_length;
+  memory->SetData32(offset, pc_start);
+  offset += 4;
+  memory->SetData32(offset, pc_length);
+  if (data != nullptr) {
+    offset += 4;
+    memory->SetMemory(offset, *data);
   }
+}
 
-  this->debug_frame_->TestSetFdeCount(0);
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
-
-  this->debug_frame_->TestSetFdeCount(9);
-  ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
-  // Odd number of elements.
-  for (size_t i = 0; i < 9; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
-                                                                             << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
-        << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+static void SetFde64(MemoryFake* memory, uint64_t offset, uint64_t length, uint64_t cie_offset,
+                     uint64_t pc_start, uint64_t pc_length, uint64_t segment_length = 0,
+                     std::vector<uint8_t>* data = nullptr) {
+  memory->SetData32(offset, 0xffffffff);
+  offset += 4;
+  memory->SetData64(offset, length);
+  offset += 8;
+  memory->SetData64(offset, cie_offset);
+  offset += 8 + segment_length;
+  memory->SetData64(offset, pc_start);
+  offset += 8;
+  memory->SetData64(offset, pc_length);
+  if (data != nullptr) {
+    offset += 8;
+    memory->SetMemory(offset, *data);
   }
+}
 
-  // Even number of elements.
-  this->debug_frame_->TestSetFdeCount(10);
-  info.start = 0xa000;
-  info.end = 0xaff0;
-  info.offset = 0x5120;
-  this->debug_frame_->TestPushFdeInfo(info);
+static void SetFourFdes32(MemoryFake* memory) {
+  SetCie32(memory, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
 
-  for (size_t i = 0; i < 10; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
-                                                                             << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
-        << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
-  }
+  // FDE 32 information.
+  SetFde32(memory, 0x5100, 0xfc, 0, 0x1500, 0x200);
+  SetFde32(memory, 0x5200, 0xfc, 0, 0x2500, 0x300);
+
+  // CIE 32 information.
+  SetCie32(memory, 0x5300, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+  // FDE 32 information.
+  SetFde32(memory, 0x5400, 0xfc, 0x300, 0x3500, 0x400);
+  SetFde32(memory, 0x5500, 0xfc, 0x300, 0x4500, 0x500);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+
+  ASSERT_EQ(4U, fdes.size());
+
+  EXPECT_EQ(0x5000U, fdes[0]->cie_offset);
+  EXPECT_EQ(0x5110U, fdes[0]->cfa_instructions_offset);
+  EXPECT_EQ(0x5200U, fdes[0]->cfa_instructions_end);
+  EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+  EXPECT_EQ(0U, fdes[0]->lsda_address);
+  EXPECT_TRUE(fdes[0]->cie != nullptr);
+
+  EXPECT_EQ(0x5000U, fdes[1]->cie_offset);
+  EXPECT_EQ(0x5210U, fdes[1]->cfa_instructions_offset);
+  EXPECT_EQ(0x5300U, fdes[1]->cfa_instructions_end);
+  EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+  EXPECT_EQ(0U, fdes[1]->lsda_address);
+  EXPECT_TRUE(fdes[1]->cie != nullptr);
+
+  EXPECT_EQ(0x5300U, fdes[2]->cie_offset);
+  EXPECT_EQ(0x5410U, fdes[2]->cfa_instructions_offset);
+  EXPECT_EQ(0x5500U, fdes[2]->cfa_instructions_end);
+  EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+  EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+  EXPECT_EQ(0U, fdes[2]->lsda_address);
+  EXPECT_TRUE(fdes[2]->cie != nullptr);
+
+  EXPECT_EQ(0x5300U, fdes[3]->cie_offset);
+  EXPECT_EQ(0x5510U, fdes[3]->cfa_instructions_offset);
+  EXPECT_EQ(0x5600U, fdes[3]->cfa_instructions_end);
+  EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+  EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+  EXPECT_EQ(0U, fdes[3]->lsda_address);
+  EXPECT_TRUE(fdes[3]->cie != nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32_after_GetFdeFromPc) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+  EXPECT_EQ(0x3900U, fde->pc_end);
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+  ASSERT_EQ(4U, fdes.size());
+
+  // Verify that they got added in the correct order.
+  EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+  EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+  EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+  EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+  EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+  EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32_not_in_section) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+
+  ASSERT_EQ(3U, fdes.size());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x1600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x1500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0);
+  ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32_reverse) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x1600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x1500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0);
+  ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32_not_in_section) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde == nullptr);
+}
+
+static void SetFourFdes64(MemoryFake* memory) {
+  // CIE 64 information.
+  SetCie64(memory, 0x5000, 0xf4, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+  // FDE 64 information.
+  SetFde64(memory, 0x5100, 0xf4, 0, 0x1500, 0x200);
+  SetFde64(memory, 0x5200, 0xf4, 0, 0x2500, 0x300);
+
+  // CIE 64 information.
+  SetCie64(memory, 0x5300, 0xf4, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+  // FDE 64 information.
+  SetFde64(memory, 0x5400, 0xf4, 0x300, 0x3500, 0x400);
+  SetFde64(memory, 0x5500, 0xf4, 0x300, 0x4500, 0x500);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+
+  ASSERT_EQ(4U, fdes.size());
+
+  EXPECT_EQ(0x5000U, fdes[0]->cie_offset);
+  EXPECT_EQ(0x5124U, fdes[0]->cfa_instructions_offset);
+  EXPECT_EQ(0x5200U, fdes[0]->cfa_instructions_end);
+  EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+  EXPECT_EQ(0U, fdes[0]->lsda_address);
+  EXPECT_TRUE(fdes[0]->cie != nullptr);
+
+  EXPECT_EQ(0x5000U, fdes[1]->cie_offset);
+  EXPECT_EQ(0x5224U, fdes[1]->cfa_instructions_offset);
+  EXPECT_EQ(0x5300U, fdes[1]->cfa_instructions_end);
+  EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+  EXPECT_EQ(0U, fdes[1]->lsda_address);
+  EXPECT_TRUE(fdes[1]->cie != nullptr);
+
+  EXPECT_EQ(0x5300U, fdes[2]->cie_offset);
+  EXPECT_EQ(0x5424U, fdes[2]->cfa_instructions_offset);
+  EXPECT_EQ(0x5500U, fdes[2]->cfa_instructions_end);
+  EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+  EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+  EXPECT_EQ(0U, fdes[2]->lsda_address);
+  EXPECT_TRUE(fdes[2]->cie != nullptr);
+
+  EXPECT_EQ(0x5300U, fdes[3]->cie_offset);
+  EXPECT_EQ(0x5524U, fdes[3]->cfa_instructions_offset);
+  EXPECT_EQ(0x5600U, fdes[3]->cfa_instructions_end);
+  EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+  EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+  EXPECT_EQ(0U, fdes[3]->lsda_address);
+  EXPECT_TRUE(fdes[3]->cie != nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64_after_GetFdeFromPc) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+  EXPECT_EQ(0x2800U, fde->pc_end);
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+  ASSERT_EQ(4U, fdes.size());
+
+  // Verify that they got added in the correct order.
+  EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+  EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+  EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+  EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+  EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+  EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64_not_in_section) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+
+  ASSERT_EQ(3U, fdes.size());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc64) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x1600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x1500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0);
+  ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc64_reverse) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x1600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x1500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0);
+  ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc64_not_in_section) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde == nullptr);
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde32) {
-  this->debug_frame_->TestSetOffset(0x4000);
-
-  // CIE 32 information.
-  this->memory_.SetData32(0xf000, 0x100);
-  this->memory_.SetData32(0xf004, 0xffffffff);
-  this->memory_.SetData8(0xf008, 0x1);
-  this->memory_.SetData8(0xf009, '\0');
-  this->memory_.SetData8(0xf00a, 4);
-  this->memory_.SetData8(0xf00b, 8);
-  this->memory_.SetData8(0xf00c, 0x20);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x14000, 0x20);
-  this->memory_.SetData32(0x14004, 0xb000);
-  this->memory_.SetData32(0x14008, 0x9000);
-  this->memory_.SetData32(0x1400c, 0x100);
+  SetCie32(&this->memory_, 0xf000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  SetFde32(&this->memory_, 0x14000, 0x20, 0xf000, 0x9000, 0x100);
 
   const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x14000);
   ASSERT_TRUE(fde != nullptr);
@@ -492,24 +428,8 @@
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde64) {
-  this->debug_frame_->TestSetOffset(0x2000);
-
-  // CIE 64 information.
-  this->memory_.SetData32(0x6000, 0xffffffff);
-  this->memory_.SetData64(0x6004, 0x100);
-  this->memory_.SetData64(0x600c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x6014, 0x1);
-  this->memory_.SetData8(0x6015, '\0');
-  this->memory_.SetData8(0x6016, 4);
-  this->memory_.SetData8(0x6017, 8);
-  this->memory_.SetData8(0x6018, 0x20);
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x8000, 0xffffffff);
-  this->memory_.SetData64(0x8004, 0x200);
-  this->memory_.SetData64(0x800c, 0x4000);
-  this->memory_.SetData64(0x8014, 0x5000);
-  this->memory_.SetData64(0x801c, 0x300);
+  SetCie64(&this->memory_, 0x6000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  SetFde64(&this->memory_, 0x8000, 0x200, 0x6000, 0x5000, 0x300);
 
   const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x8000);
   ASSERT_TRUE(fde != nullptr);
@@ -535,11 +455,357 @@
   EXPECT_EQ(0x20U, fde->cie->return_address_register);
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie,
-                           Init32_do_not_fail_on_bad_next_entry, Init64,
-                           Init64_do_not_fail_on_bad_next_entry, Init64_fde_not_following_cie,
-                           Init_version1, Init_version4, GetFdeOffsetFromPc, GetCieFde32,
-                           GetCieFde64);
+static void VerifyCieVersion(const DwarfCie* cie, uint8_t version, uint8_t segment_size,
+                             uint8_t fde_encoding, uint64_t return_address, uint64_t start_offset,
+                             uint64_t end_offset) {
+  EXPECT_EQ(version, cie->version);
+  EXPECT_EQ(fde_encoding, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(segment_size, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(return_address, cie->return_address_register);
+  EXPECT_EQ(0x5000U + start_offset, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5000U + end_offset, cie->cfa_instructions_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_cie_cached) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+
+  std::vector<uint8_t> zero(0x100, 0);
+  this->memory_.SetMemory(0x5000, zero);
+  cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_cie_cached) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+
+  std::vector<uint8_t> zero(0x100, 0);
+  this->memory_.SetMemory(0x5000, zero);
+  cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version1) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version1) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version3) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{3, '\0', 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 3, 0, DW_EH_PE_sdata4, 0x181, 0xe, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version3) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{3, '\0', 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 3, 0, DW_EH_PE_sdata8, 0x181, 0x1a, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version4) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 0, 10, 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version4) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 0, 10, 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset_version_invalid) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{0, '\0', 1, 2, 3, 4, 5, 6, 7});
+  ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x5000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+  SetCie64(&this->memory_, 0x6000, 0x100, std::vector<uint8_t>{0, '\0', 1, 2, 3, 4, 5, 6, 7});
+  ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x6000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+
+  SetCie32(&this->memory_, 0x7000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+  ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x7000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+  SetCie64(&this->memory_, 0x8000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+  ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x8000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+}
+
+static void VerifyCieAugment(const DwarfCie* cie, uint64_t inst_offset, uint64_t inst_end) {
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_udata2, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_textrel | DW_EH_PE_udata2, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(5U, cie->augmentation_string.size());
+  EXPECT_EQ('z', cie->augmentation_string[0]);
+  EXPECT_EQ('L', cie->augmentation_string[1]);
+  EXPECT_EQ('P', cie->augmentation_string[2]);
+  EXPECT_EQ('R', cie->augmentation_string[3]);
+  EXPECT_EQ('\0', cie->augmentation_string[4]);
+  EXPECT_EQ(0x12345678U, cie->personality_handler);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x10U, cie->return_address_register);
+  EXPECT_EQ(inst_offset, cie->cfa_instructions_offset);
+  EXPECT_EQ(inst_end, cie->cfa_instructions_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_augment) {
+  SetCie32(&this->memory_, 0x5000, 0x100,
+           std::vector<uint8_t>{/* version */ 1,
+                                /* augment string */ 'z', 'L', 'P', 'R', '\0',
+                                /* code alignment factor */ 4,
+                                /* data alignment factor */ 8,
+                                /* return address register */ 0x10,
+                                /* augment length */ 0xf,
+                                /* L data */ DW_EH_PE_textrel | DW_EH_PE_udata2,
+                                /* P data */ DW_EH_PE_udata4, 0x78, 0x56, 0x34, 0x12,
+                                /* R data */ DW_EH_PE_udata2});
+
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieAugment(cie, 0x5021, 0x5104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_augment) {
+  SetCie64(&this->memory_, 0x5000, 0x100,
+           std::vector<uint8_t>{/* version */ 1,
+                                /* augment string */ 'z', 'L', 'P', 'R', '\0',
+                                /* code alignment factor */ 4,
+                                /* data alignment factor */ 8,
+                                /* return address register */ 0x10,
+                                /* augment length */ 0xf,
+                                /* L data */ DW_EH_PE_textrel | DW_EH_PE_udata2,
+                                /* P data */ DW_EH_PE_udata4, 0x78, 0x56, 0x34, 0x12,
+                                /* R data */ DW_EH_PE_udata2});
+
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieAugment(cie, 0x502d, 0x510c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset32_augment) {
+  SetCie32(&this->memory_, 0x5000, 0xfc,
+           std::vector<uint8_t>{/* version */ 4,
+                                /* augment string */ 'z', '\0',
+                                /* address size */ 8,
+                                /* segment size */ 0x10,
+                                /* code alignment factor */ 16,
+                                /* data alignment factor */ 32,
+                                /* return address register */ 10,
+                                /* augment length */ 0x0});
+
+  std::vector<uint8_t> data{/* augment length */ 0x80, 0x3};
+  SetFde32(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0x10, &data);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(4U, fde->cie->version);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x53a2U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x5504U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x4300U, fde->pc_start);
+  EXPECT_EQ(0x4600U, fde->pc_end);
+  EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset64_augment) {
+  SetCie64(&this->memory_, 0x5000, 0xfc,
+           std::vector<uint8_t>{/* version */ 4,
+                                /* augment string */ 'z', '\0',
+                                /* address size */ 8,
+                                /* segment size */ 0x10,
+                                /* code alignment factor */ 16,
+                                /* data alignment factor */ 32,
+                                /* return address register */ 10,
+                                /* augment length */ 0x0});
+
+  std::vector<uint8_t> data{/* augment length */ 0x80, 0x3};
+  SetFde64(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0x10, &data);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(4U, fde->cie->version);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x53b6U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x550cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0x4300U, fde->pc_start);
+  EXPECT_EQ(0x4600U, fde->pc_end);
+  EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset32_lsda_address) {
+  SetCie32(&this->memory_, 0x5000, 0xfc,
+           std::vector<uint8_t>{/* version */ 1,
+                                /* augment string */ 'z', 'L', '\0',
+                                /* address size */ 8,
+                                /* code alignment factor */ 16,
+                                /* data alignment factor */ 32,
+                                /* return address register */ 10,
+                                /* augment length */ 0x2,
+                                /* L data */ DW_EH_PE_udata2});
+
+  std::vector<uint8_t> data{/* augment length */ 0x80, 0x3,
+                            /* lsda address */ 0x20, 0x45};
+  SetFde32(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0, &data);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x5392U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x5504U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x4300U, fde->pc_start);
+  EXPECT_EQ(0x4600U, fde->pc_end);
+  EXPECT_EQ(0x4520U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset64_lsda_address) {
+  SetCie64(&this->memory_, 0x5000, 0xfc,
+           std::vector<uint8_t>{/* version */ 1,
+                                /* augment string */ 'z', 'L', '\0',
+                                /* address size */ 8,
+                                /* code alignment factor */ 16,
+                                /* data alignment factor */ 32,
+                                /* return address register */ 10,
+                                /* augment length */ 0x2,
+                                /* L data */ DW_EH_PE_udata2});
+
+  std::vector<uint8_t> data{/* augment length */ 0x80, 0x3,
+                            /* lsda address */ 0x20, 0x45};
+  SetFde64(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0, &data);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x53a6U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x550cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0x4300U, fde->pc_start);
+  EXPECT_EQ(0x4600U, fde->pc_end);
+  EXPECT_EQ(0x4520U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc_interleaved) {
+  SetCie32(&this->memory_, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+  // FDE 0 (0x100 - 0x200)
+  SetFde32(&this->memory_, 0x5100, 0xfc, 0, 0x100, 0x100);
+  // FDE 1 (0x300 - 0x500)
+  SetFde32(&this->memory_, 0x5200, 0xfc, 0, 0x300, 0x200);
+  // FDE 2 (0x700 - 0x800)
+  SetFde32(&this->memory_, 0x5300, 0xfc, 0, 0x700, 0x100);
+  // FDE 3 (0xa00 - 0xb00)
+  SetFde32(&this->memory_, 0x5400, 0xfc, 0, 0xa00, 0x100);
+  // FDE 4 (0x100 - 0xb00)
+  SetFde32(&this->memory_, 0x5500, 0xfc, 0, 0x150, 0xa00);
+  // FDE 5 (0x0 - 0x50)
+  SetFde32(&this->memory_, 0x5600, 0xfc, 0, 0, 0x50);
+
+  this->debug_frame_->Init(0x5000, 0x700, 0);
+
+  // Force reading all entries so no entries are found.
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0xfffff);
+  ASSERT_TRUE(fde == nullptr);
+
+  //   0x0   - 0x50   FDE 5
+  fde = this->debug_frame_->GetFdeFromPc(0x10);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0U, fde->pc_start);
+  EXPECT_EQ(0x50U, fde->pc_end);
+
+  //   0x100 - 0x200  FDE 0
+  fde = this->debug_frame_->GetFdeFromPc(0x170);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x100U, fde->pc_start);
+  EXPECT_EQ(0x200U, fde->pc_end);
+
+  //   0x200 - 0x300  FDE 4
+  fde = this->debug_frame_->GetFdeFromPc(0x210);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x150U, fde->pc_start);
+  EXPECT_EQ(0xb50U, fde->pc_end);
+
+  //   0x300 - 0x500  FDE 1
+  fde = this->debug_frame_->GetFdeFromPc(0x310);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x300U, fde->pc_start);
+  EXPECT_EQ(0x500U, fde->pc_end);
+
+  //   0x700 - 0x800  FDE 2
+  fde = this->debug_frame_->GetFdeFromPc(0x790);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x700U, fde->pc_start);
+  EXPECT_EQ(0x800U, fde->pc_end);
+
+  //   0x800 - 0x900  FDE 4
+  fde = this->debug_frame_->GetFdeFromPc(0x850);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x150U, fde->pc_start);
+  EXPECT_EQ(0xb50U, fde->pc_end);
+
+  //   0xa00 - 0xb00  FDE 3
+  fde = this->debug_frame_->GetFdeFromPc(0xa35);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0xa00U, fde->pc_start);
+  EXPECT_EQ(0xb00U, fde->pc_end);
+
+  //   0xb00 - 0xb50  FDE 4
+  fde = this->debug_frame_->GetFdeFromPc(0xb20);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x150U, fde->pc_start);
+  EXPECT_EQ(0xb50U, fde->pc_end);
+}
+
+REGISTER_TYPED_TEST_CASE_P(
+    DwarfDebugFrameTest, GetFdes32, GetFdes32_after_GetFdeFromPc, GetFdes32_not_in_section,
+    GetFdeFromPc32, GetFdeFromPc32_reverse, GetFdeFromPc32_not_in_section, GetFdes64,
+    GetFdes64_after_GetFdeFromPc, GetFdes64_not_in_section, GetFdeFromPc64, GetFdeFromPc64_reverse,
+    GetFdeFromPc64_not_in_section, GetCieFde32, GetCieFde64, GetCieFromOffset32_cie_cached,
+    GetCieFromOffset64_cie_cached, GetCieFromOffset32_version1, GetCieFromOffset64_version1,
+    GetCieFromOffset32_version3, GetCieFromOffset64_version3, GetCieFromOffset32_version4,
+    GetCieFromOffset64_version4, GetCieFromOffset_version_invalid, GetCieFromOffset32_augment,
+    GetCieFromOffset64_augment, GetFdeFromOffset32_augment, GetFdeFromOffset64_augment,
+    GetFdeFromOffset32_lsda_address, GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index a73db65..9cac6e8 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -16,7 +16,6 @@
 
 #include <stdint.h>
 
-#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <unwindstack/DwarfError.h>
@@ -30,50 +29,32 @@
 namespace unwindstack {
 
 template <typename TypeParam>
-class MockDwarfEhFrame : public DwarfEhFrame<TypeParam> {
- public:
-  MockDwarfEhFrame(Memory* memory) : DwarfEhFrame<TypeParam>(memory) {}
-  ~MockDwarfEhFrame() = default;
-
-  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
-  void TestSetOffset(uint64_t offset) { this->entries_offset_ = offset; }
-  void TestSetEndOffset(uint64_t offset) { this->entries_end_ = offset; }
-  void TestPushFdeInfo(const typename DwarfEhFrame<TypeParam>::FdeInfo& info) {
-    this->fdes_.push_back(info);
-  }
-
-  uint64_t TestGetFdeCount() { return this->fde_count_; }
-  uint8_t TestGetOffset() { return this->offset_; }
-  uint8_t TestGetEndOffset() { return this->end_offset_; }
-  void TestGetFdeInfo(size_t index, typename DwarfEhFrame<TypeParam>::FdeInfo* info) {
-    *info = this->fdes_[index];
-  }
-};
-
-template <typename TypeParam>
 class DwarfEhFrameTest : public ::testing::Test {
  protected:
   void SetUp() override {
     memory_.Clear();
-    eh_frame_ = new MockDwarfEhFrame<TypeParam>(&memory_);
+    eh_frame_ = new DwarfEhFrame<TypeParam>(&memory_);
     ResetLogs();
   }
 
   void TearDown() override { delete eh_frame_; }
 
   MemoryFake memory_;
-  MockDwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
+  DwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
 };
 TYPED_TEST_CASE_P(DwarfEhFrameTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
-TYPED_TEST_P(DwarfEhFrameTest, Init32) {
+// Only verify different cie/fde format. All other DwarfSection corner
+// cases are tested in DwarfDebugFrameTest.cpp.
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeCieFromOffset32) {
   // CIE 32 information.
   this->memory_.SetData32(0x5000, 0xfc);
+  // Indicates this is a cie for eh_frame.
   this->memory_.SetData32(0x5004, 0);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
+  this->memory_.SetMemory(0x5008, std::vector<uint8_t>{1, '\0', 16, 32, 1});
 
   // FDE 32 information.
   this->memory_.SetData32(0x5100, 0xfc);
@@ -81,377 +62,70 @@
   this->memory_.SetData32(0x5108, 0x1500);
   this->memory_.SetData32(0x510c, 0x200);
 
-  this->memory_.SetData32(0x5200, 0xfc);
-  this->memory_.SetData32(0x5204, 0x204);
-  this->memory_.SetData32(0x5208, 0x2500);
-  this->memory_.SetData32(0x520c, 0x300);
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x5100);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x5110U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x5200U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x6608U, fde->pc_start);
+  EXPECT_EQ(0x6808U, fde->pc_end);
+  EXPECT_EQ(0U, fde->lsda_address);
 
-  // CIE 32 information.
-  this->memory_.SetData32(0x5300, 0xfc);
-  this->memory_.SetData32(0x5304, 0);
-  this->memory_.SetData8(0x5308, 1);
-  this->memory_.SetData8(0x5309, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5400, 0xfc);
-  this->memory_.SetData32(0x5404, 0x104);
-  this->memory_.SetData32(0x5408, 0x3500);
-  this->memory_.SetData32(0x540c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xfc);
-  this->memory_.SetData32(0x5504, 0x204);
-  this->memory_.SetData32(0x5508, 0x4500);
-  this->memory_.SetData32(0x550c, 0x500);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x6608U, info.start);
-  EXPECT_EQ(0x6808U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(1, &info);
-  EXPECT_EQ(0x5200U, info.offset);
-  EXPECT_EQ(0x7708U, info.start);
-  EXPECT_EQ(0x7a08U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(2, &info);
-  EXPECT_EQ(0x5400U, info.offset);
-  EXPECT_EQ(0x8908U, info.start);
-  EXPECT_EQ(0x8d08U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(3, &info);
-  EXPECT_EQ(0x5500U, info.offset);
-  EXPECT_EQ(0x9a08U, info.start);
-  EXPECT_EQ(0x9f08U, info.end);
+  const DwarfCie* cie = fde->cie;
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x500dU, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5100U, cie->cfa_instructions_end);
+  EXPECT_EQ(16U, cie->code_alignment_factor);
+  EXPECT_EQ(32U, cie->data_alignment_factor);
+  EXPECT_EQ(1U, cie->return_address_register);
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, Init32_fde_not_following_cie) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0x1000);
-  this->memory_.SetData32(0x5108, 0x1500);
-  this->memory_.SetData32(0x510c, 0x200);
-
-  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init64) {
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeCieFromOffset64) {
   // CIE 64 information.
   this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
+  this->memory_.SetData64(0x5004, 0xfc);
+  // Indicates this is a cie for eh_frame.
   this->memory_.SetData64(0x500c, 0);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
+  this->memory_.SetMemory(0x5014, std::vector<uint8_t>{1, '\0', 16, 32, 1});
 
   // FDE 64 information.
   this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
+  this->memory_.SetData64(0x5104, 0xfc);
   this->memory_.SetData64(0x510c, 0x10c);
   this->memory_.SetData64(0x5114, 0x1500);
   this->memory_.SetData64(0x511c, 0x200);
 
-  this->memory_.SetData32(0x5200, 0xffffffff);
-  this->memory_.SetData64(0x5204, 0xf4);
-  this->memory_.SetData64(0x520c, 0x20c);
-  this->memory_.SetData64(0x5214, 0x2500);
-  this->memory_.SetData64(0x521c, 0x300);
-
-  // CIE 64 information.
-  this->memory_.SetData32(0x5300, 0xffffffff);
-  this->memory_.SetData64(0x5304, 0xf4);
-  this->memory_.SetData64(0x530c, 0);
-  this->memory_.SetData8(0x5314, 1);
-  this->memory_.SetData8(0x5315, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5400, 0xffffffff);
-  this->memory_.SetData64(0x5404, 0xf4);
-  this->memory_.SetData64(0x540c, 0x10c);
-  this->memory_.SetData64(0x5414, 0x3500);
-  this->memory_.SetData64(0x541c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xffffffff);
-  this->memory_.SetData64(0x5504, 0xf4);
-  this->memory_.SetData64(0x550c, 0x20c);
-  this->memory_.SetData64(0x5514, 0x4500);
-  this->memory_.SetData64(0x551c, 0x500);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x6618U, info.start);
-  EXPECT_EQ(0x6818U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(1, &info);
-  EXPECT_EQ(0x5200U, info.offset);
-  EXPECT_EQ(0x7718U, info.start);
-  EXPECT_EQ(0x7a18U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(2, &info);
-  EXPECT_EQ(0x5400U, info.offset);
-  EXPECT_EQ(0x8918U, info.start);
-  EXPECT_EQ(0x8d18U, info.end);
-
-  this->eh_frame_->TestGetFdeInfo(3, &info);
-  EXPECT_EQ(0x5500U, info.offset);
-  EXPECT_EQ(0x9a18U, info.start);
-  EXPECT_EQ(0x9f18U, info.end);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init64_fde_not_following_cie) {
-  // CIE 64 information.
-  this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
-  this->memory_.SetData64(0x500c, 0);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
-  this->memory_.SetData64(0x510c, 0x1000);
-  this->memory_.SetData64(0x5114, 0x1500);
-  this->memory_.SetData64(0x511c, 0x200);
-
-  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init_version1) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0);
-  this->memory_.SetData8(0x5008, 1);
-  // Augment string.
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'});
-  // Code alignment factor.
-  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00});
-  // Data alignment factor.
-  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
-  // Return address register
-  this->memory_.SetData8(0x5014, 0x84);
-  // Augmentation length
-  this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00});
-  // R data.
-  this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0x104);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
-  ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x6606U, info.start);
-  EXPECT_EQ(0x6806U, info.end);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, Init_version4) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0);
-  this->memory_.SetData8(0x5008, 4);
-  // Augment string.
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
-  // Address size.
-  this->memory_.SetData8(0x500e, 4);
-  // Segment size.
-  this->memory_.SetData8(0x500f, 0);
-  // Code alignment factor.
-  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00});
-  // Data alignment factor.
-  this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
-  // Return address register
-  this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10});
-  // Augmentation length
-  this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00});
-  // L data.
-  this->memory_.SetData8(0x501a, 0x10);
-  // P data.
-  this->memory_.SetData8(0x501b, DW_EH_PE_udata4);
-  this->memory_.SetData32(0x501c, 0x100);
-  // R data.
-  this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0x104);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
-
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
-  ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  this->eh_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x6606U, info.start);
-  EXPECT_EQ(0x6806U, info.end);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc) {
-  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  for (size_t i = 0; i < 9; i++) {
-    info.start = 0x1000 * (i + 1);
-    info.end = 0x1000 * (i + 2) - 0x10;
-    info.offset = 0x5000 + i * 0x20;
-    this->eh_frame_->TestPushFdeInfo(info);
-  }
-
-  this->eh_frame_->TestSetFdeCount(0);
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
-
-  this->eh_frame_->TestSetFdeCount(9);
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
-  // Odd number of elements.
-  for (size_t i = 0; i < 9; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
-        << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
-  }
-
-  // Even number of elements.
-  this->eh_frame_->TestSetFdeCount(10);
-  info.start = 0xa000;
-  info.end = 0xaff0;
-  info.offset = 0x5120;
-  this->eh_frame_->TestPushFdeInfo(info);
-
-  for (size_t i = 0; i < 10; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
-        << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
-  }
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) {
-  this->eh_frame_->TestSetOffset(0x4000);
-
-  // CIE 32 information.
-  this->memory_.SetData32(0xf000, 0x100);
-  this->memory_.SetData32(0xf004, 0);
-  this->memory_.SetData8(0xf008, 0x1);
-  this->memory_.SetData8(0xf009, '\0');
-  this->memory_.SetData8(0xf00a, 4);
-  this->memory_.SetData8(0xf00b, 8);
-  this->memory_.SetData8(0xf00c, 0x20);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x14000, 0x20);
-  this->memory_.SetData32(0x14004, 0x5004);
-  this->memory_.SetData32(0x14008, 0x9000);
-  this->memory_.SetData32(0x1400c, 0x100);
-
-  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x14000);
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x5100);
   ASSERT_TRUE(fde != nullptr);
-  EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x1d008U, fde->pc_start);
-  EXPECT_EQ(0x1d108U, fde->pc_end);
-  EXPECT_EQ(0xf000U, fde->cie_offset);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x5124U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x5208U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x6618U, fde->pc_start);
+  EXPECT_EQ(0x6818U, fde->pc_end);
   EXPECT_EQ(0U, fde->lsda_address);
 
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(1U, fde->cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
-  EXPECT_EQ(0U, fde->cie->segment_size);
-  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
-  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
-  EXPECT_EQ(0U, fde->cie->personality_handler);
-  EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
-  EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
-  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
-  EXPECT_EQ(8, fde->cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+  const DwarfCie* cie = fde->cie;
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x5019U, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5108U, cie->cfa_instructions_end);
+  EXPECT_EQ(16U, cie->code_alignment_factor);
+  EXPECT_EQ(32U, cie->data_alignment_factor);
+  EXPECT_EQ(1U, cie->return_address_register);
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
-  this->eh_frame_->TestSetOffset(0x2000);
-
-  // CIE 64 information.
-  this->memory_.SetData32(0x6000, 0xffffffff);
-  this->memory_.SetData64(0x6004, 0x100);
-  this->memory_.SetData64(0x600c, 0);
-  this->memory_.SetData8(0x6014, 0x1);
-  this->memory_.SetData8(0x6015, '\0');
-  this->memory_.SetData8(0x6016, 4);
-  this->memory_.SetData8(0x6017, 8);
-  this->memory_.SetData8(0x6018, 0x20);
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x8000, 0xffffffff);
-  this->memory_.SetData64(0x8004, 0x200);
-  this->memory_.SetData64(0x800c, 0x200c);
-  this->memory_.SetData64(0x8014, 0x5000);
-  this->memory_.SetData64(0x801c, 0x300);
-
-  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x8000);
-  ASSERT_TRUE(fde != nullptr);
-  EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
-  EXPECT_EQ(0xd018U, fde->pc_start);
-  EXPECT_EQ(0xd318U, fde->pc_end);
-  EXPECT_EQ(0x6000U, fde->cie_offset);
-  EXPECT_EQ(0U, fde->lsda_address);
-
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(1U, fde->cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
-  EXPECT_EQ(0U, fde->cie->segment_size);
-  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
-  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
-  EXPECT_EQ(0U, fde->cie->personality_handler);
-  EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
-  EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
-  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
-  EXPECT_EQ(8, fde->cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, fde->cie->return_address_register);
-}
-
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init32, Init32_fde_not_following_cie, Init64,
-                           Init64_fde_not_following_cie, Init_version1, Init_version4,
-                           GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, GetFdeCieFromOffset32, GetFdeCieFromOffset64);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 4240419..910ae36 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -30,10 +30,10 @@
 namespace unwindstack {
 
 template <typename TypeParam>
-class MockDwarfEhFrameWithHdr : public DwarfEhFrameWithHdr<TypeParam> {
+class TestDwarfEhFrameWithHdr : public DwarfEhFrameWithHdr<TypeParam> {
  public:
-  MockDwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrameWithHdr<TypeParam>(memory) {}
-  ~MockDwarfEhFrameWithHdr() = default;
+  TestDwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrameWithHdr<TypeParam>(memory) {}
+  ~TestDwarfEhFrameWithHdr() = default;
 
   void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
   void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
@@ -64,14 +64,14 @@
  protected:
   void SetUp() override {
     memory_.Clear();
-    eh_frame_ = new MockDwarfEhFrameWithHdr<TypeParam>(&memory_);
+    eh_frame_ = new TestDwarfEhFrameWithHdr<TypeParam>(&memory_);
     ResetLogs();
   }
 
   void TearDown() override { delete eh_frame_; }
 
   MemoryFake memory_;
-  MockDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
+  TestDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
 };
 TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest);
 
@@ -83,7 +83,7 @@
   this->memory_.SetData16(0x1004, 0x500);
   this->memory_.SetData32(0x1006, 126);
 
-  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
   EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
   EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
   EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
@@ -97,19 +97,119 @@
 
   // Verify a zero fde count fails to init.
   this->memory_.SetData32(0x1006, 0);
-  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
   ASSERT_EQ(DWARF_ERROR_NO_FDES, this->eh_frame_->LastErrorCode());
 
   // Verify an unexpected version will cause a fail.
   this->memory_.SetData32(0x1006, 126);
   this->memory_.SetData8(0x1000, 0);
-  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
   ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
   this->memory_.SetData8(0x1000, 2);
-  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
   ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
 }
 
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init_non_zero_load_bias) {
+  this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+                                                       DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 1);
+  this->memory_.SetData32(0x100a, 0x2500);
+  this->memory_.SetData32(0x100e, 0x1400);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x1300, 0xfc);
+  this->memory_.SetData32(0x1304, 0);
+  this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x1400, 0xfc);
+  this->memory_.SetData32(0x1404, 0x104);
+  this->memory_.SetData32(0x1408, 0x10f8);
+  this->memory_.SetData32(0x140c, 0x200);
+  this->memory_.SetData16(0x1410, 0);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0x2000));
+  EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+  EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
+  EXPECT_EQ(0x1b, this->eh_frame_->TestGetTableEncoding());
+  EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+  EXPECT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+  EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
+  EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
+  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+  EXPECT_EQ(0x4700U, fde->pc_end);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdes) {
+  this->memory_.SetMemory(
+      0x1000, std::vector<uint8_t>{1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 4);
+
+  // Header information.
+  this->memory_.SetData32(0x100a, 0x4600);
+  this->memory_.SetData32(0x100e, 0x1500);
+  this->memory_.SetData32(0x1012, 0x5500);
+  this->memory_.SetData32(0x1016, 0x1400);
+  this->memory_.SetData32(0x101a, 0x6800);
+  this->memory_.SetData32(0x101e, 0x1700);
+  this->memory_.SetData32(0x1022, 0x7700);
+  this->memory_.SetData32(0x1026, 0x1600);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x1300, 0xfc);
+  this->memory_.SetData32(0x1304, 0);
+  this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, '\0', 0, 0, 0});
+
+  // FDE 32 information.
+  // pc 0x5500 - 0x5700
+  this->memory_.SetData32(0x1400, 0xfc);
+  this->memory_.SetData32(0x1404, 0x104);
+  this->memory_.SetData32(0x1408, 0x40f8);
+  this->memory_.SetData32(0x140c, 0x200);
+
+  // pc 0x4600 - 0x4800
+  this->memory_.SetData32(0x1500, 0xfc);
+  this->memory_.SetData32(0x1504, 0x204);
+  this->memory_.SetData32(0x1508, 0x30f8);
+  this->memory_.SetData32(0x150c, 0x200);
+
+  // pc 0x7700 - 0x7900
+  this->memory_.SetData32(0x1600, 0xfc);
+  this->memory_.SetData32(0x1604, 0x304);
+  this->memory_.SetData32(0x1608, 0x60f8);
+  this->memory_.SetData32(0x160c, 0x200);
+
+  // pc 0x6800 - 0x6a00
+  this->memory_.SetData32(0x1700, 0xfc);
+  this->memory_.SetData32(0x1704, 0x404);
+  this->memory_.SetData32(0x1708, 0x50f8);
+  this->memory_.SetData32(0x170c, 0x200);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->eh_frame_->GetFdes(&fdes);
+  ASSERT_EQ(4U, fdes.size());
+
+  EXPECT_EQ(0x4600U, fdes[0]->pc_start);
+  EXPECT_EQ(0x4800U, fdes[0]->pc_end);
+  EXPECT_EQ(0x5500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x5700U, fdes[1]->pc_end);
+  EXPECT_EQ(0x6800U, fdes[2]->pc_start);
+  EXPECT_EQ(0x6a00U, fdes[2]->pc_end);
+  EXPECT_EQ(0x7700U, fdes[3]->pc_start);
+  EXPECT_EQ(0x7900U, fdes[3]->pc_end);
+}
+
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_expect_cache_fail) {
   this->eh_frame_->TestSetTableEntrySize(0x10);
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
@@ -123,6 +223,7 @@
   EXPECT_EQ(0x1000U, this->eh_frame_->LastErrorAddress());
 }
 
+// We are assuming that pc rel, is really relative to the load_bias.
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_pcrel) {
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
   this->eh_frame_->TestSetEntriesOffset(0x1000);
@@ -134,8 +235,8 @@
 
   auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
   ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x1380U, info->pc);
-  EXPECT_EQ(0x1540U, info->offset);
+  EXPECT_EQ(0x340U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
 }
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_datarel) {
@@ -340,11 +441,7 @@
   // CIE 32 information.
   this->memory_.SetData32(0xf000, 0x100);
   this->memory_.SetData32(0xf004, 0);
-  this->memory_.SetData8(0xf008, 0x1);
-  this->memory_.SetData8(0xf009, '\0');
-  this->memory_.SetData8(0xf00a, 4);
-  this->memory_.SetData8(0xf00b, 8);
-  this->memory_.SetData8(0xf00c, 0x20);
+  this->memory_.SetMemory(0xf008, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
 
   // FDE 32 information.
   this->memory_.SetData32(0x14000, 0x20);
@@ -381,11 +478,7 @@
   this->memory_.SetData32(0x6000, 0xffffffff);
   this->memory_.SetData64(0x6004, 0x100);
   this->memory_.SetData64(0x600c, 0);
-  this->memory_.SetData8(0x6014, 0x1);
-  this->memory_.SetData8(0x6015, '\0');
-  this->memory_.SetData8(0x6016, 4);
-  this->memory_.SetData8(0x6017, 8);
-  this->memory_.SetData8(0x6018, 0x20);
+  this->memory_.SetMemory(0x6014, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
 
   // FDE 64 information.
   this->memory_.SetData32(0x8000, 0xffffffff);
@@ -430,14 +523,14 @@
   ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, GetFdeInfoFromIndex_expect_cache_fail,
-                           GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
-                           GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
-                           GetFdeOffsetBinary_index_fail, GetFdeOffsetSequential,
-                           GetFdeOffsetSequential_last_element, GetFdeOffsetSequential_end_check,
-                           GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_binary_search,
-                           GetFdeOffsetFromPc_sequential_search, GetCieFde32, GetCieFde64,
-                           GetFdeFromPc_fde_not_found);
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
+                           GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
+                           GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
+                           GetFdeOffsetBinary_verify, GetFdeOffsetBinary_index_fail,
+                           GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
+                           GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
+                           GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
+                           GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index c340291..46f555a 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -16,7 +16,6 @@
 
 #include <stdint.h>
 
-#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <unwindstack/DwarfError.h>
@@ -31,42 +30,27 @@
 namespace unwindstack {
 
 template <typename TypeParam>
-class MockDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
+class TestDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
  public:
-  MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
-  virtual ~MockDwarfSectionImpl() = default;
+  TestDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
+  virtual ~TestDwarfSectionImpl() = default;
 
-  MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+  bool Init(uint64_t, uint64_t, uint64_t) override { return false; }
 
-  MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
+  void GetFdes(std::vector<const DwarfFde*>*) override {}
 
-  MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
+  const DwarfFde* GetFdeFromPc(uint64_t) override { return nullptr; }
 
-  MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
+  uint64_t GetCieOffsetFromFde32(uint32_t) { return 0; }
 
-  MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+  uint64_t GetCieOffsetFromFde64(uint64_t) { return 0; }
 
-  MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
-
-  void TestSetCie32Value(uint32_t value32) { this->cie32_value_ = value32; }
-
-  void TestSetCie64Value(uint64_t value64) { this->cie64_value_ = value64; }
-
-  void TestSetCachedCieEntry(uint64_t offset, const DwarfCie& cie) {
-    this->cie_entries_[offset] = cie;
-  }
-  void TestClearCachedCieEntry() { this->cie_entries_.clear(); }
-
-  void TestSetCachedFdeEntry(uint64_t offset, const DwarfFde& fde) {
-    this->fde_entries_[offset] = fde;
-  }
-  void TestClearCachedFdeEntry() { this->fde_entries_.clear(); }
+  uint64_t AdjustPcFromFde(uint64_t) override { return 0; }
 
   void TestSetCachedCieLocRegs(uint64_t offset, const dwarf_loc_regs_t& loc_regs) {
     this->cie_loc_regs_[offset] = loc_regs;
   }
   void TestClearCachedCieLocRegs() { this->cie_loc_regs_.clear(); }
-
   void TestClearError() { this->last_error_.code = DWARF_ERROR_NONE; }
 };
 
@@ -75,21 +59,41 @@
  protected:
   void SetUp() override {
     memory_.Clear();
-    section_ = new MockDwarfSectionImpl<TypeParam>(&memory_);
+    section_ = new TestDwarfSectionImpl<TypeParam>(&memory_);
     ResetLogs();
-    section_->TestSetCie32Value(static_cast<uint32_t>(-1));
-    section_->TestSetCie64Value(static_cast<uint64_t>(-1));
   }
 
   void TearDown() override { delete section_; }
 
   MemoryFake memory_;
-  MockDwarfSectionImpl<TypeParam>* section_ = nullptr;
+  TestDwarfSectionImpl<TypeParam>* section_ = nullptr;
 };
 TYPED_TEST_CASE_P(DwarfSectionImplTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
+TYPED_TEST_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache) {
+  ASSERT_TRUE(this->section_->GetCieFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCieFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
+  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+}
+
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
   RegsImplFake<TypeParam> regs(10);
@@ -487,334 +491,6 @@
   EXPECT_EQ(0x80000000U, regs.pc());
 }
 
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_fail_should_not_cache) {
-  ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
-  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
-  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_32_version_check) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x1);
-  this->memory_.SetData8(0x5009, '\0');
-  this->memory_.SetData8(0x500a, 4);
-  this->memory_.SetData8(0x500b, 8);
-  this->memory_.SetData8(0x500c, 0x20);
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x500dU, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, cie->return_address_register);
-  EXPECT_EQ(DWARF_ERROR_NONE, this->section_->LastErrorCode());
-
-  this->section_->TestClearCachedCieEntry();
-  // Set version to 0, 2, 5 and verify we fail.
-  this->memory_.SetData8(0x5008, 0x0);
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->LastErrorCode());
-
-  this->memory_.SetData8(0x5008, 0x2);
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->LastErrorCode());
-
-  this->memory_.SetData8(0x5008, 0x5);
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->LastErrorCode());
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_negative_data_alignment_factor) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x1);
-  this->memory_.SetData8(0x5009, '\0');
-  this->memory_.SetData8(0x500a, 4);
-  this->memory_.SetMemory(0x500b, std::vector<uint8_t>{0xfc, 0xff, 0xff, 0xff, 0x7f});
-  this->memory_.SetData8(0x5010, 0x20);
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x5011U, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(-4, cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_64_no_augment) {
-  this->memory_.SetData32(0x8000, 0xffffffff);
-  this->memory_.SetData64(0x8004, 0x200);
-  this->memory_.SetData64(0x800c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x8014, 0x1);
-  this->memory_.SetData8(0x8015, '\0');
-  this->memory_.SetData8(0x8016, 4);
-  this->memory_.SetData8(0x8017, 8);
-  this->memory_.SetData8(0x8018, 0x20);
-
-  const DwarfCie* cie = this->section_->GetCie(0x8000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x8019U, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x820cU, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_augment) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x1);
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
-  this->memory_.SetData8(0x500e, 4);
-  this->memory_.SetData8(0x500f, 8);
-  this->memory_.SetData8(0x5010, 0x10);
-  // Augment length.
-  this->memory_.SetData8(0x5011, 0xf);
-  // L data.
-  this->memory_.SetData8(0x5012, DW_EH_PE_textrel | DW_EH_PE_udata2);
-  // P data.
-  this->memory_.SetData8(0x5013, DW_EH_PE_udata4);
-  this->memory_.SetData32(0x5014, 0x12345678);
-  // R data.
-  this->memory_.SetData8(0x5018, DW_EH_PE_udata2);
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_udata2, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_textrel | DW_EH_PE_udata2, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(5U, cie->augmentation_string.size());
-  EXPECT_EQ('z', cie->augmentation_string[0]);
-  EXPECT_EQ('L', cie->augmentation_string[1]);
-  EXPECT_EQ('P', cie->augmentation_string[2]);
-  EXPECT_EQ('R', cie->augmentation_string[3]);
-  EXPECT_EQ('\0', cie->augmentation_string[4]);
-  EXPECT_EQ(0x12345678U, cie->personality_handler);
-  EXPECT_EQ(0x5021U, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x10U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_3) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x3);
-  this->memory_.SetData8(0x5009, '\0');
-  this->memory_.SetData8(0x500a, 4);
-  this->memory_.SetData8(0x500b, 8);
-  this->memory_.SetMemory(0x500c, std::vector<uint8_t>{0x81, 0x03});
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(3U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x500eU, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x181U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_4) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x4);
-  this->memory_.SetData8(0x5009, '\0');
-  this->memory_.SetData8(0x500a, 4);
-  this->memory_.SetData8(0x500b, 0x13);
-  this->memory_.SetData8(0x500c, 4);
-  this->memory_.SetData8(0x500d, 8);
-  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x81, 0x03});
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(4U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0x13U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x5010U, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x181U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
-  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
-  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
-  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment) {
-  this->memory_.SetData32(0x4000, 0x20);
-  this->memory_.SetData32(0x4004, 0x8000);
-  this->memory_.SetData32(0x4008, 0x5000);
-  this->memory_.SetData32(0x400c, 0x100);
-
-  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  this->section_->TestSetCachedCieEntry(0x8000, cie);
-  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(0x4010U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x4024U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x5000U, fde->pc_start);
-  EXPECT_EQ(0x5100U, fde->pc_end);
-  EXPECT_EQ(0x8000U, fde->cie_offset);
-  EXPECT_EQ(0U, fde->lsda_address);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment_non_zero_segment_size) {
-  this->memory_.SetData32(0x4000, 0x30);
-  this->memory_.SetData32(0x4004, 0x8000);
-  this->memory_.SetData32(0x4018, 0x5000);
-  this->memory_.SetData32(0x401c, 0x100);
-
-  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  cie.segment_size = 0x10;
-  this->section_->TestSetCachedCieEntry(0x8000, cie);
-  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(0x4020U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x4034U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x5000U, fde->pc_start);
-  EXPECT_EQ(0x5100U, fde->pc_end);
-  EXPECT_EQ(0x8000U, fde->cie_offset);
-  EXPECT_EQ(0U, fde->lsda_address);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_augment) {
-  this->memory_.SetData32(0x4000, 0x100);
-  this->memory_.SetData32(0x4004, 0x8000);
-  this->memory_.SetData32(0x4008, 0x5000);
-  this->memory_.SetData32(0x400c, 0x100);
-  this->memory_.SetMemory(0x4010, std::vector<uint8_t>{0x82, 0x01});
-  this->memory_.SetData16(0x4012, 0x1234);
-
-  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  cie.augmentation_string.push_back('z');
-  cie.lsda_encoding = DW_EH_PE_udata2;
-  this->section_->TestSetCachedCieEntry(0x8000, cie);
-  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(0x4094U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x4104U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x5000U, fde->pc_start);
-  EXPECT_EQ(0x5100U, fde->pc_end);
-  EXPECT_EQ(0x8000U, fde->cie_offset);
-  EXPECT_EQ(0x1234U, fde->lsda_address);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_64_no_augment) {
-  this->memory_.SetData32(0x4000, 0xffffffff);
-  this->memory_.SetData64(0x4004, 0x100);
-  this->memory_.SetData64(0x400c, 0x12345678);
-  this->memory_.SetData32(0x4014, 0x5000);
-  this->memory_.SetData32(0x4018, 0x100);
-
-  EXPECT_CALL(*this->section_, GetCieOffsetFromFde64(0x12345678))
-      .WillOnce(::testing::Return(0x12345678));
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  this->section_->TestSetCachedCieEntry(0x12345678, cie);
-  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(0x401cU, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x410cU, fde->cfa_instructions_end);
-  EXPECT_EQ(0x5000U, fde->pc_start);
-  EXPECT_EQ(0x5100U, fde->pc_end);
-  EXPECT_EQ(0x12345678U, fde->cie_offset);
-  EXPECT_EQ(0U, fde->lsda_address);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_cached) {
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  cie.augmentation_string.push_back('z');
-  cie.lsda_encoding = DW_EH_PE_udata2;
-
-  DwarfFde fde_cached{};
-  fde_cached.cfa_instructions_offset = 0x1000;
-  fde_cached.cfa_instructions_end = 0x1100;
-  fde_cached.pc_start = 0x9000;
-  fde_cached.pc_end = 0x9400;
-  fde_cached.cie_offset = 0x30000;
-  fde_cached.cie = &cie;
-  this->section_->TestSetCachedFdeEntry(0x6000, fde_cached);
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x6000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_EQ(&cie, fde->cie);
-  EXPECT_EQ(0x1000U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x1100U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x9000U, fde->pc_start);
-  EXPECT_EQ(0x9400U, fde->pc_end);
-  EXPECT_EQ(0x30000U, fde->cie_offset);
-}
-
 TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
   DwarfCie cie{};
   cie.cfa_instructions_offset = 0x3000;
@@ -884,7 +560,7 @@
 
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
   this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
-  ASSERT_TRUE(this->section_->Log(2, 0x1000, 0x1000, &fde));
+  ASSERT_TRUE(this->section_->Log(2, 0x1000, &fde));
 
   ASSERT_EQ(
       "4 unwind     DW_CFA_nop\n"
@@ -895,18 +571,16 @@
   ASSERT_EQ("", GetFakeLogBuf());
 }
 
-REGISTER_TYPED_TEST_CASE_P(
-    DwarfSectionImplTest, Eval_cfa_expr_eval_fail, Eval_cfa_expr_no_stack,
-    Eval_cfa_expr_is_register, Eval_cfa_expr, Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa,
-    Eval_cfa_bad, Eval_cfa_register_prev, Eval_cfa_register_from_value, Eval_double_indirection,
-    Eval_register_reference_chain, Eval_dex_pc, Eval_invalid_register, Eval_different_reg_locations,
-    Eval_return_address_undefined, Eval_pc_zero, Eval_return_address, Eval_ignore_large_reg_loc,
-    Eval_reg_expr, Eval_reg_val_expr, GetCie_fail_should_not_cache, GetCie_32_version_check,
-    GetCie_negative_data_alignment_factor, GetCie_64_no_augment, GetCie_augment, GetCie_version_3,
-    GetCie_version_4, GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
-    GetFdeFromOffset_32_no_augment_non_zero_segment_size, GetFdeFromOffset_32_augment,
-    GetFdeFromOffset_64_no_augment, GetFdeFromOffset_cached, GetCfaLocationInfo_cie_not_cached,
-    GetCfaLocationInfo_cie_cached, Log);
+REGISTER_TYPED_TEST_CASE_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache,
+                           GetFdeFromOffset_fail_should_not_cache, Eval_cfa_expr_eval_fail,
+                           Eval_cfa_expr_no_stack, Eval_cfa_expr_is_register, Eval_cfa_expr,
+                           Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa, Eval_cfa_bad,
+                           Eval_cfa_register_prev, Eval_cfa_register_from_value,
+                           Eval_double_indirection, Eval_register_reference_chain, Eval_dex_pc,
+                           Eval_invalid_register, Eval_different_reg_locations,
+                           Eval_return_address_undefined, Eval_pc_zero, Eval_return_address,
+                           Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+                           GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 071d2df..d754fcc 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -30,24 +30,18 @@
   MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
   virtual ~MockDwarfSection() = default;
 
-  MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
+  MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
 
   MOCK_METHOD5(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*));
 
+  MOCK_METHOD3(Log, bool(uint8_t, uint64_t, const DwarfFde*));
+
+  MOCK_METHOD1(GetFdes, void(std::vector<const DwarfFde*>*));
+
+  MOCK_METHOD1(GetFdeFromPc, const DwarfFde*(uint64_t));
+
   MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
 
-  MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
-
-  MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
-
-  MOCK_METHOD1(GetFdeFromOffset, const DwarfFde*(uint64_t));
-
-  MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
-
-  MOCK_METHOD1(IsCie32, bool(uint32_t));
-
-  MOCK_METHOD1(IsCie64, bool(uint64_t));
-
   MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
 
   MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
@@ -57,112 +51,60 @@
 
 class DwarfSectionTest : public ::testing::Test {
  protected:
+  void SetUp() override { section_.reset(new MockDwarfSection(&memory_)); }
+
   MemoryFake memory_;
+  std::unique_ptr<MockDwarfSection> section_;
 };
 
-TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_from_pc) {
-  MockDwarfSection mock_section(&memory_);
-
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(false));
-
-  // Verify nullptr when GetFdeOffsetFromPc fails.
-  ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
-}
-
-TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_fde_pc_end) {
-  MockDwarfSection mock_section(&memory_);
-
-  DwarfFde fde{};
-  fde.pc_end = 0x500;
-
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  // Verify nullptr when GetFdeOffsetFromPc fails.
-  ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
-}
-
-TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_pass) {
-  MockDwarfSection mock_section(&memory_);
-
-  DwarfFde fde{};
-  fde.pc_end = 0x2000;
-
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  // Verify nullptr when GetFdeOffsetFromPc fails.
-  ASSERT_EQ(&fde, mock_section.GetFdeFromPc(0x1000));
-}
-
 TEST_F(DwarfSectionTest, Step_fail_fde) {
-  MockDwarfSection mock_section(&memory_);
-
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(false));
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(nullptr));
 
   bool finished;
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
+  ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_fail_cie_null) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfFde fde{};
   fde.pc_end = 0x2000;
   fde.cie = nullptr;
 
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
 
   bool finished;
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
+  ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfCie cie{};
   DwarfFde fde{};
   fde.pc_end = 0x2000;
   fde.cie = &cie;
 
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
       .WillOnce(::testing::Return(false));
 
   bool finished;
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
+  ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_pass) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfCie cie{};
   DwarfFde fde{};
   fde.pc_end = 0x2000;
   fde.cie = &cie;
 
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
       .WillOnce(::testing::Return(true));
 
   MemoryFake process;
-  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+  EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
       .WillOnce(::testing::Return(true));
 
   bool finished;
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
 }
 
 static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
@@ -173,64 +115,53 @@
 }
 
 TEST_F(DwarfSectionTest, Step_cache) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfCie cie{};
   DwarfFde fde{};
   fde.pc_start = 0x500;
   fde.pc_end = 0x2000;
   fde.cie = &cie;
 
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
       .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
 
   MemoryFake process;
-  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+  EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
       .WillRepeatedly(::testing::Return(true));
 
   bool finished;
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
-  ASSERT_TRUE(mock_section.Step(0x1500, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1500, nullptr, &process, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfCie cie{};
   DwarfFde fde0{};
   fde0.pc_start = 0x1000;
   fde0.pc_end = 0x2000;
   fde0.cie = &cie;
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde0));
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde0));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
       .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
 
   MemoryFake process;
-  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+  EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
       .WillRepeatedly(::testing::Return(true));
 
   bool finished;
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
 
   DwarfFde fde1{};
   fde1.pc_start = 0x500;
   fde1.pc_end = 0x800;
   fde1.cie = &cie;
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x600, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde1));
-  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x600)).WillOnce(::testing::Return(&fde1));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
       .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
 
-  ASSERT_TRUE(mock_section.Step(0x600, nullptr, &process, &finished));
-  ASSERT_TRUE(mock_section.Step(0x700, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x600, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x700, nullptr, &process, &finished));
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index 66207db..3d5ddd6 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -32,7 +32,7 @@
 std::deque<FunctionData> ElfInterfaceFake::functions_;
 std::deque<StepData> ElfInterfaceFake::steps_;
 
-bool ElfInterfaceFake::GetFunctionName(uint64_t, uint64_t, std::string* name, uint64_t* offset) {
+bool ElfInterfaceFake::GetFunctionName(uint64_t, std::string* name, uint64_t* offset) {
   if (functions_.empty()) {
     return false;
   }
@@ -52,7 +52,7 @@
   return true;
 }
 
-bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) {
+bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
   if (steps_.empty()) {
     return false;
   }
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index e232986..a3bf5ce 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -67,13 +67,13 @@
   virtual ~ElfInterfaceFake() = default;
 
   bool Init(uint64_t*) override { return false; }
-  void InitHeaders() override {}
+  void InitHeaders(uint64_t) override {}
   bool GetSoname(std::string*) override { return false; }
 
-  bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
+  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
   bool GetGlobalVariable(const std::string&, uint64_t*) override;
 
-  bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override;
+  bool Step(uint64_t, Regs*, Memory*, bool*) override;
 
   void FakeSetGlobalVariable(const std::string& global, uint64_t offset) {
     globals_[global] = offset;
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 70a52ad..43c6a97 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -242,59 +242,21 @@
   ASSERT_EQ(0xa020U, entries[4]);
 }
 
-TEST_F(ElfInterfaceArmTest, HandleType_not_arm_exidx) {
+TEST_F(ElfInterfaceArmTest, HandleUnknownType_arm_exidx) {
   ElfInterfaceArmFake interface(&memory_);
 
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME, 0));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK, 0));
-}
-
-TEST_F(ElfInterfaceArmTest, HandleType_arm_exidx) {
-  ElfInterfaceArmFake interface(&memory_);
-
-  Elf32_Phdr phdr;
   interface.FakeSetStartOffset(0x1000);
   interface.FakeSetTotalEntries(100);
-  phdr.p_vaddr = 0x2000;
-  phdr.p_memsz = 0xa00;
 
-  // Verify that if reads fail, we don't set the values but still get true.
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
-  ASSERT_EQ(0x1000U, interface.start_offset());
-  ASSERT_EQ(100U, interface.total_entries());
-
-  // Verify that if the second read fails, we still don't set the values.
-  memory_.SetData32(
-      0x1000 + reinterpret_cast<uint64_t>(&phdr.p_vaddr) - reinterpret_cast<uint64_t>(&phdr),
-      phdr.p_vaddr);
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
+  // Verify that if the type is not the one we want, we don't set the values.
+  interface.HandleUnknownType(0x70000000, 0x2000, 320);
   ASSERT_EQ(0x1000U, interface.start_offset());
   ASSERT_EQ(100U, interface.total_entries());
 
   // Everything is correct and present.
-  memory_.SetData32(
-      0x1000 + reinterpret_cast<uint64_t>(&phdr.p_memsz) - reinterpret_cast<uint64_t>(&phdr),
-      phdr.p_memsz);
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
+  interface.HandleUnknownType(0x70000001, 0x2000, 320);
   ASSERT_EQ(0x2000U, interface.start_offset());
-  ASSERT_EQ(320U, interface.total_entries());
-
-  // Non-zero load bias.
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0x1000));
-  ASSERT_EQ(0x1000U, interface.start_offset());
-  ASSERT_EQ(320U, interface.total_entries());
+  ASSERT_EQ(40U, interface.total_entries());
 }
 
 TEST_F(ElfInterfaceArmTest, StepExidx) {
@@ -302,7 +264,7 @@
 
   // FindEntry fails.
   bool finished;
-  ASSERT_FALSE(interface.StepExidx(0x7000, 0, nullptr, nullptr, &finished));
+  ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // ExtractEntry should fail.
@@ -316,18 +278,18 @@
   regs[ARM_REG_LR] = 0x20000;
   regs.set_sp(regs[ARM_REG_SP]);
   regs.set_pc(0x1234);
-  ASSERT_FALSE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_MEMORY_INVALID, interface.LastErrorCode());
   EXPECT_EQ(0x1004U, interface.LastErrorAddress());
 
   // Eval should fail.
   memory_.SetData32(0x1004, 0x81000000);
-  ASSERT_FALSE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // Everything should pass.
   memory_.SetData32(0x1004, 0x80b0b0b0);
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
   ASSERT_FALSE(finished);
   ASSERT_EQ(0x1000U, regs.sp());
@@ -336,11 +298,13 @@
   ASSERT_EQ(0x20000U, regs[ARM_REG_PC]);
 
   // Load bias is non-zero.
-  ASSERT_TRUE(interface.StepExidx(0x8000, 0x1000, &regs, &process_memory_, &finished));
+  interface.set_load_bias(0x1000);
+  ASSERT_TRUE(interface.StepExidx(0x8000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // Pc too small.
-  ASSERT_FALSE(interface.StepExidx(0x8000, 0x9000, &regs, &process_memory_, &finished));
+  interface.set_load_bias(0x9000);
+  ASSERT_FALSE(interface.StepExidx(0x8000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 }
 
@@ -362,7 +326,7 @@
 
   // Everything should pass.
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_FALSE(finished);
   ASSERT_EQ(0x10004U, regs.sp());
@@ -386,7 +350,7 @@
   regs.set_pc(0x1234);
 
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0x10000U, regs.sp());
@@ -409,7 +373,7 @@
   regs.set_pc(0x1234);
 
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0x10000U, regs.sp());
@@ -436,7 +400,7 @@
   regs.set_pc(0x1234);
 
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0U, regs.pc());
@@ -449,7 +413,7 @@
   regs.set_sp(regs[ARM_REG_SP]);
   regs.set_pc(0x1234);
 
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0U, regs.pc());
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index bf97e30..aa6df84 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -116,8 +116,7 @@
 template <typename Sym>
 void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
                                uint64_t sym_offset, const char* name) {
-  Sym sym;
-  memset(&sym, 0, sizeof(sym));
+  Sym sym = {};
   sym.st_info = STT_FUNC;
   sym.st_value = value;
   sym.st_size = size;
@@ -132,15 +131,13 @@
 void ElfInterfaceTest::SinglePtLoad() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 1;
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Phdr phdr = {};
   phdr.p_type = PT_LOAD;
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0x10000;
@@ -172,15 +169,13 @@
 void ElfInterfaceTest::MultipleExecutablePtLoads() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 3;
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Phdr phdr = {};
   phdr.p_type = PT_LOAD;
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0x10000;
@@ -241,15 +236,13 @@
 void ElfInterfaceTest::MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 3;
   ehdr.e_phentsize = sizeof(Phdr) + 100;
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Phdr phdr = {};
   phdr.p_type = PT_LOAD;
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0x10000;
@@ -312,15 +305,13 @@
 void ElfInterfaceTest::NonExecutablePtLoads() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 3;
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Phdr phdr = {};
   phdr.p_type = PT_LOAD;
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0x10000;
@@ -371,17 +362,15 @@
 void ElfInterfaceTest::ManyPhdrs() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 7;
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Phdr phdr;
   uint64_t phdr_offset = 0x100;
 
-  memset(&phdr, 0, sizeof(phdr));
+  Phdr phdr = {};
   phdr.p_type = PT_LOAD;
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0x10000;
@@ -444,18 +433,16 @@
 TEST_F(ElfInterfaceTest, elf32_arm) {
   ElfInterfaceArm elf_arm(&memory_);
 
-  Elf32_Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Elf32_Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 1;
   ehdr.e_phentsize = sizeof(Elf32_Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Elf32_Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Elf32_Phdr phdr = {};
   phdr.p_type = PT_ARM_EXIDX;
-  phdr.p_vaddr = 0x2000;
-  phdr.p_memsz = 16;
+  phdr.p_offset = 0x2000;
+  phdr.p_filesz = 16;
   memory_.SetMemory(0x100, &phdr, sizeof(phdr));
 
   // Add arm exidx entries.
@@ -480,8 +467,7 @@
 
 template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
 void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) {
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_shoff = 0x200;
   ehdr.e_shnum = 2;
   ehdr.e_shentsize = sizeof(Shdr);
@@ -490,8 +476,7 @@
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Shdr shdr;
-  memset(&shdr, 0, sizeof(shdr));
+  Shdr shdr = {};
   shdr.sh_type = SHT_STRTAB;
   if (test_type == SONAME_MISSING_MAP) {
     shdr.sh_addr = 0x20100;
@@ -501,8 +486,7 @@
   shdr.sh_offset = 0x10000;
   memory_.SetMemory(0x200 + sizeof(shdr), &shdr, sizeof(shdr));
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Phdr phdr = {};
   phdr.p_type = PT_DYNAMIC;
   phdr.p_offset = 0x2000;
   phdr.p_memsz = sizeof(Dyn) * 3;
@@ -647,7 +631,7 @@
   memory_.SetData32(0x10004, 0x500);
   memory_.SetData32(0x10008, 250);
 
-  elf.InitHeaders();
+  elf.InitHeaders(0);
 
   EXPECT_FALSE(elf.eh_frame() == nullptr);
   EXPECT_TRUE(elf.debug_frame() == nullptr);
@@ -672,15 +656,14 @@
 
   memory_.SetData32(0x5000, 0xfc);
   memory_.SetData32(0x5004, 0xffffffff);
-  memory_.SetData8(0x5008, 1);
-  memory_.SetData8(0x5009, '\0');
+  memory_.SetMemory(0x5008, std::vector<uint8_t>{1, '\0', 4, 8, 2});
 
   memory_.SetData32(0x5100, 0xfc);
   memory_.SetData32(0x5104, 0);
   memory_.SetData32(0x5108, 0x1500);
   memory_.SetData32(0x510c, 0x200);
 
-  elf.InitHeaders();
+  elf.InitHeaders(0);
 
   EXPECT_TRUE(elf.eh_frame() == nullptr);
   EXPECT_FALSE(elf.debug_frame() == nullptr);
@@ -694,62 +677,11 @@
   InitHeadersDebugFrame<ElfInterface64Fake>();
 }
 
-template <typename ElfType>
-void ElfInterfaceTest::InitHeadersEhFrameFail() {
-  ElfType elf(&memory_);
-
-  elf.FakeSetEhFrameOffset(0x1000);
-  elf.FakeSetEhFrameSize(0x100);
-  elf.FakeSetDebugFrameOffset(0);
-  elf.FakeSetDebugFrameSize(0);
-
-  elf.InitHeaders();
-
-  EXPECT_TRUE(elf.eh_frame() == nullptr);
-  EXPECT_EQ(0U, elf.eh_frame_offset());
-  EXPECT_EQ(static_cast<uint64_t>(-1), elf.eh_frame_size());
-  EXPECT_TRUE(elf.debug_frame() == nullptr);
-}
-
-TEST_F(ElfInterfaceTest, init_headers_eh_frame32_fail) {
-  InitHeadersEhFrameFail<ElfInterface32Fake>();
-}
-
-TEST_F(ElfInterfaceTest, init_headers_eh_frame64_fail) {
-  InitHeadersEhFrameFail<ElfInterface64Fake>();
-}
-
-template <typename ElfType>
-void ElfInterfaceTest::InitHeadersDebugFrameFail() {
-  ElfType elf(&memory_);
-
-  elf.FakeSetEhFrameOffset(0);
-  elf.FakeSetEhFrameSize(0);
-  elf.FakeSetDebugFrameOffset(0x1000);
-  elf.FakeSetDebugFrameSize(0x100);
-
-  elf.InitHeaders();
-
-  EXPECT_TRUE(elf.eh_frame() == nullptr);
-  EXPECT_TRUE(elf.debug_frame() == nullptr);
-  EXPECT_EQ(0U, elf.debug_frame_offset());
-  EXPECT_EQ(static_cast<uint64_t>(-1), elf.debug_frame_size());
-}
-
-TEST_F(ElfInterfaceTest, init_headers_debug_frame32_fail) {
-  InitHeadersDebugFrameFail<ElfInterface32Fake>();
-}
-
-TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) {
-  InitHeadersDebugFrameFail<ElfInterface64Fake>();
-}
-
 template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
 void ElfInterfaceTest::InitSectionHeadersMalformed() {
   std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_shoff = 0x1000;
   ehdr.e_shnum = 10;
   ehdr.e_shentsize = sizeof(Shdr);
@@ -774,8 +706,7 @@
 
   uint64_t offset = 0x1000;
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_shoff = offset;
   ehdr.e_shnum = 10;
   ehdr.e_shentsize = entry_size;
@@ -783,8 +714,7 @@
 
   offset += ehdr.e_shentsize;
 
-  Shdr shdr;
-  memset(&shdr, 0, sizeof(shdr));
+  Shdr shdr = {};
   shdr.sh_type = SHT_SYMTAB;
   shdr.sh_link = 4;
   shdr.sh_addr = 0x5000;
@@ -833,10 +763,10 @@
   // Look in the first symbol table.
   std::string name;
   uint64_t name_offset;
-  ASSERT_TRUE(elf->GetFunctionName(0x90010, 0, &name, &name_offset));
+  ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset));
   EXPECT_EQ("function_one", name);
   EXPECT_EQ(16U, name_offset);
-  ASSERT_TRUE(elf->GetFunctionName(0xd0020, 0, &name, &name_offset));
+  ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset));
   EXPECT_EQ("function_two", name);
   EXPECT_EQ(32U, name_offset);
 }
@@ -863,8 +793,7 @@
 
   uint64_t offset = 0x2000;
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_shoff = offset;
   ehdr.e_shnum = 10;
   ehdr.e_shentsize = sizeof(Shdr);
@@ -873,8 +802,7 @@
 
   offset += ehdr.e_shentsize;
 
-  Shdr shdr;
-  memset(&shdr, 0, sizeof(shdr));
+  Shdr shdr = {};
   shdr.sh_type = SHT_PROGBITS;
   shdr.sh_link = 2;
   shdr.sh_name = 0x200;
@@ -956,15 +884,13 @@
 TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
   std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
 
-  Elf32_Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Elf32_Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 1;
   ehdr.e_phentsize = sizeof(Elf32_Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Elf32_Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Elf32_Phdr phdr = {};
   phdr.p_type = PT_LOAD;
   phdr.p_vaddr = 0;
   phdr.p_memsz = 0x10000;
@@ -984,15 +910,13 @@
 TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load_non_zero_load_bias) {
   std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
 
-  Elf32_Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Elf32_Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 1;
   ehdr.e_phentsize = sizeof(Elf32_Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Elf32_Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Elf32_Phdr phdr = {};
   phdr.p_type = PT_LOAD;
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0x10000;
@@ -1017,16 +941,14 @@
 
   uint64_t sh_offset = 0x100;
 
-  Elf32_Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Elf32_Ehdr ehdr = {};
   ehdr.e_shstrndx = 1;
   ehdr.e_shoff = sh_offset;
   ehdr.e_shentsize = sizeof(Elf32_Shdr);
   ehdr.e_shnum = 3;
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Elf32_Shdr shdr;
-  memset(&shdr, 0, sizeof(shdr));
+  Elf32_Shdr shdr = {};
   shdr.sh_type = SHT_NULL;
   memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
 
@@ -1051,11 +973,7 @@
   // CIE 32.
   memory_.SetData32(0x600, 0xfc);
   memory_.SetData32(0x604, 0xffffffff);
-  memory_.SetData8(0x608, 1);
-  memory_.SetData8(0x609, '\0');
-  memory_.SetData8(0x60a, 0x4);
-  memory_.SetData8(0x60b, 0x4);
-  memory_.SetData8(0x60c, 0x1);
+  memory_.SetMemory(0x608, std::vector<uint8_t>{1, '\0', 4, 4, 1});
 
   // FDE 32.
   memory_.SetData32(0x700, 0xfc);
@@ -1065,7 +983,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  elf->InitHeaders();
+  elf->InitHeaders(0);
   EXPECT_EQ(0U, load_bias);
   EXPECT_FALSE(elf->IsValidPc(0));
   EXPECT_FALSE(elf->IsValidPc(0x20ff));
@@ -1080,16 +998,14 @@
 
   uint64_t sh_offset = 0x100;
 
-  Elf32_Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Elf32_Ehdr ehdr = {};
   ehdr.e_shstrndx = 1;
   ehdr.e_shoff = sh_offset;
   ehdr.e_shentsize = sizeof(Elf32_Shdr);
   ehdr.e_shnum = 3;
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Elf32_Shdr shdr;
-  memset(&shdr, 0, sizeof(shdr));
+  Elf32_Shdr shdr = {};
   shdr.sh_type = SHT_NULL;
   memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
 
@@ -1114,11 +1030,7 @@
   // CIE 32.
   memory_.SetData32(0x600, 0xfc);
   memory_.SetData32(0x604, 0);
-  memory_.SetData8(0x608, 1);
-  memory_.SetData8(0x609, '\0');
-  memory_.SetData8(0x60a, 0x4);
-  memory_.SetData8(0x60b, 0x4);
-  memory_.SetData8(0x60c, 0x1);
+  memory_.SetMemory(0x608, std::vector<uint8_t>{1, '\0', 4, 4, 1});
 
   // FDE 32.
   memory_.SetData32(0x700, 0xfc);
@@ -1128,7 +1040,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  elf->InitHeaders();
+  elf->InitHeaders(0);
   EXPECT_EQ(0U, load_bias);
   EXPECT_FALSE(elf->IsValidPc(0));
   EXPECT_FALSE(elf->IsValidPc(0x27ff));
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index aecbf6d..55fe16f 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -297,16 +297,11 @@
   elf.FakeSetInterface(interface);
 
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
   MapInfo map_info(0x1000, 0x2000);
 
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 
-  elf.FakeSetLoadBias(0x3000);
-  ASSERT_EQ(0x3101U, elf.GetRelPc(0x1101, &map_info));
-
   elf.FakeSetValid(false);
-  elf.FakeSetLoadBias(0);
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 }
 
@@ -328,7 +323,6 @@
   }
 
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
   bool finished;
   ASSERT_TRUE(elf.Step(0x3000, 0x1000, &regs, &process_memory, &finished));
   EXPECT_FALSE(finished);
@@ -342,11 +336,11 @@
   virtual ~ElfInterfaceMock() = default;
 
   bool Init(uint64_t*) override { return false; }
-  void InitHeaders() override {}
+  void InitHeaders(uint64_t) override {}
   bool GetSoname(std::string*) override { return false; }
-  bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
+  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
 
-  MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*));
+  MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
   MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
   MOCK_METHOD1(IsValidPc, bool(uint64_t));
 
@@ -358,7 +352,6 @@
 TEST_F(ElfTest, step_in_interface) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   RegsArm regs;
 
@@ -367,30 +360,12 @@
   MemoryFake process_memory;
 
   bool finished;
-  EXPECT_CALL(*interface, Step(0x1000, 0, &regs, &process_memory, &finished))
+  EXPECT_CALL(*interface, Step(0x1000, &regs, &process_memory, &finished))
       .WillOnce(::testing::Return(true));
 
   ASSERT_TRUE(elf.Step(0x1004, 0x1000, &regs, &process_memory, &finished));
 }
 
-TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
-  ElfFake elf(memory_);
-  elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0x4000);
-
-  RegsArm regs;
-
-  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
-  elf.FakeSetInterface(interface);
-  MemoryFake process_memory;
-
-  bool finished;
-  EXPECT_CALL(*interface, Step(0x7300, 0x4000, &regs, &process_memory, &finished))
-      .WillOnce(::testing::Return(true));
-
-  ASSERT_TRUE(elf.Step(0x7304, 0x7300, &regs, &process_memory, &finished));
-}
-
 TEST_F(ElfTest, get_global_invalid_elf) {
   ElfFake elf(memory_);
   elf.FakeSetValid(false);
@@ -403,7 +378,6 @@
 TEST_F(ElfTest, get_global_valid_not_in_interface) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
@@ -431,10 +405,26 @@
   ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
 }
 
+TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0x100);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+  EXPECT_EQ(0x200U, offset);
+}
+
 TEST_F(ElfTest, get_global_valid_dynamic_zero) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
@@ -456,7 +446,6 @@
 TEST_F(ElfTest, get_global_valid_in_gnu_debugdata_dynamic_zero) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
@@ -470,27 +459,9 @@
   EXPECT_EQ(0x300U, offset);
 }
 
-TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
-  ElfFake elf(memory_);
-  elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0x100);
-
-  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
-  elf.FakeSetInterface(interface);
-
-  uint64_t offset;
-  std::string global("something");
-  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
-      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
-
-  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
-  EXPECT_EQ(0x200U, offset);
-}
-
 TEST_F(ElfTest, get_global_valid_dynamic_adjust_negative) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   interface->MockSetDynamicOffset(0x400);
@@ -510,7 +481,6 @@
 TEST_F(ElfTest, get_global_valid_dynamic_adjust_positive) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   interface->MockSetDynamicOffset(0x1000);
@@ -530,7 +500,6 @@
 TEST_F(ElfTest, is_valid_pc_elf_invalid) {
   ElfFake elf(memory_);
   elf.FakeSetValid(false);
-  elf.FakeSetLoadBias(0);
 
   EXPECT_FALSE(elf.IsValidPc(0x100));
   EXPECT_FALSE(elf.IsValidPc(0x200));
@@ -539,7 +508,6 @@
 TEST_F(ElfTest, is_valid_pc_interface) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
@@ -549,25 +517,9 @@
   EXPECT_TRUE(elf.IsValidPc(0x1500));
 }
 
-TEST_F(ElfTest, is_valid_pc_non_zero_load_bias) {
-  ElfFake elf(memory_);
-  elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0x1000);
-
-  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
-  elf.FakeSetInterface(interface);
-
-  EXPECT_CALL(*interface, IsValidPc(0x500)).WillOnce(::testing::Return(true));
-
-  EXPECT_FALSE(elf.IsValidPc(0x100));
-  EXPECT_FALSE(elf.IsValidPc(0x200));
-  EXPECT_TRUE(elf.IsValidPc(0x1500));
-}
-
 TEST_F(ElfTest, is_valid_pc_from_gnu_debugdata) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
new file mode 100644
index 0000000..56a18cd
--- /dev/null
+++ b/libunwindstack/tests/LocalUnwinderTest.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/LocalUnwinder.h>
+
+namespace unwindstack {
+
+static std::vector<LocalFrameData>* g_frame_info;
+static LocalUnwinder* g_unwinder;
+
+extern "C" void SignalLocalInnerFunction() {
+  g_unwinder->Unwind(g_frame_info, 256);
+}
+
+extern "C" void SignalLocalMiddleFunction() {
+  SignalLocalInnerFunction();
+}
+
+extern "C" void SignalLocalOuterFunction() {
+  SignalLocalMiddleFunction();
+}
+
+static void SignalLocalCallerHandler(int, siginfo_t*, void*) {
+  SignalLocalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names,
+                            const std::vector<LocalFrameData>& frame_info) {
+  std::string unwind;
+  size_t i = 0;
+  for (const auto& frame : frame_info) {
+    unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++,
+                                          frame.pc, frame.rel_pc);
+    if (frame.map_info != nullptr) {
+      if (!frame.map_info->name.empty()) {
+        unwind += " " + frame.map_info->name;
+      } else {
+        unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start,
+                                              frame.map_info->end);
+      }
+      if (frame.map_info->offset != 0) {
+        unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset);
+      }
+    }
+    if (!frame.function_name.empty()) {
+      unwind += " " + frame.function_name;
+      if (frame.function_offset != 0) {
+        unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset);
+      }
+    }
+    unwind += '\n';
+  }
+
+  return std::string(
+             "Unwind completed without finding all frames\n"
+             "  Looking for function: ") +
+         function_names.front() + "\n" + "Unwind data:\n" + unwind;
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+  std::vector<LocalFrameData> frame_info;
+  g_frame_info = &frame_info;
+  g_unwinder = unwinder;
+  std::vector<const char*> expected_function_names;
+
+  if (unwind_through_signal) {
+    struct sigaction act, oldact;
+    memset(&act, 0, sizeof(act));
+    act.sa_sigaction = SignalLocalCallerHandler;
+    act.sa_flags = SA_RESTART | SA_ONSTACK;
+    ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+
+    raise(SIGUSR1);
+
+    ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+    expected_function_names = {"LocalOuterFunction",        "LocalMiddleFunction",
+                               "LocalInnerFunction",        "SignalLocalOuterFunction",
+                               "SignalLocalMiddleFunction", "SignalLocalInnerFunction"};
+  } else {
+    ASSERT_TRUE(unwinder->Unwind(&frame_info, 256));
+
+    expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"};
+  }
+
+  for (auto& frame : frame_info) {
+    if (frame.function_name == expected_function_names.back()) {
+      expected_function_names.pop_back();
+      if (expected_function_names.empty()) {
+        break;
+      }
+    }
+  }
+
+  ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+  LocalInnerFunction(unwinder, unwind_through_signal);
+}
+
+extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+  LocalMiddleFunction(unwinder, unwind_through_signal);
+}
+
+class LocalUnwinderTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    unwinder_.reset(new LocalUnwinder);
+    ASSERT_TRUE(unwinder_->Init());
+  }
+
+  std::unique_ptr<LocalUnwinder> unwinder_;
+};
+
+TEST_F(LocalUnwinderTest, local) {
+  LocalOuterFunction(unwinder_.get(), false);
+}
+
+TEST_F(LocalUnwinderTest, local_signal) {
+  LocalOuterFunction(unwinder_.get(), true);
+}
+
+TEST_F(LocalUnwinderTest, local_multiple) {
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+}
+
+// This test verifies that doing an unwind before and after a dlopen
+// works. It's verifying that the maps read during the first unwind
+// do not cause a problem when doing the unwind using the code in
+// the dlopen'd code.
+TEST_F(LocalUnwinderTest, unwind_after_dlopen) {
+  // Prime the maps data.
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+  std::string testlib(testing::internal::GetArgvs()[0]);
+  auto const value = testlib.find_last_of('/');
+  if (value == std::string::npos) {
+    testlib = "../";
+  } else {
+    testlib = testlib.substr(0, value + 1) + "../";
+  }
+  testlib += "libunwindstack_local.so";
+
+  void* handle = dlopen(testlib.c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle != nullptr);
+
+  void (*unwind_function)(void*, void*) =
+      reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1"));
+  ASSERT_TRUE(unwind_function != nullptr);
+
+  std::vector<LocalFrameData> frame_info;
+  unwind_function(unwinder_.get(), &frame_info);
+
+  ASSERT_EQ(0, dlclose(handle));
+
+  std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2",
+                                                   "TestlibLevel3", "TestlibLevel4"};
+
+  for (auto& frame : frame_info) {
+    if (frame.function_name == expected_function_names.back()) {
+      expected_function_names.pop_back();
+      if (expected_function_names.empty()) {
+        break;
+      }
+    }
+  }
+
+  ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 4a9ed9f..3655984 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -51,40 +51,6 @@
   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");
 
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index 45a7b58..b40a253 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -70,18 +70,18 @@
 
   std::string name;
   uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, &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_TRUE(symbols.GetName<TypeParam>(0x500f, &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));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, &this->memory_, &name, &func_offset));
 }
 
 TYPED_TEST_P(SymbolsTest, no_symbol) {
@@ -98,7 +98,7 @@
   // 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_TRUE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
   ASSERT_EQ("fake_function", name);
   ASSERT_EQ(0U, func_offset);
 
@@ -107,7 +107,7 @@
   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));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
 
   // Set the function back, and set the shndx to UNDEF.
   sym.st_info = STT_FUNC;
@@ -115,7 +115,7 @@
   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));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
 }
 
 TYPED_TEST_P(SymbolsTest, multiple_entries) {
@@ -144,34 +144,34 @@
 
   std::string name;
   uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, &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_TRUE(symbols.GetName<TypeParam>(0x5004, &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_TRUE(symbols.GetName<TypeParam>(0xa011, &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_TRUE(symbols.GetName<TypeParam>(0x5008, &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_TRUE(symbols.GetName<TypeParam>(0x3008, &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_TRUE(symbols.GetName<TypeParam>(0xa01a, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_three", name);
   ASSERT_EQ(0xaU, func_offset);
 }
@@ -203,47 +203,21 @@
 
   std::string name;
   uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, &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_TRUE(symbols.GetName<TypeParam>(0x5004, &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_TRUE(symbols.GetName<TypeParam>(0xa011, &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);
@@ -265,18 +239,16 @@
   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_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, &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_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, &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));
+  ASSERT_FALSE(symbols_end_at_100.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols_end_at_100.GetName<TypeParam>(0x3000, &this->memory_, &name, &func_offset));
 }
 
 // Verify the entire func table is cached.
@@ -302,9 +274,9 @@
   // 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));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
   this->memory_.Clear();
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
 
   // Clear the memory and only put the symbol data string data in memory.
   this->memory_.Clear();
@@ -317,15 +289,15 @@
   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_TRUE(symbols.GetName<TypeParam>(0x5001, &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_TRUE(symbols.GetName<TypeParam>(0x2002, &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_TRUE(symbols.GetName<TypeParam>(0x1003, &this->memory_, &name, &func_offset));
   ASSERT_EQ("third_entry", name);
   ASSERT_EQ(3U, func_offset);
 }
@@ -381,17 +353,17 @@
   EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_1", &offset));
 
   std::string name;
-  EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, 0, &this->memory_, &name, &offset));
+  EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, &this->memory_, &name, &offset));
   EXPECT_EQ("function_0", name);
   EXPECT_EQ(2U, offset);
 
-  EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, 0, &this->memory_, &name, &offset));
+  EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, &this->memory_, &name, &offset));
   EXPECT_EQ("function_1", name);
   EXPECT_EQ(4U, 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,
+                           multiple_entries_nonstandard_size, symtab_value_out_of_bounds,
                            symtab_read_cached, get_global);
 
 typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
diff --git a/libunwindstack/tests/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp
new file mode 100644
index 0000000..fa0baff
--- /dev/null
+++ b/libunwindstack/tests/TestLocal.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unwindstack/LocalUnwinder.h>
+
+#include <vector>
+
+extern "C" void TestlibLevel4(void* unwinder_data, void* frame_data) {
+  unwindstack::LocalUnwinder* unwinder =
+      reinterpret_cast<unwindstack::LocalUnwinder*>(unwinder_data);
+  std::vector<unwindstack::LocalFrameData>* frame_info =
+      reinterpret_cast<std::vector<unwindstack::LocalFrameData>*>(frame_data);
+  unwinder->Unwind(frame_info, 256);
+}
+
+extern "C" void TestlibLevel3(void* unwinder_data, void* frame_data) {
+  TestlibLevel4(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel2(void* unwinder_data, void* frame_data) {
+  TestlibLevel3(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel1(void* unwinder_data, void* frame_data) {
+  TestlibLevel2(unwinder_data, frame_data);
+}
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 285fc9e..a65c077 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1200,4 +1200,43 @@
   EXPECT_EQ(0xffcc1558U, unwinder.frames()[18].sp);
 }
 
+// Test using a non-zero load bias library that has the fde entries
+// encoded as 0xb, which is not set as pc relative.
+TEST_F(UnwindOfflineTest, debug_frame_load_bias_arm) {
+  ASSERT_NO_FATAL_FAILURE(Init("debug_frame_load_bias_arm/", ARCH_ARM));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(8U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0005138c  libc.so (__ioctl+8)\n"
+      "  #01 pc 0002140f  libc.so (ioctl+30)\n"
+      "  #02 pc 00039535  libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb+204)\n"
+      "  #03 pc 00039633  libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+10)\n"
+      "  #04 pc 00039b57  libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+38)\n"
+      "  #05 pc 00000c21  mediaserver (main+104)\n"
+      "  #06 pc 00084b89  libc.so (__libc_init+48)\n"
+      "  #07 pc 00000b77  mediaserver (_start_main+38)\n",
+      frame_info);
+
+  EXPECT_EQ(0xf0be238cU, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xffd4a638U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xf0bb240fU, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xffd4a638U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0xf1a75535U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xffd4a650U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0xf1a75633U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xffd4a6b0U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xf1a75b57U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xffd4a6d0U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x8d1cc21U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0xffd4a6e8U, unwinder.frames()[5].sp);
+  EXPECT_EQ(0xf0c15b89U, unwinder.frames()[6].pc);
+  EXPECT_EQ(0xffd4a700U, unwinder.frames()[6].sp);
+  EXPECT_EQ(0x8d1cb77U, unwinder.frames()[7].pc);
+  EXPECT_EQ(0xffd4a718U, unwinder.frames()[7].sp);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 242cc6a..ea992c7 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -32,6 +32,7 @@
 #include <vector>
 
 #include <android-base/stringprintf.h>
+#include <android-base/threads.h>
 
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
@@ -106,15 +107,12 @@
   Unwinder unwinder(512, maps, regs, process_memory);
   unwinder.Unwind();
 
-  std::string expected_function = expected_function_names.back();
-  expected_function_names.pop_back();
   for (auto& frame : unwinder.frames()) {
-    if (frame.function_name == expected_function) {
+    if (frame.function_name == expected_function_names.back()) {
+      expected_function_names.pop_back();
       if (expected_function_names.empty()) {
         break;
       }
-      expected_function = expected_function_names.back();
-      expected_function_names.pop_back();
     }
   }
 
@@ -234,8 +232,7 @@
     usleep(1000);
   }
   ASSERT_NE(0, tid.load());
-  // Portable tgkill method.
-  ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
+  ASSERT_EQ(0, tgkill(getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
 
   // Wait for context data.
   void* ucontext;
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
new file mode 100644
index 0000000..4b7bf44
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
new file mode 100644
index 0000000..013858e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
new file mode 100644
index 0000000..10f1325
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
@@ -0,0 +1,3 @@
+8d1c000-8d1f000 r-xp 0 00:00 0   mediaserver
+f0b91000-f0c2c000 r-xp 0 00:00 0   libc.so
+f1a41000-f1a97000 r-xp 0 00:00 0   libbinder.so
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
new file mode 100644
index 0000000..9e4a83f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
new file mode 100644
index 0000000..f147247
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: 3
+r1: c0306201
+r2: ffd4a658
+r3: 0
+r4: f0c36d8c
+r5: ffd4a658
+r6: f0168000
+r7: 36
+r8: ffd4a678
+r9: f016802c
+r10: ffd4a660
+r11: 0
+ip: 0
+sp: ffd4a638
+lr: f0bb2413
+pc: f0be238c
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
new file mode 100644
index 0000000..847c819
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 5a8edfd..266a6db 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -37,7 +37,7 @@
 
 namespace unwindstack {
 
-void DumpArm(ElfInterfaceArm* interface) {
+void DumpArm(Elf* elf, ElfInterfaceArm* interface) {
   if (interface == nullptr) {
     printf("No ARM Unwind Information.\n\n");
     return;
@@ -48,12 +48,11 @@
     uint64_t load_bias = entry.second.table_offset;
     printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
            entry.second.table_size + load_bias);
-    for (auto addr : *interface) {
+    for (auto pc : *interface) {
       std::string name;
-      printf("  PC 0x%" PRIx64, addr + load_bias);
+      printf("  PC 0x%" PRIx64, pc + load_bias);
       uint64_t func_offset;
-      uint64_t pc = addr + load_bias;
-      if (interface->GetFunctionName(pc, load_bias, &name, &func_offset) && !name.empty()) {
+      if (elf->GetFunctionName(pc + load_bias, &name, &func_offset) && !name.empty()) {
         printf(" <%s>", name.c_str());
       }
       printf("\n");
@@ -63,7 +62,7 @@
         continue;
       }
       ArmExidx arm(nullptr, interface->memory(), nullptr);
-      arm.set_log(true);
+      arm.set_log(ARM_LOG_FULL);
       arm.set_log_skip_execution(true);
       arm.set_log_indent(2);
       if (!arm.ExtractEntryData(entry)) {
@@ -82,21 +81,21 @@
   printf("\n");
 }
 
-void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t load_bias) {
+void DumpDwarfSection(Elf* elf, DwarfSection* section, uint64_t) {
   for (const DwarfFde* fde : *section) {
     // Sometimes there are entries that have empty length, skip those since
     // they don't contain any interesting information.
     if (fde == nullptr || fde->pc_start == fde->pc_end) {
       continue;
     }
-    printf("\n  PC 0x%" PRIx64, fde->pc_start + load_bias);
+    printf("\n  PC 0x%" PRIx64 "-0x%" PRIx64, fde->pc_start, fde->pc_end);
     std::string name;
     uint64_t func_offset;
-    if (interface->GetFunctionName(fde->pc_start, load_bias, &name, &func_offset) && !name.empty()) {
+    if (elf->GetFunctionName(fde->pc_start, &name, &func_offset) && !name.empty()) {
       printf(" <%s>", name.c_str());
     }
     printf("\n");
-    if (!section->Log(2, UINT64_MAX, load_bias, fde)) {
+    if (!section->Log(2, UINT64_MAX, fde)) {
       printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
     }
   }
@@ -126,13 +125,13 @@
 
   ElfInterface* interface = elf.interface();
   if (elf.machine_type() == EM_ARM) {
-    DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
+    DumpArm(&elf, reinterpret_cast<ElfInterfaceArm*>(interface));
     printf("\n");
   }
 
   if (interface->eh_frame() != nullptr) {
     printf("eh_frame information:\n");
-    DumpDwarfSection(interface, interface->eh_frame(), elf.GetLoadBias());
+    DumpDwarfSection(&elf, interface->eh_frame(), elf.GetLoadBias());
     printf("\n");
   } else {
     printf("\nno eh_frame information\n");
@@ -140,7 +139,7 @@
 
   if (interface->debug_frame() != nullptr) {
     printf("\ndebug_frame information:\n");
-    DumpDwarfSection(interface, interface->debug_frame(), elf.GetLoadBias());
+    DumpDwarfSection(&elf, interface->debug_frame(), elf.GetLoadBias());
     printf("\n");
   } else {
     printf("\nno debug_frame information\n");
@@ -151,12 +150,12 @@
   if (gnu_debugdata_interface != nullptr) {
     if (gnu_debugdata_interface->eh_frame() != nullptr) {
       printf("\ngnu_debugdata (eh_frame):\n");
-      DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->eh_frame(), 0);
+      DumpDwarfSection(&elf, gnu_debugdata_interface->eh_frame(), 0);
       printf("\n");
     }
     if (gnu_debugdata_interface->debug_frame() != nullptr) {
       printf("\ngnu_debugdata (debug_frame):\n");
-      DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->debug_frame(), 0);
+      DumpDwarfSection(&elf, gnu_debugdata_interface->debug_frame(), 0);
       printf("\n");
     }
   } else {
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 47a4f91..0f01566 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -34,7 +34,9 @@
 #include <unwindstack/ElfInterface.h>
 #include <unwindstack/Log.h>
 
+#include "ArmExidx.h"
 #include "DwarfOp.h"
+#include "ElfInterfaceArm.h"
 
 namespace unwindstack {
 
@@ -136,6 +138,32 @@
   }
 }
 
+void PrintArmRegInformation(ElfInterfaceArm* interface, uint64_t pc) {
+  printf("\nArm exidx:\n");
+  uint64_t entry_offset;
+  if (!interface->FindEntry(pc, &entry_offset)) {
+    return;
+  }
+
+  ArmExidx arm(nullptr, interface->memory(), nullptr);
+
+  log_to_stdout(true);
+  arm.set_log(ARM_LOG_BY_REG);
+  arm.set_log_skip_execution(true);
+  arm.set_log_indent(1);
+  if (!arm.ExtractEntryData(entry_offset)) {
+    if (arm.status() != ARM_STATUS_NO_UNWIND) {
+      printf("  Error trying to extract data.\n");
+    }
+    return;
+  }
+  if (arm.data()->size() != 0 && arm.Eval()) {
+    arm.LogByReg();
+  } else {
+    printf("  Error tring to evaluate exidx data.\n");
+  }
+}
+
 int GetInfo(const char* file, uint64_t pc) {
   MemoryFileAtOffset* memory = new MemoryFileAtOffset;
   if (!memory->Init(file, 0)) {
@@ -162,12 +190,22 @@
     printf("Soname: %s\n\n", soname.c_str());
   }
 
-  printf("PC 0x%" PRIx64 ":\n", pc);
+  printf("PC 0x%" PRIx64, pc);
+  std::string function_name;
+  uint64_t function_offset;
+  if (elf.GetFunctionName(pc, &function_name, &function_offset)) {
+    printf(" (%s)", function_name.c_str());
+  }
+  printf(":\n");
+
+  if (elf.machine_type() == EM_ARM) {
+    PrintArmRegInformation(reinterpret_cast<ElfInterfaceArm*>(interface), pc - load_bias);
+  }
 
   DwarfSection* section = interface->eh_frame();
   if (section != nullptr) {
     printf("\neh_frame:\n");
-    PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+    PrintRegInformation(section, memory, pc, elf.class_type());
   } else {
     printf("\nno eh_frame information\n");
   }
@@ -175,7 +213,7 @@
   section = interface->debug_frame();
   if (section != nullptr) {
     printf("\ndebug_frame:\n");
-    PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+    PrintRegInformation(section, memory, pc, elf.class_type());
     printf("\n");
   } else {
     printf("\nno debug_frame information\n");
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 086dffe..f8e3e92 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -95,7 +95,6 @@
   }
 
   std::string name;
-  uint64_t load_bias = elf.GetLoadBias();
   if (argc == 3) {
     std::string cur_name;
     uint64_t func_offset;
@@ -113,8 +112,8 @@
 
   // This is a crude way to get the symbols in order.
   for (const auto& entry : elf.interface()->pt_loads()) {
-    uint64_t start = entry.second.offset + load_bias;
-    uint64_t end = entry.second.table_size + load_bias;
+    uint64_t start = entry.second.offset;
+    uint64_t end = entry.second.table_size;
     for (uint64_t addr = start; addr < end; addr += 4) {
       std::string cur_name;
       uint64_t func_offset;
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 9395ef8..bbfa9d8 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -47,6 +47,7 @@
 cc_defaults {
     name: "libutils_defaults",
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -90,6 +91,10 @@
             },
         },
 
+        recovery: {
+            exclude_shared_libs: ["libvndksupport"],
+        },
+
         host: {
             cflags: ["-DLIBUTILS_NATIVE=1"],
 
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 3c4d81c..583c6b9 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -48,10 +48,10 @@
 
 // Constructor.  Create an empty object.
 FileMap::FileMap(void)
-    : mFileName(NULL),
-      mBasePtr(NULL),
+    : mFileName(nullptr),
+      mBasePtr(nullptr),
       mBaseLength(0),
-      mDataPtr(NULL),
+      mDataPtr(nullptr),
       mDataLength(0)
 #if defined(__MINGW32__)
       ,
@@ -69,9 +69,9 @@
       , mFileHandle(other.mFileHandle), mFileMapping(other.mFileMapping)
 #endif
 {
-    other.mFileName = NULL;
-    other.mBasePtr = NULL;
-    other.mDataPtr = NULL;
+    other.mFileName = nullptr;
+    other.mBasePtr = nullptr;
+    other.mDataPtr = nullptr;
 #if defined(__MINGW32__)
     other.mFileHandle = INVALID_HANDLE_VALUE;
     other.mFileMapping = NULL;
@@ -86,9 +86,9 @@
     mDataOffset = other.mDataOffset;
     mDataPtr = other.mDataPtr;
     mDataLength = other.mDataLength;
-    other.mFileName = NULL;
-    other.mBasePtr = NULL;
-    other.mDataPtr = NULL;
+    other.mFileName = nullptr;
+    other.mBasePtr = nullptr;
+    other.mDataPtr = nullptr;
 #if defined(__MINGW32__)
     mFileHandle = other.mFileHandle;
     mFileMapping = other.mFileMapping;
@@ -101,7 +101,7 @@
 // Destructor.
 FileMap::~FileMap(void)
 {
-    if (mFileName != NULL) {
+    if (mFileName != nullptr) {
         free(mFileName);
     }
 #if defined(__MINGW32__)
@@ -196,7 +196,7 @@
     if (!readOnly)
         prot |= PROT_WRITE;
 
-    ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset);
+    ptr = mmap(nullptr, adjLength, prot, flags, fd, adjOffset);
     if (ptr == MAP_FAILED) {
         ALOGE("mmap(%lld,%zu) failed: %s\n",
             (long long)adjOffset, adjLength, strerror(errno));
@@ -205,7 +205,7 @@
     mBasePtr = ptr;
 #endif // !defined(__MINGW32__)
 
-    mFileName = origFileName != NULL ? strdup(origFileName) : NULL;
+    mFileName = origFileName != nullptr ? strdup(origFileName) : nullptr;
     mBaseLength = adjLength;
     mDataOffset = offset;
     mDataPtr = (char*) mBasePtr + adjust;
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 6c57b2e..7bc2397 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -29,7 +29,7 @@
 
 void WeakMessageHandler::handleMessage(const Message& message) {
     sp<MessageHandler> handler = mHandler.promote();
-    if (handler != NULL) {
+    if (handler != nullptr) {
         handler->handleMessage(message);
     }
 }
@@ -87,7 +87,7 @@
 
 void Looper::threadDestructor(void *st) {
     Looper* const self = static_cast<Looper*>(st);
-    if (self != NULL) {
+    if (self != nullptr) {
         self->decStrong((void*)threadDestructor);
     }
 }
@@ -95,13 +95,13 @@
 void Looper::setForThread(const sp<Looper>& looper) {
     sp<Looper> old = getForThread(); // also has side-effect of initializing TLS
 
-    if (looper != NULL) {
+    if (looper != nullptr) {
         looper->incStrong((void*)threadDestructor);
     }
 
     pthread_setspecific(gTLSKey, looper.get());
 
-    if (old != NULL) {
+    if (old != nullptr) {
         old->decStrong((void*)threadDestructor);
     }
 }
@@ -116,7 +116,7 @@
 sp<Looper> Looper::prepare(int opts) {
     bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS;
     sp<Looper> looper = Looper::getForThread();
-    if (looper == NULL) {
+    if (looper == nullptr) {
         looper = new Looper(allowNonCallbacks);
         Looper::setForThread(looper);
     }
@@ -190,9 +190,9 @@
                         "fd=%d, events=0x%x, data=%p",
                         this, ident, fd, events, data);
 #endif
-                if (outFd != NULL) *outFd = fd;
-                if (outEvents != NULL) *outEvents = events;
-                if (outData != NULL) *outData = data;
+                if (outFd != nullptr) *outFd = fd;
+                if (outEvents != nullptr) *outEvents = events;
+                if (outData != nullptr) *outData = data;
                 return ident;
             }
         }
@@ -201,9 +201,9 @@
 #if DEBUG_POLL_AND_WAKE
             ALOGD("%p ~ pollOnce - returning result %d", this, result);
 #endif
-            if (outFd != NULL) *outFd = 0;
-            if (outEvents != NULL) *outEvents = 0;
-            if (outData != NULL) *outData = NULL;
+            if (outFd != nullptr) *outFd = 0;
+            if (outEvents != nullptr) *outEvents = 0;
+            if (outData != nullptr) *outData = nullptr;
             return result;
         }
 
@@ -427,7 +427,7 @@
 }
 
 int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
-    return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
+    return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : nullptr, data);
 }
 
 int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
@@ -542,7 +542,7 @@
         // updating the epoll set so that we avoid accidentally leaking callbacks.
         mRequests.removeItemsAt(requestIndex);
 
-        int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL);
+        int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, nullptr);
         if (epollResult < 0) {
             if (seq != -1 && (errno == EBADF || errno == ENOENT)) {
                 // Tolerate EBADF or ENOENT when the sequence number is known because it
diff --git a/libutils/NativeHandle.cpp b/libutils/NativeHandle.cpp
index 97d06b8..d437a9f 100644
--- a/libutils/NativeHandle.cpp
+++ b/libutils/NativeHandle.cpp
@@ -20,7 +20,7 @@
 namespace android {
 
 sp<NativeHandle> NativeHandle::create(native_handle_t* handle, bool ownsHandle) {
-    return handle ? new NativeHandle(handle, ownsHandle) : NULL;
+    return handle ? new NativeHandle(handle, ownsHandle) : nullptr;
 }
 
 NativeHandle::NativeHandle(native_handle_t* handle, bool ownsHandle)
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index cbf042e..c9ae210 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -73,7 +73,7 @@
 }
 
 void LogPrinter::printLine(const char* string) {
-    if (string == NULL) {
+    if (string == nullptr) {
         ALOGW("%s: NULL string passed in", __FUNCTION__);
         return;
     }
@@ -107,7 +107,7 @@
 }
 
 void FdPrinter::printLine(const char* string) {
-    if (string == NULL) {
+    if (string == nullptr) {
         ALOGW("%s: NULL string passed in", __FUNCTION__);
         return;
     } else if (mFd < 0) {
@@ -127,16 +127,16 @@
         mTarget(target),
         mPrefix(prefix ?: "") {
 
-    if (target == NULL) {
+    if (target == nullptr) {
         ALOGW("%s: Target string was NULL", __FUNCTION__);
     }
 }
 
 void String8Printer::printLine(const char* string) {
-    if (string == NULL) {
+    if (string == nullptr) {
         ALOGW("%s: NULL string passed in", __FUNCTION__);
         return;
-    } else if (mTarget == NULL) {
+    } else if (mTarget == nullptr) {
         ALOGW("%s: Target string was NULL", __FUNCTION__);
         return;
     }
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index b8fb6dc..f054de9 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -42,14 +42,14 @@
 static const char* PATH_SELF_TASK = "/proc/self/task";
 
 static void dumpProcessHeader(Printer& printer, pid_t pid, const char* timeStr) {
-    if (timeStr == NULL) {
+    if (timeStr == nullptr) {
         ALOGW("%s: timeStr was NULL", __FUNCTION__);
         return;
     }
 
     char path[PATH_MAX];
     char procNameBuf[MAX_PROC_PATH];
-    char* procName = NULL;
+    char* procName = nullptr;
     FILE* fp;
 
     snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
@@ -76,7 +76,7 @@
 
 static String8 getThreadName(pid_t tid) {
     char path[PATH_MAX];
-    char* procName = NULL;
+    char* procName = nullptr;
     char procNameBuf[MAX_PROC_PATH];
     FILE* fp;
 
@@ -88,7 +88,7 @@
         ALOGE("%s: Failed to open %s", __FUNCTION__, path);
     }
 
-    if (procName == NULL) {
+    if (procName == nullptr) {
         // Reading /proc/self/task/%d/comm failed due to a race
         return String8::format("[err-unknown-tid-%d]", tid);
     }
@@ -128,7 +128,7 @@
 
 void ProcessCallStack::update() {
     std::unique_ptr<DIR, decltype(&closedir)> dp(opendir(PATH_SELF_TASK), closedir);
-    if (dp == NULL) {
+    if (dp == nullptr) {
         ALOGE("%s: Failed to update the process's call stacks: %s",
               __FUNCTION__, strerror(errno));
         return;
@@ -140,7 +140,7 @@
 
     // Get current time.
     {
-        time_t t = time(NULL);
+        time_t t = time(nullptr);
         struct tm tm;
         localtime_r(&t, &tm);
 
@@ -152,7 +152,7 @@
      * - Read every file in directory => get every tid
      */
     dirent* ep;
-    while ((ep = readdir(dp.get())) != NULL) {
+    while ((ep = readdir(dp.get())) != nullptr) {
         pid_t tid = -1;
         sscanf(ep->d_name, "%d", &tid);
 
diff --git a/libutils/PropertyMap.cpp b/libutils/PropertyMap.cpp
index 4bcdd0f..b8c065d 100644
--- a/libutils/PropertyMap.cpp
+++ b/libutils/PropertyMap.cpp
@@ -112,7 +112,7 @@
 }
 
 status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
-    *outMap = NULL;
+    *outMap = nullptr;
 
     Tokenizer* tokenizer;
     status_t status = Tokenizer::open(filename, &tokenizer);
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 8bccb0f..9074850 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -712,7 +712,7 @@
         delete mRefs;
     }
     // For debugging purposes, clear mRefs.  Ineffective against outstanding wp's.
-    const_cast<weakref_impl*&>(mRefs) = NULL;
+    const_cast<weakref_impl*&>(mRefs) = nullptr;
 }
 
 void RefBase::extendObjectLifetime(int32_t mode)
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index bad98b2..7910c6e 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -75,7 +75,7 @@
                             "Invalid buffer size %zu", newSize);
 
         buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
-        if (buf != NULL) {
+        if (buf != nullptr) {
             buf->mSize = newSize;
             return buf;
         }
@@ -94,7 +94,7 @@
     if (onlyOwner()) {
         return const_cast<SharedBuffer*>(this);
     }
-    return 0;
+    return nullptr;
 }
 
 SharedBuffer* SharedBuffer::reset(size_t new_size) const
diff --git a/libutils/SharedBuffer.h b/libutils/SharedBuffer.h
index 81cadff..fdf13a9 100644
--- a/libutils/SharedBuffer.h
+++ b/libutils/SharedBuffer.h
@@ -124,11 +124,11 @@
 }
 
 SharedBuffer* SharedBuffer::bufferFromData(void* data) {
-    return data ? static_cast<SharedBuffer *>(data)-1 : 0;
+    return data ? static_cast<SharedBuffer *>(data)-1 : nullptr;
 }
     
 const SharedBuffer* SharedBuffer::bufferFromData(const void* data) {
-    return data ? static_cast<const SharedBuffer *>(data)-1 : 0;
+    return data ? static_cast<const SharedBuffer *>(data)-1 : nullptr;
 }
 
 size_t SharedBuffer::sizeFromData(const void* data) {
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 84d53dd..f820b8b 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -74,7 +74,7 @@
 }
 
 String16::String16(StaticLinkage)
-    : mString(0)
+    : mString(nullptr)
 {
     // this constructor is used when we can't rely on the static-initializers
     // having run. In this case we always allocate an empty string. It's less
@@ -336,7 +336,7 @@
 {
     const size_t N = size();
     const char16_t* str = string();
-    char16_t* edit = NULL;
+    char16_t* edit = nullptr;
     for (size_t i=0; i<N; i++) {
         const char16_t v = str[i];
         if (v >= 'A' && v <= 'Z') {
@@ -358,7 +358,7 @@
 {
     const size_t N = size();
     const char16_t* str = string();
-    char16_t* edit = NULL;
+    char16_t* edit = nullptr;
     for (size_t i=0; i<N; i++) {
         if (str[i] == replaceThis) {
             if (!edit) {
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 580e870..8d318f7 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -58,7 +58,7 @@
 {
     if (len > 0) {
         if (len == SIZE_MAX) {
-            return NULL;
+            return nullptr;
         }
         SharedBuffer* buf = SharedBuffer::alloc(len+1);
         ALOG_ASSERT(buf, "Unable to allocate shared buffer");
@@ -68,7 +68,7 @@
             str[len] = 0;
             return str;
         }
-        return NULL;
+        return nullptr;
     }
 
     return getEmptyString();
@@ -126,7 +126,7 @@
 }
 
 String8::String8(StaticLinkage)
-    : mString(0)
+    : mString(nullptr)
 {
     // this constructor is used when we can't rely on the static-initializers
     // having run. In this case we always allocate an empty string. It's less
@@ -147,7 +147,7 @@
 String8::String8(const char* o)
     : mString(allocFromUTF8(o, strlen(o)))
 {
-    if (mString == NULL) {
+    if (mString == nullptr) {
         mString = getEmptyString();
     }
 }
@@ -155,7 +155,7 @@
 String8::String8(const char* o, size_t len)
     : mString(allocFromUTF8(o, len))
 {
-    if (mString == NULL) {
+    if (mString == nullptr) {
         mString = getEmptyString();
     }
 }
@@ -319,7 +319,7 @@
      * second vsnprintf access undefined args.
      */
     va_copy(tmp_args, args);
-    n = vsnprintf(NULL, 0, fmt, tmp_args);
+    n = vsnprintf(nullptr, 0, fmt, tmp_args);
     va_end(tmp_args);
 
     if (n != 0) {
@@ -360,7 +360,7 @@
         mString = str;
         return str;
     }
-    return NULL;
+    return nullptr;
 }
 
 void String8::unlockBuffer()
@@ -512,7 +512,7 @@
     const char*const buf = mString;
 
     cp = strrchr(buf, OS_PATH_SEPARATOR);
-    if (cp == NULL)
+    if (cp == nullptr)
         return String8(*this);
     else
         return String8(cp+1);
@@ -524,7 +524,7 @@
     const char*const str = mString;
 
     cp = strrchr(str, OS_PATH_SEPARATOR);
-    if (cp == NULL)
+    if (cp == nullptr)
         return String8("");
     else
         return String8(str, cp - str);
@@ -543,7 +543,7 @@
         cp = strchr(buf, OS_PATH_SEPARATOR);
     }
 
-    if (cp == NULL) {
+    if (cp == nullptr) {
         String8 res = buf != str ? String8(buf) : *this;
         if (outRemains) *outRemains = String8("");
         return res;
@@ -567,15 +567,15 @@
 
     // only look at the filename
     lastSlash = strrchr(str, OS_PATH_SEPARATOR);
-    if (lastSlash == NULL)
+    if (lastSlash == nullptr)
         lastSlash = str;
     else
         lastSlash++;
 
     // find the last dot
     lastDot = strrchr(lastSlash, '.');
-    if (lastDot == NULL)
-        return NULL;
+    if (lastDot == nullptr)
+        return nullptr;
 
     // looks good, ship it
     return const_cast<char*>(lastDot);
@@ -586,7 +586,7 @@
     char* ext;
 
     ext = find_extension();
-    if (ext != NULL)
+    if (ext != nullptr)
         return String8(ext);
     else
         return String8("");
@@ -598,7 +598,7 @@
     const char* const str = mString;
 
     ext = find_extension();
-    if (ext == NULL)
+    if (ext == nullptr)
         return String8(*this);
     else
         return String8(str, ext - str);
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 7d7f0e2..43ec6c1 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -163,7 +163,7 @@
     // Note that *threadID is directly available to the parent only, as it is
     // assigned after the child starts.  Use memory barrier / lock if the child
     // or other threads also need access.
-    if (threadId != NULL) {
+    if (threadId != nullptr) {
         *threadId = (android_thread_id_t)thread; // XXX: this is not portable
     }
     return 1;
@@ -768,7 +768,7 @@
         strong.clear();
         // And immediately, re-acquire a strong reference for the next loop
         strong = weak.promote();
-    } while(strong != 0);
+    } while(strong != nullptr);
 
     return 0;
 }
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index b2df9a5..c3641ef 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -45,7 +45,7 @@
     // is windows.
     struct timeval t;
     t.tv_sec = t.tv_usec = 0;
-    gettimeofday(&t, NULL);
+    gettimeofday(&t, nullptr);
     return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL;
 }
 #endif
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index b68a2cf..f73d699 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -28,7 +28,7 @@
 namespace android {
 
 static inline bool isDelimiter(char ch, const char* delimiters) {
-    return strchr(delimiters, ch) != NULL;
+    return strchr(delimiters, ch) != nullptr;
 }
 
 Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,
@@ -46,7 +46,7 @@
 }
 
 status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
-    *outTokenizer = NULL;
+    *outTokenizer = nullptr;
 
     int result = NO_ERROR;
     int fd = ::open(filename.string(), O_RDONLY);
@@ -64,12 +64,12 @@
             FileMap* fileMap = new FileMap();
             bool ownBuffer = false;
             char* buffer;
-            if (fileMap->create(NULL, fd, 0, length, true)) {
+            if (fileMap->create(nullptr, fd, 0, length, true)) {
                 fileMap->advise(FileMap::SEQUENTIAL);
                 buffer = static_cast<char*>(fileMap->getDataPtr());
             } else {
                 delete fileMap;
-                fileMap = NULL;
+                fileMap = nullptr;
 
                 // Fall back to reading into a buffer since we can't mmap files in sysfs.
                 // The length we obtained from stat is wrong too (it will always be 4096)
@@ -81,7 +81,7 @@
                     result = -errno;
                     ALOGE("Error reading file '%s': %s", filename.string(), strerror(errno));
                     delete[] buffer;
-                    buffer = NULL;
+                    buffer = nullptr;
                 } else {
                     length = size_t(nrd);
                 }
@@ -98,7 +98,7 @@
 
 status_t Tokenizer::fromContents(const String8& filename,
         const char* contents, Tokenizer** outTokenizer) {
-    *outTokenizer = new Tokenizer(filename, NULL,
+    *outTokenizer = new Tokenizer(filename, nullptr,
             const_cast<char*>(contents), false, strlen(contents));
     return OK;
 }
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index 1086831..e00fb81 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -159,7 +159,7 @@
         return -1;
     }
     size_t dummy_index;
-    if (next_index == NULL) {
+    if (next_index == nullptr) {
         next_index = &dummy_index;
     }
     size_t num_read;
@@ -173,7 +173,7 @@
 
 ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len)
 {
-    if (src == NULL || src_len == 0) {
+    if (src == nullptr || src_len == 0) {
         return -1;
     }
 
@@ -195,7 +195,7 @@
 
 void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len)
 {
-    if (src == NULL || src_len == 0 || dst == NULL) {
+    if (src == nullptr || src_len == 0 || dst == nullptr) {
         return;
     }
 
@@ -363,7 +363,7 @@
 
 void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len)
 {
-    if (src == NULL || src_len == 0 || dst == NULL) {
+    if (src == nullptr || src_len == 0 || dst == nullptr) {
         return;
     }
 
@@ -440,7 +440,7 @@
 
 ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
 {
-    if (src == NULL || src_len == 0) {
+    if (src == nullptr || src_len == 0) {
         return -1;
     }
 
@@ -490,7 +490,7 @@
 
 size_t utf8_to_utf32_length(const char *src, size_t src_len)
 {
-    if (src == NULL || src_len == 0) {
+    if (src == nullptr || src_len == 0) {
         return 0;
     }
     size_t ret = 0;
@@ -515,7 +515,7 @@
 
 void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst)
 {
-    if (src == NULL || src_len == 0 || dst == NULL) {
+    if (src == nullptr || src_len == 0 || dst == nullptr) {
         return;
     }
 
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index ef3277f..00a904d 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -44,7 +44,7 @@
 // ----------------------------------------------------------------------------
 
 VectorImpl::VectorImpl(size_t itemSize, uint32_t flags)
-    : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize)
+    : mStorage(nullptr), mCount(0), mFlags(flags), mItemSize(itemSize)
 {
 }
 
@@ -77,7 +77,7 @@
             mCount = rhs.mCount;
             SharedBuffer::bufferFromData(mStorage)->acquire();
         } else {
-            mStorage = 0;
+            mStorage = nullptr;
             mCount = 0;
         }
     }
@@ -89,14 +89,14 @@
     if (mStorage) {
         const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage);
         SharedBuffer* editable = sb->attemptEdit();
-        if (editable == 0) {
+        if (editable == nullptr) {
             // If we're here, we're not the only owner of the buffer.
             // We must make a copy of it.
             editable = SharedBuffer::alloc(sb->size());
             // Fail instead of returning a pointer to storage that's not
             // editable. Otherwise we'd be editing the contents of a buffer
             // for which we're not the only owner, which is undefined behaviour.
-            LOG_ALWAYS_FATAL_IF(editable == NULL);
+            LOG_ALWAYS_FATAL_IF(editable == nullptr);
             _do_copy(editable->data(), mStorage, mCount);
             release_storage();
             mStorage = editable->data();
@@ -141,7 +141,7 @@
 
 ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
 {
-    return insertAt(0, index, numItems);
+    return insertAt(nullptr, index, numItems);
 }
 
 ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems)
@@ -177,7 +177,7 @@
     const ssize_t count = size();
     if (count > 1) {
         void* array = const_cast<void*>(arrayImpl());
-        void* temp = 0;
+        void* temp = nullptr;
         ssize_t i = 1;
         while (i < count) {
             void* item = reinterpret_cast<char*>(array) + mItemSize*(i);
@@ -205,7 +205,7 @@
                     _do_copy(next, curr, 1);
                     next = curr;
                     --j;
-                    curr = NULL;
+                    curr = nullptr;
                     if (j >= 0) {
                         curr = reinterpret_cast<char*>(array) + mItemSize*(j);
                     }
@@ -233,7 +233,7 @@
 
 void VectorImpl::push()
 {
-    push(0);
+    push(nullptr);
 }
 
 void VectorImpl::push(const void* item)
@@ -243,7 +243,7 @@
 
 ssize_t VectorImpl::add()
 {
-    return add(0);
+    return add(nullptr);
 }
 
 ssize_t VectorImpl::add(const void* item)
@@ -253,7 +253,7 @@
 
 ssize_t VectorImpl::replaceAt(size_t index)
 {
-    return replaceAt(0, index);
+    return replaceAt(nullptr, index);
 }
 
 ssize_t VectorImpl::replaceAt(const void* prototype, size_t index)
@@ -267,10 +267,10 @@
 
     void* item = editItemLocation(index);
     if (item != prototype) {
-        if (item == 0)
+        if (item == nullptr)
             return NO_MEMORY;
         _do_destroy(item, 1);
-        if (prototype == 0) {
+        if (prototype == nullptr) {
             _do_construct(item, 1);
         } else {
             _do_copy(item, prototype, 1);
@@ -294,7 +294,7 @@
 void VectorImpl::finish_vector()
 {
     release_storage();
-    mStorage = 0;
+    mStorage = nullptr;
     mCount = 0;
 }
 
@@ -315,7 +315,7 @@
             return reinterpret_cast<char*>(buffer) + index*mItemSize;
         }
     }
-    return 0;
+    return nullptr;
 }
 
 const void* VectorImpl::itemLocation(size_t index) const
@@ -330,7 +330,7 @@
             return reinterpret_cast<const char*>(buffer) + index*mItemSize;
         }
     }
-    return 0;
+    return nullptr;
 }
 
 ssize_t VectorImpl::setCapacity(size_t new_capacity)
@@ -418,7 +418,7 @@
             if (sb) {
                 mStorage = sb->data();
             } else {
-                return NULL;
+                return nullptr;
             }
         } else {
             SharedBuffer* sb = SharedBuffer::alloc(new_alloc_size);
@@ -435,7 +435,7 @@
                 release_storage();
                 mStorage = const_cast<void*>(array);
             } else {
-                return NULL;
+                return nullptr;
             }
         }
     } else {
diff --git a/libutils/include/utils/AndroidThreads.h b/libutils/include/utils/AndroidThreads.h
index dab888d..a8d7851 100644
--- a/libutils/include/utils/AndroidThreads.h
+++ b/libutils/include/utils/AndroidThreads.h
@@ -106,7 +106,7 @@
                             const char* threadName = "android:unnamed_thread",
                             int32_t threadPriority = PRIORITY_DEFAULT,
                             size_t threadStackSize = 0,
-                            thread_id_t *threadId = 0)
+                            thread_id_t *threadId = nullptr)
 {
     return androidCreateThreadEtc(entryFunction, userData, threadName,
         threadPriority, threadStackSize, threadId) ? true : false;
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 9622142..0c1b875 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -49,13 +49,13 @@
     // Dump a stack trace to the log using the supplied logtag.
     void log(const char* logtag,
              android_LogPriority priority = ANDROID_LOG_DEBUG,
-             const char* prefix = 0) const;
+             const char* prefix = nullptr) const;
 
     // Dump a stack trace to the specified file descriptor.
-    void dump(int fd, int indent = 0, const char* prefix = 0) const;
+    void dump(int fd, int indent = 0, const char* prefix = nullptr) const;
 
     // Return a string (possibly very long) containing the complete stack trace.
-    String8 toString(const char* prefix = 0) const;
+    String8 toString(const char* prefix = nullptr) const;
 
     // Dump a serialized representation of the stack trace to the specified printer.
     void print(Printer& printer) const;
diff --git a/libutils/include/utils/Condition.h b/libutils/include/utils/Condition.h
index c8da67c..540ed82 100644
--- a/libutils/include/utils/Condition.h
+++ b/libutils/include/utils/Condition.h
@@ -124,7 +124,7 @@
 #else // __APPLE__
     // Apple doesn't support POSIX clocks.
     struct timeval t;
-    gettimeofday(&t, NULL);
+    gettimeofday(&t, nullptr);
     ts.tv_sec = t.tv_sec;
     ts.tv_nsec = t.tv_usec*1000;
 #endif
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index a62e67f..4509d75 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -262,7 +262,7 @@
      */
     int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
     inline int pollOnce(int timeoutMillis) {
-        return pollOnce(timeoutMillis, NULL, NULL, NULL);
+        return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);
     }
 
     /**
@@ -272,7 +272,7 @@
      */
     int pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData);
     inline int pollAll(int timeoutMillis) {
-        return pollAll(timeoutMillis, NULL, NULL, NULL);
+        return pollAll(timeoutMillis, nullptr, nullptr, nullptr);
     }
 
     /**
diff --git a/libutils/include/utils/LruCache.h b/libutils/include/utils/LruCache.h
index 89dccd6..36775d0 100644
--- a/libutils/include/utils/LruCache.h
+++ b/libutils/include/utils/LruCache.h
@@ -71,7 +71,7 @@
         Entry* parent;
         Entry* child;
 
-        Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(NULL), child(NULL) {
+        Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(nullptr), child(nullptr) {
         }
         const TKey& getKey() const final { return key; }
     };
@@ -162,9 +162,9 @@
 template <typename TKey, typename TValue>
 LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
     : mSet(new LruCacheSet())
-    , mListener(NULL)
-    , mOldest(NULL)
-    , mYoungest(NULL)
+    , mListener(nullptr)
+    , mOldest(nullptr)
+    , mYoungest(nullptr)
     , mMaxCapacity(maxCapacity)
     , mNullValue(0) {
     mSet->max_load_factor(1.0);
@@ -236,7 +236,7 @@
 
 template <typename TKey, typename TValue>
 bool LruCache<TKey, TValue>::removeOldest() {
-    if (mOldest != NULL) {
+    if (mOldest != nullptr) {
         return remove(mOldest->key);
         // TODO: should probably abort if false
     }
@@ -254,12 +254,12 @@
 template <typename TKey, typename TValue>
 void LruCache<TKey, TValue>::clear() {
     if (mListener) {
-        for (Entry* p = mOldest; p != NULL; p = p->child) {
+        for (Entry* p = mOldest; p != nullptr; p = p->child) {
             (*mListener)(p->key, p->value);
         }
     }
-    mYoungest = NULL;
-    mOldest = NULL;
+    mYoungest = nullptr;
+    mOldest = nullptr;
     for (auto entry : *mSet.get()) {
         delete entry;
     }
@@ -268,7 +268,7 @@
 
 template <typename TKey, typename TValue>
 void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
-    if (mYoungest == NULL) {
+    if (mYoungest == nullptr) {
         mYoungest = mOldest = &entry;
     } else {
         entry.parent = mYoungest;
@@ -279,19 +279,19 @@
 
 template <typename TKey, typename TValue>
 void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
-    if (entry.parent != NULL) {
+    if (entry.parent != nullptr) {
         entry.parent->child = entry.child;
     } else {
         mOldest = entry.child;
     }
-    if (entry.child != NULL) {
+    if (entry.child != nullptr) {
         entry.child->parent = entry.parent;
     } else {
         mYoungest = entry.parent;
     }
 
-    entry.parent = NULL;
-    entry.child = NULL;
+    entry.parent = nullptr;
+    entry.child = nullptr;
 }
 
 }
diff --git a/libutils/include/utils/Mutex.h b/libutils/include/utils/Mutex.h
index 1228df4..29c2e8c 100644
--- a/libutils/include/utils/Mutex.h
+++ b/libutils/include/utils/Mutex.h
@@ -100,7 +100,7 @@
 
     Mutex();
     explicit Mutex(const char* name);
-    explicit Mutex(int type, const char* name = NULL);
+    explicit Mutex(int type, const char* name = nullptr);
     ~Mutex();
 
     // lock or unlock the mutex
@@ -160,10 +160,10 @@
 #if !defined(_WIN32)
 
 inline Mutex::Mutex() {
-    pthread_mutex_init(&mMutex, NULL);
+    pthread_mutex_init(&mMutex, nullptr);
 }
 inline Mutex::Mutex(__attribute__((unused)) const char* name) {
-    pthread_mutex_init(&mMutex, NULL);
+    pthread_mutex_init(&mMutex, nullptr);
 }
 inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) {
     if (type == SHARED) {
@@ -173,7 +173,7 @@
         pthread_mutex_init(&mMutex, &attr);
         pthread_mutexattr_destroy(&attr);
     } else {
-        pthread_mutex_init(&mMutex, NULL);
+        pthread_mutex_init(&mMutex, nullptr);
     }
 }
 inline Mutex::~Mutex() {
diff --git a/libutils/include/utils/Printer.h b/libutils/include/utils/Printer.h
index a6f6928..7465927 100644
--- a/libutils/include/utils/Printer.h
+++ b/libutils/include/utils/Printer.h
@@ -45,7 +45,7 @@
     // (Note that the default ALOG behavior is to ignore blank lines)
     LogPrinter(const char* logtag,
                android_LogPriority priority = ANDROID_LOG_DEBUG,
-               const char* prefix = 0,
+               const char* prefix = nullptr,
                bool ignoreBlankLines = false);
 
     // Print the specified line to logcat. No \n at the end is necessary.
@@ -66,7 +66,7 @@
     // Create a printer using the specified file descriptor.
     // - Each line will be prefixed with 'indent' number of blank spaces.
     // - In addition, each line will be prefixed with the 'prefix' string.
-    FdPrinter(int fd, unsigned int indent = 0, const char* prefix = 0);
+    FdPrinter(int fd, unsigned int indent = 0, const char* prefix = nullptr);
 
     // Print the specified line to the file descriptor. \n is appended automatically.
     virtual void printLine(const char* string);
@@ -90,7 +90,7 @@
     // Create a printer using the specified String8 as the target.
     // - In addition, each line will be prefixed with the 'prefix' string.
     // - target's memory lifetime must be a superset of this String8Printer.
-    String8Printer(String8* target, const char* prefix = 0);
+    String8Printer(String8* target, const char* prefix = nullptr);
 
     // Append the specified line to the String8. \n is appended automatically.
     virtual void printLine(const char* string);
diff --git a/libutils/include/utils/ProcessCallStack.h b/libutils/include/utils/ProcessCallStack.h
index b5f2edc..7e06086 100644
--- a/libutils/include/utils/ProcessCallStack.h
+++ b/libutils/include/utils/ProcessCallStack.h
@@ -43,13 +43,13 @@
 
     // Print all stack traces to the log using the supplied logtag.
     void log(const char* logtag, android_LogPriority priority = ANDROID_LOG_DEBUG,
-             const char* prefix = 0) const;
+             const char* prefix = nullptr) const;
 
     // Dump all stack traces to the specified file descriptor.
-    void dump(int fd, int indent = 0, const char* prefix = 0) const;
+    void dump(int fd, int indent = 0, const char* prefix = nullptr) const;
 
     // Return a string (possibly very long) containing all the stack traces.
-    String8 toString(const char* prefix = 0) const;
+    String8 toString(const char* prefix = nullptr) const;
 
     // Dump a serialized representation of all the stack traces to the specified printer.
     void print(Printer& printer) const;
diff --git a/libutils/include/utils/RWLock.h b/libutils/include/utils/RWLock.h
index 7d43e69..64e370e 100644
--- a/libutils/include/utils/RWLock.h
+++ b/libutils/include/utils/RWLock.h
@@ -48,7 +48,7 @@
 
                 RWLock();
     explicit    RWLock(const char* name);
-    explicit    RWLock(int type, const char* name = NULL);
+    explicit    RWLock(int type, const char* name = nullptr);
                 ~RWLock();
 
     status_t    readLock();
@@ -82,10 +82,10 @@
 };
 
 inline RWLock::RWLock() {
-    pthread_rwlock_init(&mRWLock, NULL);
+    pthread_rwlock_init(&mRWLock, nullptr);
 }
 inline RWLock::RWLock(__attribute__((unused)) const char* name) {
-    pthread_rwlock_init(&mRWLock, NULL);
+    pthread_rwlock_init(&mRWLock, nullptr);
 }
 inline RWLock::RWLock(int type, __attribute__((unused)) const char* name) {
     if (type == SHARED) {
@@ -95,7 +95,7 @@
         pthread_rwlock_init(&mRWLock, &attr);
         pthread_rwlockattr_destroy(&attr);
     } else {
-        pthread_rwlock_init(&mRWLock, NULL);
+        pthread_rwlock_init(&mRWLock, nullptr);
     }
 }
 inline RWLock::~RWLock() {
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index e817ee4..1780cf2 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -354,7 +354,7 @@
 public:
     typedef typename RefBase::weakref_type weakref_type;
 
-    inline wp() : m_ptr(0) { }
+    inline wp() : m_ptr(nullptr) { }
 
     wp(T* other);  // NOLINT(implicit)
     wp(const wp<T>& other);
@@ -505,7 +505,7 @@
 wp<T>& wp<T>::operator = (T* other)
 {
     weakref_type* newRefs =
-        other ? other->createWeak(this) : 0;
+        other ? other->createWeak(this) : nullptr;
     if (m_ptr) m_refs->decWeak(this);
     m_ptr = other;
     m_refs = newRefs;
@@ -528,7 +528,7 @@
 wp<T>& wp<T>::operator = (const sp<T>& other)
 {
     weakref_type* newRefs =
-        other != NULL ? other->createWeak(this) : 0;
+        other != nullptr ? other->createWeak(this) : nullptr;
     T* otherPtr(other.m_ptr);
     if (m_ptr) m_refs->decWeak(this);
     m_ptr = otherPtr;
@@ -563,7 +563,7 @@
 wp<T>& wp<T>::operator = (const sp<U>& other)
 {
     weakref_type* newRefs =
-        other != NULL ? other->createWeak(this) : 0;
+        other != nullptr ? other->createWeak(this) : 0;
     U* otherPtr(other.m_ptr);
     if (m_ptr) m_refs->decWeak(this);
     m_ptr = otherPtr;
diff --git a/libutils/include/utils/Singleton.h b/libutils/include/utils/Singleton.h
index 2dd5a47..44d8ad7 100644
--- a/libutils/include/utils/Singleton.h
+++ b/libutils/include/utils/Singleton.h
@@ -51,7 +51,7 @@
     static TYPE& getInstance() {
         Mutex::Autolock _l(sLock);
         TYPE* instance = sInstance;
-        if (instance == 0) {
+        if (instance == nullptr) {
             instance = new TYPE();
             sInstance = instance;
         }
@@ -60,7 +60,7 @@
 
     static bool hasInstance() {
         Mutex::Autolock _l(sLock);
-        return sInstance != 0;
+        return sInstance != nullptr;
     }
     
 protected:
@@ -90,7 +90,7 @@
 #define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE)                 \
     template<> ::android::Mutex  \
         (::android::Singleton< TYPE >::sLock)(::android::Mutex::PRIVATE);  \
-    template<> TYPE* ::android::Singleton< TYPE >::sInstance(0);  /* NOLINT */ \
+    template<> TYPE* ::android::Singleton< TYPE >::sInstance(nullptr);  /* NOLINT */ \
     template class ::android::Singleton< TYPE >;
 
 
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index 94ac32f..c8f584e 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -187,7 +187,7 @@
      * "/tmp" --> "tmp" (remain = "")
      * "bar.c" --> "bar.c" (remain = "")
      */
-    String8 walkPath(String8* outRemains = NULL) const;
+    String8 walkPath(String8* outRemains = nullptr) const;
 
     /*
      * Return the filename extension.  This is the last '.' and any number
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 9cd278f1..360fce5 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -52,7 +52,7 @@
 template<typename T>
 class sp {
 public:
-    inline sp() : m_ptr(0) { }
+    inline sp() : m_ptr(nullptr) { }
 
     sp(T* other);  // NOLINT(implicit)
     sp(const sp<T>& other);
@@ -230,7 +230,7 @@
 void sp<T>::clear() {
     if (m_ptr) {
         m_ptr->decStrong(this);
-        m_ptr = 0;
+        m_ptr = nullptr;
     }
 }
 
diff --git a/libutils/include/utils/VectorImpl.h b/libutils/include/utils/VectorImpl.h
index 55d5d98..41b9f33 100644
--- a/libutils/include/utils/VectorImpl.h
+++ b/libutils/include/utils/VectorImpl.h
@@ -157,7 +157,7 @@
     virtual int             do_compare(const void* lhs, const void* rhs) const = 0;
 
 private:
-            ssize_t         _indexOrderOf(const void* item, size_t* order = 0) const;
+            ssize_t         _indexOrderOf(const void* item, size_t* order = nullptr) const;
 
             // these are made private, because they can't be used on a SortedVector
             // (they don't have an implementation either)
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index da28dfa..f77e189 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -23,7 +23,7 @@
 #include <utils/Log.h>
 #include <utils/Vector.h>
 
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
 #include <dlfcn.h>
 #include <vndksupport/linker.h>
 #endif
@@ -41,13 +41,13 @@
 
 #if !defined(_WIN32)
 static pthread_mutex_t gSyspropMutex = PTHREAD_MUTEX_INITIALIZER;
-static Vector<sysprop_change_callback_info>* gSyspropList = NULL;
+static Vector<sysprop_change_callback_info>* gSyspropList = nullptr;
 #endif
 
 #if !defined(_WIN32)
 void add_sysprop_change_callback(sysprop_change_callback cb, int priority) {
     pthread_mutex_lock(&gSyspropMutex);
-    if (gSyspropList == NULL) {
+    if (gSyspropList == nullptr) {
         gSyspropList = new Vector<sysprop_change_callback_info>();
     }
     sysprop_change_callback_info info;
@@ -70,7 +70,7 @@
 void add_sysprop_change_callback(sysprop_change_callback, int) {}
 #endif
 
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
 void (*get_report_sysprop_change_func())() {
     void (*func)() = nullptr;
     void* handle = android_load_sphal_library("libutils.so", RTLD_NOW);
@@ -85,7 +85,7 @@
 void report_sysprop_change() {
     do_report_sysprop_change();
 
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
     // libutils.so is double loaded; from the default namespace and from the
     // 'sphal' namespace. Redirect the sysprop change event to the other instance
     // of libutils.so loaded in the 'sphal' namespace so that listeners attached
@@ -103,7 +103,7 @@
 #if !defined(_WIN32)
     pthread_mutex_lock(&gSyspropMutex);
     Vector<sysprop_change_callback_info> listeners;
-    if (gSyspropList != NULL) {
+    if (gSyspropList != nullptr) {
         listeners = *gSyspropList;
     }
     pthread_mutex_unlock(&gSyspropMutex);
diff --git a/libutils/tests/Looper_test.cpp b/libutils/tests/Looper_test.cpp
index 8ebcfaf..2282ced 100644
--- a/libutils/tests/Looper_test.cpp
+++ b/libutils/tests/Looper_test.cpp
@@ -339,7 +339,7 @@
     Pipe pipe;
 
     pipe.writeSignal();
-    mLooper->addFd(pipe.receiveFd, expectedIdent, Looper::EVENT_INPUT, NULL, expectedData);
+    mLooper->addFd(pipe.receiveFd, expectedIdent, Looper::EVENT_INPUT, nullptr, expectedData);
 
     StopWatch stopWatch("pollOnce");
     int fd;
@@ -364,7 +364,7 @@
 
 TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) {
     Pipe pipe;
-    int result = mLooper->addFd(pipe.receiveFd, 0, Looper::EVENT_INPUT, NULL, NULL);
+    int result = mLooper->addFd(pipe.receiveFd, 0, Looper::EVENT_INPUT, nullptr, nullptr);
 
     EXPECT_EQ(1, result)
             << "addFd should return 1 because FD was added";
@@ -372,7 +372,7 @@
 
 TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) {
     Pipe pipe;
-    int result = mLooper->addFd(pipe.receiveFd, -1, Looper::EVENT_INPUT, NULL, NULL);
+    int result = mLooper->addFd(pipe.receiveFd, -1, Looper::EVENT_INPUT, nullptr, nullptr);
 
     EXPECT_EQ(-1, result)
             << "addFd should return -1 because arguments were invalid";
@@ -381,7 +381,7 @@
 TEST_F(LooperTest, AddFd_WhenNoCallbackAndAllowNonCallbacksIsFalse_ReturnsError) {
     Pipe pipe;
     sp<Looper> looper = new Looper(false /*allowNonCallbacks*/);
-    int result = looper->addFd(pipe.receiveFd, 0, 0, NULL, NULL);
+    int result = looper->addFd(pipe.receiveFd, 0, 0, nullptr, nullptr);
 
     EXPECT_EQ(-1, result)
             << "addFd should return -1 because arguments were invalid";
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
index 4e885bb..c4d917b 100644
--- a/libutils/tests/LruCache_test.cpp
+++ b/libutils/tests/LruCache_test.cpp
@@ -110,7 +110,7 @@
 
 class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {
 public:
-    EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { }
+    EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(nullptr) { }
     ~EntryRemovedCallback() {}
     void operator()(SimpleKey& k, StringValue& v) {
         callbackCount += 1;
@@ -153,7 +153,7 @@
 TEST_F(LruCacheTest, Empty) {
     LruCache<SimpleKey, StringValue> cache(100);
 
-    EXPECT_EQ(NULL, cache.get(0));
+    EXPECT_EQ(nullptr, cache.get(0));
     EXPECT_EQ(0u, cache.size());
 }
 
@@ -175,7 +175,7 @@
     cache.put(1, "one");
     cache.put(2, "two");
     cache.put(3, "three");
-    EXPECT_EQ(NULL, cache.get(1));
+    EXPECT_EQ(nullptr, cache.get(1));
     EXPECT_STREQ("two", cache.get(2));
     EXPECT_STREQ("three", cache.get(3));
     EXPECT_EQ(2u, cache.size());
@@ -188,7 +188,7 @@
     cache.put(2, "two");
     cache.put(3, "three");
     cache.removeOldest();
-    EXPECT_EQ(NULL, cache.get(1));
+    EXPECT_EQ(nullptr, cache.get(1));
     EXPECT_STREQ("two", cache.get(2));
     EXPECT_STREQ("three", cache.get(3));
     EXPECT_EQ(2u, cache.size());
@@ -203,7 +203,7 @@
     EXPECT_STREQ("one", cache.get(1));
     cache.removeOldest();
     EXPECT_STREQ("one", cache.get(1));
-    EXPECT_EQ(NULL, cache.get(2));
+    EXPECT_EQ(nullptr, cache.get(2));
     EXPECT_STREQ("three", cache.get(3));
     EXPECT_EQ(2u, cache.size());
 }
@@ -230,7 +230,7 @@
         int index = random() % kNumKeys;
         uint32_t key = hash_int(index);
         const char *val = cache.get(key);
-        if (val != NULL) {
+        if (val != nullptr) {
             EXPECT_EQ(strings[index], val);
             hitCount++;
         } else {
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
index e074a92..5336c40 100644
--- a/libutils/tests/Vector_test.cpp
+++ b/libutils/tests/Vector_test.cpp
@@ -98,7 +98,7 @@
 
   // Checks that the size calculation (not the capacity calculation) doesn't
   // overflow : the size here will be (1 + SIZE_MAX).
-  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "new_size overflow");
+  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, SIZE_MAX), "new_size overflow");
 }
 
 TEST_F(VectorTest, _grow_OverflowCapacityDoubling) {
@@ -106,14 +106,14 @@
 
   // This should fail because the calculated capacity will overflow even though
   // the size of the vector doesn't.
-  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "new_capacity overflow");
+  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, (SIZE_MAX - 1)), "new_capacity overflow");
 }
 
 TEST_F(VectorTest, _grow_OverflowBufferAlloc) {
   Vector<int> vector;
   // This should fail because the capacity * sizeof(int) overflows, even
   // though the capacity itself doesn't.
-  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
+  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
 }
 
 TEST_F(VectorTest, editArray_Shared) {
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 6c06618..2606aa9 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -58,6 +58,7 @@
     name: "libziparchive",
     host_supported: true,
     vendor_available: true,
+    recovery_available: true,
     vndk: {
         enabled: true,
     },
diff --git a/logcat/.clang-format b/logcat/.clang-format
deleted file mode 100644
index 393c309..0000000
--- a/logcat/.clang-format
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: false
-
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-PenaltyExcessCharacter: 32
-
-Cpp11BracedListStyle: false
diff --git a/logcat/Android.bp b/logcat/Android.bp
index 01beb53..b0563a6 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2006-2017 The Android Open Source Project
+// Copyright (C) 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.
@@ -31,25 +31,13 @@
     logtags: ["event.logtags"],
 }
 
-cc_library {
-    name: "liblogcat",
-
-    defaults: ["logcat_defaults"],
-    srcs: [
-        "logcat.cpp",
-        "getopt_long.cpp",
-        "logcat_system.cpp",
-    ],
-    export_include_dirs: ["include"],
-}
-
 cc_binary {
     name: "logcat",
 
     defaults: ["logcat_defaults"],
-    shared_libs: ["liblogcat"],
     srcs: [
         "logcat_main.cpp",
+        "logcat.cpp",
     ],
 }
 
@@ -57,9 +45,9 @@
     name: "logcatd",
 
     defaults: ["logcat_defaults"],
-    shared_libs: ["liblogcat"],
     srcs: [
         "logcatd_main.cpp",
+        "logcat.cpp",
     ],
 }
 
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 750761f..da8d2d4 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -113,6 +113,9 @@
 # graphics timestamp
 # 60100 - 60199 reserved for surfaceflinger
 
+# audio
+# 61000 - 61199 reserved for audioserver
+
 # 0 for screen off, 1 for screen on, 2 for key-guard done
 70000 screen_toggled (screen_state|1|5)
 
diff --git a/logcat/getopt_long.cpp b/logcat/getopt_long.cpp
deleted file mode 100644
index da99906..0000000
--- a/logcat/getopt_long.cpp
+++ /dev/null
@@ -1,401 +0,0 @@
-/* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */
-/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $       */
-
-/*
- * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Sponsored in part by the Defense Advanced Research Projects
- * Agency (DARPA) and Air Force Research Laboratory, Air Force
- * Materiel Command, USAF, under agreement number F39502-99-1-0512.
- */
-/*-
- * Copyright (c) 2000 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Dieter Baron and Thomas Klausner.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-
-#include <log/getopt.h>
-
-#define PRINT_ERROR ((context->opterr) && (*options != ':'))
-
-#define FLAG_PERMUTE 0x01  // permute non-options to the end of argv
-#define FLAG_ALLARGS 0x02  // treat non-options as args to option "-1"
-
-// return values
-#define BADCH (int)'?'
-#define BADARG ((*options == ':') ? (int)':' : (int)'?')
-#define INORDER (int)1
-
-#define D_PREFIX 0
-#define DD_PREFIX 1
-#define W_PREFIX 2
-
-// Compute the greatest common divisor of a and b.
-static int gcd(int a, int b) {
-    int c = a % b;
-    while (c) {
-        a = b;
-        b = c;
-        c = a % b;
-    }
-    return b;
-}
-
-// Exchange the block from nonopt_start to nonopt_end with the block from
-// nonopt_end to opt_end (keeping the same order of arguments in each block).
-// Returns optind - (nonopt_end - nonopt_start) for convenience.
-static int permute_args(getopt_context* context, char* const* nargv) {
-    // compute lengths of blocks and number and size of cycles
-    int nnonopts = context->nonopt_end - context->nonopt_start;
-    int nopts = context->optind - context->nonopt_end;
-    int ncycle = gcd(nnonopts, nopts);
-    int cyclelen = (context->optind - context->nonopt_start) / ncycle;
-
-    for (int i = 0; i < ncycle; i++) {
-        int cstart = context->nonopt_end + i;
-        int pos = cstart;
-        for (int j = 0; j < cyclelen; j++) {
-            if (pos >= context->nonopt_end) {
-                pos -= nnonopts;
-            } else {
-                pos += nopts;
-            }
-            char* swap = nargv[pos];
-            const_cast<char**>(nargv)[pos] = nargv[cstart];
-            const_cast<char**>(nargv)[cstart] = swap;
-        }
-    }
-    return context->optind - (context->nonopt_end - context->nonopt_start);
-}
-
-// parse_long_options_r --
-//    Parse long options in argc/argv argument vector.
-// Returns -1 if short_too is set and the option does not match long_options.
-static int parse_long_options_r(char* const* nargv, const char* options,
-                                const struct option* long_options, int* idx,
-                                bool short_too, struct getopt_context* context) {
-    const char* current_argv = context->place;
-    const char* current_dash;
-    switch (context->dash_prefix) {
-        case D_PREFIX:
-            current_dash = "-";
-            break;
-        case DD_PREFIX:
-            current_dash = "--";
-            break;
-        case W_PREFIX:
-            current_dash = "-W ";
-            break;
-        default:
-            current_dash = "";
-            break;
-    }
-    context->optind++;
-
-    const char* has_equal;
-    size_t current_argv_len;
-    if (!!(has_equal = strchr(current_argv, '='))) {
-        // argument found (--option=arg)
-        current_argv_len = has_equal - current_argv;
-        has_equal++;
-    } else {
-        current_argv_len = strlen(current_argv);
-    }
-
-    int match = -1;
-    bool exact_match = false;
-    bool second_partial_match = false;
-    for (int i = 0; long_options[i].name; i++) {
-        // find matching long option
-        if (strncmp(current_argv, long_options[i].name, current_argv_len)) {
-            continue;
-        }
-
-        if (strlen(long_options[i].name) == current_argv_len) {
-            // exact match
-            match = i;
-            exact_match = true;
-            break;
-        }
-        // If this is a known short option, don't allow
-        // a partial match of a single character.
-        if (short_too && current_argv_len == 1) continue;
-
-        if (match == -1) {  // first partial match
-            match = i;
-        } else if (long_options[i].has_arg != long_options[match].has_arg ||
-                   long_options[i].flag != long_options[match].flag ||
-                   long_options[i].val != long_options[match].val) {
-            second_partial_match = true;
-        }
-    }
-    if (!exact_match && second_partial_match) {
-        // ambiguous abbreviation
-        if (PRINT_ERROR) {
-            fprintf(context->optstderr ?: stderr,
-                    "option `%s%.*s' is ambiguous", current_dash,
-                    (int)current_argv_len, current_argv);
-        }
-        context->optopt = 0;
-        return BADCH;
-    }
-    if (match != -1) {  // option found
-        if (long_options[match].has_arg == no_argument && has_equal) {
-            if (PRINT_ERROR) {
-                fprintf(context->optstderr ?: stderr,
-                        "option `%s%.*s' doesn't allow an argument",
-                        current_dash, (int)current_argv_len, current_argv);
-            }
-            // XXX: GNU sets optopt to val regardless of flag
-            context->optopt =
-                long_options[match].flag ? 0 : long_options[match].val;
-            return BADCH;
-        }
-        if (long_options[match].has_arg == required_argument ||
-            long_options[match].has_arg == optional_argument) {
-            if (has_equal) {
-                context->optarg = has_equal;
-            } else if (long_options[match].has_arg == required_argument) {
-                // optional argument doesn't use next nargv
-                context->optarg = nargv[context->optind++];
-            }
-        }
-        if ((long_options[match].has_arg == required_argument) &&
-            !context->optarg) {
-            // Missing argument; leading ':' indicates no error
-            // should be generated.
-            if (PRINT_ERROR) {
-                fprintf(context->optstderr ?: stderr,
-                        "option `%s%s' requires an argument", current_dash,
-                        current_argv);
-            }
-            // XXX: GNU sets optopt to val regardless of flag
-            context->optopt =
-                long_options[match].flag ? 0 : long_options[match].val;
-            context->optind--;
-            return BADARG;
-        }
-    } else {  // unknown option
-        if (short_too) {
-            context->optind--;
-            return -1;
-        }
-        if (PRINT_ERROR) {
-            fprintf(context->optstderr ?: stderr, "unrecognized option `%s%s'",
-                    current_dash, current_argv);
-        }
-        context->optopt = 0;
-        return BADCH;
-    }
-    if (idx) *idx = match;
-    if (long_options[match].flag) {
-        *long_options[match].flag = long_options[match].val;
-        return 0;
-    }
-    return long_options[match].val;
-}
-
-// getopt_long_r --
-//    Parse argc/argv argument vector.
-int getopt_long_r(int nargc, char* const* nargv, const char* options,
-                  const struct option* long_options, int* idx,
-                  struct getopt_context* context) {
-    if (!options) return -1;
-
-    // XXX Some GNU programs (like cvs) set optind to 0 instead of
-    // XXX using optreset.  Work around this braindamage.
-    if (!context->optind) context->optind = context->optreset = 1;
-
-    // Disable GNU extensions if options string begins with a '+'.
-    int flags = FLAG_PERMUTE;
-    if (*options == '-') {
-        flags |= FLAG_ALLARGS;
-    } else if (*options == '+') {
-        flags &= ~FLAG_PERMUTE;
-    }
-    if (*options == '+' || *options == '-') options++;
-
-    context->optarg = nullptr;
-    if (context->optreset) context->nonopt_start = context->nonopt_end = -1;
-start:
-    if (context->optreset || !*context->place) {  // update scanning pointer
-        context->optreset = 0;
-        if (context->optind >= nargc) {  // end of argument vector
-            context->place = EMSG;
-            if (context->nonopt_end != -1) {
-                // do permutation, if we have to
-                context->optind = permute_args(context, nargv);
-            } else if (context->nonopt_start != -1) {
-                // If we skipped non-options, set optind to the first of them.
-                context->optind = context->nonopt_start;
-            }
-            context->nonopt_start = context->nonopt_end = -1;
-            return -1;
-        }
-        if (*(context->place = nargv[context->optind]) != '-' ||
-            context->place[1] == '\0') {
-            context->place = EMSG;  // found non-option
-            if (flags & FLAG_ALLARGS) {
-                // GNU extension: return non-option as argument to option 1
-                context->optarg = nargv[context->optind++];
-                return INORDER;
-            }
-            if (!(flags & FLAG_PERMUTE)) {
-                // If no permutation wanted, stop parsing at first non-option.
-                return -1;
-            }
-            // do permutation
-            if (context->nonopt_start == -1) {
-                context->nonopt_start = context->optind;
-            } else if (context->nonopt_end != -1) {
-                context->nonopt_start = permute_args(context, nargv);
-                context->nonopt_end = -1;
-            }
-            context->optind++;
-            // process next argument
-            goto start;
-        }
-        if (context->nonopt_start != -1 && context->nonopt_end == -1) {
-            context->nonopt_end = context->optind;
-        }
-
-        // If we have "-" do nothing, if "--" we are done.
-        if (context->place[1] != '\0' && *++(context->place) == '-' &&
-            context->place[1] == '\0') {
-            context->optind++;
-            context->place = EMSG;
-            // We found an option (--), so if we skipped
-            // non-options, we have to permute.
-            if (context->nonopt_end != -1) {
-                context->optind = permute_args(context, nargv);
-            }
-            context->nonopt_start = context->nonopt_end = -1;
-            return -1;
-        }
-    }
-
-    int optchar;
-    // Check long options if:
-    //  1) we were passed some
-    //  2) the arg is not just "-"
-    //  3) either the arg starts with -- we are getopt_long_only()
-    if (long_options && context->place != nargv[context->optind] &&
-        (*context->place == '-')) {
-        bool short_too = false;
-        context->dash_prefix = D_PREFIX;
-        if (*context->place == '-') {
-            context->place++;  // --foo long option
-            context->dash_prefix = DD_PREFIX;
-        } else if (*context->place != ':' && strchr(options, *context->place)) {
-            short_too = true;  // could be short option too
-        }
-
-        optchar = parse_long_options_r(nargv, options, long_options, idx,
-                                       short_too, context);
-        if (optchar != -1) {
-            context->place = EMSG;
-            return optchar;
-        }
-    }
-
-    const char* oli;  // option letter list index
-    if ((optchar = (int)*(context->place)++) == (int)':' ||
-        (optchar == (int)'-' && *context->place != '\0') ||
-        !(oli = strchr(options, optchar))) {
-        // If the user specified "-" and  '-' isn't listed in
-        // options, return -1 (non-option) as per POSIX.
-        // Otherwise, it is an unknown option character (or ':').
-        if (optchar == (int)'-' && *context->place == '\0') return -1;
-        if (!*context->place) context->optind++;
-        if (PRINT_ERROR) {
-            fprintf(context->optstderr ?: stderr, "invalid option -- %c",
-                    optchar);
-        }
-        context->optopt = optchar;
-        return BADCH;
-    }
-
-    static const char recargchar[] = "option requires an argument -- %c";
-    if (long_options && optchar == 'W' && oli[1] == ';') {
-        // -W long-option
-        if (*context->place) {                      // no space
-            ;                                       // NOTHING
-        } else if (++(context->optind) >= nargc) {  // no arg
-            context->place = EMSG;
-            if (PRINT_ERROR) {
-                fprintf(context->optstderr ?: stderr, recargchar, optchar);
-            }
-            context->optopt = optchar;
-            return BADARG;
-        } else {  // white space
-            context->place = nargv[context->optind];
-        }
-        context->dash_prefix = W_PREFIX;
-        optchar = parse_long_options_r(nargv, options, long_options, idx, false,
-                                       context);
-        context->place = EMSG;
-        return optchar;
-    }
-    if (*++oli != ':') {  // doesn't take argument
-        if (!*context->place) context->optind++;
-    } else {  // takes (optional) argument
-        context->optarg = nullptr;
-        if (*context->place) {  // no white space
-            context->optarg = context->place;
-        } else if (oli[1] != ':') {              // arg not optional
-            if (++(context->optind) >= nargc) {  // no arg
-                context->place = EMSG;
-                if (PRINT_ERROR) {
-                    fprintf(context->optstderr ?: stderr, recargchar, optchar);
-                }
-                context->optopt = optchar;
-                return BADARG;
-            }
-            context->optarg = nargv[context->optind];
-        }
-        context->place = EMSG;
-        context->optind++;
-    }
-    // dump back option letter
-    return optchar;
-}
diff --git a/logcat/include/log/getopt.h b/logcat/include/log/getopt.h
deleted file mode 100644
index 0da2b10..0000000
--- a/logcat/include/log/getopt.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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 _LOG_GETOPT_H_
-#define _LOG_GETOPT_H_
-
-#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-
-#include <getopt.h>
-#include <sys/cdefs.h>
-
-struct getopt_context {
-    int opterr;
-    int optind;
-    int optopt;
-    int optreset;
-    const char* optarg;
-    FILE* optstderr; /* NULL defaults to stderr */
-    /* private */
-    const char* place;
-    int nonopt_start;
-    int nonopt_end;
-    int dash_prefix;
-    /* expansion space */
-    int __extra__;
-    void* __stuff__;
-};
-
-#define EMSG ""
-#define NO_PREFIX (-1)
-
-#define INIT_GETOPT_CONTEXT(context) \
-    context = { 1, 1, '?', 0, NULL, NULL, EMSG, -1, -1, NO_PREFIX, 0, NULL }
-
-__BEGIN_DECLS
-int getopt_long_r(int nargc, char* const* nargv, const char* options,
-                  const struct option* long_options, int* idx,
-                  struct getopt_context* context);
-
-__END_DECLS
-
-#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
-
-#endif /* !_LOG_GETOPT_H_ */
diff --git a/logcat/include/log/logcat.h b/logcat/include/log/logcat.h
deleted file mode 100644
index 009672c..0000000
--- a/logcat/include/log/logcat.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2005-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 _LIBS_LOGCAT_H /* header boilerplate */
-#define _LIBS_LOGCAT_H
-
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-
-/* For managing an in-process logcat function, rather than forking/execing
- *
- * It also serves as the basis for the logcat command.
- *
- * The following C API allows a logcat instance to be created, run
- * to completion, and then release all the associated resources.
- */
-
-/*
- * The opaque context
- */
-#ifndef __android_logcat_context_defined /* typedef boilerplate */
-#define __android_logcat_context_defined
-typedef struct android_logcat_context_internal* android_logcat_context;
-#endif
-
-/* Creates a context associated with this logcat instance
- *
- * Returns a pointer to the context, or a NULL on error.
- */
-android_logcat_context create_android_logcat();
-
-/* Collects and outputs the logcat data to output and error file descriptors
- *
- * Will block, performed in-thread and in-process
- *
- * The output file descriptor variable, if greater than or equal to 0, is
- * where the output (ie: stdout) will be sent. The file descriptor is closed
- * on android_logcat_destroy which terminates the instance, or when an -f flag
- * (output redirect to a file) is present in the command.  The error file
- * descriptor variable, if greater than or equal to 0, is where the error
- * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
- * The error file descriptor can be set to equal to the output file descriptor,
- * which will mix output and error stream content, and will defer closure of
- * the file descriptor on -f flag redirection.  Negative values for the file
- * descriptors will use stdout and stderr FILE references respectively
- * internally, and will not close the references as noted above.
- *
- * Return value is 0 for success, non-zero for errors.
- */
-int android_logcat_run_command(android_logcat_context ctx, int output, int error,
-                               int argc, char* const* argv, char* const* envp);
-
-/* Will not block, performed in-process
- *
- * Starts a thread, opens a pipe, returns reading end fd, saves off argv.
- * The command supports 2>&1 (mix content) and 2>/dev/null (drop content) for
- * scripted error (stderr) redirection.
- */
-int android_logcat_run_command_thread(android_logcat_context ctx, int argc,
-                                      char* const* argv, char* const* envp);
-int android_logcat_run_command_thread_running(android_logcat_context ctx);
-
-/* Finished with context
- *
- * Kill the command thread ASAP (if any), and free up all associated resources.
- *
- * Return value is the result of the android_logcat_run_command, or
- * non-zero for any errors.
- */
-int android_logcat_destroy(android_logcat_context* ctx);
-
-/* derived helpers */
-
-/*
- * In-process thread that acts like somewhat like libc-like system and popen
- * respectively.  Can not handle shell scripting, only pure calls to the
- * logcat operations. The android_logcat_system is a wrapper for the
- * create_android_logcat, android_logcat_run_command and android_logcat_destroy
- * API above.  The android_logcat_popen is a wrapper for the
- * android_logcat_run_command_thread API above.  The android_logcat_pclose is
- * a wrapper for a reasonable wait until output has subsided for command
- * completion, fclose on the FILE pointer and the android_logcat_destroy API.
- */
-int android_logcat_system(const char* command);
-FILE* android_logcat_popen(android_logcat_context* ctx, const char* command);
-int android_logcat_pclose(android_logcat_context* ctx, FILE* output);
-
-#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBS_LOGCAT_H */
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index ff85f54..0f56337 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
+#include "logcat.h"
+
 #include <arpa/inet.h>
 #include <assert.h>
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <getopt.h>
 #include <math.h>
 #include <pthread.h>
 #include <sched.h>
@@ -47,8 +50,6 @@
 #include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
-#include <log/getopt.h>
-#include <log/logcat.h>
 #include <log/logprint.h>
 #include <private/android_logger.h>
 #include <system/thread_defs.h>
@@ -854,14 +855,8 @@
     // net for stability dealing with possible mistaken inputs.
     static const char delimiters[] = ",:; \t\n\r\f";
 
-    struct getopt_context optctx;
-    INIT_GETOPT_CONTEXT(optctx);
-    optctx.opterr = !!context->error;
-    optctx.optstderr = context->error;
-
-    for (;;) {
-        int ret;
-
+    optind = 0;
+    while (true) {
         int option_index = 0;
         // list of long-argument only strings for later comparison
         static const char pid_str[] = "pid";
@@ -902,19 +897,18 @@
         };
         // clang-format on
 
-        ret = getopt_long_r(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
-                            long_options, &option_index, &optctx);
-        if (ret < 0) break;
+        int c = getopt_long(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
+                            &option_index);
+        if (c == -1) break;
 
-        switch (ret) {
+        switch (c) {
             case 0:
                 // only long options
                 if (long_options[option_index].name == pid_str) {
                     // ToDo: determine runtime PID_MAX?
-                    if (!getSizeTArg(optctx.optarg, &pid, 1)) {
+                    if (!getSizeTArg(optarg, &pid, 1)) {
                         logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
-                                     long_options[option_index].name,
-                                     optctx.optarg);
+                                     long_options[option_index].name, optarg);
                         goto exit;
                     }
                     break;
@@ -924,11 +918,9 @@
                             ANDROID_LOG_NONBLOCK;
                     // ToDo: implement API that supports setting a wrap timeout
                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
-                    if (optctx.optarg &&
-                        !getSizeTArg(optctx.optarg, &dummy, 1)) {
+                    if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
                         logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
-                                     long_options[option_index].name,
-                                     optctx.optarg);
+                                     long_options[option_index].name, optarg);
                         goto exit;
                     }
                     if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
@@ -949,8 +941,7 @@
                     break;
                 }
                 if (long_options[option_index].name == id_str) {
-                    setId = (optctx.optarg && optctx.optarg[0]) ? optctx.optarg
-                                                                : nullptr;
+                    setId = (optarg && optarg[0]) ? optarg : nullptr;
                 }
                 break;
 
@@ -978,32 +969,27 @@
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
             // FALLTHRU
             case 'T':
-                if (strspn(optctx.optarg, "0123456789") !=
-                    strlen(optctx.optarg)) {
-                    char* cp = parseTime(tail_time, optctx.optarg);
+                if (strspn(optarg, "0123456789") != strlen(optarg)) {
+                    char* cp = parseTime(tail_time, optarg);
                     if (!cp) {
-                        logcat_panic(context, HELP_FALSE,
-                                     "-%c \"%s\" not in time format\n", ret,
-                                     optctx.optarg);
+                        logcat_panic(context, HELP_FALSE, "-%c \"%s\" not in time format\n", c,
+                                     optarg);
                         goto exit;
                     }
                     if (*cp) {
-                        char c = *cp;
+                        char ch = *cp;
                         *cp = '\0';
                         if (context->error) {
-                            fprintf(
-                                context->error,
-                                "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
-                                ret, optctx.optarg, c, cp + 1);
+                            fprintf(context->error, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
+                                    c, optarg, ch, cp + 1);
                         }
-                        *cp = c;
+                        *cp = ch;
                     }
                 } else {
-                    if (!getSizeTArg(optctx.optarg, &tail_lines, 1)) {
+                    if (!getSizeTArg(optarg, &tail_lines, 1)) {
                         if (context->error) {
-                            fprintf(context->error,
-                                    "WARNING: -%c %s invalid, setting to 1\n",
-                                    ret, optctx.optarg);
+                            fprintf(context->error, "WARNING: -%c %s invalid, setting to 1\n", c,
+                                    optarg);
                         }
                         tail_lines = 1;
                     }
@@ -1015,21 +1001,19 @@
                 break;
 
             case 'e':
-                context->regex = new pcrecpp::RE(optctx.optarg);
+                context->regex = new pcrecpp::RE(optarg);
                 break;
 
             case 'm': {
-                if (!getSizeTArg(optctx.optarg, &context->maxCount)) {
+                if (!getSizeTArg(optarg, &context->maxCount)) {
                     logcat_panic(context, HELP_FALSE,
-                                 "-%c \"%s\" isn't an "
-                                 "integer greater than zero\n",
-                                 ret, optctx.optarg);
+                                 "-%c \"%s\" isn't an integer greater than zero\n", c, optarg);
                     goto exit;
                 }
             } break;
 
             case 'g':
-                if (!optctx.optarg) {
+                if (!optarg) {
                     getLogSize = true;
                     break;
                 }
@@ -1037,8 +1021,8 @@
 
             case 'G': {
                 char* cp;
-                if (strtoll(optctx.optarg, &cp, 0) > 0) {
-                    setLogSize = strtoll(optctx.optarg, &cp, 0);
+                if (strtoll(optarg, &cp, 0) > 0) {
+                    setLogSize = strtoll(optarg, &cp, 0);
                 } else {
                     setLogSize = 0;
                 }
@@ -1071,19 +1055,18 @@
             } break;
 
             case 'p':
-                if (!optctx.optarg) {
+                if (!optarg) {
                     getPruneList = true;
                     break;
                 }
             // FALLTHRU
 
             case 'P':
-                setPruneList = optctx.optarg;
+                setPruneList = optarg;
                 break;
 
             case 'b': {
-                std::unique_ptr<char, void (*)(void*)> buffers(
-                    strdup(optctx.optarg), free);
+                std::unique_ptr<char, void (*)(void*)> buffers(strdup(optarg), free);
                 char* arg = buffers.get();
                 unsigned idMask = 0;
                 char* sv = nullptr;  // protect against -ENOMEM above
@@ -1147,40 +1130,33 @@
 
             case 'f':
                 if ((tail_time == log_time::EPOCH) && !tail_lines) {
-                    tail_time = lastLogTime(optctx.optarg);
+                    tail_time = lastLogTime(optarg);
                 }
                 // redirect output to a file
-                context->outputFileName = optctx.optarg;
+                context->outputFileName = optarg;
                 break;
 
             case 'r':
-                if (!getSizeTArg(optctx.optarg, &context->logRotateSizeKBytes,
-                                 1)) {
-                    logcat_panic(context, HELP_TRUE,
-                                 "Invalid parameter \"%s\" to -r\n",
-                                 optctx.optarg);
+                if (!getSizeTArg(optarg, &context->logRotateSizeKBytes, 1)) {
+                    logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
                     goto exit;
                 }
                 break;
 
             case 'n':
-                if (!getSizeTArg(optctx.optarg, &context->maxRotatedLogs, 1)) {
-                    logcat_panic(context, HELP_TRUE,
-                                 "Invalid parameter \"%s\" to -n\n",
-                                 optctx.optarg);
+                if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) {
+                    logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
                     goto exit;
                 }
                 break;
 
             case 'v': {
-                if (!strcmp(optctx.optarg, "help") ||
-                    !strcmp(optctx.optarg, "--help")) {
+                if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
                     show_format_help(context);
                     context->retval = EXIT_SUCCESS;
                     goto exit;
                 }
-                std::unique_ptr<char, void (*)(void*)> formats(
-                    strdup(optctx.optarg), free);
+                std::unique_ptr<char, void (*)(void*)> formats(strdup(optarg), free);
                 char* arg = formats.get();
                 char* sv = nullptr;  // protect against -ENOMEM above
                 while (!!(arg = strtok_r(arg, delimiters, &sv))) {
@@ -1300,8 +1276,7 @@
                 break;
 
             case ':':
-                logcat_panic(context, HELP_TRUE,
-                             "Option -%c needs an argument\n", optctx.optopt);
+                logcat_panic(context, HELP_TRUE, "Option -%c needs an argument\n", optopt);
                 goto exit;
 
             case 'h':
@@ -1310,8 +1285,7 @@
                 goto exit;
 
             default:
-                logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
-                             optctx.optopt);
+                logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n", optopt);
                 goto exit;
         }
     }
@@ -1400,7 +1374,7 @@
                          "Invalid filter expression in logcat args\n");
             goto exit;
         }
-    } else if (argc == optctx.optind) {
+    } else if (argc == optind) {
         // Add from environment variable
         const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
 
@@ -1416,7 +1390,7 @@
         }
     } else {
         // Add from commandline
-        for (int i = optctx.optind ; i < argc ; i++) {
+        for (int i = optind ; i < argc ; i++) {
             // skip stderr redirections of _all_ kinds
             if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
             // skip stdout redirections of _all_ kinds
@@ -1707,105 +1681,6 @@
     return __logcat(context);
 }
 
-// starts a thread, opens a pipe, returns reading end.
-int android_logcat_run_command_thread(android_logcat_context ctx,
-                                      int argc, char* const* argv,
-                                      char* const* envp) {
-    android_logcat_context_internal* context = ctx;
-
-    int save_errno = EBUSY;
-    if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) goto exit;
-
-    if (pipe(context->fds) < 0) {
-        save_errno = errno;
-        goto exit;
-    }
-
-    pthread_attr_t attr;
-    if (pthread_attr_init(&attr)) {
-        save_errno = errno;
-        goto close_exit;
-    }
-
-    struct sched_param param;
-    memset(&param, 0, sizeof(param));
-    pthread_attr_setschedparam(&attr, &param);
-    pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
-    if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
-        save_errno = errno;
-        goto pthread_attr_exit;
-    }
-
-    context->stop = false;
-    context->thread_stopped = false;
-    context->output_fd = context->fds[1];
-    // save off arguments so they remain while thread is active.
-    for (int i = 0; i < argc; ++i) {
-        context->args.push_back(std::string(argv[i]));
-    }
-    // save off environment so they remain while thread is active.
-    if (envp) for (size_t i = 0; envp[i]; ++i) {
-        context->envs.push_back(std::string(envp[i]));
-    }
-
-    for (auto& str : context->args) {
-        context->argv_hold.push_back(str.c_str());
-    }
-    context->argv_hold.push_back(nullptr);
-    for (auto& str : context->envs) {
-        context->envp_hold.push_back(str.c_str());
-    }
-    context->envp_hold.push_back(nullptr);
-
-    context->argc = context->argv_hold.size() - 1;
-    context->argv = (char* const*)&context->argv_hold[0];
-    context->envp = (char* const*)&context->envp_hold[0];
-
-#ifdef DEBUG
-    fprintf(stderr, "argv[%d] = {", context->argc);
-    for (auto str : context->argv_hold) {
-        fprintf(stderr, " \"%s\"", str ?: "nullptr");
-    }
-    fprintf(stderr, " }\n");
-    fflush(stderr);
-#endif
-    context->retval = EXIT_SUCCESS;
-    if (pthread_create(&context->thr, &attr,
-                       (void*(*)(void*))__logcat, context)) {
-        save_errno = errno;
-        goto argv_exit;
-    }
-    pthread_attr_destroy(&attr);
-
-    return context->fds[0];
-
-argv_exit:
-    context->argv_hold.clear();
-    context->args.clear();
-    context->envp_hold.clear();
-    context->envs.clear();
-pthread_attr_exit:
-    pthread_attr_destroy(&attr);
-close_exit:
-    close(context->fds[0]);
-    context->fds[0] = -1;
-    close(context->fds[1]);
-    context->fds[1] = -1;
-exit:
-    errno = save_errno;
-    context->stop = true;
-    context->thread_stopped = true;
-    context->retval = EXIT_FAILURE;
-    return -1;
-}
-
-// test if the thread is still doing 'stuff'
-int android_logcat_run_command_thread_running(android_logcat_context ctx) {
-    android_logcat_context_internal* context = ctx;
-
-    return context->thread_stopped == false;
-}
-
 // Finished with context
 int android_logcat_destroy(android_logcat_context* ctx) {
     android_logcat_context_internal* context = *ctx;
diff --git a/logcat/logcat.h b/logcat/logcat.h
new file mode 100644
index 0000000..85ed7da
--- /dev/null
+++ b/logcat/logcat.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2005-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.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+/*
+ * The opaque context
+ */
+typedef struct android_logcat_context_internal* android_logcat_context;
+
+/* Creates a context associated with this logcat instance
+ *
+ * Returns a pointer to the context, or a NULL on error.
+ */
+android_logcat_context create_android_logcat();
+
+/* Collects and outputs the logcat data to output and error file descriptors
+ *
+ * Will block, performed in-thread and in-process
+ *
+ * The output file descriptor variable, if greater than or equal to 0, is
+ * where the output (ie: stdout) will be sent. The file descriptor is closed
+ * on android_logcat_destroy which terminates the instance, or when an -f flag
+ * (output redirect to a file) is present in the command.  The error file
+ * descriptor variable, if greater than or equal to 0, is where the error
+ * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
+ * The error file descriptor can be set to equal to the output file descriptor,
+ * which will mix output and error stream content, and will defer closure of
+ * the file descriptor on -f flag redirection.  Negative values for the file
+ * descriptors will use stdout and stderr FILE references respectively
+ * internally, and will not close the references as noted above.
+ *
+ * Return value is 0 for success, non-zero for errors.
+ */
+int android_logcat_run_command(android_logcat_context ctx, int output, int error, int argc,
+                               char* const* argv, char* const* envp);
+
+/* Finished with context
+ *
+ * Kill the command thread ASAP (if any), and free up all associated resources.
+ *
+ * Return value is the result of the android_logcat_run_command, or
+ * non-zero for any errors.
+ */
+int android_logcat_destroy(android_logcat_context* ctx);
diff --git a/logcat/logcat_main.cpp b/logcat/logcat_main.cpp
index 9477e79..ecfa2ba 100644
--- a/logcat/logcat_main.cpp
+++ b/logcat/logcat_main.cpp
@@ -17,7 +17,7 @@
 #include <signal.h>
 #include <stdlib.h>
 
-#include <log/logcat.h>
+#include "logcat.h"
 
 int main(int argc, char** argv, char** envp) {
     android_logcat_context ctx = create_android_logcat();
diff --git a/logcat/logcat_system.cpp b/logcat/logcat_system.cpp
deleted file mode 100644
index 6dfd110..0000000
--- a/logcat/logcat_system.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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 <ctype.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <string>
-#include <vector>
-
-#include <log/logcat.h>
-
-static std::string unquote(const char*& cp, const char*& delim) {
-    if ((*cp == '\'') || (*cp == '"')) {
-        // KISS: Simple quotes. Do not handle the case
-        //       of concatenation like "blah"foo'bar'
-        char quote = *cp++;
-        delim = strchr(cp, quote);
-        if (!delim) delim = cp + strlen(cp);
-        std::string str(cp, delim);
-        if (*delim) ++delim;
-        return str;
-    }
-    delim = strpbrk(cp, " \t\f\r\n");
-    if (!delim) delim = cp + strlen(cp);
-    return std::string(cp, delim);
-}
-
-static bool __android_logcat_parse(const char* command,
-                                   std::vector<std::string>& args,
-                                   std::vector<std::string>& envs) {
-    for (const char *delim, *cp = command; cp && *cp; cp = delim) {
-        while (isspace(*cp)) ++cp;
-        if ((args.size() == 0) && (*cp != '=') && !isdigit(*cp)) {
-            const char* env = cp;
-            while (isalnum(*cp) || (*cp == '_')) ++cp;
-            if (cp && (*cp == '=')) {
-                std::string str(env, ++cp);
-                str += unquote(cp, delim);
-                envs.push_back(str);
-                continue;
-            }
-            cp = env;
-        }
-        args.push_back(unquote(cp, delim));
-        if ((args.size() == 1) && (args[0] != "logcat") &&
-            (args[0] != "/system/bin/logcat")) {
-            return false;
-        }
-    }
-    return args.size() != 0;
-}
-
-FILE* android_logcat_popen(android_logcat_context* ctx, const char* command) {
-    *ctx = NULL;
-
-    std::vector<std::string> args;
-    std::vector<std::string> envs;
-    if (!__android_logcat_parse(command, args, envs)) return NULL;
-
-    std::vector<const char*> argv;
-    for (auto& str : args) {
-        argv.push_back(str.c_str());
-    }
-    argv.push_back(NULL);
-
-    std::vector<const char*> envp;
-    for (auto& str : envs) {
-        envp.push_back(str.c_str());
-    }
-    envp.push_back(NULL);
-
-    *ctx = create_android_logcat();
-    if (!*ctx) return NULL;
-
-    int fd = android_logcat_run_command_thread(
-        *ctx, argv.size() - 1, (char* const*)&argv[0], (char* const*)&envp[0]);
-    argv.clear();
-    args.clear();
-    envp.clear();
-    envs.clear();
-    if (fd < 0) {
-        android_logcat_destroy(ctx);
-        return NULL;
-    }
-
-    int duped = dup(fd);
-    FILE* retval = fdopen(duped, "reb");
-    if (!retval) {
-        close(duped);
-        android_logcat_destroy(ctx);
-    }
-    return retval;
-}
-
-int android_logcat_pclose(android_logcat_context* ctx, FILE* output) {
-    if (*ctx) {
-        static const useconds_t wait_sample = 20000;
-        // Wait two seconds maximum
-        for (size_t retry = ((2 * 1000000) + wait_sample - 1) / wait_sample;
-             android_logcat_run_command_thread_running(*ctx) && retry; --retry) {
-            usleep(wait_sample);
-        }
-    }
-
-    if (output) fclose(output);
-    return android_logcat_destroy(ctx);
-}
-
-int android_logcat_system(const char* command) {
-    std::vector<std::string> args;
-    std::vector<std::string> envs;
-    if (!__android_logcat_parse(command, args, envs)) return -1;
-
-    std::vector<const char*> argv;
-    for (auto& str : args) {
-        argv.push_back(str.c_str());
-    }
-    argv.push_back(NULL);
-
-    std::vector<const char*> envp;
-    for (auto& str : envs) {
-        envp.push_back(str.c_str());
-    }
-    envp.push_back(NULL);
-
-    android_logcat_context ctx = create_android_logcat();
-    if (!ctx) return -1;
-    /* Command return value */
-    int retval = android_logcat_run_command(ctx, -1, -1, argv.size() - 1,
-                                            (char* const*)&argv[0],
-                                            (char* const*)&envp[0]);
-    /* destroy return value */
-    int ret = android_logcat_destroy(&ctx);
-    /* Paranoia merging any discrepancies between the two return values */
-    if (!ret) ret = retval;
-    return ret;
-}
diff --git a/logcat/logcatd_main.cpp b/logcat/logcatd_main.cpp
index 9109eb1..c131846 100644
--- a/logcat/logcatd_main.cpp
+++ b/logcat/logcatd_main.cpp
@@ -21,7 +21,7 @@
 #include <string>
 #include <vector>
 
-#include <log/logcat.h>
+#include "logcat.h"
 
 int main(int argc, char** argv, char** envp) {
     android_logcat_context ctx = create_android_logcat();
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index defd3c4..66f6724 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -32,7 +32,6 @@
 
 benchmark_src_files := \
     logcat_benchmark.cpp \
-    exec_benchmark.cpp \
 
 # Build benchmarks for the device. Run with:
 #   adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
@@ -41,7 +40,7 @@
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
 LOCAL_SRC_FILES := $(benchmark_src_files)
-LOCAL_SHARED_LIBRARIES := libbase liblogcat
+LOCAL_SHARED_LIBRARIES := libbase
 include $(BUILD_NATIVE_BENCHMARK)
 
 # -----------------------------------------------------------------------------
@@ -51,7 +50,6 @@
 test_src_files := \
     logcat_test.cpp \
     logcatd_test.cpp \
-    liblogcat_test.cpp \
 
 # Build tests for the device (with .so). Run with:
 #   adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
@@ -59,6 +57,6 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libbase liblogcat
+LOCAL_SHARED_LIBRARIES := liblog libbase
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/exec_benchmark.cpp b/logcat/tests/exec_benchmark.cpp
deleted file mode 100644
index c30a5f5..0000000
--- a/logcat/tests/exec_benchmark.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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 <stdio.h>
-
-#include <android-base/file.h>
-#include <benchmark/benchmark.h>
-#include <log/logcat.h>
-
-// Dump the statistics and report results
-
-static void logcat_popen_libc(benchmark::State& state, const char* cmd) {
-    while (state.KeepRunning()) {
-        FILE* fp = popen(cmd, "r");
-        std::string ret;
-        android::base::ReadFdToString(fileno(fp), &ret);
-        pclose(fp);
-    }
-}
-
-static void BM_logcat_stat_popen_libc(benchmark::State& state) {
-    logcat_popen_libc(state, "logcat -b all -S");
-}
-BENCHMARK(BM_logcat_stat_popen_libc);
-
-static void logcat_popen_liblogcat(benchmark::State& state, const char* cmd) {
-    while (state.KeepRunning()) {
-        android_logcat_context ctx;
-        FILE* fp = android_logcat_popen(&ctx, cmd);
-        std::string ret;
-        android::base::ReadFdToString(fileno(fp), &ret);
-        android_logcat_pclose(&ctx, fp);
-    }
-}
-
-static void BM_logcat_stat_popen_liblogcat(benchmark::State& state) {
-    logcat_popen_liblogcat(state, "logcat -b all -S");
-}
-BENCHMARK(BM_logcat_stat_popen_liblogcat);
-
-static void logcat_system_libc(benchmark::State& state, const char* cmd) {
-    while (state.KeepRunning()) {
-        system(cmd);
-    }
-}
-
-static void BM_logcat_stat_system_libc(benchmark::State& state) {
-    logcat_system_libc(state, "logcat -b all -S >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_stat_system_libc);
-
-static void logcat_system_liblogcat(benchmark::State& state, const char* cmd) {
-    while (state.KeepRunning()) {
-        android_logcat_system(cmd);
-    }
-}
-
-static void BM_logcat_stat_system_liblogcat(benchmark::State& state) {
-    logcat_system_liblogcat(state, "logcat -b all -S >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_stat_system_liblogcat);
-
-// Dump the logs and report results
-
-static void BM_logcat_dump_popen_libc(benchmark::State& state) {
-    logcat_popen_libc(state, "logcat -b all -d");
-}
-BENCHMARK(BM_logcat_dump_popen_libc);
-
-static void BM_logcat_dump_popen_liblogcat(benchmark::State& state) {
-    logcat_popen_liblogcat(state, "logcat -b all -d");
-}
-BENCHMARK(BM_logcat_dump_popen_liblogcat);
-
-static void BM_logcat_dump_system_libc(benchmark::State& state) {
-    logcat_system_libc(state, "logcat -b all -d >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_dump_system_libc);
-
-static void BM_logcat_dump_system_liblogcat(benchmark::State& state) {
-    logcat_system_liblogcat(state, "logcat -b all -d >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_dump_system_liblogcat);
diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp
deleted file mode 100644
index c8a00da..0000000
--- a/logcat/tests/liblogcat_test.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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 <log/logcat.h>
-
-#define logcat_define(context) android_logcat_context context
-#define logcat_popen(context, command) android_logcat_popen(&(context), command)
-#define logcat_pclose(context, fp) android_logcat_pclose(&(context), fp)
-#define logcat_system(command) android_logcat_system(command)
-#define logcat liblogcat
-
-#include "logcat_test.cpp"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 786fb14..cc1632a 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -37,12 +37,6 @@
 #include <log/log.h>
 #include <log/log_event_list.h>
 
-#ifndef logcat_popen
-#define logcat_define(context)
-#define logcat_popen(context, command) popen((command), "r")
-#define logcat_pclose(context, fp) pclose(fp)
-#define logcat_system(command) system(command)
-#endif
 #ifndef logcat_executable
 #define USING_LOGCAT_EXECUTABLE_DEFAULT
 #define logcat_executable "logcat"
@@ -78,7 +72,6 @@
 
 TEST(logcat, buckets) {
     FILE* fp;
-    logcat_define(ctx);
 
 #undef LOG_TAG
 #define LOG_TAG "inject.buckets"
@@ -90,10 +83,9 @@
     __android_log_bswrite(0, logcat_executable ".inject.buckets");
     rest();
 
-    ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(
-                     ctx, logcat_executable
-                     " -b radio -b events -b system -b main -d 2>/dev/null")));
+    ASSERT_TRUE(NULL != (fp = popen(logcat_executable
+                                    " -b radio -b events -b system -b main -d 2>/dev/null",
+                                    "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -111,7 +103,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     EXPECT_EQ(ids, 15);
 
@@ -120,7 +112,6 @@
 
 TEST(logcat, event_tag_filter) {
     FILE* fp;
-    logcat_define(ctx);
 
 #undef LOG_TAG
 #define LOG_TAG "inject.filter"
@@ -135,7 +126,7 @@
         logcat_executable
         " -b radio -b system -b main --pid=%d -d -s inject.filter 2>/dev/null",
         getpid());
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, command.c_str())));
+    ASSERT_TRUE(NULL != (fp = popen(command.c_str(), "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -145,7 +136,7 @@
         if (strncmp(begin, buffer, sizeof(begin) - 1)) ++count;
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     // logcat, liblogcat and logcatd test instances result in the progression
     // of 3, 6 and 9 for our counts as each round is performed.
@@ -191,7 +182,6 @@
 
     do {
         FILE* fp;
-        logcat_define(ctx);
 
         char needle[32];
         time_t now;
@@ -205,9 +195,8 @@
 #endif
         strftime(needle, sizeof(needle), "[ %Y-", ptm);
 
-        ASSERT_TRUE(NULL != (fp = logcat_popen(
-                                 ctx, logcat_executable
-                                 " -v long -v year -b all -t 3 2>/dev/null")));
+        ASSERT_TRUE(NULL !=
+                    (fp = popen(logcat_executable " -v long -v year -b all -t 3 2>/dev/null", "r")));
 
         char buffer[BIG_BUFFER];
 
@@ -218,7 +207,7 @@
                 ++count;
             }
         }
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < 3) && --tries && inject(3 - count));
 
@@ -268,12 +257,10 @@
 
     do {
         FILE* fp;
-        logcat_define(ctx);
 
-        ASSERT_TRUE(NULL !=
-                    (fp = logcat_popen(ctx, logcat_executable
-                                       " -v long -v America/Los_Angeles "
-                                       "-b all -t 3 2>/dev/null")));
+        ASSERT_TRUE(NULL != (fp = popen(logcat_executable
+                                        " -v long -v America/Los_Angeles -b all -t 3 2>/dev/null",
+                                        "r")));
 
         char buffer[BIG_BUFFER];
 
@@ -287,7 +274,7 @@
             }
         }
 
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < 3) && --tries && inject(3 - count));
 
@@ -296,11 +283,11 @@
 
 TEST(logcat, ntz) {
     FILE* fp;
-    logcat_define(ctx);
 
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, logcat_executable
-                                           " -v long -v America/Los_Angeles -v "
-                                           "zone -b all -t 3 2>/dev/null")));
+    ASSERT_TRUE(NULL !=
+                (fp = popen(logcat_executable
+                            " -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null",
+                            "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -312,7 +299,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(0, count);
 }
@@ -330,8 +317,7 @@
                  "ANDROID_PRINTF_LOG=long logcat -b all -t %d 2>/dev/null", num);
 
         FILE* fp;
-        logcat_define(ctx);
-        ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+        ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
         count = 0;
 
@@ -339,7 +325,7 @@
             ++count;
         }
 
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < num) && --tries && inject(num - count));
 
@@ -377,8 +363,7 @@
 
     do {
         snprintf(buffer, sizeof(buffer), "%s -t 10 2>&1", cmd);
-        logcat_define(ctx);
-        ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+        ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
         count = 0;
 
         while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
@@ -391,7 +376,7 @@
             free(last_timestamp);
             last_timestamp = strdup(input);
         }
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < 10) && --tries && inject(10 - count));
 
@@ -401,8 +386,7 @@
     EXPECT_TRUE(second_timestamp != NULL);
 
     snprintf(buffer, sizeof(buffer), "%s -t '%s' 2>&1", cmd, first_timestamp);
-    logcat_define(ctx);
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
     int second_count = 0;
     int last_timestamp_count = -1;
@@ -442,7 +426,7 @@
             last_timestamp_count = second_count;
         }
     }
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     EXPECT_TRUE(found);
     if (!found) {
@@ -483,10 +467,8 @@
     ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
 
     FILE* fp;
-    logcat_define(ctx);
     ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(ctx, logcat_executable
-                                   " -v brief -b events -t 100 2>/dev/null")));
+                (fp = popen(logcat_executable " -v brief -b events -t 100 2>/dev/null", "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -507,7 +489,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(1, count);
 }
@@ -521,12 +503,9 @@
 
     FILE* fp[256];  // does this count as a multitude!
     memset(fp, 0, sizeof(fp));
-    logcat_define(ctx[sizeof(fp) / sizeof(fp[0])]);
     size_t num = 0;
     do {
-        EXPECT_TRUE(NULL !=
-                    (fp[num] = logcat_popen(ctx[num], logcat_executable
-                                            " -v brief -b events -t 100")));
+        EXPECT_TRUE(NULL != (fp[num] = popen(logcat_executable " -v brief -b events -t 100", "r")));
         if (!fp[num]) {
             fprintf(stderr,
                     "WARNING: limiting to %zu simultaneous logcat operations\n",
@@ -556,7 +535,7 @@
             }
         }
 
-        logcat_pclose(ctx[idx], fp[idx]);
+        pclose(fp[idx]);
     }
 
     ASSERT_EQ(num, count);
@@ -564,10 +543,9 @@
 
 static int get_groups(const char* cmd) {
     FILE* fp;
-    logcat_define(ctx);
 
     // NB: crash log only available in user space
-    EXPECT_TRUE(NULL != (fp = logcat_popen(ctx, cmd)));
+    EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
 
     if (fp == NULL) {
         return 0;
@@ -631,7 +609,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     return count;
 }
@@ -815,7 +793,7 @@
     snprintf(command, sizeof(command), comm, buf);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(ret = system(command), command));
     if (!ret) {
         snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
 
@@ -861,7 +839,7 @@
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(ret = system(command), command));
     if (!ret) {
         snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
 
@@ -920,7 +898,7 @@
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(ret = system(command), command));
     if (ret) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
@@ -969,7 +947,7 @@
     // re-run the command, it should only add a few lines more content if it
     // continues where it left off.
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
-    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(ret = system(command), command));
     if (ret) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
@@ -1052,7 +1030,7 @@
                  tmp_out_dir, log_filename, num_val);
 
         int ret;
-        EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+        EXPECT_FALSE(IsFalse(ret = system(command), command));
         if (ret) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(IsFalse(system(command), command));
@@ -1082,7 +1060,7 @@
         strcat(command, clear_cmd);
 
         int ret;
-        EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+        EXPECT_FALSE(IsFalse(ret = system(command), command));
         if (ret) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(system(command));
@@ -1120,7 +1098,7 @@
 
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
 
-    int ret = logcat_system(command);
+    int ret = system(command);
     if (ret) {
         fprintf(stderr, "system(\"%s\")=%d", command, ret);
         return -1;
@@ -1194,7 +1172,7 @@
         " -b all -d"
         " -f /das/nein/gerfingerpoken/logcat/log.txt"
         " -n 256 -r 1024";
-    EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(0 == system(command), command));
 }
 
 #ifndef logcat
@@ -1329,10 +1307,7 @@
 #endif
 
 static bool get_white_black(char** list) {
-    FILE* fp;
-    logcat_define(ctx);
-
-    fp = logcat_popen(ctx, logcat_executable " -p 2>/dev/null");
+    FILE* fp = popen(logcat_executable " -p 2>/dev/null", "r");
     if (fp == NULL) {
         fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
         return false;
@@ -1360,19 +1335,15 @@
             asprintf(list, "%s", buf);
         }
     }
-    logcat_pclose(ctx, fp);
+    pclose(fp);
     return *list != NULL;
 }
 
 static bool set_white_black(const char* list) {
-    FILE* fp;
-    logcat_define(ctx);
-
     char buffer[BIG_BUFFER];
-
     snprintf(buffer, sizeof(buffer), logcat_executable " -P '%s' 2>&1",
              list ? list : "");
-    fp = logcat_popen(ctx, buffer);
+    FILE* fp = popen(buffer, "r");
     if (fp == NULL) {
         fprintf(stderr, "ERROR: %s\n", buffer);
         return false;
@@ -1391,10 +1362,10 @@
             continue;
         }
         fprintf(stderr, "%s\n", buf);
-        logcat_pclose(ctx, fp);
+        pclose(fp);
         return false;
     }
-    return logcat_pclose(ctx, fp) == 0;
+    return pclose(fp) == 0;
 }
 
 TEST(logcat, white_black_adjust) {
@@ -1429,7 +1400,6 @@
 
 TEST(logcat, regex) {
     FILE* fp;
-    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
@@ -1450,7 +1420,7 @@
     // Let the logs settle
     rest();
 
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1462,14 +1432,13 @@
         count++;
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(2, count);
 }
 
 TEST(logcat, maxcount) {
     FILE* fp;
-    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
@@ -1488,7 +1457,7 @@
 
     rest();
 
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1498,7 +1467,7 @@
         count++;
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(3, count);
 }
@@ -1510,13 +1479,7 @@
     ;
 
 static bool End_to_End(const char* tag, const char* fmt, ...) {
-    logcat_define(ctx);
-    FILE* fp = logcat_popen(ctx, logcat_executable
-                            " -v brief"
-                            " -b events"
-                            " -v descriptive"
-                            " -t 100"
-                            " 2>/dev/null");
+    FILE* fp = popen(logcat_executable " -v brief -b events -v descriptive -t 100 2>/dev/null", "r");
     if (!fp) {
         fprintf(stderr, "End_to_End: popen failed");
         return false;
@@ -1551,7 +1514,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     if ((count == 0) && (lastMatch.length() > 0)) {
         // Help us pinpoint where things went wrong ...
@@ -1741,13 +1704,12 @@
 }
 
 static bool reportedSecurity(const char* command) {
-    logcat_define(ctx);
-    FILE* fp = logcat_popen(ctx, command);
+    FILE* fp = popen(command, "r");
     if (!fp) return true;
 
     std::string ret;
     bool val = android::base::ReadFdToString(fileno(fp), &ret);
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     if (!val) return true;
     return std::string::npos != ret.find("'security'");
@@ -1762,13 +1724,12 @@
 }
 
 static size_t commandOutputSize(const char* command) {
-    logcat_define(ctx);
-    FILE* fp = logcat_popen(ctx, command);
+    FILE* fp = popen(command, "r");
     if (!fp) return 0;
 
     std::string ret;
     if (!android::base::ReadFdToString(fileno(fp), &ret)) return 0;
-    if (logcat_pclose(ctx, fp) != 0) return 0;
+    if (pclose(fp) != 0) return 0;
 
     return ret.size();
 }
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 06c0ab5..7a843d8 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -288,9 +288,9 @@
         uid = AID_ROOT;
     }
 
-    const char* name = NULL;
-    const char* format = NULL;
-    const char* id = NULL;
+    const char* name = nullptr;
+    const char* format = nullptr;
+    const char* id = nullptr;
     for (int i = 1; i < argc; ++i) {
         static const char _name[] = "name=";
         if (!strncmp(argv[i], _name, strlen(_name))) {
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
old mode 100755
new mode 100644
index 70ecbe0..658e079
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -36,7 +36,7 @@
 // reference counts are used to ensure that individual
 // LogTimeEntry lifetime is managed when not protected.
 void FlushCommand::runSocketCommand(SocketClient* client) {
-    LogTimeEntry* entry = NULL;
+    LogTimeEntry* entry = nullptr;
     LastLogTimes& times = mReader.logbuf().mTimes;
 
     LogTimeEntry::wrlock();
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
old mode 100755
new mode 100644
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
old mode 100755
new mode 100644
index 27cd9a8..4ea7877
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -171,13 +171,13 @@
 }
 
 int LogAudit::logPrint(const char* fmt, ...) {
-    if (fmt == NULL) {
+    if (fmt == nullptr) {
         return -EINVAL;
     }
 
     va_list args;
 
-    char* str = NULL;
+    char* str = nullptr;
     va_start(args, fmt);
     int rc = vasprintf(&str, fmt, args);
     va_end(args);
@@ -228,7 +228,7 @@
         static char* last_str;
         static bool last_info;
 
-        if (last_str != NULL) {
+        if (last_str != nullptr) {
             static const char avc[] = "): avc: ";
             char* avcl = strstr(last_str, avc);
             bool skip = false;
@@ -265,10 +265,10 @@
 
                 writev(fdDmesg, iov, arraysize(iov));
                 free(last_str);
-                last_str = NULL;
+                last_str = nullptr;
             }
         }
-        if (last_str == NULL) {
+        if (last_str == nullptr) {
             count = 0;
             last_str = strdup(str);
             last_info = info;
@@ -357,7 +357,7 @@
     static const char comm_str[] = " comm=\"";
     const char* comm = strstr(str, comm_str);
     const char* estr = str + strlen(str);
-    const char* commfree = NULL;
+    const char* commfree = nullptr;
     if (comm) {
         estr = comm;
         comm += sizeof(comm_str) - 1;
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index f20ac45..2d627b9 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -91,7 +91,7 @@
 
 // caller must own and free character string
 char* android::tidToName(pid_t tid) {
-    char* retval = NULL;
+    char* retval = nullptr;
     char buffer[256];
     snprintf(buffer, sizeof(buffer), "/proc/%u/comm", tid);
     int fd = open(buffer, O_RDONLY);
@@ -114,7 +114,7 @@
     char* name = android::pidToName(tid);
     if (!retval) {
         retval = name;
-        name = NULL;
+        name = nullptr;
     }
 
     // check if comm is truncated, see if cmdline has full representation
@@ -162,15 +162,15 @@
         if (!strncmp(name + 1, commName + 1, len)) {
             if (commName[len + 1] == '\0') {
                 free(const_cast<char*>(commName));
-                commName = NULL;
+                commName = nullptr;
             } else {
                 free(const_cast<char*>(name));
-                name = NULL;
+                name = nullptr;
             }
         }
     }
     if (name) {
-        char* buf = NULL;
+        char* buf = nullptr;
         asprintf(&buf, "(%s)", name);
         if (buf) {
             free(const_cast<char*>(name));
@@ -178,7 +178,7 @@
         }
     }
     if (commName) {
-        char* buf = NULL;
+        char* buf = nullptr;
         asprintf(&buf, " %s", commName);
         if (buf) {
             free(const_cast<char*>(commName));
@@ -187,7 +187,7 @@
     }
     // identical to below to calculate the buffer size required
     const char* type = lastSame ? "identical" : "expire";
-    size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
+    size_t len = snprintf(nullptr, 0, format_uid, mUid, name ? name : "",
                           commName ? commName : "", type, getDropped(),
                           (getDropped() > 1) ? "s" : "");
 
@@ -247,7 +247,7 @@
     iovec[0].iov_base = &entry;
     iovec[0].iov_len = entry.hdr_size;
 
-    char* buffer = NULL;
+    char* buffer = nullptr;
 
     if (mDropped) {
         entry.len = populateDroppedMessage(buffer, parent, lastSame);
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
index 6d7c0a5..8bff9da 100644
--- a/logd/LogCommand.cpp
+++ b/logd/LogCommand.cpp
@@ -44,9 +44,9 @@
     char* ptr;
     static const char ws[] = " \n";
 
-    for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(NULL, ws, &ptr)) {
+    for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(nullptr, ws, &ptr)) {
         errno = 0;
-        gid_t Gid = strtol(buf, NULL, 10);
+        gid_t Gid = strtol(buf, nullptr, 10);
         if (errno != 0) {
             return false;
         }
@@ -98,7 +98,7 @@
             continue;
         }
 
-        char* line = NULL;
+        char* line = nullptr;
         size_t len = 0;
         while (getline(&line, &len, file) > 0) {
             static const char groups_string[] = "Groups:\t";
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
old mode 100755
new mode 100644
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
old mode 100755
new mode 100644
index fc51dcf..e568ddc
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -50,7 +50,7 @@
 
     alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
     struct msghdr hdr = {
-        NULL, 0, &iov, 1, control, sizeof(control), 0,
+        nullptr, 0, &iov, 1, control, sizeof(control), 0,
     };
 
     int socket = cli->getSocket();
@@ -66,10 +66,10 @@
 
     buffer[n] = 0;
 
-    struct ucred* cred = NULL;
+    struct ucred* cred = nullptr;
 
     struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
-    while (cmsg != NULL) {
+    while (cmsg != nullptr) {
         if (cmsg->cmsg_level == SOL_SOCKET &&
             cmsg->cmsg_type == SCM_CREDENTIALS) {
             cred = (struct ucred*)CMSG_DATA(cmsg);
@@ -79,7 +79,7 @@
     }
 
     struct ucred fake_cred;
-    if (cred == NULL) {
+    if (cred == nullptr) {
         cred = &fake_cred;
         cred->pid = 0;
         cred->uid = DEFAULT_OVERFLOWUID;
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
old mode 100755
new mode 100644
diff --git a/logd/LogReader.h b/logd/LogReader.h
old mode 100755
new mode 100644
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index af59ddc..cefacf7 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -56,7 +56,7 @@
 
 // caller must own and free character string
 char* pidToName(pid_t pid) {
-    char* retval = NULL;
+    char* retval = nullptr;
     if (pid == 0) {  // special case from auditd/klogd for kernel
         retval = strdup("logd");
     } else {
@@ -286,7 +286,7 @@
                     name = strdup(nameTmp);
                 } else if (fastcmp<strcmp>(name, nameTmp)) {
                     free(const_cast<char*>(name));
-                    name = NULL;
+                    name = nullptr;
                     break;
                 }
             }
@@ -872,7 +872,7 @@
     pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
     const char* name = writablePidTable.add(pid)->second.getName();
     if (!name) {
-        return NULL;
+        return nullptr;
     }
     return strdup(name);
 }
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index ff7e762..1ab9dd1 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -91,7 +91,7 @@
         fd = TEMP_FAILURE_RETRY(open(
             filename, O_WRONLY | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY));
         if (fd >= 0) {
-            time_t now = time(NULL);
+            time_t now = time(nullptr);
             struct tm tm;
             localtime_r(&now, &tm);
             char timebuf[20];
@@ -208,7 +208,7 @@
             } else if (lineStart) {
                 if (*cp == '#') {
                     /* comment; just scan to end */
-                    lineStart = NULL;
+                    lineStart = nullptr;
                 } else if (isdigit(*cp)) {
                     unsigned long Tag = strtoul(cp, &cp, 10);
                     if (warn && (Tag > emptyTag)) {
@@ -235,7 +235,7 @@
                     if (hasAlpha &&
                         ((cp >= endp) || (*cp == '#') || isspace(*cp))) {
                         if (Tag > emptyTag) {
-                            if (*cp != '\n') lineStart = NULL;
+                            if (*cp != '\n') lineStart = nullptr;
                             continue;
                         }
                         while ((cp < endp) && (*cp != '\n') && isspace(*cp))
@@ -245,14 +245,14 @@
                         while ((cp < endp) && (*cp != '\n')) {
                             if (*cp == '#') {
                                 uid = sniffUid(cp, endp);
-                                lineStart = NULL;
+                                lineStart = nullptr;
                                 break;
                             }
                             ++cp;
                         }
                         while ((cp > format) && isspace(cp[-1])) {
                             --cp;
-                            lineStart = NULL;
+                            lineStart = nullptr;
                         }
                         std::string Format(format, cp - format);
 
@@ -263,7 +263,7 @@
                             android::prdebug("tag name invalid %.*s",
                                              (int)(cp - name + 1), name);
                         }
-                        lineStart = NULL;
+                        lineStart = nullptr;
                     }
                 } else if (!isspace(*cp)) {
                     break;
@@ -364,7 +364,7 @@
     android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
 
     it = tag2name.find(tag);
-    if ((it == tag2name.end()) || (it->second.length() == 0)) return NULL;
+    if ((it == tag2name.end()) || (it->second.length() == 0)) return nullptr;
 
     return it->second.c_str();
 }
@@ -383,7 +383,7 @@
 const char* android::tagToName(uint32_t tag) {
     LogTags* me = logtags;
 
-    if (!me) return NULL;
+    if (!me) return nullptr;
     me->WritePmsgEventLogTags(tag);
     return me->tagToName(tag);
 }
@@ -412,7 +412,7 @@
     android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
 
     iform = tag2format.find(tag);
-    if (iform == tag2format.end()) return NULL;
+    if (iform == tag2format.end()) return nullptr;
 
     return iform->second.c_str();
 }
@@ -441,7 +441,7 @@
                                    bool& unique) {
     key2tag_const_iterator ik;
 
-    bool write = format != NULL;
+    bool write = format != nullptr;
     unique = write;
 
     if (!write) {
@@ -679,7 +679,7 @@
 // are in readonly mode.
 uint32_t LogTags::nameToTag(uid_t uid, const char* name, const char* format) {
     std::string Name = std::string(name);
-    bool write = format != NULL;
+    bool write = format != nullptr;
     bool updateUid = uid != AID_ROOT;
     bool updateFormat = format && *format;
     bool unique;
@@ -848,7 +848,7 @@
 
     if (!list) {
         // switch to read entry only if format == "*"
-        if (format && (format[0] == '*') && !format[1]) format = NULL;
+        if (format && (format[0] == '*') && !format[1]) format = nullptr;
 
         // WAI: for null format, only works for a single entry, we can have
         // multiple entries, one for each format, so we find first entry
diff --git a/logd/LogTags.h b/logd/LogTags.h
index 203318d..e4d165a 100644
--- a/logd/LogTags.h
+++ b/logd/LogTags.h
@@ -87,14 +87,14 @@
     bool RebuildFileEventLogTags(const char* filename, bool warn = true);
 
     void AddEventLogTags(uint32_t tag, uid_t uid, const std::string& Name,
-                         const std::string& Format, const char* source = NULL,
+                         const std::string& Format, const char* source = nullptr,
                          bool warn = false);
 
     void WriteDynamicEventLogTags(uint32_t tag, uid_t uid);
     void WriteDebugEventLogTags(uint32_t tag, uid_t uid);
     // push tag details to persistent storage
     void WritePersistEventLogTags(uint32_t tag, uid_t uid = AID_ROOT,
-                                  const char* source = NULL);
+                                  const char* source = nullptr);
 
     static const uint32_t emptyTag = uint32_t(-1);
 
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
old mode 100755
new mode 100644
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
old mode 100755
new mode 100644
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index 4b8b080..9d762dc 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -51,7 +51,7 @@
 }
 
 PruneList::PruneList() {
-    init(NULL);
+    init(nullptr);
 }
 
 PruneList::~PruneList() {
@@ -79,7 +79,7 @@
     // default here means take ro.logd.filter, persist.logd.filter then
     // internal default in that order.
     if (str && !strcmp(str, _default)) {
-        str = NULL;
+        str = nullptr;
     }
     static const char _disable[] = "disable";
     if (str && !strcmp(str, _disable)) {
diff --git a/logd/logd.rc b/logd/logd.rc
index bd303b7..c740ecf 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -6,6 +6,7 @@
     file /dev/kmsg w
     user logd
     group logd system package_info readproc
+    capabilities SYSLOG AUDIT_CONTROL SETGID
     writepid /dev/cpuset/system-background/tasks
 
 service logd-reinit /system/bin/logd --reinit
diff --git a/logd/main.cpp b/logd/main.cpp
index 606aa63..b697d44 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -87,37 +87,27 @@
 //
 
 static int drop_privs(bool klogd, bool auditd) {
-    // Tricky, if ro.build.type is "eng" then this is true because of the
-    // side effect that ro.debuggable == 1 as well, else it is false.
-    bool eng =
-        __android_logger_property_get_bool("ro.build.type", BOOL_DEFAULT_FALSE);
-
-    struct sched_param param;
-    memset(&param, 0, sizeof(param));
+    sched_param param = {};
 
     if (set_sched_policy(0, SP_BACKGROUND) < 0) {
         android::prdebug("failed to set background scheduling policy");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (sched_setscheduler((pid_t)0, SCHED_BATCH, &param) < 0) {
         android::prdebug("failed to set batch scheduler");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
         android::prdebug("failed to set background cgroup");
-        if (!eng) return -1;
-    }
-
-    if (!eng && (prctl(PR_SET_DUMPABLE, 0) < 0)) {
-        android::prdebug("failed to clear PR_SET_DUMPABLE");
         return -1;
     }
 
-    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
-        android::prdebug("failed to set PR_SET_KEEPCAPS");
-        if (!eng) return -1;
+    if (__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
+        prctl(PR_SET_DUMPABLE, 0) == -1) {
+        android::prdebug("failed to clear PR_SET_DUMPABLE");
+        return -1;
     }
 
     std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(),
@@ -138,24 +128,24 @@
         android::prdebug(
             "failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)",
             errno);
-        if (!eng) return -1;
+        return -1;
     }
 
     gid_t groups[] = { AID_READPROC };
 
     if (setgroups(arraysize(groups), groups) == -1) {
         android::prdebug("failed to set AID_READPROC groups");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (setgid(AID_LOGD) != 0) {
         android::prdebug("failed to set AID_LOGD gid");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (setuid(AID_LOGD) != 0) {
         android::prdebug("failed to set AID_LOGD uid");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) {
@@ -166,7 +156,7 @@
     }
     if (cap_set_proc(caps.get()) < 0) {
         android::prdebug("failed to clear CAP_SETGID (%d)", errno);
-        if (!eng) return -1;
+        return -1;
     }
 
     return 0;
@@ -473,7 +463,7 @@
     bool auditd =
         __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
     if (drop_privs(klogd, auditd) != 0) {
-        return -1;
+        return EXIT_FAILURE;
     }
 
     // Serves the purpose of managing the last logs times read on a
@@ -501,7 +491,7 @@
 
     LogReader* reader = new LogReader(logBuf);
     if (reader->startListener()) {
-        exit(1);
+        return EXIT_FAILURE;
     }
 
     // LogListener listens on /dev/socket/logdw for client
@@ -511,7 +501,7 @@
     LogListener* swl = new LogListener(logBuf, reader);
     // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
     if (swl->startListener(600)) {
-        exit(1);
+        return EXIT_FAILURE;
     }
 
     // Command listener listens on /dev/socket/logd for incoming logd
@@ -519,7 +509,7 @@
 
     CommandListener* cl = new CommandListener(logBuf, reader, swl);
     if (cl->startListener()) {
-        exit(1);
+        return EXIT_FAILURE;
     }
 
     // LogAudit listens on NETLINK_AUDIT socket for selinux
@@ -554,5 +544,5 @@
 
     TEMP_FAILURE_RETRY(pause());
 
-    exit(0);
+    return EXIT_SUCCESS;
 }
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index d4ba4f4..c378646 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -12,6 +12,7 @@
 cc_library {
     name: "liblogwrap",
     defaults: ["logwrapper_defaults"],
+    recovery_available: true,
     srcs: ["logwrap.c"],
     shared_libs: [
         "libcutils",
diff --git a/mkbootimg/OWNERS b/mkbootimg/OWNERS
index 39448cf..3a00860 100644
--- a/mkbootimg/OWNERS
+++ b/mkbootimg/OWNERS
@@ -1 +1,2 @@
+hridya@google.com
 tbao@google.com
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
index 406e208..bce308b 100644
--- a/mkbootimg/include/bootimg/bootimg.h
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -111,7 +111,7 @@
 
 struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
     uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO image */
-    uint64_t recovery_dtbo_offset; /* physical load addr */
+    uint64_t recovery_dtbo_offset; /* offset to recovery dtbo in boot image */
     uint32_t header_size;
 } __attribute__((packed));
 
@@ -138,11 +138,14 @@
  * 1. kernel and ramdisk are required (size != 0)
  * 2. recovery_dtbo is required for recovery.img in non-A/B devices(recovery_dtbo_size != 0)
  * 3. second is optional (second_size == 0 -> no second)
- * 4. load each element (kernel, ramdisk, second, recovery_dtbo) at
+ * 4. load each element (kernel, ramdisk, second) at
  *    the specified physical address (kernel_addr, etc)
- * 5. prepare tags at tag_addr.  kernel_args[] is
+ * 5. If booting to recovery mode in a non-A/B device, extract recovery dtbo and
+ *    apply the correct set of overlays on the base device tree depending on the
+ *    hardware/product revision.
+ * 6. prepare tags at tag_addr.  kernel_args[] is
  *    appended to the kernel commandline in the tags.
- * 6. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 7. if second_size != 0: jump to second_addr
+ * 7. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 8. if second_size != 0: jump to second_addr
  *    else: jump to kernel_addr
  */
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index ac20d05..fda9af0 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -45,6 +45,22 @@
     f.write(pack(str(pad) + 'x'))
 
 
+def get_number_of_pages(image_size, page_size):
+    """calculates the number of pages required for the image"""
+    return (image_size + page_size - 1) / page_size
+
+
+def get_recovery_dtbo_offset(args):
+    """calculates the offset of recovery_dtbo image in the boot image"""
+    num_header_pages = 1 # header occupies a page
+    num_kernel_pages = get_number_of_pages(filesize(args.kernel), args.pagesize)
+    num_ramdisk_pages = get_number_of_pages(filesize(args.ramdisk), args.pagesize)
+    num_second_pages = get_number_of_pages(filesize(args.second), args.pagesize)
+    dtbo_offset = args.pagesize * (num_header_pages + num_kernel_pages +
+                                   num_ramdisk_pages + num_second_pages)
+    return dtbo_offset
+
+
 def write_header(args):
     BOOT_MAGIC = 'ANDROID!'.encode()
     args.output.write(pack('8s', BOOT_MAGIC))
@@ -76,9 +92,12 @@
     args.output.write(pack('1024s', args.cmdline[512:].encode()))
 
     if args.header_version > 0:
-        args.output.write(pack('I', filesize(args.recovery_dtbo)))           # size in bytes
-        args.output.write(pack('Q', args.base + args.recovery_dtbo_offset))  # physical load addr
-        args.output.write(pack('I', args.output.tell() + 4))                 # size of boot header
+        args.output.write(pack('I', filesize(args.recovery_dtbo)))   # size in bytes
+        if args.recovery_dtbo:
+            args.output.write(pack('Q', get_recovery_dtbo_offset(args))) # recovery dtbo offset
+        else:
+            args.output.write(pack('Q', 0)) # Will be set to 0 for devices without a recovery dtbo
+        args.output.write(pack('I', args.output.tell() + 4))         # size of boot header
 
     pad_file(args.output, args.pagesize)
     return img_id
@@ -150,8 +169,6 @@
     parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
     parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
                         default=0x00f00000)
-    parser.add_argument('--recovery_dtbo_offset', help='recovery dtbo offset', type=parse_int,
-                        default=0x0f000000)
     parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
                         default=0)
     parser.add_argument('--os_patch_level', help='operating system patch level',
diff --git a/mkbootimg/unpack_bootimg b/mkbootimg/unpack_bootimg
index 8e42ec0..c37acd5 100755
--- a/mkbootimg/unpack_bootimg
+++ b/mkbootimg/unpack_bootimg
@@ -76,8 +76,8 @@
     if version > 0:
         recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
         print('recovery dtbo size: %s' % recovery_dtbo_size)
-        recovery_dtbo_address = unpack('Q', args.boot_img.read(8))[0]
-        print('recovery dtbo load address: %s' % recovery_dtbo_address)
+        recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0]
+        print('recovery dtbo offset: %s' % recovery_dtbo_offset)
         boot_header_size = unpack('I', args.boot_img.read(4))[0]
         print('boot header size: %s' % boot_header_size)
     else:
@@ -95,16 +95,13 @@
                                  ) # header + kernel
     image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
 
-    num_second_pages = get_number_of_pages(second_size, page_size)
     second_offset = page_size * (
         num_header_pages + num_kernel_pages + num_ramdisk_pages
     )  # header + kernel + ramdisk
     image_info_list.append((second_offset, second_size, 'second'))
 
     if recovery_dtbo_size > 0:
-        dtbo_offset = page_size * (num_header_pages + num_kernel_pages +
-                                   num_ramdisk_pages + num_second_pages)
-        image_info_list.append((dtbo_offset, recovery_dtbo_size,
+        image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
                                 'recovery_dtbo'))
 
     for image_info in image_info_list:
diff --git a/qemu_pipe/Android.bp b/qemu_pipe/Android.bp
index 93c347b..c6bda4a 100644
--- a/qemu_pipe/Android.bp
+++ b/qemu_pipe/Android.bp
@@ -3,6 +3,7 @@
 cc_library_static {
     name: "libqemu_pipe",
     vendor_available: true,
+    recovery_available: true,
     sanitize: {
         misc_undefined: ["integer"],
     },
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3c9e5f3..478b8d0 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -201,6 +201,7 @@
 	$$(hide) sed -i -e 's?%VNDK_CORE_LIBRARIES%?$$(PRIVATE_VNDK_CORE_LIBRARIES)?g' $$@
 	$$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $$@
 	$$(hide) sed -i -e 's?%VNDK_VER%?$$(PRIVATE_VNDK_VERSION)?g' $$@
+	$$(hide) sed -i -e 's?%PRODUCT%?$$(TARGET_COPY_OUT_PRODUCT)?g' $$@
 
 llndk_libraries_list :=
 vndksp_libraries_list :=
@@ -306,6 +307,16 @@
 _enforce_vndk_lite_at_runtime :=
 
 #######################################
+# ld.config.txt for recovery
+include $(CLEAR_VARS)
+LOCAL_MODULE := ld.config.recovery.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := etc/ld.config.recovery.txt
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/etc
+LOCAL_MODULE_STEM := ld.config.txt
+include $(BUILD_PREBUILT)
+
+#######################################
 # llndk.libraries.txt
 include $(CLEAR_VARS)
 LOCAL_MODULE := llndk.libraries.txt
diff --git a/rootdir/etc/ld.config.recovery.txt b/rootdir/etc/ld.config.recovery.txt
new file mode 100644
index 0000000..5d6c01a
--- /dev/null
+++ b/rootdir/etc/ld.config.recovery.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Bionic loader config file for recovery mode
+#
+
+dir.recovery = /system/bin
+
+[recovery]
+namespace.default.search.paths = /system/${LIB}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 51e3f9e..42dc7ab 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,6 +7,7 @@
 # absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
+dir.system = /%PRODUCT%/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
@@ -38,7 +39,7 @@
 namespace.default.isolated = true
 
 namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
 
 # We can't have entire /system/${LIB} as permitted paths because doing so
 # makes it possible to load libs in /system/${LIB}/vndk* directories by
@@ -50,7 +51,7 @@
 namespace.default.permitted.paths  = /system/${LIB}/drm
 namespace.default.permitted.paths += /system/${LIB}/extractors
 namespace.default.permitted.paths += /system/${LIB}/hw
-namespace.default.permitted.paths += /product/${LIB}
+namespace.default.permitted.paths += /%PRODUCT%/${LIB}
 # These are where odex files are located. libart has to be able to dlopen the files
 namespace.default.permitted.paths += /system/framework
 namespace.default.permitted.paths += /system/app
@@ -62,9 +63,9 @@
 namespace.default.permitted.paths += /odm/app
 namespace.default.permitted.paths += /odm/priv-app
 namespace.default.permitted.paths += /oem/app
-namespace.default.permitted.paths += /product/framework
-namespace.default.permitted.paths += /product/app
-namespace.default.permitted.paths += /product/priv-app
+namespace.default.permitted.paths += /%PRODUCT%/framework
+namespace.default.permitted.paths += /%PRODUCT%/app
+namespace.default.permitted.paths += /%PRODUCT%/priv-app
 namespace.default.permitted.paths += /data
 namespace.default.permitted.paths += /mnt/expand
 
@@ -87,10 +88,10 @@
 namespace.default.asan.permitted.paths += /odm/app
 namespace.default.asan.permitted.paths += /odm/priv-app
 namespace.default.asan.permitted.paths += /oem/app
-namespace.default.asan.permitted.paths += /product/${LIB}
-namespace.default.asan.permitted.paths += /product/framework
-namespace.default.asan.permitted.paths += /product/app
-namespace.default.asan.permitted.paths += /product/priv-app
+namespace.default.asan.permitted.paths += /%PRODUCT%/${LIB}
+namespace.default.asan.permitted.paths += /%PRODUCT%/framework
+namespace.default.asan.permitted.paths += /%PRODUCT%/app
+namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
 namespace.default.asan.permitted.paths += /mnt/expand
 
 ###############################################################################
@@ -326,7 +327,7 @@
 namespace.system.isolated = false
 
 namespace.system.search.paths  = /system/${LIB}
-namespace.system.search.paths += /product/${LIB}
+namespace.system.search.paths += /%PRODUCT%/${LIB}
 
 namespace.system.asan.search.paths  = /data/asan/system/${LIB}
 namespace.system.asan.search.paths +=           /system/${LIB}
@@ -344,4 +345,4 @@
 [postinstall]
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index ab03755..db65c14 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,6 +7,7 @@
 # absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
+dir.system = /product/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 197047d..d3f038e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -227,6 +227,8 @@
 
     # pstore/ramoops previous console log
     mount pstore pstore /sys/fs/pstore nodev noexec nosuid
+    chown system log /sys/fs/pstore
+    chmod 0550 /sys/fs/pstore
     chown system log /sys/fs/pstore/console-ramoops
     chmod 0440 /sys/fs/pstore/console-ramoops
     chown system log /sys/fs/pstore/console-ramoops-0
@@ -316,8 +318,8 @@
     start vndservicemanager
 
     # Once everything is setup, no need to modify /.
-    # The bind+ro combination avoids modifying any other mount flags.
-    mount rootfs rootfs / remount bind ro
+    # The bind+remount combination allows this to work in containers.
+    mount rootfs rootfs / remount bind ro nodev
     # Mount shared so changes propagate into child namespaces
     mount rootfs rootfs / shared rec
     # Mount default storage into root namespace
@@ -716,6 +718,9 @@
 
 on property:security.perf_harden=0
     write /proc/sys/kernel/perf_event_paranoid 1
+    write /proc/sys/kernel/perf_event_max_sample_rate ${debug.perf_event_max_sample_rate:-100000}
+    write /proc/sys/kernel/perf_cpu_time_max_percent ${debug.perf_cpu_time_max_percent:-25}
+    write /proc/sys/kernel/perf_event_mlock_kb ${debug.perf_event_mlock_kb:-516}
 
 on property:security.perf_harden=1
     write /proc/sys/kernel/perf_event_paranoid 3
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index b03d83b..2f85dec 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,3 +1,5 @@
+firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
+
 subsystem adf
     devname uevent_devname
 
diff --git a/run-as/Android.bp b/run-as/Android.bp
new file mode 100644
index 0000000..840a43c
--- /dev/null
+++ b/run-as/Android.bp
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+    name: "run-as",
+    srcs: [
+        "run-as.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libselinux",
+        "libpackagelistparser",
+        "libminijail",
+    ],
+}
diff --git a/run-as/Android.mk b/run-as/Android.mk
deleted file mode 100644
index 7111fbe..0000000
--- a/run-as/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_MODULE := run-as
-LOCAL_SHARED_LIBRARIES := libselinux libpackagelistparser libminijail
-LOCAL_SRC_FILES := run-as.cpp
-include $(BUILD_EXECUTABLE)
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index b27cfad..d005ecf 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -28,6 +28,7 @@
 #include <libminijail.h>
 #include <scoped_minijail.h>
 
+#include <android-base/properties.h>
 #include <packagelistparser/packagelistparser.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
@@ -40,6 +41,7 @@
 //  The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file
 //  capabilities, but will check the following:
 //
+//  - that the ro.boot.disable_runas property is not set
 //  - that it is invoked from the 'shell' or 'root' user (abort otherwise)
 //  - that '<package-name>' is the name of an installed and debuggable package
 //  - that the package's data directory is well-formed
@@ -139,6 +141,12 @@
     error(1, 0, "only 'shell' or 'root' users can run this program");
   }
 
+  // Some devices can disable running run-as, such as Chrome OS when running in
+  // non-developer mode.
+  if (android::base::GetBoolProperty("ro.boot.disable_runas", false)) {
+      error(1, 0, "run-as is disabled from the kernel commandline");
+  }
+
   char* pkgname = argv[1];
   int cmd_argv_offset = 2;
 
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 2e42b70..7834dd5 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -1,21 +1,46 @@
 phony {
     name: "shell_and_utilities",
     required: [
+        "shell_and_utilities_system",
+        "shell_and_utilities_recovery",
+        "shell_and_utilities_vendor",
+    ],
+}
+
+phony {
+    name: "shell_and_utilities_system",
+    required: [
         "awk",
-        "awk_vendor",
         "bzip2",
         "grep",
-        "grep_vendor",
         "logwrapper",
-        "logwrapper_vendor",
         "mkshrc",
-        "mkshrc_vendor",
+        "newfs_msdos",
         "reboot",
         "sh",
-        "sh_vendor",
         "toolbox",
-        "toolbox_vendor",
         "toybox",
+    ],
+}
+
+phony {
+    name: "shell_and_utilities_recovery",
+    required: [
+        "sh.recovery",
+        "toolbox.recovery",
+        "toybox.recovery",
+    ],
+}
+
+phony {
+    name: "shell_and_utilities_vendor",
+    required: [
+        "awk_vendor",
+        "grep_vendor",
+        "logwrapper_vendor",
+        "mkshrc_vendor",
+        "sh_vendor",
+        "toolbox_vendor",
         "toybox_vendor",
     ],
 }
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 678b853..e310e6b 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -18,11 +18,9 @@
 (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.
 
-Not everything is provided by toybox, though. We currently still use
-the BSD dd and grep (because the toybox versions are still unfinished),
-and for the bzip2 command-line tools we use the ones that are part of
-the bzip2 distribution. The awk added in Android P is Brian Kernighan's
-"one true" awk.
+Not everything is provided by toybox, though. For the bzip2 command-line tools
+we use the ones that are part of the bzip2 distribution. The awk added in
+Android P is Brian Kernighan's "one true" awk.
 
 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,
@@ -195,13 +193,15 @@
 Android Q
 ---------
 
+BSD: fsck\_msdos newfs\_msdos
+
 bzip2: bzcat bzip2 bunzip2
 
 one-true-awk: awk
 
 PCRE: egrep fgrep grep
 
-toolbox: getevent getprop newfs\_msdos
+toolbox: getevent getprop
 
 toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
 chroot chrt cksum clear cmp comm cp cpio cut date dd df diff dirname
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 077f542..fc51705 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -26,7 +26,6 @@
         "toolbox.c",
         "getevent.c",
         "getprop.cpp",
-        "newfs_msdos.c",
     ],
     generated_headers: [
         "toolbox_input_labels",
@@ -39,13 +38,13 @@
     symlinks: [
         "getevent",
         "getprop",
-        "newfs_msdos",
     ],
 }
 
 cc_binary {
     name: "toolbox",
     defaults: ["toolbox_binary_defaults"],
+    recovery_available: true,
 }
 
 cc_binary {
@@ -61,10 +60,3 @@
     defaults: ["toolbox_defaults"],
     srcs: ["r.c"],
 }
-
-cc_binary_host {
-    name: "newfs_msdos",
-    defaults: ["toolbox_defaults"],
-    srcs: ["newfs_msdos.c"],
-    cflags: ["-Dnewfs_msdos_main=main"]
-}
diff --git a/toolbox/MODULE_LICENSE_BSD b/toolbox/MODULE_LICENSE_APACHE2
similarity index 100%
rename from toolbox/MODULE_LICENSE_BSD
rename to toolbox/MODULE_LICENSE_APACHE2
diff --git a/toolbox/NOTICE b/toolbox/NOTICE
index e9ab58d..8e8a91c 100644
--- a/toolbox/NOTICE
+++ b/toolbox/NOTICE
@@ -1,4 +1,4 @@
-Copyright (C) 2010 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,974 +14,3 @@
 
 -------------------------------------------------------------------
 
-Copyright (C) 2014, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1987, 1993
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1987, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Jeffrey Mogul.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-David Hitz of Auspex Systems Inc.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994, 2003
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Kevin Fall.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Chris Newcomb.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Ken Smith of The State University of New York at Buffalo.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1990, 1993, 1994, 2003
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Keith Muller of the University of California, San Diego and Lance
-Visser of Convex Computer Corporation.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1992, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
-NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1998 Robert Nordier
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
-OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
-Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Luke Mewburn.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2007 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Luke Mewburn.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2008, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
-   may be used to endorse or promote products derived from this
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2009, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
-   may be used to endorse or promote products derived from this
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2010 The NetBSD Foundation, Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2010, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
-   may be used to endorse or promote products derived from this
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2012, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
-   may be used to endorse or promote products derived from this
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2013, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
-   may be used to endorse or promote products derived from this
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2014, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
-   may be used to endorse or promote products derived from this
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
diff --git a/toolbox/getprop.cpp b/toolbox/getprop.cpp
index 9e324a0..ca345cb 100644
--- a/toolbox/getprop.cpp
+++ b/toolbox/getprop.cpp
@@ -1,18 +1,18 @@
-//
-// 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.
-//
+/*
+ * 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 <getopt.h>
 #include <sys/system_properties.h>
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
deleted file mode 100644
index 5fc8b02..0000000
--- a/toolbox/newfs_msdos.c
+++ /dev/null
@@ -1,1108 +0,0 @@
-/*
- * Copyright (c) 1998 Robert Nordier
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef lint
-static const char rcsid[] =
-        "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.33 2009/04/11 14:56:29 ed Exp $";
-#endif /* not lint */
-
-#include <sys/param.h>
-
-#ifdef __APPLE__
-#elif defined(ANDROID)
-#include <linux/fs.h>
-#include <linux/hdreg.h>
-#include <stdarg.h>
-#include <sys/ioctl.h>
-#else
-#include <sys/fdcio.h>
-#include <sys/disk.h>
-#include <sys/disklabel.h>
-#include <sys/mount.h>
-#endif
-
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <paths.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
-#define MAXU16   0xffff     /* maximum unsigned 16-bit quantity */
-#define BPN      4          /* bits per nibble */
-#define NPB      2          /* nibbles per byte */
-
-#define DOSMAGIC  0xaa55    /* DOS magic number */
-#define MINBPS    512       /* minimum bytes per sector */
-#define MAXSPC    128       /* maximum sectors per cluster */
-#define MAXNFT    16        /* maximum number of FATs */
-#define DEFBLK    4096      /* default block size */
-#define DEFBLK16  2048      /* default block size FAT16 */
-#define DEFRDE    512       /* default root directory entries */
-#define RESFTE    2         /* reserved FAT entries */
-#define MINCLS12  1         /* minimum FAT12 clusters */
-#define MINCLS16  0x1000    /* minimum FAT16 clusters */
-#define MINCLS32  2         /* minimum FAT32 clusters */
-#define MAXCLS12  0xfed     /* maximum FAT12 clusters */
-#define MAXCLS16  0xfff5    /* maximum FAT16 clusters */
-#define MAXCLS32  0xffffff5 /* maximum FAT32 clusters */
-
-#define mincls(fat)  ((fat) == 12 ? MINCLS12 :    \
-                      (fat) == 16 ? MINCLS16 :    \
-                                        MINCLS32)
-
-#define maxcls(fat)  ((fat) == 12 ? MAXCLS12 :    \
-                      (fat) == 16 ? MAXCLS16 :    \
-                                        MAXCLS32)
-
-#define mk1(p, x)                           \
-    (p) = (u_int8_t)(x)
-
-#define mk2(p, x)                           \
-    (p)[0] = (u_int8_t)(x),                 \
-    (p)[1] = (u_int8_t)((x) >> 010)
-
-#define mk4(p, x)                           \
-    (p)[0] = (u_int8_t)(x),                 \
-    (p)[1] = (u_int8_t)((x) >> 010),        \
-    (p)[2] = (u_int8_t)((x) >> 020),        \
-    (p)[3] = (u_int8_t)((x) >> 030)
-
-#define argto1(arg, lo, msg)  argtou(arg, lo, 0xff, msg)
-#define argto2(arg, lo, msg)  argtou(arg, lo, 0xffff, msg)
-#define argto4(arg, lo, msg)  argtou(arg, lo, 0xffffffff, msg)
-#define argtox(arg, lo, msg)  argtou(arg, lo, UINT_MAX, msg)
-
-struct bs {
-    u_int8_t jmp[3];        /* bootstrap entry point */
-    u_int8_t oem[8];        /* OEM name and version */
-};
-
-struct bsbpb {
-    u_int8_t bps[2];    /* bytes per sector */
-    u_int8_t spc;       /* sectors per cluster */
-    u_int8_t res[2];    /* reserved sectors */
-    u_int8_t nft;       /* number of FATs */
-    u_int8_t rde[2];    /* root directory entries */
-    u_int8_t sec[2];    /* total sectors */
-    u_int8_t mid;       /* media descriptor */
-    u_int8_t spf[2];    /* sectors per FAT */
-    u_int8_t spt[2];    /* sectors per track */
-    u_int8_t hds[2];    /* drive heads */
-    u_int8_t hid[4];    /* hidden sectors */
-    u_int8_t bsec[4];   /* big total sectors */
-};
-
-struct bsxbpb {
-    u_int8_t bspf[4];       /* big sectors per FAT */
-    u_int8_t xflg[2];       /* FAT control flags */
-    u_int8_t vers[2];       /* file system version */
-    u_int8_t rdcl[4];       /* root directory start cluster */
-    u_int8_t infs[2];       /* file system info sector */
-    u_int8_t bkbs[2];       /* backup boot sector */
-    u_int8_t rsvd[12];      /* reserved */
-};
-
-struct bsx {
-    u_int8_t drv;           /* drive number */
-    u_int8_t rsvd;          /* reserved */
-    u_int8_t sig;           /* extended boot signature */
-    u_int8_t volid[4];      /* volume ID number */
-    u_int8_t label[11];     /* volume label */
-    u_int8_t type[8];       /* file system type */
-};
-
-struct de {
-    u_int8_t namext[11];    /* name and extension */
-    u_int8_t attr;          /* attributes */
-    u_int8_t rsvd[10];      /* reserved */
-    u_int8_t time[2];       /* creation time */
-    u_int8_t date[2];       /* creation date */
-    u_int8_t clus[2];       /* starting cluster */
-    u_int8_t size[4];       /* size */
-};
-
-struct bpb {
-    u_int bps;          /* bytes per sector */
-    u_int spc;          /* sectors per cluster */
-    u_int res;          /* reserved sectors */
-    u_int nft;          /* number of FATs */
-    u_int rde;          /* root directory entries */
-    u_int sec;          /* total sectors */
-    u_int mid;          /* media descriptor */
-    u_int spf;          /* sectors per FAT */
-    u_int spt;          /* sectors per track */
-    u_int hds;          /* drive heads */
-    u_int hid;          /* hidden sectors */
-    u_int bsec;         /* big total sectors */
-    u_int bspf;         /* big sectors per FAT */
-    u_int rdcl;         /* root directory start cluster */
-    u_int infs;         /* file system info sector */
-    u_int bkbs;         /* backup boot sector */
-};
-
-#define BPBGAP 0, 0, 0, 0, 0, 0
-
-static struct {
-    const char *name;
-    struct bpb bpb;
-} const stdfmt[] = {
-    {"160",  {512, 1, 1, 2,  64,  320, 0xfe, 1,  8, 1, BPBGAP}},
-    {"180",  {512, 1, 1, 2,  64,  360, 0xfc, 2,  9, 1, BPBGAP}},
-    {"320",  {512, 2, 1, 2, 112,  640, 0xff, 1,  8, 2, BPBGAP}},
-    {"360",  {512, 2, 1, 2, 112,  720, 0xfd, 2,  9, 2, BPBGAP}},
-    {"640",  {512, 2, 1, 2, 112, 1280, 0xfb, 2,  8, 2, BPBGAP}},
-    {"720",  {512, 2, 1, 2, 112, 1440, 0xf9, 3,  9, 2, BPBGAP}},
-    {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}},
-    {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2,  8, 2, BPBGAP}},
-    {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}},
-    {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}}
-};
-
-static const u_int8_t bootcode[] = {
-    0xfa,               /* cli             */
-    0x31, 0xc0,         /* xor    ax,ax    */
-    0x8e, 0xd0,         /* mov    ss,ax    */
-    0xbc, 0x00, 0x7c,   /* mov    sp,7c00h */
-    0xfb,               /* sti             */
-    0x8e, 0xd8,         /* mov    ds,ax    */
-    0xe8, 0x00, 0x00,   /* call   $ + 3    */
-    0x5e,               /* pop    si       */
-    0x83, 0xc6, 0x19,   /* add    si,+19h  */
-    0xbb, 0x07, 0x00,   /* mov    bx,0007h */
-    0xfc,               /* cld             */
-    0xac,               /* lodsb           */
-    0x84, 0xc0,         /* test   al,al    */
-    0x74, 0x06,         /* jz     $ + 8    */
-    0xb4, 0x0e,         /* mov    ah,0eh   */
-    0xcd, 0x10,         /* int    10h      */
-    0xeb, 0xf5,         /* jmp    $ - 9    */
-    0x30, 0xe4,         /* xor    ah,ah    */
-    0xcd, 0x16,         /* int    16h      */
-    0xcd, 0x19,         /* int    19h      */
-    0x0d, 0x0a,
-    'N', 'o', 'n', '-', 's', 'y', 's', 't',
-    'e', 'm', ' ', 'd', 'i', 's', 'k',
-    0x0d, 0x0a,
-    'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
-    'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
-    ' ', 'r', 'e', 'b', 'o', 'o', 't',
-    0x0d, 0x0a,
-    0
-};
-
-static void check_mounted(const char *, mode_t);
-static void getstdfmt(const char *, struct bpb *);
-static void getdiskinfo(int, const char *, const char *, int, struct bpb *);
-static void print_bpb(struct bpb *);
-static u_int ckgeom(const char *, u_int, const char *);
-static u_int argtou(const char *, u_int, u_int, const char *);
-static off_t argtooff(const char *, const char *);
-static int oklabel(const char *);
-static void mklabel(u_int8_t *, const char *);
-static void setstr(u_int8_t *, const char *, size_t);
-static void usage(void);
-
-/*
- * Construct a FAT12, FAT16, or FAT32 file system.
- */
-int newfs_msdos_main(int argc, char *argv[])
-{
-    static const char opts[] = "@:NAB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
-    const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL;
-    u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0;
-    u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0;
-    u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0;
-    u_int opt_A = 0;
-    int opt_N = 0;
-    int Iflag = 0, mflag = 0, oflag = 0;
-    char buf[MAXPATHLEN];
-    struct stat sb;
-    struct timeval tv;
-    struct bpb bpb;
-    struct tm *tm;
-    struct bs *bs;
-    struct bsbpb *bsbpb;
-    struct bsxbpb *bsxbpb;
-    struct bsx *bsx;
-    struct de *de;
-    u_int8_t *img;
-    const char *fname, *dtype, *bname;
-    ssize_t n;
-    time_t now;
-    u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
-    u_int extra_res, alignment=0, set_res, set_spf, set_spc, tempx, attempts=0;
-    int ch, fd, fd1;
-    off_t opt_create = 0, opt_ofs = 0;
-
-    while ((ch = getopt(argc, argv, opts)) != -1)
-        switch (ch) {
-        case '@':
-            opt_ofs = argtooff(optarg, "offset");
-            break;
-        case 'N':
-            opt_N = 1;
-            break;
-        case 'A':
-            opt_A = 1;
-            break;
-        case 'B':
-            opt_B = optarg;
-            break;
-        case 'C':
-            opt_create = argtooff(optarg, "create size");
-            break;
-        case 'F':
-            if (strcmp(optarg, "12") &&  strcmp(optarg, "16") && strcmp(optarg, "32"))
-                errx(1, "%s: bad FAT type", optarg);
-            opt_F = atoi(optarg);
-            break;
-        case 'I':
-            opt_I = argto4(optarg, 0, "volume ID");
-            Iflag = 1;
-            break;
-        case 'L':
-            if (!oklabel(optarg))
-                errx(1, "%s: bad volume label", optarg);
-            opt_L = optarg;
-            break;
-        case 'O':
-            if (strlen(optarg) > 8)
-                errx(1, "%s: bad OEM string", optarg);
-            opt_O = optarg;
-            break;
-        case 'S':
-            opt_S = argto2(optarg, 1, "bytes/sector");
-            break;
-        case 'a':
-            opt_a = argto4(optarg, 1, "sectors/FAT");
-            break;
-        case 'b':
-            opt_b = argtox(optarg, 1, "block size");
-            opt_c = 0;
-            break;
-        case 'c':
-            opt_c = argto1(optarg, 1, "sectors/cluster");
-            opt_b = 0;
-            break;
-        case 'e':
-            opt_e = argto2(optarg, 1, "directory entries");
-            break;
-        case 'f':
-            opt_f = optarg;
-            break;
-        case 'h':
-            opt_h = argto2(optarg, 1, "drive heads");
-            break;
-        case 'i':
-            opt_i = argto2(optarg, 1, "info sector");
-            break;
-        case 'k':
-            opt_k = argto2(optarg, 1, "backup sector");
-            break;
-        case 'm':
-            opt_m = argto1(optarg, 0, "media descriptor");
-            mflag = 1;
-            break;
-        case 'n':
-            opt_n = argto1(optarg, 1, "number of FATs");
-            break;
-        case 'o':
-            opt_o = argto4(optarg, 0, "hidden sectors");
-            oflag = 1;
-            break;
-        case 'r':
-            opt_r = argto2(optarg, 1, "reserved sectors");
-            break;
-        case 's':
-            opt_s = argto4(optarg, 1, "file system size");
-            break;
-        case 'u':
-            opt_u = argto2(optarg, 1, "sectors/track");
-            break;
-        default:
-            usage();
-        }
-    argc -= optind;
-    argv += optind;
-    if (argc < 1 || argc > 2)
-        usage();
-    fname = *argv++;
-    if (!opt_create && !strchr(fname, '/')) {
-        snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
-        if (!(fname = strdup(buf)))
-            err(1, "%s", buf);
-    }
-    dtype = *argv;
-    if (opt_A) {
-        if (opt_r)
-            errx(1, "align (-A) is incompatible with -r");
-        if (opt_N)
-            errx(1, "align (-A) is incompatible with -N");
-    }
-    if (opt_create) {
-        if (opt_N)
-            errx(1, "create (-C) is incompatible with -N");
-        fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
-        if (fd == -1)
-            errx(1, "failed to create %s", fname);
-        if (ftruncate(fd, opt_create))
-            errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create);
-    } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1)
-        err(1, "%s", fname);
-    if (fstat(fd, &sb))
-        err(1, "%s", fname);
-    if (opt_create) {
-        if (!S_ISREG(sb.st_mode))
-            warnx("warning, %s is not a regular file", fname);
-    } else {
-        if (!S_ISCHR(sb.st_mode))
-            warnx("warning, %s is not a character device", fname);
-    }
-    if (!opt_N)
-        check_mounted(fname, sb.st_mode);
-    if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET))
-        errx(1, "cannot seek to %jd", (intmax_t)opt_ofs);
-    memset(&bpb, 0, sizeof(bpb));
-    if (opt_f) {
-        getstdfmt(opt_f, &bpb);
-        bpb.bsec = bpb.sec;
-        bpb.sec = 0;
-        bpb.bspf = bpb.spf;
-        bpb.spf = 0;
-    }
-    if (opt_h)
-        bpb.hds = opt_h;
-    if (opt_u)
-        bpb.spt = opt_u;
-    if (opt_S)
-        bpb.bps = opt_S;
-    if (opt_s)
-        bpb.bsec = opt_s;
-    if (oflag)
-        bpb.hid = opt_o;
-    if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) {
-        off_t delta;
-        getdiskinfo(fd, fname, dtype, oflag, &bpb);
-        if (opt_s) {
-            bpb.bsec = opt_s;
-        }
-        bpb.bsec -= (opt_ofs / bpb.bps);
-        delta = bpb.bsec % bpb.spt;
-        if (delta != 0) {
-            warnx("trim %d sectors from %d to adjust to a multiple of %d",
-                  (int)delta, bpb.bsec, bpb.spt);
-            bpb.bsec -= delta;
-        }
-        if (bpb.spc == 0) {    /* set defaults */
-            if (bpb.bsec <= 6000)    /* about 3MB -> 512 bytes */
-                bpb.spc = 1;
-            else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */
-                bpb.spc = 8;
-            else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */
-                bpb.spc = 16;
-            else if (bpb.bsec <= (1<<22)) /* 2G -> 16k, some versions of windows
-                                         require a minimum of 65527 clusters */
-                bpb.spc = 32;
-            else
-                bpb.spc = 64;        /* otherwise 32k */
-        }
-    }
-    if (!powerof2(bpb.bps))
-        errx(1, "bytes/sector (%u) is not a power of 2", bpb.bps);
-    if (bpb.bps < MINBPS)
-        errx(1, "bytes/sector (%u) is too small; minimum is %u",
-             bpb.bps, MINBPS);
-    if (!(fat = opt_F)) {
-        if (opt_f)
-            fat = 12;
-        else if (!opt_e && (opt_i || opt_k))
-            fat = 32;
-    }
-    if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
-        errx(1, "-%c is not a legal FAT%s option",
-             fat == 32 ? 'e' : opt_i ? 'i' : 'k',
-                     fat == 32 ? "32" : "12/16");
-    if (opt_f && fat == 32)
-        bpb.rde = 0;
-    if (opt_b) {
-        if (!powerof2(opt_b))
-            errx(1, "block size (%u) is not a power of 2", opt_b);
-        if (opt_b < bpb.bps)
-            errx(1, "block size (%u) is too small; minimum is %u",
-                 opt_b, bpb.bps);
-        if (opt_b > bpb.bps * MAXSPC)
-            errx(1, "block size (%u) is too large; maximum is %u", opt_b, bpb.bps * MAXSPC);
-        bpb.spc = opt_b / bpb.bps;
-    }
-    if (opt_c) {
-        if (!powerof2(opt_c))
-            errx(1, "sectors/cluster (%u) is not a power of 2", opt_c);
-        bpb.spc = opt_c;
-    }
-    if (opt_r)
-        bpb.res = opt_r;
-    if (opt_n) {
-        if (opt_n > MAXNFT)
-            errx(1, "number of FATs (%u) is too large; maximum is %u", opt_n, MAXNFT);
-        bpb.nft = opt_n;
-    }
-    if (opt_e)
-        bpb.rde = opt_e;
-    if (mflag) {
-        if (opt_m < 0xf0)
-            errx(1, "illegal media descriptor (%#x)", opt_m);
-        bpb.mid = opt_m;
-    }
-    if (opt_a)
-        bpb.bspf = opt_a;
-    if (opt_i)
-        bpb.infs = opt_i;
-    if (opt_k)
-        bpb.bkbs = opt_k;
-    bss = 1;
-    bname = NULL;
-    fd1 = -1;
-    if (opt_B) {
-        bname = opt_B;
-        if (!strchr(bname, '/')) {
-            snprintf(buf, sizeof(buf), "/boot/%s", bname);
-            if (!(bname = strdup(buf)))
-                err(1, "%s", buf);
-        }
-        if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
-            err(1, "%s", bname);
-        if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
-                sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16)
-            errx(1, "%s: inappropriate file type or format", bname);
-        bss = sb.st_size / bpb.bps;
-    }
-    if (!bpb.nft)
-        bpb.nft = 2;
-    if (!fat) {
-        if (bpb.bsec < (bpb.res ? bpb.res : bss) +
-                howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
-                        ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
-                        bpb.nft +
-                        howmany(bpb.rde ? bpb.rde : DEFRDE,
-                                bpb.bps / sizeof(struct de)) +
-                                (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
-                                (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
-            fat = 12;
-        else if (bpb.rde || bpb.bsec <
-                (bpb.res ? bpb.res : bss) +
-                howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
-                howmany(DEFRDE, bpb.bps / sizeof(struct de)) +
-                (MAXCLS16 + 1) *
-                (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
-            fat = 16;
-        else
-            fat = 32;
-    }
-    x = bss;
-    if (fat == 32) {
-        if (!bpb.infs) {
-            if (x == MAXU16 || x == bpb.bkbs)
-                errx(1, "no room for info sector");
-            bpb.infs = x;
-        }
-        if (bpb.infs != MAXU16 && x <= bpb.infs)
-            x = bpb.infs + 1;
-        if (!bpb.bkbs) {
-            if (x == MAXU16)
-                errx(1, "no room for backup sector");
-            bpb.bkbs = x;
-        } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs)
-            errx(1, "backup sector would overwrite info sector");
-        if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
-            x = bpb.bkbs + 1;
-    }
-
-    extra_res = 0;
-    set_res = !bpb.res;
-    set_spf = !bpb.bspf;
-    set_spc = !bpb.spc;
-    tempx = x;
-    /*
-     * Attempt to align if opt_A is set. This is done by increasing the number
-     * of reserved blocks. This can cause other factors to change, which can in
-     * turn change the alignment. This should take at most 2 iterations, as
-     * increasing the reserved amount may cause the FAT size to decrease by 1,
-     * requiring another nft reserved blocks. If spc changes, it will
-     * be half of its previous size, and thus will not throw off alignment.
-     */
-    do {
-        x = tempx;
-        if (set_res)
-            bpb.res = (fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x) + extra_res;
-        else if (bpb.res < x)
-            errx(1, "too few reserved sectors");
-        if (fat != 32 && !bpb.rde)
-            bpb.rde = DEFRDE;
-        rds = howmany(bpb.rde, bpb.bps / sizeof(struct de));
-        if (set_spc)
-            for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
-                    bpb.spc < MAXSPC &&
-                    bpb.res +
-                    howmany((RESFTE + maxcls(fat)) * (fat / BPN),
-                            bpb.bps * NPB) * bpb.nft +
-                            rds +
-                            (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec;
-                    bpb.spc <<= 1);
-        if (fat != 32 && bpb.bspf > MAXU16)
-            errx(1, "too many sectors/FAT for FAT12/16");
-        x1 = bpb.res + rds;
-        x = bpb.bspf ? bpb.bspf : 1;
-        if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec)
-            errx(1, "meta data exceeds file system size");
-        x1 += x * bpb.nft;
-        x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
-                (bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft);
-        x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), bpb.bps * NPB);
-        if (set_spf) {
-            if (!bpb.bspf) {
-                bpb.bspf = x2;
-            }
-            x1 += (bpb.bspf - 1) * bpb.nft;
-        }
-        if(set_res) {
-            /* attempt to align root directory */
-            alignment = (bpb.res + bpb.bspf * bpb.nft) % bpb.spc;
-            extra_res += bpb.spc - alignment;
-        }
-        attempts++;
-    } while(opt_A && alignment != 0 && attempts < 2);
-    if (alignment != 0)
-        warnx("warning: Alignment failed.");
-
-    cls = (bpb.bsec - x1) / bpb.spc;
-    x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE;
-    if (cls > x)
-        cls = x;
-    if (bpb.bspf < x2)
-        warnx("warning: sectors/FAT limits file system to %u clusters", cls);
-    if (cls < mincls(fat))
-        errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat, mincls(fat));
-    if (cls > maxcls(fat)) {
-        cls = maxcls(fat);
-        bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
-        warnx("warning: FAT type limits file system to %u sectors", bpb.bsec);
-    }
-    printf("%s: %u sector%s in %u FAT%u cluster%s (%u bytes/cluster)\n",
-           fname, cls * bpb.spc, cls * bpb.spc == 1 ? "" : "s", cls, fat,
-           cls == 1 ? "" : "s", bpb.bps * bpb.spc);
-    if (!bpb.mid)
-        bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
-    if (fat == 32)
-        bpb.rdcl = RESFTE;
-    if (bpb.hid + bpb.bsec <= MAXU16) {
-        bpb.sec = bpb.bsec;
-        bpb.bsec = 0;
-    }
-    if (fat != 32) {
-        bpb.spf = bpb.bspf;
-        bpb.bspf = 0;
-    }
-    print_bpb(&bpb);
-    if (!opt_N) {
-        gettimeofday(&tv, NULL);
-        now = tv.tv_sec;
-        tm = localtime(&now);
-        if (!(img = malloc(bpb.bps)))
-            err(1, "%u", bpb.bps);
-        dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
-        for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) {
-            x = lsn;
-            if (opt_B && fat == 32 && bpb.bkbs != MAXU16 && bss <= bpb.bkbs && x >= bpb.bkbs) {
-                x -= bpb.bkbs;
-                if (!x && lseek(fd1, opt_ofs, SEEK_SET))
-                    err(1, "%s", bname);
-            }
-            if (opt_B && x < bss) {
-                if ((n = read(fd1, img, bpb.bps)) == -1)
-                    err(1, "%s", bname);
-                if ((unsigned)n != bpb.bps)
-                    errx(1, "%s: can't read sector %u", bname, x);
-            } else
-                memset(img, 0, bpb.bps);
-            if (!lsn || (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
-                x1 = sizeof(struct bs);
-                bsbpb = (struct bsbpb *)(img + x1);
-                mk2(bsbpb->bps, bpb.bps);
-                mk1(bsbpb->spc, bpb.spc);
-                mk2(bsbpb->res, bpb.res);
-                mk1(bsbpb->nft, bpb.nft);
-                mk2(bsbpb->rde, bpb.rde);
-                mk2(bsbpb->sec, bpb.sec);
-                mk1(bsbpb->mid, bpb.mid);
-                mk2(bsbpb->spf, bpb.spf);
-                mk2(bsbpb->spt, bpb.spt);
-                mk2(bsbpb->hds, bpb.hds);
-                mk4(bsbpb->hid, bpb.hid);
-                mk4(bsbpb->bsec, bpb.bsec);
-                x1 += sizeof(struct bsbpb);
-                if (fat == 32) {
-                    bsxbpb = (struct bsxbpb *)(img + x1);
-                    mk4(bsxbpb->bspf, bpb.bspf);
-                    mk2(bsxbpb->xflg, 0);
-                    mk2(bsxbpb->vers, 0);
-                    mk4(bsxbpb->rdcl, bpb.rdcl);
-                    mk2(bsxbpb->infs, bpb.infs);
-                    mk2(bsxbpb->bkbs, bpb.bkbs);
-                    x1 += sizeof(struct bsxbpb);
-                }
-                bsx = (struct bsx *)(img + x1);
-                mk1(bsx->sig, 0x29);
-                if (Iflag)
-                    x = opt_I;
-                else
-                    x = (((u_int)(1 + tm->tm_mon) << 8 |
-                            (u_int)tm->tm_mday) +
-                            ((u_int)tm->tm_sec << 8 |
-                                    (u_int)(tv.tv_usec / 10))) << 16 |
-                                    ((u_int)(1900 + tm->tm_year) +
-                                            ((u_int)tm->tm_hour << 8 |
-                                                    (u_int)tm->tm_min));
-                mk4(bsx->volid, x);
-                mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
-                snprintf(buf, sizeof(buf), "FAT%u", fat);
-                setstr(bsx->type, buf, sizeof(bsx->type));
-                if (!opt_B) {
-                    x1 += sizeof(struct bsx);
-                    bs = (struct bs *)img;
-                    mk1(bs->jmp[0], 0xeb);
-                    mk1(bs->jmp[1], x1 - 2);
-                    mk1(bs->jmp[2], 0x90);
-                    setstr(bs->oem, opt_O ? opt_O : "BSD  4.4",
-                            sizeof(bs->oem));
-                    memcpy(img + x1, bootcode, sizeof(bootcode));
-                    mk2(img + MINBPS - 2, DOSMAGIC);
-                }
-            } else if (fat == 32 && bpb.infs != MAXU16 &&
-                    (lsn == bpb.infs || (bpb.bkbs != MAXU16 &&
-                                    lsn == bpb.bkbs + bpb.infs))) {
-                mk4(img, 0x41615252);
-                mk4(img + MINBPS - 28, 0x61417272);
-                mk4(img + MINBPS - 24, 0xffffffff);
-                mk4(img + MINBPS - 20, bpb.rdcl);
-                mk2(img + MINBPS - 2, DOSMAGIC);
-            } else if (lsn >= bpb.res && lsn < dir &&
-                    !((lsn - bpb.res) % (bpb.spf ? bpb.spf : bpb.bspf))) {
-                mk1(img[0], bpb.mid);
-                for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
-                    mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
-            } else if (lsn == dir && opt_L) {
-                de = (struct de *)img;
-                mklabel(de->namext, opt_L);
-                mk1(de->attr, 050);
-                x = (u_int)tm->tm_hour << 11 |
-                        (u_int)tm->tm_min << 5 |
-                        (u_int)tm->tm_sec >> 1;
-                mk2(de->time, x);
-                x = (u_int)(tm->tm_year - 80) << 9 |
-                        (u_int)(tm->tm_mon + 1) << 5 |
-                        (u_int)tm->tm_mday;
-                mk2(de->date, x);
-            }
-            if ((n = write(fd, img, bpb.bps)) == -1)
-                err(1, "%s", fname);
-            if ((unsigned)n != bpb.bps) {
-                errx(1, "%s: can't write sector %u", fname, lsn);
-                exit(1);
-            }
-        }
-        free(img);
-    }
-    return 0;
-}
-
-/*
- * Exit with error if file system is mounted.
- */
-static void check_mounted(const char *fname, mode_t mode)
-{
-#ifdef ANDROID
-    warnx("Skipping mount checks");
-#else
-    struct statfs *mp;
-    const char *s1, *s2;
-    size_t len;
-    int n, r;
-
-    if (!(n = getmntinfo(&mp, MNT_NOWAIT)))
-        err(1, "getmntinfo");
-    len = strlen(_PATH_DEV);
-    s1 = fname;
-    if (!strncmp(s1, _PATH_DEV, len))
-        s1 += len;
-    r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
-    for (; n--; mp++) {
-        s2 = mp->f_mntfromname;
-        if (!strncmp(s2, _PATH_DEV, len))
-            s2 += len;
-        if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || !strcmp(s1, s2))
-            errx(1, "%s is mounted on %s", fname, mp->f_mntonname);
-    }
-#endif
-}
-
-/*
- * Get a standard format.
- */
-static void getstdfmt(const char *fmt, struct bpb *bpb)
-{
-    u_int x, i;
-
-    x = sizeof(stdfmt) / sizeof(stdfmt[0]);
-    for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
-    if (i == x)
-        errx(1, "%s: unknown standard format", fmt);
-    *bpb = stdfmt[i].bpb;
-}
-
-/*
- * Get disk slice, partition, and geometry information.
- */
-
-#ifdef __APPLE__
-static void getdiskinfo(__unused int fd, __unused const char* fname, __unused const char* dtype,
-                        __unused int oflag, __unused struct bpb* bpb) {}
-#elif ANDROID
-static void getdiskinfo(int fd, const char *fname, const char *dtype,
-                        __unused int oflag,struct bpb *bpb)
-{
-    struct hd_geometry geom;
-    u_long block_size;
-
-    if (ioctl(fd, BLKSSZGET, &bpb->bps)) {
-        fprintf(stderr, "Error getting bytes / sector (%s)\n", strerror(errno));
-        exit(1);
-    }
-
-    ckgeom(fname, bpb->bps, "bytes/sector");
-
-    if (ioctl(fd, BLKGETSIZE, &block_size)) {
-        fprintf(stderr, "Error getting blocksize (%s)\n", strerror(errno));
-        exit(1);
-    }
-
-    if (block_size > UINT32_MAX) {
-        fprintf(stderr, "Error blocksize too large: %lu\n", block_size);
-        exit(1);
-    }
-
-    bpb->bsec = (u_int)block_size;
-
-    if (ioctl(fd, HDIO_GETGEO, &geom)) {
-        fprintf(stderr, "Error getting gemoetry (%s) - trying sane values\n", strerror(errno));
-        geom.heads = 64;
-        geom.sectors = 63;
-    }
-
-    if (!geom.heads) {
-        printf("Bogus heads from kernel - setting sane value\n");
-        geom.heads = 64;
-    }
-
-    if (!geom.sectors) {
-        printf("Bogus sectors from kernel - setting sane value\n");
-        geom.sectors = 63;
-    }
-
-    bpb->spt = geom.sectors;
-    ckgeom(fname, bpb->spt, "sectors/track");
-
-    bpb->hds = geom.heads;
-    ckgeom(fname, bpb->hds, "drive heads");
-}
-
-#else
-
-static void getdiskinfo(int fd, const char *fname, const char *dtype,
-                        __unused int oflag, struct bpb *bpb)
-{
-    struct disklabel *lp, dlp;
-    struct fd_type type;
-    off_t ms, hs = 0;
-
-    lp = NULL;
-
-    /* If the user specified a disk type, try to use that */
-    if (dtype != NULL) {
-        lp = getdiskbyname(dtype);
-    }
-
-    /* Maybe it's a floppy drive */
-    if (lp == NULL) {
-        if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) {
-            struct stat st;
-
-            if (fstat(fd, &st))
-                err(1, "Cannot get disk size");
-            /* create a fake geometry for a file image */
-            ms = st.st_size;
-            dlp.d_secsize = 512;
-            dlp.d_nsectors = 63;
-            dlp.d_ntracks = 255;
-            dlp.d_secperunit = ms / dlp.d_secsize;
-            lp = &dlp;
-        } else if (ioctl(fd, FD_GTYPE, &type) != -1) {
-            dlp.d_secsize = 128 << type.secsize;
-            dlp.d_nsectors = type.sectrac;
-            dlp.d_ntracks = type.heads;
-            dlp.d_secperunit = ms / dlp.d_secsize;
-            lp = &dlp;
-        }
-    }
-
-    /* Maybe it's a fixed drive */
-    if (lp == NULL) {
-        if (ioctl(fd, DIOCGDINFO, &dlp) == -1) {
-            if (bpb->bps == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1)
-                errx(1, "Cannot get sector size, %s", strerror(errno));
-
-            /* XXX Should we use bpb->bps if it's set? */
-            dlp.d_secperunit = ms / dlp.d_secsize;
-
-            if (bpb->spt == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) {
-                warnx("Cannot get number of sectors per track, %s", strerror(errno));
-                dlp.d_nsectors = 63;
-            }
-            if (bpb->hds == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) {
-                warnx("Cannot get number of heads, %s", strerror(errno));
-                if (dlp.d_secperunit <= 63*1*1024)
-                    dlp.d_ntracks = 1;
-                else if (dlp.d_secperunit <= 63*16*1024)
-                    dlp.d_ntracks = 16;
-                else
-                    dlp.d_ntracks = 255;
-            }
-        }
-
-        hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
-        lp = &dlp;
-    }
-
-    if (bpb->bps == 0)
-        bpb->bps = ckgeom(fname, lp->d_secsize, "bytes/sector");
-    if (bpb->spt == 0)
-        bpb->spt = ckgeom(fname, lp->d_nsectors, "sectors/track");
-    if (bpb->hds == 0)
-        bpb->hds = ckgeom(fname, lp->d_ntracks, "drive heads");
-    if (bpb->bsec == 0)
-        bpb->bsec = lp->d_secperunit;
-    if (bpb->hid == 0)
-        bpb->hid = hs;
-}
-#endif
-
-/*
- * Print out BPB values.
- */
-static void print_bpb(struct bpb *bpb)
-{
-    printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
-           bpb->nft);
-    if (bpb->rde)
-        printf(" rde=%u", bpb->rde);
-    if (bpb->sec)
-        printf(" sec=%u", bpb->sec);
-    printf(" mid=%#x", bpb->mid);
-    if (bpb->spf)
-        printf(" spf=%u", bpb->spf);
-    printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
-    if (bpb->bsec)
-        printf(" bsec=%u", bpb->bsec);
-    if (!bpb->spf) {
-        printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
-        printf(" infs=");
-        printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
-        printf(" bkbs=");
-        printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
-    }
-    printf("\n");
-}
-
-/*
- * Check a disk geometry value.
- */
-static u_int ckgeom(const char *fname, u_int val, const char *msg)
-{
-    if (!val)
-        errx(1, "%s: no default %s", fname, msg);
-    if (val > MAXU16)
-        errx(1, "%s: illegal %s %d", fname, msg, val);
-    return val;
-}
-
-/*
- * Convert and check a numeric option argument.
- */
-static u_int argtou(const char *arg, u_int lo, u_int hi, const char *msg)
-{
-    char *s;
-    u_long x;
-
-    errno = 0;
-    x = strtoul(arg, &s, 0);
-    if (errno || !*arg || *s || x < lo || x > hi)
-        errx(1, "%s: bad %s", arg, msg);
-    return x;
-}
-
-/*
- * Same for off_t, with optional skmgpP suffix
- */
-static off_t argtooff(const char *arg, const char *msg)
-{
-    char *s;
-    off_t x;
-
-    x = strtoll(arg, &s, 0);
-    /* allow at most one extra char */
-    if (errno || x < 0 || (s[0] && s[1]) )
-        errx(1, "%s: bad %s", arg, msg);
-    if (*s) {    /* the extra char is the multiplier */
-        switch (*s) {
-            default:
-                errx(1, "%s: bad %s", arg, msg);
-                /* notreached */
-
-            case 's':       /* sector */
-            case 'S':
-                x <<= 9;    /* times 512 */
-                break;
-
-            case 'k':       /* kilobyte */
-            case 'K':
-                x <<= 10;   /* times 1024 */
-                break;
-
-            case 'm':       /* megabyte */
-            case 'M':
-                x <<= 20;   /* times 1024*1024 */
-                break;
-
-            case 'g':       /* gigabyte */
-            case 'G':
-                x <<= 30;   /* times 1024*1024*1024 */
-                break;
-
-            case 'p':       /* partition start */
-            case 'P':       /* partition start */
-            case 'l':       /* partition length */
-            case 'L':       /* partition length */
-                errx(1, "%s: not supported yet %s", arg, msg);
-                /* notreached */
-        }
-    }
-    return x;
-}
-
-/*
- * Check a volume label.
- */
-static int oklabel(const char *src)
-{
-    int c, i;
-
-    for (i = 0; i <= 11; i++) {
-        c = (u_char)*src++;
-        if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
-            break;
-    }
-    return i && !c;
-}
-
-/*
- * Make a volume label.
- */
-static void mklabel(u_int8_t *dest, const char *src)
-{
-    int c, i;
-
-    for (i = 0; i < 11; i++) {
-        c = *src ? toupper(*src++) : ' ';
-        *dest++ = !i && c == '\xe5' ? 5 : c;
-    }
-}
-
-/*
- * Copy string, padding with spaces.
- */
-static void setstr(u_int8_t *dest, const char *src, size_t len)
-{
-    while (len--)
-        *dest++ = *src ? *src++ : ' ';
-}
-
-/*
- * Print usage message.
- */
-static void usage(void)
-{
-    fprintf(stderr,
-            "usage: newfs_msdos [ -options ] special [disktype]\n"
-            "where the options are:\n"
-            "\t-@ create file system at specified offset\n"
-            "\t-A Attempt to cluster align root directory\n"
-            "\t-B get bootstrap from file\n"
-            "\t-C create image file with specified size\n"
-            "\t-F FAT type (12, 16, or 32)\n"
-            "\t-I volume ID\n"
-            "\t-L volume label\n"
-            "\t-N don't create file system: just print out parameters\n"
-            "\t-O OEM string\n"
-            "\t-S bytes/sector\n"
-            "\t-a sectors/FAT\n"
-            "\t-b block size\n"
-            "\t-c sectors/cluster\n"
-            "\t-e root directory entries\n"
-            "\t-f standard format\n"
-            "\t-h drive heads\n"
-            "\t-i file system info sector\n"
-            "\t-k backup boot sector\n"
-            "\t-m media descriptor\n"
-            "\t-n number of FATs\n"
-            "\t-o hidden sectors\n"
-            "\t-r reserved sectors\n"
-            "\t-s file system size (sectors)\n"
-            "\t-u sectors/track\n");
-    exit(1);
-}
diff --git a/toolbox/tools.h b/toolbox/tools.h
index 3d4bc3e..abeb3ef 100644
--- a/toolbox/tools.h
+++ b/toolbox/tools.h
@@ -1,4 +1,3 @@
 TOOL(getevent)
 TOOL(getprop)
-TOOL(newfs_msdos)
 TOOL(toolbox)