Merge "ueventd: create a /dev/block/by-name/ symlink without a partition name"
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 9c0eeca..06e4c50 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -357,9 +357,14 @@
 
     case A_OPEN: /* OPEN(local-id, 0, "destination") */
         if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
-            // TODO: Switch to string_view.
-            std::string address(p->payload.begin(), p->payload.end());
-            asocket* s = create_local_service_socket(address.c_str(), t);
+            std::string_view address(p->payload.begin(), p->payload.size());
+
+            // Historically, we received service names as a char*, and stopped at the first NUL
+            // byte. The client sent strings with null termination, which post-string_view, start
+            // being interpreted as part of the string, unless we explicitly strip them.
+            address = StripTrailingNulls(address);
+
+            asocket* s = create_local_service_socket(address, t);
             if (s == nullptr) {
                 send_close(0, p->msg.arg0, t);
             } else {
@@ -600,7 +605,7 @@
     fprintf(stderr, "Full server startup log: %s\n", GetLogFilePath().c_str());
     fprintf(stderr, "Server had pid: %d\n", pid);
 
-    android::base::unique_fd fd(unix_open(GetLogFilePath().c_str(), O_RDONLY));
+    android::base::unique_fd fd(unix_open(GetLogFilePath(), O_RDONLY));
     if (fd == -1) return;
 
     // Let's not show more than 128KiB of log...
diff --git a/adb/adb.h b/adb/adb.h
index cdd6346..47ea0e8 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -139,9 +139,9 @@
 atransport* find_emulator_transport_by_console_port(int console_port);
 #endif
 
-int service_to_fd(const char* name, atransport* transport);
+int service_to_fd(std::string_view name, atransport* transport);
 #if !ADB_HOST
-unique_fd daemon_service_to_fd(const char* name, atransport* transport);
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport);
 #endif
 
 #if ADB_HOST
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 6cc274b..e6fefda 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -38,7 +38,8 @@
 
     // The cost of sending two strings outweighs the cost of formatting.
     // "adb sync" performance is affected by this.
-    return WriteFdFmt(fd, "%04x%.*s", length, length, s.c_str());
+    auto str = android::base::StringPrintf("%04x", length).append(s);
+    return WriteFdExactly(fd, str);
 }
 
 bool ReadProtocolString(int fd, std::string* s, std::string* error) {
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 051ab73..be457a6 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -105,7 +105,7 @@
         s = create_local_socket(fd);
         if (s) {
             s->transport = listener->transport;
-            connect_to_remote(s, listener->connect_to.c_str());
+            connect_to_remote(s, listener->connect_to);
             return;
         }
 
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index a024a89..2bd6a3e 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -72,8 +72,7 @@
 }
 
 void start_device_log(void) {
-    int fd = unix_open(get_log_file_name().c_str(),
-                       O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
+    int fd = unix_open(get_log_file_name(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
     if (fd == -1) {
         return;
     }
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 6d12225..8253487 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -19,6 +19,8 @@
 #include <condition_variable>
 #include <mutex>
 #include <string>
+#include <string_view>
+#include <type_traits>
 #include <vector>
 
 #include <android-base/macros.h>
@@ -94,3 +96,47 @@
 };
 
 std::string GetLogFilePath();
+
+inline std::string_view StripTrailingNulls(std::string_view str) {
+    size_t n = 0;
+    for (auto it = str.rbegin(); it != str.rend(); ++it) {
+        if (*it != '\0') {
+            break;
+        }
+        ++n;
+    }
+
+    str.remove_suffix(n);
+    return str;
+}
+
+// Base-10 stroll on a string_view.
+template <typename T>
+inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining) {
+    if (str.empty() || !isdigit(str[0])) {
+        return false;
+    }
+
+    T value = 0;
+    std::string_view::iterator it;
+    constexpr T max = std::numeric_limits<T>::max();
+    for (it = str.begin(); it != str.end() && isdigit(*it); ++it) {
+        if (value > max / 10) {
+            return false;
+        }
+
+        value *= 10;
+
+        T digit = *it - '0';
+        if (value > max - digit) {
+            return false;
+        }
+
+        value += digit;
+    }
+    *result = value;
+    if (remaining) {
+        *remaining = str.substr(it - str.begin());
+    }
+    return true;
+}
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 870f6f0..bb09425 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -181,3 +181,48 @@
     EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:a", &error));
     EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:22x", &error));
 }
+
+void TestParseUint(std::string_view string, bool expected_success, uint32_t expected_value = 0) {
+    // Standalone.
+    {
+        uint32_t value;
+        std::string_view remaining;
+        bool success = ParseUint(&value, string, &remaining);
+        EXPECT_EQ(success, expected_success);
+        if (expected_success) {
+            EXPECT_EQ(value, expected_value);
+        }
+        EXPECT_TRUE(remaining.empty());
+    }
+
+    // With trailing text.
+    {
+        std::string text = std::string(string) + "foo";
+        uint32_t value;
+        std::string_view remaining;
+        bool success = ParseUint(&value, text, &remaining);
+        EXPECT_EQ(success, expected_success);
+        if (expected_success) {
+            EXPECT_EQ(value, expected_value);
+            EXPECT_EQ(remaining, "foo");
+        }
+    }
+}
+
+TEST(adb_utils, ParseUint) {
+    TestParseUint("", false);
+    TestParseUint("foo", false);
+    TestParseUint("foo123", false);
+    TestParseUint("-1", false);
+
+    TestParseUint("123", true, 123);
+    TestParseUint("9999999999999999999999999", false);
+    TestParseUint(std::to_string(UINT32_MAX), true, UINT32_MAX);
+    TestParseUint("0" + std::to_string(UINT32_MAX), true, UINT32_MAX);
+    TestParseUint(std::to_string(static_cast<uint64_t>(UINT32_MAX) + 1), false);
+    TestParseUint("0" + std::to_string(static_cast<uint64_t>(UINT32_MAX) + 1), false);
+
+    std::string x = std::to_string(UINT32_MAX) + "123";
+    std::string_view substr = std::string_view(x).substr(0, std::to_string(UINT32_MAX).size());
+    TestParseUint(substr, true, UINT32_MAX);
+}
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index fb581a6..2ee81a9 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -40,7 +40,7 @@
 
 static void setup_daemon_logging() {
     const std::string log_file_path(GetLogFilePath());
-    int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
+    int fd = unix_open(log_file_path, O_WRONLY | O_CREAT | O_APPEND, 0640);
     if (fd == -1) {
         PLOG(FATAL) << "cannot open " << log_file_path;
     }
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index f1bf559..1168958 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -39,6 +39,7 @@
 #include <list>
 #include <mutex>
 #include <string>
+#include <string_view>
 #include <thread>
 
 #include <android-base/file.h>
@@ -90,7 +91,7 @@
 static auto& g_usb_handles_mutex = *new std::mutex();
 static auto& g_usb_handles = *new std::list<usb_handle*>();
 
-static int is_known_device(const char* dev_name) {
+static int is_known_device(std::string_view dev_name) {
     std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
     for (usb_handle* usb : g_usb_handles) {
         if (usb->path == dev_name) {
@@ -152,11 +153,11 @@
             if (contains_non_digit(de->d_name)) continue;
 
             std::string dev_name = bus_name + "/" + de->d_name;
-            if (is_known_device(dev_name.c_str())) {
+            if (is_known_device(dev_name)) {
                 continue;
             }
 
-            int fd = unix_open(dev_name.c_str(), O_RDONLY | O_CLOEXEC);
+            int fd = unix_open(dev_name, O_RDONLY | O_CLOEXEC);
             if (fd == -1) {
                 continue;
             }
@@ -535,10 +536,10 @@
     // Initialize mark so we don't get garbage collected after the device scan.
     usb->mark = true;
 
-    usb->fd = unix_open(usb->path.c_str(), O_RDWR | O_CLOEXEC);
+    usb->fd = unix_open(usb->path, O_RDWR | O_CLOEXEC);
     if (usb->fd == -1) {
         // Opening RW failed, so see if we have RO access.
-        usb->fd = unix_open(usb->path.c_str(), O_RDONLY | O_CLOEXEC);
+        usb->fd = unix_open(usb->path, O_RDONLY | O_CLOEXEC);
         if (usb->fd == -1) {
             D("[ usb open %s failed: %s]", usb->path.c_str(), strerror(errno));
             return;
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 1a92317..80b3e06 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -103,7 +103,7 @@
 
 bool make_block_device_writable(const std::string& dev) {
     if (dev_is_overlayfs(dev)) return true;
-    int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
+    int fd = unix_open(dev, O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
         return false;
     }
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 3182ddd..3693997 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -151,14 +151,17 @@
     kick_transport(t);
 }
 
-unique_fd reverse_service(const char* command, atransport* transport) {
+unique_fd reverse_service(std::string_view command, atransport* transport) {
+    // TODO: Switch handle_forward_request to std::string_view.
+    std::string str(command);
+
     int s[2];
     if (adb_socketpair(s)) {
         PLOG(ERROR) << "cannot create service socket pair.";
         return unique_fd{};
     }
     VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
-    if (!handle_forward_request(command, transport, s[1])) {
+    if (!handle_forward_request(str.c_str(), transport, s[1])) {
         SendFail(s[1], "not a reverse forwarding command");
     }
     adb_close(s[1]);
@@ -167,15 +170,16 @@
 
 // Shell service string can look like:
 //   shell[,arg1,arg2,...]:[command]
-unique_fd ShellService(const std::string& args, const atransport* transport) {
+unique_fd ShellService(std::string_view 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;
         return unique_fd{};
     }
 
-    const std::string service_args = args.substr(0, delimiter_index);
-    const std::string command = args.substr(delimiter_index + 1);
+    // TODO: android::base::Split(const std::string_view&, ...)
+    std::string service_args(args.substr(0, delimiter_index));
+    std::string command(args.substr(delimiter_index + 1));
 
     // Defaults:
     //   PTY for interactive, raw for non-interactive.
@@ -192,15 +196,15 @@
             type = SubprocessType::kPty;
         } else if (arg == kShellServiceArgShellProtocol) {
             protocol = SubprocessProtocol::kShell;
-        } else if (android::base::StartsWith(arg, "TERM=")) {
-            terminal_type = arg.substr(5);
+        } else if (arg.starts_with("TERM=")) {
+            terminal_type = arg.substr(strlen("TERM="));
         } else if (!arg.empty()) {
             // This is not an error to allow for future expansion.
             LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
         }
     }
 
-    return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
+    return StartSubprocess(command, terminal_type.c_str(), type, protocol);
 }
 
 static void spin_service(unique_fd fd) {
@@ -323,59 +327,70 @@
     return nullptr;
 }
 
-unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
-    if (!strncmp("dev:", name, 4)) {
-        return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
-    } else if (!strncmp(name, "framebuffer:", 12)) {
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
+    if (name.starts_with("dev:")) {
+        name.remove_prefix(strlen("dev:"));
+        return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
+    } else if (name.starts_with("framebuffer:")) {
         return create_service_thread("fb", framebuffer_service);
-    } else if (!strncmp(name, "jdwp:", 5)) {
-        return create_jdwp_connection_fd(atoi(name + 5));
-    } else if (!strncmp(name, "shell", 5)) {
-        return ShellService(name + 5, transport);
-    } else if (!strncmp(name, "exec:", 5)) {
-        return StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if (!strncmp(name, "sync:", 5)) {
+    } else if (name.starts_with("jdwp:")) {
+        name.remove_prefix(strlen("jdwp:"));
+        std::string str(name);
+        return create_jdwp_connection_fd(atoi(str.c_str()));
+    } else if (name.starts_with("shell")) {
+        name.remove_prefix(strlen("shell"));
+        return ShellService(name, transport);
+    } else if (name.starts_with("exec:")) {
+        name.remove_prefix(strlen("exec:"));
+        return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
+                               SubprocessProtocol::kNone);
+    } else if (name.starts_with("sync:")) {
         return create_service_thread("sync", file_sync_service);
-    } else if (!strncmp(name, "remount:", 8)) {
-        std::string options(name + strlen("remount:"));
+    } else if (name.starts_with("remount:")) {
+        std::string arg(name.begin() + strlen("remount:"), name.end());
         return create_service_thread("remount",
-                                     std::bind(remount_service, std::placeholders::_1, options));
-    } else if (!strncmp(name, "reboot:", 7)) {
-        std::string arg(name + strlen("reboot:"));
+                                     std::bind(remount_service, std::placeholders::_1, arg));
+    } else if (name.starts_with("reboot:")) {
+        std::string arg(name.begin() + strlen("reboot:"), name.end());
         return create_service_thread("reboot",
                                      std::bind(reboot_service, std::placeholders::_1, arg));
-    } else if (!strncmp(name, "root:", 5)) {
+    } else if (name.starts_with("root:")) {
         return create_service_thread("root", restart_root_service);
-    } else if (!strncmp(name, "unroot:", 7)) {
+    } else if (name.starts_with("unroot:")) {
         return create_service_thread("unroot", restart_unroot_service);
-    } else if (!strncmp(name, "backup:", 7)) {
-        return 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 (name.starts_with("backup:")) {
+        name.remove_prefix(strlen("backup:"));
+        std::string cmd = "/system/bin/bu backup ";
+        cmd += name;
+        return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+    } else if (name.starts_with("restore:")) {
         return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
                                SubprocessProtocol::kNone);
-    } else if (!strncmp(name, "tcpip:", 6)) {
+    } else if (name.starts_with("tcpip:")) {
+        name.remove_prefix(strlen("tcpip:"));
+        std::string str(name);
+
         int port;
-        if (sscanf(name + 6, "%d", &port) != 1) {
+        if (sscanf(str.c_str(), "%d", &port) != 1) {
             return unique_fd{};
         }
         return create_service_thread("tcp",
                                      std::bind(restart_tcp_service, std::placeholders::_1, port));
-    } else if (!strncmp(name, "usb:", 4)) {
+    } else if (name.starts_with("usb:")) {
         return create_service_thread("usb", restart_usb_service);
-    } else if (!strncmp(name, "reverse:", 8)) {
-        return reverse_service(name + 8, transport);
-    } else if (!strncmp(name, "disable-verity:", 15)) {
+    } else if (name.starts_with("reverse:")) {
+        name.remove_prefix(strlen("reverse:"));
+        return reverse_service(name, transport);
+    } else if (name.starts_with("disable-verity:")) {
         return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
                                                             std::placeholders::_1, false));
-    } else if (!strncmp(name, "enable-verity:", 15)) {
+    } else if (name.starts_with("enable-verity:")) {
         return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
                                                              std::placeholders::_1, true));
-    } else if (!strcmp(name, "reconnect")) {
+    } else if (name == "reconnect") {
         return create_service_thread(
                 "reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
-    } else if (!strcmp(name, "spin")) {
+    } else if (name == "spin") {
         return create_service_thread("spin", spin_service);
     }
 
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 8805fc1..595d5c6 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -140,8 +140,8 @@
 
 class Subprocess {
   public:
-    Subprocess(const std::string& command, const char* terminal_type,
-               SubprocessType type, SubprocessProtocol protocol);
+    Subprocess(std::string command, const char* terminal_type, SubprocessType type,
+               SubprocessProtocol protocol);
     ~Subprocess();
 
     const std::string& command() const { return command_; }
@@ -191,9 +191,9 @@
     DISALLOW_COPY_AND_ASSIGN(Subprocess);
 };
 
-Subprocess::Subprocess(const std::string& command, const char* terminal_type,
-                       SubprocessType type, SubprocessProtocol protocol)
-    : command_(command),
+Subprocess::Subprocess(std::string command, const char* terminal_type, SubprocessType type,
+                       SubprocessProtocol protocol)
+    : command_(std::move(command)),
       terminal_type_(terminal_type ? terminal_type : ""),
       type_(type),
       protocol_(protocol) {
@@ -745,14 +745,13 @@
     return read;
 }
 
-unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
                           SubprocessProtocol protocol) {
     D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
       type == SubprocessType::kRaw ? "raw" : "PTY",
-      protocol == SubprocessProtocol::kNone ? "none" : "shell",
-      terminal_type, name);
+      protocol == SubprocessProtocol::kNone ? "none" : "shell", terminal_type, name.c_str());
 
-    auto subprocess = std::make_unique<Subprocess>(name, terminal_type, type, protocol);
+    auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol);
     if (!subprocess) {
         LOG(ERROR) << "failed to allocate new subprocess";
         return ReportError(protocol, "failed to allocate new subprocess");
diff --git a/adb/daemon/shell_service.h b/adb/daemon/shell_service.h
index 2a48923..421d61f 100644
--- a/adb/daemon/shell_service.h
+++ b/adb/daemon/shell_service.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <string>
+
 #include "adb_unique_fd.h"
 
 enum class SubprocessType {
@@ -32,5 +34,5 @@
 // shell is started, otherwise |name| is executed non-interactively.
 //
 // Returns an open FD connected to the subprocess or -1 on failure.
-unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
                           SubprocessProtocol protocol);
diff --git a/adb/services.cpp b/adb/services.cpp
index 4b033bd..8636657 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -71,7 +71,7 @@
     return unique_fd(s[0]);
 }
 
-int service_to_fd(const char* name, atransport* transport) {
+int service_to_fd(std::string_view name, atransport* transport) {
     int ret = -1;
 
     if (is_socket_spec(name)) {
diff --git a/adb/socket.h b/adb/socket.h
index 0905aab..e7df991 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -103,17 +103,18 @@
 void close_all_sockets(atransport *t);
 
 asocket *create_local_socket(int fd);
-asocket* create_local_service_socket(const char* destination, atransport* transport);
+asocket* create_local_service_socket(std::string_view destination, atransport* transport);
 
 asocket *create_remote_socket(unsigned id, atransport *t);
-void connect_to_remote(asocket *s, const char *destination);
+void connect_to_remote(asocket* s, std::string_view destination);
 void connect_to_smartsocket(asocket *s);
 
 // Internal functions that are only made available here for testing purposes.
 namespace internal {
 
 #if ADB_HOST
-char* skip_host_serial(char* service);
+bool parse_host_service(std::string_view* out_serial, std::string_view* out_command,
+                        std::string_view service);
 #endif
 
 }  // namespace internal
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index eb4df97..4cddc84 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -17,6 +17,7 @@
 #include "socket_spec.h"
 
 #include <string>
+#include <string_view>
 #include <unordered_map>
 #include <vector>
 
@@ -29,7 +30,8 @@
 #include "adb.h"
 #include "sysdeps.h"
 
-using android::base::StartsWith;
+using namespace std::string_literals;
+
 using android::base::StringPrintf;
 
 #if defined(__linux__)
@@ -64,10 +66,11 @@
     { "localfilesystem", { ANDROID_SOCKET_NAMESPACE_FILESYSTEM, !ADB_WINDOWS } },
 });
 
-bool parse_tcp_socket_spec(const std::string& spec, std::string* hostname, int* port,
+bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
                            std::string* error) {
-    if (!StartsWith(spec, "tcp:")) {
-        *error = StringPrintf("specification is not tcp: '%s'", spec.c_str());
+    if (!spec.starts_with("tcp:")) {
+        *error = "specification is not tcp: ";
+        *error += spec;
         return false;
     }
 
@@ -84,7 +87,7 @@
             return false;
         }
     } else {
-        std::string addr = spec.substr(4);
+        std::string addr(spec.substr(4));
         port_value = -1;
 
         // FIXME: ParseNetAddress rejects port 0. This currently doesn't hurt, because listening
@@ -94,7 +97,8 @@
         }
 
         if (port_value == -1) {
-            *error = StringPrintf("missing port in specification: '%s'", spec.c_str());
+            *error = "missing port in specification: ";
+            *error += spec;
             return false;
         }
     }
@@ -110,25 +114,25 @@
     return true;
 }
 
-static bool tcp_host_is_local(const std::string& hostname) {
+static bool tcp_host_is_local(std::string_view hostname) {
     // FIXME
     return hostname.empty() || hostname == "localhost";
 }
 
-bool is_socket_spec(const std::string& spec) {
+bool is_socket_spec(std::string_view spec) {
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix)) {
+        if (spec.starts_with(prefix)) {
             return true;
         }
     }
-    return StartsWith(spec, "tcp:");
+    return spec.starts_with("tcp:");
 }
 
-bool is_local_socket_spec(const std::string& spec) {
+bool is_local_socket_spec(std::string_view spec) {
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix)) {
+        if (spec.starts_with(prefix)) {
             return true;
         }
     }
@@ -141,8 +145,8 @@
     return tcp_host_is_local(hostname);
 }
 
-int socket_spec_connect(const std::string& spec, std::string* error) {
-    if (StartsWith(spec, "tcp:")) {
+int socket_spec_connect(std::string_view spec, std::string* error) {
+    if (spec.starts_with("tcp:")) {
         std::string hostname;
         int port;
         if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
@@ -170,7 +174,7 @@
 
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix)) {
+        if (spec.starts_with(prefix)) {
             if (!it.second.available) {
                 *error = StringPrintf("socket type %s is unavailable on this platform",
                                       it.first.c_str());
@@ -182,12 +186,13 @@
         }
     }
 
-    *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
+    *error = "unknown socket specification: ";
+    *error += spec;
     return -1;
 }
 
-int socket_spec_listen(const std::string& spec, std::string* error, int* resolved_tcp_port) {
-    if (StartsWith(spec, "tcp:")) {
+int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port) {
+    if (spec.starts_with("tcp:")) {
         std::string hostname;
         int port;
         if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
@@ -213,10 +218,10 @@
 
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix)) {
+        if (spec.starts_with(prefix)) {
             if (!it.second.available) {
-                *error = StringPrintf("attempted to listen on unavailable socket type: '%s'",
-                                      spec.c_str());
+                *error = "attempted to listen on unavailable socket type: ";
+                *error += spec;
                 return -1;
             }
 
@@ -225,6 +230,7 @@
         }
     }
 
-    *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
+    *error = "unknown socket specification:";
+    *error += spec;
     return -1;
 }
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
index 6920e91..5b06973 100644
--- a/adb/socket_spec.h
+++ b/adb/socket_spec.h
@@ -19,13 +19,12 @@
 #include <string>
 
 // Returns true if the argument starts with a plausible socket prefix.
-bool is_socket_spec(const std::string& spec);
-bool is_local_socket_spec(const std::string& spec);
+bool is_socket_spec(std::string_view spec);
+bool is_local_socket_spec(std::string_view spec);
 
-int socket_spec_connect(const std::string& spec, std::string* error);
-int socket_spec_listen(const std::string& spec, std::string* error,
-                       int* resolved_tcp_port = nullptr);
+int socket_spec_connect(std::string_view spec, std::string* error);
+int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port = nullptr);
 
 // Exposed for testing.
-bool parse_tcp_socket_spec(const std::string& spec, std::string* hostname, int* port,
+bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
                            std::string* error);
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 04214a2..80f9430 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -34,6 +34,9 @@
 #include "sysdeps.h"
 #include "sysdeps/chrono.h"
 
+using namespace std::string_literals;
+using namespace std::string_view_literals;
+
 struct ThreadArg {
     int first_read_fd;
     int last_write_fd;
@@ -303,56 +306,78 @@
 
 #if ADB_HOST
 
-// Checks that skip_host_serial(serial) returns a pointer to the part of |serial| which matches
-// |expected|, otherwise logs the failure to gtest.
-void VerifySkipHostSerial(std::string serial, const char* expected) {
-    char* result = internal::skip_host_serial(&serial[0]);
-    if (expected == nullptr) {
-        EXPECT_EQ(nullptr, result);
-    } else {
-        EXPECT_STREQ(expected, result);
-    }
-}
+#define VerifyParseHostServiceFailed(s)                                         \
+    do {                                                                        \
+        std::string service(s);                                                 \
+        std::string_view serial, command;                                       \
+        bool result = internal::parse_host_service(&serial, &command, service); \
+        EXPECT_FALSE(result);                                                   \
+    } while (0)
+
+#define VerifyParseHostService(s, expected_serial, expected_command)            \
+    do {                                                                        \
+        std::string service(s);                                                 \
+        std::string_view serial, command;                                       \
+        bool result = internal::parse_host_service(&serial, &command, service); \
+        EXPECT_TRUE(result);                                                    \
+        EXPECT_EQ(std::string(expected_serial), std::string(serial));           \
+        EXPECT_EQ(std::string(expected_command), std::string(command));         \
+    } while (0);
 
 // Check [tcp:|udp:]<serial>[:<port>]:<command> format.
-TEST(socket_test, test_skip_host_serial) {
+TEST(socket_test, test_parse_host_service) {
     for (const std::string& protocol : {"", "tcp:", "udp:"}) {
-        VerifySkipHostSerial(protocol, nullptr);
-        VerifySkipHostSerial(protocol + "foo", nullptr);
+        VerifyParseHostServiceFailed(protocol);
+        VerifyParseHostServiceFailed(protocol + "foo");
 
-        VerifySkipHostSerial(protocol + "foo:bar", ":bar");
-        VerifySkipHostSerial(protocol + "foo:bar:baz", ":bar:baz");
+        {
+            std::string serial = protocol + "foo";
+            VerifyParseHostService(serial + ":bar", serial, "bar");
+            VerifyParseHostService(serial + " :bar:baz", serial, "bar:baz");
+        }
 
-        VerifySkipHostSerial(protocol + "foo:123:bar", ":bar");
-        VerifySkipHostSerial(protocol + "foo:123:456", ":456");
-        VerifySkipHostSerial(protocol + "foo:123:bar:baz", ":bar:baz");
+        {
+            // With port.
+            std::string serial = protocol + "foo:123";
+            VerifyParseHostService(serial + ":bar", serial, "bar");
+            VerifyParseHostService(serial + ":456", serial, "456");
+            VerifyParseHostService(serial + ":bar:baz", serial, "bar:baz");
+        }
 
         // Don't register a port unless it's all numbers and ends with ':'.
-        VerifySkipHostSerial(protocol + "foo:123", ":123");
-        VerifySkipHostSerial(protocol + "foo:123bar:baz", ":123bar:baz");
+        VerifyParseHostService(protocol + "foo:123", protocol + "foo", "123");
+        VerifyParseHostService(protocol + "foo:123bar:baz", protocol + "foo", "123bar:baz");
 
-        VerifySkipHostSerial(protocol + "100.100.100.100:5555:foo", ":foo");
-        VerifySkipHostSerial(protocol + "[0123:4567:89ab:CDEF:0:9:a:f]:5555:foo", ":foo");
-        VerifySkipHostSerial(protocol + "[::1]:5555:foo", ":foo");
+        std::string addresses[] = {"100.100.100.100", "[0123:4567:89ab:CDEF:0:9:a:f]", "[::1]"};
+        for (const std::string& address : addresses) {
+            std::string serial = protocol + address;
+            std::string serial_with_port = protocol + address + ":5555";
+            VerifyParseHostService(serial + ":foo", serial, "foo");
+            VerifyParseHostService(serial_with_port + ":foo", serial_with_port, "foo");
+        }
 
         // If we can't find both [] then treat it as a normal serial with [ in it.
-        VerifySkipHostSerial(protocol + "[0123:foo", ":foo");
+        VerifyParseHostService(protocol + "[0123:foo", protocol + "[0123", "foo");
 
         // Don't be fooled by random IPv6 addresses in the command string.
-        VerifySkipHostSerial(protocol + "foo:ping [0123:4567:89ab:CDEF:0:9:a:f]:5555",
-                             ":ping [0123:4567:89ab:CDEF:0:9:a:f]:5555");
+        VerifyParseHostService(protocol + "foo:ping [0123:4567:89ab:CDEF:0:9:a:f]:5555",
+                               protocol + "foo", "ping [0123:4567:89ab:CDEF:0:9:a:f]:5555");
+
+        // Handle embedded NULs properly.
+        VerifyParseHostService(protocol + "foo:echo foo\0bar"s, protocol + "foo",
+                               "echo foo\0bar"sv);
     }
 }
 
 // Check <prefix>:<serial>:<command> format.
-TEST(socket_test, test_skip_host_serial_prefix) {
+TEST(socket_test, test_parse_host_service_prefix) {
     for (const std::string& prefix : {"usb:", "product:", "model:", "device:"}) {
-        VerifySkipHostSerial(prefix, nullptr);
-        VerifySkipHostSerial(prefix + "foo", nullptr);
+        VerifyParseHostServiceFailed(prefix);
+        VerifyParseHostServiceFailed(prefix + "foo");
 
-        VerifySkipHostSerial(prefix + "foo:bar", ":bar");
-        VerifySkipHostSerial(prefix + "foo:bar:baz", ":bar:baz");
-        VerifySkipHostSerial(prefix + "foo:123:bar", ":123:bar");
+        VerifyParseHostService(prefix + "foo:bar", prefix + "foo", "bar");
+        VerifyParseHostService(prefix + "foo:bar:baz", prefix + "foo", "bar:baz");
+        VerifyParseHostService(prefix + "foo:123:bar", prefix + "foo", "123:bar");
     }
 }
 
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 1bd57c1..47ae883 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -37,6 +37,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "transport.h"
 #include "types.h"
 
@@ -346,7 +347,7 @@
     return s;
 }
 
-asocket* create_local_service_socket(const char* name, atransport* transport) {
+asocket* create_local_service_socket(std::string_view name, atransport* transport) {
 #if !ADB_HOST
     if (asocket* s = daemon_service_to_socket(name); s) {
         return s;
@@ -358,13 +359,12 @@
     }
 
     asocket* s = create_local_socket(fd);
-    D("LS(%d): bound to '%s' via %d", s->id, name, fd);
+    LOG(VERBOSE) << "LS(" << s->id << "): bound to '" << name << "' via " << fd;
 
 #if !ADB_HOST
-    if ((!strncmp(name, "root:", 5) && getuid() != 0 && __android_log_is_debuggable()) ||
-        (!strncmp(name, "unroot:", 7) && getuid() == 0) ||
-        !strncmp(name, "usb:", 4) ||
-        !strncmp(name, "tcpip:", 6)) {
+    if ((name.starts_with("root:") && getuid() != 0 && __android_log_is_debuggable()) ||
+        (name.starts_with("unroot:") && getuid() == 0) || name.starts_with("usb:") ||
+        name.starts_with("tcpip:")) {
         D("LS(%d): enabling exit_on_close", s->id);
         s->exit_on_close = 1;
     }
@@ -462,16 +462,19 @@
     return s;
 }
 
-void connect_to_remote(asocket* s, const char* destination) {
+void connect_to_remote(asocket* s, std::string_view destination) {
     D("Connect_to_remote call RS(%d) fd=%d", s->id, s->fd);
     apacket* p = get_apacket();
 
-    D("LS(%d): connect('%s')", s->id, destination);
+    LOG(VERBOSE) << "LS(" << s->id << ": connect(" << destination << ")";
     p->msg.command = A_OPEN;
     p->msg.arg0 = s->id;
 
-    // adbd expects a null-terminated string.
-    p->payload.assign(destination, destination + strlen(destination) + 1);
+    // adbd used to expect a null-terminated string.
+    // Keep doing so to maintain backward compatibility.
+    p->payload.resize(destination.size() + 1);
+    memcpy(p->payload.data(), destination.data(), destination.size());
+    p->payload[destination.size()] = '\0';
     p->msg.data_length = p->payload.size();
 
     CHECK_LE(p->msg.data_length, s->get_max_payload());
@@ -547,57 +550,119 @@
 
 namespace internal {
 
-// Returns the position in |service| following the target serial parameter. Serial format can be
-// any of:
+// Parses a host service string of the following format:
 //   * [tcp:|udp:]<serial>[:<port>]:<command>
 //   * <prefix>:<serial>:<command>
 // Where <port> must be a base-10 number and <prefix> may be any of {usb,product,model,device}.
-//
-// The returned pointer will point to the ':' just before <command>, or nullptr if not found.
-char* skip_host_serial(char* service) {
-    static const std::vector<std::string>& prefixes =
-        *(new std::vector<std::string>{"usb:", "product:", "model:", "device:"});
+bool parse_host_service(std::string_view* out_serial, std::string_view* out_command,
+                        std::string_view full_service) {
+    if (full_service.empty()) {
+        return false;
+    }
 
-    for (const std::string& prefix : prefixes) {
-        if (!strncmp(service, prefix.c_str(), prefix.length())) {
-            return strchr(service + prefix.length(), ':');
+    std::string_view serial;
+    std::string_view command = full_service;
+    // Remove |count| bytes from the beginning of command and add them to |serial|.
+    auto consume = [&full_service, &serial, &command](size_t count) {
+        CHECK_LE(count, command.size());
+        if (!serial.empty()) {
+            CHECK_EQ(serial.data() + serial.size(), command.data());
+        }
+
+        serial = full_service.substr(0, serial.size() + count);
+        command.remove_prefix(count);
+    };
+
+    // Remove the trailing : from serial, and assign the values to the output parameters.
+    auto finish = [out_serial, out_command, &serial, &command] {
+        if (serial.empty() || command.empty()) {
+            return false;
+        }
+
+        CHECK_EQ(':', serial.back());
+        serial.remove_suffix(1);
+
+        *out_serial = serial;
+        *out_command = command;
+        return true;
+    };
+
+    static constexpr std::string_view prefixes[] = {"usb:", "product:", "model:", "device:"};
+    for (std::string_view prefix : prefixes) {
+        if (command.starts_with(prefix)) {
+            consume(prefix.size());
+
+            size_t offset = command.find_first_of(':');
+            if (offset == std::string::npos) {
+                return false;
+            }
+            consume(offset + 1);
+            return finish();
         }
     }
 
     // For fastboot compatibility, ignore protocol prefixes.
-    if (!strncmp(service, "tcp:", 4) || !strncmp(service, "udp:", 4)) {
-        service += 4;
-    }
-
-    // Check for an IPv6 address. `adb connect` creates the serial number from the canonical
-    // network address so it will always have the [] delimiters.
-    if (service[0] == '[') {
-        char* ipv6_end = strchr(service, ']');
-        if (ipv6_end != nullptr) {
-            service = ipv6_end;
+    if (command.starts_with("tcp:") || command.starts_with("udp:")) {
+        consume(4);
+        if (command.empty()) {
+            return false;
         }
     }
 
-    // The next colon we find must either begin the port field or the command field.
-    char* colon_ptr = strchr(service, ':');
-    if (!colon_ptr) {
-        // No colon in service string.
-        return nullptr;
+    bool found_address = false;
+    if (command[0] == '[') {
+        // Read an IPv6 address. `adb connect` creates the serial number from the canonical
+        // network address so it will always have the [] delimiters.
+        size_t ipv6_end = command.find_first_of(']');
+        if (ipv6_end != std::string::npos) {
+            consume(ipv6_end + 1);
+            if (command.empty()) {
+                // Nothing after the IPv6 address.
+                return false;
+            } else if (command[0] != ':') {
+                // Garbage after the IPv6 address.
+                return false;
+            }
+            consume(1);
+            found_address = true;
+        }
     }
 
-    // If the next field is only decimal digits and ends with another colon, it's a port.
-    char* serial_end = colon_ptr;
-    if (isdigit(serial_end[1])) {
-        serial_end++;
-        while (*serial_end && isdigit(*serial_end)) {
-            serial_end++;
+    if (!found_address) {
+        // Scan ahead to the next colon.
+        size_t offset = command.find_first_of(':');
+        if (offset == std::string::npos) {
+            return false;
         }
-        if (*serial_end != ':') {
-            // Something other than "<port>:" was found, this must be the command field instead.
-            serial_end = colon_ptr;
+        consume(offset + 1);
+    }
+
+    // We're either at the beginning of a port, or the command itself.
+    // Look for a port in between colons.
+    size_t next_colon = command.find_first_of(':');
+    if (next_colon == std::string::npos) {
+        // No colon, we must be at the command.
+        return finish();
+    }
+
+    bool port_valid = true;
+    if (command.size() <= next_colon) {
+        return false;
+    }
+
+    std::string_view port = command.substr(0, next_colon);
+    for (auto digit : port) {
+        if (!isdigit(digit)) {
+            // Port isn't a number.
+            port_valid = false;
+            break;
         }
     }
-    return serial_end;
+
+    if (port_valid) {
+        consume(next_colon + 1);
+    }
+    return finish();
 }
 
 }  // namespace internal
@@ -606,8 +671,8 @@
 
 static int smart_socket_enqueue(asocket* s, apacket::payload_type data) {
 #if ADB_HOST
-    char* service = nullptr;
-    char* serial = nullptr;
+    std::string_view service;
+    std::string_view serial;
     TransportId transport_id = 0;
     TransportType type = kTransportAny;
 #endif
@@ -644,49 +709,52 @@
     D("SS(%d): '%s'", s->id, (char*)(s->smart_socket_data.data() + 4));
 
 #if ADB_HOST
-    service = &s->smart_socket_data[4];
-    if (!strncmp(service, "host-serial:", strlen("host-serial:"))) {
-        char* serial_end;
-        service += strlen("host-serial:");
+    service = std::string_view(s->smart_socket_data).substr(4);
+    if (service.starts_with("host-serial:")) {
+        service.remove_prefix(strlen("host-serial:"));
 
         // serial number should follow "host:" and could be a host:port string.
-        serial_end = internal::skip_host_serial(service);
-        if (serial_end) {
-            *serial_end = 0;  // terminate string
-            serial = service;
-            service = serial_end + 1;
+        if (!internal::parse_host_service(&serial, &service, service)) {
+            LOG(ERROR) << "SS(" << s->id << "): failed to parse host service: " << service;
+            goto fail;
         }
-    } else if (!strncmp(service, "host-transport-id:", strlen("host-transport-id:"))) {
-        service += strlen("host-transport-id:");
-        transport_id = strtoll(service, &service, 10);
-
-        if (*service != ':') {
+    } else if (service.starts_with("host-transport-id:")) {
+        service.remove_prefix(strlen("host-transport-id:"));
+        if (!ParseUint(&transport_id, service, &service)) {
+            LOG(ERROR) << "SS(" << s->id << "): failed to parse host transport id: " << service;
             return -1;
         }
-        service++;
-    } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
+        if (!service.starts_with(":")) {
+            LOG(ERROR) << "SS(" << s->id << "): host-transport-id without command";
+            return -1;
+        }
+        service.remove_prefix(1);
+    } else if (service.starts_with("host-usb:")) {
         type = kTransportUsb;
-        service += strlen("host-usb:");
-    } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
+        service.remove_prefix(strlen("host-usb:"));
+    } else if (service.starts_with("host-local:")) {
         type = kTransportLocal;
-        service += strlen("host-local:");
-    } else if (!strncmp(service, "host:", strlen("host:"))) {
+        service.remove_prefix(strlen("host-local:"));
+    } else if (service.starts_with("host:")) {
         type = kTransportAny;
-        service += strlen("host:");
+        service.remove_prefix(strlen("host:"));
     } else {
-        service = nullptr;
+        service = std::string_view{};
     }
 
-    if (service) {
+    if (!service.empty()) {
         asocket* s2;
 
         // Some requests are handled immediately -- in that case the handle_host_request() routine
         // has sent the OKAY or FAIL message and all we have to do is clean up.
-        if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s)) {
-            D("SS(%d): handled host service '%s'", s->id, service);
+        // TODO: Convert to string_view.
+        if (handle_host_request(std::string(service).c_str(), type,
+                                serial.empty() ? nullptr : std::string(serial).c_str(),
+                                transport_id, s->peer->fd, s)) {
+            LOG(VERBOSE) << "SS(" << s->id << "): handled host service '" << service << "'";
             goto fail;
         }
-        if (!strncmp(service, "transport", strlen("transport"))) {
+        if (service.starts_with("transport")) {
             D("SS(%d): okay transport", s->id);
             s->smart_socket_data.clear();
             return 0;
@@ -696,9 +764,11 @@
         ** if no such service exists, we'll fail out
         ** and tear down here.
         */
-        s2 = create_host_service_socket(service, serial, transport_id);
+        // TODO: Convert to string_view.
+        s2 = create_host_service_socket(std::string(service).c_str(), std::string(serial).c_str(),
+                                        transport_id);
         if (s2 == nullptr) {
-            D("SS(%d): couldn't create host service '%s'", s->id, service);
+            LOG(VERBOSE) << "SS(" << s->id << "): couldn't create host service '" << service << "'";
             SendFail(s->peer->fd, "unknown host service");
             goto fail;
         }
@@ -759,7 +829,7 @@
     /* give him our transport and upref it */
     s->peer->transport = s->transport;
 
-    connect_to_remote(s->peer, s->smart_socket_data.data() + 4);
+    connect_to_remote(s->peer, std::string_view(s->smart_socket_data).substr(4));
     s->peer = nullptr;
     s->close(s);
     return 1;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index b8d7e06..15247e7 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -27,6 +27,7 @@
 #include <errno.h>
 
 #include <string>
+#include <string_view>
 #include <vector>
 
 // Include this before open/close/unlink are defined as macros below.
@@ -139,7 +140,7 @@
 }
 
 // See the comments for the !defined(_WIN32) version of unix_open().
-extern int unix_open(const char* path, int options, ...);
+extern int unix_open(std::string_view path, int options, ...);
 #define  open    ___xxx_unix_open
 
 // Checks if |fd| corresponds to a console.
@@ -357,20 +358,17 @@
 // by unix_read(), unix_write(), unix_close()). Also, the C Runtime has
 // configurable CR/LF translation which defaults to text mode, but is settable
 // with _setmode().
-static __inline__ int  unix_open(const char*  path, int options,...)
-{
-    if ((options & O_CREAT) == 0)
-    {
-        return  TEMP_FAILURE_RETRY( open(path, options) );
-    }
-    else
-    {
-        int      mode;
-        va_list  args;
-        va_start( args, options );
-        mode = va_arg( args, int );
-        va_end( args );
-        return TEMP_FAILURE_RETRY( open( path, options, mode ) );
+static __inline__ int unix_open(std::string_view path, int options, ...) {
+    std::string zero_terminated(path.begin(), path.end());
+    if ((options & O_CREAT) == 0) {
+        return TEMP_FAILURE_RETRY(open(zero_terminated.c_str(), options));
+    } else {
+        int mode;
+        va_list args;
+        va_start(args, options);
+        mode = va_arg(args, int);
+        va_end(args);
+        return TEMP_FAILURE_RETRY(open(zero_terminated.c_str(), options, mode));
     }
 }
 
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 8a6541d..dbc8920 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -29,6 +29,7 @@
 #include <memory>
 #include <mutex>
 #include <string>
+#include <string_view>
 #include <unordered_map>
 #include <vector>
 
@@ -2203,15 +2204,15 @@
     }
 }
 
-int unix_open(const char* path, int options, ...) {
+int unix_open(std::string_view path, int options, ...) {
     std::wstring path_wide;
-    if (!android::base::UTF8ToWide(path, &path_wide)) {
+    if (!android::base::UTF8ToWide(path.data(), path.size(), &path_wide)) {
         return -1;
     }
     if ((options & O_CREAT) == 0) {
         return _wopen(path_wide.c_str(), options);
     } else {
-        int      mode;
+        int mode;
         va_list  args;
         va_start(args, options);
         mode = va_arg(args, int);
diff --git a/base/Android.bp b/base/Android.bp
index 741664b..b0181aa 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -20,6 +20,7 @@
         "-Wall",
         "-Werror",
         "-Wextra",
+        "-D_FILE_OFFSET_BITS=64",
     ],
 }
 
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 71d2a1d..e91598d 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -329,12 +329,14 @@
     MetadataBuilder* operator->() const { return builder_.get(); }
 
   private:
+    FastbootDevice* device_;
     std::string super_device_;
     uint32_t slot_number_;
     std::unique_ptr<MetadataBuilder> builder_;
 };
 
-PartitionBuilder::PartitionBuilder(FastbootDevice* device, const std::string& partition_name) {
+PartitionBuilder::PartitionBuilder(FastbootDevice* device, const std::string& partition_name)
+    : device_(device) {
     std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);
     slot_number_ = SlotNumberForSlotSuffix(slot_suffix);
     auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number_));
@@ -350,7 +352,7 @@
     if (!metadata) {
         return false;
     }
-    return UpdateAllPartitionMetadata(super_device_, *metadata.get());
+    return UpdateAllPartitionMetadata(device_, super_device_, *metadata.get());
 }
 
 bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index fbba631..f737405 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -143,6 +143,11 @@
         return device->WriteFail("Data is not a valid logical partition metadata image");
     }
 
+    if (!FindPhysicalPartition(super_name)) {
+        return device->WriteFail("Cannot find " + super_name +
+                                 ", build may be missing broken or missing boot_devices");
+    }
+
     // If we are unable to read the existing metadata, then the super partition
     // is corrupt. In this case we reflash the whole thing using the provided
     // image.
@@ -184,7 +189,7 @@
     }
 
     // Write the new table to every metadata slot.
-    if (!UpdateAllPartitionMetadata(super_name, *new_metadata.get())) {
+    if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
         return device->WriteFail("Unable to write new partition table");
     }
     fs_mgr_overlayfs_teardown();
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 2ae9ac5..2ebd57d 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -200,10 +200,16 @@
     return cmdline.find("androidboot.verifiedbootstate=orange") == std::string::npos;
 }
 
-bool UpdateAllPartitionMetadata(const std::string& super_name,
+bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,
                                 const android::fs_mgr::LpMetadata& metadata) {
+    size_t num_slots = 1;
+    auto boot_control_hal = device->boot_control_hal();
+    if (boot_control_hal) {
+        num_slots = boot_control_hal->getNumberSlots();
+    }
+
     bool ok = true;
-    for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
+    for (size_t i = 0; i < num_slots; i++) {
         ok &= UpdatePartitionTable(super_name, metadata, i);
     }
     return ok;
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 4c6aa07..bfeeb74 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -68,5 +68,5 @@
 bool GetDeviceLockStatus();
 
 // Update all copies of metadata.
-bool UpdateAllPartitionMetadata(const std::string& super_name,
+bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,
                                 const android::fs_mgr::LpMetadata& metadata);
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index ac138a3..824511e 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -40,6 +40,7 @@
         "fs_mgr_verity.cpp",
         "fs_mgr_dm_linear.cpp",
         "fs_mgr_overlayfs.cpp",
+        "fs_mgr_roots.cpp",
         "fs_mgr_vendor_overlay.cpp",
     ],
     shared_libs: [
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index fbb5f5d..960410c 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -83,18 +83,19 @@
 -------
 
 - Space used in the backing storage is on a file by file basis
-  and will require more space than if updated in place.
+  and will require more space than if updated in place.  As such
+  it is important to be mindful of any wasted space, for instance
+  **BOARD_<partition>IMAGE_PARTITION_RESERVED_SIZE** being defined
+  will have a negative impact on the overall right-sizing of images
+  and thus free dynamic partition space.
 - Kernel must have CONFIG_OVERLAY_FS=y and will need to be patched
   with "*overlayfs: override_creds=off option bypass creator_cred*"
   if higher than 4.6.
 - *adb enable-verity* will free up overlayfs and as a bonus the
   device will be reverted pristine to before any content was updated.
   Update engine does not take advantage of this, will perform a full OTA.
-- Update engine will not run if *fs_mgr_overlayfs_is_setup*() reports
-  true as adb remount overrides are incompatable with an OTA for
-  multiple reasons.
-  NB: This is not a problem for fastbootd or recovery as overrides are
-  disabled for those special boot scenarios.
+- Update engine may not run if *fs_mgr_overlayfs_is_setup*() reports
+  true as adb remount overrides are incompatable with an OTA resources.
 - For implementation simplicity on retrofit dynamic partition devices,
   take the whole alternate super (eg: if "*a*" slot, then the whole of
   "*system_b*").
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 88f7a2c..943fe10 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1179,11 +1179,12 @@
 
 // wrapper to __mount() and expects a fully prepared fstab_rec,
 // unlike fs_mgr_do_mount which does more things with avb / verity etc.
-int fs_mgr_do_mount_one(const FstabEntry& entry) {
+int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point) {
     // Run fsck if needed
     prepare_fs_for_mount(entry.blk_device, entry);
 
-    int ret = __mount(entry.blk_device, entry.mount_point, entry);
+    int ret =
+            __mount(entry.blk_device, mount_point.empty() ? entry.mount_point : mount_point, entry);
     if (ret) {
       ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
     }
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 733ad55..abece4d 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -81,6 +81,9 @@
 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;
+    if (!cmdline.empty() && cmdline.back() == '\n') {
+        cmdline.pop_back();
+    }
     return fs_mgr_get_boot_config_from_kernel(cmdline, key, out_val);
 }
 
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
new file mode 100644
index 0000000..f44ff41
--- /dev/null
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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 "fs_mgr/roots.h"
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "fs_mgr.h"
+#include "fs_mgr_dm_linear.h"
+#include "fs_mgr_priv.h"
+
+namespace android {
+namespace fs_mgr {
+
+static constexpr const char* kSystemRoot = "/system";
+
+static bool gDidMapLogicalPartitions = false;
+
+FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path) {
+    if (path.empty()) return nullptr;
+    std::string str(path);
+    while (true) {
+        auto it = std::find_if(fstab->begin(), fstab->end(),
+                               [&str](const auto& entry) { return entry.mount_point == str; });
+        if (it != fstab->end()) return &*it;
+        if (str == "/") break;
+        auto slash = str.find_last_of('/');
+        if (slash == std::string::npos) break;
+        if (slash == 0) {
+            str = "/";
+        } else {
+            str = str.substr(0, slash);
+        }
+    }
+    return nullptr;
+}
+
+enum class MountState {
+    ERROR = -1,
+    NOT_MOUNTED = 0,
+    MOUNTED = 1,
+};
+
+static MountState GetMountState(const std::string& mount_point) {
+    Fstab mounted_fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+        LERROR << "Failed to scan mounted volumes";
+        return MountState::ERROR;
+    }
+
+    auto mv = std::find_if(
+            mounted_fstab.begin(), mounted_fstab.end(),
+            [&mount_point](const auto& entry) { return entry.mount_point == mount_point; });
+    if (mv != mounted_fstab.end()) {
+        return MountState::MOUNTED;
+    }
+    return MountState::NOT_MOUNTED;
+}
+
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_pt) {
+    auto rec = GetEntryForPath(fstab, path);
+    if (rec == nullptr) {
+        LERROR << "unknown volume for path [" << path << "]";
+        return false;
+    }
+    if (rec->fs_type == "ramdisk") {
+        // The ramdisk is always mounted.
+        return true;
+    }
+
+    // If we can't acquire the block device for a logical partition, it likely
+    // was never created. In that case we try to create it.
+    if (rec->fs_mgr_flags.logical && !fs_mgr_update_logical_partition(rec)) {
+        if (gDidMapLogicalPartitions) {
+            LERROR << "Failed to find block device for partition";
+            return false;
+        }
+        std::string super_name = fs_mgr_get_super_partition_name();
+        if (!android::fs_mgr::CreateLogicalPartitions("/dev/block/by-name/" + super_name)) {
+            LERROR << "Failed to create logical partitions";
+            return false;
+        }
+        gDidMapLogicalPartitions = true;
+        if (!fs_mgr_update_logical_partition(rec)) {
+            LERROR << "Failed to find block device for partition";
+            return false;
+        }
+    }
+
+    auto mounted = GetMountState(rec->mount_point);
+    if (mounted == MountState::ERROR) {
+        return false;
+    }
+    if (mounted == MountState::MOUNTED) {
+        return true;
+    }
+
+    const std::string mount_point = mount_pt.empty() ? rec->mount_point : mount_pt;
+
+    static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs"};
+    if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {
+        LERROR << "unknown fs_type \"" << rec->fs_type << "\" for " << mount_point;
+        return false;
+    }
+
+    int result = fs_mgr_do_mount_one(*rec, mount_point);
+    if (result == -1 && rec->fs_mgr_flags.formattable) {
+        PERROR << "Failed to mount " << mount_point << "; formatting";
+        bool crypt_footer = rec->is_encryptable() && rec->key_loc == "footer";
+        if (fs_mgr_do_format(*rec, crypt_footer) != 0) {
+            PERROR << "Failed to format " << mount_point;
+            return false;
+        }
+        result = fs_mgr_do_mount_one(*rec, mount_point);
+    }
+
+    if (result == -1) {
+        PERROR << "Failed to mount " << mount_point;
+        return false;
+    }
+    return true;
+}
+
+bool EnsurePathUnmounted(Fstab* fstab, const std::string& path) {
+    auto rec = GetEntryForPath(fstab, path);
+    if (rec == nullptr) {
+        LERROR << "unknown volume for path [" << path << "]";
+        return false;
+    }
+    if (rec->fs_type == "ramdisk") {
+        // The ramdisk is always mounted; you can't unmount it.
+        return false;
+    }
+
+    Fstab mounted_fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+        LERROR << "Failed to scan mounted volumes";
+        return false;
+    }
+
+    auto mounted = GetMountState(rec->mount_point);
+    if (mounted == MountState::ERROR) {
+        return false;
+    }
+    if (mounted == MountState::NOT_MOUNTED) {
+        return true;
+    }
+
+    int result = umount(rec->mount_point.c_str());
+    if (result == -1) {
+        PWARNING << "Failed to umount " << rec->mount_point;
+        return false;
+    }
+    return true;
+}
+
+std::string GetSystemRoot() {
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        LERROR << "Failed to read default fstab";
+        return "";
+    }
+
+    auto it = std::find_if(fstab.begin(), fstab.end(),
+                           [](const auto& entry) { return entry.mount_point == kSystemRoot; });
+    if (it == fstab.end()) {
+        return "/";
+    }
+
+    return kSystemRoot;
+}
+
+bool LogicalPartitionsMapped() {
+    return gDidMapLogicalPartitions;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 814ba46..e87332f 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -69,7 +69,7 @@
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point);
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
                     bool need_cp);
-int fs_mgr_do_mount_one(const FstabEntry& entry);
+int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point = "");
 int fs_mgr_do_mount_one(fstab_rec* rec);
 int fs_mgr_do_tmpfs_mount(const char *n_name);
 fstab_rec const* fs_mgr_get_crypt_entry(fstab const* fstab);
diff --git a/fs_mgr/include/fs_mgr/roots.h b/fs_mgr/include/fs_mgr/roots.h
new file mode 100644
index 0000000..65c59cf
--- /dev/null
+++ b/fs_mgr/include/fs_mgr/roots.h
@@ -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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <fs_mgr.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Finds the volume specified by the given path. fs_mgr_get_entry_for_mount_point() does exact match
+// only, so it attempts the prefixes recursively (e.g. "/cache/recovery/last_log",
+// "/cache/recovery", "/cache", "/" for a given path of "/cache/recovery/last_log") and returns the
+// first match or nullptr.
+FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path);
+
+// Make sure that the volume 'path' is on is mounted.
+// * If 'mount_point' is nullptr, use mount point in fstab. Caller can call
+//   fs_mgr_ensure_path_unmounted() with the same 'path' argument to unmount.
+// * If 'mount_point' is not nullptr, the mount point is overridden. Caller can
+//   call umount(mount_point) to unmount.
+// Returns true on success (volume is mounted).
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_point = "");
+
+// Make sure that the volume 'path' is on is unmounted.  Returns true on
+// success (volume is unmounted).
+bool EnsurePathUnmounted(Fstab* fstab, const std::string& path);
+
+// Return "/system" if it is in default fstab, otherwise "/".
+std::string GetSystemRoot();
+
+// Return true iff logical partitions are mapped when partitions are mounted via ensure_path_mounted
+// functions.
+bool LogicalPartitionsMapped();
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 22af123..90bce51 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -20,10 +20,6 @@
     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",
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index c4b57c7..d9786ad 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -203,7 +203,8 @@
         }
         next += vers->next;
         data_size -= vers->next;
-        vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+        vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) +
+                                                            next);
     }
 
     return true;
@@ -288,12 +289,23 @@
 }
 
 bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
+    return GetTable(name, 0, table);
+}
+
+bool DeviceMapper::GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {
+    return GetTable(name, DM_STATUS_TABLE_FLAG, table);
+}
+
+// private methods of DeviceMapper
+bool DeviceMapper::GetTable(const std::string& name, uint32_t flags,
+                            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);
+    io->flags = flags;
     if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
         PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
         return false;
@@ -327,7 +339,6 @@
     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));
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 91f8bb4..28e6e01 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -128,6 +128,10 @@
     };
     bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);
 
+    // Identical to GetTableStatus, except also retrives the active table for the device
+    // mapper device from the kernel.
+    bool GetTableInfo(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
@@ -140,6 +144,8 @@
     // limit we are imposing here of 256.
     static constexpr uint32_t kMaxPossibleDmDevices = 256;
 
+    bool GetTable(const std::string& name, uint32_t flags, std::vector<TargetInfo>* table);
+
     void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
 
     DeviceMapper();
diff --git a/fs_mgr/libfiemap_writer/.clang-format b/fs_mgr/libfiemap_writer/.clang-format
new file mode 120000
index 0000000..8b770a1
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/.clang-format
@@ -0,0 +1 @@
+../../.clang-format-4
\ No newline at end of file
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
new file mode 100644
index 0000000..33c3cad
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/Android.bp
@@ -0,0 +1,56 @@
+//
+// 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: "libfiemap_writer",
+    defaults: ["fs_mgr_defaults"],
+    recovery_available: true,
+    export_include_dirs: ["include"],
+    cflags: [
+        // TODO(b/121211685): Allows us to create a skeleton of required classes
+        "-Wno-unused-private-field",
+        "-Wno-unused-parameter",
+    ],
+
+    srcs: [
+        "fiemap_writer.cpp",
+    ],
+
+    header_libs: [
+        "libbase_headers",
+        "liblog_headers",
+    ],
+}
+
+cc_test {
+    name: "fiemap_writer_test",
+    static_libs: [
+        "libbase",
+        "libdm",
+        "libfiemap_writer",
+        "liblog",
+    ],
+
+    data: [
+        "testdata/unaligned_file",
+        "testdata/file_4k",
+        "testdata/file_32k",
+    ],
+
+    srcs: [
+        "fiemap_writer_test.cpp",
+    ],
+}
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
new file mode 100644
index 0000000..097e07f
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -0,0 +1,637 @@
+/*
+ * 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 <libfiemap_writer/fiemap_writer.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fiemap_writer {
+
+// We are expecting no more than 512 extents in a fiemap of the file we create.
+// If we find more, then it is treated as error for now.
+static constexpr const uint32_t kMaxExtents = 512;
+
+// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
+static constexpr const uint32_t kUnsupportedExtentFlags =
+        FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |
+        FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
+        FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
+
+static inline void cleanup(const std::string& file_path, bool created) {
+    if (created) {
+        unlink(file_path.c_str());
+    }
+}
+
+static bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
+    // The symlinks in /sys/dev/block point to the block device node under /sys/device/..
+    // The directory name in the target corresponds to the name of the block device. We use
+    // that to extract the block device name.
+    // e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as
+    // follows.
+    //    1:0 -> ../../devices/virtual/block/ram0
+    std::string sysfs_path = ::android::base::StringPrintf("/sys/dev/block/%u:%u", major, minor);
+    std::string sysfs_bdev;
+
+    if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {
+        PLOG(ERROR) << "Failed to read link at: " << sysfs_path;
+        return false;
+    }
+
+    *bdev_name = ::android::base::Basename(sysfs_bdev);
+    // Paranoid sanity check to make sure we just didn't get the
+    // input in return as-is.
+    if (sysfs_bdev == *bdev_name) {
+        LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
+        return false;
+    }
+
+    return true;
+}
+
+static bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
+    // TODO: Stop popping the device mapper stack if dm-linear target is found
+    if (!::android::base::StartsWith(bdev, "dm-")) {
+        // We are at the bottom of the device mapper stack.
+        *bdev_raw = bdev;
+        return true;
+    }
+
+    std::string dm_leaf_dir = ::android::base::StringPrintf("/sys/block/%s/slaves", bdev.c_str());
+    auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dm_leaf_dir.c_str()), closedir);
+    if (d == nullptr) {
+        PLOG(ERROR) << "Failed to open: " << dm_leaf_dir;
+        return false;
+    }
+
+    struct dirent* de;
+    uint32_t num_leaves = 0;
+    std::string bdev_next = "";
+    while ((de = readdir(d.get())) != nullptr) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+            continue;
+        }
+
+        // We set the first name we find here
+        if (bdev_next.empty()) {
+            bdev_next = de->d_name;
+        }
+        num_leaves++;
+    }
+
+    // if we have more than one leaves, we return immediately. We can't continue to create the
+    // file since we don't know how to write it out using fiemap, so it will be readable via the
+    // underlying block devices later. The reader will also have to construct the same device mapper
+    // target in order read the file out.
+    if (num_leaves > 1) {
+        LOG(ERROR) << "Found " << num_leaves << " leaf block devices under device mapper device "
+                   << bdev;
+        return false;
+    }
+
+    // recursively call with the block device we found in order to pop the device mapper stack.
+    return DeviceMapperStackPop(bdev_next, bdev_raw);
+}
+
+static bool FileToBlockDevicePath(const std::string& file_path, std::string* bdev_path) {
+    struct stat sb;
+    if (stat(file_path.c_str(), &sb)) {
+        PLOG(ERROR) << "Failed to get stat for: " << file_path;
+        return false;
+    }
+
+    std::string bdev;
+    if (!BlockDeviceToName(major(sb.st_dev), minor(sb.st_dev), &bdev)) {
+        LOG(ERROR) << "Failed to get block device name for " << major(sb.st_dev) << ":"
+                   << minor(sb.st_dev);
+        return false;
+    }
+
+    std::string bdev_raw;
+    if (!DeviceMapperStackPop(bdev, &bdev_raw)) {
+        LOG(ERROR) << "Failed to get the bottom of the device mapper stack for device: " << bdev;
+        return false;
+    }
+
+    LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
+               << bdev << ")";
+
+    *bdev_path = ::android::base::StringPrintf("/dev/block/%s", bdev_raw.c_str());
+
+    // Make sure we are talking to a block device before calling it a success.
+    if (stat(bdev_path->c_str(), &sb)) {
+        PLOG(ERROR) << "Failed to get stat for block device: " << *bdev_path;
+        return false;
+    }
+
+    if ((sb.st_mode & S_IFMT) != S_IFBLK) {
+        PLOG(ERROR) << "File: " << *bdev_path << " is not a block device";
+        return false;
+    }
+
+    return true;
+}
+
+static bool GetBlockDeviceParams(int bdev_fd, const std::string& bdev_path, uint64_t* blocksz,
+                                 uint64_t* bdev_size) {
+    // TODO: For some reason, the block device ioctl require the argument to be initialized
+    // to zero even if its the out parameter for the given ioctl cmd.
+    uint64_t blksz = 0;
+    if (ioctl(bdev_fd, BLKBSZGET, &blksz)) {
+        PLOG(ERROR) << "Failed to get block size for: " << bdev_path;
+        return false;
+    }
+
+    uint64_t size_in_bytes = 0;
+    if (ioctl(bdev_fd, BLKGETSIZE64, &size_in_bytes)) {
+        PLOG(ERROR) << "Failed to get total size for: " << bdev_path;
+        return false;
+    }
+
+    *blocksz = blksz;
+    *bdev_size = size_in_bytes;
+
+    return true;
+}
+
+static uint64_t GetFileSize(const std::string& file_path) {
+    struct stat sb;
+    if (stat(file_path.c_str(), &sb)) {
+        PLOG(ERROR) << "Failed to get size for file: " << file_path;
+        return 0;
+    }
+
+    return sb.st_size;
+}
+
+static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t blocksz,
+                              uint32_t* fs_type) {
+    // Check if the size aligned to the block size of the block device.
+    // We need this to be true in order to be able to write the file using FIEMAP.
+    if (file_size % blocksz) {
+        LOG(ERROR) << "File size " << file_size << " is not aligned to block size " << blocksz
+                   << " for file " << file_path;
+        return false;
+    }
+
+    struct statfs64 sfs;
+    if (statfs64(file_path.c_str(), &sfs)) {
+        PLOG(ERROR) << "Failed to read file system status at: " << file_path;
+        return false;
+    }
+
+    // Check if the filesystem is of supported types.
+    // Only ext4 and f2fs are tested and supported.
+    if ((sfs.f_type != EXT4_SUPER_MAGIC) && (sfs.f_type != F2FS_SUPER_MAGIC)) {
+        LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
+        return false;
+    }
+
+    uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
+    if (available_bytes <= file_size) {
+        LOG(ERROR) << "Not enough free space in file system to create file of size : " << file_size;
+        return false;
+    }
+
+    *fs_type = sfs.f_type;
+    return true;
+}
+
+static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
+                         uint64_t file_size) {
+    // Reserve space for the file on the file system and write it out to make sure the extents
+    // don't come back unwritten. Return from this function with the kernel file offset set to 0.
+    // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
+    // aren't moved around.
+    if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
+        PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
+        return false;
+    }
+
+    // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
+    // blocks are actually written to by the file system and thus getting rid of the holes in the
+    // file.
+    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
+    if (buffer == nullptr) {
+        LOG(ERROR) << "failed to allocate memory for writing file";
+        return false;
+    }
+
+    off64_t offset = lseek64(file_fd, 0, SEEK_SET);
+    if (offset < 0) {
+        PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
+        return false;
+    }
+
+    for (; offset < file_size; offset += blocksz) {
+        if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
+            PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
+                        << " in file " << file_path;
+            return false;
+        }
+    }
+
+    if (lseek64(file_fd, 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
+        return false;
+    }
+
+    // flush all writes here ..
+    if (fsync(file_fd)) {
+        PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
+        return false;
+    }
+
+    return true;
+}
+
+static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
+    if (fs_type == EXT4_SUPER_MAGIC) {
+        // No pinning necessary for ext4. The blocks, once allocated, are expected
+        // to be fixed.
+        return true;
+    }
+
+// F2FS-specific ioctl
+// It requires the below kernel commit merged in v4.16-rc1.
+//   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
+// In android-4.4,
+//   56ee1e817908 ("f2fs: updates on v4.16-rc1")
+// In android-4.9,
+//   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
+// In android-4.14,
+//   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
+#ifndef F2FS_IOC_SET_PIN_FILE
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#endif
+
+    uint32_t pin_status = 1;
+    int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);
+    if (error) {
+        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+            PLOG(ERROR) << "Failed to pin file, not supported by kernel: " << file_path;
+        } else {
+            PLOG(ERROR) << "Failed to pin file: " << file_path;
+        }
+        return false;
+    }
+
+    return true;
+}
+
+#if 0
+static bool PinFileStatus(int file_fd, const std::string& file_path, uint32_t fs_type) {
+    if (fs_type == EXT4_SUPER_MAGIC) {
+        // No pinning necessary for ext4. The blocks, once allocated, are expected
+        // to be fixed.
+        return true;
+    }
+
+// F2FS-specific ioctl
+// It requires the below kernel commit merged in v4.16-rc1.
+//   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
+// In android-4.4,
+//   56ee1e817908 ("f2fs: updates on v4.16-rc1")
+// In android-4.9,
+//   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
+// In android-4.14,
+//   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
+#ifndef F2FS_IOC_GET_PIN_FILE
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
+#endif
+
+    uint32_t pin_status;
+    int error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &pin_status);
+    if (error) {
+        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+            PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
+        } else {
+            PLOG(ERROR) << "Failed to get file pin status: " << file_path;
+        }
+        return false;
+    }
+
+    return !!pin_status;
+}
+#endif
+
+static void LogExtent(uint32_t num, const struct fiemap_extent& ext) {
+    LOG(INFO) << "Extent #" << num;
+    LOG(INFO) << "  fe_logical:  " << ext.fe_logical;
+    LOG(INFO) << "  fe_physical: " << ext.fe_physical;
+    LOG(INFO) << "  fe_length:   " << ext.fe_length;
+    LOG(INFO) << "  fe_flags:    0x" << std::hex << ext.fe_flags;
+}
+
+static bool ReadFiemap(int file_fd, const std::string& file_path,
+                       std::vector<struct fiemap_extent>* extents) {
+    uint64_t fiemap_size =
+            sizeof(struct fiemap_extent) + kMaxExtents * sizeof(struct fiemap_extent);
+    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
+    if (buffer == nullptr) {
+        LOG(ERROR) << "Failed to allocate memory for fiemap";
+        return false;
+    }
+
+    struct fiemap* fiemap = reinterpret_cast<struct fiemap*>(buffer.get());
+    fiemap->fm_start = 0;
+    fiemap->fm_length = UINT64_MAX;
+    // make sure file is synced to disk before we read the fiemap
+    fiemap->fm_flags = FIEMAP_FLAG_SYNC;
+    fiemap->fm_extent_count = kMaxExtents;
+
+    if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
+        PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
+        return false;
+    }
+
+    if (fiemap->fm_mapped_extents == 0) {
+        LOG(ERROR) << "File " << file_path << " has zero extents";
+        return false;
+    }
+
+    // Iterate through each extent read and make sure its valid before adding it to the vector
+    bool last_extent_seen = false;
+    struct fiemap_extent* extent = &fiemap->fm_extents[0];
+    for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++, extent++) {
+        // LogExtent(i + 1, *extent);
+        if (extent->fe_flags & kUnsupportedExtentFlags) {
+            LOG(ERROR) << "Extent " << i + 1 << " of file " << file_path
+                       << " has unsupported flags";
+            extents->clear();
+            return false;
+        }
+
+        if (extent->fe_flags & FIEMAP_EXTENT_LAST) {
+            last_extent_seen = true;
+            if (i != (fiemap->fm_mapped_extents - 1)) {
+                LOG(WARNING) << "Extents are being received out-of-order";
+            }
+        }
+        extents->emplace_back(std::move(*extent));
+    }
+
+    if (!last_extent_seen) {
+        // The file is possibly too fragmented.
+        if (fiemap->fm_mapped_extents == kMaxExtents) {
+            LOG(ERROR) << "File is too fragmented, needs more than " << kMaxExtents << " extents.";
+        }
+        extents->clear();
+    }
+
+    return last_extent_seen;
+}
+
+FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create) {
+    // if 'create' is false, open an existing file and do not truncate.
+    int open_flags = O_RDWR | O_CLOEXEC;
+    if (create) {
+        if (access(file_path.c_str(), F_OK) == 0) {
+            LOG(WARNING) << "File " << file_path << " already exists, truncating";
+        }
+        open_flags |= O_CREAT | O_TRUNC;
+    }
+    ::android::base::unique_fd file_fd(
+            TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
+    if (file_fd < 0) {
+        PLOG(ERROR) << "Failed to create file at: " << file_path;
+        return nullptr;
+    }
+
+    std::string abs_path;
+    if (!::android::base::Realpath(file_path, &abs_path)) {
+        PLOG(ERROR) << "Invalid file path: " << file_path;
+        cleanup(file_path, create);
+        return nullptr;
+    }
+
+    std::string bdev_path;
+    if (!FileToBlockDevicePath(abs_path, &bdev_path)) {
+        LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
+        cleanup(file_path, create);
+        return nullptr;
+    }
+
+    ::android::base::unique_fd bdev_fd(
+            TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDWR | O_CLOEXEC)));
+    if (bdev_fd < 0) {
+        PLOG(ERROR) << "Failed to open block device: " << bdev_path;
+        cleanup(file_path, create);
+        return nullptr;
+    }
+
+    uint64_t blocksz, bdevsz;
+    if (!GetBlockDeviceParams(bdev_fd, bdev_path, &blocksz, &bdevsz)) {
+        LOG(ERROR) << "Failed to get block device params for: " << bdev_path;
+        cleanup(file_path, create);
+        return nullptr;
+    }
+
+    if (!create) {
+        file_size = GetFileSize(abs_path);
+        if (file_size == 0) {
+            LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
+            return nullptr;
+        }
+    }
+
+    uint32_t fs_type;
+    if (!PerformFileChecks(abs_path, file_size, blocksz, &fs_type)) {
+        LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
+        cleanup(file_path, create);
+        return nullptr;
+    }
+
+    if (create) {
+        if (!AllocateFile(file_fd, abs_path, blocksz, file_size)) {
+            unlink(abs_path.c_str());
+            return nullptr;
+        }
+    }
+
+    // f2fs may move the file blocks around.
+    if (!PinFile(file_fd, file_path, fs_type)) {
+        cleanup(file_path, create);
+        LOG(ERROR) << "Failed to pin the file in storage";
+        return nullptr;
+    }
+
+    // now allocate the FiemapWriter and start setting it up
+    FiemapUniquePtr fmap(new FiemapWriter());
+    if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
+        LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
+        cleanup(file_path, create);
+        return nullptr;
+    }
+
+    fmap->file_path_ = abs_path;
+    fmap->bdev_path_ = bdev_path;
+    fmap->file_fd_ = std::move(file_fd);
+    fmap->bdev_fd_ = std::move(bdev_fd);
+    fmap->file_size_ = file_size;
+    fmap->bdev_size_ = bdevsz;
+    fmap->fs_type_ = fs_type;
+    fmap->block_size_ = blocksz;
+
+    LOG(INFO) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
+              << bdev_path;
+    return fmap;
+}
+
+bool FiemapWriter::Flush() const {
+    if (fsync(bdev_fd_)) {
+        PLOG(ERROR) << "Failed to flush " << bdev_path_ << " with fsync";
+        return false;
+    }
+    return true;
+}
+
+// TODO: Test with fs block_size > bdev block_size
+bool FiemapWriter::Write(off64_t off, uint8_t* buffer, uint64_t size) {
+    if (!size || size > file_size_) {
+        LOG(ERROR) << "Failed write: size " << size << " is invalid for file's size " << file_size_;
+        return false;
+    }
+
+    if (off + size > file_size_) {
+        LOG(ERROR) << "Failed write: Invalid offset " << off << " or size " << size
+                   << " for file size " << file_size_;
+        return false;
+    }
+
+    if ((off & (block_size_ - 1)) || (size & (block_size_ - 1))) {
+        LOG(ERROR) << "Failed write: Unaligned offset " << off << " or size " << size
+                   << " for block size " << block_size_;
+        return false;
+    }
+#if 0
+    // TODO(b/122138114): check why this fails.
+    if (!PinFileStatus(file_fd_, file_path_, fs_type_)) {
+        LOG(ERROR) << "Failed write: file " << file_path_ << " is not pinned";
+        return false;
+    }
+#endif
+    // find extents that must be written to and then write one at a time.
+    uint32_t num_extent = 1;
+    uint32_t buffer_offset = 0;
+    for (auto& extent : extents_) {
+        uint64_t e_start = extent.fe_logical;
+        uint64_t e_end = extent.fe_logical + extent.fe_length;
+        // Do we write in this extent ?
+        if (off >= e_start && off < e_end) {
+            uint64_t written = WriteExtent(extent, buffer + buffer_offset, off, size);
+            if (written == 0) {
+                return false;
+            }
+
+            buffer_offset += written;
+            off += written;
+            size -= written;
+
+            // Paranoid check to make sure we are done with this extent now
+            if (size && (off >= e_start && off < e_end)) {
+                LOG(ERROR) << "Failed to write extent fully";
+                LogExtent(num_extent, extent);
+                return false;
+            }
+
+            if (size == 0) {
+                // done
+                break;
+            }
+        }
+        num_extent++;
+    }
+
+    return true;
+}
+
+bool FiemapWriter::Read(off64_t off, uint8_t* buffer, uint64_t size) {
+    return false;
+}
+
+// private helpers
+
+// WriteExtent() Returns the total number of bytes written. It will always be multiple of
+// block_size_. 0 is returned in one of the two cases.
+//  1. Any write failed between logical_off & logical_off + length.
+//  2. The logical_offset + length doesn't overlap with the extent passed.
+// The function can either partially for fully write the extent depending on the
+// logical_off + length. It is expected that alignment checks for size and offset are
+// performed before calling into this function.
+uint64_t FiemapWriter::WriteExtent(const struct fiemap_extent& ext, uint8_t* buffer,
+                                   off64_t logical_off, uint64_t length) {
+    uint64_t e_start = ext.fe_logical;
+    uint64_t e_end = ext.fe_logical + ext.fe_length;
+    if (logical_off < e_start || logical_off >= e_end) {
+        LOG(ERROR) << "Failed write extent, invalid offset " << logical_off << " and size "
+                   << length;
+        LogExtent(0, ext);
+        return 0;
+    }
+
+    off64_t bdev_offset = ext.fe_physical + (logical_off - e_start);
+    if (bdev_offset >= bdev_size_) {
+        LOG(ERROR) << "Failed write extent, invalid block # " << bdev_offset << " for block device "
+                   << bdev_path_ << " of size " << bdev_size_ << " bytes";
+        return 0;
+    }
+    if (TEMP_FAILURE_RETRY(lseek64(bdev_fd_, bdev_offset, SEEK_SET)) == -1) {
+        PLOG(ERROR) << "Failed write extent, seek offset for " << bdev_path_ << " offset "
+                    << bdev_offset;
+        return 0;
+    }
+
+    // Determine how much we want to write at once.
+    uint64_t logical_end = logical_off + length;
+    uint64_t write_size = (e_end <= logical_end) ? (e_end - logical_off) : length;
+    if (!android::base::WriteFully(bdev_fd_, buffer, write_size)) {
+        PLOG(ERROR) << "Failed write extent, write " << bdev_path_ << " at " << bdev_offset
+                    << " size " << write_size;
+        return 0;
+    }
+
+    return write_size;
+}
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
new file mode 100644
index 0000000..6653ada
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -0,0 +1,360 @@
+/*
+ * 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 <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <libdm/loop_control.h>
+
+#include <libfiemap_writer/fiemap_writer.h>
+
+using namespace std;
+using namespace android::fiemap_writer;
+using unique_fd = android::base::unique_fd;
+using LoopDevice = android::dm::LoopDevice;
+
+std::string testfile = "";
+std::string testbdev = "";
+uint64_t testfile_size = 536870912;  // default of 512MiB
+
+TEST(FiemapWriter, CreateImpossiblyLargeFile) {
+    // Try creating a file of size ~100TB but aligned to
+    // 512 byte to make sure block alignment tests don't
+    // fail.
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1099511627997184);
+    EXPECT_EQ(fptr, nullptr);
+    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
+    EXPECT_EQ(errno, ENOENT);
+}
+
+TEST(FiemapWriter, CreateUnalignedFile) {
+    // Try creating a file of size 4097 bytes which is guaranteed
+    // to be unaligned to all known block sizes. The creation must
+    // fail.
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4097);
+    EXPECT_EQ(fptr, nullptr);
+    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
+    EXPECT_EQ(errno, ENOENT);
+}
+
+TEST(FiemapWriter, CheckFilePath) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
+    ASSERT_NE(fptr, nullptr);
+    EXPECT_EQ(fptr->size(), 4096);
+    EXPECT_EQ(fptr->file_path(), testfile);
+    EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
+}
+
+TEST(FiemapWriter, CheckBlockDevicePath) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
+    EXPECT_EQ(fptr->size(), 4096);
+    EXPECT_EQ(fptr->bdev_path(), testbdev);
+}
+
+TEST(FiemapWriter, CheckFileCreated) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 32768);
+    ASSERT_NE(fptr, nullptr);
+    unique_fd fd(open(testfile.c_str(), O_RDONLY));
+    EXPECT_GT(fd, -1);
+}
+
+TEST(FiemapWriter, CheckFileSizeActual) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
+    ASSERT_NE(fptr, nullptr);
+
+    struct stat sb;
+    ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
+    EXPECT_EQ(sb.st_size, testfile_size);
+}
+
+TEST(FiemapWriter, CheckFileExtents) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
+    ASSERT_NE(fptr, nullptr);
+    EXPECT_GT(fptr->extents().size(), 0);
+}
+
+TEST(FiemapWriter, CheckWriteError) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
+    ASSERT_NE(fptr, nullptr);
+
+    // prepare buffer for writing the pattern - 0xa0
+    uint64_t blocksize = fptr->block_size();
+    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksize), free);
+    ASSERT_NE(buffer, nullptr);
+    memset(buffer.get(), 0xa0, blocksize);
+
+    uint8_t* p = static_cast<uint8_t*>(buffer.get());
+    for (off64_t off = 0; off < testfile_size; off += blocksize) {
+        ASSERT_TRUE(fptr->Write(off, p, blocksize));
+    }
+
+    EXPECT_TRUE(fptr->Flush());
+}
+
+class TestExistingFile : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        std::string exec_dir = ::android::base::GetExecutableDirectory();
+        std::string unaligned_file = exec_dir + "/testdata/unaligned_file";
+        std::string file_4k = exec_dir + "/testdata/file_4k";
+        std::string file_32k = exec_dir + "/testdata/file_32k";
+        fptr_unaligned = FiemapWriter::Open(unaligned_file, 4097, false);
+        fptr_4k = FiemapWriter::Open(file_4k, 4096, false);
+        fptr_32k = FiemapWriter::Open(file_32k, 32768, false);
+    }
+
+    FiemapUniquePtr fptr_unaligned;
+    FiemapUniquePtr fptr_4k;
+    FiemapUniquePtr fptr_32k;
+};
+
+TEST_F(TestExistingFile, ErrorChecks) {
+    EXPECT_EQ(fptr_unaligned, nullptr);
+    EXPECT_NE(fptr_4k, nullptr);
+    EXPECT_NE(fptr_32k, nullptr);
+
+    EXPECT_EQ(fptr_4k->size(), 4096);
+    EXPECT_EQ(fptr_32k->size(), 32768);
+    EXPECT_GT(fptr_4k->extents().size(), 0);
+    EXPECT_GT(fptr_32k->extents().size(), 0);
+}
+
+TEST_F(TestExistingFile, CheckWriteError) {
+    ASSERT_NE(fptr_4k, nullptr);
+    // prepare buffer for writing the pattern - 0xa0
+    uint64_t blocksize = fptr_4k->block_size();
+    auto buff_4k = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksize), free);
+    ASSERT_NE(buff_4k, nullptr);
+    memset(buff_4k.get(), 0xa0, blocksize);
+
+    uint8_t* p = static_cast<uint8_t*>(buff_4k.get());
+    for (off64_t off = 0; off < 4096; off += blocksize) {
+        ASSERT_TRUE(fptr_4k->Write(off, p, blocksize));
+    }
+    EXPECT_TRUE(fptr_4k->Flush());
+
+    ASSERT_NE(fptr_32k, nullptr);
+    // prepare buffer for writing the pattern - 0xa0
+    blocksize = fptr_32k->block_size();
+    auto buff_32k = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksize), free);
+    ASSERT_NE(buff_32k, nullptr);
+    memset(buff_32k.get(), 0xa0, blocksize);
+    p = static_cast<uint8_t*>(buff_32k.get());
+    for (off64_t off = 0; off < 4096; off += blocksize) {
+        ASSERT_TRUE(fptr_32k->Write(off, p, blocksize));
+    }
+    EXPECT_TRUE(fptr_32k->Flush());
+}
+
+class VerifyBlockWritesExt4 : public ::testing::Test {
+    // 2GB Filesystem and 4k block size by default
+    static constexpr uint64_t block_size = 4096;
+    static constexpr uint64_t fs_size = 2147483648;
+
+  protected:
+    void SetUp() override {
+        fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
+        uint64_t count = fs_size / block_size;
+        std::string dd_cmd =
+                ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
+                                              " count=%" PRIu64 " > /dev/null 2>&1",
+                                              fs_path.c_str(), block_size, count);
+        std::string mkfs_cmd =
+                ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
+        // create mount point
+        mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
+        ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+        // create file for the file system
+        int ret = system(dd_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+        // Get and attach a loop device to the filesystem we created
+        LoopDevice loop_dev(fs_path);
+        ASSERT_TRUE(loop_dev.valid());
+        // create file system
+        ret = system(mkfs_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+
+        // mount the file system
+        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
+    }
+
+    void TearDown() override {
+        umount(mntpoint.c_str());
+        rmdir(mntpoint.c_str());
+        unlink(fs_path.c_str());
+    }
+
+    std::string mntpoint;
+    std::string fs_path;
+};
+
+TEST_F(VerifyBlockWritesExt4, CheckWrites) {
+    EXPECT_EQ(access(fs_path.c_str(), F_OK), 0);
+
+    std::string file_path = mntpoint + "/testfile";
+    uint64_t file_size = 100 * 1024 * 1024;
+    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
+    ASSERT_NE(buffer, nullptr);
+    memset(buffer.get(), 0xa0, getpagesize());
+    {
+        // scoped fiemap writer
+        FiemapUniquePtr fptr = FiemapWriter::Open(file_path, file_size);
+        ASSERT_NE(fptr, nullptr);
+        uint8_t* p = static_cast<uint8_t*>(buffer.get());
+        for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
+            ASSERT_TRUE(fptr->Write(off, p, getpagesize()));
+        }
+        EXPECT_TRUE(fptr->Flush());
+    }
+    // unmount file system here to make sure we invalidated all page cache and
+    // remount the filesystem again for verification
+    ASSERT_EQ(umount(mntpoint.c_str()), 0);
+
+    LoopDevice loop_dev(fs_path);
+    ASSERT_TRUE(loop_dev.valid());
+    ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0)
+            << "failed to mount: " << loop_dev.device() << " on " << mntpoint << ": "
+            << strerror(errno);
+
+    ::android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_SYNC));
+    ASSERT_NE(fd, -1);
+    auto filebuf = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
+    ASSERT_NE(filebuf, nullptr);
+    for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
+        memset(filebuf.get(), 0x00, getpagesize());
+        ASSERT_EQ(pread64(fd, filebuf.get(), getpagesize(), off), getpagesize());
+        ASSERT_EQ(memcmp(filebuf.get(), buffer.get(), getpagesize()), 0)
+                << "Invalid pattern at offset: " << off << " size " << getpagesize();
+    }
+}
+
+class VerifyBlockWritesF2fs : public ::testing::Test {
+    // 2GB Filesystem and 4k block size by default
+    static constexpr uint64_t block_size = 4096;
+    static constexpr uint64_t fs_size = 2147483648;
+
+  protected:
+    void SetUp() override {
+        fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
+        uint64_t count = fs_size / block_size;
+        std::string dd_cmd =
+                ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
+                                              " count=%" PRIu64 " > /dev/null 2>&1",
+                                              fs_path.c_str(), block_size, count);
+        std::string mkfs_cmd =
+                ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
+        // create mount point
+        mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
+        ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+        // create file for the file system
+        int ret = system(dd_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+        // Get and attach a loop device to the filesystem we created
+        LoopDevice loop_dev(fs_path);
+        ASSERT_TRUE(loop_dev.valid());
+        // create file system
+        ret = system(mkfs_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+
+        // mount the file system
+        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
+    }
+
+    void TearDown() override {
+        umount(mntpoint.c_str());
+        rmdir(mntpoint.c_str());
+        unlink(fs_path.c_str());
+    }
+
+    std::string mntpoint;
+    std::string fs_path;
+};
+
+TEST_F(VerifyBlockWritesF2fs, CheckWrites) {
+    EXPECT_EQ(access(fs_path.c_str(), F_OK), 0);
+
+    std::string file_path = mntpoint + "/testfile";
+    uint64_t file_size = 100 * 1024 * 1024;
+    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
+    ASSERT_NE(buffer, nullptr);
+    memset(buffer.get(), 0xa0, getpagesize());
+    {
+        // scoped fiemap writer
+        FiemapUniquePtr fptr = FiemapWriter::Open(file_path, file_size);
+        ASSERT_NE(fptr, nullptr);
+        uint8_t* p = static_cast<uint8_t*>(buffer.get());
+        for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
+            ASSERT_TRUE(fptr->Write(off, p, getpagesize()));
+        }
+        EXPECT_TRUE(fptr->Flush());
+    }
+    // unmount file system here to make sure we invalidated all page cache and
+    // remount the filesystem again for verification
+    ASSERT_EQ(umount(mntpoint.c_str()), 0);
+
+    LoopDevice loop_dev(fs_path);
+    ASSERT_TRUE(loop_dev.valid());
+    ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0)
+            << "failed to mount: " << loop_dev.device() << " on " << mntpoint << ": "
+            << strerror(errno);
+
+    ::android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_SYNC));
+    ASSERT_NE(fd, -1);
+    auto filebuf = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
+    ASSERT_NE(filebuf, nullptr);
+    for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
+        memset(filebuf.get(), 0x00, getpagesize());
+        ASSERT_EQ(pread64(fd, filebuf.get(), getpagesize(), off), getpagesize());
+        ASSERT_EQ(memcmp(filebuf.get(), buffer.get(), getpagesize()), 0)
+                << "Invalid pattern at offset: " << off << " size " << getpagesize();
+    }
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    if (argc <= 2) {
+        cerr << "Filepath with its bdev path must be provided as follows:" << endl;
+        cerr << "  $ fiemap_writer_test <path to file> </dev/block/XXXX" << endl;
+        cerr << "  where, /dev/block/XXX is the block device where the file resides" << endl;
+        exit(EXIT_FAILURE);
+    }
+    ::android::base::InitLogging(argv, ::android::base::StderrLogger);
+
+    testfile = argv[1];
+    testbdev = argv[2];
+    if (argc > 3) {
+        testfile_size = strtoull(argv[3], NULL, 0);
+        if (testfile_size == ULLONG_MAX) {
+            testfile_size = 512 * 1024 * 1024;
+        }
+    }
+
+    return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
new file mode 100644
index 0000000..ae61344
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -0,0 +1,104 @@
+/*
+ * 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 <linux/fiemap.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fiemap_writer {
+
+class FiemapWriter;
+using FiemapUniquePtr = std::unique_ptr<FiemapWriter>;
+
+class FiemapWriter final {
+  public:
+    // Factory method for FiemapWriter.
+    // The method returns FiemapUniquePtr that contains all the data necessary to be able to write
+    // to the given file directly using raw block i/o.
+    static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
+                                bool create = true);
+
+    // Syncs block device writes.
+    bool Flush() const;
+
+    // Writes the file by using its FIEMAP and performing i/o on the raw block device.
+    // The return value is success / failure. This will happen in particular if the
+    // kernel write returns errors, extents are not writeable or more importantly, if the 'size' is
+    // not aligned to the block device's block size.
+    bool Write(off64_t off, uint8_t* buffer, uint64_t size);
+
+    // The counter part of Write(). It is an error for the offset to be unaligned with
+    // the block device's block size.
+    // In case of error, the contents of buffer MUST be discarded.
+    bool Read(off64_t off, uint8_t* buffer, uint64_t size);
+
+    ~FiemapWriter() = default;
+
+    const std::string& file_path() const { return file_path_; };
+    uint64_t size() const { return file_size_; };
+    const std::string& bdev_path() const { return bdev_path_; };
+    uint64_t block_size() const { return block_size_; };
+    const std::vector<struct fiemap_extent>& extents() { return extents_; };
+
+    // Non-copyable & Non-movable
+    FiemapWriter(const FiemapWriter&) = delete;
+    FiemapWriter& operator=(const FiemapWriter&) = delete;
+    FiemapWriter& operator=(FiemapWriter&&) = delete;
+    FiemapWriter(FiemapWriter&&) = delete;
+
+  private:
+    // Name of the file managed by this class.
+    std::string file_path_;
+    // Block device on which we have created the file.
+    std::string bdev_path_;
+
+    // File descriptors for the file and block device
+    ::android::base::unique_fd file_fd_;
+    ::android::base::unique_fd bdev_fd_;
+
+    // Size in bytes of the file this class is writing
+    uint64_t file_size_;
+
+    // total size in bytes of the block device
+    uint64_t bdev_size_;
+
+    // Filesystem type where the file is being created.
+    // See: <uapi/linux/magic.h> for filesystem magic numbers
+    uint32_t fs_type_;
+
+    // block size as reported by the kernel of the underlying block device;
+    uint64_t block_size_;
+
+    // This file's fiemap
+    std::vector<struct fiemap_extent> extents_;
+
+    FiemapWriter() = default;
+
+    uint64_t WriteExtent(const struct fiemap_extent& ext, uint8_t* buffer, off64_t logical_off,
+                         uint64_t length);
+};
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/testdata/file_32k b/fs_mgr/libfiemap_writer/testdata/file_32k
new file mode 100644
index 0000000..12f3be4
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/testdata/file_32k
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/file_4k b/fs_mgr/libfiemap_writer/testdata/file_4k
new file mode 100644
index 0000000..08e7df1
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/testdata/file_4k
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/unaligned_file b/fs_mgr/libfiemap_writer/testdata/unaligned_file
new file mode 100644
index 0000000..c107c26
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/testdata/unaligned_file
Binary files differ
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 07f9d66..07e3c8a 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -212,7 +212,7 @@
     sABOverrideValue = ab_device;
 }
 
-MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
+MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false), ignore_slot_suffixing_(false) {
     memset(&geometry_, 0, sizeof(geometry_));
     geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
     geometry_.struct_size = sizeof(geometry_);
@@ -436,7 +436,7 @@
         LERROR << "Could not find partition group: " << group_name;
         return nullptr;
     }
-    if (IsABDevice() && !auto_slot_suffixing_ && name != "scratch" &&
+    if (IsABDevice() && !auto_slot_suffixing_ && name != "scratch" && !ignore_slot_suffixing_ &&
         GetPartitionSlotSuffix(name).empty()) {
         LERROR << "Unsuffixed partition not allowed on A/B device: " << name;
         return nullptr;
@@ -972,6 +972,10 @@
     auto_slot_suffixing_ = true;
 }
 
+void MetadataBuilder::IgnoreSlotSuffixing() {
+    ignore_slot_suffixing_ = true;
+}
+
 bool MetadataBuilder::IsABDevice() const {
     if (sABOverrideSet) {
         return sABOverrideValue;
@@ -983,5 +987,18 @@
     return GetBlockDevicePartitionName(block_devices_[0]) != LP_METADATA_DEFAULT_PARTITION_NAME;
 }
 
+bool MetadataBuilder::AddLinearExtent(Partition* partition, const std::string& block_device,
+                                      uint64_t num_sectors, uint64_t physical_sector) {
+    uint32_t device_index;
+    if (!FindBlockDeviceByName(block_device, &device_index)) {
+        LERROR << "Could not find backing block device for extent: " << block_device;
+        return false;
+    }
+
+    auto extent = std::make_unique<LinearExtent>(num_sectors, device_index, physical_sector);
+    partition->AddExtent(std::move(extent));
+    return true;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index f477b4b..57cce21 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -219,6 +219,10 @@
     // Find a group by name. If no group is found, nullptr is returned.
     PartitionGroup* FindGroup(const std::string& name);
 
+    // Add a predetermined extent to a partition.
+    bool AddLinearExtent(Partition* partition, const std::string& block_device,
+                         uint64_t num_sectors, uint64_t physical_sector);
+
     // Grow or shrink a partition to the requested size. This size will be
     // rounded UP to the nearest block (512 bytes).
     //
@@ -244,6 +248,9 @@
     // Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag.
     void SetAutoSlotSuffixing();
 
+    // If set, checks for slot suffixes will be ignored internally.
+    void IgnoreSlotSuffixing();
+
     bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
     bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
 
@@ -306,6 +313,7 @@
     std::vector<std::unique_ptr<PartitionGroup>> groups_;
     std::vector<LpMetadataBlockDevice> block_devices_;
     bool auto_slot_suffixing_;
+    bool ignore_slot_suffixing_;
 };
 
 // Read BlockDeviceInfo for a given block device. This always returns false
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 5957e30..9e211e3 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -92,7 +92,7 @@
 
 Returns: true if device is (likely) a debug build" ]
 isDebuggable() {
-  if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then
+  if inAdb && [ 1 -ne "`get_property ro.debuggable`" ]; then
     false
   fi
 }
@@ -389,16 +389,16 @@
   die -t "${T}" "disable-verity"
 fi
 rebooted=false
-if [ X"${D}" != X"${H}" -a X"${D}" = X"${D##*using overlayfs}" ]; then
+if [ X"${D}" != X"${H}" ]; then
   echo "${H}"
   if [ X"${D}" != X"${D##*setup failed}" ]; then
     echo "${ORANGE}[  WARNING ]${NORMAL} overlayfs setup whined" >&2
   fi
   D=`adb_sh df -k </dev/null` &&
     H=`echo "${D}" | head -1` &&
-    D=`echo "${D}" | grep "^overlay "` &&
-    [ -n "${D}" ] &&
-    ( echo "${H}" && echo "${D}" ) &&
+    D=`echo "${D}" | grep "^overlay " || true` &&
+    [ -z "${D}" ] ||
+    ( echo "${H}" && echo "${D}" && false ) ||
     die -t ${T} "overlay takeover unexpected at this phase"
   echo "${GREEN}[     INFO ]${NORMAL} rebooting as requested" >&2
   L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
@@ -426,12 +426,12 @@
   die -t "${T}" "setup for overlay"
 fi
 if [ X"${D}" != X"${D##*Successfully disabled verity}" ]; then
-  echo "${D}"
+  echo "${H}"
   D=`adb_sh df -k </dev/null` &&
     H=`echo "${D}" | head -1` &&
-    D=`echo "${D}" | grep "^overlay " | true` &&
-    [ -n "${D}" ] &&
-    ( echo "${H}" && echo "${D}" ) &&
+    D=`echo "${D}" | grep "^overlay " || true` &&
+    [ -z "${D}" ] ||
+    ( echo "${H}" && echo "${D}" && false ) ||
     ( [ -n "${L}" ] && echo "${L}" && false ) ||
     die -t "${T}" "overlay takeover unexpected"
   [ -n "${L}" ] && echo "${L}"
@@ -504,17 +504,17 @@
 
 # Check something
 
-echo "${GREEN}[ RUN      ]${NORMAL} push content to system and vendor" >&2
+echo "${GREEN}[ RUN      ]${NORMAL} push content to /system and /vendor" >&2
 
 A="Hello World! $(date)"
 echo "${A}" | adb_sh "cat - > /system/hello"
 echo "${A}" | adb_sh "cat - > /vendor/hello"
 B="`adb_cat /system/hello`" ||
   die "sytem hello"
-check_eq "${A}" "${B}" system before reboot
+check_eq "${A}" "${B}" /system before reboot
 B="`adb_cat /vendor/hello`" ||
   die "vendor hello"
-check_eq "${A}" "${B}" vendor before reboot
+check_eq "${A}" "${B}" /vendor before reboot
 
 echo "${GREEN}[ RUN      ]${NORMAL} reboot to confirm content persistent" >&2
 
@@ -537,18 +537,21 @@
 fi
 
 B="`adb_cat /system/hello`" ||
-  die "re-read system hello after reboot"
-check_eq "${A}" "${B}" system after reboot
+  die "re-read /system/hello after reboot"
+check_eq "${A}" "${B}" /system after reboot
+echo "${GREEN}[       OK ]${NORMAL} /system content remains after reboot" >&2
 # Only root can read vendor if sepolicy permissions are as expected
 if ${enforcing}; then
   B="`adb_cat /vendor/hello`" &&
-    die "re-read vendor hello after reboot w/o root"
+    die "re-read /vendor/hello after reboot w/o root"
   check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
+  echo "${GREEN}[       OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
 fi
 adb_root &&
   B="`adb_cat /vendor/hello`" ||
-  die "re-read vendor hello after reboot"
+  die "re-read /vendor/hello after reboot"
 check_eq "${A}" "${B}" vendor after reboot
+echo "${GREEN}[       OK ]${NORMAL} /vendor content remains after reboot" >&2
 
 echo "${GREEN}[ RUN      ]${NORMAL} flash vendor, confirm its content disappears" >&2
 
@@ -608,17 +611,17 @@
       echo "${H}" &&
       echo "${D}" &&
       echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
-      die  "overlay system takeover after flash vendor"
+      die  "overlay /system takeover after flash vendor"
     echo "${D}" | grep "^overlay .* /vendor\$" >/dev/null &&
-      die  "overlay minus vendor takeover after flash vendor"
+      die  "overlay supposed to be minus /vendor takeover after flash vendor"
   fi
   B="`adb_cat /system/hello`" ||
-    die "re-read system hello after flash vendor"
+    die "re-read /system/hello after flash vendor"
   check_eq "${A}" "${B}" system after flash vendor
   adb_root ||
     die "adb root"
   B="`adb_cat /vendor/hello`" &&
-    die "re-read vendor hello after flash vendor"
+    die "re-read /vendor/hello after flash vendor"
   check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
 fi
 
@@ -630,10 +633,10 @@
   adb_sh rm /system/hello </dev/null ||
   die -t ${T} "cleanup hello"
 B="`adb_cat /system/hello`" &&
-  die "re-read system hello after rm"
+  die "re-read /system/hello after rm"
 check_eq "cat: /system/hello: No such file or directory" "${B}" after flash rm
 B="`adb_cat /vendor/hello`" &&
-  die "re-read vendor hello after rm"
+  die "re-read /vendor/hello after rm"
 check_eq "cat: /vendor/hello: No such file or directory" "${B}" after flash rm
 
 if [ -n "${scratch_partition}" ]; then
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index f78093b..3b6ff9b 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -36,6 +36,8 @@
 #include <string>
 #include <vector>
 
+using namespace std::literals::string_literals;
+
 using DeviceMapper = ::android::dm::DeviceMapper;
 using DmTable = ::android::dm::DmTable;
 using DmTarget = ::android::dm::DmTarget;
@@ -51,7 +53,7 @@
     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 << "  list <devices | targets> [-v]" << std::endl;
     std::cerr << "  getpath <dm-name>" << std::endl;
     std::cerr << "  table <dm-name>" << std::endl;
     std::cerr << "  help" << std::endl;
@@ -197,7 +199,8 @@
     return 0;
 }
 
-static int DmListTargets(DeviceMapper& dm) {
+static int DmListTargets(DeviceMapper& dm, [[maybe_unused]] int argc,
+                         [[maybe_unused]] char** argv) {
     std::vector<DmTargetTypeInfo> targets;
     if (!dm.GetAvailableTargets(&targets)) {
         std::cerr << "Failed to read available device mapper targets" << std::endl;
@@ -218,7 +221,7 @@
     return 0;
 }
 
-static int DmListDevices(DeviceMapper& dm) {
+static int DmListDevices(DeviceMapper& dm, int argc, char** argv) {
     std::vector<DmBlockDevice> devices;
     if (!dm.GetAvailableDevices(&devices)) {
         std::cerr << "Failed to read available device mapper devices" << std::endl;
@@ -230,15 +233,37 @@
         return 0;
     }
 
+    bool verbose = (argc && (argv[0] == "-v"s));
     for (const auto& dev : devices) {
         std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
                   << dev.Minor() << std::endl;
+        if (verbose) {
+            std::vector<DeviceMapper::TargetInfo> table;
+            if (!dm.GetTableInfo(dev.name(), &table)) {
+                std::cerr << "Could not query table status for device \"" << dev.name() << "\"."
+                          << std::endl;
+                return -EINVAL;
+            }
+
+            uint32_t target_num = 1;
+            for (const auto& target : table) {
+                std::cout << "  target#" << target_num << ": ";
+                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;
+                target_num++;
+            }
+        }
     }
 
     return 0;
 }
 
-static const std::map<std::string, std::function<int(DeviceMapper&)>> listmap = {
+static const std::map<std::string, std::function<int(DeviceMapper&, int, char**)>> listmap = {
         {"targets", DmListTargets},
         {"devices", DmListDevices},
 };
@@ -251,7 +276,7 @@
 
     DeviceMapper& dm = DeviceMapper::Instance();
     for (const auto& l : listmap) {
-        if (l.first == argv[0]) return l.second(dm);
+        if (l.first == argv[0]) return l.second(dm, argc - 1, argv + 1);
     }
 
     std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
diff --git a/init/README.md b/init/README.md
index 5c07ac1..8d47e0a 100644
--- a/init/README.md
+++ b/init/README.md
@@ -813,3 +813,42 @@
 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.
+
+Early Init Boot Sequence
+------------------------
+The early init boot sequence is broken up into three stages: first stage init, SELinux setup, and
+second stage init.
+
+First stage init is responsible for setting up the bare minimum requirements to load the rest of the
+system. Specifically this includes mounting /dev, /proc, mounting 'early mount' partitions (which
+needs to include all partitions that contain system code, for example system and vendor), and moving
+the system.img mount to / for devices with a ramdisk.
+
+Note that in Android Q, system.img always contains TARGET_ROOT_OUT and always is mounted at / by the
+time first stage init finishes. Android Q will also require dynamic partitions and therefore will
+require using a ramdisk to boot Android. The recovery ramdisk can be used to boot to Android instead
+of a dedicated ramdisk as well.
+
+First stage init has three variations depending on the device configuration:
+1) For system-as-root devices, first stage init is part of /system/bin/init and a symlink at /init
+points to /system/bin/init for backwards compatibility. These devices do not need to do anything to
+mount system.img, since it is by definition already mounted as the rootfs by the kernel.
+
+2) For devices with a ramdisk, first stage init is a static executable located at /init. These
+devices mount system.img as /system then perform a switch root operation to move the mount at
+/system to /. The contents of the ramdisk are freed after mounting has completed.
+
+3) For devices that use recovery as a ramdisk, first stage init it contained within the shared init
+located at /init within the recovery ramdisk. These devices first switch root to
+/first_stage_ramdisk to remove the recovery components from the environment, then proceed the same
+as 2). Note that the decision to boot normally into Android instead of booting
+into recovery mode is made if androidboot.force_normal_boot=1 is present in the
+kernel commandline.
+
+Once first stage init finishes it execs /system/bin/init with the "selinux_setup" argument. This
+phase is where SELinux is optionally compiled and loaded onto the system. selinux.cpp contains more
+information on the specifics of this process.
+
+Lastly once that phase finishes, it execs /system/bin/init again with the "second_stage"
+argument. At this point the main phase of init runs and continues the boot process via the init.rc
+scripts.
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index acefdf0..b5ff658 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -395,6 +395,11 @@
         return false;
     }
     if (fs_mgr_do_mount_one(*fstab_entry)) {
+        if (fstab_entry->fs_mgr_flags.formattable) {
+            PLOG(INFO) << "Failed to mount '" << fstab_entry->mount_point << "', "
+                       << "ignoring mount for formattable partition";
+            return true;
+        }
         PLOG(ERROR) << "Failed to mount '" << fstab_entry->mount_point << "'";
         return false;
     }
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 3a09096..04ca207 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -306,6 +306,11 @@
     }
     std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
 
+    std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
+    if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
+        product_policy_cil_file.clear();
+    }
+
     // vendor_sepolicy.cil and plat_pub_versioned.cil are the new design to replace
     // nonplat_sepolicy.cil.
     std::string plat_pub_versioned_cil_file("/vendor/etc/selinux/plat_pub_versioned.cil");
@@ -342,6 +347,9 @@
     };
     // clang-format on
 
+    if (!product_policy_cil_file.empty()) {
+        compile_args.push_back(product_policy_cil_file.c_str());
+    }
     if (!plat_pub_versioned_cil_file.empty()) {
         compile_args.push_back(plat_pub_versioned_cil_file.c_str());
     }
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index 8b0c53e..ac94e69 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -311,7 +311,7 @@
 };
 
 FuseBridgeLoop::FuseBridgeLoop() : opened_(true) {
-    base::unique_fd epoll_fd(epoll_create1(/* no flag */ 0));
+    base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
     if (epoll_fd.get() == -1) {
         PLOG(ERROR) << "Failed to open FD for epoll";
         opened_ = false;
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index 39cb995..038b59e 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -76,7 +76,7 @@
     return UnwindFromContext(num_ignore_frames, ucontext);
   }
 
-  if (Tid() != android::base::GetThreadId()) {
+  if (Tid() != static_cast<pid_t>(android::base::GetThreadId())) {
     return UnwindThread(num_ignore_frames);
   }
 
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index fe28eba..f5f9b2a 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -117,7 +117,7 @@
     back_frame->map.name = frame->map_name;
     back_frame->map.start = frame->map_start;
     back_frame->map.end = frame->map_end;
-    back_frame->map.offset = frame->map_offset;
+    back_frame->map.offset = frame->map_elf_start_offset;
     back_frame->map.load_bias = frame->map_load_bias;
     back_frame->map.flags = frame->map_flags;
   }
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 68bf898..0723612 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -133,6 +133,7 @@
 #define AID_LLKD 1070            /* live lock daemon */
 #define AID_IORAPD 1071          /* input/output readahead and pin daemon */
 #define AID_GPU_SERVICE 1072     /* GPU service daemon */
+#define AID_NETWORK_STACK 1073   /* network stack service */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
index 715d63d..1f93494 100644
--- a/libmeminfo/tools/Android.bp
+++ b/libmeminfo/tools/Android.bp
@@ -13,11 +13,24 @@
 // limitations under the License.
 
 cc_binary {
+    name: "librank2",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: ["librank.cpp"],
+    shared_libs: [
+        "libbase",
+        "libmeminfo",
+    ],
+}
+
+cc_binary {
     name: "procmem2",
     cflags: [
         "-Wall",
         "-Werror",
-        "-Wno-unused-parameter",
     ],
 
     srcs: ["procmem.cpp"],
@@ -32,7 +45,6 @@
     cflags: [
         "-Wall",
         "-Werror",
-        "-Wno-unused-parameter",
     ],
 
     srcs: ["procrank.cpp"],
diff --git a/libmeminfo/tools/librank.cpp b/libmeminfo/tools/librank.cpp
new file mode 100644
index 0000000..2c2583d
--- /dev/null
+++ b/libmeminfo/tools/librank.cpp
@@ -0,0 +1,350 @@
+/*
+ * 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 <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <meminfo/procmeminfo.h>
+
+using ::android::meminfo::MemUsage;
+using ::android::meminfo::ProcMemInfo;
+using ::android::meminfo::Vma;
+
+[[noreturn]] static void usage(int exit_status) {
+    fprintf(stderr,
+            "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n"
+            "\n"
+            "Sort options:\n"
+            "    -v  Sort processes by VSS.\n"
+            "    -r  Sort processes by RSS.\n"
+            "    -p  Sort processes by PSS.\n"
+            "    -u  Sort processes by USS.\n"
+            "    -s  Sort processes by swap.\n"
+            "        (Default sort order is PSS.)\n"
+            "    -a  Show all mappings, including stack, heap and anon.\n"
+            "    -P /path  Limit libraries displayed to those in path.\n"
+            "    -R  Reverse sort order (default is descending).\n"
+            "    -m [r][w][x] Only list pages that exactly match permissions\n"
+            "    -c  Only show cached (storage backed) pages\n"
+            "    -C  Only show non-cached (ram/swap backed) pages\n"
+            "    -k  Only show pages collapsed by KSM\n"
+            "    -h  Display this help screen.\n",
+            getprogname());
+    exit(exit_status);
+}
+
+static void add_mem_usage(MemUsage* to, const MemUsage& from) {
+    to->vss += from.vss;
+    to->rss += from.rss;
+    to->pss += from.pss;
+    to->uss += from.uss;
+
+    to->swap += from.swap;
+
+    to->private_clean += from.private_clean;
+    to->private_dirty += from.private_dirty;
+
+    to->shared_clean += from.shared_clean;
+    to->shared_dirty += from.shared_dirty;
+}
+
+struct ProcessRecord {
+  public:
+    ProcessRecord(pid_t pid) : pid_(-1), cmdline_("") {
+        std::string fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
+        std::string cmdline;
+        if (!::android::base::ReadFileToString(fname, &cmdline)) {
+            fprintf(stderr, "Failed to read cmdline from: %s\n", fname.c_str());
+            return;
+        }
+        // We deliberately don't read the proc/<pid>cmdline file directly into 'cmdline_'
+        // because of some processes showing up cmdlines that end with "0x00 0x0A 0x00"
+        // e.g. xtra-daemon, lowi-server
+        // The .c_str() assignment below then takes care of trimming the cmdline at the first
+        // 0x00. This is how original procrank worked (luckily)
+        cmdline_ = cmdline.c_str();
+        pid_ = pid;
+        usage_.clear();
+    }
+
+    ~ProcessRecord() = default;
+
+    bool valid() const { return pid_ != -1; }
+
+    // Getters
+    pid_t pid() const { return pid_; }
+    const std::string& cmdline() const { return cmdline_; }
+    const MemUsage& usage() const { return usage_; }
+
+    // Add to the usage
+    void AddUsage(const MemUsage& mem_usage) { add_mem_usage(&usage_, mem_usage); }
+
+  private:
+    pid_t pid_;
+    std::string cmdline_;
+    MemUsage usage_;
+};
+
+struct LibRecord {
+  public:
+    LibRecord(const std::string& name) : name_(name) {}
+    ~LibRecord() = default;
+
+    const std::string& name() const { return name_; }
+    const MemUsage& usage() const { return usage_; }
+    const std::vector<ProcessRecord>& processes() const { return procs_; }
+    uint64_t pss() const { return usage_.pss; }
+    void AddUsage(const ProcessRecord& proc, const MemUsage& mem_usage) {
+        auto process = std::find_if(procs_.begin(), procs_.end(),
+                                    [&](auto p) -> bool { return p.pid() == proc.pid(); });
+        if (process == procs_.end()) {
+            process = procs_.emplace(procs_.end(), proc.pid());
+        }
+        process->AddUsage(mem_usage);
+        add_mem_usage(&usage_, mem_usage);
+    }
+
+    void Sort(std::function<bool(const ProcessRecord&, const ProcessRecord&)>& sorter) {
+        std::sort(procs_.begin(), procs_.end(), sorter);
+    }
+
+  private:
+    std::string name_;
+    MemUsage usage_;
+    std::vector<ProcessRecord> procs_;
+};
+
+// List of every library / map
+static std::vector<LibRecord> g_libs;
+
+// List of library/map names that we don't want to show by default
+static const std::vector<std::string> g_blacklisted_libs = {"[heap]", "[stack]"};
+
+// Global flags affected by command line
+static uint64_t g_pgflags = 0;
+static uint64_t g_pgflags_mask = 0;
+static uint16_t g_mapflags_mask = 0;
+static bool g_all_libs = false;
+static bool g_has_swap = false;
+static bool g_reverse_sort = false;
+static std::string g_prefix_filter = "";
+
+static bool read_all_pids(std::function<bool(pid_t pid)> for_each_pid) {
+    std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
+    if (!procdir) return false;
+
+    struct dirent* dir;
+    pid_t pid;
+    while ((dir = readdir(procdir.get()))) {
+        if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
+        if (!for_each_pid(pid)) return false;
+    }
+
+    return true;
+}
+
+static bool scan_libs_per_process(pid_t pid) {
+    ProcMemInfo pmem(pid, false, g_pgflags, g_pgflags_mask);
+    const std::vector<Vma> maps = pmem.Maps();
+    if (maps.size() == 0) {
+        // nothing to do here, continue
+        return true;
+    }
+
+    ProcessRecord proc(pid);
+    if (!proc.valid()) {
+        fprintf(stderr, "Failed to create process record for process: %d\n", pid);
+        return false;
+    }
+
+    for (auto& map : maps) {
+        // skip library / map if prefix for the path doesn't match
+        if (!g_prefix_filter.empty() && !::android::base::StartsWith(map.name, g_prefix_filter)) {
+            continue;
+        }
+        // Skip maps based on map permissions
+        if (g_mapflags_mask &&
+            ((map.flags & (PROT_READ | PROT_WRITE | PROT_EXEC)) != g_mapflags_mask)) {
+            continue;
+        }
+
+        // skip blacklisted library / map names
+        if (!g_all_libs && (std::find(g_blacklisted_libs.begin(), g_blacklisted_libs.end(),
+                                      map.name) != g_blacklisted_libs.end())) {
+            continue;
+        }
+
+        auto lib = std::find_if(g_libs.begin(), g_libs.end(),
+                                [&](auto l) -> bool { return map.name == l.name(); });
+        if (lib == g_libs.end()) {
+            lib = g_libs.emplace(g_libs.end(), map.name);
+        }
+
+        lib->AddUsage(proc, map.usage);
+        if (!g_has_swap && map.usage.swap) {
+            g_has_swap = true;
+        }
+    }
+
+    return true;
+}
+
+static uint16_t parse_mapflags(const char* mapflags) {
+    uint16_t ret = 0;
+    for (const char* p = mapflags; *p; p++) {
+        switch (*p) {
+            case 'r':
+                ret |= PROT_READ;
+                break;
+            case 'w':
+                ret |= PROT_WRITE;
+                break;
+            case 'x':
+                ret |= PROT_EXEC;
+                break;
+            default:
+                error(EXIT_FAILURE, 0, "Invalid permissions string: %s, %s", mapflags, p);
+        }
+    }
+
+    return ret;
+}
+
+int main(int argc, char* argv[]) {
+    int opt;
+
+    auto pss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().pss < b.usage().pss : a.usage().pss > b.usage().pss;
+    };
+
+    auto uss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().uss < b.usage().uss : a.usage().uss > b.usage().uss;
+    };
+
+    auto vss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().vss < b.usage().vss : a.usage().vss > b.usage().vss;
+    };
+
+    auto rss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().rss < b.usage().rss : a.usage().rss > b.usage().rss;
+    };
+
+    auto swap_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().swap < b.usage().swap : a.usage().swap > b.usage().swap;
+    };
+
+    std::function<bool(const ProcessRecord&, const ProcessRecord&)> sort_func = pss_sort;
+
+    while ((opt = getopt(argc, argv, "acChkm:pP:uvrsR")) != -1) {
+        switch (opt) {
+            case 'a':
+                g_all_libs = true;
+                break;
+            case 'c':
+                g_pgflags = 0;
+                g_pgflags_mask = (1 << KPF_SWAPBACKED);
+                break;
+            case 'C':
+                g_pgflags = g_pgflags_mask = (1 << KPF_SWAPBACKED);
+                break;
+            case 'h':
+                usage(EXIT_SUCCESS);
+            case 'k':
+                g_pgflags = g_pgflags_mask = (1 << KPF_KSM);
+                break;
+            case 'm':
+                g_mapflags_mask = parse_mapflags(optarg);
+                break;
+            case 'p':
+                sort_func = pss_sort;
+                break;
+            case 'P':
+                g_prefix_filter = optarg;
+                break;
+            case 'u':
+                sort_func = uss_sort;
+                break;
+            case 'v':
+                sort_func = vss_sort;
+                break;
+            case 'r':
+                sort_func = rss_sort;
+                break;
+            case 's':
+                sort_func = swap_sort;
+                break;
+            case 'R':
+                g_reverse_sort = true;
+                break;
+            default:
+                usage(EXIT_FAILURE);
+        }
+    }
+
+    if (!read_all_pids(scan_libs_per_process)) {
+        error(EXIT_FAILURE, 0, "Failed to read all pids from the system");
+    }
+
+    printf(" %6s   %7s   %6s   %6s   %6s  ", "RSStot", "VSS", "RSS", "PSS", "USS");
+    if (g_has_swap) {
+        printf(" %6s  ", "Swap");
+    }
+    printf("Name/PID\n");
+
+    // sort the libraries by their pss
+    std::sort(g_libs.begin(), g_libs.end(),
+              [](const LibRecord& l1, const LibRecord& l2) { return l1.pss() > l2.pss(); });
+
+    for (auto& lib : g_libs) {
+        printf("%6" PRIu64 "K   %7s   %6s   %6s   %6s  ", lib.pss() / 1024, "", "", "", "");
+        if (g_has_swap) {
+            printf(" %6s  ", "");
+        }
+        printf("%s\n", lib.name().c_str());
+
+        // sort all mappings first
+        lib.Sort(sort_func);
+
+        for (auto& p : lib.processes()) {
+            const MemUsage& usage = p.usage();
+            printf(" %6s  %7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ", "",
+                   usage.vss / 1024, usage.rss / 1024, usage.pss / 1024, usage.uss / 1024);
+            if (g_has_swap) {
+                printf("%6" PRIu64 "K  ", usage.swap / 1024);
+            }
+            printf("  %s [%d]\n", p.cmdline().c_str(), p.pid());
+        }
+    }
+
+    return 0;
+}
diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp
index 56de12d..b9b174d 100644
--- a/libmeminfo/tools/procmem.cpp
+++ b/libmeminfo/tools/procmem.cpp
@@ -42,7 +42,7 @@
 // Show working set, mutually exclusive with reset_wss;
 bool show_wss = false;
 
-static void usage(const char* cmd) {
+[[noreturn]] static void usage(int exit_status) {
     fprintf(stderr,
             "Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
             "    -i  Uses idle page tracking for working set statistics.\n"
@@ -52,7 +52,9 @@
             "    -u  Sort by USS.\n"
             "    -m  Sort by mapping order (as read from /proc).\n"
             "    -h  Hide maps with no RSS.\n",
-            cmd);
+            getprogname());
+
+    exit(exit_status);
 }
 
 static void print_separator(std::stringstream& ss) {
@@ -154,17 +156,15 @@
                 show_wss = true;
                 break;
             case '?':
-                usage(argv[0]);
-                return 0;
+                usage(EXIT_SUCCESS);
             default:
-                abort();
+                usage(EXIT_FAILURE);
         }
     }
 
     if (optind != (argc - 1)) {
         fprintf(stderr, "Need exactly one pid at the end\n");
-        usage(argv[0]);
-        exit(EXIT_FAILURE);
+        usage(EXIT_FAILURE);
     }
 
     pid_t pid = atoi(argv[optind]);
diff --git a/libmeminfo/tools/procrank.cpp b/libmeminfo/tools/procrank.cpp
index e39e7fa..a751722 100644
--- a/libmeminfo/tools/procrank.cpp
+++ b/libmeminfo/tools/procrank.cpp
@@ -142,8 +142,9 @@
 uint64_t total_uswap = 0;
 uint64_t total_zswap = 0;
 
-static void usage(const char* myname) {
-    std::cerr << "Usage: " << myname << " [ -W ] [ -v | -r | -p | -u | -s | -h ]" << std::endl
+[[noreturn]] static void usage(int exit_status) {
+    std::cerr << "Usage: " << getprogname() << " [ -W ] [ -v | -r | -p | -u | -s | -h ]"
+              << std::endl
               << "    -v  Sort by VSS." << std::endl
               << "    -r  Sort by RSS." << std::endl
               << "    -p  Sort by PSS." << std::endl
@@ -159,6 +160,7 @@
               << "    -o  Show and sort by oom score against lowmemorykiller thresholds."
               << std::endl
               << "    -h  Display this help screen." << std::endl;
+    exit(exit_status);
 }
 
 static bool read_all_pids(std::vector<pid_t>* pids, std::function<bool(pid_t pid)> for_each_pid) {
@@ -386,9 +388,7 @@
                 pgflags_mask = (1 << KPF_SWAPBACKED);
                 break;
             case 'h':
-                usage(argv[0]);
-                return 0;
-                break;
+                usage(EXIT_SUCCESS);
             case 'k':
                 pgflags = (1 << KPF_KSM);
                 pgflags_mask = (1 << KPF_KSM);
@@ -422,7 +422,7 @@
                 reset_wss = true;
                 break;
             default:
-                abort();
+                usage(EXIT_FAILURE);
         }
     }
 
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index b3e2b97..de7ea08 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -639,7 +639,11 @@
   UNUSED(target_sdk_version);
   if (class_loader == nullptr) {
     *needs_native_bridge = false;
-    return dlopen(path, RTLD_NOW);
+    void* handle = dlopen(path, RTLD_NOW);
+    if (handle == nullptr) {
+      *error_msg = dlerror();
+    }
+    return handle;
   }
 
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index 41ca79b..162d1cf 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -24,7 +24,7 @@
 #endif
 void reset_log_context(android_log_context ctx);
 int write_to_logger(android_log_context context, log_id_t id);
-void note_log_drop();
+void note_log_drop(int error);
 void stats_log_close();
 int android_log_write_char_array(android_log_context ctx, const char* value, size_t len);
 #ifdef __cplusplus
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index f4a7e94..5b90361 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -120,8 +120,8 @@
     return retValue;
 }
 
-void note_log_drop() {
-    statsdLoggerWrite.noteDrop();
+void note_log_drop(int error) {
+    statsdLoggerWrite.noteDrop(error);
 }
 
 void stats_log_close() {
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index 88f7d44..f00fc2d 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -48,6 +48,7 @@
 
 static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
 static atomic_int dropped = 0;
+static atomic_int log_error = 0;
 
 void statsd_writer_init_lock() {
     /*
@@ -150,8 +151,9 @@
     return 1;
 }
 
-static void statsdNoteDrop() {
+static void statsdNoteDrop(int error) {
     atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+    atomic_exchange_explicit(&log_error, error, memory_order_relaxed);
 }
 
 static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
@@ -202,7 +204,8 @@
         if (snapshot) {
             android_log_event_int_t buffer;
             header.id = LOG_ID_STATS;
-            buffer.header.tag = htole32(LIBLOG_LOG_TAG);
+            // store the last log error in the tag field. This tag field is not used by statsd.
+            buffer.header.tag = htole32(atomic_load(&log_error));
             buffer.payload.type = EVENT_TYPE_INT;
             buffer.payload.data = htole32(snapshot);
 
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
index 7289441..4fc3f8b 100644
--- a/libstats/statsd_writer.h
+++ b/libstats/statsd_writer.h
@@ -39,7 +39,7 @@
     /* write log to transport, returns number of bytes propagated, or -errno */
     int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
     /* note one log drop */
-    void (*noteDrop)();
+    void (*noteDrop)(int error);
 };
 
 #endif  // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 4ba8a4b..c128b9b 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -204,7 +204,7 @@
 bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
                                            uint64_t* cur_pc) {
   const auto* cfa = &DwarfCfaInfo::kTable[op];
-  if (cfa->name == nullptr) {
+  if (cfa->name[0] == '\0') {
     log(indent, "Illegal");
     log(indent, "Raw Data: 0x%02x", op);
     return true;
@@ -677,29 +677,29 @@
         {DW_EH_PE_uleb128, DW_EH_PE_block},
         {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
     },
-    {nullptr, 0, 0, {}, {}},  // 0x17 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x18 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x19 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1a illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1b illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1c DW_CFA_lo_user (Treat as illegal)
-    {nullptr, 0, 0, {}, {}},  // 0x1d illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1e illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1f illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x20 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x21 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x22 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x23 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x24 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x25 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x26 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x27 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x28 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x29 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x2a illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x2b illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x2c illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+    {"", 0, 0, {}, {}},  // 0x17 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x18 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x19 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1a illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1b illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1c DW_CFA_lo_user (Treat as illegal)
+    {"", 0, 0, {}, {}},  // 0x1d illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1e illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1f illegal cfa
+    {"", 0, 0, {}, {}},  // 0x20 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x21 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x22 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x23 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x24 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x25 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x26 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x27 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x28 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x29 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x2a illegal cfa
+    {"", 0, 0, {}, {}},  // 0x2b illegal cfa
+    {"", 0, 0, {}, {}},  // 0x2c illegal cfa
+    {"", 0, 0, {}, {}},  // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
     {
         "DW_CFA_GNU_args_size",  // 0x2e DW_CFA_GNU_args_size
         2,
@@ -714,21 +714,21 @@
         {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
         {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
     },
-    {nullptr, 0, 0, {}, {}},  // 0x31 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x32 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x33 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x34 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x35 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x36 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x37 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x38 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x39 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3a illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3b illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3c illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3d illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3e illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3f DW_CFA_hi_user (Treat as illegal)
+    {"", 0, 0, {}, {}},  // 0x31 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x32 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x33 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x34 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x35 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x36 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x37 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x38 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x39 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3a illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3b illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3c illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3d illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3e illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3f DW_CFA_hi_user (Treat as illegal)
 };
 
 // Explicitly instantiate DwarfCfa.
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index c5ffb8e..569c17c 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -49,7 +49,14 @@
   };
 
   struct Info {
-    const char* name;
+    // It may seem cleaner to just change the type of 'name' to 'const char *'.
+    // However, having a pointer here would require relocation at runtime,
+    // causing 'kTable' to be placed in data.rel.ro section instead of rodata
+    // section, adding memory pressure to the system.  Note that this is only
+    // safe because this is only used in C++ code.  C++ standard, unlike C
+    // standard, mandates the array size to be large enough to hold the NULL
+    // terminator when initialized with a string literal.
+    const char name[36];
     uint8_t supported_version;
     uint8_t num_operands;
     uint8_t operands[2];
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 44ec5c1..39a09cf 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -32,33 +32,27 @@
 bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
   // One last attempt, see if the previous map is read-only with the
   // same name and stretches across this map.
-  for (auto iter = maps_->begin(); iter != maps_->end(); ++iter) {
-    if (*iter == this) {
-      if (iter == maps_->begin()) {
-        return false;
-      }
-      --iter;
-      MapInfo* prev_map = *iter;
-      // Make sure this is a read-only map.
-      if (prev_map->flags != PROT_READ) {
-        return false;
-      }
-      uint64_t map_size = end - prev_map->end;
-      if (!memory->Init(name, prev_map->offset, map_size)) {
-        return false;
-      }
-      uint64_t max_size;
-      if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
-        return false;
-      }
-      if (!memory->Init(name, prev_map->offset, max_size)) {
-        return false;
-      }
-      elf_offset = offset - prev_map->offset;
-      return true;
-    }
+  if (prev_map == nullptr || prev_map->flags != PROT_READ) {
+    return false;
   }
-  return false;
+
+  uint64_t map_size = end - prev_map->end;
+  if (!memory->Init(name, prev_map->offset, map_size)) {
+    return false;
+  }
+
+  uint64_t max_size;
+  if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
+    return false;
+  }
+
+  if (!memory->Init(name, prev_map->offset, max_size)) {
+    return false;
+  }
+
+  elf_offset = offset - prev_map->offset;
+  elf_start_offset = prev_map->offset;
+  return true;
 }
 
 Memory* MapInfo::GetFileMemory() {
@@ -91,14 +85,13 @@
 
   // Check if the start of this map is an embedded elf.
   uint64_t max_size = 0;
-  uint64_t file_offset = offset;
   if (Elf::GetInfo(memory.get(), &max_size)) {
     if (max_size > map_size) {
-      if (memory->Init(name, file_offset, max_size)) {
+      if (memory->Init(name, offset, max_size)) {
         return memory.release();
       }
       // Try to reinit using the default map_size.
-      if (memory->Init(name, file_offset, map_size)) {
+      if (memory->Init(name, offset, map_size)) {
         return memory.release();
       }
       return nullptr;
@@ -109,6 +102,13 @@
   // No elf at offset, try to init as if the whole file is an elf.
   if (memory->Init(name, 0) && Elf::IsValidElf(memory.get())) {
     elf_offset = offset;
+    // Need to check how to set the elf start offset. If this map is not
+    // the r-x map of a r-- map, then use the real offset value. Otherwise,
+    // use 0.
+    if (prev_map == nullptr || prev_map->offset != 0 || prev_map->flags != PROT_READ ||
+        prev_map->name != name) {
+      elf_start_offset = offset;
+    }
     return memory.release();
   }
 
@@ -156,35 +156,24 @@
     return memory.release();
   }
 
-  if (name.empty() || maps_ == nullptr) {
-    return nullptr;
-  }
-
   // Find the read-only map by looking at the previous map. The linker
   // doesn't guarantee that this invariant will always be true. However,
   // if that changes, there is likely something else that will change and
   // break something.
-  MapInfo* ro_map_info = nullptr;
-  for (auto iter = maps_->begin(); iter != maps_->end(); ++iter) {
-    if (*iter == this) {
-      if (iter != maps_->begin()) {
-        --iter;
-        ro_map_info = *iter;
-      }
-      break;
-    }
-  }
-
-  if (ro_map_info == nullptr || ro_map_info->name != name || ro_map_info->offset >= offset) {
+  if (offset == 0 || name.empty() || prev_map == nullptr || prev_map->name != name ||
+      prev_map->offset >= offset) {
     return nullptr;
   }
 
   // Make sure that relative pc values are corrected properly.
-  elf_offset = offset - ro_map_info->offset;
+  elf_offset = offset - prev_map->offset;
+  // Use this as the elf start offset, otherwise, you always get offsets into
+  // the r-x section, which is not quite the right information.
+  elf_start_offset = prev_map->offset;
 
   MemoryRanges* ranges = new MemoryRanges;
-  ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
-                                 ro_map_info->end - ro_map_info->start, 0));
+  ranges->Insert(
+      new MemoryRange(process_memory, prev_map->start, prev_map->end - prev_map->start, 0));
   ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
 
   return ranges;
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index a9fb859..c90e383 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -67,13 +67,15 @@
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
+        maps_.push_back(
+            new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, pgoff, flags, name));
       });
 }
 
 void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
                const std::string& name, uint64_t load_bias) {
-  MapInfo* map_info = new MapInfo(this, start, end, offset, flags, name);
+  MapInfo* map_info =
+      new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, offset, flags, name);
   map_info->load_bias = load_bias;
   maps_.push_back(map_info);
 }
@@ -81,6 +83,13 @@
 void Maps::Sort() {
   std::sort(maps_.begin(), maps_.end(),
             [](const MapInfo* a, const MapInfo* b) { return a->start < b->start; });
+
+  // Set the prev_map values on the info objects.
+  MapInfo* prev_map = nullptr;
+  for (MapInfo* map_info : maps_) {
+    map_info->prev_map = prev_map;
+    prev_map = map_info;
+  }
 }
 
 Maps::~Maps() {
@@ -98,7 +107,8 @@
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
+        maps_.push_back(
+            new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, pgoff, flags, name));
       });
 }
 
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 792cd0b..8133639 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -59,7 +59,8 @@
   if (info != nullptr) {
     frame->map_start = info->start;
     frame->map_end = info->end;
-    frame->map_offset = info->offset;
+    frame->map_elf_start_offset = info->elf_start_offset;
+    frame->map_exact_offset = info->offset;
     frame->map_load_bias = info->load_bias;
     frame->map_flags = info->flags;
     if (resolve_names_) {
@@ -102,7 +103,8 @@
   if (resolve_names_) {
     frame->map_name = map_info->name;
   }
-  frame->map_offset = map_info->offset;
+  frame->map_elf_start_offset = map_info->elf_start_offset;
+  frame->map_exact_offset = map_info->offset;
   frame->map_start = map_info->start;
   frame->map_end = map_info->end;
   frame->map_flags = map_info->flags;
@@ -290,10 +292,6 @@
     data += android::base::StringPrintf("  #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
   }
 
-  if (frame.map_offset != 0) {
-    data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_offset);
-  }
-
   if (frame.map_start == frame.map_end) {
     // No valid map associated with this frame.
     data += "  <unknown>";
@@ -302,6 +300,11 @@
   } else {
     data += android::base::StringPrintf("  <anonymous:%" PRIx64 ">", frame.map_start);
   }
+
+  if (frame.map_elf_start_offset != 0) {
+    data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_elf_start_offset);
+  }
+
   if (!frame.function_name.empty()) {
     data += " (" + frame.function_name;
     if (frame.function_offset != 0) {
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index cfdefd0..5e3d6f6 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -25,38 +25,31 @@
 #include <string>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
 
 namespace unwindstack {
 
-// Forward declarations.
-class Maps;
-class Memory;
-
 struct MapInfo {
-  MapInfo(Maps* maps) : maps_(maps) {}
-  MapInfo(Maps* maps, uint64_t start, uint64_t end) : maps_(maps), start(start), end(end) {}
-  MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+  MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
           const char* name)
-      : maps_(maps),
-        start(start),
+      : start(start),
         end(end),
         offset(offset),
         flags(flags),
         name(name),
+        prev_map(map_info),
         load_bias(static_cast<uint64_t>(-1)) {}
-  MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+  MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
           const std::string& name)
-      : maps_(maps),
-        start(start),
+      : start(start),
         end(end),
         offset(offset),
         flags(flags),
         name(name),
+        prev_map(map_info),
         load_bias(static_cast<uint64_t>(-1)) {}
   ~MapInfo() = default;
 
-  Maps* maps_ = nullptr;
-
   uint64_t start = 0;
   uint64_t end = 0;
   uint64_t offset = 0;
@@ -64,10 +57,14 @@
   std::string name;
   std::shared_ptr<Elf> elf;
   // This value is only non-zero if the offset is non-zero but there is
-  // no elf signature found at that offset. This indicates that the
-  // entire file is represented by the Memory object returned by CreateMemory,
-  // instead of a portion of the file.
+  // no elf signature found at that offset.
   uint64_t elf_offset = 0;
+  // This value is the offset from the map in memory that is the start
+  // of the elf. This is not equal to offset when the linker splits
+  // shared libraries into a read-only and read-execute map.
+  uint64_t elf_start_offset = 0;
+
+  MapInfo* prev_map = nullptr;
 
   std::atomic_uint64_t load_bias;
 
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 56b0581..d7bbd9d 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -48,7 +48,13 @@
   uint64_t function_offset = 0;
 
   std::string map_name;
-  uint64_t map_offset = 0;
+  // The offset from the first map representing the frame. When there are
+  // two maps (read-only and read-execute) this will be the offset from
+  // the read-only map. When there is only one map, this will be the
+  // same as the actual offset of the map and match map_exact_offset.
+  uint64_t map_elf_start_offset = 0;
+  // The actual offset from the map where the pc lies.
+  uint64_t map_exact_offset = 0;
   uint64_t map_start = 0;
   uint64_t map_end = 0;
   uint64_t map_load_bias = 0;
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 7766218..f7689ce 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -269,7 +269,7 @@
   elf.FakeSetInterface(interface);
 
   elf.FakeSetValid(true);
-  MapInfo map_info(nullptr, 0x1000, 0x2000);
+  MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
 
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 943b3c9..a66685a 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -118,6 +118,7 @@
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
   ASSERT_EQ(0x100U, info.elf_offset);
+  EXPECT_EQ(0x100U, info.elf_start_offset);
 
   // Read the entire file.
   std::vector<uint8_t> buffer(1024);
@@ -129,6 +130,44 @@
   }
 
   ASSERT_FALSE(memory->ReadFully(1024, buffer.data(), 1));
+
+  // Now verify the elf start offset is set correctly based on the previous
+  // info.
+  MapInfo prev_info(nullptr, 0, 0x100, 0x10, 0, "");
+  info.prev_map = &prev_info;
+
+  // No preconditions met, change each one until it should set the elf start
+  // offset to zero.
+  info.elf_offset = 0;
+  info.elf_start_offset = 0;
+  memory.reset(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0x100U, info.elf_offset);
+  EXPECT_EQ(0x100U, info.elf_start_offset);
+
+  prev_info.offset = 0;
+  info.elf_offset = 0;
+  info.elf_start_offset = 0;
+  memory.reset(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0x100U, info.elf_offset);
+  EXPECT_EQ(0x100U, info.elf_start_offset);
+
+  prev_info.flags = PROT_READ;
+  info.elf_offset = 0;
+  info.elf_start_offset = 0;
+  memory.reset(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0x100U, info.elf_offset);
+  EXPECT_EQ(0x100U, info.elf_start_offset);
+
+  prev_info.name = info.name;
+  info.elf_offset = 0;
+  info.elf_start_offset = 0;
+  memory.reset(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0x100U, info.elf_offset);
+  EXPECT_EQ(0U, info.elf_start_offset);
 }
 
 // Verify that if the offset is non-zero and there is an elf at that
@@ -139,6 +178,7 @@
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
   ASSERT_EQ(0U, info.elf_offset);
+  EXPECT_EQ(0U, info.elf_start_offset);
 
   // Read the valid part of the file.
   std::vector<uint8_t> buffer(0x100);
@@ -162,6 +202,7 @@
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
   ASSERT_EQ(0U, info.elf_offset);
+  EXPECT_EQ(0U, info.elf_start_offset);
 
   // Verify the memory is a valid elf.
   uint8_t e_ident[SELFMAG + 1];
@@ -178,6 +219,7 @@
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
   ASSERT_EQ(0U, info.elf_offset);
+  EXPECT_EQ(0U, info.elf_start_offset);
 
   // Verify the memory is a valid elf.
   uint8_t e_ident[SELFMAG + 1];
@@ -250,6 +292,7 @@
   ASSERT_TRUE(mem.get() != nullptr);
   EXPECT_EQ(0x4000UL, map_info->elf_offset);
   EXPECT_EQ(0x4000UL, map_info->offset);
+  EXPECT_EQ(0U, map_info->elf_start_offset);
 
   // Verify that reading values from this memory works properly.
   std::vector<uint8_t> buffer(0x4000);
@@ -295,6 +338,7 @@
   ASSERT_TRUE(mem.get() != nullptr);
   EXPECT_EQ(0x1000UL, map_info->elf_offset);
   EXPECT_EQ(0xb000UL, map_info->offset);
+  EXPECT_EQ(0xa000UL, map_info->elf_start_offset);
 
   // Verify that reading values from this memory works properly.
   std::vector<uint8_t> buffer(0x4000);
@@ -333,6 +377,7 @@
   std::vector<uint8_t> buffer(0x100);
   EXPECT_EQ(0x2000U, map_info->offset);
   EXPECT_EQ(0U, map_info->elf_offset);
+  EXPECT_EQ(0U, map_info->elf_start_offset);
   ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100));
   EXPECT_EQ(0xffU, buffer[0]);
 
@@ -346,6 +391,7 @@
   memory.reset(map_info->CreateMemory(process_memory_));
   EXPECT_EQ(0x2000U, map_info->offset);
   EXPECT_EQ(0x1000U, map_info->elf_offset);
+  EXPECT_EQ(0x1000U, map_info->elf_start_offset);
   Elf64_Ehdr ehdr_mem;
   ASSERT_TRUE(memory->ReadFully(0, &ehdr_mem, sizeof(ehdr_mem)));
   EXPECT_TRUE(memcmp(&ehdr, &ehdr_mem, sizeof(ehdr)) == 0);
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 80e292a..b4197f2 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -62,7 +62,7 @@
 }
 
 TEST(MapsTest, verify_parse_line) {
-  MapInfo info(nullptr);
+  MapInfo info(nullptr, 0, 0, 0, 0, "");
 
   VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
   EXPECT_EQ(1U, info.start);
@@ -135,7 +135,7 @@
 }
 
 TEST(MapsTest, verify_large_values) {
-  MapInfo info(nullptr);
+  MapInfo info(nullptr, 0, 0, 0, 0, "");
 #if defined(__LP64__)
   VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
   EXPECT_EQ(0xfabcdef012345678UL, info.start);
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 00264c2..472d1cf 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -182,7 +182,7 @@
   RegsX86_64 regs_x86_64;
   RegsMips regs_mips;
   RegsMips64 regs_mips64;
-  MapInfo map_info(nullptr, 0x1000, 0x2000);
+  MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
   Elf* invalid_elf = new Elf(nullptr);
   map_info.elf.reset(invalid_elf);
 
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index dc015b4..aab9ec2 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -298,7 +298,7 @@
   EXPECT_EQ(
       "  #00 pc 00068fb8  libarttestd.so (_ZN3artL13CauseSegfaultEv+72)\n"
       "  #01 pc 00067f00  libarttestd.so (Java_Main_unwindInProcess+10032)\n"
-      "  #02 pc 000021a8 (offset 0x2000)  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
+      "  #02 pc 000021a8  137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
       "boolean)+136)\n"
       "  #03 pc 0000fe80  anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
       "  #04 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
@@ -591,7 +591,7 @@
   ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
       "  #00 pc 00018a5e  libarttestd.so (Java_Main_unwindInProcess+866)\n"
-      "  #01 pc 0000212d (offset 0x2000)  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
+      "  #01 pc 0000212d  137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
       "boolean)+92)\n"
       "  #02 pc 00011cb1  anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
       "  #03 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
@@ -1135,29 +1135,29 @@
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
-      "  #00 pc 0032bfa0 (offset 0x42000)  libunwindstack_test (SignalInnerFunction+40)\n"
-      "  #01 pc 0032bfeb (offset 0x42000)  libunwindstack_test (SignalMiddleFunction+2)\n"
-      "  #02 pc 0032bff3 (offset 0x42000)  libunwindstack_test (SignalOuterFunction+2)\n"
-      "  #03 pc 0032fed3 (offset 0x42000)  libunwindstack_test "
+      "  #00 pc 0032bfa0  libunwindstack_test (SignalInnerFunction+40)\n"
+      "  #01 pc 0032bfeb  libunwindstack_test (SignalMiddleFunction+2)\n"
+      "  #02 pc 0032bff3  libunwindstack_test (SignalOuterFunction+2)\n"
+      "  #03 pc 0032fed3  libunwindstack_test "
       "(_ZN11unwindstackL19SignalCallerHandlerEiP7siginfoPv+26)\n"
-      "  #04 pc 00026528 (offset 0x25000)  libc.so\n"
+      "  #04 pc 00026528  libc.so\n"
       "  #05 pc 00000000  <unknown>\n"
-      "  #06 pc 0032c2d9 (offset 0x42000)  libunwindstack_test (InnerFunction+736)\n"
-      "  #07 pc 0032cc4f (offset 0x42000)  libunwindstack_test (MiddleFunction+42)\n"
-      "  #08 pc 0032cc81 (offset 0x42000)  libunwindstack_test (OuterFunction+42)\n"
-      "  #09 pc 0032e547 (offset 0x42000)  libunwindstack_test "
+      "  #06 pc 0032c2d9  libunwindstack_test (InnerFunction+736)\n"
+      "  #07 pc 0032cc4f  libunwindstack_test (MiddleFunction+42)\n"
+      "  #08 pc 0032cc81  libunwindstack_test (OuterFunction+42)\n"
+      "  #09 pc 0032e547  libunwindstack_test "
       "(_ZN11unwindstackL19RemoteThroughSignalEij+270)\n"
-      "  #10 pc 0032ed99 (offset 0x42000)  libunwindstack_test "
+      "  #10 pc 0032ed99  libunwindstack_test "
       "(_ZN11unwindstack55UnwindTest_remote_through_signal_with_invalid_func_Test8TestBodyEv+16)\n"
-      "  #11 pc 00354453 (offset 0x42000)  libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
-      "  #12 pc 00354de7 (offset 0x42000)  libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
-      "  #13 pc 00355105 (offset 0x42000)  libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
-      "  #14 pc 0035a215 (offset 0x42000)  libunwindstack_test "
+      "  #11 pc 00354453  libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
+      "  #12 pc 00354de7  libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
+      "  #13 pc 00355105  libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
+      "  #14 pc 0035a215  libunwindstack_test "
       "(_ZN7testing8internal12UnitTestImpl11RunAllTestsEv+664)\n"
-      "  #15 pc 00359f4f (offset 0x42000)  libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
-      "  #16 pc 0034d3db (offset 0x42000)  libunwindstack_test (main+38)\n"
-      "  #17 pc 00092c0d (offset 0x25000)  libc.so (__libc_init+48)\n"
-      "  #18 pc 0004202f (offset 0x42000)  libunwindstack_test (_start_main+38)\n",
+      "  #15 pc 00359f4f  libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
+      "  #16 pc 0034d3db  libunwindstack_test (main+38)\n"
+      "  #17 pc 00092c0d  libc.so (__libc_init+48)\n"
+      "  #18 pc 0004202f  libunwindstack_test (_start_main+38)\n",
       frame_info);
 
   EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
@@ -1248,14 +1248,14 @@
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
-      "  #00 pc 000000000014ccbc (offset 0x39000)  linker64 (__dl_syscall+28)\n"
-      "  #01 pc 000000000005426c (offset 0x39000)  linker64 "
+      "  #00 pc 000000000014ccbc  linker64 (__dl_syscall+28)\n"
+      "  #01 pc 000000000005426c  linker64 "
       "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
       "  #02 pc 00000000000008bc  vdso.so\n"
-      "  #03 pc 00000000000846f4 (offset 0x40000)  libc.so (abort+172)\n"
-      "  #04 pc 0000000000084ad4 (offset 0x40000)  libc.so (__assert2+36)\n"
-      "  #05 pc 000000000003d5b4 (offset 0x40000)  ANGLEPrebuilt.apk (ANGLEGetUtilityAPI+56)\n"
-      "  #06 pc 000000000007fe68 (offset 0x40000)  libc.so (__libc_init)\n",
+      "  #03 pc 00000000000846f4  libc.so (abort+172)\n"
+      "  #04 pc 0000000000084ad4  libc.so (__assert2+36)\n"
+      "  #05 pc 000000000003d5b4  ANGLEPrebuilt.apk (offset 0x4000) (ANGLEGetUtilityAPI+56)\n"
+      "  #06 pc 000000000007fe68  libc.so (__libc_init)\n",
       frame_info);
 
   EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
@@ -1287,14 +1287,14 @@
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
-      "  #00 pc 000000000014ccbc (offset 0x39000)  linker64 (__dl_syscall+28)\n"
-      "  #01 pc 000000000005426c (offset 0x39000)  linker64 "
+      "  #00 pc 000000000014ccbc  linker64 (__dl_syscall+28)\n"
+      "  #01 pc 000000000005426c  linker64 "
       "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
       "  #02 pc 00000000000008bc  vdso.so\n"
-      "  #03 pc 00000000000846f4 (offset 0x40000)  libc.so (abort+172)\n"
-      "  #04 pc 0000000000084ad4 (offset 0x40000)  libc.so (__assert2+36)\n"
-      "  #05 pc 000000000003d5b4 (offset 0x2211000)  ANGLEPrebuilt.apk\n"
-      "  #06 pc 000000000007fe68 (offset 0x40000)  libc.so (__libc_init)\n",
+      "  #03 pc 00000000000846f4  libc.so (abort+172)\n"
+      "  #04 pc 0000000000084ad4  libc.so (__assert2+36)\n"
+      "  #05 pc 000000000003d5b4  ANGLEPrebuilt.apk (offset 0x21d5000)\n"
+      "  #06 pc 000000000007fe68  libc.so (__libc_init)\n",
       frame_info);
 
   EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index c4b8763..1fdeee5 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -42,84 +42,64 @@
 
 namespace unwindstack {
 
-class MapsFake : public Maps {
- public:
-  MapsFake() = default;
-  virtual ~MapsFake() = default;
-
-  bool Parse() { return true; }
-
-  void FakeClear() { maps_.clear(); }
-
-  void FakeAddMapInfo(MapInfo* map_info) { maps_.push_back(map_info); }
-};
-
 class UnwinderTest : public ::testing::Test {
  protected:
+  static void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+                         const char* name, Elf* elf = nullptr) {
+    std::string str_name(name);
+    maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
+    if (elf != nullptr) {
+      MapInfo* map_info = *--maps_->end();
+      map_info->elf.reset(elf);
+    }
+  }
+
   static void SetUpTestCase() {
-    maps_.FakeClear();
-    MapInfo* info =
-        new MapInfo(&maps_, 0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
+    maps_.reset(new Maps);
+
     ElfFake* elf = new ElfFake(new MemoryFake);
-    info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
-    maps_.FakeAddMapInfo(info);
+    AddMapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so", elf);
 
-    info = new MapInfo(&maps_, 0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
-    maps_.FakeAddMapInfo(info);
+    AddMapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
 
-    info = new MapInfo(&maps_, 0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
-                       "/dev/fake_device");
-    maps_.FakeAddMapInfo(info);
+    AddMapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+               "/dev/fake_device");
 
-    info = new MapInfo(&maps_, 0x20000, 0x22000, 0, PROT_READ | PROT_WRITE,
-                       "/system/fake/libunwind.so");
     elf = new ElfFake(new MemoryFake);
-    info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
-    maps_.FakeAddMapInfo(info);
+    AddMapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so", elf);
 
-    info = new MapInfo(&maps_, 0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
     elf = new ElfFake(new MemoryFake);
-    info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
-    maps_.FakeAddMapInfo(info);
+    AddMapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so", elf);
 
-    info = new MapInfo(&maps_, 0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
     elf = new ElfFake(new MemoryFake);
-    info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
-    maps_.FakeAddMapInfo(info);
+    AddMapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so", elf);
 
-    info = new MapInfo(&maps_, 0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
     elf = new ElfFake(new MemoryFake);
-    info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
-    maps_.FakeAddMapInfo(info);
+    AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
 
-    info = new MapInfo(&maps_, 0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
-    maps_.FakeAddMapInfo(info);
+    AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
 
-    info = new MapInfo(&maps_, 0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
-                       "/fake/fake.vdex");
+    AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+    MapInfo* info = *--maps_->end();
     info->load_bias = 0;
-    maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(&maps_, 0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
-                       "/fake/fake_load_bias.so");
     elf = new ElfFake(new MemoryFake);
-    info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     elf->FakeSetLoadBias(0x5000);
-    maps_.FakeAddMapInfo(info);
+    AddMapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_load_bias.so",
+               elf);
 
-    info = new MapInfo(&maps_, 0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
-                       "/fake/fake_offset.oat");
     elf = new ElfFake(new MemoryFake);
-    info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_offset.oat",
+               elf);
+    info = *--maps_->end();
     info->elf_offset = 0x8000;
-    maps_.FakeAddMapInfo(info);
 
     process_memory_.reset(new MemoryFake);
   }
@@ -130,12 +110,12 @@
     regs_.FakeSetReturnAddressValid(false);
   }
 
-  static MapsFake maps_;
+  static std::unique_ptr<Maps> maps_;
   static RegsFake regs_;
   static std::shared_ptr<Memory> process_memory_;
 };
 
-MapsFake UnwinderTest::maps_;
+std::unique_ptr<Maps> UnwinderTest::maps_;
 RegsFake UnwinderTest::regs_(5);
 std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
 
@@ -150,7 +130,7 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -164,7 +144,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -178,7 +159,8 @@
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -192,7 +174,8 @@
   EXPECT_EQ("Frame2", frame->function_name);
   EXPECT_EQ(2U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -210,7 +193,7 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.SetResolveNames(false);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -225,7 +208,8 @@
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -239,7 +223,8 @@
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -253,7 +238,8 @@
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -267,7 +253,7 @@
   regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -281,7 +267,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/fake/fake_load_bias.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0xa5000U, frame->map_start);
   EXPECT_EQ(0xa6000U, frame->map_end);
   EXPECT_EQ(0x5000U, frame->map_load_bias);
@@ -295,7 +282,7 @@
   regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -309,7 +296,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/fake/fake_offset.oat", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0xa7000U, frame->map_start);
   EXPECT_EQ(0xa8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -323,7 +311,7 @@
   regs_.set_sp(0x10000);
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -337,7 +325,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/fake/fake.apk", frame->map_name);
-  EXPECT_EQ(0x1d000U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0x1d000U, frame->map_exact_offset);
   EXPECT_EQ(0x43000U, frame->map_start);
   EXPECT_EQ(0x44000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -358,7 +347,7 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -372,7 +361,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -389,7 +379,7 @@
   regs_.set_pc(0x1000);
   regs_.set_sp(0x10000);
 
-  Unwinder unwinder(20, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(20, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
 
@@ -404,7 +394,8 @@
     EXPECT_EQ("Frame" + std::to_string(i), frame->function_name) << "Failed at frame " << i;
     EXPECT_EQ(i, frame->function_offset) << "Failed at frame " << i;
     EXPECT_EQ("/system/fake/libc.so", frame->map_name) << "Failed at frame " << i;
-    EXPECT_EQ(0U, frame->map_offset) << "Failed at frame " << i;
+    EXPECT_EQ(0U, frame->map_elf_start_offset) << "Failed at frame " << i;
+    EXPECT_EQ(0U, frame->map_exact_offset) << "Failed at frame " << i;
     EXPECT_EQ(0x1000U, frame->map_start) << "Failed at frame " << i;
     EXPECT_EQ(0x8000U, frame->map_end) << "Failed at frame " << i;
     EXPECT_EQ(0U, frame->map_load_bias) << "Failed at frame " << i;
@@ -429,7 +420,7 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10070, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
   unwinder.Unwind(&skip_libs);
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -444,7 +435,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -458,7 +450,8 @@
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
   EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x20000U, frame->map_start);
   EXPECT_EQ(0x22000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -472,7 +465,7 @@
   EXPECT_EQ("Frame2", frame->function_name);
   EXPECT_EQ(2U, frame->function_offset);
   EXPECT_EQ("/fake/libanother.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x23000U, frame->map_start);
   EXPECT_EQ(0x24000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -489,7 +482,7 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -503,7 +496,7 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -517,7 +510,7 @@
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
   EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x20000U, frame->map_start);
   EXPECT_EQ(0x22000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -536,7 +529,7 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -555,7 +548,7 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -569,7 +562,7 @@
   regs_.set_pc(0x41000);
   regs_.set_sp(0x13000);
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
 
@@ -583,7 +576,8 @@
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0U, frame->map_start);
   EXPECT_EQ(0U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -604,7 +598,7 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -618,7 +612,8 @@
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0U, frame->map_start);
   EXPECT_EQ(0U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -632,7 +627,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -646,7 +642,8 @@
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
   EXPECT_EQ("/fake/libanother.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x23000U, frame->map_start);
   EXPECT_EQ(0x24000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -666,7 +663,7 @@
   regs_.FakeSetReturnAddress(0x12);
   regs_.FakeSetReturnAddressValid(true);
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
 
@@ -680,7 +677,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x20000U, frame->map_start);
   EXPECT_EQ(0x22000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -694,7 +692,8 @@
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0U, frame->map_start);
   EXPECT_EQ(0U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -713,7 +712,7 @@
   regs_.FakeSetReturnAddress(0x1202);
   regs_.FakeSetReturnAddressValid(true);
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -727,7 +726,8 @@
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0U, frame->map_start);
   EXPECT_EQ(0U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -741,7 +741,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -762,14 +763,14 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x53502, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   std::vector<std::string> suffixes{"oat"};
   unwinder.Unwind(nullptr, &suffixes);
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
   ASSERT_EQ(2U, unwinder.NumFrames());
   // Make sure the elf was not initialized.
-  MapInfo* map_info = maps_.Find(0x53000);
+  MapInfo* map_info = maps_->Find(0x53000);
   ASSERT_TRUE(map_info != nullptr);
   EXPECT_TRUE(map_info->elf == nullptr);
 
@@ -781,7 +782,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -795,7 +797,8 @@
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
   EXPECT_EQ("/fake/fake.apk", frame->map_name);
-  EXPECT_EQ(0x1d000U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0x1d000U, frame->map_exact_offset);
   EXPECT_EQ(0x43000U, frame->map_start);
   EXPECT_EQ(0x44000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -819,7 +822,7 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
 
@@ -833,7 +836,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -847,7 +851,8 @@
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
   EXPECT_EQ("/fake/compressed.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x33000U, frame->map_start);
   EXPECT_EQ(0x34000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -861,7 +866,8 @@
   EXPECT_EQ("Frame2", frame->function_name);
   EXPECT_EQ(2U, frame->function_offset);
   EXPECT_EQ("/fake/compressed.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x33000U, frame->map_start);
   EXPECT_EQ(0x34000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -874,7 +880,7 @@
   regs_.set_sp(0x10000);
   regs_.FakeSetDexPc(0xa3400);
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -888,7 +894,8 @@
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/fake/fake.vdex", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0xa3000U, frame->map_start);
   EXPECT_EQ(0xa4000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -902,7 +909,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -915,7 +923,7 @@
   regs_.set_sp(0x10000);
   regs_.FakeSetDexPc(0x50000);
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -929,7 +937,8 @@
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0U, frame->map_start);
   EXPECT_EQ(0U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -943,7 +952,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -959,7 +969,7 @@
   ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
   ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
 
-  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
@@ -973,7 +983,8 @@
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/fake/fake.vdex", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0xa3000U, frame->map_start);
   EXPECT_EQ(0xa4000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -987,7 +998,8 @@
   EXPECT_EQ("Frame0", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/system/fake/libc.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x1000U, frame->map_start);
   EXPECT_EQ(0x8000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -1001,7 +1013,8 @@
   EXPECT_EQ("Frame1", frame->function_name);
   EXPECT_EQ(1U, frame->function_offset);
   EXPECT_EQ("/fake/compressed.so", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0x33000U, frame->map_start);
   EXPECT_EQ(0x34000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -1014,7 +1027,7 @@
   regs_.set_sp(0x10000);
   regs_.FakeSetDexPc(0xa3400);
 
-  Unwinder unwinder(1, &maps_, &regs_, process_memory_);
+  Unwinder unwinder(1, maps_.get(), &regs_, process_memory_);
   unwinder.Unwind();
   EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
 
@@ -1028,7 +1041,8 @@
   EXPECT_EQ("", frame->function_name);
   EXPECT_EQ(0U, frame->function_offset);
   EXPECT_EQ("/fake/fake.vdex", frame->map_name);
-  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_elf_start_offset);
+  EXPECT_EQ(0U, frame->map_exact_offset);
   EXPECT_EQ(0xa3000U, frame->map_start);
   EXPECT_EQ(0xa4000U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
@@ -1045,17 +1059,17 @@
   frame.function_name = "function";
   frame.function_offset = 100;
   frame.map_name = "/fake/libfake.so";
-  frame.map_offset = 0x2000;
+  frame.map_elf_start_offset = 0x2000;
   frame.map_start = 0x3000;
   frame.map_end = 0x6000;
   frame.map_flags = PROT_READ;
 
-  EXPECT_EQ("  #01 pc 0000000000001000 (offset 0x2000)  /fake/libfake.so (function+100)",
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (offset 0x2000) (function+100)",
             Unwinder::FormatFrame(frame, false));
-  EXPECT_EQ("  #01 pc 00001000 (offset 0x2000)  /fake/libfake.so (function+100)",
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (offset 0x2000) (function+100)",
             Unwinder::FormatFrame(frame, true));
 
-  frame.map_offset = 0;
+  frame.map_elf_start_offset = 0;
   EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (function+100)",
             Unwinder::FormatFrame(frame, false));
   EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100)",
@@ -1130,7 +1144,7 @@
   for (auto regs : reg_list) {
     ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
 
-    Unwinder unwinder(64, &maps_, regs, process_memory_);
+    Unwinder unwinder(64, maps_.get(), regs, process_memory_);
     unwinder.Unwind();
 
     ASSERT_EQ(1U, unwinder.NumFrames());
diff --git a/libunwindstack/tests/files/offline/offset_arm/maps.txt b/libunwindstack/tests/files/offline/offset_arm/maps.txt
index 6224464..768dd9f 100644
--- a/libunwindstack/tests/files/offline/offset_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/offset_arm/maps.txt
@@ -1,2 +1,4 @@
+2b2a000-2b6c000 r--p 0 00:00 0   libunwindstack_test
 2b6c000-2e92000 r-xp 42000 00:00 0   libunwindstack_test
+f4110000-f4135000 r--p 0 00:00 0   libc.so
 f4135000-f41a9000 r-xp 25000 00:00 0   libc.so
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 652dbd1..5ae8874 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -22,6 +22,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/mman.h>
 #include <sys/ptrace.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -46,6 +47,7 @@
   uint64_t start;
   uint64_t end;
   uint64_t offset;
+  uint64_t flags;
   std::string name;
 };
 
@@ -163,14 +165,19 @@
   return true;
 }
 
-bool CopyElfFromFile(map_info_t* info) {
+bool CopyElfFromFile(map_info_t* info, bool* file_copied) {
+  std::string cur_name = basename(info->name.c_str());
+  if (*file_copied) {
+    info->name = cur_name;
+    return true;
+  }
+
   std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(info->name.c_str(), "r"), &fclose);
   if (fp == nullptr) {
     perror((std::string("Cannot open ") + info->name).c_str());
     return false;
   }
 
-  std::string cur_name = basename(info->name.c_str());
   std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
   if (output == nullptr) {
     perror((std::string("Cannot create file " + cur_name)).c_str());
@@ -193,6 +200,39 @@
   return true;
 }
 
+map_info_t* FillInAndGetMapInfo(std::unordered_map<uint64_t, map_info_t>& maps_by_start,
+                                unwindstack::MapInfo* map_info) {
+  auto info = &maps_by_start[map_info->start];
+  info->start = map_info->start;
+  info->end = map_info->end;
+  info->offset = map_info->offset;
+  info->name = map_info->name;
+  info->flags = map_info->flags;
+
+  return info;
+}
+
+void SaveMapInformation(std::shared_ptr<unwindstack::Memory>& process_memory, map_info_t* info,
+                        bool* file_copied) {
+  if (CopyElfFromFile(info, file_copied)) {
+    return;
+  }
+  *file_copied = false;
+
+  // Try to create the elf from memory, this will handle cases where
+  // the data only exists in memory such as vdso data on x86.
+  if (CreateElfFromMemory(process_memory, info)) {
+    return;
+  }
+
+  printf("Cannot save memory or file for map ");
+  if (!info->name.empty()) {
+    printf("%s\n", info->name.c_str());
+  } else {
+    printf("anonymous:%" PRIx64 "\n", info->start);
+  }
+}
+
 int SaveData(pid_t pid) {
   unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
   if (regs == nullptr) {
@@ -237,22 +277,21 @@
     }
 
     if (maps_by_start.count(frame.map_start) == 0) {
-      auto info = &maps_by_start[frame.map_start];
-      info->start = frame.map_start;
-      info->end = frame.map_end;
-      info->offset = frame.map_offset;
-      info->name = frame.map_name;
-      if (!CopyElfFromFile(info)) {
-        // Try to create the elf from memory, this will handle cases where
-        // the data only exists in memory such as vdso data on x86.
-        if (!CreateElfFromMemory(process_memory, info)) {
-          printf("Ignoring map ");
-          if (!info->name.empty()) {
-            printf("%s\n", info->name.c_str());
-          } else {
-            printf("anonymous:%" PRIx64 "\n", info->start);
-          }
-        }
+      map_info = maps.Find(frame.map_start);
+
+      auto info = FillInAndGetMapInfo(maps_by_start, map_info);
+      bool file_copied = false;
+      SaveMapInformation(process_memory, info, &file_copied);
+
+      // If you are using a a linker that creates two maps (one read-only, one
+      // read-executable), it's necessary to capture the previous map
+      // information if needed.
+      unwindstack::MapInfo* prev_map = map_info->prev_map;
+      if (prev_map != nullptr && map_info->offset != 0 && prev_map->offset == 0 &&
+          prev_map->flags == PROT_READ && map_info->name == prev_map->name &&
+          maps_by_start.count(prev_map->start) == 0) {
+        info = FillInAndGetMapInfo(maps_by_start, prev_map);
+        SaveMapInformation(process_memory, info, &file_copied);
       }
     }
   }
@@ -277,8 +316,18 @@
   }
 
   for (auto& element : sorted_maps) {
+    char perms[5] = {"---p"};
     map_info_t& map = element.second;
-    fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " r-xp %" PRIx64 " 00:00 0", map.start, map.end,
+    if (map.flags & PROT_READ) {
+      perms[0] = 'r';
+    }
+    if (map.flags & PROT_WRITE) {
+      perms[1] = 'w';
+    }
+    if (map.flags & PROT_EXEC) {
+      perms[2] = 'x';
+    }
+    fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " 00:00 0", map.start, map.end, perms,
             map.offset);
     if (!map.name.empty()) {
       fprintf(fp.get(), "   %s", map.name.c_str());
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 102fdf0..b3f943d 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -51,9 +51,6 @@
 
 // --- Looper ---
 
-// Hint for number of file descriptors to be associated with the epoll instance.
-static const int EPOLL_SIZE_HINT = 8;
-
 // Maximum number of file descriptors for which to retrieve poll events each iteration.
 static const int EPOLL_MAX_EVENTS = 16;
 
@@ -139,7 +136,7 @@
     }
 
     // Allocate the new epoll instance and register the wake pipe.
-    mEpollFd.reset(epoll_create(EPOLL_SIZE_HINT));
+    mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
 
     struct epoll_event eventItem;
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 56004fe..7a4a345 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -123,13 +123,14 @@
         if (reinterpret_cast<uintptr_t>(logStackInternal) != 0 && stack != nullptr) {
             logStackInternal(logtag, stack, priority);
         } else {
-            ALOGW("CallStack::logStackInternal not linked");
+            ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
         }
     }
 
 #else
-    static void ALWAYS_INLINE logStack(const char*, CallStack* = getCurrent().get(),
+    static void ALWAYS_INLINE logStack(const char* logtag, CallStack* = getCurrent().get(),
                                        android_LogPriority = ANDROID_LOG_DEBUG) {
+        ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
     }
 #endif // !WEAKS_AVAILABLE
 
@@ -139,13 +140,13 @@
         if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {
             return stackToStringInternal(prefix, stack);
         } else {
-            return String8("<CallStack package not linked>");
+            return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
         }
     }
 #else // !WEAKS_AVAILABLE
-    static String8 ALWAYS_INLINE stackToString(const char* = nullptr,
+    static String8 ALWAYS_INLINE stackToString(const char* prefix = nullptr,
                                                const CallStack* = getCurrent().get()) {
-        return String8("<CallStack package not linked>");
+        return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
     }
 #endif // !WEAKS_AVAILABLE
 
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 9eb7f2c..6b9f6e1 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -491,7 +491,7 @@
 }
 
 int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
-  const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY, 0);
+  const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
   ZipArchive* archive = new ZipArchive(fd, true);
   *handle = archive;
 
diff --git a/llkd/README.md b/llkd/README.md
index 3da7a2f..7cc7e1f 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -135,8 +135,9 @@
 default 2 minutes samples of threads for D or Z.
 
 #### ro.llk.stack
-default cma_alloc,__get_user_pages,bit_wait_io comma separated list of kernel
-symbols.  The string "*false*" is the equivalent to an *empty* list.
+default cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
+comma separated list of kernel symbols.
+The string "*false*" is the equivalent to an *empty* list.
 Look for kernel stack symbols that if ever persistently present can
 indicate a subsystem is locked up.
 Beware, check does not on purpose do forward scheduling ABA except by polling
@@ -160,7 +161,7 @@
 NB: false is a very very very unlikely process to want to blacklist.
 
 #### ro.llk.blacklist.parent
-default 0,2 (kernel and [kthreadd]).
+default 0,2,adbd (kernel, [kthreadd] and adbd).
 The string "*false*" is the equivalent to an *empty* list.
 Do not watch processes that have this parent.
 A parent process can be comm, cmdline or pid reference.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index b16b1d8..7b7dbf9 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -50,12 +50,17 @@
 /* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
 #define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
 #define LLK_CHECK_STACK_PROPERTY       "ro.llk.stack"
-#define LLK_CHECK_STACK_DEFAULT        "cma_alloc,__get_user_pages,bit_wait_io"
+#define LLK_CHECK_STACK_DEFAULT        \
+    "cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable"
 #define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
 #define LLK_BLACKLIST_PROCESS_DEFAULT  \
     "0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
 #define LLK_BLACKLIST_PARENT_PROPERTY  "ro.llk.blacklist.parent"
+#ifdef __PTRACE_ENABLED__  // defined if userdebug build
+#define LLK_BLACKLIST_PARENT_DEFAULT   "0,2,[kthreadd],adbd"
+#else
 #define LLK_BLACKLIST_PARENT_DEFAULT   "0,2,[kthreadd]"
+#endif
 #define LLK_BLACKLIST_UID_PROPERTY     "ro.llk.blacklist.uid"
 #define LLK_BLACKLIST_UID_DEFAULT      ""
 #define LLK_BLACKLIST_STACK_PROPERTY   "ro.llk.blacklist.process.stack"
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 267da4a..7f47fc9 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -737,19 +737,21 @@
 
     auto kernel_stack = ReadFile(piddir + "/stack");
     if (kernel_stack.empty()) {
-        LOG(INFO) << piddir << "/stack empty comm=" << procp->getComm()
-                  << " cmdline=" << procp->getCmdline();
+        LOG(VERBOSE) << piddir << "/stack empty comm=" << procp->getComm()
+                     << " cmdline=" << procp->getCmdline();
         return false;
     }
     // A scheduling incident that should not reset count_stack
     if (kernel_stack.find(" cpu_worker_pools+0x") != std::string::npos) return false;
     char idx = -1;
     char match = -1;
+    std::string matched_stack_symbol = "<unknown>";
     for (const auto& stack : llkCheckStackSymbols) {
         if (++idx < 0) break;
         if ((kernel_stack.find(" "s + stack + "+0x") != std::string::npos) ||
             (kernel_stack.find(" "s + stack + ".cfi+0x") != std::string::npos)) {
             match = idx;
+            matched_stack_symbol = stack;
             break;
         }
     }
@@ -760,7 +762,9 @@
     }
     if (match == char(-1)) return false;
     procp->count_stack += llkCycle;
-    return procp->count_stack >= llkStateTimeoutMs[llkStateStack];
+    if (procp->count_stack < llkStateTimeoutMs[llkStateStack]) return false;
+    LOG(WARNING) << "Found " << matched_stack_symbol << " in stack for pid " << procp->pid;
+    return true;
 }
 #endif
 
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 19e6d68..2fd9f95 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -55,8 +55,19 @@
       mMsgLen(elem.mMsgLen),
       mLogId(elem.mLogId),
       mDropped(elem.mDropped) {
-    mMsg = new char[mMsgLen];
-    memcpy(mMsg, elem.mMsg, mMsgLen);
+    if (mDropped) {
+        if (elem.isBinary() && elem.mMsg != nullptr) {
+            // for the following "len" value, refer to : setDropped(uint16_t value), getTag()
+            const int len = sizeof(android_event_header_t);
+            mMsg = new char[len];
+            memcpy(mMsg, elem.mMsg, len);
+        } else {
+            mMsg = nullptr;
+        }
+    } else {
+        mMsg = new char[mMsgLen];
+        memcpy(mMsg, elem.mMsg, mMsgLen);
+    }
 }
 
 LogBufferElement::~LogBufferElement() {
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index f4e165f..9f6f203 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -18,6 +18,7 @@
 #define _LOGD_LOG_TIMES_H__
 
 #include <pthread.h>
+#include <sys/socket.h>
 #include <sys/types.h>
 #include <time.h>
 
@@ -82,6 +83,8 @@
     void cleanSkip_Locked(void);
 
     void release_Locked(void) {
+        // gracefully shut down the socket.
+        shutdown(mClient->getSocket(), SHUT_RDWR);
         mRelease = true;
         pthread_cond_signal(&threadTriggeredCondition);
     }
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 264c612..49ae960 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -73,6 +73,7 @@
 namespace.default.permitted.paths += /%PRODUCT_SERVICES%/priv-app
 namespace.default.permitted.paths += /data
 namespace.default.permitted.paths += /mnt/expand
+namespace.default.permitted.paths += /apex/com.android.resolv/${LIB}
 
 namespace.default.asan.search.paths  = /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
@@ -104,6 +105,7 @@
 namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
 namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
 namespace.default.asan.permitted.paths += /mnt/expand
+namespace.default.asan.permitted.paths += /apex/com.android.resolv/${LIB}
 
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.default.links = runtime
@@ -332,7 +334,9 @@
 ###############################################################################
 namespace.runtime.isolated = true
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.links = default
+namespace.runtime.links = system,default
+namespace.runtime.link.system.shared_libs  = %LLNDK_LIBRARIES%
+namespace.runtime.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 # TODO(b/119867084): Restrict to Bionic dlopen dependencies.
 namespace.runtime.link.default.allow_all_shared_libs = true
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6fb1a8b..483fc51 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -545,6 +545,7 @@
     mkdir /data/anr 0775 system system
 
     mkdir /data/apex 0770 root root
+    mkdir /data/staging 0750 system system
 
     # NFC: create data/nfc for nv storage
     mkdir /data/nfc 0770 nfc nfc
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index a9658a4..35f469a 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,9 +1,6 @@
 firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
 uevent_socket_rcvbuf_size 16M
 
-subsystem adf
-    devname uevent_devname
-
 subsystem graphics
     devname uevent_devpath
     dirname /dev/graphics
@@ -12,26 +9,10 @@
     devname uevent_devpath
     dirname /dev/dri
 
-subsystem oncrpc
-    devname uevent_devpath
-    dirname /dev/oncrpc
-
-subsystem adsp
-    devname uevent_devpath
-    dirname /dev/adsp
-
-subsystem msm_camera
-    devname uevent_devpath
-    dirname /dev/msm_camera
-
 subsystem input
     devname uevent_devpath
     dirname /dev/input
 
-subsystem mtd
-    devname uevent_devpath
-    dirname /dev/mtd
-
 subsystem sound
     devname uevent_devpath
     dirname /dev/snd
@@ -59,73 +40,27 @@
 
 /dev/pmsg0                0222   root       log
 
-# the msm hw3d client device node is world writable/readable.
-/dev/msm_hw3dc            0666   root       root
-
-# gpu driver for adreno200 is globally accessible
-/dev/kgsl                 0666   root       root
-
 # kms driver for drm based gpu
 /dev/dri/*                0666   root       graphics
 
 # these should not be world writable
 /dev/diag                 0660   radio      radio
-/dev/diag_arm9            0660   radio      radio
 /dev/ttyMSM0              0600   bluetooth  bluetooth
 /dev/uhid                 0660   uhid       uhid
 /dev/uinput               0660   uhid       uhid
-/dev/alarm                0664   system     radio
 /dev/rtc0                 0640   system     system
 /dev/tty0                 0660   root       system
 /dev/graphics/*           0660   root       graphics
-/dev/msm_hw3dm            0660   system     graphics
 /dev/input/*              0660   root       input
 /dev/v4l-touch*           0660   root       input
-/dev/eac                  0660   root       audio
-/dev/cam                  0660   root       camera
-/dev/pmem                 0660   system     graphics
-/dev/pmem_adsp*           0660   system     audio
-/dev/pmem_camera*         0660   system     camera
-/dev/oncrpc/*             0660   root       system
-/dev/adsp/*               0660   system     audio
 /dev/snd/*                0660   system     audio
-/dev/mt9t013              0660   system     system
-/dev/msm_camera/*         0660   system     system
-/dev/akm8976_daemon       0640   compass    system
-/dev/akm8976_aot          0640   compass    system
-/dev/akm8973_daemon       0640   compass    system
-/dev/akm8973_aot          0640   compass    system
-/dev/bma150               0640   compass    system
-/dev/cm3602               0640   compass    system
-/dev/akm8976_pffd         0640   compass    system
-/dev/lightsensor          0640   system     system
-/dev/msm_pcm_out*         0660   system     audio
-/dev/msm_pcm_in*          0660   system     audio
-/dev/msm_pcm_ctl*         0660   system     audio
-/dev/msm_snd*             0660   system     audio
 /dev/msm_mp3*             0660   system     audio
-/dev/audience_a1026*      0660   system     audio
-/dev/tpa2018d1*           0660   system     audio
-/dev/msm_audpre           0660   system     audio
-/dev/msm_audio_ctl        0660   system     audio
-/dev/htc-acoustic         0660   system     audio
-/dev/vdec                 0660   system     audio
-/dev/q6venc               0660   system     audio
-/dev/snd/dsp              0660   system     audio
-/dev/snd/dsp1             0660   system     audio
-/dev/snd/mixer            0660   system     audio
-/dev/smd0                 0640   radio      radio
-/dev/qmi                  0640   radio      radio
-/dev/qmi0                 0640   radio      radio
-/dev/qmi1                 0640   radio      radio
-/dev/qmi2                 0640   radio      radio
 /dev/bus/usb/*            0660   root       usb
 /dev/mtp_usb              0660   root       mtp
 /dev/usb_accessory        0660   root       usb
 /dev/tun                  0660   system     vpn
 
 # CDMA radio interface MUX
-/dev/ts0710mux*           0640   radio      radio
 /dev/ppp                  0660   radio      vpn
 
 # sysfs properties
@@ -135,6 +70,3 @@
 /sys/devices/virtual/usb_composite/*   enable      0664  root   system
 /sys/devices/system/cpu/cpu*   cpufreq/scaling_max_freq   0664  system system
 /sys/devices/system/cpu/cpu*   cpufreq/scaling_min_freq   0664  system system
-
-# DVB API device nodes
-/dev/dvb*                 0660   root       system
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index 56a30b2..79bed7b 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -38,9 +38,11 @@
 
 sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
   $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
   $(UBSAN_RUNTIME_LIBRARY) \
   $(TSAN_RUNTIME_LIBRARY) \
   $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(2ND_HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
   $(2ND_UBSAN_RUNTIME_LIBRARY) \
   $(2ND_TSAN_RUNTIME_LIBRARY)))
 # If BOARD_VNDK_VERSION is not defined, VNDK version suffix will not be used.