Merge "usblib: fix race & delay waiting for perms on open"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 304ce4e..e2e9577 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -60,4 +60,14 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.$(TARGET_DEVICE).so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/vendor)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libtrusty.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libtrusty.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/keystore.trusty.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/keystore.trusty.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/gatekeeper.trusty.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.trusty.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/secure-storage-unit-test)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/storageproxyd)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/tipc-test)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/trusty_keymaster_tipc)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/root)
diff --git a/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT
index c40695a..29a6992 100644
--- a/adb/OVERVIEW.TXT
+++ b/adb/OVERVIEW.TXT
@@ -7,16 +7,16 @@
 - keep track of all Android devices and emulators instances
   connected to or running on a given host developer machine
 
-- implement various control commands (e.g. "adb shell", "adb pull", etc..)
+- implement various control commands (e.g. "adb shell", "adb pull", etc.)
   for the benefit of clients (command-line users, or helper programs like
-  DDMS). These commands are what is called a 'service' in ADB.
+  DDMS). These commands are called 'services' in ADB.
 
 As a whole, everything works through the following components:
 
   1. The ADB server
 
     This is a background process that runs on the host machine. Its purpose
-    if to sense the USB ports to know when devices are attached/removed,
+    is to sense the USB ports to know when devices are attached/removed,
     as well as when emulator instances start/stop.
 
     It thus maintains a list of "connected devices" and assigns a 'state'
@@ -40,7 +40,7 @@
     meaning that the ADB server detected a new device/emulator, but could not
     connect to the adbd daemon.
 
-    the BOOTLOADER and RECOVERY states correspond to alternate states of
+    The BOOTLOADER and RECOVERY states correspond to alternate states of
     devices when they are in the bootloader or recovery mode.
 
   3. The ADB command-line client
@@ -49,8 +49,7 @@
     or a script. It first tries to locate the ADB server on the host machine,
     and will start one automatically if none is found.
 
-    then, the client sends its service requests to the ADB server. It doesn't
-    need to know.
+    Then, the client sends its service requests to the ADB server.
 
     Currently, a single 'adb' binary is used for both the server and client.
     this makes distribution and starting the server easier.
@@ -61,13 +60,13 @@
     There are essentially two kinds of services that a client can talk to.
 
     Host Services:
-      these services run within the ADB Server and thus do not need to
+      These services run within the ADB Server and thus do not need to
       communicate with a device at all. A typical example is "adb devices"
       which is used to return the list of currently known devices and their
-      state. They are a few couple other services though.
+      states. They are a few other services though.
 
     Local Services:
-      these services either run within the adbd daemon, or are started by
+      These services either run within the adbd daemon, or are started by
       it on the device. The ADB server is used to multiplex streams
       between the client and the service running in adbd. In this case
       its role is to initiate the connection, then of being a pass-through
@@ -109,7 +108,7 @@
 
     Note that the connection is still alive after an OKAY, which allows the
     client to make other requests. But in certain cases, an OKAY will even
-    change the state of the connection. 
+    change the state of the connection.
 
     For example, the case of the 'host:transport:<serialnumber>' request,
     where '<serialnumber>' is used to identify a given device/emulator; after
diff --git a/adb/SYNC.TXT b/adb/SYNC.TXT
index 06d7804..4445a76 100644
--- a/adb/SYNC.TXT
+++ b/adb/SYNC.TXT
@@ -1,4 +1,4 @@
-This file tries to document file related requests a client can make
+This file tries to document file-related requests a client can make
 to the ADB server of an adbd daemon. See the OVERVIEW.TXT document
 to understand what's going on here. See the SERVICES.TXT to learn more
 about the other requests that are possible.
@@ -8,16 +8,16 @@
 
 Requesting the sync service ("sync:") using the protocol as described in
 SERVICES.TXT sets the connection in sync mode. This mode is a binary mode that
-differ from the regular adb protocol. The connection stays in sync mode until
+differs from the regular adb protocol. The connection stays in sync mode until
 explicitly terminated (see below).
 
 After the initial "sync:" command is sent the server must respond with either
-"OKAY" or "FAIL" as per usual. 
+"OKAY" or "FAIL" as per usual.
 
 In sync mode both the server and the client will frequently use eight-byte
-packets to communicate in this document called sync request and sync
-responses. The first four bytes is an id and specifies sync request is
-represented by four utf-8 characters. The last four bytes is a Little-Endian
+packets to communicate. In this document these are called sync requests and sync
+responses. The first four bytes are an id that specifies the sync request. It is
+represented by four utf-8 characters. The last four bytes are a Little-Endian
 integer, with various uses. This number will be called "length" below. In fact
 all binary integers are Little-Endian in the sync mode. Sync mode is
 implicitly exited after each sync request, and normal adb communication
@@ -29,8 +29,8 @@
 SEND - Send a file to device
 STAT - Stat a file
 
-For all of the sync request above the must be followed by length number of
-bytes containing an utf-8 string with a remote filename.
+All of the sync requests above must be followed by "length": the number of
+bytes containing a utf-8 string with a remote filename.
 
 LIST:
 Lists files in the directory specified by the remote filename. The server will
@@ -45,7 +45,7 @@
 6. length number of bytes containing an utf-8 string representing the file
    name.
 
-When an sync response "DONE" is received the listing is done.
+When a sync response "DONE" is received the listing is done.
 
 SEND:
 The remote file name is split into two parts separated by the last
@@ -65,7 +65,7 @@
 
 When the file is transferred a sync request "DONE" is sent, where length is set
 to the last modified time for the file. The server responds to this last
-request (but not to chuck requests) with an "OKAY" sync response (length can
+request (but not to chunk requests) with an "OKAY" sync response (length can
 be ignored).
 
 
@@ -73,9 +73,8 @@
 Retrieves a file from device to a local file. The remote path is the path to
 the file that will be returned. Just as for the SEND sync request the file
 received is split up into chunks. The sync response id is "DATA" and length is
-the chuck size. After follows chunk size number of bytes. This is repeated
-until the file is transferred. Each chuck will not be larger than 64k.
+the chunk size. After follows chunk size number of bytes. This is repeated
+until the file is transferred. Each chunk will not be larger than 64k.
 
 When the file is transferred a sync response "DONE" is retrieved where the
 length can be ignored.
-
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index c6f3e66..9a87931 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -62,7 +62,7 @@
     }
 
     vfs_cap_data cap_data = {};
-    cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
+    cap_data.magic_etc = VFS_CAP_REVISION_2 | VFS_CAP_FLAGS_EFFECTIVE;
     cap_data.data[0].permitted = (capabilities & 0xffffffff);
     cap_data.data[0].inheritable = 0;
     cap_data.data[1].permitted = (capabilities >> 32);
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 0abb680..307be6d 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -31,6 +31,7 @@
 
 // Include this before open/close/unlink are defined as macros below.
 #include <android-base/errors.h>
+#include <android-base/macros.h>
 #include <android-base/unique_fd.h>
 #include <android-base/utf8.h>
 
@@ -38,21 +39,6 @@
 #include "sysdeps/network.h"
 #include "sysdeps/stat.h"
 
-/*
- * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
- * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
- * not already defined, then define it here.
- */
-#ifndef TEMP_FAILURE_RETRY
-/* Used to retry syscalls that can return EINTR. */
-#define TEMP_FAILURE_RETRY(exp) ({         \
-    typeof (exp) _rc;                      \
-    do {                                   \
-        _rc = (exp);                       \
-    } while (_rc == -1 && errno == EINTR); \
-    _rc; })
-#endif
-
 // Some printf-like functions are implemented in terms of
 // android::base::StringAppendV, so they should use the same attribute for
 // compile-time format string checking. On Windows, if the mingw version of
diff --git a/adb/test_device.py b/adb/test_device.py
index ddceda9..4cf2206 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -34,11 +34,8 @@
 import time
 import unittest
 
-import mock
-
 import adb
 
-
 def requires_root(func):
     def wrapper(self, *args):
         if self.device.get_prop('ro.debuggable') != '1':
@@ -76,59 +73,6 @@
     return wrapper
 
 
-class GetDeviceTest(unittest.TestCase):
-    def setUp(self):
-        self.android_serial = os.getenv('ANDROID_SERIAL')
-        if 'ANDROID_SERIAL' in os.environ:
-            del os.environ['ANDROID_SERIAL']
-
-    def tearDown(self):
-        if self.android_serial is not None:
-            os.environ['ANDROID_SERIAL'] = self.android_serial
-        else:
-            if 'ANDROID_SERIAL' in os.environ:
-                del os.environ['ANDROID_SERIAL']
-
-    @mock.patch('adb.device.get_devices')
-    def test_explicit(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        device = adb.get_device('foo')
-        self.assertEqual(device.serial, 'foo')
-
-    @mock.patch('adb.device.get_devices')
-    def test_from_env(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        os.environ['ANDROID_SERIAL'] = 'foo'
-        device = adb.get_device()
-        self.assertEqual(device.serial, 'foo')
-
-    @mock.patch('adb.device.get_devices')
-    def test_arg_beats_env(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        os.environ['ANDROID_SERIAL'] = 'bar'
-        device = adb.get_device('foo')
-        self.assertEqual(device.serial, 'foo')
-
-    @mock.patch('adb.device.get_devices')
-    def test_no_such_device(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        self.assertRaises(adb.DeviceNotFoundError, adb.get_device, ['baz'])
-
-        os.environ['ANDROID_SERIAL'] = 'baz'
-        self.assertRaises(adb.DeviceNotFoundError, adb.get_device)
-
-    @mock.patch('adb.device.get_devices')
-    def test_unique_device(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo']
-        device = adb.get_device()
-        self.assertEqual(device.serial, 'foo')
-
-    @mock.patch('adb.device.get_devices')
-    def test_no_unique_device(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        self.assertRaises(adb.NoUniqueDeviceError, adb.get_device)
-
-
 class DeviceTest(unittest.TestCase):
     def setUp(self):
         self.device = adb.get_device()
diff --git a/adf/libadf/Android.bp b/adf/libadf/Android.bp
index c276c53..8eef2ea 100644
--- a/adf/libadf/Android.bp
+++ b/adf/libadf/Android.bp
@@ -12,8 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_static {
+cc_library {
     name: "libadf",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
     srcs: ["adf.cpp"],
     cflags: ["-Werror"],
     local_include_dirs: ["include"],
diff --git a/base/Android.bp b/base/Android.bp
index 7ff02a0..ad0edf4 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -129,6 +129,7 @@
         },
         windows: {
             srcs: ["utf8_test.cpp"],
+            cflags: ["-Wno-unused-parameter"],
             enabled: true,
         },
     },
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index 07a5edd..4cfa06b 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -24,6 +24,7 @@
 class TemporaryFile {
  public:
   TemporaryFile();
+  explicit TemporaryFile(const std::string& tmp_dir);
   ~TemporaryFile();
 
   // Release the ownership of fd, caller is reponsible for closing the
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index adb041b..6f05d9b 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -192,6 +192,7 @@
 #undef CHECK_WOULD_LOG_ENABLED
 
 
+#if !defined(_WIN32)
 static std::string make_log_pattern(android::base::LogSeverity severity,
                                     const char* message) {
   static const char log_characters[] = "VDIWEFF";
@@ -203,6 +204,7 @@
       "%c \\d+-\\d+ \\d+:\\d+:\\d+ \\s*\\d+ \\s*\\d+ %s:\\d+] %s",
       log_char, basename(&holder[0]), message);
 }
+#endif
 
 static void CheckMessage(const CapturedStderr& cap,
                          android::base::LogSeverity severity, const char* expected) {
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 1cfa9e6..9d8dfb2 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -84,6 +84,10 @@
   init(GetSystemTempDir());
 }
 
+TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
+  init(tmp_dir);
+}
+
 TemporaryFile::~TemporaryFile() {
   if (fd != -1) {
     close(fd);
diff --git a/base/utf8.cpp b/base/utf8.cpp
index 5984fb0..adb46d0 100644
--- a/base/utf8.cpp
+++ b/base/utf8.cpp
@@ -195,7 +195,7 @@
   return _wfopen(name_utf16.c_str(), mode_utf16.c_str());
 }
 
-int mkdir(const char* name, mode_t mode) {
+int mkdir(const char* name, mode_t) {
   std::wstring name_utf16;
   if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
     return -1;
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index c1d5430..79702a6 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -778,7 +778,12 @@
   checkDebugBuild || return
   duration_test ">90"
   panic_msg="kernel_panic,sysrq"
-  enterPstore || panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
+  enterPstore
+  if [ ${?} != 0 ]; then
+    echo "         or functional bootloader" >&2
+    panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
+    pstore_ok=true
+  fi
   echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 8c11289..40ebde0 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -228,6 +228,24 @@
     {"reboot,its_just_so_hard", 88},  // produced by boot_reason_test
     {"reboot,Its Just So Hard", 89},  // produced by boot_reason_test
     {"usb", 90},
+    {"charge", 91},
+    {"oem_tz_crash", 92},
+    {"uvlo", 93},
+    {"oem_ps_hold", 94},
+    {"abnormal_reset", 95},
+    {"oemerr_unknown", 96},
+    {"reboot_fastboot_mode", 97},
+    {"watchdog_apps_bite", 98},
+    {"xpu_err", 99},
+    {"power_on_usb", 100},
+    {"watchdog_rpm", 101},
+    {"watchdog_nonsec", 102},
+    {"watchdog_apps_bark", 103},
+    {"reboot_dmverity_corrupted", 104},
+    {"reboot_smpl", 105},
+    {"watchdog_sdi_apps_reset", 106},
+    {"smpl", 107},
+    {"oem_modem_failed_to_powerup", 108},
 };
 
 // Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index d6b6d58..bd202ff 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -429,7 +429,12 @@
     abort_message = g_callbacks.get_abort_message();
   }
 
-  if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
+  // If sival_int is ~0, it means that the fallback handler has been called
+  // once before and this function is being called again to dump the stack
+  // of a specific thread. It is possible that the prctl call might return 1,
+  // then return 0 in subsequent calls, so check the sival_int to determine if
+  // the fallback handler should be called first.
+  if (info->si_value.sival_int == ~0 || prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
     // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,
     // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
     // ANR trace.
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 725c42c..a0ba81b 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -417,7 +417,7 @@
        "memory map (%zu entr%s):",
        map->size(), map->size() == 1 ? "y" : "ies");
   if (print_fault_address_marker) {
-    if (map->begin() != map->end() && addr < map->begin()->start) {
+    if (map->begin() != map->end() && addr < (*map->begin())->start) {
       _LOG(log, logtype::MAPS, "\n--->Fault address falls at %s before any mapped regions\n",
            get_addr_string(addr).c_str());
       print_fault_address_marker = false;
@@ -429,49 +429,50 @@
   }
 
   std::string line;
-  for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
+  for (auto it = map->begin(); it != map->end(); ++it) {
+    const backtrace_map_t* entry = *it;
     line = "    ";
     if (print_fault_address_marker) {
-      if (addr < it->start) {
+      if (addr < entry->start) {
         _LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n",
              get_addr_string(addr).c_str());
         print_fault_address_marker = false;
-      } else if (addr >= it->start && addr < it->end) {
+      } else if (addr >= entry->start && addr < entry->end) {
         line = "--->";
         print_fault_address_marker = false;
       }
     }
-    line += get_addr_string(it->start) + '-' + get_addr_string(it->end - 1) + ' ';
-    if (it->flags & PROT_READ) {
+    line += get_addr_string(entry->start) + '-' + get_addr_string(entry->end - 1) + ' ';
+    if (entry->flags & PROT_READ) {
       line += 'r';
     } else {
       line += '-';
     }
-    if (it->flags & PROT_WRITE) {
+    if (entry->flags & PROT_WRITE) {
       line += 'w';
     } else {
       line += '-';
     }
-    if (it->flags & PROT_EXEC) {
+    if (entry->flags & PROT_EXEC) {
       line += 'x';
     } else {
       line += '-';
     }
-    line += StringPrintf("  %8" PRIxPTR "  %8" PRIxPTR, it->offset, it->end - it->start);
+    line += StringPrintf("  %8" PRIxPTR "  %8" PRIxPTR, entry->offset, entry->end - entry->start);
     bool space_needed = true;
-    if (it->name.length() > 0) {
+    if (entry->name.length() > 0) {
       space_needed = false;
-      line += "  " + it->name;
+      line += "  " + entry->name;
       std::string build_id;
-      if ((it->flags & PROT_READ) && elf_get_build_id(backtrace, it->start, &build_id)) {
+      if ((entry->flags & PROT_READ) && elf_get_build_id(backtrace, entry->start, &build_id)) {
         line += " (BuildId: " + build_id + ")";
       }
     }
-    if (it->load_bias != 0) {
+    if (entry->load_bias != 0) {
       if (space_needed) {
         line += ' ';
       }
-      line += StringPrintf(" (load bias 0x%" PRIxPTR ")", it->load_bias);
+      line += StringPrintf(" (load bias 0x%" PRIxPTR ")", entry->load_bias);
     }
     _LOG(log, logtype::MAPS, "%s\n", line.c_str());
   }
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index bc88002..e0f702d 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -23,7 +23,6 @@
 LOCAL_C_INCLUDES := \
   $(LOCAL_PATH)/../adb \
   $(LOCAL_PATH)/../mkbootimg \
-  $(LOCAL_PATH)/../../extras/f2fs_utils \
 
 LOCAL_SRC_FILES := \
     bootimg_utils.cpp \
@@ -40,7 +39,7 @@
 LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
-LOCAL_REQUIRED_MODULES := mke2fs e2fsdroid
+LOCAL_REQUIRED_MODULES := mke2fs e2fsdroid mke2fs.conf make_f2fs
 
 LOCAL_SRC_FILES_linux := usb_linux.cpp
 LOCAL_STATIC_LIBRARIES_linux := libselinux
@@ -67,14 +66,6 @@
     libcutils \
     libgtest_host \
 
-# libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn")
-LOCAL_CFLAGS_linux := -DUSE_F2FS
-LOCAL_LDFLAGS_linux := -ldl -rdynamic -Wl,-rpath,.
-LOCAL_REQUIRED_MODULES_linux := libf2fs_fmt_host_dyn
-# The following libf2fs_* are from system/extras/f2fs_utils,
-# and do not use code in external/f2fs-tools.
-LOCAL_STATIC_LIBRARIES_linux += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
-
 LOCAL_CXX_STL := libc++_static
 
 # Don't add anything here, we don't want additional shared dependencies
@@ -87,9 +78,7 @@
 my_dist_files := $(LOCAL_BUILT_MODULE)
 my_dist_files += $(HOST_OUT_EXECUTABLES)/mke2fs$(HOST_EXECUTABLE_SUFFIX)
 my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid$(HOST_EXECUTABLE_SUFFIX)
-ifeq ($(HOST_OS),linux)
-my_dist_files += $(HOST_LIBRARY_PATH)/libf2fs_fmt_host_dyn$(HOST_SHLIB_SUFFIX)
-endif
+my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs$(HOST_EXECUTABLE_SUFFIX)
 $(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
 ifdef HOST_CROSS_OS
 # Archive fastboot.exe for win_sdk build.
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 40c18e0..6175f59 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -485,7 +485,7 @@
     return bdata;
 }
 
-static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
+static void* unzip_to_memory(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
     ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
@@ -495,7 +495,7 @@
 
     *sz = zip_entry.uncompressed_length;
 
-    fprintf(stderr, "extracting %s (%" PRId64 " MB)...\n", entry_name, *sz / 1024 / 1024);
+    fprintf(stderr, "extracting %s (%" PRId64 " MB) to RAM...\n", entry_name, *sz / 1024 / 1024);
     uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
     if (data == nullptr) die("failed to allocate %" PRId64 " bytes for '%s'", *sz, entry_name);
 
@@ -613,17 +613,20 @@
         return -1;
     }
 
-    fprintf(stderr, "extracting %s (%" PRIu32 " MB)...\n", entry_name,
+    fprintf(stderr, "extracting %s (%" PRIu32 " MB) to disk...", entry_name,
             zip_entry.uncompressed_length / 1024 / 1024);
+    double start = now();
     int error = ExtractEntryToFile(zip, &zip_entry, fd);
     if (error != 0) {
-        die("failed to extract '%s': %s", entry_name, ErrorCodeString(error));
+        die("\nfailed to extract '%s': %s", entry_name, ErrorCodeString(error));
     }
 
     if (lseek(fd, 0, SEEK_SET) != 0) {
-        die("lseek on extracted file '%s' failed: %s", entry_name, strerror(errno));
+        die("\nlseek on extracted file '%s' failed: %s", entry_name, strerror(errno));
     }
 
+    fprintf(stderr, " took %.3fs\n", now() - start);
+
     return fd.release();
 }
 
@@ -1091,7 +1094,7 @@
 
 static void do_update_signature(ZipArchiveHandle zip, const char* filename) {
     int64_t sz;
-    void* data = unzip_file(zip, filename, &sz);
+    void* data = unzip_to_memory(zip, filename, &sz);
     if (data == nullptr) return;
     fb_queue_download("signature", data, sz);
     fb_queue_command("signature", "installing signature");
@@ -1130,7 +1133,7 @@
     }
 
     int64_t sz;
-    void* data = unzip_file(zip, "android-info.txt", &sz);
+    void* data = unzip_to_memory(zip, "android-info.txt", &sz);
     if (data == nullptr) {
         die("update package '%s' has no android-info.txt", filename);
     }
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 2d77dd6..9949eae 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -1,7 +1,6 @@
 #include "fs.h"
 
 #include "fastboot.h"
-#include "make_f2fs.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -23,13 +22,13 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
-#include <sparse/sparse.h>
 
+using android::base::GetExecutableDirectory;
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
 #ifdef WIN32
-static int exec_e2fs_cmd(const char* path, char* const argv[]) {
+static int exec_e2fs_cmd(const char* /*path*/, const char** argv, const char** envp) {
     std::string cmd;
     int i = 0;
     while (argv[i] != nullptr) {
@@ -46,7 +45,13 @@
     si.cb = sizeof(si);
     ZeroMemory(&pi, sizeof(pi));
 
-    SetEnvironmentVariableA("MKE2FS_CONFIG", "");
+    std::string env_str;
+    if (envp != nullptr) {
+        while (*envp != nullptr) {
+            env_str += std::string(*envp) + std::string("\0", 1);
+            envp++;
+        }
+    }
 
     if (!CreateProcessA(nullptr,                         // No module name (use command line)
                         const_cast<char*>(cmd.c_str()),  // Command line
@@ -54,10 +59,10 @@
                         nullptr,                         // Thread handle not inheritable
                         FALSE,                           // Set handle inheritance to FALSE
                         0,                               // No creation flags
-                        nullptr,                         // Use parent's environment block
-                        nullptr,                         // Use parent's starting directory
-                        &si,                             // Pointer to STARTUPINFO structure
-                        &pi)                             // Pointer to PROCESS_INFORMATION structure
+                        env_str.empty() ? nullptr : LPSTR(env_str.c_str()),
+                        nullptr,  // Use parent's starting directory
+                        &si,      // Pointer to STARTUPINFO structure
+                        &pi)      // Pointer to PROCESS_INFORMATION structure
     ) {
         fprintf(stderr, "CreateProcess failed: %s\n",
                 android::base::SystemErrorCodeToString(GetLastError()).c_str());
@@ -74,12 +79,11 @@
     return exit_code != 0;
 }
 #else
-static int exec_e2fs_cmd(const char* path, char* const argv[]) {
+static int exec_e2fs_cmd(const char* path, const char** argv, const char** envp) {
     int status;
     pid_t child;
     if ((child = fork()) == 0) {
-        setenv("MKE2FS_CONFIG", "", 1);
-        execvp(path, argv);
+        execve(path, const_cast<char**>(argv), const_cast<char**>(envp));
         _exit(EXIT_FAILURE);
     }
     if (child < 0) {
@@ -133,7 +137,10 @@
     mke2fs_args.push_back(size_str.c_str());
     mke2fs_args.push_back(nullptr);
 
-    int ret = exec_e2fs_cmd(mke2fs_args[0], const_cast<char**>(mke2fs_args.data()));
+    const std::string mke2fs_env = "MKE2FS_CONFIG=" + GetExecutableDirectory() + "/mke2fs.conf";
+    std::vector<const char*> mke2fs_envp = {mke2fs_env.c_str(), nullptr};
+
+    int ret = exec_e2fs_cmd(mke2fs_args[0], mke2fs_args.data(), mke2fs_envp.data());
     if (ret != 0) {
         fprintf(stderr, "mke2fs failed: %d\n", ret);
         return -1;
@@ -147,7 +154,7 @@
     std::vector<const char*> e2fsdroid_args = {e2fsdroid_path.c_str(), "-f", initial_dir.c_str(),
                                                fileName, nullptr};
 
-    ret = exec_e2fs_cmd(e2fsdroid_args[0], const_cast<char**>(e2fsdroid_args.data()));
+    ret = exec_e2fs_cmd(e2fsdroid_args[0], e2fsdroid_args.data(), nullptr);
     if (ret != 0) {
         fprintf(stderr, "e2fsdroid failed: %d\n", ret);
         return -1;
@@ -156,22 +163,42 @@
     return 0;
 }
 
-#ifdef USE_F2FS
 static int generate_f2fs_image(const char* fileName, long long partSize, const std::string& initial_dir,
                                unsigned /* unused */, unsigned /* unused */)
 {
+#ifndef WIN32
+    const std::string exec_dir = android::base::GetExecutableDirectory();
+    const std::string mkf2fs_path = exec_dir + "/make_f2fs";
+    std::vector<const char*> mkf2fs_args = {mkf2fs_path.c_str()};
+
+    mkf2fs_args.push_back("-S");
+    std::string size_str = std::to_string(partSize);
+    mkf2fs_args.push_back(size_str.c_str());
+    mkf2fs_args.push_back("-f");
+    mkf2fs_args.push_back("-O");
+    mkf2fs_args.push_back("encrypt");
+    mkf2fs_args.push_back("-O");
+    mkf2fs_args.push_back("quota");
+    mkf2fs_args.push_back(fileName);
+    mkf2fs_args.push_back(nullptr);
+
+    int ret = exec_e2fs_cmd(mkf2fs_args[0], mkf2fs_args.data(), nullptr);
+    if (ret != 0) {
+        fprintf(stderr, "mkf2fs failed: %d\n", ret);
+        return -1;
+    }
+
     if (!initial_dir.empty()) {
-        fprintf(stderr, "Unable to set initial directory on F2FS filesystem: %s\n", strerror(errno));
-        return -1;
+        fprintf(stderr, "sload.f2s not supported yet\n");
+        return 0;
     }
-    unique_fd fd(open(fileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
-    if (fd == -1) {
-        fprintf(stderr, "Unable to open output file for F2FS filesystem: %s\n", strerror(errno));
-        return -1;
-    }
-    return make_f2fs_sparse_fd(fd, partSize, NULL, NULL);
-}
+    return 0;
+#else
+    UNUSED(fileName, partSize, initial_dir);
+    fprintf(stderr, "make_f2fs not supported on Windows\n");
+    return -1;
 #endif
+}
 
 static const struct fs_generator {
     const char* fs_type;  //must match what fastboot reports for partition type
@@ -182,9 +209,7 @@
 
 } generators[] = {
     { "ext4", generate_ext4_image},
-#ifdef USE_F2FS
     { "f2fs", generate_f2fs_image},
-#endif
 };
 
 const struct fs_generator* fs_get_generator(const std::string& fs_type) {
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
deleted file mode 100644
index 007189d..0000000
--- a/fs_mgr/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2011 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-common_static_libraries := \
-    liblogwrap \
-    libfec \
-    libfec_rs \
-    libbase \
-    libcrypto_utils \
-    libcrypto \
-    libext4_utils \
-    libsquashfs_utils \
-    libselinux \
-    libavb
-
-include $(CLEAR_VARS)
-LOCAL_SANITIZE := integer
-LOCAL_SRC_FILES:= fs_mgr_main.cpp
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_MODULE:= fs_mgr
-LOCAL_MODULE_TAGS := optional
-LOCAL_REQUIRED_MODULES := mke2fs mke2fs.conf e2fsdroid
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
-LOCAL_STATIC_LIBRARIES := libfs_mgr \
-    $(common_static_libraries) \
-    libcutils \
-    liblog \
-    libc \
-    libsparse \
-    libz \
-    libselinux
-LOCAL_CXX_STL := libc++_static
-LOCAL_CFLAGS := -Werror
-include $(BUILD_EXECUTABLE)
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 75feee7..fc88217 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -38,7 +38,6 @@
 {
     uint64_t dev_sz;
     int fd, rc = 0;
-    int status;
 
     if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
         PERROR << "Cannot open block device";
@@ -62,7 +61,7 @@
     const char* const mke2fs_args[] = {
         "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", fs_blkdev, size_str.c_str(), nullptr};
 
-    rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), &status,
+    rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), NULL,
                                  true, LOG_KLOG, true, nullptr, nullptr, 0);
     if (rc) {
         LERROR << "mke2fs returned " << rc;
@@ -78,7 +77,7 @@
         nullptr};
 
     rc = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args),
-                                 &status, true, LOG_KLOG, true, nullptr, nullptr, 0);
+                                 NULL, true, LOG_KLOG, true, nullptr, nullptr, 0);
     if (rc) {
         LERROR << "e2fsdroid returned " << rc;
     }
@@ -88,10 +87,9 @@
 
 static int format_f2fs(char *fs_blkdev)
 {
-    int status;
     const char* const args[] = {"/system/bin/make_f2fs", "-f", "-O encrypt", fs_blkdev, nullptr};
 
-    return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), &status, true,
+    return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), NULL, true,
                                    LOG_KLOG, true, nullptr, nullptr, 0);
 }
 
diff --git a/fs_mgr/fs_mgr_main.cpp b/fs_mgr/fs_mgr_main.cpp
deleted file mode 100644
index f3919d9..0000000
--- a/fs_mgr/fs_mgr_main.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include "fs_mgr_priv.h"
-
-#ifdef _LIBGEN_H
-#warning "libgen.h must not be included"
-#endif
-
-char *me = nullptr;
-
-static void usage(void)
-{
-    LERROR << me << ": usage: " << me
-           << " <-a | -n mnt_point blk_dev | -u> <fstab_file>";
-    exit(1);
-}
-
-/* Parse the command line.  If an error is encountered, print an error message
- * and exit the program, do not return to the caller.
- * Return the number of argv[] entries consumed.
- */
-static void parse_options(int argc, char * const argv[], int *a_flag, int *u_flag, int *n_flag,
-                     const char **n_name, const char **n_blk_dev)
-{
-    me = basename(argv[0]);
-
-    if (argc <= 1) {
-        usage();
-    }
-
-    if (!strcmp(argv[1], "-a")) {
-        if (argc != 3) {
-            usage();
-        }
-        *a_flag = 1;
-    }
-    if (!strcmp(argv[1], "-n")) {
-        if (argc != 5) {
-            usage();
-        }
-        *n_flag = 1;
-        *n_name = argv[2];
-        *n_blk_dev = argv[3];
-    }
-    if (!strcmp(argv[1], "-u")) {
-        if (argc != 3) {
-            usage();
-        }
-        *u_flag = 1;
-    }
-
-    /* If no flag is specified, it's an error */
-    if (!(*a_flag | *n_flag | *u_flag)) {
-        usage();
-    }
-
-    /* If more than one flag is specified, it's an error */
-    if ((*a_flag + *n_flag + *u_flag) > 1) {
-        usage();
-    }
-
-    return;
-}
-
-int main(int argc, char * const argv[])
-{
-    int a_flag=0;
-    int u_flag=0;
-    int n_flag=0;
-    const char *n_name=NULL;
-    const char *n_blk_dev=NULL;
-    const char *fstab_file=NULL;
-    struct fstab *fstab=NULL;
-
-    setenv("ANDROID_LOG_TAGS", "*:i", 1); // Set log level to INFO
-    android::base::InitLogging(
-        const_cast<char **>(argv), &android::base::KernelLogger);
-
-    parse_options(argc, argv, &a_flag, &u_flag, &n_flag, &n_name, &n_blk_dev);
-
-    /* The name of the fstab file is last, after the option */
-    fstab_file = argv[argc - 1];
-
-    fstab = fs_mgr_read_fstab(fstab_file);
-
-    if (a_flag) {
-        return fs_mgr_mount_all(fstab, MOUNT_MODE_DEFAULT);
-    } else if (n_flag) {
-        return fs_mgr_do_mount(fstab, n_name, (char *)n_blk_dev, 0);
-    } else if (u_flag) {
-        return fs_mgr_unmount_all(fstab);
-    } else {
-        LERROR << me << ": Internal error, unknown option";
-        exit(1);
-    }
-
-    fs_mgr_free_fstab(fstab);
-
-    /* Should not get here */
-    exit(1);
-}
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 33fd562..0a113b4 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -40,7 +40,7 @@
             char *tmp;
             if (ab_suffix.empty()) {
                 ab_suffix = fs_mgr_get_slot_suffix();
-                // Returns false as non A/B devices should not have MF_SLOTSELECT.
+                // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.
                 if (ab_suffix.empty()) return false;
             }
             if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device, ab_suffix.c_str()) > 0) {
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk
index 0dfd9d8..28f0b07 100644
--- a/gatekeeperd/Android.mk
+++ b/gatekeeperd/Android.mk
@@ -21,8 +21,7 @@
 LOCAL_SRC_FILES := \
 	SoftGateKeeperDevice.cpp \
 	IGateKeeperService.cpp \
-	gatekeeperd.cpp \
-	IUserManager.cpp
+	gatekeeperd.cpp
 
 LOCAL_MODULE := gatekeeperd
 LOCAL_SHARED_LIBRARIES := \
diff --git a/gatekeeperd/IUserManager.cpp b/gatekeeperd/IUserManager.cpp
deleted file mode 100644
index 8167d19..0000000
--- a/gatekeeperd/IUserManager.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IUserManager"
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/Log.h>
-#include <binder/Parcel.h>
-
-#include "IUserManager.h"
-
-namespace android {
-
-class BpUserManager : public BpInterface<IUserManager>
-{
-public:
-    explicit BpUserManager(const sp<IBinder>& impl) :
-            BpInterface<IUserManager>(impl) {
-    }
-    virtual int32_t getCredentialOwnerProfile(int32_t user_id) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IUserManager::getInterfaceDescriptor());
-        data.writeInt32(user_id);
-        status_t rc = remote()->transact(GET_CREDENTIAL_OWNER_PROFILE, data, &reply, 0);
-        if (rc != NO_ERROR) {
-            ALOGE("%s: failed (%d)\n", __func__, rc);
-            return -1;
-        }
-
-        int32_t exception = reply.readExceptionCode();
-        if (exception != 0) {
-            ALOGE("%s: got exception (%d)\n", __func__, exception);
-            return -1;
-        }
-
-        return reply.readInt32();
-    }
-
-};
-
-IMPLEMENT_META_INTERFACE(UserManager, "android.os.IUserManager");
-
-}; // namespace android
-
diff --git a/gatekeeperd/IUserManager.h b/gatekeeperd/IUserManager.h
deleted file mode 100644
index 640e9b5..0000000
--- a/gatekeeperd/IUserManager.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef IUSERMANAGER_H_
-#define IUSERMANAGER_H_
-
-#include <inttypes.h>
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-/*
-* Communication channel to UserManager
-*/
-class IUserManager : public IInterface {
-    public:
-        // must be kept in sync with IUserManager.aidl
-        enum {
-            GET_CREDENTIAL_OWNER_PROFILE = IBinder::FIRST_CALL_TRANSACTION + 0,
-        };
-
-        virtual int32_t getCredentialOwnerProfile(int32_t user_id) = 0;
-
-        DECLARE_META_INTERFACE(UserManager);
-};
-
-}; // namespace android
-
-#endif // IUSERMANAGER_H_
-
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index d581736..61c8804 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -38,7 +38,6 @@
 #include <utils/String16.h>
 
 #include "SoftGateKeeperDevice.h"
-#include "IUserManager.h"
 
 #include <hidl/HidlSupport.h>
 #include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
@@ -335,23 +334,7 @@
         return ret;
     }
 
-    virtual uint64_t getSecureUserId(uint32_t uid) {
-        uint64_t sid = read_sid(uid);
-         if (sid == 0) {
-            // might be a work profile, look up the parent
-            sp<IServiceManager> sm = defaultServiceManager();
-            sp<IBinder> binder = sm->getService(String16("user"));
-            sp<IUserManager> um = interface_cast<IUserManager>(binder);
-            int32_t parent = um->getCredentialOwnerProfile(uid);
-            if (parent < 0) {
-                return 0;
-            } else if (parent != (int32_t) uid) {
-                return read_sid(parent);
-            }
-        }
-        return sid;
-
-    }
+    virtual uint64_t getSecureUserId(uint32_t uid) { return read_sid(uid); }
 
     virtual void clearSecureUserId(uint32_t uid) {
         IPCThreadState* ipc = IPCThreadState::self();
diff --git a/healthd/Android.bp b/healthd/Android.bp
new file mode 100644
index 0000000..56f5148
--- /dev/null
+++ b/healthd/Android.bp
@@ -0,0 +1,7 @@
+cc_library_headers {
+    name: "libhealthd_headers",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+    header_libs: ["libbatteryservice_headers"],
+    export_header_lib_headers: ["libbatteryservice_headers"],
+}
diff --git a/init/action.cpp b/init/action.cpp
index 2617d00..5fa6bec 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -379,10 +379,12 @@
     return action_ ? action_->AddCommand(std::move(args), line) : Success();
 }
 
-void ActionParser::EndSection() {
+Result<Success> ActionParser::EndSection() {
     if (action_ && action_->NumCommands() > 0) {
         action_manager_->AddAction(std::move(action_));
     }
+
+    return Success();
 }
 
 }  // namespace init
diff --git a/init/action.h b/init/action.h
index cdfc6a0..1bfc6c7 100644
--- a/init/action.h
+++ b/init/action.h
@@ -130,7 +130,7 @@
     Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line) override;
     Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
-    void EndSection() override;
+    Result<Success> EndSection() override;
 
   private:
     ActionManager* action_manager_;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 950a551..f584021 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -65,6 +65,7 @@
 #include "property_service.h"
 #include "reboot.h"
 #include "rlimit_parser.h"
+#include "selinux.h"
 #include "service.h"
 #include "subcontext.h"
 #include "util.h"
@@ -641,8 +642,26 @@
     return Success();
 }
 
+static int MakeSymlink(const std::string& target, const std::string& linkpath) {
+    std::string secontext;
+    // Passing 0 for mode should work.
+    if (SelabelLookupFileContext(linkpath, 0, &secontext) && !secontext.empty()) {
+        setfscreatecon(secontext.c_str());
+    }
+
+    int rc = symlink(target.c_str(), linkpath.c_str());
+
+    if (!secontext.empty()) {
+        int save_errno = errno;
+        setfscreatecon(nullptr);
+        errno = save_errno;
+    }
+
+    return rc;
+}
+
 static Result<Success> do_symlink(const BuiltinArguments& args) {
-    if (symlink(args[1].c_str(), args[2].c_str()) < 0) {
+    if (MakeSymlink(args[1], args[2]) < 0) {
         // The symlink builtin is often used to create symlinks for older devices to be backwards
         // compatible with new paths, therefore we skip reporting this error.
         if (errno == EEXIST && android::base::GetMinimumLogSeverity() > android::base::DEBUG) {
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index 642a364..50987db 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -194,5 +194,18 @@
     return SetAmbientCaps(to_keep);
 }
 
+bool DropInheritableCaps() {
+    ScopedCaps caps(cap_get_proc());
+    if (cap_clear_flag(caps.get(), CAP_INHERITABLE) == -1) {
+        PLOG(ERROR) << "cap_clear_flag(INHERITABLE) failed";
+        return false;
+    }
+    if (cap_set_proc(caps.get()) != 0) {
+        PLOG(ERROR) << "cap_set_proc() failed";
+        return false;
+    }
+    return true;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/capabilities.h b/init/capabilities.h
index ede85c3..fc80c98 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -35,6 +35,7 @@
 bool CapAmbientSupported();
 unsigned int GetLastValidCap();
 bool SetCapsForExec(const CapSet& to_keep);
+bool DropInheritableCaps();
 
 }  // namespace init
 }  // namespace android
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index b686885..8c8d9f2 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -78,8 +78,8 @@
         return;
     }
 
-    static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
-                                          "/firmware/image/"};
+    static const char* firmware_dirs[] = {"/etc/firmware/", "/odm/firmware/",
+                                          "/vendor/firmware/", "/firmware/image/"};
 
 try_loading_again:
     for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 0f7e38f..6fa07e7 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -118,7 +118,10 @@
 FirstStageMount::FirstStageMount()
     : need_dm_verity_(false), device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
     if (!device_tree_fstab_) {
-        LOG(ERROR) << "Failed to read fstab from device tree";
+        // The client of FirstStageMount should check the existence of fstab in device-tree
+        // in advance, without parsing it. Reaching here means there is a FATAL error when
+        // parsing the fstab. So stop here to expose the failure.
+        LOG(FATAL) << "Failed to read fstab from device tree";
         return;
     }
     // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 29a65ab..268873c 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -25,6 +25,7 @@
 #include "import_parser.h"
 #include "keyword_map.h"
 #include "parser.h"
+#include "service.h"
 #include "test_function_map.h"
 #include "util.h"
 
@@ -34,12 +35,13 @@
 using ActionManagerCommand = std::function<void(ActionManager&)>;
 
 void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
-              const std::vector<ActionManagerCommand>& commands) {
+              const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
     ActionManager am;
 
     Action::set_function_map(&test_function_map);
 
     Parser parser;
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(service_list, nullptr));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
@@ -55,11 +57,11 @@
 }
 
 void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
-                  const std::vector<ActionManagerCommand>& commands) {
+                  const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
-    TestInit(tf.path, test_function_map, commands);
+    TestInit(tf.path, test_function_map, commands, service_list);
 }
 
 TEST(init, SimpleEventTrigger) {
@@ -76,7 +78,8 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
-    TestInitText(init_script, test_function_map, commands);
+    ServiceList service_list;
+    TestInitText(init_script, test_function_map, commands, &service_list);
 
     EXPECT_TRUE(expect_true);
 }
@@ -104,7 +107,30 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
-    TestInitText(init_script, test_function_map, commands);
+    ServiceList service_list;
+    TestInitText(init_script, test_function_map, commands, &service_list);
+}
+
+TEST(init, OverrideService) {
+    std::string init_script = R"init(
+service A something
+    class first
+
+service A something
+    class second
+    override
+
+)init";
+
+    ServiceList service_list;
+    TestInitText(init_script, TestFunctionMap(), {}, &service_list);
+    ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));
+
+    auto service = service_list.begin()->get();
+    ASSERT_NE(nullptr, service);
+    EXPECT_EQ(std::set<std::string>({"second"}), service->classnames());
+    EXPECT_EQ("A", service->name());
+    EXPECT_TRUE(service->is_override());
 }
 
 TEST(init, EventTriggerOrderMultipleFiles) {
@@ -162,7 +188,9 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
-    TestInit(start.path, test_function_map, commands);
+    ServiceList service_list;
+
+    TestInit(start.path, test_function_map, commands, &service_list);
 
     EXPECT_EQ(6, num_executed);
 }
diff --git a/init/parser.cpp b/init/parser.cpp
index 8a4e798..6ddb09f 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -50,12 +50,24 @@
     state.nexttoken = 0;
 
     SectionParser* section_parser = nullptr;
+    int section_start_line = -1;
     std::vector<std::string> args;
 
+    auto end_section = [&] {
+        if (section_parser == nullptr) return;
+
+        if (auto result = section_parser->EndSection(); !result) {
+            LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
+        }
+
+        section_parser = nullptr;
+        section_start_line = -1;
+    };
+
     for (;;) {
         switch (next_token(&state)) {
             case T_EOF:
-                if (section_parser) section_parser->EndSection();
+                end_section();
                 return;
             case T_NEWLINE:
                 state.line++;
@@ -65,18 +77,18 @@
                 // uevent.
                 for (const auto& [prefix, callback] : line_callbacks_) {
                     if (android::base::StartsWith(args[0], prefix.c_str())) {
-                        if (section_parser) section_parser->EndSection();
+                        end_section();
 
                         if (auto result = callback(std::move(args)); !result) {
                             LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         }
-                        section_parser = nullptr;
                         break;
                     }
                 }
                 if (section_parsers_.count(args[0])) {
-                    if (section_parser) section_parser->EndSection();
+                    end_section();
                     section_parser = section_parsers_[args[0]].get();
+                    section_start_line = state.line;
                     if (auto result =
                             section_parser->ParseSection(std::move(args), filename, state.line);
                         !result) {
diff --git a/init/parser.h b/init/parser.h
index 4ab24a4..110a468 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -26,24 +26,22 @@
 
 //  SectionParser is an interface that can parse a given 'section' in init.
 //
-//  You can implement up to 4 functions below, with ParseSection() being mandatory.
-//  The first two function return bool with false indicating a failure and has a std::string* err
-//  parameter into which an error string can be written.  It will be reported along with the
-//  filename and line number of where the error occurred.
+//  You can implement up to 4 functions below, with ParseSection being mandatory. The first two
+//  functions return Result<Success> indicating if they have an error. It will be reported along
+//  with the filename and line number of where the error occurred.
 //
-//  1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
-//                       int line, std::string* err)
+//  1) ParseSection
 //    This function is called when a section is first encountered.
 //
-//  2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
+//  2) ParseLineSection
 //    This function is called on each subsequent line until the next section is encountered.
 //
-//  3) bool EndSection()
+//  3) EndSection
 //    This function is called either when a new section is found or at the end of the file.
 //    It indicates that parsing of the current section is complete and any relevant objects should
 //    be committed.
 //
-//  4) bool EndFile()
+//  4) EndFile
 //    This function is called at the end of the file.
 //    It indicates that the parsing has completed and any relevant objects should be committed.
 
@@ -56,7 +54,7 @@
     virtual Result<Success> ParseSection(std::vector<std::string>&& args,
                                          const std::string& filename, int line) = 0;
     virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
-    virtual void EndSection(){};
+    virtual Result<Success> EndSection() { return Success(); };
     virtual void EndFile(){};
 };
 
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 331fad6..21010b0 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -302,18 +302,18 @@
     }
     std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
 
-    // vendor_sepolicy.cil and nonplat_declaration.cil are the new design to replace
+    // vendor_sepolicy.cil and plat_pub_versioned.cil are the new design to replace
     // nonplat_sepolicy.cil.
-    std::string nonplat_declaration_cil_file("/vendor/etc/selinux/nonplat_declaration.cil");
+    std::string plat_pub_versioned_cil_file("/vendor/etc/selinux/plat_pub_versioned.cil");
     std::string vendor_policy_cil_file("/vendor/etc/selinux/vendor_sepolicy.cil");
 
     if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {
         // For backward compatibility.
         // TODO: remove this after no device is using nonplat_sepolicy.cil.
         vendor_policy_cil_file = "/vendor/etc/selinux/nonplat_sepolicy.cil";
-        nonplat_declaration_cil_file.clear();
-    } else if (access(nonplat_declaration_cil_file.c_str(), F_OK) == -1) {
-        LOG(ERROR) << "Missing " << nonplat_declaration_cil_file;
+        plat_pub_versioned_cil_file.clear();
+    } else if (access(plat_pub_versioned_cil_file.c_str(), F_OK) == -1) {
+        LOG(ERROR) << "Missing " << plat_pub_versioned_cil_file;
         return false;
     }
 
@@ -338,8 +338,8 @@
     };
     // clang-format on
 
-    if (!nonplat_declaration_cil_file.empty()) {
-        compile_args.push_back(nonplat_declaration_cil_file.c_str());
+    if (!plat_pub_versioned_cil_file.empty()) {
+        compile_args.push_back(plat_pub_versioned_cil_file.c_str());
     }
     if (!vendor_policy_cil_file.empty()) {
         compile_args.push_back(vendor_policy_cil_file.c_str());
@@ -418,7 +418,6 @@
     selinux_android_restorecon("/dev/urandom", 0);
     selinux_android_restorecon("/dev/__properties__", 0);
 
-    selinux_android_restorecon("/file_contexts.bin", 0);
     selinux_android_restorecon("/plat_file_contexts", 0);
     selinux_android_restorecon("/nonplat_file_contexts", 0);
     selinux_android_restorecon("/plat_property_contexts", 0);
diff --git a/init/service.cpp b/init/service.cpp
index 12acfc6..331b859 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -291,6 +291,11 @@
         if (!SetCapsForExec(capabilities_)) {
             LOG(FATAL) << "cannot set capabilities for " << name_;
         }
+    } else if (uid_) {
+        // Inheritable caps can be non-zero when running in a container.
+        if (!DropInheritableCaps()) {
+            LOG(FATAL) << "cannot drop inheritable caps for " << name_;
+        }
     }
 }
 
@@ -530,6 +535,11 @@
     return Success();
 }
 
+Result<Success> Service::ParseOverride(const std::vector<std::string>& args) {
+    override_ = true;
+    return Success();
+}
+
 Result<Success> Service::ParseMemcgSwappiness(const std::vector<std::string>& args) {
     if (!ParseInt(args[1], &swappiness_, 0)) {
         return Error() << "swappiness value must be equal or greater than 0";
@@ -671,6 +681,7 @@
         {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
         {"oneshot",     {0,     0,    &Service::ParseOneshot}},
         {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
+        {"override",    {0,     0,    &Service::ParseOverride}},
         {"oom_score_adjust",
                         {1,     1,    &Service::ParseOomScoreAdjust}},
         {"memcg.swappiness",
@@ -1111,11 +1122,6 @@
         return Error() << "invalid service name '" << name << "'";
     }
 
-    Service* old_service = service_list_->FindService(name);
-    if (old_service) {
-        return Error() << "ignored duplicate definition of service '" << name << "'";
-    }
-
     Subcontext* restart_action_subcontext = nullptr;
     if (subcontexts_) {
         for (auto& subcontext : *subcontexts_) {
@@ -1135,10 +1141,23 @@
     return service_ ? service_->ParseLine(std::move(args)) : Success();
 }
 
-void ServiceParser::EndSection() {
+Result<Success> ServiceParser::EndSection() {
     if (service_) {
+        Service* old_service = service_list_->FindService(service_->name());
+        if (old_service) {
+            if (!service_->is_override()) {
+                return Error() << "ignored duplicate definition of service '" << service_->name()
+                               << "'";
+            }
+
+            service_list_->RemoveService(*old_service);
+            old_service = nullptr;
+        }
+
         service_list_->AddService(std::move(service_));
     }
+
+    return Success();
 }
 
 bool ServiceParser::IsValidName(const std::string& name) const {
diff --git a/init/service.h b/init/service.h
index 593f782..d46a413 100644
--- a/init/service.h
+++ b/init/service.h
@@ -111,6 +111,7 @@
     const std::set<std::string>& interfaces() const { return interfaces_; }
     int priority() const { return priority_; }
     int oom_score_adjust() const { return oom_score_adjust_; }
+    bool is_override() const { return override_; }
     bool process_cgroup_empty() const { return process_cgroup_empty_; }
     unsigned long start_order() const { return start_order_; }
     const std::vector<std::string>& args() const { return args_; }
@@ -139,6 +140,7 @@
     Result<Success> ParseOneshot(const std::vector<std::string>& args);
     Result<Success> ParseOnrestart(const std::vector<std::string>& args);
     Result<Success> ParseOomScoreAdjust(const std::vector<std::string>& args);
+    Result<Success> ParseOverride(const std::vector<std::string>& args);
     Result<Success> ParseMemcgLimitInBytes(const std::vector<std::string>& args);
     Result<Success> ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args);
     Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
@@ -201,6 +203,8 @@
 
     bool process_cgroup_empty_ = false;
 
+    bool override_ = false;
+
     unsigned long start_order_;
 
     std::vector<std::pair<int, rlimit>> rlimits_;
@@ -248,7 +252,7 @@
     Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line) override;
     Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
-    void EndSection() override;
+    Result<Success> EndSection() override;
 
   private:
     bool IsValidName(const std::string& name) const;
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 84feeee..068be6e 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -23,7 +23,6 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <selinux/android.h>
 
@@ -32,7 +31,6 @@
 #include "system/core/init/subcontext.pb.h"
 #include "util.h"
 
-using android::base::GetBoolProperty;
 using android::base::GetExecutablePath;
 using android::base::Join;
 using android::base::Socketpair;
@@ -262,13 +260,11 @@
 static std::vector<Subcontext> subcontexts;
 
 std::vector<Subcontext>* InitializeSubcontexts() {
-    if (GetBoolProperty("ro.init.subcontexts_enabled", false)) {
-        static const char* const paths_and_secontexts[][2] = {
-            {"/vendor", kVendorContext.c_str()},
-        };
-        for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
-            subcontexts.emplace_back(path_prefix, secontext);
-        }
+    static const char* const paths_and_secontexts[][2] = {
+        {"/vendor", kVendorContext.c_str()},
+    };
+    for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
+        subcontexts.emplace_back(path_prefix, secontext);
     }
     return &subcontexts;
 }
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index cd7adb4..f74c878 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -132,8 +132,10 @@
     return std::invoke(*parser, this, std::move(args));
 }
 
-void SubsystemParser::EndSection() {
+Result<Success> SubsystemParser::EndSection() {
     subsystems_->emplace_back(std::move(subsystem_));
+
+    return Success();
 }
 
 }  // namespace init
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 18d1027..83684f3 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -32,7 +32,7 @@
     Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line) override;
     Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
-    void EndSection() override;
+    Result<Success> EndSection() override;
 
   private:
     Result<Success> ParseDevName(std::vector<std::string>&& args);
diff --git a/init/util.cpp b/init/util.cpp
index a19a6f3..d80cb1e 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -178,9 +178,26 @@
     return content;
 }
 
+static int OpenFile(const std::string& path, int flags, mode_t mode) {
+    std::string secontext;
+    if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
+        setfscreatecon(secontext.c_str());
+    }
+
+    int rc = open(path.c_str(), flags, mode);
+
+    if (!secontext.empty()) {
+        int save_errno = errno;
+        setfscreatecon(nullptr);
+        errno = save_errno;
+    }
+
+    return rc;
+}
+
 Result<Success> WriteFile(const std::string& path, const std::string& content) {
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(
-        open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
+        OpenFile(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
     if (fd == -1) {
         return ErrnoError() << "open() failed";
     }
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index 0792307..8b0c53e 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -179,7 +179,11 @@
         }
 
         const uint32_t opcode = buffer_.request.header.opcode;
-        LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
+        const uint64_t unique = buffer_.request.header.unique;
+        LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode << " unique=" << unique;
+        if (unique == 0) {
+            return FuseBridgeState::kWaitToReadEither;
+        }
         switch (opcode) {
             case FUSE_FORGET:
                 // Do not reply to FUSE_FORGET.
diff --git a/libappfuse/tests/FuseBridgeLoopTest.cc b/libappfuse/tests/FuseBridgeLoopTest.cc
index 51d6051..0a28451 100644
--- a/libappfuse/tests/FuseBridgeLoopTest.cc
+++ b/libappfuse/tests/FuseBridgeLoopTest.cc
@@ -67,6 +67,7 @@
     memset(&request_, 0, sizeof(FuseRequest));
     request_.header.opcode = opcode;
     request_.header.len = sizeof(fuse_in_header);
+    request_.header.unique = 1;
     ASSERT_TRUE(request_.Write(dev_sockets_[0]));
 
     memset(&response_, 0, sizeof(FuseResponse));
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 0e31495..0f1ae11 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -40,9 +40,10 @@
 
 void BacktraceMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
   ScopedBacktraceMapIteratorLock lock(this);
-  for (BacktraceMap::const_iterator it = begin(); it != end(); ++it) {
-    if (addr >= it->start && addr < it->end) {
-      *map = *it;
+  for (auto it = begin(); it != end(); ++it) {
+    const backtrace_map_t* entry = *it;
+    if (addr >= entry->start && addr < entry->end) {
+      *map = *entry;
       return;
     }
   }
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index 0a2f5a3..3041492 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -320,7 +320,7 @@
   }
   if (debug_frame->has_debug_frame || debug_frame->has_gnu_debugdata) {
     unw_dyn_info_t di;
-    unw_word_t segbase = map.start - map.offset;
+    unw_word_t segbase = map.start - debug_frame->min_vaddr;
     // TODO: http://b/32916571
     // TODO: Do it ourselves is more efficient than calling libunwind functions.
     int found = dwarf_find_debug_frame(0, &di, ip, segbase, filename.c_str(), map.start, map.end);
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 3a38839..d61384e 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -99,8 +99,7 @@
     // one extra function call appearing in the unwind.
     unwindstack::RegsGetLocal(regs.get());
   } else {
-    regs.reset(
-        unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentMachineType(), ucontext));
+    regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
   }
 
   error_ = BACKTRACE_UNWIND_NO_ERROR;
@@ -120,8 +119,7 @@
   if (context == nullptr) {
     regs.reset(unwindstack::Regs::RemoteGet(Tid()));
   } else {
-    regs.reset(
-        unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentMachineType(), context));
+    regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), context));
   }
 
   error_ = BACKTRACE_UNWIND_NO_ERROR;
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 25e5002..836a774 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -44,15 +44,15 @@
   }
 
   // Iterate through the maps and fill in the backtrace_map_t structure.
-  for (auto& map_info : *stack_maps_) {
+  for (auto* map_info : *stack_maps_) {
     backtrace_map_t map;
-    map.start = map_info.start;
-    map.end = map_info.end;
-    map.offset = map_info.offset;
+    map.start = map_info->start;
+    map.end = map_info->end;
+    map.offset = map_info->offset;
     // Set to -1 so that it is demand loaded.
     map.load_bias = static_cast<uintptr_t>(-1);
-    map.flags = map_info.flags;
-    map.name = map_info.name;
+    map.flags = map_info->flags;
+    map.name = map_info->name;
 
     maps_.push_back(map);
   }
@@ -71,8 +71,19 @@
   if (map_info == nullptr) {
     return;
   }
-  unwindstack::Elf* elf = map_info->GetElf(process_memory_, true);
-  map->load_bias = elf->GetLoadBias();
+  map->load_bias = map_info->GetLoadBias(process_memory_);
+}
+
+uint64_t UnwindStackMap::GetLoadBias(size_t index) {
+  if (index >= stack_maps_->Total()) {
+    return 0;
+  }
+
+  unwindstack::MapInfo* map_info = stack_maps_->Get(index);
+  if (map_info == nullptr) {
+    return 0;
+  }
+  return map_info->GetLoadBias(process_memory_);
 }
 
 std::string UnwindStackMap::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index bc432e7..2f63655 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -42,6 +42,8 @@
   const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
 
  protected:
+  uint64_t GetLoadBias(size_t index) override;
+
   std::unique_ptr<unwindstack::Maps> stack_maps_;
   std::shared_ptr<unwindstack::Memory> process_memory_;
 };
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 0a1f33d..d1b44a1 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -27,6 +27,7 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -171,10 +172,12 @@
   testdata += android::base::StringPrintf("pid: %d tid: %d\n", getpid(), arg.tid);
   // 2. Dump maps
   for (auto it = map->begin(); it != map->end(); ++it) {
-    testdata += android::base::StringPrintf(
-        "map: start: %" PRIxPTR " end: %" PRIxPTR " offset: %" PRIxPTR " load_bias: %" PRIxPTR
-        " flags: %d name: %s\n",
-        it->start, it->end, it->offset, it->load_bias, it->flags, it->name.c_str());
+    const backtrace_map_t* entry = *it;
+    testdata +=
+        android::base::StringPrintf("map: start: %" PRIxPTR " end: %" PRIxPTR " offset: %" PRIxPTR
+                                    " load_bias: %" PRIxPTR " flags: %d name: %s\n",
+                                    entry->start, entry->end, entry->offset, entry->load_bias,
+                                    entry->flags, entry->name.c_str());
   }
   // 3. Dump registers
   testdata += android::base::StringPrintf("registers: %zu ", sizeof(arg.unw_context));
@@ -249,12 +252,21 @@
         return false;
       }
       HexStringToRawData(&line[pos], &testdata->unw_context, size);
+#if defined(__arm__)
+    } else if (android::base::StartsWith(line, "regs:")) {
+      uint64_t pc;
+      uint64_t sp;
+      sscanf(line.c_str(), "regs: pc: %" SCNx64 " sp: %" SCNx64, &pc, &sp);
+      testdata->unw_context.regs[13] = sp;
+      testdata->unw_context.regs[15] = pc;
+#endif
     } else if (android::base::StartsWith(line, "stack:")) {
       size_t size;
       int pos;
       sscanf(line.c_str(),
              "stack: start: %" SCNx64 " end: %" SCNx64 " size: %zu %n",
              &testdata->stack_info.start, &testdata->stack_info.end, &size, &pos);
+      CHECK_EQ(testdata->stack_info.end - testdata->stack_info.start, size);
       testdata->stack.resize(size);
       HexStringToRawData(&line[pos], &testdata->stack[0], size);
       testdata->stack_info.data = testdata->stack.data();
@@ -389,3 +401,44 @@
     ASSERT_EQ(name, testdata.symbols[i].name);
   }
 }
+
+TEST(libbacktrace, offline_debug_frame_with_load_bias) {
+  if (std::string(ABI_STRING) != "arm") {
+    GTEST_LOG_(INFO) << "Skipping test since offline for arm on " << ABI_STRING
+                     << " isn't supported.";
+    return;
+  }
+  const std::string testlib_path(GetTestPath("libandroid_runtime.so"));
+  struct stat st;
+  ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;
+
+  const std::string offline_testdata_path(GetTestPath("offline_testdata_for_libandroid_runtime"));
+  OfflineTestData testdata;
+  ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
+
+  // Fix path of /system/lib/libandroid_runtime.so.
+  for (auto& map : testdata.maps) {
+    if (map.name.find("libandroid_runtime.so") != std::string::npos) {
+      map.name = testlib_path;
+    }
+  }
+
+  // Do offline backtrace.
+  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(testdata.pid, testdata.maps));
+  ASSERT_TRUE(map != nullptr);
+
+  std::unique_ptr<Backtrace> backtrace(
+      Backtrace::CreateOffline(testdata.pid, testdata.tid, map.get(), testdata.stack_info));
+  ASSERT_TRUE(backtrace != nullptr);
+
+  ucontext_t ucontext = GetUContextFromUnwContext(testdata.unw_context);
+  ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
+
+  ASSERT_EQ(testdata.symbols.size(), backtrace->NumFrames());
+  for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
+    uintptr_t vaddr_in_file =
+        backtrace->GetFrame(i)->pc - testdata.maps[0].start + testdata.maps[0].load_bias;
+    std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols);
+    ASSERT_EQ(name, testdata.symbols[i].name);
+  }
+}
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 0a60ec4..890ab3f 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -77,6 +77,7 @@
 
 struct dump_thread_t {
   thread_t thread;
+  BacktraceMap* map;
   Backtrace* backtrace;
   int32_t* now;
   int32_t done;
@@ -632,7 +633,7 @@
   }
 
   // The status of the actual unwind will be checked elsewhere.
-  dump->backtrace = Backtrace::Create(getpid(), dump->thread.tid);
+  dump->backtrace = Backtrace::Create(getpid(), dump->thread.tid, dump->map);
   dump->backtrace->Unwind(0);
 
   android_atomic_acquire_store(1, &dump->done);
@@ -640,8 +641,8 @@
   return nullptr;
 }
 
-TEST(libbacktrace, thread_multiple_dump) {
-  // Dump NUM_THREADS simultaneously.
+static void MultipleThreadDumpTest(bool share_map) {
+  // Dump NUM_THREADS simultaneously using the same map.
   std::vector<thread_t> runners(NUM_THREADS);
   std::vector<dump_thread_t> dumpers(NUM_THREADS);
 
@@ -662,12 +663,17 @@
 
   // Start all of the dumpers at once, they will spin until they are signalled
   // to begin their dump run.
+  std::unique_ptr<BacktraceMap> map;
+  if (share_map) {
+    map.reset(BacktraceMap::Create(getpid()));
+  }
   int32_t dump_now = 0;
   for (size_t i = 0; i < NUM_THREADS; i++) {
     dumpers[i].thread.tid = runners[i].tid;
     dumpers[i].thread.state = 0;
     dumpers[i].done = 0;
     dumpers[i].now = &dump_now;
+    dumpers[i].map = map.get();
 
     ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
   }
@@ -689,47 +695,12 @@
   }
 }
 
-TEST(libbacktrace, thread_multiple_dump_same_thread) {
-  pthread_attr_t attr;
-  pthread_attr_init(&attr);
-  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-  thread_t runner;
-  runner.tid = 0;
-  runner.state = 0;
-  ASSERT_TRUE(pthread_create(&runner.threadId, &attr, ThreadMaxRun, &runner) == 0);
+TEST(libbacktrace, thread_multiple_dump) {
+  MultipleThreadDumpTest(false);
+}
 
-  // Wait for tids to be set.
-  ASSERT_TRUE(WaitForNonZero(&runner.state, 30));
-
-  // Start all of the dumpers at once, they will spin until they are signalled
-  // to begin their dump run.
-  int32_t dump_now = 0;
-  // Dump the same thread NUM_THREADS simultaneously.
-  std::vector<dump_thread_t> dumpers(NUM_THREADS);
-  for (size_t i = 0; i < NUM_THREADS; i++) {
-    dumpers[i].thread.tid = runner.tid;
-    dumpers[i].thread.state = 0;
-    dumpers[i].done = 0;
-    dumpers[i].now = &dump_now;
-
-    ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
-  }
-
-  // Start all of the dumpers going at once.
-  android_atomic_acquire_store(1, &dump_now);
-
-  for (size_t i = 0; i < NUM_THREADS; i++) {
-    ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 30));
-
-    ASSERT_TRUE(dumpers[i].backtrace != nullptr);
-    VerifyMaxDump(dumpers[i].backtrace);
-
-    delete dumpers[i].backtrace;
-    dumpers[i].backtrace = nullptr;
-  }
-
-  // Tell the runner thread to exit its infinite loop.
-  android_atomic_acquire_store(0, &runner.state);
+TEST(libbacktrace, thread_multiple_dump_same_map) {
+  MultipleThreadDumpTest(true);
 }
 
 // This test is for UnwindMaps that should share the same map cursor when
@@ -886,6 +857,34 @@
 
 static bool map_sort(map_test_t i, map_test_t j) { return i.start < j.start; }
 
+static std::string GetTestMapsAsString(const std::vector<map_test_t>& maps) {
+  if (maps.size() == 0) {
+    return "No test map entries\n";
+  }
+  std::string map_txt;
+  for (auto map : maps) {
+    map_txt += android::base::StringPrintf("%" PRIxPTR "-%" PRIxPTR "\n", map.start, map.end);
+  }
+  return map_txt;
+}
+
+static std::string GetMapsAsString(BacktraceMap* maps) {
+  if (maps->size() == 0) {
+    return "No map entries\n";
+  }
+  std::string map_txt;
+  for (const backtrace_map_t* map : *maps) {
+    map_txt += android::base::StringPrintf(
+        "%" PRIxPTR "-%" PRIxPTR " flags: 0x%x offset: 0x%" PRIxPTR " load_bias: 0x%" PRIxPTR,
+        map->start, map->end, map->flags, map->offset, map->load_bias);
+    if (!map->name.empty()) {
+      map_txt += ' ' + map->name;
+    }
+    map_txt += '\n';
+  }
+  return map_txt;
+}
+
 static void VerifyMap(pid_t pid) {
   char buffer[4096];
   snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid);
@@ -904,12 +903,20 @@
   std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
 
   // Basic test that verifies that the map is in the expected order.
-  ScopedBacktraceMapIteratorLock lock(map.get());
-  std::vector<map_test_t>::const_iterator test_it = test_maps.begin();
-  for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
-    ASSERT_TRUE(test_it != test_maps.end());
-    ASSERT_EQ(test_it->start, it->start);
-    ASSERT_EQ(test_it->end, it->end);
+  auto test_it = test_maps.begin();
+  for (auto it = map->begin(); it != map->end(); ++it) {
+    ASSERT_TRUE(test_it != test_maps.end()) << "Mismatch in number of maps, expected test maps:\n"
+                                            << GetTestMapsAsString(test_maps) << "Actual maps:\n"
+                                            << GetMapsAsString(map.get());
+    ASSERT_EQ(test_it->start, (*it)->start) << "Mismatch in map data, expected test maps:\n"
+                                            << GetTestMapsAsString(test_maps) << "Actual maps:\n"
+                                            << GetMapsAsString(map.get());
+    ASSERT_EQ(test_it->end, (*it)->end) << "Mismatch maps in map data, expected test maps:\n"
+                                        << GetTestMapsAsString(test_maps) << "Actual maps:\n"
+                                        << GetMapsAsString(map.get());
+    // Make sure the load bias get set to a value.
+    ASSERT_NE(static_cast<uint64_t>(-1), (*it)->load_bias) << "Found uninitialized load_bias\n"
+                                                           << GetMapsAsString(map.get());
     ++test_it;
   }
   ASSERT_TRUE(test_it == test_maps.end());
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index d078392..4ae68dd 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -30,6 +30,7 @@
 #endif
 
 #include <deque>
+#include <iterator>
 #include <string>
 #include <vector>
 
@@ -61,6 +62,49 @@
 
   virtual ~BacktraceMap();
 
+  class iterator : public std::iterator<std::bidirectional_iterator_tag, backtrace_map_t*> {
+   public:
+    iterator(BacktraceMap* map, size_t index) : map_(map), index_(index) {}
+
+    iterator& operator++() {
+      index_++;
+      return *this;
+    }
+    iterator& operator++(int increment) {
+      index_ += increment;
+      return *this;
+    }
+    iterator& operator--() {
+      index_--;
+      return *this;
+    }
+    iterator& operator--(int decrement) {
+      index_ -= decrement;
+      return *this;
+    }
+
+    bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
+    bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
+
+    const backtrace_map_t* operator*() {
+      if (index_ >= map_->size()) {
+        return nullptr;
+      }
+      backtrace_map_t* map = &map_->maps_[index_];
+      if (map->load_bias == static_cast<uintptr_t>(-1)) {
+        map->load_bias = map_->GetLoadBias(index_);
+      }
+      return map;
+    }
+
+   private:
+    BacktraceMap* map_ = nullptr;
+    size_t index_ = 0;
+  };
+
+  iterator begin() { return iterator(this, 0); }
+  iterator end() { return iterator(this, maps_.size()); }
+
   // Fill in the map data structure for the given address.
   virtual void FillIn(uintptr_t addr, backtrace_map_t* map);
 
@@ -89,14 +133,6 @@
   virtual void LockIterator() {}
   virtual void UnlockIterator() {}
 
-  typedef std::deque<backtrace_map_t>::iterator iterator;
-  iterator begin() { return maps_.begin(); }
-  iterator end() { return maps_.end(); }
-
-  typedef std::deque<backtrace_map_t>::const_iterator const_iterator;
-  const_iterator begin() const { return maps_.begin(); }
-  const_iterator end() const { return maps_.end(); }
-
   size_t size() const { return maps_.size(); }
 
   virtual bool Build();
@@ -114,6 +150,8 @@
  protected:
   BacktraceMap(pid_t pid);
 
+  virtual uint64_t GetLoadBias(size_t /* index */) { return 0; }
+
   virtual bool ParseLine(const char* line, backtrace_map_t* map);
 
   pid_t pid_;
diff --git a/libbacktrace/testdata/arm/libandroid_runtime.so b/libbacktrace/testdata/arm/libandroid_runtime.so
new file mode 100644
index 0000000..e4283e6
--- /dev/null
+++ b/libbacktrace/testdata/arm/libandroid_runtime.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libandroid_runtime b/libbacktrace/testdata/arm/offline_testdata_for_libandroid_runtime
new file mode 100644
index 0000000..a12bc3c
--- /dev/null
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libandroid_runtime
@@ -0,0 +1,6 @@
+pid: 7288 tid: 31656
+regs: pc: f1f6dc49 sp: d8fe6930
+map: start: f1f10000 end: f2049000 offset: 0 load_bias: 10000 flags: 5 name: /system/lib/libandroid_runtime.so
+stack: start: d8fe6954 end: d8fe6958 size: 4 e7dcf6f1
+function: start: 6dbf9 end: 6dce5 name: android::AndroidRuntime::javaThreadShell
+function: start: 6dce5 end: 6dd79 name: android::AndroidRuntime::javaCreateThreadEtc
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index cfe8d29..9cba109 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -19,14 +19,14 @@
 // which are also hard or even impossible to port to native Win32
 libcutils_nonwindows_sources = [
     "android_get_control_file.cpp",
-    "fs.c",
-    "multiuser.c",
-    "socket_inaddr_any_server_unix.c",
-    "socket_local_client_unix.c",
-    "socket_local_server_unix.c",
-    "socket_network_client_unix.c",
+    "fs.cpp",
+    "multiuser.cpp",
+    "socket_inaddr_any_server_unix.cpp",
+    "socket_local_client_unix.cpp",
+    "socket_local_server_unix.cpp",
+    "socket_network_client_unix.cpp",
     "sockets_unix.cpp",
-    "str_parms.c",
+    "str_parms.cpp",
 ]
 
 cc_library_headers {
@@ -56,41 +56,37 @@
     },
     host_supported: true,
     srcs: [
-        "config_utils.c",
+        "config_utils.cpp",
         "fs_config.cpp",
-        "canned_fs_config.c",
-        "hashmap.c",
-        "iosched_policy.c",
-        "load_file.c",
-        "native_handle.c",
+        "canned_fs_config.cpp",
+        "hashmap.cpp",
+        "iosched_policy.cpp",
+        "load_file.cpp",
+        "native_handle.cpp",
         "open_memstream.c",
-        "record_stream.c",
+        "record_stream.cpp",
         "sched_policy.cpp",
         "sockets.cpp",
-        "strdup16to8.c",
-        "strdup8to16.c",
+        "strdup16to8.cpp",
+        "strdup8to16.cpp",
         "strlcpy.c",
-        "threads.c",
+        "threads.cpp",
     ],
 
     target: {
-        host: {
-            srcs: ["dlmalloc_stubs.c"],
-        },
         linux_bionic: {
             enabled: true,
-            exclude_srcs: ["dlmalloc_stubs.c"],
         },
         not_windows: {
             srcs: libcutils_nonwindows_sources + [
-                "ashmem-host.c",
-                "trace-host.c",
+                "ashmem-host.cpp",
+                "trace-host.cpp",
             ],
         },
         windows: {
             srcs: [
-                "socket_inaddr_any_server_windows.c",
-                "socket_network_client_windows.c",
+                "socket_inaddr_any_server_windows.cpp",
+                "socket_network_client_windows.cpp",
                 "sockets_windows.cpp",
             ],
 
@@ -105,32 +101,41 @@
 
         android: {
             srcs: libcutils_nonwindows_sources + [
-                "android_reboot.c",
-                "ashmem-dev.c",
+                "android_reboot.cpp",
+                "ashmem-dev.cpp",
                 "klog.cpp",
-                "partition_utils.c",
+                "partition_utils.cpp",
                 "properties.cpp",
-                "qtaguid.c",
-                "trace-dev.c",
-                "uevent.c",
+                "qtaguid.cpp",
+                "trace-dev.cpp",
+                "uevent.cpp",
             ],
+        },
+
+        android_arm: {
+            srcs: ["arch-arm/memset32.S"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
+        },
+        android_arm64: {
+            srcs: ["arch-arm64/android_memset.S"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
         },
 
-        android_arm: {
-            srcs: ["arch-arm/memset32.S"],
-        },
-        android_arm64: {
-            srcs: ["arch-arm64/android_memset.S"],
-        },
-
         android_mips: {
             srcs: ["arch-mips/android_memset.c"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
         android_mips64: {
             srcs: ["arch-mips/android_memset.c"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
 
         android_x86: {
@@ -138,6 +143,12 @@
                 "arch-x86/android_memset16.S",
                 "arch-x86/android_memset32.S",
             ],
+            // TODO: This is to work around b/29412086.
+            // Remove once __mulodi4 is available and move the "sanitize" block
+            // to the android target.
+            sanitize: {
+                misc_undefined: [],
+            },
         },
 
         android_x86_64: {
@@ -145,6 +156,9 @@
                 "arch-x86_64/android_memset16.S",
                 "arch-x86_64/android_memset32.S",
             ],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
     },
 
diff --git a/libcutils/android_get_control_file.cpp b/libcutils/android_get_control_file.cpp
index 780d9f1..d8121f5 100644
--- a/libcutils/android_get_control_file.cpp
+++ b/libcutils/android_get_control_file.cpp
@@ -25,6 +25,9 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
+#include <cutils/android_get_control_file.h>
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -36,8 +39,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <cutils/android_get_control_file.h>
-
 #include "android_get_control_env.h"
 
 #ifndef TEMP_FAILURE_RETRY
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.cpp
similarity index 92%
rename from libcutils/android_reboot.c
rename to libcutils/android_reboot.cpp
index 996d89d..ce41cd3 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.cpp
@@ -13,20 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include <cutils/android_reboot.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <cutils/android_reboot.h>
 #include <cutils/properties.h>
 
 #define TAG "android_reboot"
 
-int android_reboot(int cmd, int flags __unused, const char* arg) {
+int android_reboot(int cmd, int /*flags*/, const char* arg) {
     int ret;
     const char* restart_cmd = NULL;
     char* prop_value;
 
-    switch (cmd) {
+    switch (static_cast<unsigned>(cmd)) {
         case ANDROID_RB_RESTART:  // deprecated
         case ANDROID_RB_RESTART2:
             restart_cmd = "reboot";
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.cpp
similarity index 94%
rename from libcutils/ashmem-dev.c
rename to libcutils/ashmem-dev.cpp
index 95f2259..15ace0e 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/ashmem.h>
+
 /*
  * Implementation of the user-space ashmem API for devices, which have our
  * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
@@ -31,8 +33,6 @@
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <unistd.h>
-
-#include <cutils/ashmem.h>
 #include <log/log.h>
 
 #define ASHMEM_DEVICE "/dev/ashmem"
@@ -192,7 +192,8 @@
 
 int ashmem_pin_region(int fd, size_t offset, size_t len)
 {
-    struct ashmem_pin pin = { offset, len };
+    // TODO: should LP64 reject too-large offset/len?
+    ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
 
     int ret = __ashmem_is_ashmem(fd, 1);
     if (ret < 0) {
@@ -204,7 +205,8 @@
 
 int ashmem_unpin_region(int fd, size_t offset, size_t len)
 {
-    struct ashmem_pin pin = { offset, len };
+    // TODO: should LP64 reject too-large offset/len?
+    ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
 
     int ret = __ashmem_is_ashmem(fd, 1);
     if (ret < 0) {
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.cpp
similarity index 75%
rename from libcutils/ashmem-host.c
rename to libcutils/ashmem-host.cpp
index 1f9f753..b2bec99 100644
--- a/libcutils/ashmem-host.c
+++ b/libcutils/ashmem-host.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/ashmem.h>
+
 /*
  * Implementation of the user-space ashmem API for the simulator, which lacks
  * an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version.
@@ -31,21 +33,15 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <cutils/ashmem.h>
 #include <utils/Compat.h>
 
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
-int ashmem_create_region(const char *ignored __unused, size_t size)
-{
-    char template[PATH_MAX];
-    snprintf(template, sizeof(template), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
-    int fd = mkstemp(template);
+int ashmem_create_region(const char* /*ignored*/, size_t size) {
+    char pattern[PATH_MAX];
+    snprintf(pattern, sizeof(pattern), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
+    int fd = mkstemp(pattern);
     if (fd == -1) return -1;
 
-    unlink(template);
+    unlink(pattern);
 
     if (TEMP_FAILURE_RETRY(ftruncate(fd, size)) == -1) {
       close(fd);
@@ -55,18 +51,15 @@
     return fd;
 }
 
-int ashmem_set_prot_region(int fd __unused, int prot __unused)
-{
+int ashmem_set_prot_region(int /*fd*/, int /*prot*/) {
     return 0;
 }
 
-int ashmem_pin_region(int fd __unused, size_t offset __unused, size_t len __unused)
-{
+int ashmem_pin_region(int /*fd*/, size_t /*offset*/, size_t /*len*/) {
     return 0 /*ASHMEM_NOT_PURGED*/;
 }
 
-int ashmem_unpin_region(int fd __unused, size_t offset __unused, size_t len __unused)
-{
+int ashmem_unpin_region(int /*fd*/, size_t /*offset*/, size_t /*len*/) {
     return 0 /*ASHMEM_IS_UNPINNED*/;
 }
 
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.cpp
similarity index 99%
rename from libcutils/canned_fs_config.c
rename to libcutils/canned_fs_config.cpp
index 819a846..6b5763b 100644
--- a/libcutils/canned_fs_config.c
+++ b/libcutils/canned_fs_config.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#include <private/android_filesystem_config.h>
+#include <private/canned_fs_config.h>
+#include <private/fs_config.h>
+
 #include <errno.h>
 #include <inttypes.h>
 #include <limits.h>
@@ -22,10 +26,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <private/android_filesystem_config.h>
-#include <private/fs_config.h>
-#include <private/canned_fs_config.h>
-
 typedef struct {
     const char* path;
     unsigned uid;
diff --git a/libcutils/config_utils.c b/libcutils/config_utils.cpp
similarity index 97%
rename from libcutils/config_utils.c
rename to libcutils/config_utils.cpp
index fc5ca78..a3af01a 100644
--- a/libcutils/config_utils.c
+++ b/libcutils/config_utils.cpp
@@ -14,20 +14,19 @@
  * limitations under the License.
  */
 
+#include <cutils/config_utils.h>
+
 #include <string.h>
 #include <ctype.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include <unistd.h>
 
-#include <cutils/config_utils.h>
 #include <cutils/misc.h>
 
 cnode* config_node(const char *name, const char *value)
 {
-    cnode *node;
-
-    node = calloc(sizeof(cnode), 1);
+    cnode* node = static_cast<cnode*>(calloc(sizeof(cnode), 1));
     if(node) {
         node->name = name ? name : "";
         node->value = value ? value : "";
@@ -311,9 +310,9 @@
 
 void config_load_file(cnode *root, const char *fn)
 {
-    char *data;
-    data = load_file(fn, 0);
+    char* data = static_cast<char*>(load_file(fn, nullptr));
     config_load(root, data);
+    // TODO: deliberate leak :-/
 }
 
 void config_free(cnode *root)
diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c
deleted file mode 100644
index 2cff9dd..0000000
--- a/libcutils/dlmalloc_stubs.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "dlmalloc-stubs"
-
-#include "log/log.h"
-
-#define UNUSED __attribute__((__unused__))
-
-/*
- * Stubs for functions defined in bionic/libc/bionic/dlmalloc.c. These
- * are used in host builds, as the host libc will not contain these
- * functions.
- */
-void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*) UNUSED,
-                          void* arg UNUSED)
-{
-  ALOGW("Called host unimplemented stub: dlmalloc_inspect_all");
-}
-
-int dlmalloc_trim(size_t unused UNUSED)
-{
-  ALOGW("Called host unimplemented stub: dlmalloc_trim");
-  return 0;
-}
diff --git a/libcutils/fs.c b/libcutils/fs.cpp
similarity index 93%
rename from libcutils/fs.c
rename to libcutils/fs.cpp
index b253b1c..ef85acc 100644
--- a/libcutils/fs.c
+++ b/libcutils/fs.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/fs.h>
+
 #define LOG_TAG "cutils"
 
 /* These defines are only needed because prebuilt headers are out of date */
@@ -32,7 +34,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <cutils/fs.h>
 #include <log/log.h>
 
 #define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
@@ -40,6 +41,11 @@
 
 static int fs_prepare_path_impl(const char* path, mode_t mode, uid_t uid, gid_t gid,
         int allow_fixup, int prepare_as_dir) {
+    // TODO: fix the goto hell below.
+    int type_ok;
+    int owner_match;
+    int mode_match;
+
     // Check if path needs to be created
     struct stat sb;
     int create_result = -1;
@@ -53,14 +59,14 @@
     }
 
     // Exists, verify status
-    int type_ok = prepare_as_dir ? S_ISDIR(sb.st_mode) : S_ISREG(sb.st_mode);
+    type_ok = prepare_as_dir ? S_ISDIR(sb.st_mode) : S_ISREG(sb.st_mode);
     if (!type_ok) {
         ALOGE("Not a %s: %s", (prepare_as_dir ? "directory" : "regular file"), path);
         return -1;
     }
 
-    int owner_match = ((sb.st_uid == uid) && (sb.st_gid == gid));
-    int mode_match = ((sb.st_mode & ALL_PERMS) == mode);
+    owner_match = ((sb.st_uid == uid) && (sb.st_gid == gid));
+    mode_match = ((sb.st_mode & ALL_PERMS) == mode);
     if (owner_match && mode_match) {
         return 0;
     } else if (allow_fixup) {
@@ -188,23 +194,20 @@
 #ifndef __APPLE__
 
 int fs_mkdirs(const char* path, mode_t mode) {
-    int res = 0;
-    int fd = 0;
-    struct stat sb;
-    char* buf = strdup(path);
-
-    if (*buf != '/') {
-        ALOGE("Relative paths are not allowed: %s", buf);
-        res = -EINVAL;
-        goto done;
+    if (*path != '/') {
+        ALOGE("Relative paths are not allowed: %s", path);
+        return -EINVAL;
     }
 
-    if ((fd = open("/", 0)) == -1) {
+    int fd = open("/", 0);
+    if (fd == -1) {
         ALOGE("Failed to open(/): %s", strerror(errno));
-        res = -errno;
-        goto done;
+        return -errno;
     }
 
+    struct stat sb;
+    int res = 0;
+    char* buf = strdup(path);
     char* segment = buf + 1;
     char* p = segment;
     while (*p != '\0') {
@@ -266,7 +269,6 @@
 
 done_close:
     close(fd);
-done:
     free(buf);
     return res;
 }
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 7603ffc..f45472e 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <private/fs_config.h>
+
 // This file is used to define the properties of the filesystem
 // images generated by build tools (mkbootfs and mkyaffs2image) and
 // by the device side of adb.
@@ -31,7 +33,6 @@
 
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
-#include <private/fs_config.h>
 #include <utils/Compat.h>
 
 #ifndef O_BINARY
diff --git a/libcutils/hashmap.c b/libcutils/hashmap.cpp
similarity index 96%
rename from libcutils/hashmap.c
rename to libcutils/hashmap.cpp
index ede3b98..65b6ab1 100644
--- a/libcutils/hashmap.c
+++ b/libcutils/hashmap.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <cutils/hashmap.h>
+
 #include <assert.h>
 #include <errno.h>
 #include <cutils/threads.h>
@@ -45,7 +46,7 @@
     assert(hash != NULL);
     assert(equals != NULL);
     
-    Hashmap* map = malloc(sizeof(Hashmap));
+    Hashmap* map = static_cast<Hashmap*>(malloc(sizeof(Hashmap)));
     if (map == NULL) {
         return NULL;
     }
@@ -58,7 +59,7 @@
         map->bucketCount <<= 1; 
     }
 
-    map->buckets = calloc(map->bucketCount, sizeof(Entry*));
+    map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));
     if (map->buckets == NULL) {
         free(map);
         return NULL;
@@ -106,7 +107,7 @@
     if (map->size > (map->bucketCount * 3 / 4)) {
         // Start off with a 0.33 load factor.
         size_t newBucketCount = map->bucketCount << 1;
-        Entry** newBuckets = calloc(newBucketCount, sizeof(Entry*));
+        Entry** newBuckets = static_cast<Entry**>(calloc(newBucketCount, sizeof(Entry*)));
         if (newBuckets == NULL) {
             // Abort expansion.
             return;
@@ -171,7 +172,7 @@
 }
 
 static Entry* createEntry(void* key, int hash, void* value) {
-    Entry* entry = malloc(sizeof(Entry));
+    Entry* entry = static_cast<Entry*>(malloc(sizeof(Entry)));
     if (entry == NULL) {
         return NULL;
     }
diff --git a/libcutils/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index a903adb..99030ed 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -17,6 +17,7 @@
 #ifndef __CUTILS_ANDROID_REBOOT_H__
 #define __CUTILS_ANDROID_REBOOT_H__
 
+#include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
diff --git a/libcutils/include/cutils/partition_utils.h b/libcutils/include/cutils/partition_utils.h
index 72ca80d..7518559 100644
--- a/libcutils/include/cutils/partition_utils.h
+++ b/libcutils/include/cutils/partition_utils.h
@@ -17,6 +17,8 @@
 #ifndef __CUTILS_PARTITION_WIPED_H__
 #define __CUTILS_PARTITION_WIPED_H__
 
+#include <sys/cdefs.h>
+
 __BEGIN_DECLS
 
 int partition_wiped(char *source);
diff --git a/libcutils/include/cutils/qtaguid.h b/libcutils/include/cutils/qtaguid.h
index 803fe0d..3f5e41f 100644
--- a/libcutils/include/cutils/qtaguid.h
+++ b/libcutils/include/cutils/qtaguid.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,18 +17,14 @@
 #ifndef __CUTILS_QTAGUID_H
 #define __CUTILS_QTAGUID_H
 
-#include <stdint.h>
 #include <sys/types.h>
-#include <unistd.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /*
- * Set tags (and owning UIDs) for network sockets. The socket must be untagged
- * by calling qtaguid_untagSocket() before closing it, otherwise the qtaguid
- * module will keep a reference to it even after close.
+ * Set tags (and owning UIDs) for network sockets.
  */
 extern int qtaguid_tagSocket(int sockfd, int tag, uid_t uid);
 
@@ -46,8 +42,8 @@
 
 /*
  * Delete all tag info that relates to the given tag an uid.
- * If the tag is 0, then ALL info about the uid is freeded.
- * The delete data also affects active tagged socketd, which are
+ * If the tag is 0, then ALL info about the uid is freed.
+ * The delete data also affects active tagged sockets, which are
  * then untagged.
  * The calling process can only operate on its own tags.
  * Unless it is part of the happy AID_NET_BW_ACCT group.
diff --git a/libcutils/include/cutils/record_stream.h b/libcutils/include/cutils/record_stream.h
index bfac87a..bcfc80d 100644
--- a/libcutils/include/cutils/record_stream.h
+++ b/libcutils/include/cutils/record_stream.h
@@ -25,6 +25,7 @@
 extern "C" {
 #endif
 
+#include <stddef.h>
 
 typedef struct RecordStream RecordStream;
 
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 55ece54..2ecf5bc 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -170,6 +170,14 @@
 #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
 #define AID_SHARED_GID_END 59999   /* end of gids for apps in each user to share */
 
+/*
+ * This is a magic number in the kernel and not something that was picked
+ * arbitrarily. This value is returned whenever a uid that has no mapping in the
+ * user namespace is returned to userspace:
+ * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/highuid.h?h=v4.4#n40
+ */
+#define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */
+
 #define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
 #define AID_ISOLATED_END 99999   /* end of uids for fully isolated sandboxed processes */
 
diff --git a/libcutils/include/private/canned_fs_config.h b/libcutils/include/private/canned_fs_config.h
index 71e1537..135b91c 100644
--- a/libcutils/include/private/canned_fs_config.h
+++ b/libcutils/include/private/canned_fs_config.h
@@ -19,8 +19,12 @@
 
 #include <inttypes.h>
 
+__BEGIN_DECLS
+
 int load_canned_fs_config(const char* fn);
 void canned_fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid,
                       unsigned* gid, unsigned* mode, uint64_t* capabilities);
 
+__END_DECLS
+
 #endif
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index aab5042..8926491 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -24,6 +24,7 @@
 
 #include <stdint.h>
 #include <sys/cdefs.h>
+#include <sys/types.h>
 
 #if defined(__BIONIC__)
 #include <linux/capability.h>
diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.cpp
similarity index 95%
rename from libcutils/iosched_policy.c
rename to libcutils/iosched_policy.cpp
index 13c2ceb..012c537 100644
--- a/libcutils/iosched_policy.c
+++ b/libcutils/iosched_policy.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#include <cutils/iosched_policy.h>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
@@ -21,8 +23,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <cutils/iosched_policy.h>
-
 #if defined(__ANDROID__)
 #define IOPRIO_WHO_PROCESS (1)
 #define IOPRIO_CLASS_SHIFT (13)
@@ -49,7 +49,7 @@
         return -1;
     }
 
-    *clazz = (rc >> IOPRIO_CLASS_SHIFT);
+    *clazz = static_cast<IoSchedClass>(rc >> IOPRIO_CLASS_SHIFT);
     *ioprio = (rc & 0xff);
 #else
     *clazz = IoSchedClass_NONE;
diff --git a/libcutils/klog.cpp b/libcutils/klog.cpp
index d301276..6a9f4df 100644
--- a/libcutils/klog.cpp
+++ b/libcutils/klog.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/klog.h>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
@@ -25,7 +27,6 @@
 #include <unistd.h>
 
 #include <cutils/android_get_control_file.h>
-#include <cutils/klog.h>
 
 static int klog_level = KLOG_INFO_LEVEL;
 
diff --git a/libcutils/load_file.c b/libcutils/load_file.cpp
similarity index 97%
rename from libcutils/load_file.c
rename to libcutils/load_file.cpp
index 99f2965..346105c 100644
--- a/libcutils/load_file.c
+++ b/libcutils/load_file.cpp
@@ -15,6 +15,8 @@
 ** limitations under the License.
 */
 
+#include <cutils/misc.h>
+
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.cpp
similarity index 90%
rename from libcutils/multiuser.c
rename to libcutils/multiuser.cpp
index 61403f4..0fd3d0c 100644
--- a/libcutils/multiuser.c
+++ b/libcutils/multiuser.cpp
@@ -53,9 +53,11 @@
     }
 }
 
-gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id) {
+gid_t multiuser_get_shared_gid(userid_t, appid_t app_id) {
     if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
-        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_SHARED_GID_START);
+        return (app_id - AID_APP_START) + AID_SHARED_GID_START;
+    } else if (app_id >= AID_ROOT && app_id <= AID_APP_START) {
+        return app_id;
     } else {
         return -1;
     }
diff --git a/libcutils/native_handle.c b/libcutils/native_handle.cpp
similarity index 96%
rename from libcutils/native_handle.c
rename to libcutils/native_handle.cpp
index 95bbc41..66f7a3d 100644
--- a/libcutils/native_handle.c
+++ b/libcutils/native_handle.cpp
@@ -45,7 +45,7 @@
     }
 
     size_t mallocSize = sizeof(native_handle_t) + (sizeof(int) * (numFds + numInts));
-    native_handle_t* h = malloc(mallocSize);
+    native_handle_t* h = static_cast<native_handle_t*>(malloc(mallocSize));
     if (h) {
         h->version = sizeof(native_handle_t);
         h->numFds = numFds;
diff --git a/libcutils/partition_utils.c b/libcutils/partition_utils.cpp
similarity index 97%
rename from libcutils/partition_utils.c
rename to libcutils/partition_utils.cpp
index 823b162..6735d6c 100644
--- a/libcutils/partition_utils.c
+++ b/libcutils/partition_utils.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/partition_utils.h>
+
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h> /* for BLKGETSIZE */
diff --git a/libcutils/properties.cpp b/libcutils/properties.cpp
index 25ff1a3..5dbbeba 100644
--- a/libcutils/properties.cpp
+++ b/libcutils/properties.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/properties.h>
+
 #define LOG_TAG "properties"
 // #define LOG_NDEBUG 0
 
@@ -25,7 +27,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <cutils/properties.h>
 #include <cutils/sockets.h>
 #include <log/log.h>
 
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
new file mode 100644
index 0000000..b94d134
--- /dev/null
+++ b/libcutils/qtaguid.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/qtaguid.h>
+
+// #define LOG_NDEBUG 0
+
+#define LOG_TAG "qtaguid"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+class netdHandler {
+  public:
+    int (*netdTagSocket)(int, uint32_t, uid_t);
+    int (*netdUntagSocket)(int);
+    int (*netdSetCounterSet)(uint32_t, uid_t);
+    int (*netdDeleteTagData)(uint32_t, uid_t);
+};
+
+int dummyTagSocket(int, uint32_t, uid_t) {
+    return -EREMOTEIO;
+}
+
+int dummyUntagSocket(int) {
+    return -EREMOTEIO;
+}
+
+int dummySetCounterSet(uint32_t, uid_t) {
+    return -EREMOTEIO;
+}
+
+int dummyDeleteTagData(uint32_t, uid_t) {
+    return -EREMOTEIO;
+}
+
+netdHandler initHandler(void) {
+    netdHandler handler = {dummyTagSocket, dummyUntagSocket, dummySetCounterSet, dummyDeleteTagData};
+
+    void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
+    if (!netdClientHandle) {
+        ALOGE("Failed to open libnetd_client.so: %s", dlerror());
+        return handler;
+    }
+
+    handler.netdTagSocket = (int (*)(int, uint32_t, uid_t))dlsym(netdClientHandle, "tagSocket");
+    if (!handler.netdTagSocket) {
+        ALOGE("load netdTagSocket handler failed: %s", dlerror());
+    }
+
+    handler.netdUntagSocket = (int (*)(int))dlsym(netdClientHandle, "untagSocket");
+    if (!handler.netdUntagSocket) {
+        ALOGE("load netdUntagSocket handler failed: %s", dlerror());
+    }
+
+    handler.netdSetCounterSet = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "setCounterSet");
+    if (!handler.netdSetCounterSet) {
+        ALOGE("load netdSetCounterSet handler failed: %s", dlerror());
+    }
+
+    handler.netdDeleteTagData = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "deleteTagData");
+    if (!handler.netdDeleteTagData) {
+        ALOGE("load netdDeleteTagData handler failed: %s", dlerror());
+    }
+    return handler;
+}
+
+// The language guarantees that this object will be initialized in a thread-safe way.
+static netdHandler& getHandler() {
+    static netdHandler instance = initHandler();
+    return instance;
+}
+
+int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) {
+    // Check the socket fd passed to us is still valid before we load the netd
+    // client. Pass a already closed socket fd to netd client may let netd open
+    // the unix socket with the same fd number and pass it to server for
+    // tagging.
+    // TODO: move the check into netdTagSocket.
+    int res = fcntl(sockfd, F_GETFD);
+    if (res < 0) return res;
+
+    ALOGV("Tagging socket %d with tag %u for uid %d", sockfd, tag, uid);
+    return getHandler().netdTagSocket(sockfd, tag, uid);
+}
+
+int qtaguid_untagSocket(int sockfd) {
+    // Similiar to tag socket. We need a check before untag to make sure untag a closed socket fail
+    // as expected.
+    // TODO: move the check into netdTagSocket.
+    int res = fcntl(sockfd, F_GETFD);
+    if (res < 0) return res;
+
+    ALOGV("Untagging socket %d", sockfd);
+    return getHandler().netdUntagSocket(sockfd);
+}
+
+int qtaguid_setCounterSet(int counterSetNum, uid_t uid) {
+    ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
+    return getHandler().netdSetCounterSet(counterSetNum, uid);
+}
+
+int qtaguid_deleteTagData(int tag, uid_t uid) {
+    ALOGV("Deleting tag data with tag %u for uid %d", tag, uid);
+    return getHandler().netdDeleteTagData(tag, uid);
+}
diff --git a/libcutils/record_stream.c b/libcutils/record_stream.cpp
similarity index 99%
rename from libcutils/record_stream.c
rename to libcutils/record_stream.cpp
index 2bc4226..5a86b83 100644
--- a/libcutils/record_stream.c
+++ b/libcutils/record_stream.cpp
@@ -15,11 +15,12 @@
 ** limitations under the License.
 */
 
+#include <cutils/record_stream.h>
+
 #include <stdlib.h>
 #include <unistd.h>
 #include <assert.h>
 #include <errno.h>
-#include <cutils/record_stream.h>
 #include <string.h>
 #include <stdint.h>
 #if defined(_WIN32)
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index b00fa85..f5ce82f 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#include <cutils/sched_policy.h>
+
 #define LOG_TAG "SchedPolicy"
 
 #include <errno.h>
@@ -24,9 +26,6 @@
 #include <unistd.h>
 
 #include <log/log.h>
-#include <cutils/sched_policy.h>
-
-#define UNUSED __attribute__((__unused__))
 
 /* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
  * Call this any place a SchedPolicy is used as an input parameter.
@@ -444,13 +443,11 @@
 
 /* Stubs for non-Android targets. */
 
-int set_sched_policy(int tid UNUSED, SchedPolicy policy UNUSED)
-{
+int set_sched_policy(int /*tid*/, SchedPolicy /*policy*/) {
     return 0;
 }
 
-int get_sched_policy(int tid UNUSED, SchedPolicy *policy)
-{
+int get_sched_policy(int /*tid*/, SchedPolicy* policy) {
     *policy = SP_SYSTEM_DEFAULT;
     return 0;
 }
diff --git a/libcutils/socket_inaddr_any_server_unix.c b/libcutils/socket_inaddr_any_server_unix.cpp
similarity index 99%
rename from libcutils/socket_inaddr_any_server_unix.c
rename to libcutils/socket_inaddr_any_server_unix.cpp
index 387258f..27c5333 100644
--- a/libcutils/socket_inaddr_any_server_unix.c
+++ b/libcutils/socket_inaddr_any_server_unix.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#include <cutils/sockets.h>
+
 #include <errno.h>
 #include <stddef.h>
 #include <stdlib.h>
@@ -25,8 +27,6 @@
 #include <sys/types.h>
 #include <netinet/in.h>
 
-#include <cutils/sockets.h>
-
 #define LISTEN_BACKLOG 4
 
 /* open listen() port on any interface */
diff --git a/libcutils/socket_inaddr_any_server_windows.c b/libcutils/socket_inaddr_any_server_windows.cpp
similarity index 99%
rename from libcutils/socket_inaddr_any_server_windows.c
rename to libcutils/socket_inaddr_any_server_windows.cpp
index c15200a..1d73206 100644
--- a/libcutils/socket_inaddr_any_server_windows.c
+++ b/libcutils/socket_inaddr_any_server_windows.cpp
@@ -26,10 +26,10 @@
  * SUCH DAMAGE.
  */
 
-#include <errno.h>
-
 #include <cutils/sockets.h>
 
+#include <errno.h>
+
 #define LISTEN_BACKLOG 4
 
 extern bool initialize_windows_sockets();
diff --git a/libcutils/socket_local_client_unix.c b/libcutils/socket_local_client_unix.cpp
similarity index 96%
rename from libcutils/socket_local_client_unix.c
rename to libcutils/socket_local_client_unix.cpp
index 92fb9f1..d2b4909 100644
--- a/libcutils/socket_local_client_unix.c
+++ b/libcutils/socket_local_client_unix.cpp
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
+#include <cutils/sockets.h>
+
 #include <errno.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <cutils/sockets.h>
-
 #if defined(_WIN32)
 
 int socket_local_client(const char *name, int namespaceId, int type)
@@ -39,8 +39,6 @@
 
 #include "socket_local_unix.h"
 
-#define UNUSED __attribute__((unused))
-
 #define LISTEN_BACKLOG 4
 
 /* Documented in header file. */
@@ -123,9 +121,7 @@
  * 
  * Used by AndroidSocketImpl
  */
-int socket_local_client_connect(int fd, const char *name, int namespaceId, 
-        int type UNUSED)
-{
+int socket_local_client_connect(int fd, const char* name, int namespaceId, int /*type*/) {
     struct sockaddr_un addr;
     socklen_t alen;
     int err;
diff --git a/libcutils/socket_local_server_unix.c b/libcutils/socket_local_server_unix.cpp
similarity index 94%
rename from libcutils/socket_local_server_unix.c
rename to libcutils/socket_local_server_unix.cpp
index db9e1e0..855e5da 100644
--- a/libcutils/socket_local_server_unix.c
+++ b/libcutils/socket_local_server_unix.cpp
@@ -94,7 +94,7 @@
  *  Returns fd on success, -1 on fail
  */
 
-int socket_local_server(const char *name, int namespace, int type)
+int socket_local_server(const char *name, int namespaceId, int type)
 {
     int err;
     int s;
@@ -102,7 +102,7 @@
     s = socket(AF_LOCAL, type, 0);
     if (s < 0) return -1;
 
-    err = socket_local_server_bind(s, name, namespace);
+    err = socket_local_server_bind(s, name, namespaceId);
 
     if (err < 0) {
         close(s);
diff --git a/libcutils/socket_network_client_unix.c b/libcutils/socket_network_client_unix.cpp
similarity index 99%
rename from libcutils/socket_network_client_unix.c
rename to libcutils/socket_network_client_unix.cpp
index 1b87c49..be3c535 100644
--- a/libcutils/socket_network_client_unix.c
+++ b/libcutils/socket_network_client_unix.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#include <cutils/sockets.h>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stddef.h>
@@ -27,8 +29,6 @@
 #include <netinet/in.h>
 #include <netdb.h>
 
-#include <cutils/sockets.h>
-
 static int toggle_O_NONBLOCK(int s) {
     int flags = fcntl(s, F_GETFL);
     if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) {
diff --git a/libcutils/socket_network_client_windows.c b/libcutils/socket_network_client_windows.cpp
similarity index 100%
rename from libcutils/socket_network_client_windows.c
rename to libcutils/socket_network_client_windows.cpp
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index e91f358..2849aa8 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/sockets.h>
+
 #define LOG_TAG "socket-unix"
 
 #include <stdio.h>
@@ -26,7 +28,6 @@
 #include <unistd.h>
 
 #include <cutils/android_get_control_file.h>
-#include <cutils/sockets.h>
 #include <log/log.h>
 
 #include "android_get_control_env.h"
diff --git a/libcutils/sockets_windows.cpp b/libcutils/sockets_windows.cpp
index 3064c70..df14712 100644
--- a/libcutils/sockets_windows.cpp
+++ b/libcutils/sockets_windows.cpp
@@ -37,7 +37,7 @@
 // Both adb (1) and Chrome (2) purposefully avoid WSACleanup() with no issues.
 // (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp
 // (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc
-extern "C" bool initialize_windows_sockets() {
+bool initialize_windows_sockets() {
     // There's no harm in calling WSAStartup() multiple times but no benefit
     // either, we may as well skip it after the first.
     static bool init_success = false;
@@ -85,6 +85,6 @@
     return -1;
 }
 
-int android_get_control_socket(const char* name) {
+int android_get_control_socket(const char*) {
     return -1;
 }
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.cpp
similarity index 89%
rename from libcutils/str_parms.c
rename to libcutils/str_parms.cpp
index 8dafded..f5a52a7 100644
--- a/libcutils/str_parms.c
+++ b/libcutils/str_parms.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/str_parms.h>
+
 #define LOG_TAG "str_params"
 //#define LOG_NDEBUG 0
 
@@ -26,11 +28,8 @@
 
 #include <cutils/hashmap.h>
 #include <cutils/memory.h>
-#include <cutils/str_parms.h>
 #include <log/log.h>
 
-#define UNUSED __attribute__((unused))
-
 /* When an object is allocated but not freed in a function,
  * because its ownership is released to other object like a hashmap,
  * call RELEASE_OWNERSHIP to tell the clang analyzer and avoid
@@ -62,30 +61,24 @@
 static int str_hash_fn(void *str)
 {
     uint32_t hash = 5381;
-    char *p;
 
-    for (p = str; p && *p; p++)
+    for (char* p = static_cast<char*>(str); p && *p; p++)
         hash = ((hash << 5) + hash) + *p;
     return (int)hash;
 }
 
 struct str_parms *str_parms_create(void)
 {
-    struct str_parms *str_parms;
+    str_parms* s = static_cast<str_parms*>(calloc(1, sizeof(str_parms)));
+    if (!s) return NULL;
 
-    str_parms = calloc(1, sizeof(struct str_parms));
-    if (!str_parms)
+    s->map = hashmapCreate(5, str_hash_fn, str_eq);
+    if (!s->map) {
+        free(s);
         return NULL;
+    }
 
-    str_parms->map = hashmapCreate(5, str_hash_fn, str_eq);
-    if (!str_parms->map)
-        goto err;
-
-    return str_parms;
-
-err:
-    free(str_parms);
-    return NULL;
+    return s;
 }
 
 struct remove_ctxt {
@@ -95,7 +88,7 @@
 
 static bool remove_pair(void *key, void *value, void *context)
 {
-    struct remove_ctxt *ctxt = context;
+    remove_ctxt* ctxt = static_cast<remove_ctxt*>(context);
     bool should_continue;
 
     /*
@@ -109,7 +102,7 @@
     if (!ctxt->key) {
         should_continue = true;
         goto do_remove;
-    } else if (!strcmp(ctxt->key, key)) {
+    } else if (!strcmp(ctxt->key, static_cast<const char*>(key))) {
         should_continue = false;
         goto do_remove;
     }
@@ -292,9 +285,8 @@
 int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
                       int len)
 {
-    char *value;
-
-    value = hashmapGet(str_parms->map, (void *)key);
+    // TODO: hashmapGet should take a const* key.
+    char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
     if (value)
         return strlcpy(val, value, len);
 
@@ -303,10 +295,10 @@
 
 int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
 {
-    char *value;
     char *end;
 
-    value = hashmapGet(str_parms->map, (void *)key);
+    // TODO: hashmapGet should take a const* key.
+    char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
     if (!value)
         return -ENOENT;
 
@@ -321,10 +313,10 @@
                         float *val)
 {
     float out;
-    char *value;
     char *end;
 
-    value = hashmapGet(str_parms->map, (void *)key);
+    // TODO: hashmapGet should take a const* key.
+    char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)(key)));
     if (!value)
         return -ENOENT;
 
@@ -338,7 +330,7 @@
 
 static bool combine_strings(void *key, void *value, void *context)
 {
-    char **old_str = context;
+    char** old_str = static_cast<char**>(context);
     char *new_str;
     int ret;
 
@@ -370,8 +362,7 @@
     return str;
 }
 
-static bool dump_entry(void *key, void *value, void *context UNUSED)
-{
+static bool dump_entry(void* key, void* value, void* /*context*/) {
     ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
     return true;
 }
diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.cpp
similarity index 97%
rename from libcutils/strdup16to8.c
rename to libcutils/strdup16to8.cpp
index 4dc987e..d89181e 100644
--- a/libcutils/strdup16to8.c
+++ b/libcutils/strdup16to8.cpp
@@ -15,10 +15,10 @@
 ** limitations under the License.
 */
 
-#include <limits.h>  /* for SIZE_MAX */
-
 #include <cutils/jstring.h>
+
 #include <assert.h>
+#include <limits.h>  /* for SIZE_MAX */
 #include <stdlib.h>
 
 
@@ -145,14 +145,11 @@
  */
 char * strndup16to8 (const char16_t* s, size_t n)
 {
-    char*   ret;
-    size_t  len;
-
     if (s == NULL) {
         return NULL;
     }
 
-    len = strnlen16to8(s, n);
+    size_t len = strnlen16to8(s, n);
 
     /* We are paranoid, and we check for SIZE_MAX-1
      * too since it is an overflow value for our
@@ -161,7 +158,7 @@
     if (len >= SIZE_MAX-1)
         return NULL;
 
-    ret = malloc(len + 1);
+    char* ret = static_cast<char*>(malloc(len + 1));
     if (ret == NULL)
         return NULL;
 
diff --git a/libcutils/strdup8to16.c b/libcutils/strdup8to16.cpp
similarity index 98%
rename from libcutils/strdup8to16.c
rename to libcutils/strdup8to16.cpp
index c23cf8b..d1e51b9 100644
--- a/libcutils/strdup8to16.c
+++ b/libcutils/strdup8to16.cpp
@@ -16,9 +16,10 @@
 */
 
 #include <cutils/jstring.h>
+
 #include <assert.h>
-#include <stdlib.h>
 #include <limits.h>
+#include <stdlib.h>
 
 /* See http://www.unicode.org/reports/tr22/ for discussion
  * on invalid sequences
@@ -116,7 +117,7 @@
     int i;
 
     /* Mask for leader byte for lengths 1, 2, 3, and 4 respectively*/
-    static const char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07};
+    static const unsigned char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07};
 
     /* Bytes that start with bits "10" are not leading characters. */
     if (((**pUtf8Ptr) & 0xc0) == 0x80) {
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
index 2f9d854..4b0fd13 100644
--- a/libcutils/tests/multiuser_test.cpp
+++ b/libcutils/tests/multiuser_test.cpp
@@ -57,7 +57,10 @@
     EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 1000));
     EXPECT_EQ(20000U, multiuser_get_cache_gid(0, 10000));
     EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 50000));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 1000));
     EXPECT_EQ(1020000U, multiuser_get_cache_gid(10, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 50000));
 }
 
 TEST(MultiuserTest, TestExt) {
@@ -77,9 +80,12 @@
 }
 
 TEST(MultiuserTest, TestShared) {
-    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 0));
-    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 1000));
+    EXPECT_EQ(0U, multiuser_get_shared_gid(0, 0));
+    EXPECT_EQ(1000U, multiuser_get_shared_gid(0, 1000));
     EXPECT_EQ(50000U, multiuser_get_shared_gid(0, 10000));
     EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 50000));
-    EXPECT_EQ(1050000U, multiuser_get_shared_gid(10, 10000));
+    EXPECT_EQ(0U, multiuser_get_shared_gid(10, 0));
+    EXPECT_EQ(1000U, multiuser_get_shared_gid(10, 1000));
+    EXPECT_EQ(50000U, multiuser_get_shared_gid(10, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(10, 50000));
 }
diff --git a/libcutils/tests/trace-dev_test.cpp b/libcutils/tests/trace-dev_test.cpp
index edf981b..f8d4f00 100644
--- a/libcutils/tests/trace-dev_test.cpp
+++ b/libcutils/tests/trace-dev_test.cpp
@@ -25,7 +25,7 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-#include "../trace-dev.c"
+#include "../trace-dev.cpp"
 
 class TraceDevTest : public ::testing::Test {
  protected:
diff --git a/libcutils/threads.c b/libcutils/threads.cpp
similarity index 96%
rename from libcutils/threads.c
rename to libcutils/threads.cpp
index 4bae39e..a7e6b2d 100644
--- a/libcutils/threads.c
+++ b/libcutils/threads.cpp
@@ -14,7 +14,7 @@
 ** limitations under the License.
 */
 
-#include "cutils/threads.h"
+#include <cutils/threads.h>
 
 // For gettid.
 #if defined(__APPLE__)
@@ -84,7 +84,7 @@
 
 void   thread_store_set( thread_store_t*          store,
                          void*                    value,
-                         thread_store_destruct_t  destroy )
+                         thread_store_destruct_t  /*destroy*/ )
 {
     /* XXX: can't use destructor on thread exit */
     if (!store->lock_init) {
diff --git a/libcutils/trace-container.c b/libcutils/trace-container.cpp
similarity index 99%
rename from libcutils/trace-container.c
rename to libcutils/trace-container.cpp
index 03e91b1..d981f8f 100644
--- a/libcutils/trace-container.c
+++ b/libcutils/trace-container.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/trace.h>
+
 #include "trace-dev.inc"
 
 #include <cutils/sockets.h>
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.cpp
similarity index 98%
rename from libcutils/trace-dev.c
rename to libcutils/trace-dev.cpp
index 4468e83..4da8215 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/trace.h>
+
 #include "trace-dev.inc"
 
 static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
diff --git a/libcutils/trace-host.c b/libcutils/trace-host.cpp
similarity index 61%
rename from libcutils/trace-host.c
rename to libcutils/trace-host.cpp
index 05842cd..d47cc18 100644
--- a/libcutils/trace-host.c
+++ b/libcutils/trace-host.cpp
@@ -16,21 +16,17 @@
 
 #include <cutils/trace.h>
 
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
 atomic_bool             atrace_is_ready      = ATOMIC_VAR_INIT(true);
 int                     atrace_marker_fd     = -1;
 uint64_t                atrace_enabled_tags  = 0;
 
-void atrace_set_debuggable(bool debuggable __unused) { }
-void atrace_set_tracing_enabled(bool enabled __unused) { }
+void atrace_set_debuggable(bool /*debuggable*/) {}
+void atrace_set_tracing_enabled(bool /*enabled*/) {}
 void atrace_update_tags() { }
 void atrace_setup() { }
-void atrace_begin_body(const char* name __unused) { }
+void atrace_begin_body(const char* /*name*/) {}
 void atrace_end_body() { }
-void atrace_async_begin_body(const char* name __unused, int32_t cookie __unused) { }
-void atrace_async_end_body(const char* name __unused, int32_t cookie __unused) { }
-void atrace_int_body(const char* name __unused, int32_t value __unused) { }
-void atrace_int64_body(const char* name __unused, int64_t value __unused) { }
+void atrace_async_begin_body(const char* /*name*/, int32_t /*cookie*/) {}
+void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
+void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
+void atrace_int64_body(const char* /*name*/, int64_t /*value*/) {}
diff --git a/libcutils/uevent.c b/libcutils/uevent.c
deleted file mode 100644
index f548dca..0000000
--- a/libcutils/uevent.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cutils/uevent.h>
-
-#include <errno.h>
-#include <stdbool.h>
-#include <string.h>
-#include <strings.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <linux/netlink.h>
-
-/**
- * Like recv(), but checks that messages actually originate from the kernel.
- */
-ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length)
-{
-    uid_t uid = -1;
-    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
-}
-
-/**
- * Like the above, but passes a uid_t in by pointer. In the event that this
- * fails due to a bad uid check, the uid_t will be set to the uid of the
- * socket's peer.
- *
- * If this method rejects a netlink message from outside the kernel, it
- * returns -1, sets errno to EIO, and sets "user" to the UID associated with the
- * message. If the peer UID cannot be determined, "user" is set to -1."
- */
-ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid)
-{
-    return uevent_kernel_recv(socket, buffer, length, true, uid);
-}
-
-ssize_t uevent_kernel_recv(int socket, void *buffer, size_t length, bool require_group, uid_t *uid)
-{
-    struct iovec iov = { buffer, length };
-    struct sockaddr_nl addr;
-    char control[CMSG_SPACE(sizeof(struct ucred))];
-    struct msghdr hdr = {
-        &addr,
-        sizeof(addr),
-        &iov,
-        1,
-        control,
-        sizeof(control),
-        0,
-    };
-
-    *uid = -1;
-    ssize_t n = recvmsg(socket, &hdr, 0);
-    if (n <= 0) {
-        return n;
-    }
-
-    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
-    if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
-        /* ignoring netlink message with no sender credentials */
-        goto out;
-    }
-
-    struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
-    *uid = cred->uid;
-    if (cred->uid != 0) {
-        /* ignoring netlink message from non-root user */
-        goto out;
-    }
-
-    if (addr.nl_pid != 0) {
-        /* ignore non-kernel */
-        goto out;
-    }
-    if (require_group && addr.nl_groups == 0) {
-        /* ignore unicast messages when requested */
-        goto out;
-    }
-
-    return n;
-
-out:
-    /* clear residual potentially malicious data */
-    bzero(buffer, length);
-    errno = EIO;
-    return -1;
-}
-
-int uevent_open_socket(int buf_sz, bool passcred)
-{
-    struct sockaddr_nl addr;
-    int on = passcred;
-    int s;
-
-    memset(&addr, 0, sizeof(addr));
-    addr.nl_family = AF_NETLINK;
-    addr.nl_pid = getpid();
-    addr.nl_groups = 0xffffffff;
-
-    s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
-    if(s < 0)
-        return -1;
-
-    /* buf_sz should be less than net.core.rmem_max for this to succeed */
-    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
-        close(s);
-        return -1;
-    }
-
-    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
-
-    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        close(s);
-        return -1;
-    }
-
-    return s;
-}
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
new file mode 100644
index 0000000..a84e5b0
--- /dev/null
+++ b/libcutils/uevent.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/uevent.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <linux/netlink.h>
+
+#include <fstream>
+
+#include <private/android_filesystem_config.h>
+
+namespace {
+
+// Returns the uid of root in the current user namespace.
+// Returns AID_OVERFLOWUID if the root user is not mapped in the current
+// namespace.
+// Returns 0 if the kernel is not user namespace-aware (for backwards
+// compatibility) or if AID_OVERFLOWUID could not be validated to match what the
+// kernel would return.
+uid_t GetRootUid() {
+    constexpr uid_t kParentRootUid = 0;
+
+    std::ifstream uid_map_file("/proc/self/uid_map");
+    if (!uid_map_file) {
+        // The kernel does not support user namespaces.
+        return kParentRootUid;
+    }
+
+    uid_t current_namespace_uid, parent_namespace_uid;
+    uint32_t length;
+    while (uid_map_file >> current_namespace_uid >> parent_namespace_uid >> length) {
+        // Since kParentRootUid is 0, it should be the first entry in the mapped
+        // range.
+        if (parent_namespace_uid != kParentRootUid || length < 1) continue;
+        return current_namespace_uid;
+    }
+
+    // Sanity check: verify that the overflow UID is the one to be returned by
+    // the kernel.
+    std::ifstream overflowuid_file("/proc/sys/kernel/overflowuid");
+    if (!overflowuid_file) {
+        // It's better to return 0 in case we cannot make sure that the overflow
+        // UID matches.
+        return kParentRootUid;
+    }
+    uid_t kernel_overflow_uid;
+    if (!(overflowuid_file >> kernel_overflow_uid) || kernel_overflow_uid != AID_OVERFLOWUID)
+        return kParentRootUid;
+
+    // root is unmapped, use the kernel "overflow" uid.
+    return AID_OVERFLOWUID;
+}
+
+}  // namespace
+
+extern "C" {
+
+/**
+ * Like recv(), but checks that messages actually originate from the kernel.
+ */
+ssize_t uevent_kernel_multicast_recv(int socket, void* buffer, size_t length) {
+    uid_t uid = -1;
+    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
+}
+
+/**
+ * Like the above, but passes a uid_t in by pointer. In the event that this
+ * fails due to a bad uid check, the uid_t will be set to the uid of the
+ * socket's peer.
+ *
+ * If this method rejects a netlink message from outside the kernel, it
+ * returns -1, sets errno to EIO, and sets "user" to the UID associated with the
+ * message. If the peer UID cannot be determined, "user" is set to -1."
+ */
+ssize_t uevent_kernel_multicast_uid_recv(int socket, void* buffer, size_t length, uid_t* uid) {
+    return uevent_kernel_recv(socket, buffer, length, true, uid);
+}
+
+ssize_t uevent_kernel_recv(int socket, void* buffer, size_t length, bool require_group, uid_t* uid) {
+    static const uid_t root_uid = GetRootUid();
+    struct iovec iov = {buffer, length};
+    struct sockaddr_nl addr;
+    char control[CMSG_SPACE(sizeof(struct ucred))];
+    struct msghdr hdr = {
+        &addr, sizeof(addr), &iov, 1, control, sizeof(control), 0,
+    };
+    struct ucred* cred;
+
+    *uid = -1;
+    ssize_t n = recvmsg(socket, &hdr, 0);
+    if (n <= 0) {
+        return n;
+    }
+
+    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+    if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
+        /* ignoring netlink message with no sender credentials */
+        goto out;
+    }
+
+    cred = (struct ucred*)CMSG_DATA(cmsg);
+    *uid = cred->uid;
+    if (cred->uid != root_uid) {
+        /* ignoring netlink message from non-root user */
+        goto out;
+    }
+
+    if (addr.nl_pid != 0) {
+        /* ignore non-kernel */
+        goto out;
+    }
+    if (require_group && addr.nl_groups == 0) {
+        /* ignore unicast messages when requested */
+        goto out;
+    }
+
+    return n;
+
+out:
+    /* clear residual potentially malicious data */
+    bzero(buffer, length);
+    errno = EIO;
+    return -1;
+}
+
+int uevent_open_socket(int buf_sz, bool passcred) {
+    struct sockaddr_nl addr;
+    int on = passcred;
+    int s;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.nl_family = AF_NETLINK;
+    addr.nl_pid = getpid();
+    addr.nl_groups = 0xffffffff;
+
+    s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
+    if (s < 0) return -1;
+
+    /* buf_sz should be less than net.core.rmem_max for this to succeed */
+    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+
+    if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    return s;
+}
+
+}  // extern "C"
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 83064fd..2e2bf87 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -25,9 +25,9 @@
 #include <string.h>
 #include <sys/mman.h>
 
-#include <experimental/string_view>
 #include <functional>
 #include <string>
+#include <string_view>
 #include <unordered_map>
 
 #include <log/event_tag_map.h>
@@ -44,10 +44,10 @@
 class MapString {
  private:
   const std::string* alloc;                  // HAS-AN
-  const std::experimental::string_view str;  // HAS-A
+  const std::string_view str;                // HAS-A
 
  public:
-  operator const std::experimental::string_view() const {
+  operator const std::string_view() const {
     return str;
   }
 
@@ -92,8 +92,7 @@
     : public std::unary_function<const MapString&, size_t> {
   size_t operator()(const MapString& __t) const noexcept {
     if (!__t.length()) return 0;
-    return std::hash<std::experimental::string_view>()(
-        std::experimental::string_view(__t));
+    return std::hash<std::string_view>()(std::string_view(__t));
   }
 };
 
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index d01708d..3813e6e 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -95,6 +95,8 @@
                           size_t len);
 int __android_log_bswrite(int32_t tag, const char* payload);
 
+int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len);
+
 #define android_bWriteLog(tag, payload, len) \
   __android_log_bwrite(tag, payload, len)
 #define android_btWriteLog(tag, type, payload, len) \
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index 7bfa277..c44f5a2 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -31,8 +31,9 @@
   LOG_ID_EVENTS = 2,
   LOG_ID_SYSTEM = 3,
   LOG_ID_CRASH = 4,
-  LOG_ID_SECURITY = 5,
-  LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
+  LOG_ID_STATS = 5,
+  LOG_ID_SECURITY = 6,
+  LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
 
   LOG_ID_MAX
 } log_id_t;
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
index 59ea5ef..a59cb87 100644
--- a/liblog/log_event_list.c
+++ b/liblog/log_event_list.c
@@ -301,7 +301,7 @@
   const char* msg;
   ssize_t len;
 
-  if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY)) {
+  if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) {
     return -EINVAL;
   }
 
@@ -326,7 +326,9 @@
   }
   return (id == LOG_ID_EVENTS)
              ? __android_log_bwrite(context->tag, msg, len)
-             : __android_log_security_bwrite(context->tag, msg, len);
+             : ((id == LOG_ID_STATS)
+                    ? __android_log_stats_bwrite(context->tag, msg, len)
+                    : __android_log_security_bwrite(context->tag, msg, len));
 }
 
 LIBLOG_ABI_PRIVATE int android_log_write_list_buffer(android_log_context ctx,
diff --git a/liblog/logger_name.c b/liblog/logger_name.c
index a5a83e0..479bbfe 100644
--- a/liblog/logger_name.c
+++ b/liblog/logger_name.c
@@ -22,12 +22,13 @@
 
 /* In the future, we would like to make this list extensible */
 static const char* LOG_NAME[LOG_ID_MAX] = {
-  /* clang-format off */
+      /* clang-format off */
   [LOG_ID_MAIN] = "main",
   [LOG_ID_RADIO] = "radio",
   [LOG_ID_EVENTS] = "events",
   [LOG_ID_SYSTEM] = "system",
   [LOG_ID_CRASH] = "crash",
+  [LOG_ID_STATS] = "stats",
   [LOG_ID_SECURITY] = "security",
   [LOG_ID_KERNEL] = "kernel",
   /* clang-format on */
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index 84feb20..d03a2b6 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -270,7 +270,7 @@
       /* If only we could reset downstream logd counter */
       return -EPERM;
     }
-  } else if (log_id == LOG_ID_EVENTS) {
+  } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
     const char* tag;
     size_t len;
     EventTagMap *m, *f;
@@ -546,6 +546,19 @@
   return write_to_log(LOG_ID_EVENTS, vec, 2);
 }
 
+LIBLOG_ABI_PUBLIC int __android_log_stats_bwrite(int32_t tag,
+                                                 const void* payload,
+                                                 size_t len) {
+  struct iovec vec[2];
+
+  vec[0].iov_base = &tag;
+  vec[0].iov_len = sizeof(tag);
+  vec[1].iov_base = (void*)payload;
+  vec[1].iov_len = len;
+
+  return write_to_log(LOG_ID_STATS, vec, 2);
+}
+
 LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag,
                                                     const void* payload,
                                                     size_t len) {
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index 275a2d6..39b52ac 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -54,7 +54,7 @@
     -Werror \
     -fno-builtin \
 
-test_src_files := \
+cts_src_files := \
     libc_test.cpp \
     liblog_test_default.cpp \
     liblog_test_local.cpp \
@@ -67,6 +67,9 @@
     log_time_test.cpp \
     log_wrap_test.cpp
 
+test_src_files := \
+    $(cts_src_files) \
+
 # Build tests for the device (with .so). Run with:
 #   adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
 include $(CLEAR_VARS)
@@ -82,15 +85,15 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := $(cts_executable)
 LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_CFLAGS += $(test_c_flags) -DNO_PSTORE
+LOCAL_SRC_FILES := $(cts_src_files)
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
 LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
 LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_COMPATIBILITY_SUITE := cts vts
 LOCAL_CTS_TEST_PACKAGE := android.core.liblog
 include $(BUILD_CTS_EXECUTABLE)
 
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index e2d5aeb..597a6bb 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -116,6 +116,7 @@
   return ret;
 }
 
+#ifndef NO_PSTORE
 static bool isPmsgActive() {
   pid_t pid = getpid();
 
@@ -125,6 +126,7 @@
 
   return std::string::npos != myPidFds.find(" -> /dev/pmsg0");
 }
+#endif /* NO_PSTORE */
 
 static bool isLogdwActive() {
   std::string logdwSignature =
@@ -189,22 +191,25 @@
   EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
 #ifdef USING_LOGGER_DEFAULT
   // Check that we can close and reopen the logger
-  bool pmsgActiveAfter__android_log_btwrite;
   bool logdwActiveAfter__android_log_btwrite;
   if (getuid() == AID_ROOT) {
     tested__android_log_close = true;
-    pmsgActiveAfter__android_log_btwrite = isPmsgActive();
-    logdwActiveAfter__android_log_btwrite = isLogdwActive();
+#ifndef NO_PSTORE
+    bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
     EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+#endif /* NO_PSTORE */
+    logdwActiveAfter__android_log_btwrite = isLogdwActive();
     EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
   } else if (!tested__android_log_close) {
     fprintf(stderr, "WARNING: can not test __android_log_close()\n");
   }
   __android_log_close();
   if (getuid() == AID_ROOT) {
+#ifndef NO_PSTORE
     bool pmsgActiveAfter__android_log_close = isPmsgActive();
-    bool logdwActiveAfter__android_log_close = isLogdwActive();
     EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+#endif /* NO_PSTORE */
+    bool logdwActiveAfter__android_log_close = isLogdwActive();
     EXPECT_FALSE(logdwActiveAfter__android_log_close);
   }
 #endif
@@ -213,9 +218,11 @@
   EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
 #ifdef USING_LOGGER_DEFAULT
   if (getuid() == AID_ROOT) {
-    pmsgActiveAfter__android_log_btwrite = isPmsgActive();
-    logdwActiveAfter__android_log_btwrite = isLogdwActive();
+#ifndef NO_PSTORE
+    bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
     EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+#endif /* NO_PSTORE */
+    logdwActiveAfter__android_log_btwrite = isLogdwActive();
     EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
   }
 #endif
@@ -3036,12 +3043,15 @@
 
 #ifdef USING_LOGGER_DEFAULT  // Do not retest pmsg functionality
 #ifdef __ANDROID__
+#ifndef NO_PSTORE
 static const char __pmsg_file[] =
     "/data/william-shakespeare/MuchAdoAboutNothing.txt";
+#endif /* NO_PSTORE */
 #endif
 
 TEST(liblog, __android_log_pmsg_file_write) {
 #ifdef __ANDROID__
+#ifndef NO_PSTORE
   __android_log_close();
   if (getuid() == AID_ROOT) {
     tested__android_log_close = true;
@@ -3092,12 +3102,16 @@
     EXPECT_TRUE(pmsgActiveAfter__android_pmsg_file_write);
     EXPECT_TRUE(logdwActiveAfter__android_pmsg_file_write);
   }
+#else  /* NO_PSTORE */
+  GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
 #ifdef __ANDROID__
+#ifndef NO_PSTORE
 static ssize_t __pmsg_fn(log_id_t logId, char prio, const char* filename,
                          const char* buf, size_t len, void* arg) {
   EXPECT_TRUE(NULL == arg);
@@ -3118,10 +3132,12 @@
              ? -ENOEXEC
              : 1;
 }
+#endif /* NO_PSTORE */
 #endif
 
 TEST(liblog, __android_log_pmsg_file_read) {
 #ifdef __ANDROID__
+#ifndef NO_PSTORE
   signaled = 0;
 
   __android_log_close();
@@ -3155,6 +3171,9 @@
 
   EXPECT_LT(0, ret);
   EXPECT_EQ(1U, signaled);
+#else  /* NO_PSTORE */
+  GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 99ae3a7..3563fc1 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -34,6 +34,7 @@
                                    int32_t target_sdk_version,
                                    jobject class_loader,
                                    bool is_shared,
+                                   bool is_for_vendor,
                                    jstring library_path,
                                    jstring permitted_path);
 
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 7ccd7db..5d160ee 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -82,6 +82,11 @@
                                   "/etc/public.libraries.txt";
 static constexpr const char* kPublicNativeLibrariesVendorConfig =
                                   "/vendor/etc/public.libraries.txt";
+static constexpr const char* kLlndkNativeLibrariesSystemConfigPathFromRoot =
+                                  "/etc/llndk.libraries.txt";
+static constexpr const char* kVndkspNativeLibrariesSystemConfigPathFromRoot =
+                                  "/etc/vndksp.libraries.txt";
+
 
 // The device may be configured to have the vendor libraries loaded to a separate namespace.
 // For historical reasons this namespace was named sphal but effectively it is intended
@@ -89,6 +94,11 @@
 // vendor and system namespaces.
 static constexpr const char* kVendorNamespaceName = "sphal";
 
+static constexpr const char* kVndkNamespaceName = "vndk";
+
+static constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
+static constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
+
 // (http://b/27588281) This is a workaround for apps using custom classloaders and calling
 // System.load() with an absolute path which is outside of the classloader library search path.
 // This list includes all directories app is allowed to access this way.
@@ -108,6 +118,7 @@
               uint32_t target_sdk_version,
               jobject class_loader,
               bool is_shared,
+              bool is_for_vendor,
               jstring java_library_path,
               jstring java_permitted_path,
               NativeLoaderNamespace* ns,
@@ -163,9 +174,39 @@
       is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
     }
 
+    std::string system_exposed_libraries = system_public_libraries_;
+    const char* namespace_name = kClassloaderNamespaceName;
+    android_namespace_t* vndk_ns = nullptr;
+    if (is_for_vendor && !is_shared) {
+      LOG_FATAL_IF(is_native_bridge, "Unbundled vendor apk must not use translated architecture");
+
+      // For vendor apks, give access to the vendor lib even though
+      // they are treated as unbundled; the libs and apks are still bundled
+      // together in the vendor partition.
+#if defined(__LP64__)
+      std::string vendor_lib_path = "/vendor/lib64";
+#else
+      std::string vendor_lib_path = "/vendor/lib";
+#endif
+      library_path = library_path + ":" + vendor_lib_path.c_str();
+      permitted_path = permitted_path + ":" + vendor_lib_path.c_str();
+
+      // Also give access to LLNDK libraries since they are available to vendors
+      system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str();
+
+      // Give access to VNDK-SP libraries from the 'vndk' namespace.
+      vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
+      LOG_ALWAYS_FATAL_IF(vndk_ns == nullptr,
+                          "Cannot find \"%s\" namespace for vendor apks", kVndkNamespaceName);
+
+      // Different name is useful for debugging
+      namespace_name = kVendorClassloaderNamespaceName;
+      ALOGD("classloader namespace configured for unbundled vendor apk. library_path=%s", library_path.c_str());
+    }
+
     NativeLoaderNamespace native_loader_ns;
     if (!is_native_bridge) {
-      android_namespace_t* ns = android_create_namespace("classloader-namespace",
+      android_namespace_t* ns = android_create_namespace(namespace_name,
                                                          nullptr,
                                                          library_path.c_str(),
                                                          namespace_type,
@@ -181,11 +222,19 @@
       // which is expected behavior in this case.
       android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
 
-      if (!android_link_namespaces(ns, nullptr, system_public_libraries_.c_str())) {
+      if (!android_link_namespaces(ns, nullptr, system_exposed_libraries.c_str())) {
         *error_msg = dlerror();
         return false;
       }
 
+      if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) {
+        // vendor apks are allowed to use VNDK-SP libraries.
+        if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) {
+          *error_msg = dlerror();
+          return false;
+        }
+      }
+
       if (!vendor_public_libraries_.empty()) {
         if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
           *error_msg = dlerror();
@@ -195,7 +244,7 @@
 
       native_loader_ns = NativeLoaderNamespace(ns);
     } else {
-      native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace",
+      native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name,
                                                                   nullptr,
                                                                   library_path.c_str(),
                                                                   namespace_type,
@@ -209,7 +258,7 @@
 
       native_bridge_namespace_t* vendor_ns = NativeBridgeGetVendorNamespace();
 
-      if (!NativeBridgeLinkNamespaces(ns, nullptr, system_public_libraries_.c_str())) {
+      if (!NativeBridgeLinkNamespaces(ns, nullptr, system_exposed_libraries.c_str())) {
         *error_msg = NativeBridgeGetError();
         return false;
       }
@@ -259,6 +308,10 @@
     std::string root_dir = android_root_env != nullptr ? android_root_env : "/system";
     std::string public_native_libraries_system_config =
             root_dir + kPublicNativeLibrariesSystemConfigPathFromRoot;
+    std::string llndk_native_libraries_system_config =
+            root_dir + kLlndkNativeLibrariesSystemConfigPathFromRoot;
+    std::string vndksp_native_libraries_system_config =
+            root_dir + kVndkspNativeLibrariesSystemConfigPathFromRoot;
 
     std::string error_msg;
     LOG_ALWAYS_FATAL_IF(!ReadConfig(public_native_libraries_system_config, &sonames, &error_msg),
@@ -294,6 +347,14 @@
     system_public_libraries_ = base::Join(sonames, ':');
 
     sonames.clear();
+    ReadConfig(kLlndkNativeLibrariesSystemConfigPathFromRoot, &sonames);
+    system_llndk_libraries_ = base::Join(sonames, ':');
+
+    sonames.clear();
+    ReadConfig(kVndkspNativeLibrariesSystemConfigPathFromRoot, &sonames);
+    system_vndksp_libraries_ = base::Join(sonames, ':');
+
+    sonames.clear();
     // This file is optional, quietly ignore if the file does not exist.
     ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
 
@@ -404,6 +465,8 @@
   std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
   std::string system_public_libraries_;
   std::string vendor_public_libraries_;
+  std::string system_llndk_libraries_;
+  std::string system_vndksp_libraries_;
 
   DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
 };
@@ -430,6 +493,7 @@
                                    int32_t target_sdk_version,
                                    jobject class_loader,
                                    bool is_shared,
+                                   bool is_for_vendor,
                                    jstring library_path,
                                    jstring permitted_path) {
 #if defined(__ANDROID__)
@@ -441,6 +505,7 @@
                                       target_sdk_version,
                                       class_loader,
                                       is_shared,
+                                      is_for_vendor,
                                       library_path,
                                       permitted_path,
                                       &ns,
@@ -449,7 +514,7 @@
     return env->NewStringUTF(error_msg.c_str());
   }
 #else
-  UNUSED(env, target_sdk_version, class_loader, is_shared,
+  UNUSED(env, target_sdk_version, class_loader, is_shared, is_for_vendor,
          library_path, permitted_path);
 #endif
   return nullptr;
@@ -478,7 +543,8 @@
     if (!g_namespaces->Create(env,
                               target_sdk_version,
                               class_loader,
-                              false,
+                              false /* is_shared */,
+                              false /* is_for_vendor */,
                               library_path,
                               nullptr,
                               &ns,
diff --git a/libqtaguid/Android.bp b/libqtaguid/Android.bp
new file mode 100644
index 0000000..de632ca
--- /dev/null
+++ b/libqtaguid/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_headers {
+    name: "libqtaguid_headers",
+    vendor_available: false,
+    host_supported: false,
+    export_include_dirs: ["include"],
+    target: {
+        linux_bionic: {
+            enabled: true,
+        },
+    },
+}
+
+cc_library {
+    name: "libqtaguid",
+    vendor_available: false,
+    host_supported: false,
+    target: {
+        android: {
+            srcs: [
+                "qtaguid.c",
+            ],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
+        },
+    },
+
+    shared_libs: ["liblog"],
+    header_libs: [
+        "libqtaguid_headers",
+    ],
+    export_header_lib_headers: ["libqtaguid_headers"],
+    local_include_dirs: ["include"],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+}
diff --git a/libqtaguid/include/qtaguid/qtaguid.h b/libqtaguid/include/qtaguid/qtaguid.h
new file mode 100644
index 0000000..72285e5
--- /dev/null
+++ b/libqtaguid/include/qtaguid/qtaguid.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LEGACY_QTAGUID_H
+#define __LEGACY_QTAGUID_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Set tags (and owning UIDs) for network sockets. The socket must be untagged
+ * by calling qtaguid_untagSocket() before closing it, otherwise the qtaguid
+ * module will keep a reference to it even after close.
+ */
+extern int legacy_tagSocket(int sockfd, int tag, uid_t uid);
+
+/*
+ * Untag a network socket before closing.
+ */
+extern int legacy_untagSocket(int sockfd);
+
+/*
+ * For the given uid, switch counter sets.
+ * The kernel only keeps a limited number of sets.
+ * 2 for now.
+ */
+extern int legacy_setCounterSet(int counterSetNum, uid_t uid);
+
+/*
+ * Delete all tag info that relates to the given tag an uid.
+ * If the tag is 0, then ALL info about the uid is freeded.
+ * The delete data also affects active tagged socketd, which are
+ * then untagged.
+ * The calling process can only operate on its own tags.
+ * Unless it is part of the happy AID_NET_BW_ACCT group.
+ * In which case it can clobber everything.
+ */
+extern int legacy_deleteTagData(int tag, uid_t uid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LEGACY_QTAGUID_H */
diff --git a/libcutils/qtaguid.c b/libqtaguid/qtaguid.c
similarity index 72%
rename from libcutils/qtaguid.c
rename to libqtaguid/qtaguid.c
index 22b8325..cd38bad 100644
--- a/libcutils/qtaguid.c
+++ b/libqtaguid/qtaguid.c
@@ -27,12 +27,10 @@
 #include <unistd.h>
 
 #include <log/log.h>
-#include <cutils/qtaguid.h>
+#include <qtaguid/qtaguid.h>
 
 static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl";
 static const int CTRL_MAX_INPUT_LEN = 128;
-static const char *GLOBAL_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/passive";
-static const char *TAG_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/tag_tracking_passive";
 
 /*
  * One per proccess.
@@ -46,7 +44,7 @@
 pthread_once_t resTrackInitDone = PTHREAD_ONCE_INIT;
 
 /* Only call once per process. */
-void qtaguid_resTrack(void) {
+void legacy_resTrack(void) {
     resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY | O_CLOEXEC));
 }
 
@@ -55,7 +53,7 @@
  *   0 on success.
  *   -errno on failure.
  */
-static int write_ctrl(const char *cmd) {
+static int write_ctrl(const char* cmd) {
     int fd, res, savedErrno;
 
     ALOGV("write_ctrl(%s)", cmd);
@@ -79,28 +77,12 @@
     return -savedErrno;
 }
 
-static int write_param(const char *param_path, const char *value) {
-    int param_fd;
-    int res;
-
-    param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY | O_CLOEXEC));
-    if (param_fd < 0) {
-        return -errno;
-    }
-    res = TEMP_FAILURE_RETRY(write(param_fd, value, strlen(value)));
-    if (res < 0) {
-        return -errno;
-    }
-    close(param_fd);
-    return 0;
-}
-
-int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) {
+int legacy_tagSocket(int sockfd, int tag, uid_t uid) {
     char lineBuf[CTRL_MAX_INPUT_LEN];
     int res;
     uint64_t kTag = ((uint64_t)tag << 32);
 
-    pthread_once(&resTrackInitDone, qtaguid_resTrack);
+    pthread_once(&resTrackInitDone, legacy_resTrack);
 
     snprintf(lineBuf, sizeof(lineBuf), "t %d %" PRIu64 " %d", sockfd, kTag, uid);
 
@@ -108,14 +90,14 @@
 
     res = write_ctrl(lineBuf);
     if (res < 0) {
-        ALOGI("Tagging socket %d with tag %" PRIx64 "(%d) for uid %d failed errno=%d",
-             sockfd, kTag, tag, uid, res);
+        ALOGI("Tagging socket %d with tag %" PRIx64 "(%d) for uid %d failed errno=%d", sockfd, kTag,
+              tag, uid, res);
     }
 
     return res;
 }
 
-int qtaguid_untagSocket(int sockfd) {
+int legacy_untagSocket(int sockfd) {
     char lineBuf[CTRL_MAX_INPUT_LEN];
     int res;
 
@@ -130,7 +112,7 @@
     return res;
 }
 
-int qtaguid_setCounterSet(int counterSetNum, uid_t uid) {
+int legacy_setCounterSet(int counterSetNum, uid_t uid) {
     char lineBuf[CTRL_MAX_INPUT_LEN];
     int res;
 
@@ -141,34 +123,21 @@
     return res;
 }
 
-int qtaguid_deleteTagData(int tag, uid_t uid) {
+int legacy_deleteTagData(int tag, uid_t uid) {
     char lineBuf[CTRL_MAX_INPUT_LEN];
     int cnt = 0, res = 0;
     uint64_t kTag = (uint64_t)tag << 32;
 
     ALOGV("Deleting tag data with tag %" PRIx64 "{%d,0} for uid %d", kTag, tag, uid);
 
-    pthread_once(&resTrackInitDone, qtaguid_resTrack);
+    pthread_once(&resTrackInitDone, legacy_resTrack);
 
     snprintf(lineBuf, sizeof(lineBuf), "d %" PRIu64 " %d", kTag, uid);
     res = write_ctrl(lineBuf);
     if (res < 0) {
         ALOGI("Deleting tag data with tag %" PRIx64 "/%d for uid %d failed with cnt=%d errno=%d",
-             kTag, tag, uid, cnt, errno);
+              kTag, tag, uid, cnt, errno);
     }
 
     return res;
 }
-
-int qtaguid_setPacifier(int on) {
-    const char *value;
-
-    value = on ? "Y" : "N";
-    if (write_param(GLOBAL_PACIFIER_PARAM, value) < 0) {
-        return -errno;
-    }
-    if (write_param(TAG_PACIFIER_PARAM, value) < 0) {
-        return -errno;
-    }
-    return 0;
-}
diff --git a/libsystem/include/system/thread_defs.h b/libsystem/include/system/thread_defs.h
index 377a48c..80d1160 100644
--- a/libsystem/include/system/thread_defs.h
+++ b/libsystem/include/system/thread_defs.h
@@ -55,6 +55,9 @@
     /* ui service treads might want to run at a urgent display (uncommon) */
     ANDROID_PRIORITY_URGENT_DISPLAY =  HAL_PRIORITY_URGENT_DISPLAY,
 
+    /* all normal video threads */
+    ANDROID_PRIORITY_VIDEO          = -10,
+
     /* all normal audio threads */
     ANDROID_PRIORITY_AUDIO          = -16,
 
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 00b1ee2..7f92904 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -501,6 +501,8 @@
         asprintf(&mParams[0], "INTERFACE=%s", ifname);
         asprintf(&mParams[1], "LIFETIME=%u", lifetime);
         mParams[2] = buf;
+    } else if (opthdr->nd_opt_type == ND_OPT_DNSSL) {
+        // TODO: support DNSSL.
     } else {
         SLOGD("Unknown ND option type %d\n", opthdr->nd_opt_type);
         return false;
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index c885c3f..4125b12 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -48,8 +48,7 @@
     srcs: [
         "ArmExidx.cpp",
         "DwarfCfa.cpp",
-        "DwarfDebugFrame.cpp",
-        "DwarfEhFrame.cpp",
+        "DwarfEhFrameWithHdr.cpp",
         "DwarfMemory.cpp",
         "DwarfOp.cpp",
         "DwarfSection.cpp",
@@ -61,6 +60,10 @@
         "Maps.cpp",
         "Memory.cpp",
         "Regs.cpp",
+        "RegsArm.cpp",
+        "RegsArm64.cpp",
+        "RegsX86.cpp",
+        "RegsX86_64.cpp",
         "Unwinder.cpp",
         "Symbols.cpp",
     ],
@@ -106,6 +109,7 @@
         "tests/DwarfCfaTest.cpp",
         "tests/DwarfDebugFrameTest.cpp",
         "tests/DwarfEhFrameTest.cpp",
+        "tests/DwarfEhFrameWithHdrTest.cpp",
         "tests/DwarfMemoryTest.cpp",
         "tests/DwarfOpLogTest.cpp",
         "tests/DwarfOpTest.cpp",
@@ -118,6 +122,7 @@
         "tests/ElfTestUtils.cpp",
         "tests/LogFake.cpp",
         "tests/MapInfoGetElfTest.cpp",
+        "tests/MapInfoGetLoadBiasTest.cpp",
         "tests/MapsTest.cpp",
         "tests/MemoryBufferTest.cpp",
         "tests/MemoryFake.cpp",
@@ -130,6 +135,7 @@
         "tests/RegsStepIfSignalHandlerTest.cpp",
         "tests/RegsTest.cpp",
         "tests/SymbolsTest.cpp",
+        "tests/UnwindOfflineTest.cpp",
         "tests/UnwindTest.cpp",
         "tests/UnwinderTest.cpp",
     ],
@@ -153,6 +159,8 @@
     data: [
         "tests/files/elf32.xz",
         "tests/files/elf64.xz",
+        "tests/files/offline/straddle_arm32/*",
+        "tests/files/offline/straddle_arm64/*",
     ],
 }
 
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index fed3e0e..6b0646f 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -23,11 +23,11 @@
 
 #include <unwindstack/Log.h>
 #include <unwindstack/Memory.h>
-#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
 
 #include "ArmExidx.h"
 #include "Check.h"
-#include "Machine.h"
+#include "MachineArm.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp
deleted file mode 100644
index 5707596..0000000
--- a/libunwindstack/DwarfDebugFrame.cpp
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-
-#include <algorithm>
-
-#include <unwindstack/DwarfStructs.h>
-#include <unwindstack/Memory.h>
-
-#include "DwarfDebugFrame.h"
-#include "DwarfEncoding.h"
-#include "DwarfError.h"
-
-namespace unwindstack {
-
-template <typename AddressType>
-bool DwarfDebugFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
-  offset_ = offset;
-  end_offset_ = offset + size;
-
-  memory_.clear_func_offset();
-  memory_.clear_text_offset();
-  memory_.set_data_offset(offset);
-  memory_.set_cur_offset(offset);
-
-  return CreateSortedFdeList();
-}
-
-template <typename AddressType>
-bool DwarfDebugFrame<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
-  uint8_t version;
-  if (!memory_.ReadBytes(&version, 1)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
-    return false;
-  }
-  // Read the augmentation string.
-  std::vector<char> aug_string;
-  char aug_value;
-  bool get_encoding = false;
-  do {
-    if (!memory_.ReadBytes(&aug_value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-    if (aug_value == 'R') {
-      get_encoding = true;
-    }
-    aug_string.push_back(aug_value);
-  } while (aug_value != '\0');
-
-  if (version == 4) {
-    // Skip the Address Size field.
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-
-    // Read the segment size.
-    if (!memory_.ReadBytes(segment_size, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-  } else {
-    *segment_size = 0;
-  }
-
-  if (aug_string[0] != 'z' || !get_encoding) {
-    // No encoding
-    return true;
-  }
-
-  // Skip code alignment factor
-  uint8_t value;
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-  } while (value & 0x80);
-
-  // Skip data alignment factor
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-  } while (value & 0x80);
-
-  if (version == 1) {
-    // Skip return address register.
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-  } else {
-    // Skip return address register.
-    do {
-      if (!memory_.ReadBytes(&value, 1)) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-    } while (value & 0x80);
-  }
-
-  // Skip the augmentation length.
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-  } while (value & 0x80);
-
-  for (size_t i = 1; i < aug_string.size(); i++) {
-    if (aug_string[i] == 'R') {
-      if (!memory_.ReadBytes(encoding, 1)) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-      // Got the encoding, that's all we are looking for.
-      return true;
-    } else if (aug_string[i] == 'L') {
-      memory_.set_cur_offset(memory_.cur_offset() + 1);
-    } else if (aug_string[i] == 'P') {
-      uint8_t encoding;
-      if (!memory_.ReadBytes(&encoding, 1)) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-      uint64_t value;
-      if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-    }
-  }
-
-  // It should be impossible to get here.
-  abort();
-}
-
-template <typename AddressType>
-bool DwarfDebugFrame<AddressType>::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size,
-                                              uint8_t encoding) {
-  if (segment_size != 0) {
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-  }
-
-  uint64_t start;
-  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
-    return false;
-  }
-
-  uint64_t length;
-  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
-    return false;
-  }
-  if (length != 0) {
-    fdes_.emplace_back(entry_offset, start, length);
-  }
-
-  return true;
-}
-
-template <typename AddressType>
-bool DwarfDebugFrame<AddressType>::CreateSortedFdeList() {
-  memory_.set_cur_offset(offset_);
-
-  // Loop through all of the entries and read just enough to create
-  // a sorted list of pcs.
-  // This code assumes that first comes the cie, then the fdes that
-  // it applies to.
-  uint64_t cie_offset = 0;
-  uint8_t address_encoding;
-  uint8_t segment_size;
-  while (memory_.cur_offset() < end_offset_) {
-    uint64_t cur_entry_offset = memory_.cur_offset();
-
-    // Figure out the entry length and type.
-    uint32_t value32;
-    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-
-    uint64_t next_entry_offset;
-    if (value32 == static_cast<uint32_t>(-1)) {
-      uint64_t value64;
-      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-      next_entry_offset = memory_.cur_offset() + value64;
-
-      // Read the Cie Id of a Cie or the pointer of the Fde.
-      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-
-      if (value64 == static_cast<uint64_t>(-1)) {
-        // Cie 64 bit
-        address_encoding = DW_EH_PE_sdata8;
-        if (!GetCieInfo(&segment_size, &address_encoding)) {
-          return false;
-        }
-        cie_offset = cur_entry_offset;
-      } else {
-        if (offset_ + value64 != cie_offset) {
-          // This means that this Fde is not following the Cie.
-          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
-          return false;
-        }
-
-        // Fde 64 bit
-        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
-          return false;
-        }
-      }
-    } else {
-      next_entry_offset = memory_.cur_offset() + value32;
-
-      // Read the Cie Id of a Cie or the pointer of the Fde.
-      if (!memory_.ReadBytes(&value32, sizeof(value32))) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-
-      if (value32 == static_cast<uint32_t>(-1)) {
-        // Cie 32 bit
-        address_encoding = DW_EH_PE_sdata4;
-        if (!GetCieInfo(&segment_size, &address_encoding)) {
-          return false;
-        }
-        cie_offset = cur_entry_offset;
-      } else {
-        if (offset_ + value32 != cie_offset) {
-          // This means that this Fde is not following the Cie.
-          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
-          return false;
-        }
-
-        // Fde 32 bit
-        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
-          return false;
-        }
-      }
-    }
-
-    if (next_entry_offset < memory_.cur_offset()) {
-      // This indicates some kind of corruption, or malformed section data.
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-    memory_.set_cur_offset(next_entry_offset);
-  }
-
-  // Sort the entries.
-  std::sort(fdes_.begin(), fdes_.end(), [](const FdeInfo& a, const FdeInfo& b) {
-    if (a.start == b.start) return a.end < b.end;
-    return a.start < b.start;
-  });
-
-  fde_count_ = fdes_.size();
-
-  return true;
-}
-
-template <typename AddressType>
-bool DwarfDebugFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
-  if (fde_count_ == 0) {
-    return false;
-  }
-
-  size_t first = 0;
-  size_t last = fde_count_;
-  while (first < last) {
-    size_t current = (first + last) / 2;
-    const FdeInfo* info = &fdes_[current];
-    if (pc >= info->start && pc <= info->end) {
-      *fde_offset = info->offset;
-      return true;
-    }
-
-    if (pc < info->start) {
-      last = current;
-    } else {
-      first = current + 1;
-    }
-  }
-  return false;
-}
-
-template <typename AddressType>
-const DwarfFde* DwarfDebugFrame<AddressType>::GetFdeFromIndex(size_t index) {
-  if (index >= fdes_.size()) {
-    return nullptr;
-  }
-  return this->GetFdeFromOffset(fdes_[index].offset);
-}
-
-// Explicitly instantiate DwarfDebugFrame.
-template class DwarfDebugFrame<uint32_t>;
-template class DwarfDebugFrame<uint64_t>;
-
-}  // namespace unwindstack
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
index 6a6178e..635cefd 100644
--- a/libunwindstack/DwarfDebugFrame.h
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -28,51 +28,21 @@
 template <typename AddressType>
 class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
  public:
-  // Add these so that the protected members of DwarfSectionImpl
-  // can be accessed without needing a this->.
-  using DwarfSectionImpl<AddressType>::memory_;
-  using DwarfSectionImpl<AddressType>::fde_count_;
-  using DwarfSectionImpl<AddressType>::last_error_;
-
-  struct FdeInfo {
-    FdeInfo(uint64_t offset, uint64_t start, uint64_t length)
-        : offset(offset), start(start), end(start + length) {}
-
-    uint64_t offset;
-    AddressType start;
-    AddressType end;
-  };
-
-  DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {
+    this->cie32_value_ = static_cast<uint32_t>(-1);
+    this->cie64_value_ = static_cast<uint64_t>(-1);
+  }
   virtual ~DwarfDebugFrame() = default;
 
-  bool Init(uint64_t offset, uint64_t size) override;
+  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+    return this->entries_offset_ + pointer;
+  }
 
-  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
-
-  const DwarfFde* GetFdeFromIndex(size_t index) override;
-
-  bool IsCie32(uint32_t value32) override { return value32 == static_cast<uint32_t>(-1); }
-
-  bool IsCie64(uint64_t value64) override { return value64 == static_cast<uint64_t>(-1); }
-
-  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override { return offset_ + pointer; }
-
-  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override { return offset_ + pointer; }
+  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+    return this->entries_offset_ + pointer;
+  }
 
   uint64_t AdjustPcFromFde(uint64_t pc) override { return pc; }
-
-  bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
-
-  bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding);
-
-  bool CreateSortedFdeList();
-
- protected:
-  uint64_t offset_;
-  uint64_t end_offset_;
-
-  std::vector<FdeInfo> fdes_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
index 4207b42..2589c89 100644
--- a/libunwindstack/DwarfEhFrame.h
+++ b/libunwindstack/DwarfEhFrame.h
@@ -20,74 +20,30 @@
 #include <stdint.h>
 
 #include <unwindstack/DwarfSection.h>
+#include <unwindstack/Memory.h>
 
 namespace unwindstack {
 
-// Forward declarations.
-class Memory;
-
 template <typename AddressType>
 class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
  public:
-  // Add these so that the protected members of DwarfSectionImpl
-  // can be accessed without needing a this->.
-  using DwarfSectionImpl<AddressType>::memory_;
-  using DwarfSectionImpl<AddressType>::fde_count_;
-  using DwarfSectionImpl<AddressType>::last_error_;
-
-  struct FdeInfo {
-    AddressType pc;
-    uint64_t offset;
-  };
-
   DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
   virtual ~DwarfEhFrame() = default;
 
-  bool Init(uint64_t offset, uint64_t size) override;
-
-  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
-
-  const DwarfFde* GetFdeFromIndex(size_t index) override;
-
-  bool IsCie32(uint32_t value32) override { return value32 == 0; }
-
-  bool IsCie64(uint64_t value64) override { return value64 == 0; }
-
   uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
-    return memory_.cur_offset() - pointer - 4;
+    return this->memory_.cur_offset() - pointer - 4;
   }
 
   uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
-    return memory_.cur_offset() - pointer - 8;
+    return this->memory_.cur_offset() - pointer - 8;
   }
 
   uint64_t AdjustPcFromFde(uint64_t pc) override {
     // The eh_frame uses relative pcs.
-    return pc + memory_.cur_offset();
+    return pc + this->memory_.cur_offset() - 4;
   }
-
-  const FdeInfo* GetFdeInfoFromIndex(size_t index);
-
-  bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);
-
-  bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
-
- protected:
-  uint8_t version_;
-  uint8_t ptr_encoding_;
-  uint8_t table_encoding_;
-  size_t table_entry_size_;
-
-  uint64_t ptr_offset_;
-
-  uint64_t entries_offset_;
-  uint64_t entries_end_;
-  uint64_t entries_data_offset_;
-  uint64_t cur_entries_offset_ = 0;
-
-  std::unordered_map<uint64_t, FdeInfo> fde_info_;
 };
 
 }  // namespace unwindstack
 
-#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
similarity index 85%
rename from libunwindstack/DwarfEhFrame.cpp
rename to libunwindstack/DwarfEhFrameWithHdr.cpp
index db8f558..0337dba 100644
--- a/libunwindstack/DwarfEhFrame.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -20,13 +20,13 @@
 #include <unwindstack/Memory.h>
 
 #include "Check.h"
-#include "DwarfEhFrame.h"
+#include "DwarfEhFrameWithHdr.h"
 #include "DwarfError.h"
 
 namespace unwindstack {
 
 template <typename AddressType>
-bool DwarfEhFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size) {
   uint8_t data[4];
 
   memory_.clear_func_offset();
@@ -73,7 +73,7 @@
 }
 
 template <typename AddressType>
-const DwarfFde* DwarfEhFrame<AddressType>::GetFdeFromIndex(size_t index) {
+const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromIndex(size_t index) {
   const FdeInfo* info = GetFdeInfoFromIndex(index);
   if (info == nullptr) {
     return nullptr;
@@ -82,8 +82,8 @@
 }
 
 template <typename AddressType>
-const typename DwarfEhFrame<AddressType>::FdeInfo* DwarfEhFrame<AddressType>::GetFdeInfoFromIndex(
-    size_t index) {
+const typename DwarfEhFrameWithHdr<AddressType>::FdeInfo*
+DwarfEhFrameWithHdr<AddressType>::GetFdeInfoFromIndex(size_t index) {
   auto entry = fde_info_.find(index);
   if (entry != fde_info_.end()) {
     return &fde_info_[index];
@@ -105,8 +105,8 @@
 }
 
 template <typename AddressType>
-bool DwarfEhFrame<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
-                                                   uint64_t total_entries) {
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
+                                                          uint64_t total_entries) {
   CHECK(fde_count_ > 0);
   CHECK(total_entries <= fde_count_);
 
@@ -115,6 +115,9 @@
   while (first < last) {
     size_t current = (first + last) / 2;
     const FdeInfo* info = GetFdeInfoFromIndex(current);
+    if (info == nullptr) {
+      return false;
+    }
     if (pc == info->pc) {
       *fde_offset = info->offset;
       return true;
@@ -127,6 +130,9 @@
   }
   if (last != 0) {
     const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
+    if (info == nullptr) {
+      return false;
+    }
     *fde_offset = info->offset;
     return true;
   }
@@ -134,7 +140,7 @@
 }
 
 template <typename AddressType>
-bool DwarfEhFrame<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
   CHECK(fde_count_ != 0);
   last_error_ = DWARF_ERROR_NONE;
   // We can do a binary search if the pc is in the range of the elements
@@ -196,7 +202,7 @@
 }
 
 template <typename AddressType>
-bool DwarfEhFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
   if (fde_count_ == 0) {
     return false;
   }
@@ -210,8 +216,8 @@
   }
 }
 
-// Explicitly instantiate DwarfEhFrame.
-template class DwarfEhFrame<uint32_t>;
-template class DwarfEhFrame<uint64_t>;
+// Explicitly instantiate DwarfEhFrameWithHdr
+template class DwarfEhFrameWithHdr<uint32_t>;
+template class DwarfEhFrameWithHdr<uint64_t>;
 
 }  // namespace unwindstack
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
new file mode 100644
index 0000000..3571166
--- /dev/null
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
+#define _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
+
+#include <stdint.h>
+
+#include <unordered_map>
+
+#include "DwarfEhFrame.h"
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+template <typename AddressType>
+class DwarfEhFrameWithHdr : public DwarfEhFrame<AddressType> {
+ public:
+  // Add these so that the protected members of DwarfSectionImpl
+  // can be accessed without needing a this->.
+  using DwarfSectionImpl<AddressType>::memory_;
+  using DwarfSectionImpl<AddressType>::fde_count_;
+  using DwarfSectionImpl<AddressType>::entries_offset_;
+  using DwarfSectionImpl<AddressType>::entries_end_;
+  using DwarfSectionImpl<AddressType>::last_error_;
+
+  struct FdeInfo {
+    AddressType pc;
+    uint64_t offset;
+  };
+
+  DwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrame<AddressType>(memory) {}
+  virtual ~DwarfEhFrameWithHdr() = default;
+
+  bool Init(uint64_t offset, uint64_t size) override;
+
+  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+  const DwarfFde* GetFdeFromIndex(size_t index) override;
+
+  const FdeInfo* GetFdeInfoFromIndex(size_t index);
+
+  bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);
+
+  bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
+
+ protected:
+  uint8_t version_;
+  uint8_t ptr_encoding_;
+  uint8_t table_encoding_;
+  size_t table_entry_size_;
+
+  uint64_t ptr_offset_;
+
+  uint64_t entries_data_offset_;
+  uint64_t cur_entries_offset_ = 0;
+
+  std::unordered_map<uint64_t, FdeInfo> fde_info_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
index 901f492..6ffdc0d 100644
--- a/libunwindstack/DwarfMemory.cpp
+++ b/libunwindstack/DwarfMemory.cpp
@@ -27,7 +27,7 @@
 namespace unwindstack {
 
 bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) {
-  if (!memory_->Read(cur_offset_, dst, num_bytes)) {
+  if (!memory_->ReadFully(cur_offset_, dst, num_bytes)) {
     return false;
   }
   cur_offset_ += num_bytes;
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
index b3fd0df..3b3d340 100644
--- a/libunwindstack/DwarfOp.cpp
+++ b/libunwindstack/DwarfOp.cpp
@@ -141,7 +141,7 @@
   // Read the address and dereference it.
   AddressType addr = StackPop();
   AddressType value;
-  if (!regular_memory()->Read(addr, &value, sizeof(value))) {
+  if (!regular_memory()->ReadFully(addr, &value, sizeof(value))) {
     last_error_ = DWARF_ERROR_MEMORY_INVALID;
     return false;
   }
@@ -159,7 +159,7 @@
   // Read the address and dereference it.
   AddressType addr = StackPop();
   AddressType value = 0;
-  if (!regular_memory()->Read(addr, &value, bytes_to_read)) {
+  if (!regular_memory()->ReadFully(addr, &value, bytes_to_read)) {
     last_error_ = DWARF_ERROR_MEMORY_INVALID;
     return false;
   }
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 91aef80..0954187 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -29,6 +29,9 @@
 #include "DwarfError.h"
 #include "DwarfOp.h"
 
+#include "DwarfDebugFrame.h"
+#include "DwarfEhFrame.h"
+
 namespace unwindstack {
 
 DwarfSection::DwarfSection(Memory* memory) : memory_(memory), last_error_(DWARF_ERROR_NONE) {}
@@ -139,7 +142,7 @@
         return false;
       }
       if (loc->type == DWARF_LOCATION_EXPRESSION) {
-        if (!regular_memory->Read(value, &cfa, sizeof(AddressType))) {
+        if (!regular_memory->ReadFully(value, &cfa, sizeof(AddressType))) {
           last_error_ = DWARF_ERROR_MEMORY_INVALID;
           return false;
         }
@@ -172,7 +175,8 @@
     const DwarfLocation* loc = &entry.second;
     switch (loc->type) {
       case DWARF_LOCATION_OFFSET:
-        if (!regular_memory->Read(cfa + loc->values[0], &(*cur_regs)[reg], sizeof(AddressType))) {
+        if (!regular_memory->ReadFully(cfa + loc->values[0], &(*cur_regs)[reg],
+                                       sizeof(AddressType))) {
           last_error_ = DWARF_ERROR_MEMORY_INVALID;
           return false;
         }
@@ -207,7 +211,7 @@
           return false;
         }
         if (loc->type == DWARF_LOCATION_EXPRESSION) {
-          if (!regular_memory->Read(value, &(*cur_regs)[reg], sizeof(AddressType))) {
+          if (!regular_memory->ReadFully(value, &(*cur_regs)[reg], sizeof(AddressType))) {
             last_error_ = DWARF_ERROR_MEMORY_INVALID;
             return false;
           }
@@ -282,7 +286,7 @@
       last_error_ = DWARF_ERROR_MEMORY_INVALID;
       return false;
     }
-    if (!IsCie64(cie_id)) {
+    if (cie_id != cie64_value_) {
       // This is not a Cie, something has gone horribly wrong.
       last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
@@ -297,7 +301,7 @@
       last_error_ = DWARF_ERROR_MEMORY_INVALID;
       return false;
     }
-    if (!IsCie32(cie_id)) {
+    if (cie_id != cie32_value_) {
       // This is not a Cie, something has gone horribly wrong.
       last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
@@ -440,7 +444,7 @@
       last_error_ = DWARF_ERROR_MEMORY_INVALID;
       return false;
     }
-    if (IsCie64(value64)) {
+    if (value64 == cie64_value_) {
       // This is a Cie, this means something has gone wrong.
       last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
@@ -458,7 +462,7 @@
       last_error_ = DWARF_ERROR_MEMORY_INVALID;
       return false;
     }
-    if (IsCie32(value32)) {
+    if (value32 == cie32_value_) {
       // This is a Cie, this means something has gone wrong.
       last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
@@ -556,8 +560,301 @@
   return true;
 }
 
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size) {
+  entries_offset_ = offset;
+  entries_end_ = offset + size;
+
+  memory_.clear_func_offset();
+  memory_.clear_text_offset();
+  memory_.set_data_offset(offset);
+  memory_.set_cur_offset(offset);
+  memory_.set_pc_offset(offset);
+
+  return CreateSortedFdeList();
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
+  uint8_t version;
+  if (!memory_.ReadBytes(&version, 1)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  // Read the augmentation string.
+  std::vector<char> aug_string;
+  char aug_value;
+  bool get_encoding = false;
+  do {
+    if (!memory_.ReadBytes(&aug_value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (aug_value == 'R') {
+      get_encoding = true;
+    }
+    aug_string.push_back(aug_value);
+  } while (aug_value != '\0');
+
+  if (version == 4) {
+    // Skip the Address Size field.
+    memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+    // Read the segment size.
+    if (!memory_.ReadBytes(segment_size, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+  } else {
+    *segment_size = 0;
+  }
+
+  if (aug_string[0] != 'z' || !get_encoding) {
+    // No encoding
+    return true;
+  }
+
+  // Skip code alignment factor
+  uint8_t value;
+  do {
+    if (!memory_.ReadBytes(&value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+  } while (value & 0x80);
+
+  // Skip data alignment factor
+  do {
+    if (!memory_.ReadBytes(&value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+  } while (value & 0x80);
+
+  if (version == 1) {
+    // Skip return address register.
+    memory_.set_cur_offset(memory_.cur_offset() + 1);
+  } else {
+    // Skip return address register.
+    do {
+      if (!memory_.ReadBytes(&value, 1)) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+    } while (value & 0x80);
+  }
+
+  // Skip the augmentation length.
+  do {
+    if (!memory_.ReadBytes(&value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+  } while (value & 0x80);
+
+  for (size_t i = 1; i < aug_string.size(); i++) {
+    if (aug_string[i] == 'R') {
+      if (!memory_.ReadBytes(encoding, 1)) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+      // Got the encoding, that's all we are looking for.
+      return true;
+    } else if (aug_string[i] == 'L') {
+      memory_.set_cur_offset(memory_.cur_offset() + 1);
+    } else if (aug_string[i] == 'P') {
+      uint8_t encoding;
+      if (!memory_.ReadBytes(&encoding, 1)) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+      uint64_t value;
+      if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+    }
+  }
+
+  // It should be impossible to get here.
+  abort();
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size,
+                                               uint8_t encoding) {
+  if (segment_size != 0) {
+    memory_.set_cur_offset(memory_.cur_offset() + 1);
+  }
+
+  uint64_t start;
+  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  start = AdjustPcFromFde(start);
+
+  uint64_t length;
+  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  if (length != 0) {
+    fdes_.emplace_back(entry_offset, start, length);
+  }
+
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::CreateSortedFdeList() {
+  memory_.set_cur_offset(entries_offset_);
+
+  // Loop through all of the entries and read just enough to create
+  // a sorted list of pcs.
+  // This code assumes that first comes the cie, then the fdes that
+  // it applies to.
+  uint64_t cie_offset = 0;
+  uint8_t address_encoding;
+  uint8_t segment_size;
+  while (memory_.cur_offset() < entries_end_) {
+    uint64_t cur_entry_offset = memory_.cur_offset();
+
+    // Figure out the entry length and type.
+    uint32_t value32;
+    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+
+    uint64_t next_entry_offset;
+    if (value32 == static_cast<uint32_t>(-1)) {
+      uint64_t value64;
+      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+      next_entry_offset = memory_.cur_offset() + value64;
+
+      // Read the Cie Id of a Cie or the pointer of the Fde.
+      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+
+      if (value64 == cie64_value_) {
+        // Cie 64 bit
+        address_encoding = DW_EH_PE_sdata8;
+        if (!GetCieInfo(&segment_size, &address_encoding)) {
+          return false;
+        }
+        cie_offset = cur_entry_offset;
+      } else {
+        uint64_t last_cie_offset = GetCieOffsetFromFde64(value64);
+        if (last_cie_offset != cie_offset) {
+          // This means that this Fde is not following the Cie.
+          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          return false;
+        }
+
+        // Fde 64 bit
+        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
+          return false;
+        }
+      }
+    } else {
+      next_entry_offset = memory_.cur_offset() + value32;
+
+      // Read the Cie Id of a Cie or the pointer of the Fde.
+      if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+
+      if (value32 == cie32_value_) {
+        // Cie 32 bit
+        address_encoding = DW_EH_PE_sdata4;
+        if (!GetCieInfo(&segment_size, &address_encoding)) {
+          return false;
+        }
+        cie_offset = cur_entry_offset;
+      } else {
+        uint64_t last_cie_offset = GetCieOffsetFromFde32(value32);
+        if (last_cie_offset != cie_offset) {
+          // This means that this Fde is not following the Cie.
+          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          return false;
+        }
+
+        // Fde 32 bit
+        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
+          return false;
+        }
+      }
+    }
+
+    if (next_entry_offset < memory_.cur_offset()) {
+      // This indicates some kind of corruption, or malformed section data.
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+    memory_.set_cur_offset(next_entry_offset);
+  }
+
+  // Sort the entries.
+  std::sort(fdes_.begin(), fdes_.end(), [](const FdeInfo& a, const FdeInfo& b) {
+    if (a.start == b.start) return a.end < b.end;
+    return a.start < b.start;
+  });
+
+  fde_count_ = fdes_.size();
+
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+  if (fde_count_ == 0) {
+    return false;
+  }
+
+  size_t first = 0;
+  size_t last = fde_count_;
+  while (first < last) {
+    size_t current = (first + last) / 2;
+    const FdeInfo* info = &fdes_[current];
+    if (pc >= info->start && pc <= info->end) {
+      *fde_offset = info->offset;
+      return true;
+    }
+
+    if (pc < info->start) {
+      last = current;
+    } else {
+      first = current + 1;
+    }
+  }
+  return false;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromIndex(size_t index) {
+  if (index >= fdes_.size()) {
+    return nullptr;
+  }
+  return this->GetFdeFromOffset(fdes_[index].offset);
+}
+
 // Explicitly instantiate DwarfSectionImpl
 template class DwarfSectionImpl<uint32_t>;
 template class DwarfSectionImpl<uint64_t>;
 
+// Explicitly instantiate DwarfDebugFrame
+template class DwarfDebugFrame<uint32_t>;
+template class DwarfDebugFrame<uint64_t>;
+
+// Explicitly instantiate DwarfEhFrame
+template class DwarfEhFrame<uint32_t>;
+template class DwarfEhFrame<uint64_t>;
+
 }  // namespace unwindstack
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index edf7ac2..025429f 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -18,6 +18,7 @@
 #include <string.h>
 
 #include <memory>
+#include <mutex>
 #include <string>
 
 #define LOG_TAG "unwind"
@@ -30,7 +31,6 @@
 #include <unwindstack/Regs.h>
 
 #include "ElfInterfaceArm.h"
-#include "Machine.h"
 #include "Symbols.h"
 
 namespace unwindstack {
@@ -87,6 +87,7 @@
 }
 
 bool Elf::GetSoname(std::string* name) {
+  std::lock_guard<std::mutex> guard(lock_);
   return valid_ && interface_->GetSoname(name);
 }
 
@@ -95,14 +96,15 @@
 }
 
 bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
+  std::lock_guard<std::mutex> guard(lock_);
   return valid_ && (interface_->GetFunctionName(addr, load_bias_, name, func_offset) ||
                     (gnu_debugdata_interface_ && gnu_debugdata_interface_->GetFunctionName(
                                                      addr, load_bias_, name, func_offset)));
 }
 
 // The relative pc is always relative to the start of the map from which it comes.
-bool Elf::Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory,
-               bool* finished) {
+bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
+               Memory* process_memory, bool* finished) {
   if (!valid_) {
     return false;
   }
@@ -114,14 +116,16 @@
   }
 
   // Adjust the load bias to get the real relative pc.
-  if (rel_pc < load_bias_) {
+  if (adjusted_rel_pc < load_bias_) {
     return false;
   }
-  rel_pc -= load_bias_;
+  adjusted_rel_pc -= load_bias_;
 
-  return interface_->Step(rel_pc, regs, process_memory, finished) ||
+  // Lock during the step which can update information in the object.
+  std::lock_guard<std::mutex> guard(lock_);
+  return interface_->Step(adjusted_rel_pc, regs, process_memory, finished) ||
          (gnu_debugdata_interface_ &&
-          gnu_debugdata_interface_->Step(rel_pc, regs, process_memory, finished));
+          gnu_debugdata_interface_->Step(adjusted_rel_pc, regs, process_memory, finished));
 }
 
 bool Elf::IsValidElf(Memory* memory) {
@@ -131,7 +135,7 @@
 
   // Verify that this is a valid elf file.
   uint8_t e_ident[SELFMAG + 1];
-  if (!memory->Read(0, e_ident, SELFMAG)) {
+  if (!memory->ReadFully(0, e_ident, SELFMAG)) {
     return false;
   }
 
@@ -151,7 +155,7 @@
 
   // Now read the section header information.
   uint8_t class_type;
-  if (!memory->Read(EI_CLASS, &class_type, 1)) {
+  if (!memory->ReadFully(EI_CLASS, &class_type, 1)) {
     return;
   }
   if (class_type == ELFCLASS32) {
@@ -169,45 +173,65 @@
   }
 
   std::unique_ptr<ElfInterface> interface;
-  if (!memory->Read(EI_CLASS, &class_type_, 1)) {
+  if (!memory->ReadFully(EI_CLASS, &class_type_, 1)) {
     return nullptr;
   }
   if (class_type_ == ELFCLASS32) {
     Elf32_Half e_machine;
-    if (!memory->Read(EI_NIDENT + sizeof(Elf32_Half), &e_machine, sizeof(e_machine))) {
-      return nullptr;
-    }
-
-    if (e_machine != EM_ARM && e_machine != EM_386) {
-      // Unsupported.
-      ALOGI("32 bit elf that is neither arm nor x86: e_machine = %d\n", e_machine);
+    if (!memory->ReadFully(EI_NIDENT + sizeof(Elf32_Half), &e_machine, sizeof(e_machine))) {
       return nullptr;
     }
 
     machine_type_ = e_machine;
     if (e_machine == EM_ARM) {
+      arch_ = ARCH_ARM;
       interface.reset(new ElfInterfaceArm(memory));
     } else if (e_machine == EM_386) {
+      arch_ = ARCH_X86;
       interface.reset(new ElfInterface32(memory));
     } else {
+      // Unsupported.
       ALOGI("32 bit elf that is neither arm nor x86: e_machine = %d\n", e_machine);
       return nullptr;
     }
   } else if (class_type_ == ELFCLASS64) {
     Elf64_Half e_machine;
-    if (!memory->Read(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) {
+    if (!memory->ReadFully(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) {
       return nullptr;
     }
-    if (e_machine != EM_AARCH64 && e_machine != EM_X86_64) {
+
+    machine_type_ = e_machine;
+    if (e_machine == EM_AARCH64) {
+      arch_ = ARCH_ARM64;
+    } else if (e_machine == EM_X86_64) {
+      arch_ = ARCH_X86_64;
+    } else {
       // Unsupported.
       ALOGI("64 bit elf that is neither aarch64 nor x86_64: e_machine = %d\n", e_machine);
       return nullptr;
     }
-    machine_type_ = e_machine;
     interface.reset(new ElfInterface64(memory));
   }
 
   return interface.release();
 }
 
+uint64_t Elf::GetLoadBias(Memory* memory) {
+  if (!IsValidElf(memory)) {
+    return 0;
+  }
+
+  uint8_t class_type;
+  if (!memory->Read(EI_CLASS, &class_type, 1)) {
+    return 0;
+  }
+
+  if (class_type == ELFCLASS32) {
+    return ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(memory);
+  } else if (class_type == ELFCLASS64) {
+    return ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(memory);
+  }
+  return 0;
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 20cc1b0..334cf76 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -32,6 +32,7 @@
 
 #include "DwarfDebugFrame.h"
 #include "DwarfEhFrame.h"
+#include "DwarfEhFrameWithHdr.h"
 #include "Symbols.h"
 
 namespace unwindstack {
@@ -52,7 +53,7 @@
   Crc64GenerateTable();
 
   std::vector<uint8_t> src(gnu_debugdata_size_);
-  if (!memory_->Read(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) {
+  if (!memory_->ReadFully(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) {
     gnu_debugdata_offset_ = 0;
     gnu_debugdata_size_ = static_cast<uint64_t>(-1);
     return nullptr;
@@ -98,7 +99,17 @@
 
 template <typename AddressType>
 void ElfInterface::InitHeadersWithTemplate() {
-  if (eh_frame_offset_ != 0) {
+  if (eh_frame_hdr_offset_ != 0) {
+    eh_frame_.reset(new DwarfEhFrameWithHdr<AddressType>(memory_));
+    if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_)) {
+      // Even if the eh_frame_offset_ is non-zero, do not bother
+      // trying to read that since something has gone wrong.
+      eh_frame_.reset(nullptr);
+      eh_frame_hdr_offset_ = 0;
+      eh_frame_hdr_size_ = static_cast<uint64_t>(-1);
+    }
+  } else if (eh_frame_offset_ != 0) {
+    // If there is a eh_frame section without a eh_frame_hdr section.
     eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
     if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) {
       eh_frame_.reset(nullptr);
@@ -120,7 +131,7 @@
 template <typename EhdrType, typename PhdrType, typename ShdrType>
 bool ElfInterface::ReadAllHeaders(uint64_t* load_bias) {
   EhdrType ehdr;
-  if (!memory_->Read(0, &ehdr, sizeof(ehdr))) {
+  if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) {
     return false;
   }
 
@@ -137,6 +148,26 @@
 }
 
 template <typename EhdrType, typename PhdrType>
+uint64_t ElfInterface::GetLoadBias(Memory* memory) {
+  EhdrType ehdr;
+  if (!memory->Read(0, &ehdr, sizeof(ehdr))) {
+    return false;
+  }
+
+  uint64_t offset = ehdr.e_phoff;
+  for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
+    PhdrType phdr;
+    if (!memory->Read(offset, &phdr, sizeof(phdr))) {
+      return 0;
+    }
+    if (phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
+      return phdr.p_vaddr;
+    }
+  }
+  return 0;
+}
+
+template <typename EhdrType, typename PhdrType>
 bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
   uint64_t offset = ehdr.e_phoff;
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
@@ -181,11 +212,12 @@
       if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
         return false;
       }
-      eh_frame_offset_ = phdr.p_offset;
+      // This is really the pointer to the .eh_frame_hdr section.
+      eh_frame_hdr_offset_ = phdr.p_offset;
       if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
         return false;
       }
-      eh_frame_size_ = phdr.p_memsz;
+      eh_frame_hdr_size_ = phdr.p_memsz;
       break;
 
     case PT_DYNAMIC:
@@ -230,7 +262,7 @@
     }
 
     if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
-      if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
+      if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
         return false;
       }
       // Need to go get the information about the section that contains
@@ -271,6 +303,12 @@
           } else if (name == ".gnu_debugdata") {
             offset_ptr = &gnu_debugdata_offset_;
             size_ptr = &gnu_debugdata_size_;
+          } else if (name == ".eh_frame") {
+            offset_ptr = &eh_frame_offset_;
+            size_ptr = &eh_frame_size_;
+          } else if (eh_frame_hdr_offset_ == 0 && name == ".eh_frame_hdr") {
+            offset_ptr = &eh_frame_hdr_offset_;
+            size_ptr = &eh_frame_hdr_size_;
           }
           if (offset_ptr != nullptr &&
               memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
@@ -306,7 +344,7 @@
   uint64_t offset = dynamic_offset_;
   uint64_t max_offset = offset + dynamic_size_;
   for (uint64_t offset = dynamic_offset_; offset < max_offset; offset += sizeof(DynType)) {
-    if (!memory_->Read(offset, &dyn, sizeof(dyn))) {
+    if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
       return false;
     }
 
@@ -370,7 +408,7 @@
 template <typename EhdrType>
 void ElfInterface::GetMaxSizeWithTemplate(Memory* memory, uint64_t* size) {
   EhdrType ehdr;
-  if (!memory->Read(0, &ehdr, sizeof(ehdr))) {
+  if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
     return;
   }
   if (ehdr.e_shnum == 0) {
@@ -403,4 +441,7 @@
 template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
 template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
 
+template uint64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
+template uint64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
+
 }  // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index 170a5cd..9841e24 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -18,11 +18,11 @@
 #include <stdint.h>
 
 #include <unwindstack/Memory.h>
-#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
 
 #include "ArmExidx.h"
 #include "ElfInterfaceArm.h"
-#include "Machine.h"
+#include "MachineArm.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/Machine.h b/libunwindstack/Machine.h
deleted file mode 100644
index 1fb9309..0000000
--- a/libunwindstack/Machine.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBUNWINDSTACK_MACHINE_H
-#define _LIBUNWINDSTACK_MACHINE_H
-
-#include <stdint.h>
-
-namespace unwindstack {
-
-enum ArmReg : uint16_t {
-  ARM_REG_R0 = 0,
-  ARM_REG_R1,
-  ARM_REG_R2,
-  ARM_REG_R3,
-  ARM_REG_R4,
-  ARM_REG_R5,
-  ARM_REG_R6,
-  ARM_REG_R7,
-  ARM_REG_R8,
-  ARM_REG_R9,
-  ARM_REG_R10,
-  ARM_REG_R11,
-  ARM_REG_R12,
-  ARM_REG_R13,
-  ARM_REG_R14,
-  ARM_REG_R15,
-  ARM_REG_LAST,
-
-  ARM_REG_SP = ARM_REG_R13,
-  ARM_REG_LR = ARM_REG_R14,
-  ARM_REG_PC = ARM_REG_R15,
-};
-
-enum Arm64Reg : uint16_t {
-  ARM64_REG_R0 = 0,
-  ARM64_REG_R1,
-  ARM64_REG_R2,
-  ARM64_REG_R3,
-  ARM64_REG_R4,
-  ARM64_REG_R5,
-  ARM64_REG_R6,
-  ARM64_REG_R7,
-  ARM64_REG_R8,
-  ARM64_REG_R9,
-  ARM64_REG_R10,
-  ARM64_REG_R11,
-  ARM64_REG_R12,
-  ARM64_REG_R13,
-  ARM64_REG_R14,
-  ARM64_REG_R15,
-  ARM64_REG_R16,
-  ARM64_REG_R17,
-  ARM64_REG_R18,
-  ARM64_REG_R19,
-  ARM64_REG_R20,
-  ARM64_REG_R21,
-  ARM64_REG_R22,
-  ARM64_REG_R23,
-  ARM64_REG_R24,
-  ARM64_REG_R25,
-  ARM64_REG_R26,
-  ARM64_REG_R27,
-  ARM64_REG_R28,
-  ARM64_REG_R29,
-  ARM64_REG_R30,
-  ARM64_REG_R31,
-  ARM64_REG_PC,
-  ARM64_REG_LAST,
-
-  ARM64_REG_SP = ARM64_REG_R31,
-  ARM64_REG_LR = ARM64_REG_R30,
-};
-
-// Matches the numbers for the registers as generated by compilers.
-// If this is changed, then unwinding will fail.
-enum X86Reg : uint16_t {
-  X86_REG_EAX = 0,
-  X86_REG_ECX = 1,
-  X86_REG_EDX = 2,
-  X86_REG_EBX = 3,
-  X86_REG_ESP = 4,
-  X86_REG_EBP = 5,
-  X86_REG_ESI = 6,
-  X86_REG_EDI = 7,
-  X86_REG_EIP = 8,
-  X86_REG_EFL = 9,
-  X86_REG_CS = 10,
-  X86_REG_SS = 11,
-  X86_REG_DS = 12,
-  X86_REG_ES = 13,
-  X86_REG_FS = 14,
-  X86_REG_GS = 15,
-  X86_REG_LAST,
-
-  X86_REG_SP = X86_REG_ESP,
-  X86_REG_PC = X86_REG_EIP,
-};
-
-// Matches the numbers for the registers as generated by compilers.
-// If this is changed, then unwinding will fail.
-enum X86_64Reg : uint16_t {
-  X86_64_REG_RAX = 0,
-  X86_64_REG_RDX = 1,
-  X86_64_REG_RCX = 2,
-  X86_64_REG_RBX = 3,
-  X86_64_REG_RSI = 4,
-  X86_64_REG_RDI = 5,
-  X86_64_REG_RBP = 6,
-  X86_64_REG_RSP = 7,
-  X86_64_REG_R8 = 8,
-  X86_64_REG_R9 = 9,
-  X86_64_REG_R10 = 10,
-  X86_64_REG_R11 = 11,
-  X86_64_REG_R12 = 12,
-  X86_64_REG_R13 = 13,
-  X86_64_REG_R14 = 14,
-  X86_64_REG_R15 = 15,
-  X86_64_REG_RIP = 16,
-  X86_64_REG_LAST,
-
-  X86_64_REG_SP = X86_64_REG_RSP,
-  X86_64_REG_PC = X86_64_REG_RIP,
-};
-
-}  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_MACHINE_H
diff --git a/libunwindstack/MachineArm.h b/libunwindstack/MachineArm.h
new file mode 100644
index 0000000..3f902b1
--- /dev/null
+++ b/libunwindstack/MachineArm.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MACHINE_ARM_H
+#define _LIBUNWINDSTACK_MACHINE_ARM_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum ArmReg : uint16_t {
+  ARM_REG_R0 = 0,
+  ARM_REG_R1,
+  ARM_REG_R2,
+  ARM_REG_R3,
+  ARM_REG_R4,
+  ARM_REG_R5,
+  ARM_REG_R6,
+  ARM_REG_R7,
+  ARM_REG_R8,
+  ARM_REG_R9,
+  ARM_REG_R10,
+  ARM_REG_R11,
+  ARM_REG_R12,
+  ARM_REG_R13,
+  ARM_REG_R14,
+  ARM_REG_R15,
+  ARM_REG_LAST,
+
+  ARM_REG_SP = ARM_REG_R13,
+  ARM_REG_LR = ARM_REG_R14,
+  ARM_REG_PC = ARM_REG_R15,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MACHINE_ARM_H
diff --git a/libunwindstack/MachineArm64.h b/libunwindstack/MachineArm64.h
new file mode 100644
index 0000000..e8b778b
--- /dev/null
+++ b/libunwindstack/MachineArm64.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MACHINE_ARM64_H
+#define _LIBUNWINDSTACK_MACHINE_ARM64_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum Arm64Reg : uint16_t {
+  ARM64_REG_R0 = 0,
+  ARM64_REG_R1,
+  ARM64_REG_R2,
+  ARM64_REG_R3,
+  ARM64_REG_R4,
+  ARM64_REG_R5,
+  ARM64_REG_R6,
+  ARM64_REG_R7,
+  ARM64_REG_R8,
+  ARM64_REG_R9,
+  ARM64_REG_R10,
+  ARM64_REG_R11,
+  ARM64_REG_R12,
+  ARM64_REG_R13,
+  ARM64_REG_R14,
+  ARM64_REG_R15,
+  ARM64_REG_R16,
+  ARM64_REG_R17,
+  ARM64_REG_R18,
+  ARM64_REG_R19,
+  ARM64_REG_R20,
+  ARM64_REG_R21,
+  ARM64_REG_R22,
+  ARM64_REG_R23,
+  ARM64_REG_R24,
+  ARM64_REG_R25,
+  ARM64_REG_R26,
+  ARM64_REG_R27,
+  ARM64_REG_R28,
+  ARM64_REG_R29,
+  ARM64_REG_R30,
+  ARM64_REG_R31,
+  ARM64_REG_PC,
+  ARM64_REG_LAST,
+
+  ARM64_REG_SP = ARM64_REG_R31,
+  ARM64_REG_LR = ARM64_REG_R30,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MACHINE_ARM64_H
diff --git a/libunwindstack/MachineX86.h b/libunwindstack/MachineX86.h
new file mode 100644
index 0000000..02adb98
--- /dev/null
+++ b/libunwindstack/MachineX86.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MACHINE_X86_H
+#define _LIBUNWINDSTACK_MACHINE_X86_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
+enum X86Reg : uint16_t {
+  X86_REG_EAX = 0,
+  X86_REG_ECX = 1,
+  X86_REG_EDX = 2,
+  X86_REG_EBX = 3,
+  X86_REG_ESP = 4,
+  X86_REG_EBP = 5,
+  X86_REG_ESI = 6,
+  X86_REG_EDI = 7,
+  X86_REG_EIP = 8,
+  X86_REG_EFL = 9,
+  X86_REG_CS = 10,
+  X86_REG_SS = 11,
+  X86_REG_DS = 12,
+  X86_REG_ES = 13,
+  X86_REG_FS = 14,
+  X86_REG_GS = 15,
+  X86_REG_LAST,
+
+  X86_REG_SP = X86_REG_ESP,
+  X86_REG_PC = X86_REG_EIP,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MACHINE_X86_H
diff --git a/libunwindstack/MachineX86_64.h b/libunwindstack/MachineX86_64.h
new file mode 100644
index 0000000..af33fea
--- /dev/null
+++ b/libunwindstack/MachineX86_64.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MACHINE_X86_64_H
+#define _LIBUNWINDSTACK_MACHINE_X86_64_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
+enum X86_64Reg : uint16_t {
+  X86_64_REG_RAX = 0,
+  X86_64_REG_RDX = 1,
+  X86_64_REG_RCX = 2,
+  X86_64_REG_RBX = 3,
+  X86_64_REG_RSI = 4,
+  X86_64_REG_RDI = 5,
+  X86_64_REG_RBP = 6,
+  X86_64_REG_RSP = 7,
+  X86_64_REG_R8 = 8,
+  X86_64_REG_R9 = 9,
+  X86_64_REG_R10 = 10,
+  X86_64_REG_R11 = 11,
+  X86_64_REG_R12 = 12,
+  X86_64_REG_R13 = 13,
+  X86_64_REG_R14 = 14,
+  X86_64_REG_R15 = 15,
+  X86_64_REG_RIP = 16,
+  X86_64_REG_LAST,
+
+  X86_64_REG_SP = X86_64_REG_RSP,
+  X86_64_REG_PC = X86_64_REG_RIP,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MACHINE_X86_64_H
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 140d05a..51bce8e 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -19,6 +19,7 @@
 #include <unistd.h>
 
 #include <memory>
+#include <mutex>
 #include <string>
 
 #include <unwindstack/Elf.h>
@@ -101,10 +102,13 @@
   if (!(flags & PROT_READ)) {
     return nullptr;
   }
-  return new MemoryRange(process_memory, start, end);
+  return new MemoryRange(process_memory, start, end - start, 0);
 }
 
 Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
+  // Make sure no other thread is trying to add the elf to this map.
+  std::lock_guard<std::mutex> guard(mutex_);
+
   if (elf) {
     return elf;
   }
@@ -117,4 +121,23 @@
   return elf;
 }
 
+uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
+  {
+    // Make sure no other thread is trying to add the elf to this map.
+    std::lock_guard<std::mutex> guard(mutex_);
+    if (elf != nullptr) {
+      if (elf->valid()) {
+        return elf->GetLoadBias();
+      } else {
+        return 0;
+      }
+    }
+  }
+
+  // Call lightweight static function that will only read enough of the
+  // elf data to get the load bias.
+  std::unique_ptr<Memory> memory(CreateMemory(process_memory));
+  return Elf::GetLoadBias(memory.get());
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 5e1c0a2..56370c1 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -44,7 +44,7 @@
   size_t last = maps_.size();
   while (first < last) {
     size_t index = (first + last) / 2;
-    MapInfo* cur = &maps_[index];
+    MapInfo* cur = maps_[index];
     if (pc >= cur->start && pc < cur->end) {
       return cur;
     } else if (pc < cur->start) {
@@ -57,22 +57,22 @@
 }
 
 // Assumes that line does not end in '\n'.
-static bool InternalParseLine(const char* line, MapInfo* map_info) {
+static MapInfo* InternalParseLine(const char* line) {
   // Do not use a sscanf implementation since it is not performant.
 
   // Example linux /proc/<pid>/maps lines:
   // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so
   char* str;
   const char* old_str = line;
-  map_info->start = strtoul(old_str, &str, 16);
+  uint64_t start = strtoull(old_str, &str, 16);
   if (old_str == str || *str++ != '-') {
-    return false;
+    return nullptr;
   }
 
   old_str = str;
-  map_info->end = strtoul(old_str, &str, 16);
+  uint64_t end = strtoull(old_str, &str, 16);
   if (old_str == str || !std::isspace(*str++)) {
-    return false;
+    return nullptr;
   }
 
   while (std::isspace(*str)) {
@@ -81,82 +81,81 @@
 
   // Parse permissions data.
   if (*str == '\0') {
-    return false;
+    return nullptr;
   }
-  map_info->flags = 0;
+  uint16_t flags = 0;
   if (*str == 'r') {
-    map_info->flags |= PROT_READ;
+    flags |= PROT_READ;
   } else if (*str != '-') {
-    return false;
+    return nullptr;
   }
   str++;
   if (*str == 'w') {
-    map_info->flags |= PROT_WRITE;
+    flags |= PROT_WRITE;
   } else if (*str != '-') {
-    return false;
+    return nullptr;
   }
   str++;
   if (*str == 'x') {
-    map_info->flags |= PROT_EXEC;
+    flags |= PROT_EXEC;
   } else if (*str != '-') {
-    return false;
+    return nullptr;
   }
   str++;
   if (*str != 'p' && *str != 's') {
-    return false;
+    return nullptr;
   }
   str++;
 
   if (!std::isspace(*str++)) {
-    return false;
+    return nullptr;
   }
 
   old_str = str;
-  map_info->offset = strtoul(old_str, &str, 16);
+  uint64_t offset = strtoull(old_str, &str, 16);
   if (old_str == str || !std::isspace(*str)) {
-    return false;
+    return nullptr;
   }
 
   // Ignore the 00:00 values.
   old_str = str;
-  (void)strtoul(old_str, &str, 16);
+  (void)strtoull(old_str, &str, 16);
   if (old_str == str || *str++ != ':') {
-    return false;
+    return nullptr;
   }
   if (std::isspace(*str)) {
-    return false;
+    return nullptr;
   }
 
   // Skip the inode.
   old_str = str;
-  (void)strtoul(str, &str, 16);
+  (void)strtoull(str, &str, 16);
   if (old_str == str || !std::isspace(*str++)) {
-    return false;
+    return nullptr;
   }
 
   // Skip decimal digit.
   old_str = str;
-  (void)strtoul(old_str, &str, 10);
+  (void)strtoull(old_str, &str, 10);
   if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
-    return false;
+    return nullptr;
   }
 
   while (std::isspace(*str)) {
     str++;
   }
   if (*str == '\0') {
-    map_info->name = str;
-    return true;
+    return new MapInfo(start, end, offset, flags, "");
   }
 
   // Save the name data.
-  map_info->name = str;
+  std::string name(str);
 
   // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
-  if (map_info->name.substr(0, 5) == "/dev/" && map_info->name.substr(5, 7) != "ashmem/") {
-    map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
+  if (name.substr(0, 5) == "/dev/" && name.substr(5, 7) != "ashmem/") {
+    flags |= MAPS_FLAGS_DEVICE_MAP;
   }
-  return true;
+  return new MapInfo(start, end, offset, flags, name);
 }
 
 bool Maps::Parse() {
@@ -187,8 +186,8 @@
       }
       *newline = '\0';
 
-      MapInfo map_info;
-      if (!InternalParseLine(line, &map_info)) {
+      MapInfo* map_info = InternalParseLine(line);
+      if (map_info == nullptr) {
         return_value = false;
         break;
       }
@@ -205,8 +204,7 @@
 
 Maps::~Maps() {
   for (auto& map : maps_) {
-    delete map.elf;
-    map.elf = nullptr;
+    delete map;
   }
 }
 
@@ -222,8 +220,8 @@
       end_of_line++;
     }
 
-    MapInfo map_info;
-    if (!InternalParseLine(line.c_str(), &map_info)) {
+    MapInfo* map_info = InternalParseLine(line.c_str());
+    if (map_info == nullptr) {
       return false;
     }
     maps_.push_back(map_info);
@@ -252,24 +250,27 @@
 
   std::vector<char> name;
   while (true) {
-    MapInfo map_info;
-    ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.start, sizeof(map_info.start)));
+    uint64_t start;
+    ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &start, sizeof(start)));
     if (bytes == 0) {
       break;
     }
-    if (bytes == -1 || bytes != sizeof(map_info.start)) {
+    if (bytes == -1 || bytes != sizeof(start)) {
       return false;
     }
-    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.end, sizeof(map_info.end)));
-    if (bytes == -1 || bytes != sizeof(map_info.end)) {
+    uint64_t end;
+    bytes = TEMP_FAILURE_RETRY(read(fd, &end, sizeof(end)));
+    if (bytes == -1 || bytes != sizeof(end)) {
       return false;
     }
-    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.offset, sizeof(map_info.offset)));
-    if (bytes == -1 || bytes != sizeof(map_info.offset)) {
+    uint64_t offset;
+    bytes = TEMP_FAILURE_RETRY(read(fd, &offset, sizeof(offset)));
+    if (bytes == -1 || bytes != sizeof(offset)) {
       return false;
     }
-    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.flags, sizeof(map_info.flags)));
-    if (bytes == -1 || bytes != sizeof(map_info.flags)) {
+    uint16_t flags;
+    bytes = TEMP_FAILURE_RETRY(read(fd, &flags, sizeof(flags)));
+    if (bytes == -1 || bytes != sizeof(flags)) {
       return false;
     }
     uint16_t len;
@@ -283,9 +284,10 @@
       if (bytes == -1 || bytes != len) {
         return false;
       }
-      map_info.name = std::string(name.data(), len);
+      maps_.push_back(new MapInfo(start, end, offset, flags, std::string(name.data(), len)));
+    } else {
+      maps_.push_back(new MapInfo(start, end, offset, flags, ""));
     }
-    maps_.push_back(map_info);
   }
   return true;
 }
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 32753df..b1b39a0 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -32,14 +32,69 @@
 
 #include "Check.h"
 
+static size_t ProcessVmRead(pid_t pid, void* dst, uint64_t remote_src, size_t len) {
+  struct iovec dst_iov = {
+      .iov_base = dst,
+      .iov_len = len,
+  };
+
+  // Split up the remote read across page boundaries.
+  // From the manpage:
+  //   A partial read/write may result if one of the remote_iov elements points to an invalid
+  //   memory region in the remote process.
+  //
+  //   Partial transfers apply at the granularity of iovec elements.  These system calls won't
+  //   perform a partial transfer that splits a single iovec element.
+  constexpr size_t kMaxIovecs = 64;
+  struct iovec src_iovs[kMaxIovecs];
+  size_t iovecs_used = 0;
+
+  uint64_t cur = remote_src;
+  while (len > 0) {
+    if (iovecs_used == kMaxIovecs) {
+      errno = EINVAL;
+      return 0;
+    }
+
+    // struct iovec uses void* for iov_base.
+    if (cur >= UINTPTR_MAX) {
+      errno = EFAULT;
+      return 0;
+    }
+
+    src_iovs[iovecs_used].iov_base = reinterpret_cast<void*>(cur);
+
+    uintptr_t misalignment = cur & (getpagesize() - 1);
+    size_t iov_len = getpagesize() - misalignment;
+    iov_len = std::min(iov_len, len);
+
+    len -= iov_len;
+    if (__builtin_add_overflow(cur, iov_len, &cur)) {
+      errno = EFAULT;
+      return 0;
+    }
+
+    src_iovs[iovecs_used].iov_len = iov_len;
+    ++iovecs_used;
+  }
+
+  ssize_t rc = process_vm_readv(pid, &dst_iov, 1, src_iovs, iovecs_used, 0);
+  return rc == -1 ? 0 : rc;
+}
+
 namespace unwindstack {
 
+bool Memory::ReadFully(uint64_t addr, void* dst, size_t size) {
+  size_t rc = Read(addr, dst, size);
+  return rc == size;
+}
+
 bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
   string->clear();
   uint64_t bytes_read = 0;
   while (bytes_read < max_read) {
     uint8_t value;
-    if (!Read(addr, &value, sizeof(value))) {
+    if (!ReadFully(addr, &value, sizeof(value))) {
       return false;
     }
     if (value == '\0') {
@@ -59,16 +114,17 @@
   return std::shared_ptr<Memory>(new MemoryRemote(pid));
 }
 
-bool MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
-  uint64_t last_read_byte;
-  if (__builtin_add_overflow(size, addr, &last_read_byte)) {
-    return false;
+size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
+  if (addr >= raw_.size()) {
+    return 0;
   }
-  if (last_read_byte > raw_.size()) {
-    return false;
-  }
-  memcpy(dst, &raw_[addr], size);
-  return true;
+
+  size_t bytes_left = raw_.size() - static_cast<size_t>(addr);
+  const unsigned char* actual_base = static_cast<const unsigned char*>(raw_.data()) + addr;
+  size_t actual_len = std::min(bytes_left, size);
+
+  memcpy(dst, actual_base, actual_len);
+  return actual_len;
 }
 
 uint8_t* MemoryBuffer::GetPtr(size_t offset) {
@@ -129,45 +185,43 @@
   return true;
 }
 
-bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
-  uint64_t max_size;
-  if (__builtin_add_overflow(addr, size, &max_size) || max_size > size_) {
-    return false;
+size_t MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
+  if (addr >= size_) {
+    return 0;
   }
-  memcpy(dst, &data_[addr], size);
-  return true;
+
+  size_t bytes_left = size_ - static_cast<size_t>(addr);
+  const unsigned char* actual_base = static_cast<const unsigned char*>(data_) + addr;
+  size_t actual_len = std::min(bytes_left, size);
+
+  memcpy(dst, actual_base, actual_len);
+  return actual_len;
 }
 
-bool MemoryRemote::PtraceRead(uint64_t addr, long* value) {
-#if !defined(__LP64__)
-  // Cannot read an address greater than 32 bits.
-  if (addr > UINT32_MAX) {
-    return false;
-  }
-#endif
+static bool PtraceReadLong(pid_t pid, uint64_t addr, long* value) {
   // ptrace() returns -1 and sets errno when the operation fails.
   // To disambiguate -1 from a valid result, we clear errno beforehand.
   errno = 0;
-  *value = ptrace(PTRACE_PEEKTEXT, pid_, reinterpret_cast<void*>(addr), nullptr);
+  *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
   if (*value == -1 && errno) {
     return false;
   }
   return true;
 }
 
-bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
+static size_t ReadWithPtrace(pid_t pid, uint64_t addr, void* dst, size_t bytes) {
   // Make sure that there is no overflow.
   uint64_t max_size;
   if (__builtin_add_overflow(addr, bytes, &max_size)) {
-    return false;
+    return 0;
   }
 
   size_t bytes_read = 0;
   long data;
   size_t align_bytes = addr & (sizeof(long) - 1);
   if (align_bytes != 0) {
-    if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) {
-      return false;
+    if (!PtraceReadLong(pid, addr & ~(sizeof(long) - 1), &data)) {
+      return 0;
     }
     size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
     memcpy(dst, reinterpret_cast<uint8_t*>(&data) + align_bytes, copy_bytes);
@@ -178,8 +232,8 @@
   }
 
   for (size_t i = 0; i < bytes / sizeof(long); i++) {
-    if (!PtraceRead(addr, &data)) {
-      return false;
+    if (!PtraceReadLong(pid, addr, &data)) {
+      return bytes_read;
     }
     memcpy(dst, &data, sizeof(long));
     dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long));
@@ -189,85 +243,79 @@
 
   size_t left_over = bytes & (sizeof(long) - 1);
   if (left_over) {
-    if (!PtraceRead(addr, &data)) {
-      return false;
+    if (!PtraceReadLong(pid, addr, &data)) {
+      return bytes_read;
     }
     memcpy(dst, &data, left_over);
     bytes_read += left_over;
   }
-  return true;
+  return bytes_read;
 }
 
-bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
-  // Make sure that there is no overflow.
-  uint64_t max_size;
-  if (__builtin_add_overflow(addr, size, &max_size)) {
-    return false;
+size_t MemoryRemote::Read(uint64_t addr, void* dst, size_t size) {
+#if !defined(__LP64__)
+  // Cannot read an address greater than 32 bits.
+  if (addr > UINT32_MAX) {
+    return 0;
+  }
+#endif
+  return ReadWithPtrace(pid_, addr, dst, size);
+}
+
+size_t MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
+  return ProcessVmRead(getpid(), dst, addr, size);
+}
+
+MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
+                         uint64_t offset)
+    : memory_(memory), begin_(begin), length_(length), offset_(offset) {}
+
+size_t MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
+  if (addr < offset_) {
+    return 0;
   }
 
-  // The process_vm_readv call will not always work on remote
-  // processes, so only use it for reads from the current pid.
-  // Use this method to avoid crashes if an address is invalid since
-  // unwind data could try to access any part of the address space.
-  struct iovec local_io;
-  local_io.iov_base = dst;
-  local_io.iov_len = size;
-
-  struct iovec remote_io;
-  remote_io.iov_base = reinterpret_cast<void*>(static_cast<uintptr_t>(addr));
-  remote_io.iov_len = size;
-
-  ssize_t bytes_read = process_vm_readv(getpid(), &local_io, 1, &remote_io, 1, 0);
-  if (bytes_read == -1) {
-    return false;
+  uint64_t read_offset = addr - offset_;
+  if (read_offset >= length_) {
+    return 0;
   }
-  return static_cast<size_t>(bytes_read) == size;
+
+  uint64_t read_length = std::min(static_cast<uint64_t>(size), length_ - read_offset);
+  uint64_t read_addr;
+  if (__builtin_add_overflow(read_offset, begin_, &read_addr)) {
+    return 0;
+  }
+
+  return memory_->Read(read_addr, dst, read_length);
 }
 
 bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
-  if (!MemoryFileAtOffset::Init(file, offset)) {
+  auto memory_file = std::make_shared<MemoryFileAtOffset>();
+  if (!memory_file->Init(file, offset)) {
     return false;
   }
+
   // The first uint64_t value is the start of memory.
-  if (!MemoryFileAtOffset::Read(0, &start_, sizeof(start_))) {
+  uint64_t start;
+  if (!memory_file->ReadFully(0, &start, sizeof(start))) {
     return false;
   }
-  // Subtract the first 64 bit value from the total size.
-  size_ -= sizeof(start_);
+
+  uint64_t size = memory_file->Size();
+  if (__builtin_sub_overflow(size, sizeof(start), &size)) {
+    return false;
+  }
+
+  memory_ = std::make_unique<MemoryRange>(memory_file, sizeof(start), size, start);
   return true;
 }
 
-bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
-  uint64_t max_size;
-  if (__builtin_add_overflow(addr, size, &max_size)) {
-    return false;
+size_t MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
+  if (!memory_) {
+    return 0;
   }
 
-  uint64_t real_size;
-  if (__builtin_add_overflow(start_, offset_, &real_size) ||
-      __builtin_add_overflow(real_size, size_, &real_size)) {
-    return false;
-  }
-
-  if (addr < start_ || max_size > real_size) {
-    return false;
-  }
-  memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
-  return true;
-}
-
-MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t end)
-    : memory_(memory), begin_(begin), length_(end - begin) {
-  CHECK(end > begin);
-}
-
-bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
-  uint64_t max_read;
-  if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) {
-    return false;
-  }
-  // The check above guarantees that addr + begin_ will not overflow.
-  return memory_->Read(addr + begin_, dst, size);
+  return memory_->Read(addr, dst, size);
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index 36b8e25..29dbf9d 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <elf.h>
 #include <stdint.h>
 #include <sys/ptrace.h>
 #include <sys/uio.h>
@@ -23,315 +22,21 @@
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
-#include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
 
-#include "Check.h"
-#include "Machine.h"
-#include "Ucontext.h"
-#include "User.h"
+#include "UserArm.h"
+#include "UserArm64.h"
+#include "UserX86.h"
+#include "UserX86_64.h"
 
 namespace unwindstack {
 
-RegsArm::RegsArm()
-    : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
-
-uint32_t RegsArm::MachineType() {
-  return EM_ARM;
-}
-
-uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
-  }
-
-  uint64_t load_bias = elf->GetLoadBias();
-  if (rel_pc < load_bias) {
-    return rel_pc;
-  }
-  uint64_t adjusted_rel_pc = rel_pc - load_bias;
-
-  if (adjusted_rel_pc < 5) {
-    return rel_pc;
-  }
-
-  if (adjusted_rel_pc & 1) {
-    // This is a thumb instruction, it could be 2 or 4 bytes.
-    uint32_t value;
-    if (rel_pc < 5 || !elf->memory()->Read(adjusted_rel_pc - 5, &value, sizeof(value)) ||
-        (value & 0xe000f000) != 0xe000f000) {
-      return rel_pc - 2;
-    }
-  }
-  return rel_pc - 4;
-}
-
-void RegsArm::SetFromRaw() {
-  set_pc(regs_[ARM_REG_PC]);
-  set_sp(regs_[ARM_REG_SP]);
-}
-
-bool RegsArm::SetPcFromReturnAddress(Memory*) {
-  if (pc() == regs_[ARM_REG_LR]) {
-    return false;
-  }
-
-  set_pc(regs_[ARM_REG_LR]);
-  return true;
-}
-
-void RegsArm::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
-  fn("r0", regs_[ARM_REG_R0]);
-  fn("r1", regs_[ARM_REG_R1]);
-  fn("r2", regs_[ARM_REG_R2]);
-  fn("r3", regs_[ARM_REG_R3]);
-  fn("r4", regs_[ARM_REG_R4]);
-  fn("r5", regs_[ARM_REG_R5]);
-  fn("r6", regs_[ARM_REG_R6]);
-  fn("r7", regs_[ARM_REG_R7]);
-  fn("r8", regs_[ARM_REG_R8]);
-  fn("r9", regs_[ARM_REG_R9]);
-  fn("r10", regs_[ARM_REG_R10]);
-  fn("r11", regs_[ARM_REG_R11]);
-  fn("ip", regs_[ARM_REG_R12]);
-  fn("sp", regs_[ARM_REG_SP]);
-  fn("lr", regs_[ARM_REG_LR]);
-  fn("pc", regs_[ARM_REG_PC]);
-}
-
-RegsArm64::RegsArm64()
-    : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
-
-uint32_t RegsArm64::MachineType() {
-  return EM_AARCH64;
-}
-
-uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
-  }
-
-  if (rel_pc < 4) {
-    return rel_pc;
-  }
-  return rel_pc - 4;
-}
-
-void RegsArm64::SetFromRaw() {
-  set_pc(regs_[ARM64_REG_PC]);
-  set_sp(regs_[ARM64_REG_SP]);
-}
-
-bool RegsArm64::SetPcFromReturnAddress(Memory*) {
-  if (pc() == regs_[ARM64_REG_LR]) {
-    return false;
-  }
-
-  set_pc(regs_[ARM64_REG_LR]);
-  return true;
-}
-
-void RegsArm64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
-  fn("x0", regs_[ARM64_REG_R0]);
-  fn("x1", regs_[ARM64_REG_R1]);
-  fn("x2", regs_[ARM64_REG_R2]);
-  fn("x3", regs_[ARM64_REG_R3]);
-  fn("x4", regs_[ARM64_REG_R4]);
-  fn("x5", regs_[ARM64_REG_R5]);
-  fn("x6", regs_[ARM64_REG_R6]);
-  fn("x7", regs_[ARM64_REG_R7]);
-  fn("x8", regs_[ARM64_REG_R8]);
-  fn("x9", regs_[ARM64_REG_R9]);
-  fn("x10", regs_[ARM64_REG_R10]);
-  fn("x11", regs_[ARM64_REG_R11]);
-  fn("x12", regs_[ARM64_REG_R12]);
-  fn("x13", regs_[ARM64_REG_R13]);
-  fn("x14", regs_[ARM64_REG_R14]);
-  fn("x15", regs_[ARM64_REG_R15]);
-  fn("x16", regs_[ARM64_REG_R16]);
-  fn("x17", regs_[ARM64_REG_R17]);
-  fn("x18", regs_[ARM64_REG_R18]);
-  fn("x19", regs_[ARM64_REG_R19]);
-  fn("x20", regs_[ARM64_REG_R20]);
-  fn("x21", regs_[ARM64_REG_R21]);
-  fn("x22", regs_[ARM64_REG_R22]);
-  fn("x23", regs_[ARM64_REG_R23]);
-  fn("x24", regs_[ARM64_REG_R24]);
-  fn("x25", regs_[ARM64_REG_R25]);
-  fn("x26", regs_[ARM64_REG_R26]);
-  fn("x27", regs_[ARM64_REG_R27]);
-  fn("x28", regs_[ARM64_REG_R28]);
-  fn("x29", regs_[ARM64_REG_R29]);
-  fn("sp", regs_[ARM64_REG_SP]);
-  fn("lr", regs_[ARM64_REG_LR]);
-  fn("pc", regs_[ARM64_REG_PC]);
-}
-
-RegsX86::RegsX86()
-    : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
-
-uint32_t RegsX86::MachineType() {
-  return EM_386;
-}
-
-uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
-  }
-
-  if (rel_pc == 0) {
-    return 0;
-  }
-  return rel_pc - 1;
-}
-
-void RegsX86::SetFromRaw() {
-  set_pc(regs_[X86_REG_PC]);
-  set_sp(regs_[X86_REG_SP]);
-}
-
-bool RegsX86::SetPcFromReturnAddress(Memory* process_memory) {
-  // Attempt to get the return address from the top of the stack.
-  uint32_t new_pc;
-  if (!process_memory->Read(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
-    return false;
-  }
-
-  set_pc(new_pc);
-  return true;
-}
-
-void RegsX86::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
-  fn("eax", regs_[X86_REG_EAX]);
-  fn("ebx", regs_[X86_REG_EBX]);
-  fn("ecx", regs_[X86_REG_ECX]);
-  fn("edx", regs_[X86_REG_EDX]);
-  fn("ebp", regs_[X86_REG_EBP]);
-  fn("edi", regs_[X86_REG_EDI]);
-  fn("esi", regs_[X86_REG_ESI]);
-  fn("esp", regs_[X86_REG_ESP]);
-  fn("eip", regs_[X86_REG_EIP]);
-}
-
-RegsX86_64::RegsX86_64()
-    : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
-
-uint32_t RegsX86_64::MachineType() {
-  return EM_X86_64;
-}
-
-uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
-  }
-
-  if (rel_pc == 0) {
-    return 0;
-  }
-
-  return rel_pc - 1;
-}
-
-void RegsX86_64::SetFromRaw() {
-  set_pc(regs_[X86_64_REG_PC]);
-  set_sp(regs_[X86_64_REG_SP]);
-}
-
-bool RegsX86_64::SetPcFromReturnAddress(Memory* process_memory) {
-  // Attempt to get the return address from the top of the stack.
-  uint64_t new_pc;
-  if (!process_memory->Read(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
-    return false;
-  }
-
-  set_pc(new_pc);
-  return true;
-}
-
-void RegsX86_64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
-  fn("rax", regs_[X86_64_REG_RAX]);
-  fn("rbx", regs_[X86_64_REG_RBX]);
-  fn("rcx", regs_[X86_64_REG_RCX]);
-  fn("rdx", regs_[X86_64_REG_RDX]);
-  fn("r8", regs_[X86_64_REG_R8]);
-  fn("r9", regs_[X86_64_REG_R9]);
-  fn("r10", regs_[X86_64_REG_R10]);
-  fn("r11", regs_[X86_64_REG_R11]);
-  fn("r12", regs_[X86_64_REG_R12]);
-  fn("r13", regs_[X86_64_REG_R13]);
-  fn("r14", regs_[X86_64_REG_R14]);
-  fn("r15", regs_[X86_64_REG_R15]);
-  fn("rdi", regs_[X86_64_REG_RDI]);
-  fn("rsi", regs_[X86_64_REG_RSI]);
-  fn("rbp", regs_[X86_64_REG_RBP]);
-  fn("rsp", regs_[X86_64_REG_RSP]);
-  fn("rip", regs_[X86_64_REG_RIP]);
-}
-
-static Regs* ReadArm(void* remote_data) {
-  arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
-
-  RegsArm* regs = new RegsArm();
-  memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
-  regs->SetFromRaw();
-  return regs;
-}
-
-static Regs* ReadArm64(void* remote_data) {
-  arm64_user_regs* user = reinterpret_cast<arm64_user_regs*>(remote_data);
-
-  RegsArm64* regs = new RegsArm64();
-  memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t));
-  uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
-  reg_data[ARM64_REG_PC] = user->pc;
-  reg_data[ARM64_REG_SP] = user->sp;
-  regs->SetFromRaw();
-  return regs;
-}
-
-static Regs* ReadX86(void* remote_data) {
-  x86_user_regs* user = reinterpret_cast<x86_user_regs*>(remote_data);
-
-  RegsX86* regs = new RegsX86();
-  (*regs)[X86_REG_EAX] = user->eax;
-  (*regs)[X86_REG_EBX] = user->ebx;
-  (*regs)[X86_REG_ECX] = user->ecx;
-  (*regs)[X86_REG_EDX] = user->edx;
-  (*regs)[X86_REG_EBP] = user->ebp;
-  (*regs)[X86_REG_EDI] = user->edi;
-  (*regs)[X86_REG_ESI] = user->esi;
-  (*regs)[X86_REG_ESP] = user->esp;
-  (*regs)[X86_REG_EIP] = user->eip;
-
-  regs->SetFromRaw();
-  return regs;
-}
-
-static Regs* ReadX86_64(void* remote_data) {
-  x86_64_user_regs* user = reinterpret_cast<x86_64_user_regs*>(remote_data);
-
-  RegsX86_64* regs = new RegsX86_64();
-  (*regs)[X86_64_REG_RAX] = user->rax;
-  (*regs)[X86_64_REG_RBX] = user->rbx;
-  (*regs)[X86_64_REG_RCX] = user->rcx;
-  (*regs)[X86_64_REG_RDX] = user->rdx;
-  (*regs)[X86_64_REG_R8] = user->r8;
-  (*regs)[X86_64_REG_R9] = user->r9;
-  (*regs)[X86_64_REG_R10] = user->r10;
-  (*regs)[X86_64_REG_R11] = user->r11;
-  (*regs)[X86_64_REG_R12] = user->r12;
-  (*regs)[X86_64_REG_R13] = user->r13;
-  (*regs)[X86_64_REG_R14] = user->r14;
-  (*regs)[X86_64_REG_R15] = user->r15;
-  (*regs)[X86_64_REG_RDI] = user->rdi;
-  (*regs)[X86_64_REG_RSI] = user->rsi;
-  (*regs)[X86_64_REG_RBP] = user->rbp;
-  (*regs)[X86_64_REG_RSP] = user->rsp;
-  (*regs)[X86_64_REG_RIP] = user->rip;
-
-  regs->SetFromRaw();
-  return regs;
-}
+// The largest user structure.
+constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
 
 // This function assumes that reg_data is already aligned to a 64 bit value.
 // If not this could crash with an unaligned access.
@@ -348,106 +53,42 @@
 
   switch (io.iov_len) {
   case sizeof(x86_user_regs):
-    return ReadX86(buffer.data());
+    return RegsX86::Read(buffer.data());
   case sizeof(x86_64_user_regs):
-    return ReadX86_64(buffer.data());
+    return RegsX86_64::Read(buffer.data());
   case sizeof(arm_user_regs):
-    return ReadArm(buffer.data());
+    return RegsArm::Read(buffer.data());
   case sizeof(arm64_user_regs):
-    return ReadArm64(buffer.data());
+    return RegsArm64::Read(buffer.data());
   }
   return nullptr;
 }
 
-static Regs* CreateFromArmUcontext(void* ucontext) {
-  arm_ucontext_t* arm_ucontext = reinterpret_cast<arm_ucontext_t*>(ucontext);
-
-  RegsArm* regs = new RegsArm();
-  memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t));
-  regs->SetFromRaw();
-  return regs;
-}
-
-static Regs* CreateFromArm64Ucontext(void* ucontext) {
-  arm64_ucontext_t* arm64_ucontext = reinterpret_cast<arm64_ucontext_t*>(ucontext);
-
-  RegsArm64* regs = new RegsArm64();
-  memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t));
-  regs->SetFromRaw();
-  return regs;
-}
-
-void RegsX86::SetFromUcontext(x86_ucontext_t* ucontext) {
-  // Put the registers in the expected order.
-  regs_[X86_REG_EDI] = ucontext->uc_mcontext.edi;
-  regs_[X86_REG_ESI] = ucontext->uc_mcontext.esi;
-  regs_[X86_REG_EBP] = ucontext->uc_mcontext.ebp;
-  regs_[X86_REG_ESP] = ucontext->uc_mcontext.esp;
-  regs_[X86_REG_EBX] = ucontext->uc_mcontext.ebx;
-  regs_[X86_REG_EDX] = ucontext->uc_mcontext.edx;
-  regs_[X86_REG_ECX] = ucontext->uc_mcontext.ecx;
-  regs_[X86_REG_EAX] = ucontext->uc_mcontext.eax;
-  regs_[X86_REG_EIP] = ucontext->uc_mcontext.eip;
-  SetFromRaw();
-}
-
-static Regs* CreateFromX86Ucontext(void* ucontext) {
-  x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext);
-
-  RegsX86* regs = new RegsX86();
-  regs->SetFromUcontext(x86_ucontext);
-  return regs;
-}
-
-void RegsX86_64::SetFromUcontext(x86_64_ucontext_t* ucontext) {
-  // R8-R15
-  memcpy(&regs_[X86_64_REG_R8], &ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
-
-  // Rest of the registers.
-  regs_[X86_64_REG_RDI] = ucontext->uc_mcontext.rdi;
-  regs_[X86_64_REG_RSI] = ucontext->uc_mcontext.rsi;
-  regs_[X86_64_REG_RBP] = ucontext->uc_mcontext.rbp;
-  regs_[X86_64_REG_RBX] = ucontext->uc_mcontext.rbx;
-  regs_[X86_64_REG_RDX] = ucontext->uc_mcontext.rdx;
-  regs_[X86_64_REG_RAX] = ucontext->uc_mcontext.rax;
-  regs_[X86_64_REG_RCX] = ucontext->uc_mcontext.rcx;
-  regs_[X86_64_REG_RSP] = ucontext->uc_mcontext.rsp;
-  regs_[X86_64_REG_RIP] = ucontext->uc_mcontext.rip;
-
-  SetFromRaw();
-}
-
-static Regs* CreateFromX86_64Ucontext(void* ucontext) {
-  x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext);
-
-  RegsX86_64* regs = new RegsX86_64();
-  regs->SetFromUcontext(x86_64_ucontext);
-  return regs;
-}
-
-Regs* Regs::CreateFromUcontext(uint32_t machine_type, void* ucontext) {
-  switch (machine_type) {
-    case EM_386:
-      return CreateFromX86Ucontext(ucontext);
-    case EM_X86_64:
-      return CreateFromX86_64Ucontext(ucontext);
-    case EM_ARM:
-      return CreateFromArmUcontext(ucontext);
-    case EM_AARCH64:
-      return CreateFromArm64Ucontext(ucontext);
+Regs* Regs::CreateFromUcontext(ArchEnum arch, void* ucontext) {
+  switch (arch) {
+    case ARCH_X86:
+      return RegsX86::CreateFromUcontext(ucontext);
+    case ARCH_X86_64:
+      return RegsX86_64::CreateFromUcontext(ucontext);
+    case ARCH_ARM:
+      return RegsArm::CreateFromUcontext(ucontext);
+    case ARCH_ARM64:
+      return RegsArm64::CreateFromUcontext(ucontext);
+    case ARCH_UNKNOWN:
+    default:
+      return nullptr;
   }
-  return nullptr;
 }
 
-uint32_t Regs::CurrentMachineType() {
+ArchEnum Regs::CurrentArch() {
 #if defined(__arm__)
-  return EM_ARM;
+  return ARCH_ARM;
 #elif defined(__aarch64__)
-  return EM_AARCH64;
+  return ARCH_ARM64;
 #elif defined(__i386__)
-  return EM_386;
+  return ARCH_X86;
 #elif defined(__x86_64__)
-  return EM_X86_64;
+  return ARCH_X86_64;
 #else
   abort();
 #endif
@@ -469,191 +110,4 @@
   return regs;
 }
 
-bool RegsArm::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
-  uint32_t data;
-  Memory* elf_memory = elf->memory();
-  // Read from elf memory since it is usually more expensive to read from
-  // process memory.
-  if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
-    return false;
-  }
-
-  uint64_t offset = 0;
-  if (data == 0xe3a07077 || data == 0xef900077 || data == 0xdf002777) {
-    // non-RT sigreturn call.
-    // __restore:
-    //
-    // Form 1 (arm):
-    // 0x77 0x70              mov r7, #0x77
-    // 0xa0 0xe3              svc 0x00000000
-    //
-    // Form 2 (arm):
-    // 0x77 0x00 0x90 0xef    svc 0x00900077
-    //
-    // Form 3 (thumb):
-    // 0x77 0x27              movs r7, #77
-    // 0x00 0xdf              svc 0
-    if (!process_memory->Read(sp(), &data, sizeof(data))) {
-      return false;
-    }
-    if (data == 0x5ac3c35a) {
-      // SP + uc_mcontext offset + r0 offset.
-      offset = sp() + 0x14 + 0xc;
-    } else {
-      // SP + r0 offset
-      offset = sp() + 0xc;
-    }
-  } else if (data == 0xe3a070ad || data == 0xef9000ad || data == 0xdf0027ad) {
-    // RT sigreturn call.
-    // __restore_rt:
-    //
-    // Form 1 (arm):
-    // 0xad 0x70      mov r7, #0xad
-    // 0xa0 0xe3      svc 0x00000000
-    //
-    // Form 2 (arm):
-    // 0xad 0x00 0x90 0xef    svc 0x009000ad
-    //
-    // Form 3 (thumb):
-    // 0xad 0x27              movs r7, #ad
-    // 0x00 0xdf              svc 0
-    if (!process_memory->Read(sp(), &data, sizeof(data))) {
-      return false;
-    }
-    if (data == sp() + 8) {
-      // SP + 8 + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
-      offset = sp() + 8 + 0x80 + 0x14 + 0xc;
-    } else {
-      // SP + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
-      offset = sp() + 0x80 + 0x14 + 0xc;
-    }
-  }
-  if (offset == 0) {
-    return false;
-  }
-
-  if (!process_memory->Read(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) {
-    return false;
-  }
-  SetFromRaw();
-  return true;
-}
-
-bool RegsArm64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
-  uint64_t data;
-  Memory* elf_memory = elf->memory();
-  // Read from elf memory since it is usually more expensive to read from
-  // process memory.
-  if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
-    return false;
-  }
-
-  // Look for the kernel sigreturn function.
-  // __kernel_rt_sigreturn:
-  // 0xd2801168     mov x8, #0x8b
-  // 0xd4000001     svc #0x0
-  if (data != 0xd4000001d2801168ULL) {
-    return false;
-  }
-
-  // SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset.
-  if (!process_memory->Read(sp() + 0x80 + 0xb0 + 0x08, regs_.data(),
-                            sizeof(uint64_t) * ARM64_REG_LAST)) {
-    return false;
-  }
-
-  SetFromRaw();
-  return true;
-}
-
-bool RegsX86::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
-  uint64_t data;
-  Memory* elf_memory = elf->memory();
-  // Read from elf memory since it is usually more expensive to read from
-  // process memory.
-  if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
-    return false;
-  }
-
-  if (data == 0x80cd00000077b858ULL) {
-    // Without SA_SIGINFO set, the return sequence is:
-    //
-    //   __restore:
-    //   0x58                            pop %eax
-    //   0xb8 0x77 0x00 0x00 0x00        movl 0x77,%eax
-    //   0xcd 0x80                       int 0x80
-    //
-    // SP points at arguments:
-    //   int signum
-    //   struct sigcontext (same format as mcontext)
-    struct x86_mcontext_t context;
-    if (!process_memory->Read(sp() + 4, &context, sizeof(context))) {
-      return false;
-    }
-    regs_[X86_REG_EBP] = context.ebp;
-    regs_[X86_REG_ESP] = context.esp;
-    regs_[X86_REG_EBX] = context.ebx;
-    regs_[X86_REG_EDX] = context.edx;
-    regs_[X86_REG_ECX] = context.ecx;
-    regs_[X86_REG_EAX] = context.eax;
-    regs_[X86_REG_EIP] = context.eip;
-    SetFromRaw();
-    return true;
-  } else if ((data & 0x00ffffffffffffffULL) == 0x0080cd000000adb8ULL) {
-    // With SA_SIGINFO set, the return sequence is:
-    //
-    //   __restore_rt:
-    //   0xb8 0xad 0x00 0x00 0x00        movl 0xad,%eax
-    //   0xcd 0x80                       int 0x80
-    //
-    // SP points at arguments:
-    //   int signum
-    //   siginfo*
-    //   ucontext*
-
-    // Get the location of the sigcontext data.
-    uint32_t ptr;
-    if (!process_memory->Read(sp() + 8, &ptr, sizeof(ptr))) {
-      return false;
-    }
-    // Only read the portion of the data structure we care about.
-    x86_ucontext_t x86_ucontext;
-    if (!process_memory->Read(ptr + 0x14, &x86_ucontext.uc_mcontext, sizeof(x86_mcontext_t))) {
-      return false;
-    }
-    SetFromUcontext(&x86_ucontext);
-    return true;
-  }
-  return false;
-}
-
-bool RegsX86_64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
-  uint64_t data;
-  Memory* elf_memory = elf->memory();
-  // Read from elf memory since it is usually more expensive to read from
-  // process memory.
-  if (!elf_memory->Read(rel_pc, &data, sizeof(data)) || data != 0x0f0000000fc0c748) {
-    return false;
-  }
-
-  uint16_t data2;
-  if (!elf_memory->Read(rel_pc + 8, &data2, sizeof(data2)) || data2 != 0x0f05) {
-    return false;
-  }
-
-  // __restore_rt:
-  // 0x48 0xc7 0xc0 0x0f 0x00 0x00 0x00   mov $0xf,%rax
-  // 0x0f 0x05                            syscall
-  // 0x0f                                 nopl 0x0($rax)
-
-  // Read the mcontext data from the stack.
-  // sp points to the ucontext data structure, read only the mcontext part.
-  x86_64_ucontext_t x86_64_ucontext;
-  if (!process_memory->Read(sp() + 0x28, &x86_64_ucontext.uc_mcontext, sizeof(x86_64_mcontext_t))) {
-    return false;
-  }
-  SetFromUcontext(&x86_64_ucontext);
-  return true;
-}
-
 }  // namespace unwindstack
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
new file mode 100644
index 0000000..34f29bd
--- /dev/null
+++ b/libunwindstack/RegsArm.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsArm.h>
+
+#include "MachineArm.h"
+#include "UcontextArm.h"
+#include "UserArm.h"
+
+namespace unwindstack {
+
+RegsArm::RegsArm()
+    : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
+
+ArchEnum RegsArm::Arch() {
+  return ARCH_ARM;
+}
+
+uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid()) {
+    return rel_pc;
+  }
+
+  uint64_t load_bias = elf->GetLoadBias();
+  if (rel_pc < load_bias) {
+    return rel_pc;
+  }
+  uint64_t adjusted_rel_pc = rel_pc - load_bias;
+
+  if (adjusted_rel_pc < 5) {
+    return rel_pc;
+  }
+
+  if (adjusted_rel_pc & 1) {
+    // This is a thumb instruction, it could be 2 or 4 bytes.
+    uint32_t value;
+    if (rel_pc < 5 || !elf->memory()->ReadFully(adjusted_rel_pc - 5, &value, sizeof(value)) ||
+        (value & 0xe000f000) != 0xe000f000) {
+      return rel_pc - 2;
+    }
+  }
+  return rel_pc - 4;
+}
+
+void RegsArm::SetFromRaw() {
+  set_pc(regs_[ARM_REG_PC]);
+  set_sp(regs_[ARM_REG_SP]);
+}
+
+bool RegsArm::SetPcFromReturnAddress(Memory*) {
+  if (pc() == regs_[ARM_REG_LR]) {
+    return false;
+  }
+
+  set_pc(regs_[ARM_REG_LR]);
+  return true;
+}
+
+void RegsArm::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("r0", regs_[ARM_REG_R0]);
+  fn("r1", regs_[ARM_REG_R1]);
+  fn("r2", regs_[ARM_REG_R2]);
+  fn("r3", regs_[ARM_REG_R3]);
+  fn("r4", regs_[ARM_REG_R4]);
+  fn("r5", regs_[ARM_REG_R5]);
+  fn("r6", regs_[ARM_REG_R6]);
+  fn("r7", regs_[ARM_REG_R7]);
+  fn("r8", regs_[ARM_REG_R8]);
+  fn("r9", regs_[ARM_REG_R9]);
+  fn("r10", regs_[ARM_REG_R10]);
+  fn("r11", regs_[ARM_REG_R11]);
+  fn("ip", regs_[ARM_REG_R12]);
+  fn("sp", regs_[ARM_REG_SP]);
+  fn("lr", regs_[ARM_REG_LR]);
+  fn("pc", regs_[ARM_REG_PC]);
+}
+
+Regs* RegsArm::Read(void* remote_data) {
+  arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
+
+  RegsArm* regs = new RegsArm();
+  memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
+  regs->SetFromRaw();
+  return regs;
+}
+
+Regs* RegsArm::CreateFromUcontext(void* ucontext) {
+  arm_ucontext_t* arm_ucontext = reinterpret_cast<arm_ucontext_t*>(ucontext);
+
+  RegsArm* regs = new RegsArm();
+  memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t));
+  regs->SetFromRaw();
+  return regs;
+}
+
+bool RegsArm::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint32_t data;
+  Memory* elf_memory = elf->memory();
+  // Read from elf memory since it is usually more expensive to read from
+  // process memory.
+  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
+    return false;
+  }
+
+  uint64_t offset = 0;
+  if (data == 0xe3a07077 || data == 0xef900077 || data == 0xdf002777) {
+    // non-RT sigreturn call.
+    // __restore:
+    //
+    // Form 1 (arm):
+    // 0x77 0x70              mov r7, #0x77
+    // 0xa0 0xe3              svc 0x00000000
+    //
+    // Form 2 (arm):
+    // 0x77 0x00 0x90 0xef    svc 0x00900077
+    //
+    // Form 3 (thumb):
+    // 0x77 0x27              movs r7, #77
+    // 0x00 0xdf              svc 0
+    if (!process_memory->ReadFully(sp(), &data, sizeof(data))) {
+      return false;
+    }
+    if (data == 0x5ac3c35a) {
+      // SP + uc_mcontext offset + r0 offset.
+      offset = sp() + 0x14 + 0xc;
+    } else {
+      // SP + r0 offset
+      offset = sp() + 0xc;
+    }
+  } else if (data == 0xe3a070ad || data == 0xef9000ad || data == 0xdf0027ad) {
+    // RT sigreturn call.
+    // __restore_rt:
+    //
+    // Form 1 (arm):
+    // 0xad 0x70      mov r7, #0xad
+    // 0xa0 0xe3      svc 0x00000000
+    //
+    // Form 2 (arm):
+    // 0xad 0x00 0x90 0xef    svc 0x009000ad
+    //
+    // Form 3 (thumb):
+    // 0xad 0x27              movs r7, #ad
+    // 0x00 0xdf              svc 0
+    if (!process_memory->ReadFully(sp(), &data, sizeof(data))) {
+      return false;
+    }
+    if (data == sp() + 8) {
+      // SP + 8 + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
+      offset = sp() + 8 + 0x80 + 0x14 + 0xc;
+    } else {
+      // SP + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
+      offset = sp() + 0x80 + 0x14 + 0xc;
+    }
+  }
+  if (offset == 0) {
+    return false;
+  }
+
+  if (!process_memory->ReadFully(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) {
+    return false;
+  }
+  SetFromRaw();
+  return true;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
new file mode 100644
index 0000000..2077bc5
--- /dev/null
+++ b/libunwindstack/RegsArm64.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsArm64.h>
+
+#include "MachineArm64.h"
+#include "UcontextArm64.h"
+#include "UserArm64.h"
+
+namespace unwindstack {
+
+RegsArm64::RegsArm64()
+    : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
+
+ArchEnum RegsArm64::Arch() {
+  return ARCH_ARM64;
+}
+
+uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid()) {
+    return rel_pc;
+  }
+
+  if (rel_pc < 4) {
+    return rel_pc;
+  }
+  return rel_pc - 4;
+}
+
+void RegsArm64::SetFromRaw() {
+  set_pc(regs_[ARM64_REG_PC]);
+  set_sp(regs_[ARM64_REG_SP]);
+}
+
+bool RegsArm64::SetPcFromReturnAddress(Memory*) {
+  if (pc() == regs_[ARM64_REG_LR]) {
+    return false;
+  }
+
+  set_pc(regs_[ARM64_REG_LR]);
+  return true;
+}
+
+void RegsArm64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("x0", regs_[ARM64_REG_R0]);
+  fn("x1", regs_[ARM64_REG_R1]);
+  fn("x2", regs_[ARM64_REG_R2]);
+  fn("x3", regs_[ARM64_REG_R3]);
+  fn("x4", regs_[ARM64_REG_R4]);
+  fn("x5", regs_[ARM64_REG_R5]);
+  fn("x6", regs_[ARM64_REG_R6]);
+  fn("x7", regs_[ARM64_REG_R7]);
+  fn("x8", regs_[ARM64_REG_R8]);
+  fn("x9", regs_[ARM64_REG_R9]);
+  fn("x10", regs_[ARM64_REG_R10]);
+  fn("x11", regs_[ARM64_REG_R11]);
+  fn("x12", regs_[ARM64_REG_R12]);
+  fn("x13", regs_[ARM64_REG_R13]);
+  fn("x14", regs_[ARM64_REG_R14]);
+  fn("x15", regs_[ARM64_REG_R15]);
+  fn("x16", regs_[ARM64_REG_R16]);
+  fn("x17", regs_[ARM64_REG_R17]);
+  fn("x18", regs_[ARM64_REG_R18]);
+  fn("x19", regs_[ARM64_REG_R19]);
+  fn("x20", regs_[ARM64_REG_R20]);
+  fn("x21", regs_[ARM64_REG_R21]);
+  fn("x22", regs_[ARM64_REG_R22]);
+  fn("x23", regs_[ARM64_REG_R23]);
+  fn("x24", regs_[ARM64_REG_R24]);
+  fn("x25", regs_[ARM64_REG_R25]);
+  fn("x26", regs_[ARM64_REG_R26]);
+  fn("x27", regs_[ARM64_REG_R27]);
+  fn("x28", regs_[ARM64_REG_R28]);
+  fn("x29", regs_[ARM64_REG_R29]);
+  fn("sp", regs_[ARM64_REG_SP]);
+  fn("lr", regs_[ARM64_REG_LR]);
+  fn("pc", regs_[ARM64_REG_PC]);
+}
+
+Regs* RegsArm64::Read(void* remote_data) {
+  arm64_user_regs* user = reinterpret_cast<arm64_user_regs*>(remote_data);
+
+  RegsArm64* regs = new RegsArm64();
+  memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t));
+  uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+  reg_data[ARM64_REG_PC] = user->pc;
+  reg_data[ARM64_REG_SP] = user->sp;
+  regs->SetFromRaw();
+  return regs;
+}
+
+Regs* RegsArm64::CreateFromUcontext(void* ucontext) {
+  arm64_ucontext_t* arm64_ucontext = reinterpret_cast<arm64_ucontext_t*>(ucontext);
+
+  RegsArm64* regs = new RegsArm64();
+  memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t));
+  regs->SetFromRaw();
+  return regs;
+}
+
+bool RegsArm64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint64_t data;
+  Memory* elf_memory = elf->memory();
+  // Read from elf memory since it is usually more expensive to read from
+  // process memory.
+  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
+    return false;
+  }
+
+  // Look for the kernel sigreturn function.
+  // __kernel_rt_sigreturn:
+  // 0xd2801168     mov x8, #0x8b
+  // 0xd4000001     svc #0x0
+  if (data != 0xd4000001d2801168ULL) {
+    return false;
+  }
+
+  // SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset.
+  if (!process_memory->ReadFully(sp() + 0x80 + 0xb0 + 0x08, regs_.data(),
+                                 sizeof(uint64_t) * ARM64_REG_LAST)) {
+    return false;
+  }
+
+  SetFromRaw();
+  return true;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/RegsX86.cpp b/libunwindstack/RegsX86.cpp
new file mode 100644
index 0000000..ef2f3de
--- /dev/null
+++ b/libunwindstack/RegsX86.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsX86.h>
+
+#include "MachineX86.h"
+#include "UcontextX86.h"
+#include "UserX86.h"
+
+namespace unwindstack {
+
+RegsX86::RegsX86()
+    : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
+
+ArchEnum RegsX86::Arch() {
+  return ARCH_X86;
+}
+
+uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid()) {
+    return rel_pc;
+  }
+
+  if (rel_pc == 0) {
+    return 0;
+  }
+  return rel_pc - 1;
+}
+
+void RegsX86::SetFromRaw() {
+  set_pc(regs_[X86_REG_PC]);
+  set_sp(regs_[X86_REG_SP]);
+}
+
+bool RegsX86::SetPcFromReturnAddress(Memory* process_memory) {
+  // Attempt to get the return address from the top of the stack.
+  uint32_t new_pc;
+  if (!process_memory->ReadFully(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
+    return false;
+  }
+
+  set_pc(new_pc);
+  return true;
+}
+
+void RegsX86::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("eax", regs_[X86_REG_EAX]);
+  fn("ebx", regs_[X86_REG_EBX]);
+  fn("ecx", regs_[X86_REG_ECX]);
+  fn("edx", regs_[X86_REG_EDX]);
+  fn("ebp", regs_[X86_REG_EBP]);
+  fn("edi", regs_[X86_REG_EDI]);
+  fn("esi", regs_[X86_REG_ESI]);
+  fn("esp", regs_[X86_REG_ESP]);
+  fn("eip", regs_[X86_REG_EIP]);
+}
+
+Regs* RegsX86::Read(void* user_data) {
+  x86_user_regs* user = reinterpret_cast<x86_user_regs*>(user_data);
+
+  RegsX86* regs = new RegsX86();
+  (*regs)[X86_REG_EAX] = user->eax;
+  (*regs)[X86_REG_EBX] = user->ebx;
+  (*regs)[X86_REG_ECX] = user->ecx;
+  (*regs)[X86_REG_EDX] = user->edx;
+  (*regs)[X86_REG_EBP] = user->ebp;
+  (*regs)[X86_REG_EDI] = user->edi;
+  (*regs)[X86_REG_ESI] = user->esi;
+  (*regs)[X86_REG_ESP] = user->esp;
+  (*regs)[X86_REG_EIP] = user->eip;
+
+  regs->SetFromRaw();
+  return regs;
+}
+
+void RegsX86::SetFromUcontext(x86_ucontext_t* ucontext) {
+  // Put the registers in the expected order.
+  regs_[X86_REG_EDI] = ucontext->uc_mcontext.edi;
+  regs_[X86_REG_ESI] = ucontext->uc_mcontext.esi;
+  regs_[X86_REG_EBP] = ucontext->uc_mcontext.ebp;
+  regs_[X86_REG_ESP] = ucontext->uc_mcontext.esp;
+  regs_[X86_REG_EBX] = ucontext->uc_mcontext.ebx;
+  regs_[X86_REG_EDX] = ucontext->uc_mcontext.edx;
+  regs_[X86_REG_ECX] = ucontext->uc_mcontext.ecx;
+  regs_[X86_REG_EAX] = ucontext->uc_mcontext.eax;
+  regs_[X86_REG_EIP] = ucontext->uc_mcontext.eip;
+  SetFromRaw();
+}
+
+Regs* RegsX86::CreateFromUcontext(void* ucontext) {
+  x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext);
+
+  RegsX86* regs = new RegsX86();
+  regs->SetFromUcontext(x86_ucontext);
+  return regs;
+}
+
+bool RegsX86::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint64_t data;
+  Memory* elf_memory = elf->memory();
+  // Read from elf memory since it is usually more expensive to read from
+  // process memory.
+  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
+    return false;
+  }
+
+  if (data == 0x80cd00000077b858ULL) {
+    // Without SA_SIGINFO set, the return sequence is:
+    //
+    //   __restore:
+    //   0x58                            pop %eax
+    //   0xb8 0x77 0x00 0x00 0x00        movl 0x77,%eax
+    //   0xcd 0x80                       int 0x80
+    //
+    // SP points at arguments:
+    //   int signum
+    //   struct sigcontext (same format as mcontext)
+    struct x86_mcontext_t context;
+    if (!process_memory->ReadFully(sp() + 4, &context, sizeof(context))) {
+      return false;
+    }
+    regs_[X86_REG_EBP] = context.ebp;
+    regs_[X86_REG_ESP] = context.esp;
+    regs_[X86_REG_EBX] = context.ebx;
+    regs_[X86_REG_EDX] = context.edx;
+    regs_[X86_REG_ECX] = context.ecx;
+    regs_[X86_REG_EAX] = context.eax;
+    regs_[X86_REG_EIP] = context.eip;
+    SetFromRaw();
+    return true;
+  } else if ((data & 0x00ffffffffffffffULL) == 0x0080cd000000adb8ULL) {
+    // With SA_SIGINFO set, the return sequence is:
+    //
+    //   __restore_rt:
+    //   0xb8 0xad 0x00 0x00 0x00        movl 0xad,%eax
+    //   0xcd 0x80                       int 0x80
+    //
+    // SP points at arguments:
+    //   int signum
+    //   siginfo*
+    //   ucontext*
+
+    // Get the location of the sigcontext data.
+    uint32_t ptr;
+    if (!process_memory->ReadFully(sp() + 8, &ptr, sizeof(ptr))) {
+      return false;
+    }
+    // Only read the portion of the data structure we care about.
+    x86_ucontext_t x86_ucontext;
+    if (!process_memory->ReadFully(ptr + 0x14, &x86_ucontext.uc_mcontext, sizeof(x86_mcontext_t))) {
+      return false;
+    }
+    SetFromUcontext(&x86_ucontext);
+    return true;
+  }
+  return false;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
new file mode 100644
index 0000000..70921f8
--- /dev/null
+++ b/libunwindstack/RegsX86_64.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsX86_64.h>
+
+#include "MachineX86_64.h"
+#include "UcontextX86_64.h"
+#include "UserX86_64.h"
+
+namespace unwindstack {
+
+RegsX86_64::RegsX86_64()
+    : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
+
+ArchEnum RegsX86_64::Arch() {
+  return ARCH_X86_64;
+}
+
+uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid()) {
+    return rel_pc;
+  }
+
+  if (rel_pc == 0) {
+    return 0;
+  }
+
+  return rel_pc - 1;
+}
+
+void RegsX86_64::SetFromRaw() {
+  set_pc(regs_[X86_64_REG_PC]);
+  set_sp(regs_[X86_64_REG_SP]);
+}
+
+bool RegsX86_64::SetPcFromReturnAddress(Memory* process_memory) {
+  // Attempt to get the return address from the top of the stack.
+  uint64_t new_pc;
+  if (!process_memory->ReadFully(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
+    return false;
+  }
+
+  set_pc(new_pc);
+  return true;
+}
+
+void RegsX86_64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("rax", regs_[X86_64_REG_RAX]);
+  fn("rbx", regs_[X86_64_REG_RBX]);
+  fn("rcx", regs_[X86_64_REG_RCX]);
+  fn("rdx", regs_[X86_64_REG_RDX]);
+  fn("r8", regs_[X86_64_REG_R8]);
+  fn("r9", regs_[X86_64_REG_R9]);
+  fn("r10", regs_[X86_64_REG_R10]);
+  fn("r11", regs_[X86_64_REG_R11]);
+  fn("r12", regs_[X86_64_REG_R12]);
+  fn("r13", regs_[X86_64_REG_R13]);
+  fn("r14", regs_[X86_64_REG_R14]);
+  fn("r15", regs_[X86_64_REG_R15]);
+  fn("rdi", regs_[X86_64_REG_RDI]);
+  fn("rsi", regs_[X86_64_REG_RSI]);
+  fn("rbp", regs_[X86_64_REG_RBP]);
+  fn("rsp", regs_[X86_64_REG_RSP]);
+  fn("rip", regs_[X86_64_REG_RIP]);
+}
+
+Regs* RegsX86_64::Read(void* remote_data) {
+  x86_64_user_regs* user = reinterpret_cast<x86_64_user_regs*>(remote_data);
+
+  RegsX86_64* regs = new RegsX86_64();
+  (*regs)[X86_64_REG_RAX] = user->rax;
+  (*regs)[X86_64_REG_RBX] = user->rbx;
+  (*regs)[X86_64_REG_RCX] = user->rcx;
+  (*regs)[X86_64_REG_RDX] = user->rdx;
+  (*regs)[X86_64_REG_R8] = user->r8;
+  (*regs)[X86_64_REG_R9] = user->r9;
+  (*regs)[X86_64_REG_R10] = user->r10;
+  (*regs)[X86_64_REG_R11] = user->r11;
+  (*regs)[X86_64_REG_R12] = user->r12;
+  (*regs)[X86_64_REG_R13] = user->r13;
+  (*regs)[X86_64_REG_R14] = user->r14;
+  (*regs)[X86_64_REG_R15] = user->r15;
+  (*regs)[X86_64_REG_RDI] = user->rdi;
+  (*regs)[X86_64_REG_RSI] = user->rsi;
+  (*regs)[X86_64_REG_RBP] = user->rbp;
+  (*regs)[X86_64_REG_RSP] = user->rsp;
+  (*regs)[X86_64_REG_RIP] = user->rip;
+
+  regs->SetFromRaw();
+  return regs;
+}
+
+void RegsX86_64::SetFromUcontext(x86_64_ucontext_t* ucontext) {
+  // R8-R15
+  memcpy(&regs_[X86_64_REG_R8], &ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
+
+  // Rest of the registers.
+  regs_[X86_64_REG_RDI] = ucontext->uc_mcontext.rdi;
+  regs_[X86_64_REG_RSI] = ucontext->uc_mcontext.rsi;
+  regs_[X86_64_REG_RBP] = ucontext->uc_mcontext.rbp;
+  regs_[X86_64_REG_RBX] = ucontext->uc_mcontext.rbx;
+  regs_[X86_64_REG_RDX] = ucontext->uc_mcontext.rdx;
+  regs_[X86_64_REG_RAX] = ucontext->uc_mcontext.rax;
+  regs_[X86_64_REG_RCX] = ucontext->uc_mcontext.rcx;
+  regs_[X86_64_REG_RSP] = ucontext->uc_mcontext.rsp;
+  regs_[X86_64_REG_RIP] = ucontext->uc_mcontext.rip;
+
+  SetFromRaw();
+}
+
+Regs* RegsX86_64::CreateFromUcontext(void* ucontext) {
+  x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext);
+
+  RegsX86_64* regs = new RegsX86_64();
+  regs->SetFromUcontext(x86_64_ucontext);
+  return regs;
+}
+
+bool RegsX86_64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint64_t data;
+  Memory* elf_memory = elf->memory();
+  // Read from elf memory since it is usually more expensive to read from
+  // process memory.
+  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data)) || data != 0x0f0000000fc0c748) {
+    return false;
+  }
+
+  uint16_t data2;
+  if (!elf_memory->ReadFully(rel_pc + 8, &data2, sizeof(data2)) || data2 != 0x0f05) {
+    return false;
+  }
+
+  // __restore_rt:
+  // 0x48 0xc7 0xc0 0x0f 0x00 0x00 0x00   mov $0xf,%rax
+  // 0x0f 0x05                            syscall
+  // 0x0f                                 nopl 0x0($rax)
+
+  // Read the mcontext data from the stack.
+  // sp points to the ucontext data structure, read only the mcontext part.
+  x86_64_ucontext_t x86_64_ucontext;
+  if (!process_memory->ReadFully(sp() + 0x28, &x86_64_ucontext.uc_mcontext,
+                                 sizeof(x86_64_mcontext_t))) {
+    return false;
+  }
+  SetFromUcontext(&x86_64_ucontext);
+  return true;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index 42d816a..b4b92d6 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -71,7 +71,7 @@
   bool return_value = false;
   while (cur_offset_ + entry_size_ <= end_) {
     SymType entry;
-    if (!elf_memory->Read(cur_offset_, &entry, sizeof(entry))) {
+    if (!elf_memory->ReadFully(cur_offset_, &entry, sizeof(entry))) {
       // Stop all processing, something looks like it is corrupted.
       cur_offset_ = UINT64_MAX;
       return false;
diff --git a/libunwindstack/Ucontext.h b/libunwindstack/Ucontext.h
deleted file mode 100644
index 22f6a89..0000000
--- a/libunwindstack/Ucontext.h
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _LIBUNWINDSTACK_UCONTEXT_H
-#define _LIBUNWINDSTACK_UCONTEXT_H
-
-#include <stdint.h>
-
-namespace unwindstack {
-
-//-------------------------------------------------------------------
-// ARM ucontext structures
-//-------------------------------------------------------------------
-struct arm_stack_t {
-  uint32_t ss_sp;    // void __user*
-  int32_t ss_flags;  // int
-  uint32_t ss_size;  // size_t
-};
-
-struct arm_mcontext_t {
-  uint32_t trap_no;             // unsigned long
-  uint32_t error_code;          // unsigned long
-  uint32_t oldmask;             // unsigned long
-  uint32_t regs[ARM_REG_LAST];  // unsigned long
-  uint32_t cpsr;                // unsigned long
-  uint32_t fault_address;       // unsigned long
-};
-
-struct arm_ucontext_t {
-  uint32_t uc_flags;  // unsigned long
-  uint32_t uc_link;   // struct ucontext*
-  arm_stack_t uc_stack;
-  arm_mcontext_t uc_mcontext;
-  // Nothing else is used, so don't define it.
-};
-//-------------------------------------------------------------------
-
-//-------------------------------------------------------------------
-// ARM64 ucontext structures
-//-------------------------------------------------------------------
-struct arm64_stack_t {
-  uint64_t ss_sp;    // void __user*
-  int32_t ss_flags;  // int
-  uint64_t ss_size;  // size_t
-};
-
-struct arm64_sigset_t {
-  uint64_t sig;  // unsigned long
-};
-
-struct arm64_mcontext_t {
-  uint64_t fault_address;         // __u64
-  uint64_t regs[ARM64_REG_LAST];  // __u64
-  uint64_t pstate;                // __u64
-  // Nothing else is used, so don't define it.
-};
-
-struct arm64_ucontext_t {
-  uint64_t uc_flags;  // unsigned long
-  uint64_t uc_link;   // struct ucontext*
-  arm64_stack_t uc_stack;
-  arm64_sigset_t uc_sigmask;
-  // The kernel adds extra padding after uc_sigmask to match glibc sigset_t on ARM64.
-  char __padding[128 - sizeof(arm64_sigset_t)];
-  // The full structure requires 16 byte alignment, but our partial structure
-  // doesn't, so force the alignment.
-  arm64_mcontext_t uc_mcontext __attribute__((aligned(16)));
-};
-//-------------------------------------------------------------------
-
-//-------------------------------------------------------------------
-// X86 ucontext structures
-//-------------------------------------------------------------------
-struct x86_stack_t {
-  uint32_t ss_sp;    // void __user*
-  int32_t ss_flags;  // int
-  uint32_t ss_size;  // size_t
-};
-
-struct x86_mcontext_t {
-  uint32_t gs;
-  uint32_t fs;
-  uint32_t es;
-  uint32_t ds;
-  uint32_t edi;
-  uint32_t esi;
-  uint32_t ebp;
-  uint32_t esp;
-  uint32_t ebx;
-  uint32_t edx;
-  uint32_t ecx;
-  uint32_t eax;
-  uint32_t trapno;
-  uint32_t err;
-  uint32_t eip;
-  uint32_t cs;
-  uint32_t efl;
-  uint32_t uesp;
-  uint32_t ss;
-  // Only care about the registers, skip everything else.
-};
-
-struct x86_ucontext_t {
-  uint32_t uc_flags;  // unsigned long
-  uint32_t uc_link;   // struct ucontext*
-  x86_stack_t uc_stack;
-  x86_mcontext_t uc_mcontext;
-  // Nothing else is used, so don't define it.
-};
-//-------------------------------------------------------------------
-
-//-------------------------------------------------------------------
-// X86_64 ucontext structures
-//-------------------------------------------------------------------
-struct x86_64_stack_t {
-  uint64_t ss_sp;    // void __user*
-  int32_t ss_flags;  // int
-  uint64_t ss_size;  // size_t
-};
-
-struct x86_64_mcontext_t {
-  uint64_t r8;
-  uint64_t r9;
-  uint64_t r10;
-  uint64_t r11;
-  uint64_t r12;
-  uint64_t r13;
-  uint64_t r14;
-  uint64_t r15;
-  uint64_t rdi;
-  uint64_t rsi;
-  uint64_t rbp;
-  uint64_t rbx;
-  uint64_t rdx;
-  uint64_t rax;
-  uint64_t rcx;
-  uint64_t rsp;
-  uint64_t rip;
-  uint64_t efl;
-  uint64_t csgsfs;
-  uint64_t err;
-  uint64_t trapno;
-  uint64_t oldmask;
-  uint64_t cr2;
-  // Only care about the registers, skip everything else.
-};
-
-struct x86_64_ucontext_t {
-  uint64_t uc_flags;  // unsigned long
-  uint64_t uc_link;   // struct ucontext*
-  x86_64_stack_t uc_stack;
-  x86_64_mcontext_t uc_mcontext;
-  // Nothing else is used, so don't define it.
-};
-//-------------------------------------------------------------------
-
-}  // namespace unwindstack
-
-#endif  // _LIBUNWINDSTACK_UCONTEXT_H
diff --git a/libunwindstack/UcontextArm.h b/libunwindstack/UcontextArm.h
new file mode 100644
index 0000000..8c94166
--- /dev/null
+++ b/libunwindstack/UcontextArm.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_ARM_H
+#define _LIBUNWINDSTACK_UCONTEXT_ARM_H
+
+#include <stdint.h>
+
+#include "MachineArm.h"
+
+namespace unwindstack {
+
+struct arm_stack_t {
+  uint32_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint32_t ss_size;  // size_t
+};
+
+struct arm_mcontext_t {
+  uint32_t trap_no;             // unsigned long
+  uint32_t error_code;          // unsigned long
+  uint32_t oldmask;             // unsigned long
+  uint32_t regs[ARM_REG_LAST];  // unsigned long
+  uint32_t cpsr;                // unsigned long
+  uint32_t fault_address;       // unsigned long
+};
+
+struct arm_ucontext_t {
+  uint32_t uc_flags;  // unsigned long
+  uint32_t uc_link;   // struct ucontext*
+  arm_stack_t uc_stack;
+  arm_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UCONTEXT_ARM_H
diff --git a/libunwindstack/UcontextArm64.h b/libunwindstack/UcontextArm64.h
new file mode 100644
index 0000000..655719f
--- /dev/null
+++ b/libunwindstack/UcontextArm64.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_ARM64_H
+#define _LIBUNWINDSTACK_UCONTEXT_ARM64_H
+
+#include <stdint.h>
+
+#include "MachineArm64.h"
+
+namespace unwindstack {
+
+struct arm64_stack_t {
+  uint64_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint64_t ss_size;  // size_t
+};
+
+struct arm64_sigset_t {
+  uint64_t sig;  // unsigned long
+};
+
+struct arm64_mcontext_t {
+  uint64_t fault_address;         // __u64
+  uint64_t regs[ARM64_REG_LAST];  // __u64
+  uint64_t pstate;                // __u64
+  // Nothing else is used, so don't define it.
+};
+
+struct arm64_ucontext_t {
+  uint64_t uc_flags;  // unsigned long
+  uint64_t uc_link;   // struct ucontext*
+  arm64_stack_t uc_stack;
+  arm64_sigset_t uc_sigmask;
+  // The kernel adds extra padding after uc_sigmask to match glibc sigset_t on ARM64.
+  char __padding[128 - sizeof(arm64_sigset_t)];
+  // The full structure requires 16 byte alignment, but our partial structure
+  // doesn't, so force the alignment.
+  arm64_mcontext_t uc_mcontext __attribute__((aligned(16)));
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UCONTEXT_ARM64_H
diff --git a/libunwindstack/User.h b/libunwindstack/UcontextX86.h
similarity index 62%
copy from libunwindstack/User.h
copy to libunwindstack/UcontextX86.h
index 53f7e50..f79d92b 100644
--- a/libunwindstack/User.h
+++ b/libunwindstack/UcontextX86.h
@@ -26,75 +26,52 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_USER_H
-#define _LIBUNWINDSTACK_USER_H
+#ifndef _LIBUNWINDSTACK_UCONTEXT_X86_H
+#define _LIBUNWINDSTACK_UCONTEXT_X86_H
+
+#include <stdint.h>
+
+#include "MachineX86.h"
 
 namespace unwindstack {
 
-struct x86_user_regs {
-  uint32_t ebx;
-  uint32_t ecx;
-  uint32_t edx;
-  uint32_t esi;
+struct x86_stack_t {
+  uint32_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint32_t ss_size;  // size_t
+};
+
+struct x86_mcontext_t {
+  uint32_t gs;
+  uint32_t fs;
+  uint32_t es;
+  uint32_t ds;
   uint32_t edi;
+  uint32_t esi;
   uint32_t ebp;
-  uint32_t eax;
-  uint32_t xds;
-  uint32_t xes;
-  uint32_t xfs;
-  uint32_t xgs;
-  uint32_t orig_eax;
-  uint32_t eip;
-  uint32_t xcs;
-  uint32_t eflags;
   uint32_t esp;
-  uint32_t xss;
+  uint32_t ebx;
+  uint32_t edx;
+  uint32_t ecx;
+  uint32_t eax;
+  uint32_t trapno;
+  uint32_t err;
+  uint32_t eip;
+  uint32_t cs;
+  uint32_t efl;
+  uint32_t uesp;
+  uint32_t ss;
+  // Only care about the registers, skip everything else.
 };
 
-struct x86_64_user_regs {
-  uint64_t r15;
-  uint64_t r14;
-  uint64_t r13;
-  uint64_t r12;
-  uint64_t rbp;
-  uint64_t rbx;
-  uint64_t r11;
-  uint64_t r10;
-  uint64_t r9;
-  uint64_t r8;
-  uint64_t rax;
-  uint64_t rcx;
-  uint64_t rdx;
-  uint64_t rsi;
-  uint64_t rdi;
-  uint64_t orig_rax;
-  uint64_t rip;
-  uint64_t cs;
-  uint64_t eflags;
-  uint64_t rsp;
-  uint64_t ss;
-  uint64_t fs_base;
-  uint64_t gs_base;
-  uint64_t ds;
-  uint64_t es;
-  uint64_t fs;
-  uint64_t gs;
+struct x86_ucontext_t {
+  uint32_t uc_flags;  // unsigned long
+  uint32_t uc_link;   // struct ucontext*
+  x86_stack_t uc_stack;
+  x86_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
 };
 
-struct arm_user_regs {
-  uint32_t regs[18];
-};
-
-struct arm64_user_regs {
-  uint64_t regs[31];
-  uint64_t sp;
-  uint64_t pc;
-  uint64_t pstate;
-};
-
-// The largest user structure.
-constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
-
 }  // namespace unwindstack
 
-#endif  // _LIBUNWINDSTACK_USER_H
+#endif  // _LIBUNWINDSTACK_UCONTEXT_X86_H
diff --git a/libunwindstack/User.h b/libunwindstack/UcontextX86_64.h
similarity index 67%
copy from libunwindstack/User.h
copy to libunwindstack/UcontextX86_64.h
index 53f7e50..d689796 100644
--- a/libunwindstack/User.h
+++ b/libunwindstack/UcontextX86_64.h
@@ -26,75 +26,56 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_USER_H
-#define _LIBUNWINDSTACK_USER_H
+#ifndef _LIBUNWINDSTACK_UCONTEXT_X86_64_H
+#define _LIBUNWINDSTACK_UCONTEXT_X86_64_H
+
+#include <stdint.h>
+
+#include "MachineX86_64.h"
 
 namespace unwindstack {
 
-struct x86_user_regs {
-  uint32_t ebx;
-  uint32_t ecx;
-  uint32_t edx;
-  uint32_t esi;
-  uint32_t edi;
-  uint32_t ebp;
-  uint32_t eax;
-  uint32_t xds;
-  uint32_t xes;
-  uint32_t xfs;
-  uint32_t xgs;
-  uint32_t orig_eax;
-  uint32_t eip;
-  uint32_t xcs;
-  uint32_t eflags;
-  uint32_t esp;
-  uint32_t xss;
+struct x86_64_stack_t {
+  uint64_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint64_t ss_size;  // size_t
 };
 
-struct x86_64_user_regs {
-  uint64_t r15;
-  uint64_t r14;
-  uint64_t r13;
+struct x86_64_mcontext_t {
+  uint64_t r8;
+  uint64_t r9;
+  uint64_t r10;
+  uint64_t r11;
   uint64_t r12;
+  uint64_t r13;
+  uint64_t r14;
+  uint64_t r15;
+  uint64_t rdi;
+  uint64_t rsi;
   uint64_t rbp;
   uint64_t rbx;
-  uint64_t r11;
-  uint64_t r10;
-  uint64_t r9;
-  uint64_t r8;
+  uint64_t rdx;
   uint64_t rax;
   uint64_t rcx;
-  uint64_t rdx;
-  uint64_t rsi;
-  uint64_t rdi;
-  uint64_t orig_rax;
-  uint64_t rip;
-  uint64_t cs;
-  uint64_t eflags;
   uint64_t rsp;
-  uint64_t ss;
-  uint64_t fs_base;
-  uint64_t gs_base;
-  uint64_t ds;
-  uint64_t es;
-  uint64_t fs;
-  uint64_t gs;
+  uint64_t rip;
+  uint64_t efl;
+  uint64_t csgsfs;
+  uint64_t err;
+  uint64_t trapno;
+  uint64_t oldmask;
+  uint64_t cr2;
+  // Only care about the registers, skip everything else.
 };
 
-struct arm_user_regs {
-  uint32_t regs[18];
+struct x86_64_ucontext_t {
+  uint64_t uc_flags;  // unsigned long
+  uint64_t uc_link;   // struct ucontext*
+  x86_64_stack_t uc_stack;
+  x86_64_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
 };
 
-struct arm64_user_regs {
-  uint64_t regs[31];
-  uint64_t sp;
-  uint64_t pc;
-  uint64_t pstate;
-};
-
-// The largest user structure.
-constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
-
 }  // namespace unwindstack
 
-#endif  // _LIBUNWINDSTACK_USER_H
+#endif  // _LIBUNWINDSTACK_UCONTEXT_X86_64_H
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 2e46a11..4ae365d 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -32,27 +32,20 @@
 
 namespace unwindstack {
 
-void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc) {
+void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc) {
   size_t frame_num = frames_.size();
   frames_.resize(frame_num + 1);
   FrameData* frame = &frames_.at(frame_num);
   frame->num = frame_num;
-  frame->pc = regs_->pc();
   frame->sp = regs_->sp();
-  frame->rel_pc = rel_pc;
+  frame->rel_pc = adjusted_rel_pc;
 
   if (map_info == nullptr) {
+    frame->pc = regs_->pc();
     return;
   }
 
-  if (adjust_pc) {
-    // Don't adjust the first frame pc.
-    frame->rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
-
-    // Adjust the original pc.
-    frame->pc -= rel_pc - frame->rel_pc;
-  }
-
+  frame->pc = map_info->start + adjusted_rel_pc;
   frame->map_name = map_info->name;
   frame->map_offset = map_info->offset;
   frame->map_start = map_info->start;
@@ -92,21 +85,29 @@
 
     MapInfo* map_info = maps_->Find(regs_->pc());
     uint64_t rel_pc;
+    uint64_t adjusted_rel_pc;
     Elf* elf;
     if (map_info == nullptr) {
       rel_pc = regs_->pc();
+      adjusted_rel_pc = rel_pc;
     } else {
       if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
         break;
       }
       elf = map_info->GetElf(process_memory_, true);
       rel_pc = elf->GetRelPc(regs_->pc(), map_info);
+      if (adjust_pc) {
+        adjusted_rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
+      } else {
+        adjusted_rel_pc = rel_pc;
+      }
     }
 
     if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
         std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
                   basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
-      FillInFrame(map_info, elf, rel_pc, adjust_pc);
+      FillInFrame(map_info, elf, adjusted_rel_pc);
+
       // Once a frame is added, stop skipping frames.
       initial_map_names_to_skip = nullptr;
     }
@@ -133,7 +134,8 @@
           in_device_map = true;
         } else {
           bool finished;
-          stepped = elf->Step(rel_pc, map_info->elf_offset, regs_, process_memory_.get(), &finished);
+          stepped = elf->Step(rel_pc, adjusted_rel_pc, map_info->elf_offset, regs_,
+                              process_memory_.get(), &finished);
           if (stepped && finished) {
             break;
           }
@@ -172,8 +174,7 @@
   if (frame_num >= frames_.size()) {
     return "";
   }
-  return FormatFrame(frames_[frame_num],
-                     regs_->MachineType() == EM_ARM || regs_->MachineType() == EM_386);
+  return FormatFrame(frames_[frame_num], regs_->Arch() == ARCH_ARM || regs_->Arch() == ARCH_X86);
 }
 
 std::string Unwinder::FormatFrame(const FrameData& frame, bool bits32) {
diff --git a/libunwindstack/UserArm.h b/libunwindstack/UserArm.h
new file mode 100644
index 0000000..7388c03
--- /dev/null
+++ b/libunwindstack/UserArm.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_ARM_H
+#define _LIBUNWINDSTACK_USER_ARM_H
+
+namespace unwindstack {
+
+struct arm_user_regs {
+  uint32_t regs[18];
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_USER_ARM_H
diff --git a/libunwindstack/User.h b/libunwindstack/UserArm64.h
similarity index 60%
copy from libunwindstack/User.h
copy to libunwindstack/UserArm64.h
index 53f7e50..d74983f 100644
--- a/libunwindstack/User.h
+++ b/libunwindstack/UserArm64.h
@@ -26,65 +26,11 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_USER_H
-#define _LIBUNWINDSTACK_USER_H
+#ifndef _LIBUNWINDSTACK_USER_ARM64_H
+#define _LIBUNWINDSTACK_USER_ARM64_H
 
 namespace unwindstack {
 
-struct x86_user_regs {
-  uint32_t ebx;
-  uint32_t ecx;
-  uint32_t edx;
-  uint32_t esi;
-  uint32_t edi;
-  uint32_t ebp;
-  uint32_t eax;
-  uint32_t xds;
-  uint32_t xes;
-  uint32_t xfs;
-  uint32_t xgs;
-  uint32_t orig_eax;
-  uint32_t eip;
-  uint32_t xcs;
-  uint32_t eflags;
-  uint32_t esp;
-  uint32_t xss;
-};
-
-struct x86_64_user_regs {
-  uint64_t r15;
-  uint64_t r14;
-  uint64_t r13;
-  uint64_t r12;
-  uint64_t rbp;
-  uint64_t rbx;
-  uint64_t r11;
-  uint64_t r10;
-  uint64_t r9;
-  uint64_t r8;
-  uint64_t rax;
-  uint64_t rcx;
-  uint64_t rdx;
-  uint64_t rsi;
-  uint64_t rdi;
-  uint64_t orig_rax;
-  uint64_t rip;
-  uint64_t cs;
-  uint64_t eflags;
-  uint64_t rsp;
-  uint64_t ss;
-  uint64_t fs_base;
-  uint64_t gs_base;
-  uint64_t ds;
-  uint64_t es;
-  uint64_t fs;
-  uint64_t gs;
-};
-
-struct arm_user_regs {
-  uint32_t regs[18];
-};
-
 struct arm64_user_regs {
   uint64_t regs[31];
   uint64_t sp;
@@ -92,9 +38,6 @@
   uint64_t pstate;
 };
 
-// The largest user structure.
-constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
-
 }  // namespace unwindstack
 
-#endif  // _LIBUNWINDSTACK_USER_H
+#endif  // _LIBUNWINDSTACK_USER_ARM64_H
diff --git a/libunwindstack/User.h b/libunwindstack/UserX86.h
similarity index 68%
copy from libunwindstack/User.h
copy to libunwindstack/UserX86.h
index 53f7e50..a040560 100644
--- a/libunwindstack/User.h
+++ b/libunwindstack/UserX86.h
@@ -26,8 +26,8 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_USER_H
-#define _LIBUNWINDSTACK_USER_H
+#ifndef _LIBUNWINDSTACK_USER_X86_H
+#define _LIBUNWINDSTACK_USER_X86_H
 
 namespace unwindstack {
 
@@ -51,50 +51,6 @@
   uint32_t xss;
 };
 
-struct x86_64_user_regs {
-  uint64_t r15;
-  uint64_t r14;
-  uint64_t r13;
-  uint64_t r12;
-  uint64_t rbp;
-  uint64_t rbx;
-  uint64_t r11;
-  uint64_t r10;
-  uint64_t r9;
-  uint64_t r8;
-  uint64_t rax;
-  uint64_t rcx;
-  uint64_t rdx;
-  uint64_t rsi;
-  uint64_t rdi;
-  uint64_t orig_rax;
-  uint64_t rip;
-  uint64_t cs;
-  uint64_t eflags;
-  uint64_t rsp;
-  uint64_t ss;
-  uint64_t fs_base;
-  uint64_t gs_base;
-  uint64_t ds;
-  uint64_t es;
-  uint64_t fs;
-  uint64_t gs;
-};
-
-struct arm_user_regs {
-  uint32_t regs[18];
-};
-
-struct arm64_user_regs {
-  uint64_t regs[31];
-  uint64_t sp;
-  uint64_t pc;
-  uint64_t pstate;
-};
-
-// The largest user structure.
-constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
-
 }  // namespace unwindstack
 
-#endif  // _LIBUNWINDSTACK_USER_H
+#endif  // _LIBUNWINDSTACK_USER_X86_H
diff --git a/libunwindstack/User.h b/libunwindstack/UserX86_64.h
similarity index 74%
rename from libunwindstack/User.h
rename to libunwindstack/UserX86_64.h
index 53f7e50..b80d201 100644
--- a/libunwindstack/User.h
+++ b/libunwindstack/UserX86_64.h
@@ -26,31 +26,11 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _LIBUNWINDSTACK_USER_H
-#define _LIBUNWINDSTACK_USER_H
+#ifndef _LIBUNWINDSTACK_USER_X86_64_H
+#define _LIBUNWINDSTACK_USER_X86_64_H
 
 namespace unwindstack {
 
-struct x86_user_regs {
-  uint32_t ebx;
-  uint32_t ecx;
-  uint32_t edx;
-  uint32_t esi;
-  uint32_t edi;
-  uint32_t ebp;
-  uint32_t eax;
-  uint32_t xds;
-  uint32_t xes;
-  uint32_t xfs;
-  uint32_t xgs;
-  uint32_t orig_eax;
-  uint32_t eip;
-  uint32_t xcs;
-  uint32_t eflags;
-  uint32_t esp;
-  uint32_t xss;
-};
-
 struct x86_64_user_regs {
   uint64_t r15;
   uint64_t r14;
@@ -81,20 +61,6 @@
   uint64_t gs;
 };
 
-struct arm_user_regs {
-  uint32_t regs[18];
-};
-
-struct arm64_user_regs {
-  uint64_t regs[31];
-  uint64_t sp;
-  uint64_t pc;
-  uint64_t pstate;
-};
-
-// The largest user structure.
-constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
-
 }  // namespace unwindstack
 
-#endif  // _LIBUNWINDSTACK_USER_H
+#endif  // _LIBUNWINDSTACK_USER_X86_64_H
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 1e843c3..10be6b4 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -90,10 +90,6 @@
 
   virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
 
-  virtual bool IsCie32(uint32_t value32) = 0;
-
-  virtual bool IsCie64(uint64_t value64) = 0;
-
   virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
 
   virtual uint64_t GetCieOffsetFromFde64(uint64_t pointer) = 0;
@@ -106,6 +102,9 @@
   DwarfMemory memory_;
   DwarfError last_error_;
 
+  uint32_t cie32_value_ = 0;
+  uint64_t cie64_value_ = 0;
+
   uint64_t fde_count_ = 0;
   std::unordered_map<uint64_t, DwarfFde> fde_entries_;
   std::unordered_map<uint64_t, DwarfCie> cie_entries_;
@@ -115,9 +114,24 @@
 template <typename AddressType>
 class DwarfSectionImpl : public DwarfSection {
  public:
+  struct FdeInfo {
+    FdeInfo(uint64_t offset, uint64_t start, uint64_t length)
+        : offset(offset), start(start), end(start + length) {}
+
+    uint64_t offset;
+    AddressType start;
+    AddressType end;
+  };
+
   DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
   virtual ~DwarfSectionImpl() = default;
 
+  bool Init(uint64_t offset, uint64_t size) override;
+
+  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+  const DwarfFde* GetFdeFromIndex(size_t index) override;
+
   bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
             Regs* regs, bool* finished) override;
 
@@ -134,6 +148,16 @@
  protected:
   bool EvalExpression(const DwarfLocation& loc, uint8_t version, Memory* regular_memory,
                       AddressType* value);
+
+  bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
+
+  bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding);
+
+  bool CreateSortedFdeList();
+
+  std::vector<FdeInfo> fdes_;
+  uint64_t entries_offset_;
+  uint64_t entries_end_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 294d742..d27727b 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -20,6 +20,7 @@
 #include <stddef.h>
 
 #include <memory>
+#include <mutex>
 #include <string>
 
 #include <unwindstack/ElfInterface.h>
@@ -35,6 +36,14 @@
 struct MapInfo;
 class Regs;
 
+enum ArchEnum : uint8_t {
+  ARCH_UNKNOWN = 0,
+  ARCH_ARM,
+  ARCH_ARM64,
+  ARCH_X86,
+  ARCH_X86_64,
+};
+
 class Elf {
  public:
   Elf(Memory* memory) : memory_(memory) {}
@@ -50,8 +59,8 @@
 
   uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
 
-  bool Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory,
-            bool* finished);
+  bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
+            Memory* process_memory, bool* finished);
 
   ElfInterface* CreateInterfaceFromMemory(Memory* memory);
 
@@ -63,6 +72,8 @@
 
   uint8_t class_type() { return class_type_; }
 
+  ArchEnum arch() { return arch_; }
+
   Memory* memory() { return memory_.get(); }
 
   ElfInterface* interface() { return interface_.get(); }
@@ -73,6 +84,8 @@
 
   static void GetInfo(Memory* memory, bool* valid, uint64_t* size);
 
+  static uint64_t GetLoadBias(Memory* memory);
+
  protected:
   bool valid_ = false;
   uint64_t load_bias_ = 0;
@@ -80,6 +93,9 @@
   std::unique_ptr<Memory> memory_;
   uint32_t machine_type_;
   uint8_t class_type_;
+  ArchEnum arch_;
+  // Protect calls that can modify internal state of the interface object.
+  std::mutex lock_;
 
   std::unique_ptr<Memory> gnu_debugdata_memory_;
   std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 319623d..5cfe74d 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -70,6 +70,8 @@
 
   uint64_t dynamic_offset() { return dynamic_offset_; }
   uint64_t dynamic_size() { return dynamic_size_; }
+  uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
+  uint64_t eh_frame_hdr_size() { return eh_frame_hdr_size_; }
   uint64_t eh_frame_offset() { return eh_frame_offset_; }
   uint64_t eh_frame_size() { return eh_frame_size_; }
   uint64_t debug_frame_offset() { return debug_frame_offset_; }
@@ -80,6 +82,9 @@
   DwarfSection* eh_frame() { return eh_frame_.get(); }
   DwarfSection* debug_frame() { return debug_frame_.get(); }
 
+  template <typename EhdrType, typename PhdrType>
+  static uint64_t GetLoadBias(Memory* memory);
+
  protected:
   template <typename AddressType>
   void InitHeadersWithTemplate();
@@ -112,6 +117,9 @@
   uint64_t dynamic_offset_ = 0;
   uint64_t dynamic_size_ = 0;
 
+  uint64_t eh_frame_hdr_offset_ = 0;
+  uint64_t eh_frame_hdr_size_ = 0;
+
   uint64_t eh_frame_offset_ = 0;
   uint64_t eh_frame_size_ = 0;
 
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index f108766..6f8ceca 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -19,34 +19,50 @@
 
 #include <stdint.h>
 
+#include <mutex>
 #include <string>
 
+#include <unwindstack/Elf.h>
+
 namespace unwindstack {
 
 // Forward declarations.
-class Elf;
 class Memory;
 
 struct MapInfo {
-  uint64_t start;
-  uint64_t end;
-  uint64_t offset;
-  uint16_t flags;
+  MapInfo() = default;
+  MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
+  MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
+      : start(start), end(end), offset(offset), flags(flags), name(name) {}
+  ~MapInfo() { delete elf; }
+
+  uint64_t start = 0;
+  uint64_t end = 0;
+  uint64_t offset = 0;
+  uint16_t flags = 0;
   std::string name;
   Elf* elf = nullptr;
   // 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.
-  uint64_t elf_offset;
+  uint64_t elf_offset = 0;
 
   // This function guarantees it will never return nullptr.
   Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
 
+  uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
+
  private:
+  MapInfo(const MapInfo&) = delete;
+  void operator=(const MapInfo&) = delete;
+
   Memory* GetFileMemory();
 
   Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
+
+  // Protect the creation of the elf object.
+  std::mutex mutex_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 22122a9..34fef7f 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -42,11 +42,11 @@
 
   virtual const std::string GetMapsFile() const { return ""; }
 
-  typedef std::vector<MapInfo>::iterator iterator;
+  typedef std::vector<MapInfo*>::iterator iterator;
   iterator begin() { return maps_.begin(); }
   iterator end() { return maps_.end(); }
 
-  typedef std::vector<MapInfo>::const_iterator const_iterator;
+  typedef std::vector<MapInfo*>::const_iterator const_iterator;
   const_iterator begin() const { return maps_.begin(); }
   const_iterator end() const { return maps_.end(); }
 
@@ -54,11 +54,11 @@
 
   MapInfo* Get(size_t index) {
     if (index >= maps_.size()) return nullptr;
-    return &maps_[index];
+    return maps_[index];
   }
 
  protected:
-  std::vector<MapInfo> maps_;
+  std::vector<MapInfo*> maps_;
 };
 
 class RemoteMaps : public Maps {
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 183b899..8163152 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -36,7 +36,9 @@
 
   virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
 
-  virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
+  virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
+
+  bool ReadFully(uint64_t addr, void* dst, size_t size);
 
   inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) {
     if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) {
@@ -47,12 +49,16 @@
       return false;
     }
     // The read will check if offset + size overflows.
-    return Read(offset, field, size);
+    return ReadFully(offset, field, size);
   }
 
-  inline bool Read32(uint64_t addr, uint32_t* dst) { return Read(addr, dst, sizeof(uint32_t)); }
+  inline bool Read32(uint64_t addr, uint32_t* dst) {
+    return ReadFully(addr, dst, sizeof(uint32_t));
+  }
 
-  inline bool Read64(uint64_t addr, uint64_t* dst) { return Read(addr, dst, sizeof(uint64_t)); }
+  inline bool Read64(uint64_t addr, uint64_t* dst) {
+    return ReadFully(addr, dst, sizeof(uint64_t));
+  }
 };
 
 class MemoryBuffer : public Memory {
@@ -60,7 +66,7 @@
   MemoryBuffer() = default;
   virtual ~MemoryBuffer() = default;
 
-  bool Read(uint64_t addr, void* dst, size_t size) override;
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
 
   uint8_t* GetPtr(size_t offset);
 
@@ -79,7 +85,9 @@
 
   bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX);
 
-  bool Read(uint64_t addr, void* dst, size_t size) override;
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+  size_t Size() { return size_; }
 
   void Clear();
 
@@ -89,31 +97,15 @@
   uint8_t* data_ = nullptr;
 };
 
-class MemoryOffline : public MemoryFileAtOffset {
- public:
-  MemoryOffline() = default;
-  virtual ~MemoryOffline() = default;
-
-  bool Init(const std::string& file, uint64_t offset);
-
-  bool Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
-  uint64_t start_;
-};
-
 class MemoryRemote : public Memory {
  public:
   MemoryRemote(pid_t pid) : pid_(pid) {}
   virtual ~MemoryRemote() = default;
 
-  bool Read(uint64_t addr, void* dst, size_t size) override;
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
 
   pid_t pid() { return pid_; }
 
- protected:
-  virtual bool PtraceRead(uint64_t addr, long* value);
-
  private:
   pid_t pid_;
 };
@@ -123,20 +115,38 @@
   MemoryLocal() = default;
   virtual ~MemoryLocal() = default;
 
-  bool Read(uint64_t addr, void* dst, size_t size) override;
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
 };
 
+// MemoryRange maps one address range onto another.
+// The range [src_begin, src_begin + length) in the underlying Memory is mapped onto offset,
+// such that range.read(offset) is equivalent to underlying.read(src_begin).
 class MemoryRange : public Memory {
  public:
-  MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t end);
+  MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
+              uint64_t offset);
   virtual ~MemoryRange() = default;
 
-  bool Read(uint64_t addr, void* dst, size_t size) override;
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
 
  private:
   std::shared_ptr<Memory> memory_;
   uint64_t begin_;
   uint64_t length_;
+  uint64_t offset_;
+};
+
+class MemoryOffline : public Memory {
+ public:
+  MemoryOffline() = default;
+  virtual ~MemoryOffline() = default;
+
+  bool Init(const std::string& file, uint64_t offset);
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+  std::unique_ptr<MemoryRange> memory_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 6576e4c..7025fcf 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -27,10 +27,8 @@
 
 // Forward declarations.
 class Elf;
-struct MapInfo;
+enum ArchEnum : uint8_t;
 class Memory;
-struct x86_ucontext_t;
-struct x86_64_ucontext_t;
 
 class Regs {
  public:
@@ -51,7 +49,7 @@
       : total_regs_(total_regs), sp_reg_(sp_reg), return_loc_(return_loc) {}
   virtual ~Regs() = default;
 
-  virtual uint32_t MachineType() = 0;
+  virtual ArchEnum Arch() = 0;
 
   virtual void* RawData() = 0;
   virtual uint64_t pc() = 0;
@@ -70,9 +68,9 @@
   uint16_t sp_reg() { return sp_reg_; }
   uint16_t total_regs() { return total_regs_; }
 
-  static uint32_t CurrentMachineType();
+  static ArchEnum CurrentArch();
   static Regs* RemoteGet(pid_t pid);
-  static Regs* CreateFromUcontext(uint32_t machine_type, void* ucontext);
+  static Regs* CreateFromUcontext(ArchEnum arch, void* ucontext);
   static Regs* CreateFromLocal();
 
  protected:
@@ -110,82 +108,6 @@
   std::vector<AddressType> regs_;
 };
 
-class RegsArm : public RegsImpl<uint32_t> {
- public:
-  RegsArm();
-  virtual ~RegsArm() = default;
-
-  virtual uint32_t MachineType() override final;
-
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
-
-  void SetFromRaw() override;
-
-  bool SetPcFromReturnAddress(Memory* process_memory) override;
-
-  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
-
-  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
-};
-
-class RegsArm64 : public RegsImpl<uint64_t> {
- public:
-  RegsArm64();
-  virtual ~RegsArm64() = default;
-
-  virtual uint32_t MachineType() override final;
-
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
-
-  void SetFromRaw() override;
-
-  bool SetPcFromReturnAddress(Memory* process_memory) override;
-
-  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
-
-  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
-};
-
-class RegsX86 : public RegsImpl<uint32_t> {
- public:
-  RegsX86();
-  virtual ~RegsX86() = default;
-
-  virtual uint32_t MachineType() override final;
-
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
-
-  void SetFromRaw() override;
-
-  bool SetPcFromReturnAddress(Memory* process_memory) override;
-
-  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
-
-  void SetFromUcontext(x86_ucontext_t* ucontext);
-
-  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
-};
-
-class RegsX86_64 : public RegsImpl<uint64_t> {
- public:
-  RegsX86_64();
-  virtual ~RegsX86_64() = default;
-
-  virtual uint32_t MachineType() override final;
-
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
-
-  void SetFromRaw() override;
-
-  bool SetPcFromReturnAddress(Memory* process_memory) override;
-
-  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
-
-  void SetFromUcontext(x86_64_ucontext_t* ucontext);
-
-  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
-};
-
 }  // namespace unwindstack
 
 #endif  // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/include/unwindstack/RegsArm.h b/libunwindstack/include/unwindstack/RegsArm.h
new file mode 100644
index 0000000..b5d344b
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsArm.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_ARM_H
+#define _LIBUNWINDSTACK_REGS_ARM_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsArm : public RegsImpl<uint32_t> {
+ public:
+  RegsArm();
+  virtual ~RegsArm() = default;
+
+  virtual ArchEnum Arch() override final;
+
+  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+  void SetFromRaw() override;
+
+  bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+  static Regs* Read(void* data);
+
+  static Regs* CreateFromUcontext(void* ucontext);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_ARM_H
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
new file mode 100644
index 0000000..30e626c
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_ARM64_H
+#define _LIBUNWINDSTACK_REGS_ARM64_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsArm64 : public RegsImpl<uint64_t> {
+ public:
+  RegsArm64();
+  virtual ~RegsArm64() = default;
+
+  virtual ArchEnum Arch() override final;
+
+  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+  void SetFromRaw() override;
+
+  bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+  static Regs* Read(void* data);
+
+  static Regs* CreateFromUcontext(void* ucontext);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_ARM64_H
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
index d1461d8..c59e081 100644
--- a/libunwindstack/include/unwindstack/RegsGetLocal.h
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -33,7 +33,7 @@
 
 #if defined(__arm__)
 
-inline void RegsGetLocal(Regs* regs) {
+inline __always_inline void RegsGetLocal(Regs* regs) {
   void* reg_data = regs->RawData();
   asm volatile(
       ".align 2\n"
@@ -57,7 +57,7 @@
 
 #elif defined(__aarch64__)
 
-inline void RegsGetLocal(Regs* regs) {
+inline __always_inline void RegsGetLocal(Regs* regs) {
   void* reg_data = regs->RawData();
   asm volatile(
       "1:\n"
diff --git a/libunwindstack/include/unwindstack/RegsX86.h b/libunwindstack/include/unwindstack/RegsX86.h
new file mode 100644
index 0000000..a695bbf
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsX86.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_X86_H
+#define _LIBUNWINDSTACK_REGS_X86_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+struct x86_ucontext_t;
+
+class RegsX86 : public RegsImpl<uint32_t> {
+ public:
+  RegsX86();
+  virtual ~RegsX86() = default;
+
+  virtual ArchEnum Arch() override final;
+
+  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+  void SetFromRaw() override;
+
+  bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+  void SetFromUcontext(x86_ucontext_t* ucontext);
+
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+  static Regs* Read(void* data);
+
+  static Regs* CreateFromUcontext(void* ucontext);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_X86_H
diff --git a/libunwindstack/include/unwindstack/RegsX86_64.h b/libunwindstack/include/unwindstack/RegsX86_64.h
new file mode 100644
index 0000000..23a3f20
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsX86_64.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_X86_64_H
+#define _LIBUNWINDSTACK_REGS_X86_64_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+struct x86_64_ucontext_t;
+
+class RegsX86_64 : public RegsImpl<uint64_t> {
+ public:
+  RegsX86_64();
+  virtual ~RegsX86_64() = default;
+
+  virtual ArchEnum Arch() override final;
+
+  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+  void SetFromRaw() override;
+
+  bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+  void SetFromUcontext(x86_64_ucontext_t* ucontext);
+
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+  static Regs* Read(void* data);
+
+  static Regs* CreateFromUcontext(void* ucontext);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_X86_64_H
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 37a76b2..b64d460 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -70,7 +70,7 @@
   static std::string FormatFrame(const FrameData& frame, bool bits32);
 
  private:
-  void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc);
+  void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc);
 
   size_t max_frames_;
   Maps* maps_;
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 94cb493..8d6d00d 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -24,7 +24,7 @@
 #include <gtest/gtest.h>
 
 #include <unwindstack/Log.h>
-#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
 
 #include "ArmExidx.h"
 
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 90baabe..07204bc 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -35,8 +35,8 @@
   ~MockDwarfDebugFrame() = default;
 
   void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
-  void TestSetOffset(uint64_t offset) { this->offset_ = offset; }
-  void TestSetEndOffset(uint64_t offset) { this->end_offset_ = offset; }
+  void TestSetOffset(uint64_t offset) { this->entries_offset_ = offset; }
+  void TestSetEndOffset(uint64_t offset) { this->entries_end_ = offset; }
   void TestPushFdeInfo(const typename DwarfDebugFrame<TypeParam>::FdeInfo& info) {
     this->fdes_.push_back(info);
   }
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 3dbabe1..3a629f8 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -34,28 +34,19 @@
   MockDwarfEhFrame(Memory* memory) : DwarfEhFrame<TypeParam>(memory) {}
   ~MockDwarfEhFrame() = default;
 
-  void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
-  void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
-  void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; }
-  void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; }
-  void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; }
-  void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
-
   void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
-  void TestSetFdeInfo(uint64_t index, const typename DwarfEhFrame<TypeParam>::FdeInfo& info) {
-    this->fde_info_[index] = info;
+  void TestSetOffset(uint64_t offset) { this->entries_offset_ = offset; }
+  void TestSetEndOffset(uint64_t offset) { this->entries_end_ = offset; }
+  void TestPushFdeInfo(const typename DwarfEhFrame<TypeParam>::FdeInfo& info) {
+    this->fdes_.push_back(info);
   }
 
-  uint8_t TestGetVersion() { return this->version_; }
-  uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; }
-  uint64_t TestGetPtrOffset() { return this->ptr_offset_; }
-  uint8_t TestGetTableEncoding() { return this->table_encoding_; }
-  uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
   uint64_t TestGetFdeCount() { return this->fde_count_; }
-  uint64_t TestGetEntriesOffset() { return this->entries_offset_; }
-  uint64_t TestGetEntriesEnd() { return this->entries_end_; }
-  uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; }
-  uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; }
+  uint8_t TestGetOffset() { return this->offset_; }
+  uint8_t TestGetEndOffset() { return this->end_offset_; }
+  void TestGetFdeInfo(size_t index, typename DwarfEhFrame<TypeParam>::FdeInfo* info) {
+    *info = this->fdes_[index];
+  }
 };
 
 template <typename TypeParam>
@@ -76,248 +67,304 @@
 
 // NOTE: All test class variables need to be referenced as this->.
 
-TYPED_TEST_P(DwarfEhFrameTest, Init) {
-  this->memory_.SetMemory(
-      0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
-  this->memory_.SetData16(0x1004, 0x500);
-  this->memory_.SetData32(0x1006, 126);
+TYPED_TEST_P(DwarfEhFrameTest, Init32) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0);
+  this->memory_.SetData8(0x5008, 1);
+  this->memory_.SetData8(0x5009, '\0');
 
-  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
-  EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
-  EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
-  EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
-  EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
-  EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
-  EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
-  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
-  EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
-  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
-  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0x104);
+  this->memory_.SetData32(0x5108, 0x1500);
+  this->memory_.SetData32(0x510c, 0x200);
 
-  // Verify an unexpected version will cause a fail.
-  this->memory_.SetData8(0x1000, 0);
-  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
-  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
-  this->memory_.SetData8(0x1000, 2);
-  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
-  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+  this->memory_.SetData32(0x5200, 0xfc);
+  this->memory_.SetData32(0x5204, 0x204);
+  this->memory_.SetData32(0x5208, 0x2500);
+  this->memory_.SetData32(0x520c, 0x300);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x5300, 0xfc);
+  this->memory_.SetData32(0x5304, 0);
+  this->memory_.SetData8(0x5308, 1);
+  this->memory_.SetData8(0x5309, '\0');
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5400, 0xfc);
+  this->memory_.SetData32(0x5404, 0x104);
+  this->memory_.SetData32(0x5408, 0x3500);
+  this->memory_.SetData32(0x540c, 0x400);
+
+  this->memory_.SetData32(0x5500, 0xfc);
+  this->memory_.SetData32(0x5504, 0x204);
+  this->memory_.SetData32(0x5508, 0x4500);
+  this->memory_.SetData32(0x550c, 0x500);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+  this->eh_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x6608U, info.start);
+  EXPECT_EQ(0x6808U, info.end);
+
+  this->eh_frame_->TestGetFdeInfo(1, &info);
+  EXPECT_EQ(0x5200U, info.offset);
+  EXPECT_EQ(0x7708U, info.start);
+  EXPECT_EQ(0x7a08U, info.end);
+
+  this->eh_frame_->TestGetFdeInfo(2, &info);
+  EXPECT_EQ(0x5400U, info.offset);
+  EXPECT_EQ(0x8908U, info.start);
+  EXPECT_EQ(0x8d08U, info.end);
+
+  this->eh_frame_->TestGetFdeInfo(3, &info);
+  EXPECT_EQ(0x5500U, info.offset);
+  EXPECT_EQ(0x9a08U, info.start);
+  EXPECT_EQ(0x9f08U, info.end);
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_expect_cache_fail) {
-  this->eh_frame_->TestSetTableEntrySize(0x10);
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-  ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
-  ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+TYPED_TEST_P(DwarfEhFrameTest, Init32_fde_not_following_cie) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0);
+  this->memory_.SetData8(0x5008, 1);
+  this->memory_.SetData8(0x5009, '\0');
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0x1000);
+  this->memory_.SetData32(0x5108, 0x1500);
+  this->memory_.SetData32(0x510c, 0x200);
+
+  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->last_error());
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_pcrel) {
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
-  this->eh_frame_->TestSetEntriesOffset(0x1000);
-  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
-  this->eh_frame_->TestSetTableEntrySize(0x10);
+TYPED_TEST_P(DwarfEhFrameTest, Init64) {
+  // CIE 64 information.
+  this->memory_.SetData32(0x5000, 0xffffffff);
+  this->memory_.SetData64(0x5004, 0xf4);
+  this->memory_.SetData64(0x500c, 0);
+  this->memory_.SetData8(0x5014, 1);
+  this->memory_.SetData8(0x5015, '\0');
 
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
+  // FDE 64 information.
+  this->memory_.SetData32(0x5100, 0xffffffff);
+  this->memory_.SetData64(0x5104, 0xf4);
+  this->memory_.SetData64(0x510c, 0x10c);
+  this->memory_.SetData64(0x5114, 0x1500);
+  this->memory_.SetData64(0x511c, 0x200);
 
-  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
-  ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x1384U, info->pc);
-  EXPECT_EQ(0x1540U, info->offset);
+  this->memory_.SetData32(0x5200, 0xffffffff);
+  this->memory_.SetData64(0x5204, 0xf4);
+  this->memory_.SetData64(0x520c, 0x20c);
+  this->memory_.SetData64(0x5214, 0x2500);
+  this->memory_.SetData64(0x521c, 0x300);
+
+  // CIE 64 information.
+  this->memory_.SetData32(0x5300, 0xffffffff);
+  this->memory_.SetData64(0x5304, 0xf4);
+  this->memory_.SetData64(0x530c, 0);
+  this->memory_.SetData8(0x5314, 1);
+  this->memory_.SetData8(0x5315, '\0');
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x5400, 0xffffffff);
+  this->memory_.SetData64(0x5404, 0xf4);
+  this->memory_.SetData64(0x540c, 0x10c);
+  this->memory_.SetData64(0x5414, 0x3500);
+  this->memory_.SetData64(0x541c, 0x400);
+
+  this->memory_.SetData32(0x5500, 0xffffffff);
+  this->memory_.SetData64(0x5504, 0xf4);
+  this->memory_.SetData64(0x550c, 0x20c);
+  this->memory_.SetData64(0x5514, 0x4500);
+  this->memory_.SetData64(0x551c, 0x500);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+  this->eh_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x6618U, info.start);
+  EXPECT_EQ(0x6818U, info.end);
+
+  this->eh_frame_->TestGetFdeInfo(1, &info);
+  EXPECT_EQ(0x5200U, info.offset);
+  EXPECT_EQ(0x7718U, info.start);
+  EXPECT_EQ(0x7a18U, info.end);
+
+  this->eh_frame_->TestGetFdeInfo(2, &info);
+  EXPECT_EQ(0x5400U, info.offset);
+  EXPECT_EQ(0x8918U, info.start);
+  EXPECT_EQ(0x8d18U, info.end);
+
+  this->eh_frame_->TestGetFdeInfo(3, &info);
+  EXPECT_EQ(0x5500U, info.offset);
+  EXPECT_EQ(0x9a18U, info.start);
+  EXPECT_EQ(0x9f18U, info.end);
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_datarel) {
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
-  this->eh_frame_->TestSetEntriesOffset(0x1000);
-  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
-  this->eh_frame_->TestSetTableEntrySize(0x10);
+TYPED_TEST_P(DwarfEhFrameTest, Init64_fde_not_following_cie) {
+  // CIE 64 information.
+  this->memory_.SetData32(0x5000, 0xffffffff);
+  this->memory_.SetData64(0x5004, 0xf4);
+  this->memory_.SetData64(0x500c, 0);
+  this->memory_.SetData8(0x5014, 1);
+  this->memory_.SetData8(0x5015, '\0');
 
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
+  // FDE 64 information.
+  this->memory_.SetData32(0x5100, 0xffffffff);
+  this->memory_.SetData64(0x5104, 0xf4);
+  this->memory_.SetData64(0x510c, 0x1000);
+  this->memory_.SetData64(0x5114, 0x1500);
+  this->memory_.SetData64(0x511c, 0x200);
 
-  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
-  ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x3344U, info->pc);
-  EXPECT_EQ(0x3500U, info->offset);
+  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->last_error());
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_cached) {
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-  this->eh_frame_->TestSetEntriesOffset(0x1000);
-  this->eh_frame_->TestSetTableEntrySize(0x10);
+TYPED_TEST_P(DwarfEhFrameTest, Init_version1) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0);
+  this->memory_.SetData8(0x5008, 1);
+  // Augment string.
+  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'});
+  // Code alignment factor.
+  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00});
+  // Data alignment factor.
+  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+  // Return address register
+  this->memory_.SetData8(0x5014, 0x84);
+  // Augmentation length
+  this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00});
+  // R data.
+  this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2);
 
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0x104);
+  this->memory_.SetData16(0x5108, 0x1500);
+  this->memory_.SetData16(0x510a, 0x200);
 
-  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
-  ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x344U, info->pc);
-  EXPECT_EQ(0x500U, info->offset);
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
+  ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
 
-  // Clear the memory so that this will fail if it doesn't read cached data.
-  this->memory_.Clear();
-
-  info = this->eh_frame_->GetFdeInfoFromIndex(2);
-  ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x344U, info->pc);
-  EXPECT_EQ(0x500U, info->offset);
+  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  this->eh_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x6606U, info.start);
+  EXPECT_EQ(0x6806U, info.end);
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetBinary_verify) {
-  this->eh_frame_->TestSetTableEntrySize(0x10);
-  this->eh_frame_->TestSetFdeCount(10);
+TYPED_TEST_P(DwarfEhFrameTest, Init_version4) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0);
+  this->memory_.SetData8(0x5008, 4);
+  // Augment string.
+  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
+  // Address size.
+  this->memory_.SetData8(0x500e, 4);
+  // Segment size.
+  this->memory_.SetData8(0x500f, 0);
+  // Code alignment factor.
+  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00});
+  // Data alignment factor.
+  this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+  // Return address register
+  this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10});
+  // Augmentation length
+  this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00});
+  // L data.
+  this->memory_.SetData8(0x501a, 0x10);
+  // P data.
+  this->memory_.SetData8(0x501b, DW_EH_PE_udata4);
+  this->memory_.SetData32(0x501c, 0x100);
+  // R data.
+  this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2);
 
-  typename DwarfEhFrame<TypeParam>::FdeInfo info;
-  for (size_t i = 0; i < 10; i++) {
-    info.pc = 0x1000 * (i + 1);
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0x104);
+  this->memory_.SetData16(0x5108, 0x1500);
+  this->memory_.SetData16(0x510a, 0x200);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
+  ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  this->eh_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x6606U, info.start);
+  EXPECT_EQ(0x6806U, info.end);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc) {
+  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  for (size_t i = 0; i < 9; i++) {
+    info.start = 0x1000 * (i + 1);
+    info.end = 0x1000 * (i + 2) - 0x10;
     info.offset = 0x5000 + i * 0x20;
-    this->eh_frame_->TestSetFdeInfo(i, info);
+    this->eh_frame_->TestPushFdeInfo(info);
   }
 
+  this->eh_frame_->TestSetFdeCount(0);
   uint64_t fde_offset;
-  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
-  // Not an error, just not found.
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
   ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
-  // Even number of elements.
-  for (size_t i = 0; i < 10; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10)) << "Failed at index "
-                                                                              << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-  }
+
+  this->eh_frame_->TestSetFdeCount(9);
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
   // Odd number of elements.
   for (size_t i = 0; i < 9; i++) {
     TypeParam pc = 0x1000 * (i + 1);
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i;
+    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
     EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9)) << "Failed at index "
-                                                                             << i;
+    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " << i;
     EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
+    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
         << "Failed at index " << i;
     EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+        << "Failed at index " << i;
+    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+  }
+
+  // Even number of elements.
+  this->eh_frame_->TestSetFdeCount(10);
+  info.start = 0xa000;
+  info.end = 0xaff0;
+  info.offset = 0x5120;
+  this->eh_frame_->TestPushFdeInfo(info);
+
+  for (size_t i = 0; i < 10; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+        << "Failed at index " << i;
+    ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
   }
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential) {
-  this->eh_frame_->TestSetFdeCount(10);
-  this->eh_frame_->TestSetEntriesDataOffset(0x100);
-  this->eh_frame_->TestSetEntriesEnd(0x2000);
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
-
-  this->memory_.SetData32(0x1048, 0x440);
-  this->memory_.SetData32(0x104c, 0x600);
-
-  // Verify that if entries is zero, that it fails.
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
-  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
-
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
-  EXPECT_EQ(0x500U, fde_offset);
-
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
-  EXPECT_EQ(0x600U, fde_offset);
-
-  // Expect that the data is cached so no more memory reads will occur.
-  this->memory_.Clear();
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
-  EXPECT_EQ(0x600U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_last_element) {
-  this->eh_frame_->TestSetFdeCount(2);
-  this->eh_frame_->TestSetEntriesDataOffset(0x100);
-  this->eh_frame_->TestSetEntriesEnd(0x2000);
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
-
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
-
-  this->memory_.SetData32(0x1048, 0x440);
-  this->memory_.SetData32(0x104c, 0x600);
-
-  uint64_t fde_offset;
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
-  EXPECT_EQ(0x600U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_end_check) {
-  this->eh_frame_->TestSetFdeCount(2);
-  this->eh_frame_->TestSetEntriesDataOffset(0x100);
-  this->eh_frame_->TestSetEntriesEnd(0x1048);
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
-
-  this->memory_.SetData32(0x1048, 0x440);
-  this->memory_.SetData32(0x104c, 0x600);
-
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_fail_fde_count) {
-  this->eh_frame_->TestSetFdeCount(0);
-
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_binary_search) {
-  this->eh_frame_->TestSetTableEntrySize(16);
-  this->eh_frame_->TestSetFdeCount(10);
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info;
-  info.pc = 0x550;
-  info.offset = 0x10500;
-  this->eh_frame_->TestSetFdeInfo(5, info);
-  info.pc = 0x750;
-  info.offset = 0x10700;
-  this->eh_frame_->TestSetFdeInfo(7, info);
-  info.pc = 0x850;
-  info.offset = 0x10800;
-  this->eh_frame_->TestSetFdeInfo(8, info);
-
-  uint64_t fde_offset;
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x800, &fde_offset));
-  EXPECT_EQ(0x10700U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_sequential_search) {
-  this->eh_frame_->TestSetFdeCount(10);
-  this->eh_frame_->TestSetTableEntrySize(0);
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info;
-  info.pc = 0x50;
-  info.offset = 0x10000;
-  this->eh_frame_->TestSetFdeInfo(0, info);
-  info.pc = 0x150;
-  info.offset = 0x10100;
-  this->eh_frame_->TestSetFdeInfo(1, info);
-  info.pc = 0x250;
-  info.offset = 0x10200;
-  this->eh_frame_->TestSetFdeInfo(2, info);
-
-  uint64_t fde_offset;
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset));
-  EXPECT_EQ(0x10100U, fde_offset);
-}
-
 TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) {
+  this->eh_frame_->TestSetOffset(0x4000);
+
   // CIE 32 information.
   this->memory_.SetData32(0xf000, 0x100);
   this->memory_.SetData32(0xf004, 0);
@@ -337,8 +384,8 @@
   ASSERT_TRUE(fde != nullptr);
   EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
   EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x1d00cU, fde->pc_start);
-  EXPECT_EQ(0x1d10cU, fde->pc_end);
+  EXPECT_EQ(0x1d008U, fde->pc_start);
+  EXPECT_EQ(0x1d108U, fde->pc_end);
   EXPECT_EQ(0xf000U, fde->cie_offset);
   EXPECT_EQ(0U, fde->lsda_address);
 
@@ -358,6 +405,8 @@
 }
 
 TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
+  this->eh_frame_->TestSetOffset(0x2000);
+
   // CIE 64 information.
   this->memory_.SetData32(0x6000, 0xffffffff);
   this->memory_.SetData64(0x6004, 0x100);
@@ -379,8 +428,8 @@
   ASSERT_TRUE(fde != nullptr);
   EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
   EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
-  EXPECT_EQ(0xd01cU, fde->pc_start);
-  EXPECT_EQ(0xd31cU, fde->pc_end);
+  EXPECT_EQ(0xd018U, fde->pc_start);
+  EXPECT_EQ(0xd318U, fde->pc_end);
   EXPECT_EQ(0x6000U, fde->cie_offset);
   EXPECT_EQ(0U, fde->lsda_address);
 
@@ -399,25 +448,9 @@
   EXPECT_EQ(0x20U, fde->cie->return_address_register);
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeFromPc_fde_not_found) {
-  this->eh_frame_->TestSetTableEntrySize(16);
-  this->eh_frame_->TestSetFdeCount(1);
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info;
-  info.pc = 0x550;
-  info.offset = 0x10500;
-  this->eh_frame_->TestSetFdeInfo(0, info);
-
-  ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
-}
-
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init, GetFdeInfoFromIndex_expect_cache_fail,
-                           GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
-                           GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
-                           GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
-                           GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
-                           GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
-                           GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init32, Init32_fde_not_following_cie, Init64,
+                           Init64_fde_not_following_cie, Init_version1, Init_version4,
+                           GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
new file mode 100644
index 0000000..64b325b
--- /dev/null
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DwarfEhFrameWithHdr.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class MockDwarfEhFrameWithHdr : public DwarfEhFrameWithHdr<TypeParam> {
+ public:
+  MockDwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrameWithHdr<TypeParam>(memory) {}
+  ~MockDwarfEhFrameWithHdr() = default;
+
+  void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
+  void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
+  void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; }
+  void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; }
+  void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; }
+  void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
+
+  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+  void TestSetFdeInfo(uint64_t index, const typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo& info) {
+    this->fde_info_[index] = info;
+  }
+
+  uint8_t TestGetVersion() { return this->version_; }
+  uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; }
+  uint64_t TestGetPtrOffset() { return this->ptr_offset_; }
+  uint8_t TestGetTableEncoding() { return this->table_encoding_; }
+  uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
+  uint64_t TestGetFdeCount() { return this->fde_count_; }
+  uint64_t TestGetEntriesOffset() { return this->entries_offset_; }
+  uint64_t TestGetEntriesEnd() { return this->entries_end_; }
+  uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; }
+  uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; }
+};
+
+template <typename TypeParam>
+class DwarfEhFrameWithHdrTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    eh_frame_ = new MockDwarfEhFrameWithHdr<TypeParam>(&memory_);
+    ResetLogs();
+  }
+
+  void TearDown() override { delete eh_frame_; }
+
+  MemoryFake memory_;
+  MockDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init) {
+  this->memory_.SetMemory(
+      0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 126);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
+  EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+  EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
+  EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
+  EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+  EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
+  EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
+  EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
+  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+
+  // Verify an unexpected version will cause a fail.
+  this->memory_.SetData8(0x1000, 0);
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+  this->memory_.SetData8(0x1000, 2);
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_expect_cache_fail) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+  ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_pcrel) {
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x1384U, info->pc);
+  EXPECT_EQ(0x1540U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_datarel) {
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x3344U, info->pc);
+  EXPECT_EQ(0x3500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_cached) {
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x344U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
+
+  // Clear the memory so that this will fail if it doesn't read cached data.
+  this->memory_.Clear();
+
+  info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x344U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_verify) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
+  for (size_t i = 0; i < 10; i++) {
+    info.pc = 0x1000 * (i + 1);
+    info.offset = 0x5000 + i * 0x20;
+    this->eh_frame_->TestSetFdeInfo(i, info);
+  }
+
+  uint64_t fde_offset;
+  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
+  // Not an error, just not found.
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+  // Even number of elements.
+  for (size_t i = 0; i < 10; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+  }
+  // Odd number of elements.
+  for (size_t i = 0; i < 9; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+  }
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_index_fail) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  uint64_t fde_offset;
+  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x1000, &fde_offset, 10));
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential) {
+  this->eh_frame_->TestSetFdeCount(10);
+  this->eh_frame_->TestSetEntriesDataOffset(0x100);
+  this->eh_frame_->TestSetEntriesEnd(0x2000);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  this->memory_.SetData32(0x1048, 0x440);
+  this->memory_.SetData32(0x104c, 0x600);
+
+  // Verify that if entries is zero, that it fails.
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
+  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
+  EXPECT_EQ(0x500U, fde_offset);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+
+  // Expect that the data is cached so no more memory reads will occur.
+  this->memory_.Clear();
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential_last_element) {
+  this->eh_frame_->TestSetFdeCount(2);
+  this->eh_frame_->TestSetEntriesDataOffset(0x100);
+  this->eh_frame_->TestSetEntriesEnd(0x2000);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  this->memory_.SetData32(0x1048, 0x440);
+  this->memory_.SetData32(0x104c, 0x600);
+
+  uint64_t fde_offset;
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential_end_check) {
+  this->eh_frame_->TestSetFdeCount(2);
+  this->eh_frame_->TestSetEntriesDataOffset(0x100);
+  this->eh_frame_->TestSetEntriesEnd(0x1048);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  this->memory_.SetData32(0x1048, 0x440);
+  this->memory_.SetData32(0x104c, 0x600);
+
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_fail_fde_count) {
+  this->eh_frame_->TestSetFdeCount(0);
+
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_binary_search) {
+  this->eh_frame_->TestSetTableEntrySize(16);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
+  info.pc = 0x550;
+  info.offset = 0x10500;
+  this->eh_frame_->TestSetFdeInfo(5, info);
+  info.pc = 0x750;
+  info.offset = 0x10700;
+  this->eh_frame_->TestSetFdeInfo(7, info);
+  info.pc = 0x850;
+  info.offset = 0x10800;
+  this->eh_frame_->TestSetFdeInfo(8, info);
+
+  uint64_t fde_offset;
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x800, &fde_offset));
+  EXPECT_EQ(0x10700U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_sequential_search) {
+  this->eh_frame_->TestSetFdeCount(10);
+  this->eh_frame_->TestSetTableEntrySize(0);
+
+  typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
+  info.pc = 0x50;
+  info.offset = 0x10000;
+  this->eh_frame_->TestSetFdeInfo(0, info);
+  info.pc = 0x150;
+  info.offset = 0x10100;
+  this->eh_frame_->TestSetFdeInfo(1, info);
+  info.pc = 0x250;
+  info.offset = 0x10200;
+  this->eh_frame_->TestSetFdeInfo(2, info);
+
+  uint64_t fde_offset;
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset));
+  EXPECT_EQ(0x10100U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde32) {
+  // CIE 32 information.
+  this->memory_.SetData32(0xf000, 0x100);
+  this->memory_.SetData32(0xf004, 0);
+  this->memory_.SetData8(0xf008, 0x1);
+  this->memory_.SetData8(0xf009, '\0');
+  this->memory_.SetData8(0xf00a, 4);
+  this->memory_.SetData8(0xf00b, 8);
+  this->memory_.SetData8(0xf00c, 0x20);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x14000, 0x20);
+  this->memory_.SetData32(0x14004, 0x5004);
+  this->memory_.SetData32(0x14008, 0x9000);
+  this->memory_.SetData32(0x1400c, 0x100);
+
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x14000);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x1d008U, fde->pc_start);
+  EXPECT_EQ(0x1d108U, fde->pc_end);
+  EXPECT_EQ(0xf000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+  EXPECT_EQ(0U, fde->cie->segment_size);
+  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+  EXPECT_EQ(0U, fde->cie->personality_handler);
+  EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
+  EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
+  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+  EXPECT_EQ(8, fde->cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde64) {
+  // CIE 64 information.
+  this->memory_.SetData32(0x6000, 0xffffffff);
+  this->memory_.SetData64(0x6004, 0x100);
+  this->memory_.SetData64(0x600c, 0);
+  this->memory_.SetData8(0x6014, 0x1);
+  this->memory_.SetData8(0x6015, '\0');
+  this->memory_.SetData8(0x6016, 4);
+  this->memory_.SetData8(0x6017, 8);
+  this->memory_.SetData8(0x6018, 0x20);
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x8000, 0xffffffff);
+  this->memory_.SetData64(0x8004, 0x200);
+  this->memory_.SetData64(0x800c, 0x200c);
+  this->memory_.SetData64(0x8014, 0x5000);
+  this->memory_.SetData64(0x801c, 0x300);
+
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x8000);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0xd018U, fde->pc_start);
+  EXPECT_EQ(0xd318U, fde->pc_end);
+  EXPECT_EQ(0x6000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+  EXPECT_EQ(0U, fde->cie->segment_size);
+  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+  EXPECT_EQ(0U, fde->cie->personality_handler);
+  EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+  EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+  EXPECT_EQ(8, fde->cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeFromPc_fde_not_found) {
+  this->eh_frame_->TestSetTableEntrySize(16);
+  this->eh_frame_->TestSetFdeCount(1);
+
+  typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
+  info.pc = 0x550;
+  info.offset = 0x10500;
+  this->eh_frame_->TestSetFdeInfo(0, info);
+
+  ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, GetFdeInfoFromIndex_expect_cache_fail,
+                           GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
+                           GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
+                           GetFdeOffsetBinary_index_fail, GetFdeOffsetSequential,
+                           GetFdeOffsetSequential_last_element, GetFdeOffsetSequential_end_check,
+                           GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_binary_search,
+                           GetFdeOffsetFromPc_sequential_search, GetCieFde32, GetCieFde64,
+                           GetFdeFromPc_fde_not_found);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 5b9f3ee..d54b0bf 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -42,16 +42,16 @@
 
   MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
 
-  MOCK_METHOD1(IsCie32, bool(uint32_t));
-
-  MOCK_METHOD1(IsCie64, bool(uint64_t));
-
   MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
 
   MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
 
   MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
 
+  void TestSetCie32Value(uint32_t value32) { this->cie32_value_ = value32; }
+
+  void TestSetCie64Value(uint64_t value64) { this->cie64_value_ = value64; }
+
   void TestSetCachedCieEntry(uint64_t offset, const DwarfCie& cie) {
     this->cie_entries_[offset] = cie;
   }
@@ -77,6 +77,8 @@
     memory_.Clear();
     section_ = new MockDwarfSectionImpl<TypeParam>(&memory_);
     ResetLogs();
+    section_->TestSetCie32Value(static_cast<uint32_t>(-1));
+    section_->TestSetCie64Value(static_cast<uint64_t>(-1));
   }
 
   void TearDown() override { delete section_; }
@@ -448,8 +450,6 @@
   this->memory_.SetData8(0x500b, 8);
   this->memory_.SetData8(0x500c, 0x20);
 
-  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
-
   const DwarfCie* cie = this->section_->GetCie(0x5000);
   ASSERT_TRUE(cie != nullptr);
   EXPECT_EQ(1U, cie->version);
@@ -493,8 +493,6 @@
   this->memory_.SetMemory(0x500b, std::vector<uint8_t>{0xfc, 0xff, 0xff, 0xff, 0x7f});
   this->memory_.SetData8(0x5010, 0x20);
 
-  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
-
   const DwarfCie* cie = this->section_->GetCie(0x5000);
   ASSERT_TRUE(cie != nullptr);
   EXPECT_EQ(1U, cie->version);
@@ -514,15 +512,13 @@
 TYPED_TEST_P(DwarfSectionImplTest, GetCie_64_no_augment) {
   this->memory_.SetData32(0x8000, 0xffffffff);
   this->memory_.SetData64(0x8004, 0x200);
-  this->memory_.SetData64(0x800c, 0xffffffff);
+  this->memory_.SetData64(0x800c, 0xffffffffffffffffULL);
   this->memory_.SetData8(0x8014, 0x1);
   this->memory_.SetData8(0x8015, '\0');
   this->memory_.SetData8(0x8016, 4);
   this->memory_.SetData8(0x8017, 8);
   this->memory_.SetData8(0x8018, 0x20);
 
-  EXPECT_CALL(*this->section_, IsCie64(0xffffffff)).WillRepeatedly(::testing::Return(true));
-
   const DwarfCie* cie = this->section_->GetCie(0x8000);
   ASSERT_TRUE(cie != nullptr);
   EXPECT_EQ(1U, cie->version);
@@ -557,8 +553,6 @@
   // R data.
   this->memory_.SetData8(0x5018, DW_EH_PE_udata2);
 
-  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
-
   const DwarfCie* cie = this->section_->GetCie(0x5000);
   ASSERT_TRUE(cie != nullptr);
   EXPECT_EQ(1U, cie->version);
@@ -588,8 +582,6 @@
   this->memory_.SetData8(0x500b, 8);
   this->memory_.SetMemory(0x500c, std::vector<uint8_t>{0x81, 0x03});
 
-  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
-
   const DwarfCie* cie = this->section_->GetCie(0x5000);
   ASSERT_TRUE(cie != nullptr);
   EXPECT_EQ(3U, cie->version);
@@ -617,8 +609,6 @@
   this->memory_.SetData8(0x500d, 8);
   this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x81, 0x03});
 
-  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
-
   const DwarfCie* cie = this->section_->GetCie(0x5000);
   ASSERT_TRUE(cie != nullptr);
   EXPECT_EQ(4U, cie->version);
@@ -649,7 +639,6 @@
   this->memory_.SetData32(0x4008, 0x5000);
   this->memory_.SetData32(0x400c, 0x100);
 
-  EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
   EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
   DwarfCie cie{};
   cie.fde_address_encoding = DW_EH_PE_udata4;
@@ -673,7 +662,6 @@
   this->memory_.SetData32(0x4018, 0x5000);
   this->memory_.SetData32(0x401c, 0x100);
 
-  EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
   EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
   DwarfCie cie{};
   cie.fde_address_encoding = DW_EH_PE_udata4;
@@ -700,7 +688,6 @@
   this->memory_.SetMemory(0x4010, std::vector<uint8_t>{0x82, 0x01});
   this->memory_.SetData16(0x4012, 0x1234);
 
-  EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
   EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
   DwarfCie cie{};
   cie.fde_address_encoding = DW_EH_PE_udata4;
@@ -727,7 +714,6 @@
   this->memory_.SetData32(0x4014, 0x5000);
   this->memory_.SetData32(0x4018, 0x100);
 
-  EXPECT_CALL(*this->section_, IsCie64(0x12345678)).WillOnce(::testing::Return(false));
   EXPECT_CALL(*this->section_, GetCieOffsetFromFde64(0x12345678))
       .WillOnce(::testing::Return(0x12345678));
   DwarfCie cie{};
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 4b621c9..5f7cf60 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -20,10 +20,10 @@
 
 #include <vector>
 
-#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
 
 #include "ElfInterfaceArm.h"
-#include "Machine.h"
+#include "MachineArm.h"
 
 #include "ElfFake.h"
 #include "MemoryFake.h"
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 2752e99..e138c3a 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -910,8 +910,32 @@
   memory_.SetMemory(offset, &shdr, sizeof(shdr));
   offset += ehdr.e_shentsize;
 
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_link = 2;
+  shdr.sh_name = 0x300;
+  shdr.sh_addr = 0x7000;
+  shdr.sh_offset = 0x7000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = 0x800;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_link = 2;
+  shdr.sh_name = 0x400;
+  shdr.sh_addr = 0x6000;
+  shdr.sh_offset = 0xa000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = 0xf00;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
   memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
   memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata"));
+  memory_.SetMemory(0xf300, ".eh_frame", sizeof(".eh_frame"));
+  memory_.SetMemory(0xf400, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
@@ -920,6 +944,10 @@
   EXPECT_EQ(0x500U, elf->debug_frame_size());
   EXPECT_EQ(0x5000U, elf->gnu_debugdata_offset());
   EXPECT_EQ(0x800U, elf->gnu_debugdata_size());
+  EXPECT_EQ(0x7000U, elf->eh_frame_offset());
+  EXPECT_EQ(0x800U, elf->eh_frame_size());
+  EXPECT_EQ(0xa000U, elf->eh_frame_hdr_offset());
+  EXPECT_EQ(0xf00U, elf->eh_frame_hdr_size());
 }
 
 TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 4e48fa1..00192f1 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -25,6 +25,7 @@
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
+#include <unwindstack/RegsArm.h>
 
 #include "ElfFake.h"
 #include "ElfTestUtils.h"
@@ -132,7 +133,7 @@
   ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
 
   bool finished;
-  ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
+  ASSERT_FALSE(elf.Step(0, 0, 0, nullptr, nullptr, &finished));
 }
 
 TEST_F(ElfTest, elf32_invalid_machine) {
@@ -273,7 +274,7 @@
 
   elf.FakeSetValid(true);
   elf.FakeSetLoadBias(0);
-  MapInfo map_info{.start = 0x1000, .end = 0x2000};
+  MapInfo map_info(0x1000, 0x2000);
 
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 
@@ -306,7 +307,7 @@
   elf.FakeSetValid(true);
   elf.FakeSetLoadBias(0);
   bool finished;
-  ASSERT_TRUE(elf.Step(0x1000, 0x2000, &regs, &process_memory, &finished));
+  ASSERT_TRUE(elf.Step(0x1000, 0x1000, 0x2000, &regs, &process_memory, &finished));
   EXPECT_FALSE(finished);
   EXPECT_EQ(15U, regs.pc());
   EXPECT_EQ(13U, regs.sp());
@@ -339,7 +340,7 @@
   EXPECT_CALL(*interface, Step(0x1000, &regs, &process_memory, &finished))
       .WillOnce(::testing::Return(true));
 
-  ASSERT_TRUE(elf.Step(0x1000, 0x2000, &regs, &process_memory, &finished));
+  ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, &regs, &process_memory, &finished));
 }
 
 TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
@@ -355,12 +356,12 @@
 
   // Invalid relative pc given load_bias.
   bool finished;
-  ASSERT_FALSE(elf.Step(0x1000, 0x2000, &regs, &process_memory, &finished));
+  ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, &regs, &process_memory, &finished));
 
   EXPECT_CALL(*interface, Step(0x3300, &regs, &process_memory, &finished))
       .WillOnce(::testing::Return(true));
 
-  ASSERT_TRUE(elf.Step(0x7300, 0x2000, &regs, &process_memory, &finished));
+  ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, &regs, &process_memory, &finished));
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.cpp b/libunwindstack/tests/ElfTestUtils.cpp
index 069386b..69163ac 100644
--- a/libunwindstack/tests/ElfTestUtils.cpp
+++ b/libunwindstack/tests/ElfTestUtils.cpp
@@ -47,7 +47,7 @@
   ehdr->e_ehsize = sizeof(Ehdr);
 }
 
-static std::string GetTestFileDirectory() {
+std::string TestGetFileDirectory() {
   std::string exec(testing::internal::GetArgvs()[0]);
   auto const value = exec.find_last_of('/');
   if (value == std::string::npos) {
@@ -102,7 +102,7 @@
   offset = symtab_offset + 0x100;
   if (init_gnu_debugdata) {
     // Read in the compressed elf data and copy it in.
-    name = GetTestFileDirectory();
+    name = TestGetFileDirectory();
     if (elf_class == ELFCLASS32) {
       name += "elf32.xz";
     } else {
diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h
index 6ef00e1..62cd59a 100644
--- a/libunwindstack/tests/ElfTestUtils.h
+++ b/libunwindstack/tests/ElfTestUtils.h
@@ -18,6 +18,7 @@
 #define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
 
 #include <functional>
+#include <string>
 
 namespace unwindstack {
 
@@ -30,6 +31,8 @@
 void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata,
                           TestCopyFuncType copy_func);
 
+std::string TestGetFileDirectory();
+
 }  // namespace unwindstack
 
 #endif  // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index d2aad49..866b5b4 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -94,7 +94,7 @@
 TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
 
 TEST_F(MapInfoCreateMemoryTest, end_le_start) {
-  MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
+  MapInfo info(0x100, 0x100, 0, 0, elf_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
@@ -112,7 +112,7 @@
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
+  MapInfo info(0x100, 0x200, 0x100, 0, elf_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -120,20 +120,20 @@
 
   // Read the entire file.
   std::vector<uint8_t> buffer(1024);
-  ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
+  ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 1024));
   ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
   ASSERT_EQ(ELFCLASS32, buffer[EI_CLASS]);
   for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
     ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_FALSE(memory->Read(1024, buffer.data(), 1));
+  ASSERT_FALSE(memory->ReadFully(1024, buffer.data(), 1));
 }
 
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
+  MapInfo info(0x100, 0x200, 0x100, 0, elf_at_100_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -141,14 +141,14 @@
 
   // Read the valid part of the file.
   std::vector<uint8_t> buffer(0x100);
-  ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
+  ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100));
   ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
   ASSERT_EQ(ELFCLASS64, buffer[EI_CLASS]);
   for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
     ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
+  ASSERT_FALSE(memory->ReadFully(0x100, buffer.data(), 1));
 }
 
 // Verify that if the offset is non-zero and there is an elf at that
@@ -156,7 +156,7 @@
 // embedded elf is bigger than the initial map, the new object is larger
 // than the original map size. Do this for a 32 bit elf and a 64 bit elf.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
-  MapInfo info{.start = 0x5000, .end = 0x6000, .offset = 0x1000, .name = elf32_at_map_.path};
+  MapInfo info(0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -164,15 +164,15 @@
 
   // Verify the memory is a valid elf.
   uint8_t e_ident[SELFMAG + 1];
-  ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG));
+  ASSERT_TRUE(memory->ReadFully(0, e_ident, SELFMAG));
   ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
 
   // Read past the end of what would normally be the size of the map.
-  ASSERT_TRUE(memory->Read(0x1000, e_ident, 1));
+  ASSERT_TRUE(memory->ReadFully(0x1000, e_ident, 1));
 }
 
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info{.start = 0x7000, .end = 0x8000, .offset = 0x2000, .name = elf64_at_map_.path};
+  MapInfo info(0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -180,11 +180,11 @@
 
   // Verify the memory is a valid elf.
   uint8_t e_ident[SELFMAG + 1];
-  ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG));
+  ASSERT_TRUE(memory->ReadFully(0, e_ident, SELFMAG));
   ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
 
   // Read past the end of what would normally be the size of the map.
-  ASSERT_TRUE(memory->Read(0x1000, e_ident, 1));
+  ASSERT_TRUE(memory->ReadFully(0x1000, e_ident, 1));
 }
 
 // Verify that device file names will never result in Memory object creation.
@@ -221,13 +221,13 @@
   ASSERT_TRUE(memory.get() != nullptr);
 
   memset(buffer.data(), 0, buffer.size());
-  ASSERT_TRUE(memory->Read(0, buffer.data(), buffer.size()));
+  ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size()));
   for (size_t i = 0; i < buffer.size(); i++) {
     ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
   }
 
   // Try to read outside of the map size.
-  ASSERT_FALSE(memory->Read(buffer.size(), buffer.data(), 1));
+  ASSERT_FALSE(memory->ReadFully(buffer.size(), buffer.data(), 1));
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index 0b70d13..948597b 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -23,7 +23,9 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <atomic>
 #include <memory>
+#include <thread>
 #include <vector>
 
 #include <android-base/file.h>
@@ -67,52 +69,52 @@
 };
 
 TEST_F(MapInfoGetElfTest, invalid) {
-  MapInfo info{.start = 0x1000, .end = 0x2000, .offset = 0, .flags = PROT_READ, .name = ""};
+  MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
 
   // The map is empty, but this should still create an invalid elf object.
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
-  ASSERT_TRUE(elf.get() != nullptr);
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, valid32) {
-  MapInfo info{.start = 0x3000, .end = 0x4000, .offset = 0, .flags = PROT_READ, .name = ""};
+  MapInfo info(0x3000, 0x4000, 0, PROT_READ, "");
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
-  ASSERT_TRUE(elf.get() != nullptr);
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
   EXPECT_EQ(ELFCLASS32, elf->class_type());
 }
 
 TEST_F(MapInfoGetElfTest, valid64) {
-  MapInfo info{.start = 0x8000, .end = 0x9000, .offset = 0, .flags = PROT_READ, .name = ""};
+  MapInfo info(0x8000, 0x9000, 0, PROT_READ, "");
 
   Elf64_Ehdr ehdr;
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
   memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
-  ASSERT_TRUE(elf.get() != nullptr);
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
   EXPECT_EQ(ELFCLASS64, elf->class_type());
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
-  MapInfo info{.start = 0x4000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
+  MapInfo info(0x4000, 0x8000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
                                                  memory_->SetMemory(0x4000 + offset, ptr, size);
                                                });
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
-  ASSERT_TRUE(elf.get() != nullptr);
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
   EXPECT_EQ(ELFCLASS32, elf->class_type());
@@ -120,15 +122,15 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
-  MapInfo info{.start = 0x6000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
+  MapInfo info(0x6000, 0x8000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
                                                  memory_->SetMemory(0x6000 + offset, ptr, size);
                                                });
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
-  ASSERT_TRUE(elf.get() != nullptr);
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
   EXPECT_EQ(ELFCLASS64, elf->class_type());
@@ -136,15 +138,15 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
-  MapInfo info{.start = 0x2000, .end = 0x3000, .offset = 0, .flags = PROT_READ, .name = ""};
+  MapInfo info(0x2000, 0x3000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
                                                  memory_->SetMemory(0x2000 + offset, ptr, size);
                                                });
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, true));
-  ASSERT_TRUE(elf.get() != nullptr);
+  Elf* elf = info.GetElf(process_memory_, true);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
   EXPECT_EQ(ELFCLASS32, elf->class_type());
@@ -152,15 +154,15 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
-  MapInfo info{.start = 0x5000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
+  MapInfo info(0x5000, 0x8000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
                                                  memory_->SetMemory(0x5000 + offset, ptr, size);
                                                });
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, true));
-  ASSERT_TRUE(elf.get() != nullptr);
+  Elf* elf = info.GetElf(process_memory_, true);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
   EXPECT_EQ(ELFCLASS64, elf->class_type());
@@ -168,32 +170,36 @@
 }
 
 TEST_F(MapInfoGetElfTest, end_le_start) {
-  MapInfo info{.start = 0x1000, .end = 0x1000, .offset = 0, .flags = PROT_READ, .name = elf_.path};
+  MapInfo info(0x1000, 0x1000, 0, PROT_READ, elf_.path);
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
+  delete info.elf;
   info.elf = nullptr;
   info.end = 0xfff;
-  elf.reset(info.GetElf(process_memory_, false));
+  elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   // Make sure this test is valid.
+  delete info.elf;
   info.elf = nullptr;
   info.end = 0x2000;
-  elf.reset(info.GetElf(process_memory_, false));
+  elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
 }
 
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info{
-      .start = 0x1000, .end = 0x2000, .offset = 0x100, .flags = PROT_READ, .name = elf_.path};
+  MapInfo info(0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x1000);
   memset(buffer.data(), 0, buffer.size());
@@ -202,27 +208,27 @@
   memcpy(buffer.data(), &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
   ASSERT_EQ(0x100U, info.elf_offset);
 
   // Read the entire file.
   memset(buffer.data(), 0, buffer.size());
-  ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), buffer.size()));
+  ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), buffer.size()));
   ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
   for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
     ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_FALSE(elf->memory()->Read(buffer.size(), buffer.data(), 1));
+  ASSERT_FALSE(elf->memory()->ReadFully(buffer.size(), buffer.data(), 1));
 }
 
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info{
-      .start = 0x1000, .end = 0x2000, .offset = 0x2000, .flags = PROT_READ, .name = elf_.path};
+  MapInfo info(0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -231,19 +237,20 @@
   memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
   ASSERT_EQ(0U, info.elf_offset);
 
   // Read the valid part of the file.
-  ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
+  ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
   ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
   for (size_t i = sizeof(ehdr); i < 0x1000; i++) {
     ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_FALSE(elf->memory()->Read(0x1000, buffer.data(), 1));
+  ASSERT_FALSE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
 }
 
 // Verify that if the offset is non-zero and there is an elf at that
@@ -251,8 +258,7 @@
 // embedded elf is bigger than the initial map, the new object is larger
 // than the original map size. Do this for a 32 bit elf and a 64 bit elf.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
-  MapInfo info{
-      .start = 0x5000, .end = 0x6000, .offset = 0x1000, .flags = PROT_READ, .name = elf_.path};
+  MapInfo info(0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -264,23 +270,23 @@
   memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
   ASSERT_EQ(0U, info.elf_offset);
 
   // Verify the memory is a valid elf.
   memset(buffer.data(), 0, buffer.size());
-  ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
+  ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
   ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
 
   // Read past the end of what would normally be the size of the map.
-  ASSERT_TRUE(elf->memory()->Read(0x1000, buffer.data(), 1));
+  ASSERT_TRUE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
 }
 
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info{
-      .start = 0x7000, .end = 0x8000, .offset = 0x1000, .flags = PROT_READ, .name = elf_.path};
+  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -292,22 +298,23 @@
   memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
   ASSERT_EQ(0U, info.elf_offset);
 
   // Verify the memory is a valid elf.
   memset(buffer.data(), 0, buffer.size());
-  ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
+  ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
   ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
 
   // Read past the end of what would normally be the size of the map.
-  ASSERT_TRUE(elf->memory()->Read(0x1000, buffer.data(), 1));
+  ASSERT_TRUE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
 }
 
 TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
-  MapInfo info{.start = 0x9000, .end = 0xa000, .offset = 0x1000, .flags = 0, .name = ""};
+  MapInfo info(0x9000, 0xa000, 0x1000, 0, "");
 
   // Create valid elf data in process memory only.
   Elf64_Ehdr ehdr;
@@ -317,21 +324,19 @@
   ehdr.e_shnum = 4;
   memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
+  delete info.elf;
   info.elf = nullptr;
   info.flags = PROT_READ;
-  elf.reset(info.GetElf(process_memory_, false));
+  elf = info.GetElf(process_memory_, false);
   ASSERT_TRUE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, check_device_maps) {
-  MapInfo info{.start = 0x7000,
-               .end = 0x8000,
-               .offset = 0x1000,
-               .flags = PROT_READ | MAPS_FLAGS_DEVICE_MAP,
-               .name = "/dev/something"};
+  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP, "/dev/something");
 
   // Create valid elf data in process memory for this to verify that only
   // the name is causing invalid elf data.
@@ -342,20 +347,68 @@
   ehdr.e_shnum = 4;
   memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
 
-  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   // Set the name to nothing to verify that it still fails.
+  delete info.elf;
   info.elf = nullptr;
   info.name = "";
-  elf.reset(info.GetElf(process_memory_, false));
+  elf = info.GetElf(process_memory_, false);
   ASSERT_FALSE(elf->valid());
 
   // Change the flags and verify the elf is valid now.
+  delete info.elf;
   info.elf = nullptr;
   info.flags = PROT_READ;
-  elf.reset(info.GetElf(process_memory_, false));
+  elf = info.GetElf(process_memory_, false);
   ASSERT_TRUE(elf->valid());
 }
 
+TEST_F(MapInfoGetElfTest, multiple_thread_get_elf) {
+  static constexpr size_t kNumConcurrentThreads = 100;
+
+  Elf64_Ehdr ehdr;
+  TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+  ehdr.e_shnum = 4;
+  memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
+
+  Elf* elf_in_threads[kNumConcurrentThreads];
+  std::vector<std::thread*> threads;
+
+  std::atomic_bool wait;
+  wait = true;
+  // Create all of the threads and have them do the GetElf at the same time
+  // to make it likely that a race will occur.
+  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, "");
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
+      while (wait)
+        ;
+      Elf* elf = info.GetElf(process_memory_, false);
+      elf_in_threads[i] = elf;
+    });
+    threads.push_back(thread);
+  }
+  ASSERT_TRUE(info.elf == nullptr);
+
+  // Set them all going and wait for the threads to finish.
+  wait = false;
+  for (auto thread : threads) {
+    thread->join();
+    delete thread;
+  }
+
+  // Now verify that all of the elf files are exactly the same and valid.
+  Elf* elf = info.elf;
+  ASSERT_TRUE(elf != nullptr);
+  EXPECT_TRUE(elf->valid());
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    EXPECT_EQ(elf, elf_in_threads[i]) << "Thread " << i << " mismatched.";
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
new file mode 100644
index 0000000..44a73a8
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <memory>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfFake.h"
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoGetLoadBiasTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+    elf_ = new ElfFake(new MemoryFake);
+    elf_container_.reset(elf_);
+    map_info_.reset(new MapInfo(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+  }
+
+  void MultipleThreadTest(uint64_t expected_load_bias);
+
+  std::shared_ptr<Memory> process_memory_;
+  MemoryFake* memory_;
+  ElfFake* elf_;
+  std::unique_ptr<ElfFake> elf_container_;
+  std::unique_ptr<MapInfo> map_info_;
+};
+
+TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
+  MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+
+  EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, elf_exists) {
+  map_info_->elf = elf_container_.release();
+
+  elf_->FakeSetLoadBias(0);
+  EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
+
+  elf_->FakeSetLoadBias(0x1000);
+  EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_));
+}
+
+void MapInfoGetLoadBiasTest::MultipleThreadTest(uint64_t expected_load_bias) {
+  static constexpr size_t kNumConcurrentThreads = 100;
+
+  uint64_t load_bias_values[kNumConcurrentThreads];
+  std::vector<std::thread*> threads;
+
+  std::atomic_bool wait;
+  wait = true;
+  // Create all of the threads and have them do the GetLoadBias at the same time
+  // to make it likely that a race will occur.
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    std::thread* thread = new std::thread([i, this, &wait, &load_bias_values]() {
+      while (wait)
+        ;
+      load_bias_values[i] = map_info_->GetLoadBias(process_memory_);
+    });
+    threads.push_back(thread);
+  }
+
+  // Set them all going and wait for the threads to finish.
+  wait = false;
+  for (auto thread : threads) {
+    thread->join();
+    delete thread;
+  }
+
+  // Now verify that all of the elf files are exactly the same and valid.
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    EXPECT_EQ(expected_load_bias, load_bias_values[i]) << "Thread " << i << " mismatched.";
+  }
+}
+
+TEST_F(MapInfoGetLoadBiasTest, multiple_thread_elf_exists) {
+  map_info_->elf = elf_container_.release();
+  elf_->FakeSetLoadBias(0x1000);
+
+  MultipleThreadTest(0x1000);
+}
+
+static void InitElfData(MemoryFake* memory, uint64_t offset) {
+  Elf32_Ehdr ehdr;
+  TestInitEhdr(&ehdr, ELFCLASS32, EM_ARM);
+  ehdr.e_phoff = 0x5000;
+  ehdr.e_phnum = 2;
+  ehdr.e_phentsize = sizeof(Elf32_Phdr);
+  memory->SetMemory(offset, &ehdr, sizeof(ehdr));
+
+  Elf32_Phdr phdr;
+  memset(&phdr, 0, sizeof(phdr));
+  phdr.p_type = PT_NULL;
+  memory->SetMemory(offset + 0x5000, &phdr, sizeof(phdr));
+  phdr.p_type = PT_LOAD;
+  phdr.p_offset = 0;
+  phdr.p_vaddr = 0xe000;
+  memory->SetMemory(offset + 0x5000 + sizeof(phdr), &phdr, sizeof(phdr));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, elf_exists_in_memory) {
+  InitElfData(memory_, map_info_->start);
+
+  EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, multiple_thread_elf_exists_in_memory) {
+  InitElfData(memory_, map_info_->start);
+
+  MultipleThreadTest(0xe000);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 2d15a4e..8dc884f 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -35,7 +35,12 @@
     ASSERT_TRUE(maps.Parse()) << "Failed on: " + line;
     MapInfo* element = maps.Get(0);
     ASSERT_TRUE(element != nullptr) << "Failed on: " + line;
-    *info = *element;
+    info->start = element->start;
+    info->end = element->end;
+    info->offset = element->offset;
+    info->flags = element->flags;
+    info->name = element->name;
+    info->elf_offset = element->elf_offset;
   }
 }
 
@@ -139,38 +144,48 @@
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(5U, maps.Total());
-  auto it = maps.begin();
-  ASSERT_EQ(PROT_NONE, it->flags);
-  ASSERT_EQ(0x1000U, it->start);
-  ASSERT_EQ(0x2000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ("", it->name);
-  ++it;
-  ASSERT_EQ(PROT_READ, it->flags);
-  ASSERT_EQ(0x2000U, it->start);
-  ASSERT_EQ(0x3000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ("", it->name);
-  ++it;
-  ASSERT_EQ(PROT_WRITE, it->flags);
-  ASSERT_EQ(0x3000U, it->start);
-  ASSERT_EQ(0x4000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ("", it->name);
-  ++it;
-  ASSERT_EQ(PROT_EXEC, it->flags);
-  ASSERT_EQ(0x4000U, it->start);
-  ASSERT_EQ(0x5000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ("", it->name);
-  ++it;
-  ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, it->flags);
-  ASSERT_EQ(0x5000U, it->start);
-  ASSERT_EQ(0x6000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ("", it->name);
-  ++it;
-  ASSERT_EQ(it, maps.end());
+
+  MapInfo* info = maps.Get(0);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(PROT_NONE, info->flags);
+  EXPECT_EQ(0x1000U, info->start);
+  EXPECT_EQ(0x2000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ("", info->name);
+
+  info = maps.Get(1);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(PROT_READ, info->flags);
+  EXPECT_EQ(0x2000U, info->start);
+  EXPECT_EQ(0x3000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ("", info->name);
+
+  info = maps.Get(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(PROT_WRITE, info->flags);
+  EXPECT_EQ(0x3000U, info->start);
+  EXPECT_EQ(0x4000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ("", info->name);
+
+  info = maps.Get(3);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(PROT_EXEC, info->flags);
+  EXPECT_EQ(0x4000U, info->start);
+  EXPECT_EQ(0x5000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ("", info->name);
+
+  info = maps.Get(4);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+  EXPECT_EQ(0x5000U, info->start);
+  EXPECT_EQ(0x6000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ("", info->name);
+
+  ASSERT_TRUE(maps.Get(5) == nullptr);
 }
 
 TEST(MapsTest, parse_name) {
@@ -181,26 +196,32 @@
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(3U, maps.Total());
-  auto it = maps.begin();
-  ASSERT_EQ("", it->name);
-  ASSERT_EQ(0x7b29b000U, it->start);
-  ASSERT_EQ(0x7b29e000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
-  ++it;
-  ASSERT_EQ("/system/lib/fake.so", it->name);
-  ASSERT_EQ(0x7b29e000U, it->start);
-  ASSERT_EQ(0x7b29f000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
-  ++it;
-  ASSERT_EQ("", it->name);
-  ASSERT_EQ(0x7b29f000U, it->start);
-  ASSERT_EQ(0x7b2a0000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
-  ++it;
-  ASSERT_EQ(it, maps.end());
+
+  MapInfo* info = maps.Get(0);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ("", info->name);
+  EXPECT_EQ(0x7b29b000U, info->start);
+  EXPECT_EQ(0x7b29e000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+
+  info = maps.Get(1);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ("/system/lib/fake.so", info->name);
+  EXPECT_EQ(0x7b29e000U, info->start);
+  EXPECT_EQ(0x7b29f000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+
+  info = maps.Get(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ("", info->name);
+  EXPECT_EQ(0x7b29f000U, info->start);
+  EXPECT_EQ(0x7b2a0000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+
+  ASSERT_TRUE(maps.Get(3) == nullptr);
 }
 
 TEST(MapsTest, parse_offset) {
@@ -210,20 +231,60 @@
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(2U, maps.Total());
-  auto it = maps.begin();
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ(0xa000U, it->start);
-  ASSERT_EQ(0xe000U, it->end);
-  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
-  ASSERT_EQ("/system/lib/fake.so", it->name);
+
+  MapInfo* info = maps.Get(0);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ(0xa000U, info->start);
+  EXPECT_EQ(0xe000U, info->end);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+  EXPECT_EQ("/system/lib/fake.so", info->name);
+
+  info = maps.Get(1);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0xa12345U, info->offset);
+  EXPECT_EQ(0xe000U, info->start);
+  EXPECT_EQ(0xf000U, info->end);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+  EXPECT_EQ("/system/lib/fake.so", info->name);
+
+  ASSERT_TRUE(maps.Get(2) == nullptr);
+}
+
+TEST(MapsTest, iterate) {
+  BufferMaps maps(
+      "a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+      "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(2U, maps.Total());
+
+  Maps::iterator it = maps.begin();
+  EXPECT_EQ(0xa000U, (*it)->start);
+  EXPECT_EQ(0xe000U, (*it)->end);
   ++it;
-  ASSERT_EQ(0xa12345U, it->offset);
-  ASSERT_EQ(0xe000U, it->start);
-  ASSERT_EQ(0xf000U, it->end);
-  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
-  ASSERT_EQ("/system/lib/fake.so", it->name);
+  EXPECT_EQ(0xe000U, (*it)->start);
+  EXPECT_EQ(0xf000U, (*it)->end);
   ++it;
-  ASSERT_EQ(maps.end(), it);
+  EXPECT_EQ(maps.end(), it);
+}
+
+TEST(MapsTest, const_iterate) {
+  BufferMaps maps(
+      "a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+      "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(2U, maps.Total());
+
+  Maps::const_iterator it = maps.begin();
+  EXPECT_EQ(0xa000U, (*it)->start);
+  EXPECT_EQ(0xe000U, (*it)->end);
+  ++it;
+  EXPECT_EQ(0xe000U, (*it)->start);
+  EXPECT_EQ(0xf000U, (*it)->end);
+  ++it;
+  EXPECT_EQ(maps.end(), it);
 }
 
 TEST(MapsTest, device) {
@@ -235,18 +296,23 @@
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(4U, maps.Total());
-  auto it = maps.begin();
-  ASSERT_TRUE(it->flags & 0x8000);
-  ASSERT_EQ("/dev/", it->name);
-  ++it;
-  ASSERT_TRUE(it->flags & 0x8000);
-  ASSERT_EQ("/dev/does_not_exist", it->name);
-  ++it;
-  ASSERT_FALSE(it->flags & 0x8000);
-  ASSERT_EQ("/dev/ashmem/does_not_exist", it->name);
-  ++it;
-  ASSERT_FALSE(it->flags & 0x8000);
-  ASSERT_EQ("/devsomething/does_not_exist", it->name);
+
+  MapInfo* info = maps.Get(0);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_TRUE(info->flags & 0x8000);
+  EXPECT_EQ("/dev/", info->name);
+
+  info = maps.Get(1);
+  EXPECT_TRUE(info->flags & 0x8000);
+  EXPECT_EQ("/dev/does_not_exist", info->name);
+
+  info = maps.Get(2);
+  EXPECT_FALSE(info->flags & 0x8000);
+  EXPECT_EQ("/dev/ashmem/does_not_exist", info->name);
+
+  info = maps.Get(3);
+  EXPECT_FALSE(info->flags & 0x8000);
+  EXPECT_EQ("/devsomething/does_not_exist", info->name);
 }
 
 TEST(MapsTest, file_smoke) {
@@ -263,26 +329,32 @@
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(3U, maps.Total());
-  auto it = maps.begin();
-  ASSERT_EQ(0x7b29b000U, it->start);
-  ASSERT_EQ(0x7b29e000U, it->end);
-  ASSERT_EQ(0xa0000000U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
-  ASSERT_EQ("/fake.so", it->name);
-  ++it;
-  ASSERT_EQ(0x7b2b0000U, it->start);
-  ASSERT_EQ(0x7b2e0000U, it->end);
-  ASSERT_EQ(0xb0000000U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
-  ASSERT_EQ("/fake2.so", it->name);
-  ++it;
-  ASSERT_EQ(0x7b2e0000U, it->start);
-  ASSERT_EQ(0x7b2f0000U, it->end);
-  ASSERT_EQ(0xc0000000U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
-  ASSERT_EQ("/fake3.so", it->name);
-  ++it;
-  ASSERT_EQ(it, maps.end());
+
+  MapInfo* info = maps.Get(0);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x7b29b000U, info->start);
+  EXPECT_EQ(0x7b29e000U, info->end);
+  EXPECT_EQ(0xa0000000U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+  EXPECT_EQ("/fake.so", info->name);
+
+  info = maps.Get(1);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x7b2b0000U, info->start);
+  EXPECT_EQ(0x7b2e0000U, info->end);
+  EXPECT_EQ(0xb0000000U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+  EXPECT_EQ("/fake2.so", info->name);
+
+  info = maps.Get(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x7b2e0000U, info->start);
+  EXPECT_EQ(0x7b2f0000U, info->end);
+  EXPECT_EQ(0xc0000000U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+  EXPECT_EQ("/fake3.so", info->name);
+
+  ASSERT_TRUE(maps.Get(3) == nullptr);
 }
 
 TEST(MapsTest, file_no_map_name) {
@@ -299,26 +371,32 @@
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(3U, maps.Total());
-  auto it = maps.begin();
-  ASSERT_EQ(0x7b29b000U, it->start);
-  ASSERT_EQ(0x7b29e000U, it->end);
-  ASSERT_EQ(0xa0000000U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
-  ASSERT_EQ("", it->name);
-  ++it;
-  ASSERT_EQ(0x7b2b0000U, it->start);
-  ASSERT_EQ(0x7b2e0000U, it->end);
-  ASSERT_EQ(0xb0000000U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
-  ASSERT_EQ("/fake2.so", it->name);
-  ++it;
-  ASSERT_EQ(0x7b2e0000U, it->start);
-  ASSERT_EQ(0x7b2f0000U, it->end);
-  ASSERT_EQ(0xc0000000U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
-  ASSERT_EQ("", it->name);
-  ++it;
-  ASSERT_EQ(it, maps.end());
+
+  MapInfo* info = maps.Get(0);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x7b29b000U, info->start);
+  EXPECT_EQ(0x7b29e000U, info->end);
+  EXPECT_EQ(0xa0000000U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+  EXPECT_EQ("", info->name);
+
+  info = maps.Get(1);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x7b2b0000U, info->start);
+  EXPECT_EQ(0x7b2e0000U, info->end);
+  EXPECT_EQ(0xb0000000U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+  EXPECT_EQ("/fake2.so", info->name);
+
+  info = maps.Get(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x7b2e0000U, info->start);
+  EXPECT_EQ(0x7b2f0000U, info->end);
+  EXPECT_EQ(0xc0000000U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+  EXPECT_EQ("", info->name);
+
+  ASSERT_TRUE(maps.Get(3) == nullptr);
 }
 
 // Verify that a file that crosses a buffer is parsed correctly.
@@ -435,10 +513,10 @@
   ASSERT_EQ(5000U, maps.Total());
   for (size_t i = 0; i < 5000; i++) {
     MapInfo* info = maps.Get(i);
-    ASSERT_EQ(start + i * 4096, info->start) << "Failed at map " + std::to_string(i);
-    ASSERT_EQ(start + (i + 1) * 4096, info->end) << "Failed at map " + std::to_string(i);
+    EXPECT_EQ(start + i * 4096, info->start) << "Failed at map " + std::to_string(i);
+    EXPECT_EQ(start + (i + 1) * 4096, info->end) << "Failed at map " + std::to_string(i);
     std::string name = "/fake" + std::to_string(i) + ".so";
-    ASSERT_EQ(name, info->name) << "Failed at map " + std::to_string(i);
+    EXPECT_EQ(name, info->name) << "Failed at map " + std::to_string(i);
   }
 }
 
@@ -452,52 +530,52 @@
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(5U, maps.Total());
 
-  ASSERT_TRUE(maps.Find(0x500) == nullptr);
-  ASSERT_TRUE(maps.Find(0x2000) == nullptr);
-  ASSERT_TRUE(maps.Find(0x5010) == nullptr);
-  ASSERT_TRUE(maps.Find(0x9a00) == nullptr);
-  ASSERT_TRUE(maps.Find(0xf000) == nullptr);
-  ASSERT_TRUE(maps.Find(0xf010) == nullptr);
+  EXPECT_TRUE(maps.Find(0x500) == nullptr);
+  EXPECT_TRUE(maps.Find(0x2000) == nullptr);
+  EXPECT_TRUE(maps.Find(0x5010) == nullptr);
+  EXPECT_TRUE(maps.Find(0x9a00) == nullptr);
+  EXPECT_TRUE(maps.Find(0xf000) == nullptr);
+  EXPECT_TRUE(maps.Find(0xf010) == nullptr);
 
   MapInfo* info = maps.Find(0x1000);
   ASSERT_TRUE(info != nullptr);
-  ASSERT_EQ(0x1000U, info->start);
-  ASSERT_EQ(0x2000U, info->end);
-  ASSERT_EQ(0x10U, info->offset);
-  ASSERT_EQ(PROT_READ, info->flags);
-  ASSERT_EQ("/system/lib/fake1.so", info->name);
+  EXPECT_EQ(0x1000U, info->start);
+  EXPECT_EQ(0x2000U, info->end);
+  EXPECT_EQ(0x10U, info->offset);
+  EXPECT_EQ(PROT_READ, info->flags);
+  EXPECT_EQ("/system/lib/fake1.so", info->name);
 
   info = maps.Find(0x3020);
   ASSERT_TRUE(info != nullptr);
-  ASSERT_EQ(0x3000U, info->start);
-  ASSERT_EQ(0x4000U, info->end);
-  ASSERT_EQ(0x20U, info->offset);
-  ASSERT_EQ(PROT_WRITE, info->flags);
-  ASSERT_EQ("/system/lib/fake2.so", info->name);
+  EXPECT_EQ(0x3000U, info->start);
+  EXPECT_EQ(0x4000U, info->end);
+  EXPECT_EQ(0x20U, info->offset);
+  EXPECT_EQ(PROT_WRITE, info->flags);
+  EXPECT_EQ("/system/lib/fake2.so", info->name);
 
   info = maps.Find(0x6020);
   ASSERT_TRUE(info != nullptr);
-  ASSERT_EQ(0x6000U, info->start);
-  ASSERT_EQ(0x8000U, info->end);
-  ASSERT_EQ(0x30U, info->offset);
-  ASSERT_EQ(PROT_EXEC, info->flags);
-  ASSERT_EQ("/system/lib/fake3.so", info->name);
+  EXPECT_EQ(0x6000U, info->start);
+  EXPECT_EQ(0x8000U, info->end);
+  EXPECT_EQ(0x30U, info->offset);
+  EXPECT_EQ(PROT_EXEC, info->flags);
+  EXPECT_EQ("/system/lib/fake3.so", info->name);
 
   info = maps.Find(0xafff);
   ASSERT_TRUE(info != nullptr);
-  ASSERT_EQ(0xa000U, info->start);
-  ASSERT_EQ(0xb000U, info->end);
-  ASSERT_EQ(0x40U, info->offset);
-  ASSERT_EQ(PROT_READ | PROT_WRITE, info->flags);
-  ASSERT_EQ("/system/lib/fake4.so", info->name);
+  EXPECT_EQ(0xa000U, info->start);
+  EXPECT_EQ(0xb000U, info->end);
+  EXPECT_EQ(0x40U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+  EXPECT_EQ("/system/lib/fake4.so", info->name);
 
   info = maps.Find(0xe500);
   ASSERT_TRUE(info != nullptr);
-  ASSERT_EQ(0xe000U, info->start);
-  ASSERT_EQ(0xf000U, info->end);
-  ASSERT_EQ(0x50U, info->offset);
-  ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
-  ASSERT_EQ("/system/lib/fake5.so", info->name);
+  EXPECT_EQ(0xe000U, info->start);
+  EXPECT_EQ(0xf000U, info->end);
+  EXPECT_EQ(0x50U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+  EXPECT_EQ("/system/lib/fake5.so", info->name);
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryBufferTest.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
index 50a8a1b..28e0e76 100644
--- a/libunwindstack/tests/MemoryBufferTest.cpp
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -36,7 +36,7 @@
 TEST_F(MemoryBufferTest, empty) {
   ASSERT_EQ(0U, memory_->Size());
   std::vector<uint8_t> buffer(1024);
-  ASSERT_FALSE(memory_->Read(0, buffer.data(), 1));
+  ASSERT_FALSE(memory_->ReadFully(0, buffer.data(), 1));
   ASSERT_EQ(nullptr, memory_->GetPtr(0));
   ASSERT_EQ(nullptr, memory_->GetPtr(1));
 }
@@ -55,7 +55,7 @@
   }
 
   std::vector<uint8_t> buffer(memory_->Size());
-  ASSERT_TRUE(memory_->Read(0, buffer.data(), buffer.size()));
+  ASSERT_TRUE(memory_->ReadFully(0, buffer.data(), buffer.size()));
   for (size_t i = 0; i < buffer.size(); i++) {
     ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i;
   }
@@ -64,18 +64,38 @@
 TEST_F(MemoryBufferTest, read_failures) {
   memory_->Resize(100);
   std::vector<uint8_t> buffer(200);
-  ASSERT_FALSE(memory_->Read(0, buffer.data(), 101));
-  ASSERT_FALSE(memory_->Read(100, buffer.data(), 1));
-  ASSERT_FALSE(memory_->Read(101, buffer.data(), 2));
-  ASSERT_FALSE(memory_->Read(99, buffer.data(), 2));
-  ASSERT_TRUE(memory_->Read(99, buffer.data(), 1));
+  ASSERT_FALSE(memory_->ReadFully(0, buffer.data(), 101));
+  ASSERT_FALSE(memory_->ReadFully(100, buffer.data(), 1));
+  ASSERT_FALSE(memory_->ReadFully(101, buffer.data(), 2));
+  ASSERT_FALSE(memory_->ReadFully(99, buffer.data(), 2));
+  ASSERT_TRUE(memory_->ReadFully(99, buffer.data(), 1));
 }
 
 TEST_F(MemoryBufferTest, read_failure_overflow) {
   memory_->Resize(100);
   std::vector<uint8_t> buffer(200);
 
-  ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200));
+  ASSERT_FALSE(memory_->ReadFully(UINT64_MAX - 100, buffer.data(), 200));
+}
+
+TEST_F(MemoryBufferTest, Read) {
+  memory_->Resize(256);
+  ASSERT_EQ(256U, memory_->Size());
+  ASSERT_TRUE(memory_->GetPtr(0) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(1) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(255) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(256) == nullptr);
+
+  uint8_t* data = memory_->GetPtr(0);
+  for (size_t i = 0; i < memory_->Size(); i++) {
+    data[i] = i;
+  }
+
+  std::vector<uint8_t> buffer(memory_->Size());
+  ASSERT_EQ(128U, memory_->Read(128, buffer.data(), buffer.size()));
+  for (size_t i = 0; i < 128; i++) {
+    ASSERT_EQ(128 + i, buffer[i]) << "Failed at byte " << i;
+  }
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
index 2026acc..60936cd 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -35,16 +35,16 @@
   }
 }
 
-bool MemoryFake::Read(uint64_t addr, void* memory, size_t size) {
+size_t MemoryFake::Read(uint64_t addr, void* memory, size_t size) {
   uint8_t* dst = reinterpret_cast<uint8_t*>(memory);
   for (size_t i = 0; i < size; i++, addr++) {
     auto value = data_.find(addr);
     if (value == data_.end()) {
-      return false;
+      return i;
     }
     dst[i] = value->second;
   }
-  return true;
+  return size;
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index d374261..764a6c3 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -32,7 +32,7 @@
   MemoryFake() = default;
   virtual ~MemoryFake() = default;
 
-  bool Read(uint64_t addr, void* buffer, size_t size) override;
+  size_t Read(uint64_t addr, void* buffer, size_t size) override;
 
   void SetMemory(uint64_t addr, const void* memory, size_t length);
 
@@ -71,21 +71,9 @@
   MemoryFakeAlwaysReadZero() = default;
   virtual ~MemoryFakeAlwaysReadZero() = default;
 
-  bool Read(uint64_t, void* buffer, size_t size) override {
+  size_t Read(uint64_t, void* buffer, size_t size) override {
     memset(buffer, 0, size);
-    return true;
-  }
-};
-
-class MemoryFakeRemote : public MemoryRemote {
- public:
-  MemoryFakeRemote() : MemoryRemote(0) {}
-  virtual ~MemoryFakeRemote() = default;
-
- protected:
-  bool PtraceRead(uint64_t, long* value) override {
-    *value = 0;
-    return true;
+    return size;
   }
 };
 
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index a204bae..d7d1ace 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -49,7 +49,7 @@
 
   ASSERT_TRUE(memory_.Init(tf_->path, 0));
   std::vector<char> buffer(11);
-  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10));
   buffer[10] = '\0';
   ASSERT_STREQ("0123456789", buffer.data());
 }
@@ -59,7 +59,7 @@
 
   ASSERT_TRUE(memory_.Init(tf_->path, 10));
   std::vector<char> buffer(11);
-  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10));
   buffer[10] = '\0';
   ASSERT_STREQ("abcdefghij", buffer.data());
 }
@@ -75,7 +75,7 @@
 
   ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 15));
   std::vector<char> buffer(9);
-  ASSERT_TRUE(memory_.Read(0, buffer.data(), 8));
+  ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 8));
   buffer[8] = '\0';
   ASSERT_STREQ("abcdefgh", buffer.data());
 }
@@ -91,7 +91,7 @@
 
   ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize));
   std::vector<char> buffer(11);
-  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10));
   buffer[10] = '\0';
   std::string expected_str;
   for (size_t i = 0; i < 5; i++) {
@@ -112,7 +112,7 @@
 
   ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize + 10));
   std::vector<char> buffer(11);
-  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10));
   buffer[10] = '\0';
   std::string expected_str;
   for (size_t i = 0; i < 5; i++) {
@@ -149,19 +149,19 @@
   std::vector<char> buffer(100);
 
   // Read before init.
-  ASSERT_FALSE(memory_.Read(0, buffer.data(), 10));
+  ASSERT_FALSE(memory_.ReadFully(0, buffer.data(), 10));
 
   ASSERT_TRUE(memory_.Init(tf_->path, 0));
 
-  ASSERT_FALSE(memory_.Read(10000, buffer.data(), 10));
-  ASSERT_FALSE(memory_.Read(5000, buffer.data(), 10));
-  ASSERT_FALSE(memory_.Read(4990, buffer.data(), 11));
-  ASSERT_TRUE(memory_.Read(4990, buffer.data(), 10));
-  ASSERT_FALSE(memory_.Read(4999, buffer.data(), 2));
-  ASSERT_TRUE(memory_.Read(4999, buffer.data(), 1));
+  ASSERT_FALSE(memory_.ReadFully(10000, buffer.data(), 10));
+  ASSERT_FALSE(memory_.ReadFully(5000, buffer.data(), 10));
+  ASSERT_FALSE(memory_.ReadFully(4990, buffer.data(), 11));
+  ASSERT_TRUE(memory_.ReadFully(4990, buffer.data(), 10));
+  ASSERT_FALSE(memory_.ReadFully(4999, buffer.data(), 2));
+  ASSERT_TRUE(memory_.ReadFully(4999, buffer.data(), 1));
 
   // Check that overflow fails properly.
-  ASSERT_FALSE(memory_.Read(UINT64_MAX - 100, buffer.data(), 200));
+  ASSERT_FALSE(memory_.ReadFully(UINT64_MAX - 100, buffer.data(), 200));
 }
 
 TEST_F(MemoryFileTest, read_past_file_within_mapping) {
@@ -178,7 +178,8 @@
 
   for (size_t i = 0; i < 100; i++) {
     uint8_t value;
-    ASSERT_FALSE(memory_.Read(buffer.size() + i, &value, 1)) << "Should have failed at value " << i;
+    ASSERT_FALSE(memory_.ReadFully(buffer.size() + i, &value, 1))
+        << "Should have failed at value " << i;
   }
 }
 
@@ -195,8 +196,8 @@
 
   std::vector<uint8_t> read_buffer(pagesize * 2);
   // Make sure that reading after mapped data is a failure.
-  ASSERT_FALSE(memory_.Read(pagesize * 2, read_buffer.data(), 1));
-  ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize * 2));
+  ASSERT_FALSE(memory_.ReadFully(pagesize * 2, read_buffer.data(), 1));
+  ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize * 2));
   for (size_t i = 0; i < pagesize; i++) {
     ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
   }
@@ -219,8 +220,8 @@
 
   std::vector<uint8_t> read_buffer(pagesize * 2);
   // Make sure that reading after mapped data is a failure.
-  ASSERT_FALSE(memory_.Read(pagesize * 2, read_buffer.data(), 1));
-  ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize * 2));
+  ASSERT_FALSE(memory_.ReadFully(pagesize * 2, read_buffer.data(), 1));
+  ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize * 2));
   for (size_t i = 0; i < pagesize - 0x100; i++) {
     ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
   }
@@ -245,8 +246,8 @@
   ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 0x100, UINT64_MAX));
 
   std::vector<uint8_t> read_buffer(pagesize * 10);
-  ASSERT_FALSE(memory_.Read(pagesize * 9 - 0x100 + 1, read_buffer.data(), 1));
-  ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize * 9 - 0x100));
+  ASSERT_FALSE(memory_.ReadFully(pagesize * 9 - 0x100 + 1, read_buffer.data(), 1));
+  ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize * 9 - 0x100));
 }
 
 TEST_F(MemoryFileTest, init_reinit) {
@@ -259,14 +260,14 @@
 
   ASSERT_TRUE(memory_.Init(tf_->path, 0));
   std::vector<uint8_t> read_buffer(buffer.size());
-  ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize));
+  ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize));
   for (size_t i = 0; i < pagesize; i++) {
     ASSERT_EQ(1, read_buffer[i]) << "Failed at byte " << i;
   }
 
   // Now reinit.
   ASSERT_TRUE(memory_.Init(tf_->path, pagesize));
-  ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize));
+  ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize));
   for (size_t i = 0; i < pagesize; i++) {
     ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
   }
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index 73eebdd..5a389d0 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -16,6 +16,7 @@
 
 #include <stdint.h>
 #include <string.h>
+#include <sys/mman.h>
 
 #include <vector>
 
@@ -32,14 +33,14 @@
   MemoryLocal local;
 
   std::vector<uint8_t> dst(1024);
-  ASSERT_TRUE(local.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+  ASSERT_TRUE(local.ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
   ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
   for (size_t i = 0; i < 1024; i++) {
     ASSERT_EQ(0x4cU, dst[i]);
   }
 
   memset(src.data(), 0x23, 512);
-  ASSERT_TRUE(local.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+  ASSERT_TRUE(local.ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
   ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
   for (size_t i = 0; i < 512; i++) {
     ASSERT_EQ(0x23U, dst[i]);
@@ -53,8 +54,8 @@
   MemoryLocal local;
 
   std::vector<uint8_t> dst(100);
-  ASSERT_FALSE(local.Read(0, dst.data(), 1));
-  ASSERT_FALSE(local.Read(0, dst.data(), 100));
+  ASSERT_FALSE(local.ReadFully(0, dst.data(), 1));
+  ASSERT_FALSE(local.ReadFully(0, dst.data(), 100));
 }
 
 TEST(MemoryLocalTest, read_overflow) {
@@ -64,7 +65,47 @@
   // version will always go through the overflow check.
   std::vector<uint8_t> dst(100);
   uint64_t value;
-  ASSERT_FALSE(local.Read(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
+  ASSERT_FALSE(local.ReadFully(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
+}
+
+TEST(MemoryLocalTest, Read) {
+  char* mapping = static_cast<char*>(
+      mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+
+  ASSERT_NE(MAP_FAILED, mapping);
+
+  mprotect(mapping + getpagesize(), getpagesize(), PROT_NONE);
+  memset(mapping + getpagesize() - 1024, 0x4c, 1024);
+
+  MemoryLocal local;
+
+  std::vector<uint8_t> dst(4096);
+  ASSERT_EQ(1024U, local.Read(reinterpret_cast<uint64_t>(mapping + getpagesize() - 1024),
+                              dst.data(), 4096));
+  for (size_t i = 0; i < 1024; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_EQ(0, munmap(mapping, 2 * getpagesize()));
+}
+
+TEST(MemoryLocalTest, read_hole) {
+  void* mapping =
+      mmap(nullptr, 3 * 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  ASSERT_NE(MAP_FAILED, mapping);
+  memset(mapping, 0xFF, 3 * 4096);
+  mprotect(static_cast<char*>(mapping) + 4096, 4096, PROT_NONE);
+
+  MemoryLocal local;
+  std::vector<uint8_t> dst(4096 * 3, 0xCC);
+  ASSERT_EQ(4096U, local.Read(reinterpret_cast<uintptr_t>(mapping), dst.data(), 4096 * 3));
+  for (size_t i = 0; i < 4096; ++i) {
+    ASSERT_EQ(0xFF, dst[i]);
+  }
+  for (size_t i = 4096; i < 4096 * 3; ++i) {
+    ASSERT_EQ(0xCC, dst[i]);
+  }
+  ASSERT_EQ(0, munmap(mapping, 3 * 4096));
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index 680fae9..cb1a0c9 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -35,10 +35,10 @@
   std::shared_ptr<Memory> process_memory(memory_fake);
   memory_fake->SetMemory(9001, src);
 
-  MemoryRange range(process_memory, 9001, 9001 + src.size());
+  MemoryRange range(process_memory, 9001, src.size(), 0);
 
   std::vector<uint8_t> dst(1024);
-  ASSERT_TRUE(range.Read(0, dst.data(), src.size()));
+  ASSERT_TRUE(range.ReadFully(0, dst.data(), src.size()));
   for (size_t i = 0; i < 1024; i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
@@ -51,29 +51,44 @@
   std::shared_ptr<Memory> process_memory(memory_fake);
   memory_fake->SetMemory(1000, src);
 
-  MemoryRange range(process_memory, 1000, 2024);
+  MemoryRange range(process_memory, 1000, 1024, 0);
 
   std::vector<uint8_t> dst(1024);
-  ASSERT_TRUE(range.Read(1020, dst.data(), 4));
+  ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
   for (size_t i = 0; i < 4; i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
   // Verify that reads outside of the range will fail.
-  ASSERT_FALSE(range.Read(1020, dst.data(), 5));
-  ASSERT_FALSE(range.Read(1024, dst.data(), 1));
-  ASSERT_FALSE(range.Read(1024, dst.data(), 1024));
+  ASSERT_FALSE(range.ReadFully(1020, dst.data(), 5));
+  ASSERT_FALSE(range.ReadFully(1024, dst.data(), 1));
+  ASSERT_FALSE(range.ReadFully(1024, dst.data(), 1024));
 
   // Verify that reading up to the end works.
-  ASSERT_TRUE(range.Read(1020, dst.data(), 4));
+  ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
 }
 
 TEST(MemoryRangeTest, read_overflow) {
   std::vector<uint8_t> buffer(100);
 
   std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
-  std::unique_ptr<MemoryRange> overflow(new MemoryRange(process_memory, 100, 200));
-  ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
+  std::unique_ptr<MemoryRange> overflow(new MemoryRange(process_memory, 100, 200, 0));
+  ASSERT_FALSE(overflow->ReadFully(UINT64_MAX - 10, buffer.data(), 100));
+}
+
+TEST(MemoryRangeTest, Read) {
+  std::vector<uint8_t> src(4096);
+  memset(src.data(), 0x4c, 4096);
+  MemoryFake* memory_fake = new MemoryFake;
+  std::shared_ptr<Memory> process_memory(memory_fake);
+  memory_fake->SetMemory(1000, src);
+
+  MemoryRange range(process_memory, 1000, 1024, 0);
+  std::vector<uint8_t> dst(1024);
+  ASSERT_EQ(4U, range.Read(1020, dst.data(), 1024));
+  for (size_t i = 0; i < 4; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index a66d0c5..8aa8605 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -71,7 +71,7 @@
   MemoryRemote remote(pid);
 
   std::vector<uint8_t> dst(1024);
-  ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+  ASSERT_TRUE(remote.ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
   for (size_t i = 0; i < 1024; i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
@@ -79,6 +79,50 @@
   ASSERT_TRUE(Detach(pid));
 }
 
+TEST_F(MemoryRemoteTest, read_partial) {
+  char* mapping = static_cast<char*>(
+      mmap(nullptr, 4 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+  ASSERT_NE(MAP_FAILED, mapping);
+  memset(mapping, 0x4c, 4 * getpagesize());
+  ASSERT_EQ(0, mprotect(mapping + getpagesize(), getpagesize(), PROT_NONE));
+  ASSERT_EQ(0, munmap(mapping + 3 * getpagesize(), getpagesize()));
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  // Unmap from our process.
+  ASSERT_EQ(0, munmap(mapping, 3 * getpagesize()));
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+
+  std::vector<uint8_t> dst(4096);
+  size_t bytes =
+      remote.Read(reinterpret_cast<uint64_t>(mapping + getpagesize() - 1024), dst.data(), 4096);
+  // Some read methods can read PROT_NONE maps, allow that.
+  ASSERT_LE(1024U, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+
+  // Now verify that reading stops at the end of a map.
+  bytes =
+      remote.Read(reinterpret_cast<uint64_t>(mapping + 3 * getpagesize() - 1024), dst.data(), 4096);
+  ASSERT_EQ(1024U, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_TRUE(Detach(pid));
+}
+
 TEST_F(MemoryRemoteTest, read_fail) {
   int pagesize = getpagesize();
   void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0);
@@ -101,17 +145,17 @@
   MemoryRemote remote(pid);
 
   std::vector<uint8_t> dst(pagesize);
-  ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src), dst.data(), pagesize));
+  ASSERT_TRUE(remote.ReadFully(reinterpret_cast<uint64_t>(src), dst.data(), pagesize));
   for (size_t i = 0; i < 1024; i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize, dst.data(), 1));
-  ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
-  ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
+  ASSERT_FALSE(remote.ReadFully(reinterpret_cast<uint64_t>(src) + pagesize, dst.data(), 1));
+  ASSERT_TRUE(remote.ReadFully(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
+  ASSERT_FALSE(remote.ReadFully(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
 
   // Check overflow condition is caught properly.
-  ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+  ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
 
   ASSERT_EQ(0, munmap(src, pagesize));
 
@@ -119,11 +163,24 @@
 }
 
 TEST_F(MemoryRemoteTest, read_overflow) {
-  MemoryFakeRemote remote;
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
 
   // Check overflow condition is caught properly.
   std::vector<uint8_t> dst(200);
-  ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+  ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
+
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST_F(MemoryRemoteTest, read_illegal) {
@@ -140,10 +197,77 @@
   MemoryRemote remote(pid);
 
   std::vector<uint8_t> dst(100);
-  ASSERT_FALSE(remote.Read(0, dst.data(), 1));
-  ASSERT_FALSE(remote.Read(0, dst.data(), 100));
+  ASSERT_FALSE(remote.ReadFully(0, dst.data(), 1));
+  ASSERT_FALSE(remote.ReadFully(0, dst.data(), 100));
 
   ASSERT_TRUE(Detach(pid));
 }
 
+TEST_F(MemoryRemoteTest, read_mprotect_hole) {
+  size_t page_size = getpagesize();
+  void* mapping =
+      mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  ASSERT_NE(MAP_FAILED, mapping);
+  memset(mapping, 0xFF, 3 * page_size);
+  ASSERT_EQ(0, mprotect(static_cast<char*>(mapping) + page_size, page_size, PROT_NONE));
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true);
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_EQ(0, munmap(mapping, 3 * page_size));
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+  std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
+  size_t read_size = remote.Read(reinterpret_cast<uintptr_t>(mapping), dst.data(), page_size * 3);
+  // Some read methods can read PROT_NONE maps, allow that.
+  ASSERT_LE(page_size, read_size);
+  for (size_t i = 0; i < read_size; ++i) {
+    ASSERT_EQ(0xFF, dst[i]);
+  }
+  for (size_t i = read_size; i < dst.size(); ++i) {
+    ASSERT_EQ(0xCC, dst[i]);
+  }
+}
+
+TEST_F(MemoryRemoteTest, read_munmap_hole) {
+  size_t page_size = getpagesize();
+  void* mapping =
+      mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  ASSERT_NE(MAP_FAILED, mapping);
+  memset(mapping, 0xFF, 3 * page_size);
+  ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + page_size, page_size));
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_EQ(0, munmap(mapping, page_size));
+  ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + 2 * page_size, page_size));
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+  std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
+  size_t read_size = remote.Read(reinterpret_cast<uintptr_t>(mapping), dst.data(), page_size * 3);
+  ASSERT_EQ(page_size, read_size);
+  for (size_t i = 0; i < read_size; ++i) {
+    ASSERT_EQ(0xFF, dst[i]);
+  }
+  for (size_t i = read_size; i < dst.size(); ++i) {
+    ASSERT_EQ(0xCC, dst[i]);
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index fedaf87..3c5af4a 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 
+#include <unwindstack/Elf.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
 
@@ -30,7 +31,7 @@
       : Regs(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
   virtual ~RegsFake() = default;
 
-  uint32_t MachineType() override { return fake_type_; }
+  ArchEnum Arch() override { return fake_arch_; }
   void* RawData() override { return nullptr; }
   uint64_t pc() override { return fake_pc_; }
   uint64_t sp() override { return fake_sp_; }
@@ -50,14 +51,14 @@
 
   void SetFromRaw() override {}
 
-  void FakeSetMachineType(uint32_t type) { fake_type_ = type; }
+  void FakeSetArch(ArchEnum arch) { fake_arch_ = arch; }
   void FakeSetPc(uint64_t pc) { fake_pc_ = pc; }
   void FakeSetSp(uint64_t sp) { fake_sp_ = sp; }
   void FakeSetReturnAddress(uint64_t return_address) { fake_return_address_ = return_address; }
   void FakeSetReturnAddressValid(bool valid) { fake_return_address_valid_ = valid; }
 
  private:
-  uint32_t fake_type_ = 0;
+  ArchEnum fake_arch_ = ARCH_UNKNOWN;
   uint64_t fake_pc_ = 0;
   uint64_t fake_sp_ = 0;
   bool fake_return_address_valid_ = false;
@@ -71,7 +72,7 @@
       : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
   virtual ~RegsImplFake() = default;
 
-  uint32_t MachineType() override { return 0; }
+  ArchEnum Arch() override { return ARCH_UNKNOWN; }
 
   uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
   void SetFromRaw() override {}
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index c8d1d98..0cb70ba 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -25,9 +25,15 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/ElfInterface.h>
 #include <unwindstack/MapInfo.h>
-#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
 
-#include "Machine.h"
+#include "MachineArm.h"
+#include "MachineArm64.h"
+#include "MachineX86.h"
+#include "MachineX86_64.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
index 85192d5..ae57caf 100644
--- a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -19,9 +19,15 @@
 #include <gtest/gtest.h>
 
 #include <unwindstack/Elf.h>
-#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
 
-#include "Machine.h"
+#include "MachineArm.h"
+#include "MachineArm64.h"
+#include "MachineX86.h"
+#include "MachineX86_64.h"
 
 #include "MemoryFake.h"
 
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 2a02669..a932973 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -21,7 +21,10 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/ElfInterface.h>
 #include <unwindstack/MapInfo.h>
-#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
 
 #include "ElfFake.h"
 #include "MemoryFake.h"
@@ -147,28 +150,29 @@
 }
 
 TEST_F(RegsTest, elf_invalid) {
-  Elf invalid_elf(new MemoryFake);
   RegsArm regs_arm;
   RegsArm64 regs_arm64;
   RegsX86 regs_x86;
   RegsX86_64 regs_x86_64;
-  MapInfo map_info{.start = 0x1000, .end = 0x2000};
+  MapInfo map_info(0x1000, 0x2000);
+  Elf* invalid_elf = new Elf(new MemoryFake);
+  map_info.elf = invalid_elf;
 
   regs_arm.set_pc(0x1500);
-  ASSERT_EQ(0x500U, invalid_elf.GetRelPc(regs_arm.pc(), &map_info));
-  ASSERT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, &invalid_elf));
+  EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
+  EXPECT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, invalid_elf));
 
   regs_arm64.set_pc(0x1600);
-  ASSERT_EQ(0x600U, invalid_elf.GetRelPc(regs_arm64.pc(), &map_info));
-  ASSERT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, &invalid_elf));
+  EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
+  EXPECT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, invalid_elf));
 
   regs_x86.set_pc(0x1700);
-  ASSERT_EQ(0x700U, invalid_elf.GetRelPc(regs_x86.pc(), &map_info));
-  ASSERT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, &invalid_elf));
+  EXPECT_EQ(0x700U, invalid_elf->GetRelPc(regs_x86.pc(), &map_info));
+  EXPECT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, invalid_elf));
 
   regs_x86_64.set_pc(0x1800);
-  ASSERT_EQ(0x800U, invalid_elf.GetRelPc(regs_x86_64.pc(), &map_info));
-  ASSERT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, &invalid_elf));
+  EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info));
+  EXPECT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, invalid_elf));
 }
 
 TEST_F(RegsTest, arm_set_from_raw) {
@@ -211,4 +215,18 @@
   EXPECT_EQ(0x4900000000U, x86_64.pc());
 }
 
+TEST_F(RegsTest, machine_type) {
+  RegsArm arm_regs;
+  EXPECT_EQ(ARCH_ARM, arm_regs.Arch());
+
+  RegsArm64 arm64_regs;
+  EXPECT_EQ(ARCH_ARM64, arm64_regs.Arch());
+
+  RegsX86 x86_regs;
+  EXPECT_EQ(ARCH_X86, x86_regs.Arch());
+
+  RegsX86_64 x86_64_regs;
+  EXPECT_EQ(ARCH_X86_64, x86_64_regs.Arch());
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
new file mode 100644
index 0000000..962f744
--- /dev/null
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/Unwinder.h>
+
+#include "MachineArm.h"
+#include "MachineArm64.h"
+
+#include "ElfTestUtils.h"
+
+namespace unwindstack {
+
+static std::string DumpFrames(Unwinder& unwinder) {
+  std::string str;
+  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+    str += unwinder.FormatFrame(i) + "\n";
+  }
+  return str;
+}
+
+TEST(UnwindOfflineTest, pc_straddle_arm32) {
+  std::string dir(TestGetFileDirectory() + "offline/straddle_arm32/");
+
+  MemoryOffline* memory = new MemoryOffline;
+  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
+
+  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
+  ASSERT_TRUE(fp != nullptr);
+  RegsArm regs;
+  uint64_t reg_value;
+  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
+  regs[ARM_REG_PC] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
+  regs[ARM_REG_SP] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
+  regs[ARM_REG_LR] = reg_value;
+  regs.SetFromRaw();
+  fclose(fp);
+
+  fp = fopen((dir + "maps.txt").c_str(), "r");
+  ASSERT_TRUE(fp != nullptr);
+  // The file is guaranteed to be less than 4096 bytes.
+  std::vector<char> buffer(4096);
+  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
+  fclose(fp);
+
+  BufferMaps maps(buffer.data());
+  ASSERT_TRUE(maps.Parse());
+
+  ASSERT_EQ(ARCH_ARM, regs.Arch());
+
+  std::shared_ptr<Memory> process_memory(memory);
+
+  char* cwd = getcwd(nullptr, 0);
+  ASSERT_EQ(0, chdir(dir.c_str()));
+  Unwinder unwinder(128, &maps, &regs, process_memory);
+  unwinder.Unwind();
+  ASSERT_EQ(0, chdir(cwd));
+  free(cwd);
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0001a9f8  libc.so (abort+63)\n"
+      "  #01 pc 00006a1b  libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
+      "  #02 pc 00007441  libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
+      "  #03 pc 00015149  /does/not/exist/libhidlbase.so\n",
+      frame_info);
+}
+
+TEST(UnwindOfflineTest, pc_straddle_arm64) {
+  std::string dir(TestGetFileDirectory() + "offline/straddle_arm64/");
+
+  MemoryOffline* memory = new MemoryOffline;
+  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
+
+  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
+  ASSERT_TRUE(fp != nullptr);
+  RegsArm64 regs;
+  uint64_t reg_value;
+  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
+  regs[ARM64_REG_PC] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
+  regs[ARM64_REG_SP] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
+  regs[ARM64_REG_LR] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "x29: %" SCNx64 "\n", &reg_value));
+  regs[ARM64_REG_R29] = reg_value;
+  regs.SetFromRaw();
+  fclose(fp);
+
+  fp = fopen((dir + "maps.txt").c_str(), "r");
+  ASSERT_TRUE(fp != nullptr);
+  // The file is guaranteed to be less than 4096 bytes.
+  std::vector<char> buffer(4096);
+  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
+  fclose(fp);
+
+  BufferMaps maps(buffer.data());
+  ASSERT_TRUE(maps.Parse());
+
+  ASSERT_EQ(ARCH_ARM64, regs.Arch());
+
+  std::shared_ptr<Memory> process_memory(memory);
+
+  char* cwd = getcwd(nullptr, 0);
+  ASSERT_EQ(0, chdir(dir.c_str()));
+  Unwinder unwinder(128, &maps, &regs, process_memory);
+  unwinder.Unwind();
+  ASSERT_EQ(0, chdir(cwd));
+  free(cwd);
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0000000000429fd8  libunwindstack_test (SignalInnerFunction+24)\n"
+      "  #01 pc 000000000042a078  libunwindstack_test (SignalMiddleFunction+8)\n"
+      "  #02 pc 000000000042a08c  libunwindstack_test (SignalOuterFunction+8)\n"
+      "  #03 pc 000000000042d8fc  libunwindstack_test "
+      "(_ZN11unwindstackL19RemoteThroughSignalEij+20)\n"
+      "  #04 pc 000000000042d8d8  libunwindstack_test "
+      "(_ZN11unwindstack37UnwindTest_remote_through_signal_Test8TestBodyEv+32)\n"
+      "  #05 pc 0000000000455d70  libunwindstack_test (_ZN7testing4Test3RunEv+392)\n",
+      frame_info);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 9f9ca8b..242cc6a 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -174,7 +174,7 @@
       MemoryRemote memory(pid);
       // Read the remote value to see if we are ready.
       bool value;
-      if (memory.Read(addr, &value, sizeof(value)) && value) {
+      if (memory.ReadFully(addr, &value, sizeof(value)) && value) {
         *completed = true;
       }
       if (!*completed || !leave_attached) {
@@ -250,7 +250,7 @@
 
   LocalMaps maps;
   ASSERT_TRUE(maps.Parse());
-  std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentMachineType(), ucontext));
+  std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
 
   VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
 
@@ -311,4 +311,39 @@
   RemoteThroughSignal(SIGSEGV, SA_SIGINFO);
 }
 
+// Verify that using the same map while unwinding multiple threads at the
+// same time doesn't cause problems.
+TEST_F(UnwindTest, multiple_threads_unwind_same_map) {
+  static constexpr size_t kNumConcurrentThreads = 100;
+
+  LocalMaps maps;
+  ASSERT_TRUE(maps.Parse());
+  auto process_memory(Memory::CreateProcessMemory(getpid()));
+
+  std::vector<std::thread*> threads;
+
+  std::atomic_bool wait;
+  wait = true;
+  size_t frames[kNumConcurrentThreads];
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    std::thread* thread = new std::thread([i, &frames, &maps, &process_memory, &wait]() {
+      while (wait)
+        ;
+      std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+      RegsGetLocal(regs.get());
+
+      Unwinder unwinder(512, &maps, regs.get(), process_memory);
+      unwinder.Unwind();
+      frames[i] = unwinder.NumFrames();
+      ASSERT_LE(3U, frames[i]) << "Failed for thread " << i;
+    });
+    threads.push_back(thread);
+  }
+  wait = false;
+  for (auto thread : threads) {
+    thread->join();
+    delete thread;
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 869d118..71103b4 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -45,88 +45,57 @@
 
   void FakeClear() { maps_.clear(); }
 
-  void FakeAddMapInfo(const MapInfo& map_info) { maps_.push_back(map_info); }
+  void FakeAddMapInfo(MapInfo* map_info) { maps_.push_back(map_info); }
 };
 
 class UnwinderTest : public ::testing::Test {
  protected:
   static void SetUpTestCase() {
     maps_.FakeClear();
-    MapInfo info;
-    info.name = "/system/fake/libc.so";
-    info.start = 0x1000;
-    info.end = 0x8000;
-    info.offset = 0;
-    info.flags = PROT_READ | PROT_WRITE;
+    MapInfo* info = new MapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
     ElfFake* elf = new ElfFake(nullptr);
-    info.elf = elf;
+    info->elf = elf;
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
-    info.elf_offset = 0;
     maps_.FakeAddMapInfo(info);
 
-    info.name = "[stack]";
-    info.start = 0x10000;
-    info.end = 0x12000;
-    info.flags = PROT_READ | PROT_WRITE;
-    info.elf = nullptr;
+    info = new MapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
     maps_.FakeAddMapInfo(info);
 
-    info.name = "/dev/fake_device";
-    info.start = 0x13000;
-    info.end = 0x15000;
-    info.flags = PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP;
-    info.elf = nullptr;
+    info = new MapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+                       "/dev/fake_device");
     maps_.FakeAddMapInfo(info);
 
-    info.name = "/system/fake/libunwind.so";
-    info.start = 0x20000;
-    info.end = 0x22000;
-    info.flags = PROT_READ | PROT_WRITE;
+    info = new MapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so");
     elf = new ElfFake(nullptr);
-    info.elf = elf;
+    info->elf = elf;
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info.name = "/fake/libanother.so";
-    info.start = 0x23000;
-    info.end = 0x24000;
-    info.flags = PROT_READ | PROT_WRITE;
+    info = new MapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
     elf = new ElfFake(nullptr);
-    info.elf = elf;
+    info->elf = elf;
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info.name = "/fake/compressed.so";
-    info.start = 0x33000;
-    info.end = 0x34000;
-    info.flags = PROT_READ | PROT_WRITE;
+    info = new MapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
     elf = new ElfFake(nullptr);
-    info.elf = elf;
+    info->elf = elf;
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info.name = "/fake/fake.apk";
-    info.start = 0x43000;
-    info.end = 0x44000;
-    info.offset = 0x1d000;
-    info.flags = PROT_READ | PROT_WRITE;
+    info = new MapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
     elf = new ElfFake(nullptr);
-    info.elf = elf;
+    info->elf = elf;
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info.name = "/fake/fake.oat";
-    info.start = 0x53000;
-    info.end = 0x54000;
-    info.offset = 0;
-    info.flags = PROT_READ | PROT_WRITE;
-    info.elf = nullptr;
+    info = new MapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
     maps_.FakeAddMapInfo(info);
   }
 
   void SetUp() override {
     ElfInterfaceFake::FakeClear();
-    regs_.FakeSetMachineType(EM_ARM);
+    regs_.FakeSetArch(ARCH_ARM);
     regs_.FakeSetReturnAddressValid(false);
   }
 
@@ -733,14 +702,14 @@
 
   ASSERT_EQ(1U, unwinder.NumFrames());
 
-  regs_.FakeSetMachineType(EM_ARM);
+  regs_.FakeSetArch(ARCH_ARM);
   EXPECT_EQ("  #00 pc 00001300  /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
-  regs_.FakeSetMachineType(EM_386);
+  regs_.FakeSetArch(ARCH_X86);
   EXPECT_EQ("  #00 pc 00001300  /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
 
-  regs_.FakeSetMachineType(EM_AARCH64);
+  regs_.FakeSetArch(ARCH_ARM64);
   EXPECT_EQ("  #00 pc 0000000000001300  /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
-  regs_.FakeSetMachineType(EM_X86_64);
+  regs_.FakeSetArch(ARCH_X86_64);
   EXPECT_EQ("  #00 pc 0000000000001300  /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
 
   EXPECT_EQ("", unwinder.FormatFrame(1));
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/libbase.so b/libunwindstack/tests/files/offline/straddle_arm32/libbase.so
new file mode 100644
index 0000000..d1f16ee
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/libbase.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/libc.so b/libunwindstack/tests/files/offline/straddle_arm32/libc.so
new file mode 100644
index 0000000..4dc19ca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/maps.txt b/libunwindstack/tests/files/offline/straddle_arm32/maps.txt
new file mode 100644
index 0000000..8c26479
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/maps.txt
@@ -0,0 +1,4 @@
+f2d9a000-f2da7fff r-xp 00000000 00:00 0   libbase.so
+f3002000-f3005fff rw-p 00000000 00:00 0   [stack:25941]
+f31d0000-f326bfff r-xp 00000000 00:00 0   libc.so
+f3352000-f336bfff r-xp 00000000 00:00 0   /does/not/exist/libhidlbase.so
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/regs.txt b/libunwindstack/tests/files/offline/straddle_arm32/regs.txt
new file mode 100644
index 0000000..3baedf3
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/regs.txt
@@ -0,0 +1,3 @@
+pc: f31ea9f8
+sp: e9c866f8
+lr: f31f179f
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/stack.data b/libunwindstack/tests/files/offline/straddle_arm32/stack.data
new file mode 100644
index 0000000..83aeb4a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
new file mode 100644
index 0000000..092fc3a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/maps.txt b/libunwindstack/tests/files/offline/straddle_arm64/maps.txt
new file mode 100644
index 0000000..bdf29b5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/maps.txt
@@ -0,0 +1,2 @@
+00000064d05ab000-00000064d0a6cfff r-xp 00000000 00:00 0  libunwindstack_test
+0000007fe0d64000-0000007fe0d84fff rw-p 00000000 00:00 0  [stack]
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/regs.txt b/libunwindstack/tests/files/offline/straddle_arm64/regs.txt
new file mode 100644
index 0000000..ff8a936
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/regs.txt
@@ -0,0 +1,4 @@
+pc: 00000064d09d4fd8
+sp: 0000007fe0d84040
+lr: 00000064d09d507c
+x29: 0000007fe0d84070
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/stack.data b/libunwindstack/tests/files/offline/straddle_arm64/stack.data
new file mode 100644
index 0000000..824d0e2
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index f2530d7..7896279 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -63,17 +63,17 @@
   }
 
   printf("ABI: ");
-  switch (regs->MachineType()) {
-    case EM_ARM:
+  switch (regs->Arch()) {
+    case unwindstack::ARCH_ARM:
       printf("arm");
       break;
-    case EM_386:
+    case unwindstack::ARCH_X86:
       printf("x86");
       break;
-    case EM_AARCH64:
+    case unwindstack::ARCH_ARM64:
       printf("arm64");
       break;
-    case EM_X86_64:
+    case unwindstack::ARCH_X86_64:
       printf("x86_64");
       break;
     default:
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 77f3bb2..a00b2ee 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -19,6 +19,7 @@
 #include <fcntl.h>
 #include <inttypes.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
@@ -102,12 +103,12 @@
   }
 }
 
-int GetElfInfo(const char* file) {
+int GetElfInfo(const char* file, uint64_t offset) {
   // Send all log messages to stdout.
   log_to_stdout(true);
 
   MemoryFileAtOffset* memory = new MemoryFileAtOffset;
-  if (!memory->Init(file, 0)) {
+  if (!memory->Init(file, offset)) {
     // Initializatation failed.
     printf("Failed to init\n");
     return 1;
@@ -164,8 +165,12 @@
 }  // namespace unwindstack
 
 int main(int argc, char** argv) {
-  if (argc != 2) {
-    printf("Need to pass the name of an elf file to the program.\n");
+  if (argc != 2 && argc != 3) {
+    printf("Usage: unwind_info ELF_FILE [OFFSET]\n");
+    printf("  ELF_FILE\n");
+    printf("    The path to an elf file.\n");
+    printf("  OFFSET\n");
+    printf("    Use the offset into the ELF file as the beginning of the elf.\n");
     return 1;
   }
 
@@ -179,5 +184,15 @@
     return 1;
   }
 
-  return unwindstack::GetElfInfo(argv[1]);
+  uint64_t offset = 0;
+  if (argc == 3) {
+    char* end;
+    offset = strtoull(argv[2], &end, 16);
+    if (*end != '\0') {
+      printf("Malformed OFFSET value: %s\n", argv[2]);
+      return 1;
+    }
+  }
+
+  return unwindstack::GetElfInfo(argv[1], offset);
 }
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 4bd2a98..6b50f0c 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -136,9 +136,12 @@
             cflags: ["-Wno-unused-parameter"],
         },
 
-        // Under MinGW, ctype.h doesn't need multi-byte support
         windows: {
-            cflags: ["-DMB_CUR_MAX=1"],
+            cflags: [
+                // Under MinGW, ctype.h doesn't need multi-byte support
+                "-DMB_CUR_MAX=1",
+                "-Wno-unused-private-field",
+            ],
 
             enabled: true,
         },
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 6317c32..7d7f0e2 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -348,7 +348,7 @@
     mState = (void*) hMutex;
 }
 
-Mutex::Mutex(const char* name)
+Mutex::Mutex(const char* /*name*/)
 {
     // XXX: name not used for now
     HANDLE hMutex;
@@ -359,7 +359,7 @@
     mState = (void*) hMutex;
 }
 
-Mutex::Mutex(int type, const char* name)
+Mutex::Mutex(int /*type*/, const char* /*name*/)
 {
     // XXX: type and name not used for now
     HANDLE hMutex;
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index e7520a8..1086831 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -180,7 +180,15 @@
     size_t ret = 0;
     const char32_t *end = src + src_len;
     while (src < end) {
-        ret += utf32_codepoint_utf8_length(*src++);
+        size_t char_len = utf32_codepoint_utf8_length(*src++);
+        if (SSIZE_MAX - char_len < ret) {
+            // If this happens, we would overflow the ssize_t type when
+            // returning from this function, so we cannot express how
+            // long this string is in an ssize_t.
+            android_errorWriteLog(0x534e4554, "37723026");
+            return -1;
+        }
+        ret += char_len;
     }
     return ret;
 }
@@ -439,14 +447,23 @@
     size_t ret = 0;
     const char16_t* const end = src + src_len;
     while (src < end) {
+        size_t char_len;
         if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
                 && (*(src + 1) & 0xFC00) == 0xDC00) {
             // surrogate pairs are always 4 bytes.
-            ret += 4;
+            char_len = 4;
             src += 2;
         } else {
-            ret += utf32_codepoint_utf8_length((char32_t) *src++);
+            char_len = utf32_codepoint_utf8_length((char32_t)*src++);
         }
+        if (SSIZE_MAX - char_len < ret) {
+            // If this happens, we would overflow the ssize_t type when
+            // returning from this function, so we cannot express how
+            // long this string is in an ssize_t.
+            android_errorWriteLog(0x534e4554, "37723026");
+            return -1;
+        }
+        ret += char_len;
     }
     return ret;
 }
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index cb3d338..15ed19f 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -35,9 +35,7 @@
 
 // ---------------------------------------------------------------------------
 
-class SharedBuffer;
 class String8;
-class TextOutput;
 
 //! This is a string holding UTF-16 characters.
 class String16
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index 1f3e5d8..0225c6b 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -31,7 +31,6 @@
 namespace android {
 
 class String16;
-class TextOutput;
 
 //! This is a string holding UTF-8 characters. Does not allow the value more
 // than 0x10FFFF, which is not valid unicode codepoint.
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index d95fd05..da28dfa 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -44,8 +44,8 @@
 static Vector<sysprop_change_callback_info>* gSyspropList = NULL;
 #endif
 
-void add_sysprop_change_callback(sysprop_change_callback cb, int priority) {
 #if !defined(_WIN32)
+void add_sysprop_change_callback(sysprop_change_callback cb, int priority) {
     pthread_mutex_lock(&gSyspropMutex);
     if (gSyspropList == NULL) {
         gSyspropList = new Vector<sysprop_change_callback_info>();
@@ -65,8 +65,10 @@
         gSyspropList->add(info);
     }
     pthread_mutex_unlock(&gSyspropMutex);
-#endif
 }
+#else
+void add_sysprop_change_callback(sysprop_change_callback, int) {}
+#endif
 
 #if defined(__ANDROID__)
 void (*get_report_sysprop_change_func())() {
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
index 696e978..bc5620b 100644
--- a/libvndksupport/linker.c
+++ b/libvndksupport/linker.c
@@ -21,26 +21,46 @@
 #define LOG_TAG "vndksupport"
 #include <log/log.h>
 
-extern struct android_namespace_t* android_get_exported_namespace(const char*);
+__attribute__((weak)) extern struct android_namespace_t* android_get_exported_namespace(const char*);
+__attribute__((weak)) extern void* android_dlopen_ext(const char*, int, const android_dlextinfo*);
+
+static const char* namespace_name = NULL;
+
+static struct android_namespace_t* get_vendor_namespace() {
+    const char* namespace_names[] = {"sphal", "default", NULL};
+    static struct android_namespace_t* vendor_namespace = NULL;
+    if (vendor_namespace == NULL) {
+        int name_idx = 0;
+        while (namespace_names[name_idx] != NULL) {
+            if (android_get_exported_namespace != NULL) {
+                vendor_namespace = android_get_exported_namespace(namespace_names[name_idx]);
+            }
+            if (vendor_namespace != NULL) {
+                namespace_name = namespace_names[name_idx];
+                break;
+            }
+            name_idx++;
+        }
+    }
+    return vendor_namespace;
+}
 
 void* android_load_sphal_library(const char* name, int flag) {
-    struct android_namespace_t* sphal_namespace = android_get_exported_namespace("sphal");
-    if (sphal_namespace != NULL) {
+    struct android_namespace_t* vendor_namespace = get_vendor_namespace();
+    if (vendor_namespace != NULL) {
         const android_dlextinfo dlextinfo = {
-            .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = sphal_namespace,
+            .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = vendor_namespace,
         };
-        void* handle = android_dlopen_ext(name, flag, &dlextinfo);
+        void* handle = NULL;
+        if (android_dlopen_ext != NULL) {
+            handle = android_dlopen_ext(name, flag, &dlextinfo);
+        }
         if (!handle) {
-            ALOGE(
-                "Could not load %s from sphal namespace: %s. ",
-                name, dlerror());
+            ALOGE("Could not load %s from %s namespace: %s.", name, namespace_name, dlerror());
         }
         return handle;
     } else {
-        ALOGI(
-            "sphal namespace is not configured for this process. "
-            "Loading %s from the current namespace instead.",
-            name);
+        ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
         return dlopen(name, flag);
     }
 }
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 35d0f0b..5ccbcc2 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -100,6 +100,11 @@
 }
 
 static uint32_t ComputeHash(const ZipString& name) {
+#if !defined(_WIN32)
+  return std::hash<std::string_view>{}(
+      std::string_view(reinterpret_cast<const char*>(name.name), name.name_length));
+#else
+  // Remove this code path once the windows compiler knows how to compile the above statement.
   uint32_t hash = 0;
   uint16_t len = name.name_length;
   const uint8_t* str = name.name;
@@ -109,6 +114,7 @@
   }
 
   return hash;
+#endif
 }
 
 /*
@@ -379,6 +385,22 @@
       return -1;
     }
   }
+
+  uint32_t lfh_start_bytes;
+  if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>(&lfh_start_bytes),
+                                        sizeof(uint32_t), 0)) {
+    ALOGW("Zip: Unable to read header for entry at offset == 0.");
+    return -1;
+  }
+
+  if (lfh_start_bytes != LocalFileHeader::kSignature) {
+    ALOGW("Zip: Entry at offset zero has invalid LFH signature %" PRIx32, lfh_start_bytes);
+#if defined(__ANDROID__)
+    android_errorWriteLog(0x534e4554, "64211847");
+#endif
+    return -1;
+  }
+
   ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
 
   return 0;
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 8a6168c..5cfa2c8 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -18,19 +18,21 @@
 
 #include <arpa/inet.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <sched.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
-#include <time.h>
 #include <sys/cdefs.h>
 #include <sys/epoll.h>
 #include <sys/eventfd.h>
 #include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/types.h>
+#include <time.h>
 #include <unistd.h>
 
+#include <cutils/properties.h>
 #include <cutils/sockets.h>
 #include <log/log.h>
 #include <processgroup/processgroup.h>
@@ -40,7 +42,10 @@
 #endif
 
 #define MEMCG_SYSFS_PATH "/dev/memcg/"
-#define MEMPRESSURE_WATCH_LEVEL "low"
+#define MEMCG_MEMORY_USAGE "/dev/memcg/memory.usage_in_bytes"
+#define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
+#define MEMPRESSURE_WATCH_MEDIUM_LEVEL "medium"
+#define MEMPRESSURE_WATCH_CRITICAL_LEVEL "critical"
 #define ZONEINFO_PATH "/proc/zoneinfo"
 #define LINE_MAX 128
 
@@ -48,6 +53,7 @@
 #define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"
 
 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(*(x)))
+#define EIGHT_MEGA (1 << 23)
 
 enum lmk_cmd {
     LMK_TARGET,
@@ -64,17 +70,28 @@
 
 /* default to old in-kernel interface if no memory pressure events */
 static int use_inkernel_interface = 1;
+static bool has_inkernel_module;
 
 /* memory pressure level medium event */
-static int mpevfd;
+static int mpevfd[2];
+#define CRITICAL_INDEX 1
+#define MEDIUM_INDEX 0
+
+static int medium_oomadj;
+static int critical_oomadj;
+static bool debug_process_killing;
+static bool enable_pressure_upgrade;
+static int64_t upgrade_pressure;
+static int64_t downgrade_pressure;
+static bool is_go_device;
 
 /* control socket listen and data */
 static int ctrl_lfd;
 static int ctrl_dfd = -1;
 static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
 
-/* 1 memory pressure level, 1 ctrl listen socket, 1 ctrl data socket */
-#define MAX_EPOLL_EVENTS 3
+/* 2 memory pressure levels, 1 ctrl listen socket, 1 ctrl data socket */
+#define MAX_EPOLL_EVENTS 4
 static int epollfd;
 static int maxevents;
 
@@ -113,14 +130,6 @@
 #define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
 static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
 
-/*
- * Wait 1-2 seconds for the death report of a killed process prior to
- * considering killing more processes.
- */
-#define KILL_TIMEOUT 2
-/* Time of last process kill we initiated, stop me before I kill again */
-static time_t kill_lasttime;
-
 /* PAGE_SIZE / 1024 */
 static long page_k;
 
@@ -241,6 +250,7 @@
     struct proc *procp;
     char path[80];
     char val[20];
+    int soft_limit_mult;
 
     if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) {
         ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
@@ -254,6 +264,38 @@
     if (use_inkernel_interface)
         return;
 
+    if (oomadj >= 900) {
+        soft_limit_mult = 0;
+    } else if (oomadj >= 800) {
+        soft_limit_mult = 0;
+    } else if (oomadj >= 700) {
+        soft_limit_mult = 0;
+    } else if (oomadj >= 600) {
+        // Launcher should be perceptible, don't kill it.
+        oomadj = 200;
+        soft_limit_mult = 1;
+    } else if (oomadj >= 500) {
+        soft_limit_mult = 0;
+    } else if (oomadj >= 400) {
+        soft_limit_mult = 0;
+    } else if (oomadj >= 300) {
+        soft_limit_mult = 1;
+    } else if (oomadj >= 200) {
+        soft_limit_mult = 2;
+    } else if (oomadj >= 100) {
+        soft_limit_mult = 10;
+    } else if (oomadj >=   0) {
+        soft_limit_mult = 20;
+    } else {
+        // Persistent processes will have a large
+        // soft limit 512MB.
+        soft_limit_mult = 64;
+    }
+
+    snprintf(path, sizeof(path), "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", uid, pid);
+    snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
+    writefilestring(path, val);
+
     procp = pid_lookup(pid);
     if (!procp) {
             procp = malloc(sizeof(struct proc));
@@ -278,7 +320,6 @@
         return;
 
     pid_remove(pid);
-    kill_lasttime = 0;
 }
 
 static void cmd_target(int ntargets, int *params) {
@@ -294,7 +335,7 @@
 
     lowmem_targets_size = ntargets;
 
-    if (use_inkernel_interface) {
+    if (has_inkernel_module) {
         char minfreestr[128];
         char killpriostr[128];
 
@@ -309,9 +350,9 @@
                 strlcat(killpriostr, ",", sizeof(killpriostr));
             }
 
-            snprintf(val, sizeof(val), "%d", lowmem_minfree[i]);
+            snprintf(val, sizeof(val), "%d", use_inkernel_interface ? lowmem_minfree[i] : 0);
             strlcat(minfreestr, val, sizeof(minfreestr));
-            snprintf(val, sizeof(val), "%d", lowmem_adj[i]);
+            snprintf(val, sizeof(val), "%d", use_inkernel_interface ? lowmem_adj[i] : 0);
             strlcat(killpriostr, val, sizeof(killpriostr));
         }
 
@@ -546,9 +587,7 @@
 }
 
 /* Kill one process specified by procp.  Returns the size of the process killed */
-static int kill_one_process(struct proc *procp, int other_free, int other_file,
-        int minfree, int min_score_adj, bool first)
-{
+static int kill_one_process(struct proc* procp, int min_score_adj, bool is_critical) {
     int pid = procp->pid;
     uid_t uid = procp->uid;
     char *taskname;
@@ -567,14 +606,12 @@
         return -1;
     }
 
-    ALOGI("Killing '%s' (%d), uid %d, adj %d\n"
-          "   to free %ldkB because cache %s%ldkB is below limit %ldkB for oom_adj %d\n"
-          "   Free memory is %s%ldkB %s reserved",
-          taskname, pid, uid, procp->oomadj, tasksize * page_k,
-          first ? "" : "~", other_file * page_k, minfree * page_k, min_score_adj,
-          first ? "" : "~", other_free * page_k, other_free >= 0 ? "above" : "below");
+    ALOGI(
+        "Killing '%s' (%d), uid %d, adj %d\n"
+        "   to free %ldkB because system is under %s memory pressure oom_adj %d\n",
+        taskname, pid, uid, procp->oomadj, tasksize * page_k, is_critical ? "critical" : "medium",
+        min_score_adj);
     r = kill(pid, SIGKILL);
-    killProcessGroup(uid, pid, SIGKILL);
     pid_remove(pid);
 
     if (r) {
@@ -589,23 +626,10 @@
  * Find a process to kill based on the current (possibly estimated) free memory
  * and cached memory sizes.  Returns the size of the killed processes.
  */
-static int find_and_kill_process(int other_free, int other_file, bool first)
-{
+static int find_and_kill_process(bool is_critical) {
     int i;
-    int min_score_adj = OOM_SCORE_ADJ_MAX + 1;
-    int minfree = 0;
     int killed_size = 0;
-
-    for (i = 0; i < lowmem_targets_size; i++) {
-        minfree = lowmem_minfree[i];
-        if (other_free < minfree && other_file < minfree) {
-            min_score_adj = lowmem_adj[i];
-            break;
-        }
-    }
-
-    if (min_score_adj == OOM_SCORE_ADJ_MAX + 1)
-        return 0;
+    int min_score_adj = is_critical ? critical_oomadj : medium_oomadj;
 
     for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
         struct proc *procp;
@@ -614,7 +638,7 @@
         procp = proc_adj_lru(i);
 
         if (procp) {
-            killed_size = kill_one_process(procp, other_free, other_file, minfree, min_score_adj, first);
+            killed_size = kill_one_process(procp, min_score_adj, is_critical);
             if (killed_size < 0) {
                 goto retry;
             } else {
@@ -626,42 +650,91 @@
     return 0;
 }
 
-static void mp_event(uint32_t events __unused) {
+static int64_t get_memory_usage(const char* path) {
+    int ret;
+    int64_t mem_usage;
+    char buf[32];
+    int fd = open(path, O_RDONLY | O_CLOEXEC);
+    if (fd == -1) {
+        ALOGE("%s open: errno=%d", path, errno);
+        return -1;
+    }
+
+    ret = read_all(fd, buf, sizeof(buf) - 1);
+    close(fd);
+    if (ret < 0) {
+        ALOGE("%s error: errno=%d", path, errno);
+        return -1;
+    }
+    sscanf(buf, "%" SCNd64, &mem_usage);
+    if (mem_usage == 0) {
+        ALOGE("No memory!");
+        return -1;
+    }
+    return mem_usage;
+}
+
+static void mp_event_common(bool is_critical) {
     int ret;
     unsigned long long evcount;
-    struct sysmeminfo mi;
-    int other_free;
-    int other_file;
-    int killed_size;
-    bool first = true;
+    int index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
+    int64_t mem_usage, memsw_usage;
+    int64_t mem_pressure;
 
-    ret = read(mpevfd, &evcount, sizeof(evcount));
+    ret = read(mpevfd[index], &evcount, sizeof(evcount));
     if (ret < 0)
         ALOGE("Error reading memory pressure event fd; errno=%d",
               errno);
 
-    if (time(NULL) - kill_lasttime < KILL_TIMEOUT)
+    mem_usage = get_memory_usage(MEMCG_MEMORY_USAGE);
+    memsw_usage = get_memory_usage(MEMCG_MEMORYSW_USAGE);
+    if (memsw_usage < 0 || mem_usage < 0) {
+        find_and_kill_process(is_critical);
         return;
-
-    while (zoneinfo_parse(&mi) < 0) {
-        // Failed to read /proc/zoneinfo, assume ENOMEM and kill something
-        find_and_kill_process(0, 0, true);
     }
 
-    other_free = mi.nr_free_pages - mi.totalreserve_pages;
-    other_file = mi.nr_file_pages - mi.nr_shmem;
+    // Calculate percent for swappinness.
+    mem_pressure = (mem_usage * 100) / memsw_usage;
 
-    do {
-        killed_size = find_and_kill_process(other_free, other_file, first);
-        if (killed_size > 0) {
-            first = false;
-            other_free += killed_size;
-            other_file += killed_size;
+    if (enable_pressure_upgrade && !is_critical) {
+        // We are swapping too much.
+        if (mem_pressure < upgrade_pressure) {
+            ALOGI("Event upgraded to critical.");
+            is_critical = true;
         }
-    } while (killed_size > 0);
+    }
+
+    // If the pressure is larger than downgrade_pressure lmk will not
+    // kill any process, since enough memory is available.
+    if (mem_pressure > downgrade_pressure) {
+        if (debug_process_killing) {
+            ALOGI("Ignore %s memory pressure", is_critical ? "critical" : "medium");
+        }
+        return;
+    } else if (is_critical && mem_pressure > upgrade_pressure) {
+        if (debug_process_killing) {
+            ALOGI("Downgrade critical memory pressure");
+        }
+        // Downgrade event to medium, since enough memory available.
+        is_critical = false;
+    }
+
+    if (find_and_kill_process(is_critical) == 0) {
+        if (debug_process_killing) {
+            ALOGI("Nothing to kill");
+        }
+    }
 }
 
-static int init_mp(char *levelstr, void *event_handler)
+static void mp_event(uint32_t events __unused) {
+    mp_event_common(false);
+}
+
+static void mp_event_critical(uint32_t events __unused) {
+    mp_event_common(true);
+}
+
+static int init_mp_common(char *levelstr, void *event_handler, bool is_critical)
 {
     int mpfd;
     int evfd;
@@ -669,6 +742,7 @@
     char buf[256];
     struct epoll_event epev;
     int ret;
+    int mpevfd_index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
 
     mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
     if (mpfd < 0) {
@@ -709,7 +783,7 @@
         goto err;
     }
     maxevents++;
-    mpevfd = evfd;
+    mpevfd[mpevfd_index] = evfd;
     return 0;
 
 err:
@@ -722,6 +796,16 @@
     return -1;
 }
 
+static int init_mp_medium()
+{
+    return init_mp_common(MEMPRESSURE_WATCH_MEDIUM_LEVEL, (void *)&mp_event, false);
+}
+
+static int init_mp_critical()
+{
+    return init_mp_common(MEMPRESSURE_WATCH_CRITICAL_LEVEL, (void *)&mp_event_critical, true);
+}
+
 static int init(void) {
     struct epoll_event epev;
     int i;
@@ -758,12 +842,14 @@
     }
     maxevents++;
 
-    use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK);
+    has_inkernel_module = !access(INKERNEL_MINFREE_PATH, W_OK);
+    use_inkernel_interface = has_inkernel_module && !is_go_device;
 
     if (use_inkernel_interface) {
         ALOGI("Using in-kernel low memory killer interface");
     } else {
-        ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event);
+        ret = init_mp_medium();
+        ret |= init_mp_critical();
         if (ret)
             ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
     }
@@ -806,6 +892,14 @@
             .sched_priority = 1,
     };
 
+    medium_oomadj = property_get_int32("ro.lmk.medium", 800);
+    critical_oomadj = property_get_int32("ro.lmk.critical", 0);
+    debug_process_killing = property_get_bool("ro.lmk.debug", false);
+    enable_pressure_upgrade = property_get_bool("ro.lmk.critical_upgrade", false);
+    upgrade_pressure = (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 50);
+    downgrade_pressure = (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 60);
+    is_go_device = property_get_bool("ro.config.low_ram", false);
+
     mlockall(MCL_FUTURE);
     sched_setscheduler(0, SCHED_FIFO, &param);
     if (!init())
diff --git a/logcat/Android.bp b/logcat/Android.bp
index 729c8ff..afc7a01 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -67,6 +67,7 @@
     name: "logpersist.start",
     srcs: ["logpersist"],
     init_rc: ["logcatd.rc"],
+    required: ["logcatd"],
     symlinks: ["logpersist.stop", "logpersist.cat"],
     strip: {
         none: true,
diff --git a/logcat/event.logtags b/logcat/event.logtags
index efcc817..0983676 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -140,5 +140,8 @@
 
 1397638484 snet_event_log (subtag|3) (uid|1) (message|3)
 
+# for events that go to stats log buffer
+1937006964 stats_log (atom_id|1|5),(data|4)
+
 # NOTE - the range 1000000-2000000 is reserved for partners and others who
 # want to define their own log tags without conflicting with the core platform.
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index a2aa486..ff85f54 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1126,8 +1126,9 @@
                     }
                     if (found) continue;
 
-                    bool binary =
-                        !strcmp(name, "events") || !strcmp(name, "security");
+                    bool binary = !strcmp(name, "events") ||
+                                  !strcmp(name, "security") ||
+                                  !strcmp(name, "stats");
                     log_device_t* d = new log_device_t(name, binary);
 
                     if (dev) {
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
old mode 100644
new mode 100755
index a9edc3e..70ecbe0
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -26,18 +26,6 @@
 #include "LogTimes.h"
 #include "LogUtils.h"
 
-FlushCommand::FlushCommand(LogReader& reader, bool nonBlock, unsigned long tail,
-                           unsigned int logMask, pid_t pid, log_time start,
-                           uint64_t timeout)
-    : mReader(reader),
-      mNonBlock(nonBlock),
-      mTail(tail),
-      mLogMask(logMask),
-      mPid(pid),
-      mStart(start),
-      mTimeout((start != log_time::EPOCH) ? timeout : 0) {
-}
-
 // runSocketCommand is called once for every open client on the
 // log reader socket. Here we manage and associated the reader
 // client tracking and log region locks LastLogTimes list of
@@ -56,6 +44,10 @@
     while (it != times.end()) {
         entry = (*it);
         if (entry->mClient == client) {
+            if (!entry->isWatchingMultiple(mLogMask)) {
+                LogTimeEntry::unlock();
+                return;
+            }
             if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
                 if (mReader.logbuf().isMonotonic()) {
                     LogTimeEntry::unlock();
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
old mode 100644
new mode 100755
index 7cdd03f..543dfc3
--- a/logd/FlushCommand.h
+++ b/logd/FlushCommand.h
@@ -29,16 +29,36 @@
     LogReader& mReader;
     bool mNonBlock;
     unsigned long mTail;
-    unsigned int mLogMask;
+    log_mask_t mLogMask;
     pid_t mPid;
     log_time mStart;
     uint64_t mTimeout;
 
    public:
-    explicit FlushCommand(LogReader& mReader, bool nonBlock = false,
-                          unsigned long tail = -1, unsigned int logMask = -1,
-                          pid_t pid = 0, log_time start = log_time::EPOCH,
-                          uint64_t timeout = 0);
+    // for opening a reader
+    explicit FlushCommand(LogReader& reader, bool nonBlock, unsigned long tail,
+                          log_mask_t logMask, pid_t pid, log_time start,
+                          uint64_t timeout)
+        : mReader(reader),
+          mNonBlock(nonBlock),
+          mTail(tail),
+          mLogMask(logMask),
+          mPid(pid),
+          mStart(start),
+          mTimeout((start != log_time::EPOCH) ? timeout : 0) {
+    }
+
+    // for notification of an update
+    explicit FlushCommand(LogReader& reader, log_mask_t logMask)
+        : mReader(reader),
+          mNonBlock(false),
+          mTail(-1),
+          mLogMask(logMask),
+          mPid(0),
+          mStart(log_time::EPOCH),
+          mTimeout(0) {
+    }
+
     virtual void runSocketCommand(SocketClient* client);
 
     static bool hasReadLogs(SocketClient* client);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
old mode 100644
new mode 100755
index cfcbaa5..1d0cc33
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -365,7 +365,7 @@
                   : LOGGER_ENTRY_MAX_PAYLOAD;
     size_t message_len = str_len + sizeof(android_log_event_string_t);
 
-    bool notify = false;
+    log_mask_t notify = 0;
 
     if (events) {  // begin scope for event buffer
         uint32_t buffer[(message_len + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
@@ -384,7 +384,7 @@
             (message_len <= USHRT_MAX) ? (unsigned short)message_len
                                        : USHRT_MAX);
         if (rc >= 0) {
-            notify = true;
+            notify |= 1 << LOG_ID_EVENTS;
         }
         // end scope for event buffer
     }
@@ -440,7 +440,7 @@
                                                     : USHRT_MAX);
 
         if (rc >= 0) {
-            notify = true;
+            notify |= 1 << LOG_ID_MAIN;
         }
         // end scope for main buffer
     }
@@ -449,7 +449,7 @@
     free(str);
 
     if (notify) {
-        reader->notifyNewLog();
+        reader->notifyNewLog(notify);
         if (rc < 0) {
             rc = message_len;
         }
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 7498325..560f490 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -212,13 +212,19 @@
     if (log_id != LOG_ID_SECURITY) {
         int prio = ANDROID_LOG_INFO;
         const char* tag = nullptr;
-        if (log_id == LOG_ID_EVENTS) {
+        size_t tag_len = 0;
+        if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
             tag = tagToName(elem->getTag());
+            if (tag) {
+                tag_len = strlen(tag);
+            }
         } else {
             prio = *msg;
             tag = msg + 1;
+            tag_len = strnlen(tag, len - 1);
         }
-        if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+        if (!__android_log_is_loggable_len(prio, tag, tag_len,
+                                           ANDROID_LOG_VERBOSE)) {
             // Log traffic received to total
             wrlock();
             stats.addTotal(elem);
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
old mode 100644
new mode 100755
index a7e7208..7a7ac7d
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -826,7 +826,7 @@
 
     // notify readers
     if (!rc) {
-        reader->notifyNewLog();
+        reader->notifyNewLog(static_cast<log_mask_t>(1 << LOG_ID_KERNEL));
     }
 
     return rc;
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
old mode 100644
new mode 100755
index d2df68e..fc51dcf
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -43,9 +43,10 @@
         name_set = true;
     }
 
+    // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
     char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) +
-                LOGGER_ENTRY_MAX_PAYLOAD];
-    struct iovec iov = { buffer, sizeof(buffer) };
+                LOGGER_ENTRY_MAX_PAYLOAD + 1];
+    struct iovec iov = { buffer, sizeof(buffer) - 1 };
 
     alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
     struct msghdr hdr = {
@@ -55,13 +56,16 @@
     int socket = cli->getSocket();
 
     // To clear the entire buffer is secure/safe, but this contributes to 1.68%
-    // overhead under logging load. We are safe because we check counts.
+    // overhead under logging load. We are safe because we check counts, but
+    // still need to clear null terminator
     // memset(buffer, 0, sizeof(buffer));
     ssize_t n = recvmsg(socket, &hdr, 0);
     if (n <= (ssize_t)(sizeof(android_log_header_t))) {
         return false;
     }
 
+    buffer[n] = 0;
+
     struct ucred* cred = NULL;
 
     struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
@@ -90,12 +94,13 @@
 
     android_log_header_t* header =
         reinterpret_cast<android_log_header_t*>(buffer);
-    if (/* header->id < LOG_ID_MIN || */ header->id >= LOG_ID_MAX ||
-        header->id == LOG_ID_KERNEL) {
+    log_id_t logId = static_cast<log_id_t>(header->id);
+    if (/* logId < LOG_ID_MIN || */ logId >= LOG_ID_MAX ||
+        logId == LOG_ID_KERNEL) {
         return false;
     }
 
-    if ((header->id == LOG_ID_SECURITY) &&
+    if ((logId == LOG_ID_SECURITY) &&
         (!__android_log_security() ||
          !clientHasLogCredentials(cred->uid, cred->gid, cred->pid))) {
         return false;
@@ -130,11 +135,10 @@
 
     if (logbuf != nullptr) {
         int res = logbuf->log(
-            (log_id_t)header->id, header->realtime, cred->uid, cred->pid,
-            header->tid, msg,
+            logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
             ((size_t)n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX);
         if (res > 0 && reader != nullptr) {
-            reader->notifyNewLog();
+            reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
         }
     }
 
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
old mode 100644
new mode 100755
index 6d69316..2b6556d
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -35,9 +35,9 @@
 }
 
 // When we are notified a new log entry is available, inform
-// all of our listening sockets.
-void LogReader::notifyNewLog() {
-    FlushCommand command(*this);
+// listening sockets who are watching this entry's log id.
+void LogReader::notifyNewLog(log_mask_t logMask) {
+    FlushCommand command(*this, logMask);
     runOnEachSocket(&command);
 }
 
diff --git a/logd/LogReader.h b/logd/LogReader.h
old mode 100644
new mode 100755
index 271e08c..b5312b6
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -19,6 +19,8 @@
 
 #include <sysutils/SocketListener.h>
 
+#include "LogTimes.h"
+
 #define LOGD_SNDTIMEO 32
 
 class LogBuffer;
@@ -28,7 +30,7 @@
 
    public:
     explicit LogReader(LogBuffer* logbuf);
-    void notifyNewLog();
+    void notifyNewLog(log_mask_t logMask);
 
     LogBuffer& logbuf(void) const {
         return mLogbuf;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 8808aac..ac3cf9a 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -25,9 +25,9 @@
 #include <sys/types.h>
 
 #include <algorithm>  // std::max
-#include <experimental/string_view>
 #include <memory>
-#include <string>  // std::string
+#include <string>
+#include <string_view>
 #include <unordered_map>
 
 #include <android-base/stringprintf.h>
@@ -495,7 +495,7 @@
 
 struct TagNameKey {
     std::string* alloc;
-    std::experimental::string_view name;  // Saves space if const char*
+    std::string_view name;  // Saves space if const char*
 
     explicit TagNameKey(const LogBufferElement* element)
         : alloc(nullptr), name("", strlen("")) {
@@ -504,31 +504,31 @@
             if (tag) {
                 const char* cp = android::tagToName(tag);
                 if (cp) {
-                    name = std::experimental::string_view(cp, strlen(cp));
+                    name = std::string_view(cp, strlen(cp));
                     return;
                 }
             }
             alloc = new std::string(
                 android::base::StringPrintf("[%" PRIu32 "]", tag));
             if (!alloc) return;
-            name = std::experimental::string_view(alloc->c_str(), alloc->size());
+            name = std::string_view(alloc->c_str(), alloc->size());
             return;
         }
         const char* msg = element->getMsg();
         if (!msg) {
-            name = std::experimental::string_view("chatty", strlen("chatty"));
+            name = std::string_view("chatty", strlen("chatty"));
             return;
         }
         ++msg;
         unsigned short len = element->getMsgLen();
         len = (len <= 1) ? 0 : strnlen(msg, len - 1);
         if (!len) {
-            name = std::experimental::string_view("<NULL>", strlen("<NULL>"));
+            name = std::string_view("<NULL>", strlen("<NULL>"));
             return;
         }
         alloc = new std::string(msg, len);
         if (!alloc) return;
-        name = std::experimental::string_view(alloc->c_str(), alloc->size());
+        name = std::string_view(alloc->c_str(), alloc->size());
     }
 
     explicit TagNameKey(TagNameKey&& rval)
@@ -545,7 +545,7 @@
         if (alloc) delete alloc;
     }
 
-    operator const std::experimental::string_view() const {
+    operator const std::string_view() const {
         return name;
     }
 
@@ -576,8 +576,7 @@
     : public std::unary_function<const TagNameKey&, size_t> {
     size_t operator()(const TagNameKey& __t) const noexcept {
         if (!__t.length()) return 0;
-        return std::hash<std::experimental::string_view>()(
-            std::experimental::string_view(__t));
+        return std::hash<std::string_view>()(std::string_view(__t));
     }
 };
 
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
old mode 100644
new mode 100755
index 25c2ad2..7a6f84b
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -28,9 +28,8 @@
 pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
 
 LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client,
-                           bool nonBlock, unsigned long tail,
-                           unsigned int logMask, pid_t pid, log_time start,
-                           uint64_t timeout)
+                           bool nonBlock, unsigned long tail, log_mask_t logMask,
+                           pid_t pid, log_time start, uint64_t timeout)
     : mRefCount(1),
       mRelease(false),
       mError(false),
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
old mode 100644
new mode 100755
index 9ca2aea..76d016c
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -26,6 +26,8 @@
 #include <log/log.h>
 #include <sysutils/SocketClient.h>
 
+typedef unsigned int log_mask_t;
+
 class LogReader;
 class LogBufferElement;
 
@@ -41,7 +43,7 @@
     LogReader& mReader;
     static void* threadStart(void* me);
     static void threadStop(void* me);
-    const unsigned int mLogMask;
+    const log_mask_t mLogMask;
     const pid_t mPid;
     unsigned int skipAhead[LOG_ID_MAX];
     pid_t mLastTid[LOG_ID_MAX];
@@ -51,7 +53,7 @@
 
    public:
     LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock,
-                 unsigned long tail, unsigned int logMask, pid_t pid,
+                 unsigned long tail, log_mask_t logMask, pid_t pid,
                  log_time start, uint64_t timeout);
 
     SocketClient* mClient;
@@ -133,8 +135,11 @@
         // No one else is holding a reference to this
         delete this;
     }
-    bool isWatching(log_id_t id) {
-        return (mLogMask & (1 << id)) != 0;
+    bool isWatching(log_id_t id) const {
+        return mLogMask & (1 << id);
+    }
+    bool isWatchingMultiple(log_mask_t logMask) const {
+        return mLogMask & logMask;
     }
     // flushTo filter callbacks
     static int FilterFirstPass(const LogBufferElement* element, void* me);
diff --git a/logd/logd.rc b/logd/logd.rc
index 8804246..bd303b7 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -20,4 +20,3 @@
 "
     chown logd logd /dev/event-log-tags
     chmod 0644 /dev/event-log-tags
-    restorecon /dev/event-log-tags
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
index 1915677..a0875ea 100644
--- a/logd/tests/Android.mk
+++ b/logd/tests/Android.mk
@@ -63,7 +63,7 @@
 LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
 LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_COMPATIBILITY_SUITE := cts vts
 LOCAL_CTS_TEST_PACKAGE := android.core.logd
 include $(BUILD_CTS_EXECUTABLE)
 
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index f75036d..6daeb75 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -142,6 +142,7 @@
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
     sbin dev proc sys system data oem acct config storage mnt $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
     ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
@@ -206,10 +207,6 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 LOCAL_MODULE_STEM := $(LOCAL_MODULE)
 include $(BUILD_SYSTEM)/base_rules.mk
-vndk_lib_md5 := $(word 1, $(shell echo $(LLNDK_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES) | $(MD5SUM)))
-vndk_lib_dep := $(intermediates)/$(vndk_lib_md5).dep
-$(vndk_lib_dep):
-	$(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.dep && touch $@
 
 llndk_libraries := $(subst $(space),:,$(addsuffix .so,$(LLNDK_LIBRARIES)))
 
@@ -230,7 +227,7 @@
 $(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_PRIVATE_LIBRARIES := $(llndk_private_libraries)
 $(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(vndk_core_libraries)
 $(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(sanitizer_runtime_libraries)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/ld.config.txt.in $(vndk_lib_dep)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/ld.config.txt.in
 	@echo "Generate: $< -> $@"
 	@mkdir -p $(dir $@)
 	$(hide) sed -e 's?%LLNDK_LIBRARIES%?$(PRIVATE_LLNDK_LIBRARIES)?g' $< >$@
@@ -238,8 +235,6 @@
 	$(hide) sed -i -e 's?%VNDK_CORE_LIBRARIES%?$(PRIVATE_VNDK_CORE_LIBRARIES)?g' $@
 	$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $@
 
-vndk_lib_md5 :=
-vndk_lib_dep :=
 llndk_libraries :=
 vndk_sameprocess_libraries :=
 vndk_core_libraries :=
@@ -247,7 +242,7 @@
 else # if _enforce_vndk_at_runtime is not true
 
 LOCAL_MODULE := ld.config.txt
-ifeq ($(PRODUCT_FULL_TREBLE)|$(SANITIZE_TARGET),true|)
+ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
 LOCAL_SRC_FILES := etc/ld.config.txt
 else
 LOCAL_SRC_FILES := etc/ld.config.legacy.txt
@@ -257,3 +252,35 @@
 LOCAL_MODULE_STEM := $(LOCAL_MODULE)
 include $(BUILD_PREBUILT)
 endif
+
+#######################################
+# llndk.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := llndk.libraries.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(LLNDK_LIBRARIES)
+$(LOCAL_BUILT_MODULE):
+	@echo "Generate: $@"
+	@mkdir -p $(dir $@)
+	$(hide) echo -n > $@
+	$(hide) $(foreach lib,$(PRIVATE_LLNDK_LIBRARIES), \
+		echo $(lib).so >> $@;)
+
+#######################################
+# vndksp.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := vndksp.libraries.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(VNDK_SAMEPROCESS_LIBRARIES)
+$(LOCAL_BUILT_MODULE):
+	@echo "Generate: $@"
+	@mkdir -p $(dir $@)
+	$(hide) echo -n > $@
+	$(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \
+		echo $(lib).so >> $@;)
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index c22edfe..4d058db 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -7,9 +7,10 @@
 # All binaries gets the same configuration 'legacy'
 dir.legacy = /system
 dir.legacy = /vendor
+dir.legacy = /odm
 dir.legacy = /sbin
 
 [legacy]
 namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}:/vendor/${LIB}
-namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.default.search.paths = /system/${LIB}:/vendor/${LIB}:/odm/${LIB}
+namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/odm/${LIB}:/odm/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 8aa3369..cae6e13 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -6,7 +6,12 @@
 # Don't change the order here.
 dir.system = /system/bin/
 dir.system = /system/xbin/
+dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
+dir.vendor = /data/nativetest/odm
+dir.vendor = /data/nativetest64/odm
+dir.vendor = /data/benchmarktest/odm
+dir.vendor = /data/benchmarktest64/odm
 dir.vendor = /data/nativetest/vendor
 dir.vendor = /data/nativetest64/vendor
 dir.vendor = /data/benchmarktest/vendor
@@ -26,8 +31,8 @@
 # can't be loaded in this namespace.
 ###############################################################################
 namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}:/vendor/${LIB}
-namespace.default.permitted.paths = /system/${LIB}:/vendor/${LIB}
+namespace.default.search.paths = /system/${LIB}:/odm/${LIB}:/vendor/${LIB}
+namespace.default.permitted.paths = /system/${LIB}:/odm/${LIB}:/vendor/${LIB}
 
 namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
 namespace.default.asan.permitted.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
@@ -123,6 +128,6 @@
 ###############################################################################
 [vendor]
 namespace.default.isolated = false
-namespace.default.search.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/system/${LIB}/vndk:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/system/${LIB}
+namespace.default.search.paths = /odm/${LIB}/hw:/odm/${LIB}/egl:/odm/${LIB}:/vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/system/${LIB}/vndk:/odm/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/system/${LIB}
 
-namespace.default.asan.search.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/system/${LIB}/vndk:/system/${LIB}/vndk:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/system/${LIB}:/system/${LIB}
+namespace.default.asan.search.paths = /data/asan/odm/${LIB}/hw:/odm/${LIB}/hw:/data/asan/odm/${LIB}/egl:/odm/${LIB}/egl:/data/asan/odm/${LIB}:/odm/${LIB}:/data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/system/${LIB}/vndk:/system/${LIB}/vndk:/data/asan/odm/${LIB}/vndk-sp:/odm/${LIB}/vndk-sp:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/system/${LIB}:/system/${LIB}
diff --git a/rootdir/etc/ld.config.txt.in b/rootdir/etc/ld.config.txt.in
index e741a34..9c108e2 100644
--- a/rootdir/etc/ld.config.txt.in
+++ b/rootdir/etc/ld.config.txt.in
@@ -4,7 +4,7 @@
 #
 
 # Don't change the order here. The first pattern that matches with the
-# absolution path of an executable is selected.
+# absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
 dir.vendor = /vendor/bin/
@@ -27,16 +27,12 @@
 # can't be loaded in this namespace.
 ###############################################################################
 namespace.default.isolated = true
-# TODO(b/63553457): remove /vendor/lib from the search path. For now, this is
-# required since the classloader namespace for vendor apks should have access
-# vendor libraries in the directory. These search paths are copied to the search
-# paths of the classloader namespace.
-namespace.default.search.paths = /system/${LIB}:/vendor/${LIB}
+namespace.default.search.paths = /system/${LIB}
 # /vendor/app, /vendor/framework were added since libart should be able to dlopen
 # the odex files from the directory.
 namespace.default.permitted.paths = /system/${LIB}/drm:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/data:/mnt/expand
 
-namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}
 namespace.default.asan.permitted.paths = /data:/system/${LIB}/drm:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/mnt/expand
 
 ###############################################################################
@@ -99,6 +95,7 @@
 # This namespace is exclusively for vndk-sp libs.
 ###############################################################################
 namespace.vndk.isolated = true
+namespace.vndk.visible = true
 namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
 namespace.vndk.permitted.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl
 
@@ -135,9 +132,7 @@
 namespace.default.asan.permitted.paths = /data/asan/vendor:/vendor:/data/asan/system/${LIB}/vndk:/system/${LIB}/vndk:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
 
 namespace.default.links = system
-namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%:libmedia.so:libandroid_runtime.so
-# libmedia.so must be removed after we have fix for lib-dplmedia.so (b/64427765)
-# libandroid_runtime.so must be removed after we have a fix for qseeproxydaemon (b/64820887)
+namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
 
 ###############################################################################
 # "system" namespace
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index c5e149c..e20b95d 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -15,6 +15,7 @@
 libmediandk.so
 libm.so
 libnativewindow.so
+libneuralnetworks.so
 libOpenMAXAL.so
 libOpenSLES.so
 libRS.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index a4ca683..3c46094 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -15,6 +15,7 @@
 libmediandk.so
 libm.so
 libnativewindow.so
+libneuralnetworks.so
 libOpenMAXAL.so
 libOpenSLES.so
 libRS.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 96eb6f3..5fa77d8 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -48,8 +48,10 @@
     copy /proc/cmdline /dev/urandom
     copy /default.prop /dev/urandom
 
-    # Backward compatibility.
+    symlink /system/bin /bin
     symlink /system/etc /etc
+
+    # Backward compatibility.
     symlink /sys/kernel/debug /d
 
     # Link /vendor to /system/vendor for devices without a vendor partition.
@@ -148,6 +150,9 @@
     write /proc/sys/net/ipv4/conf/all/accept_redirects 0
     write /proc/sys/net/ipv6/conf/all/accept_redirects 0
 
+    # /proc/net/fib_trie leaks interface IP addresses
+    chmod 0400 /proc/net/fib_trie
+
     # Create cgroup mount points for process groups
     mkdir /dev/cpuctl
     mount cgroup none /dev/cpuctl cpu
@@ -409,6 +414,7 @@
     mkdir /data/misc/net 0750 root shell
     mkdir /data/misc/radio 0770 system radio
     mkdir /data/misc/sms 0770 system radio
+    mkdir /data/misc/carrierid 0770 system radio
     mkdir /data/misc/zoneinfo 0775 system system
     mkdir /data/misc/textclassifier 0771 system system
     mkdir /data/misc/vpn 0770 system vpn
@@ -430,7 +436,10 @@
     mkdir /data/misc/vold 0700 root root
     mkdir /data/misc/boottrace 0771 system shell
     mkdir /data/misc/update_engine 0700 root root
+    mkdir /data/misc/update_engine_log 02750 root log
     mkdir /data/misc/trace 0700 root root
+    # create location to store surface and window trace files
+    mkdir /data/misc/wmtrace 0700 system system
     # profile file layout
     mkdir /data/misc/profiles 0771 system system
     mkdir /data/misc/profiles/cur 0771 system system
@@ -480,6 +489,10 @@
 
     mkdir /data/anr 0775 system system
 
+    # NFC: create data/nfc for nv storage
+    mkdir /data/nfc 0770 nfc nfc
+    mkdir /data/nfc/param 0770 nfc nfc
+
     # Create all remaining /data root dirs so that they are made through init
     # and get proper encryption policy installed
     mkdir /data/backup 0700 system system
@@ -725,6 +738,8 @@
     # Give writes to anyone for the trace folder on debug builds.
     # The folder is used to store method traces.
     chmod 0773 /data/misc/trace
+    # Give reads to anyone for the window trace folder on debug builds.
+    chmod 0775 /data/misc/wmtrace
     start console
 
 service flash_recovery /system/bin/install-recovery.sh
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index 343a903..77d5a91 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -319,17 +319,31 @@
     LOG(FATAL) << "terminated prematurely";
 }
 
-static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path, uid_t fsuid,
-                        gid_t fsgid, bool multi_user, userid_t userid, gid_t gid, mode_t mask) {
-    std::string opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
-            fsuid, fsgid, multi_user?"multiuser,":"", mask, userid, gid);
+static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
+                           uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
+                           mode_t mask, bool derive_gid) {
+    std::string opts = android::base::StringPrintf(
+        "fsuid=%d,fsgid=%d,%s%smask=%d,userid=%d,gid=%d", fsuid, fsgid,
+        multi_user ? "multiuser," : "", derive_gid ? "derive_gid," : "", mask, userid, gid);
 
     if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs",
               MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
-        PLOG(ERROR) << "failed to mount sdcardfs filesystem";
-        return false;
+        if (derive_gid) {
+            PLOG(ERROR) << "trying to mount sdcardfs filesystem without derive_gid";
+            /* Maybe this isn't supported on this kernel. Try without. */
+            opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
+                                               fsuid, fsgid, multi_user ? "multiuser," : "", mask,
+                                               userid, gid);
+            if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs",
+                      MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
+                PLOG(ERROR) << "failed to mount sdcardfs filesystem";
+                return false;
+            }
+        } else {
+            PLOG(ERROR) << "failed to mount sdcardfs filesystem";
+            return false;
+        }
     }
-
     return true;
 }
 
@@ -355,7 +369,8 @@
 }
 
 static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
-        gid_t gid, userid_t userid, bool multi_user, bool full_write) {
+                         gid_t gid, userid_t userid, bool multi_user, bool full_write,
+                         bool derive_gid) {
     std::string dest_path_default = "/mnt/runtime/default/" + label;
     std::string dest_path_read = "/mnt/runtime/read/" + label;
     std::string dest_path_write = "/mnt/runtime/write/" + label;
@@ -365,10 +380,10 @@
         // Multi-user storage is fully isolated per user, so "other"
         // permissions are completely masked off.
         if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
-                                                      AID_SDCARD_RW, 0006)
-                || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, 0027)
-                || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write,
-                                                      AID_EVERYBODY, full_write ? 0007 : 0027)) {
+                            AID_SDCARD_RW, 0006, derive_gid) ||
+            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, 0027) ||
+            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
+                                         full_write ? 0007 : 0027)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     } else {
@@ -376,11 +391,11 @@
         // the Android directories are masked off to a single user
         // deep inside attr_from_stat().
         if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
-                                                      AID_SDCARD_RW, 0006)
-                || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read,
-                                                      AID_EVERYBODY, full_write ? 0027 : 0022)
-                || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write,
-                                                      AID_EVERYBODY, full_write ? 0007 : 0022)) {
+                            AID_SDCARD_RW, 0006, derive_gid) ||
+            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY,
+                                         full_write ? 0027 : 0022) ||
+            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
+                                         full_write ? 0007 : 0022)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     }
@@ -437,7 +452,8 @@
                << "    -g: specify GID to run as"
                << "    -U: specify user ID that owns device"
                << "    -m: source_path is multi-user"
-               << "    -w: runtime write mount has full write access";
+               << "    -w: runtime write mount has full write access"
+               << "    -P  preserve owners on the lower file system";
     return 1;
 }
 
@@ -449,12 +465,13 @@
     userid_t userid = 0;
     bool multi_user = false;
     bool full_write = false;
+    bool derive_gid = false;
     int i;
     struct rlimit rlim;
     int fs_version;
 
     int opt;
-    while ((opt = getopt(argc, argv, "u:g:U:mw")) != -1) {
+    while ((opt = getopt(argc, argv, "u:g:U:mwG")) != -1) {
         switch (opt) {
             case 'u':
                 uid = strtoul(optarg, NULL, 10);
@@ -471,6 +488,9 @@
             case 'w':
                 full_write = true;
                 break;
+            case 'G':
+                derive_gid = true;
+                break;
             case '?':
             default:
                 return usage();
@@ -514,7 +534,7 @@
     }
 
     if (should_use_sdcardfs()) {
-        run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write);
+        run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid);
     } else {
         run(source_path, label, uid, gid, userid, multi_user, full_write);
     }
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 9620d63..4203db4 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -1,6 +1,8 @@
 phony {
     name: "shell_and_utilities",
     required: [
+        "awk",
+        "awk_vendor",
         "bzip2",
         "grep",
         "grep_vendor",
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index c4e8aac..206204b 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -21,7 +21,8 @@
 Not everything is provided by toybox, though. We currently still use
 the BSD dd and grep (because the toybox versions are still unfinished),
 and for the bzip2 command-line tools we use the ones that are part of
-the bzip2 distribution.
+the bzip2 distribution. The awk added in Android P is Brian Kernighan's
+"one true" awk.
 
 The lists below show what tools were provided and where they came from in
 each release starting with Gingerbread. This doesn't tell the full story,
@@ -164,3 +165,29 @@
 sysctl tac tail tar taskset tee time timeout top touch tr true truncate
 tty ulimit umount uname uniq unix2dos uptime usleep uudecode uuencode
 vmstat wc which whoami xargs xxd yes zcat
+
+Android P
+---------
+
+BSD: dd grep
+
+bzip2: bzcat bzip2 bunzip2
+
+one-true-awk: awk
+
+toolbox: getevent newfs\_msdos
+
+toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
+dos2unix du echo env expand expr fallocate false file find flock free
+getenforce getprop groups gunzip gzip head hostname hwclock id ifconfig
+inotifyd insmod ionice iorenice kill killall ln load\_policy log logname
+losetup ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod
+mkswap mktemp modinfo modprobe more mount mountpoint mv netstat nice
+nl nohup od paste patch pgrep pidof pkill pmap printenv printf ps pwd
+readlink realpath renice restorecon rm rmdir rmmod runcon sed sendevent
+seq setenforce setprop setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split start stat stop strings swapoff swapon sync
+sysctl tac tail tar taskset tee time timeout top touch tr true truncate
+tty ulimit umount uname uniq unix2dos uptime usleep uudecode uuencode
+vmstat wc which whoami xargs xxd yes zcat
diff --git a/storaged/Android.mk b/storaged/Android.mk
index 5e6a3c0..a1abe0f 100644
--- a/storaged/Android.mk
+++ b/storaged/Android.mk
@@ -9,7 +9,6 @@
     libcutils \
     liblog \
     libsysutils \
-    libpackagelistparser \
     libbatteryservice \
 
 include $(CLEAR_VARS)
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index 514798b..fa68406 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -256,6 +256,7 @@
     uid_monitor mUidm;
     time_t mStarttime;
     sp<IBatteryPropertiesRegistrar> battery_properties;
+    std::unique_ptr<storage_info_t> storage_info;
 public:
     storaged_t(void);
     ~storaged_t() {}
@@ -285,6 +286,8 @@
     void init_battery_service();
     virtual void batteryPropertiesChanged(struct BatteryProperties props);
     void binderDied(const wp<IBinder>& who);
+
+    void report_storage_info();
 };
 
 // Eventlog tag
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
index 913c814..7d04c7a 100644
--- a/storaged/include/storaged_info.h
+++ b/storaged/include/storaged_info.h
@@ -27,39 +27,46 @@
 class storage_info_t {
 protected:
     FRIEND_TEST(storaged_test, storage_info_t);
+    // emmc lifetime
     uint16_t eol;                   // pre-eol (end of life) information
     uint16_t lifetime_a;            // device life time estimation (type A)
     uint16_t lifetime_b;            // device life time estimation (type B)
     string version;                 // version string
+    // free space
+    const string userdata_path = "/data";
+    uint64_t userdata_total_kb;
+    uint64_t userdata_free_kb;
+
+    storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0),
+        userdata_total_kb(0), userdata_free_kb(0) {}
     void publish();
+    storage_info_t* s_info;
 public:
-    storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0) {}
+    static storage_info_t* get_storage_info();
     virtual ~storage_info_t() {}
-    virtual bool report() = 0;
+    virtual void report() {};
+    void refresh();
 };
 
 class emmc_info_t : public storage_info_t {
 private:
-    const string emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
-    const string emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
-    const char* emmc_ver_str[9] = {
-        "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
-    };
-public:
-    virtual ~emmc_info_t() {}
-    bool report();
     bool report_sysfs();
     bool report_debugfs();
+public:
+    static const string emmc_sysfs;
+    static const string emmc_debugfs;
+    static const char* emmc_ver_str[];
+
+    virtual ~emmc_info_t() {}
+    virtual void report();
 };
 
 class ufs_info_t : public storage_info_t {
-private:
-    const string health_file = "/sys/devices/soc/624000.ufshc/health";
 public:
-    virtual ~ufs_info_t() {}
-    bool report();
-};
+    static const string health_file;
 
-void report_storage_health();
+    virtual ~ufs_info_t() {}
+    virtual void report();
+};
 
 #endif /* _STORAGED_INFO_H_ */
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 4d1e430..6b82904 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -49,6 +49,7 @@
     storaged = new storaged_t();
 
     storaged->init_battery_service();
+    storaged->report_storage_info();
 
     LOG_TO(SYSTEM, INFO) << "storaged: Start";
 
@@ -113,7 +114,6 @@
     }
 
     if (flag_main_service) { // start main thread
-        report_storage_health();
         // Start the main thread of storaged
         pthread_t storaged_main_thread;
         errno = pthread_create(&storaged_main_thread, NULL, storaged_main, NULL);
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 54d429c..06afea6 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -200,6 +200,10 @@
     }
 }
 
+void storaged_t::report_storage_info() {
+    storage_info->report();
+}
+
 /* storaged_t */
 storaged_t::storaged_t(void) {
     if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) {
@@ -222,6 +226,8 @@
     mConfig.periodic_chores_interval_uid_io =
         property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
 
+    storage_info.reset(storage_info_t::get_storage_info());
+
     mStarttime = time(NULL);
 }
 
@@ -229,6 +235,7 @@
     if (mConfig.diskstats_available) {
         mDiskStats.update();
         mDsm.update();
+        storage_info->refresh();
         if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) {
             mDiskStats.publish();
         }
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 434bd74..b5fb13e 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -18,6 +18,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <sys/statvfs.h>
 
 #include <android-base/file.h>
 #include <android-base/parseint.h>
@@ -30,13 +31,42 @@
 using namespace std;
 using namespace android::base;
 
-void report_storage_health()
-{
-    emmc_info_t mmc;
-    ufs_info_t ufs;
+const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
+const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
+const char* emmc_info_t::emmc_ver_str[9] = {
+    "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
+};
 
-    mmc.report();
-    ufs.report();
+const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health";
+
+static bool FileExists(const std::string& filename)
+{
+  struct stat buffer;
+  return stat(filename.c_str(), &buffer) == 0;
+}
+
+storage_info_t* storage_info_t::get_storage_info()
+{
+    if (FileExists(emmc_info_t::emmc_sysfs) ||
+        FileExists(emmc_info_t::emmc_debugfs)) {
+        return new emmc_info_t;
+    }
+    if (FileExists(ufs_info_t::health_file)) {
+        return new ufs_info_t;
+    }
+    return new storage_info_t;
+}
+
+void storage_info_t::refresh()
+{
+    struct statvfs buf;
+    if (statvfs(userdata_path.c_str(), &buf) != 0) {
+        PLOG_TO(SYSTEM, WARNING) << "Failed to get userdata info";
+        return;
+    }
+
+    userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;
+    userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;
 }
 
 void storage_info_t::publish()
@@ -46,13 +76,12 @@
         << LOG_ID_EVENTS;
 }
 
-bool emmc_info_t::report()
+void emmc_info_t::report()
 {
     if (!report_sysfs() && !report_debugfs())
-        return false;
+        return;
 
     publish();
-    return true;
 }
 
 bool emmc_info_t::report_sysfs()
@@ -136,21 +165,21 @@
     return true;
 }
 
-bool ufs_info_t::report()
+void ufs_info_t::report()
 {
     string buffer;
     if (!ReadFileToString(health_file, &buffer)) {
-        return false;
+        return;
     }
 
     vector<string> lines = Split(buffer, "\n");
     if (lines.empty()) {
-        return false;
+        return;
     }
 
     char rev[8];
     if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
-        return false;
+        return;
     }
 
     version = "ufs " + string(rev);
@@ -175,10 +204,9 @@
     }
 
     if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {
-        return false;
+        return;
     }
 
     publish();
-    return true;
 }
 
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index 5bb98e1..dd8bdd6 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -22,33 +22,24 @@
 #include <string>
 #include <unordered_map>
 
+#include <android/content/pm/IPackageManagerNative.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 #include <android-base/stringprintf.h>
+#include <binder/IServiceManager.h>
 #include <log/log_event_list.h>
-#include <packagelistparser/packagelistparser.h>
 
 #include "storaged.h"
 #include "storaged_uid_monitor.h"
 
 using namespace android;
 using namespace android::base;
+using namespace android::content::pm;
 
-static bool packagelist_parse_cb(pkg_info* info, void* userdata)
-{
-    std::unordered_map<uint32_t, struct uid_info>* uids =
-        reinterpret_cast<std::unordered_map<uint32_t, struct uid_info>*>(userdata);
-
-    if (uids->find(info->uid) != uids->end()) {
-        (*uids)[info->uid].name = info->name;
-    }
-
-    packagelist_free(info);
-    return true;
-}
+static bool refresh_uid_names;
 
 std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats()
 {
@@ -56,6 +47,38 @@
     return get_uid_io_stats_locked();
 };
 
+static void get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names)
+{
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == NULL) {
+        LOG_TO(SYSTEM, ERROR) << "defaultServiceManager failed";
+        return;
+    }
+
+    sp<IBinder> binder = sm->getService(String16("package_native"));
+    if (binder == NULL) {
+        LOG_TO(SYSTEM, ERROR) << "getService package_native failed";
+        return;
+    }
+
+    sp<IPackageManagerNative> package_mgr = interface_cast<IPackageManagerNative>(binder);
+    std::vector<std::string> names;
+    binder::Status status = package_mgr->getNamesForUids(uids, &names);
+    if (!status.isOk()) {
+        LOG_TO(SYSTEM, ERROR) << "package_native::getNamesForUids failed: "
+                              << status.exceptionMessage();
+        return;
+    }
+
+    for (uint32_t i = 0; i < uid_names.size(); i++) {
+        if (!names[i].empty()) {
+            *uid_names[i] = names[i];
+        }
+    }
+
+    refresh_uid_names = false;
+}
+
 std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_locked()
 {
     std::unordered_map<uint32_t, struct uid_info> uid_io_stats;
@@ -67,7 +90,8 @@
 
     std::vector<std::string> io_stats = Split(buffer, "\n");
     struct uid_info u;
-    bool refresh_uid = false;
+    vector<int> uids;
+    vector<std::string*> uid_names;
 
     for (uint32_t i = 0; i < io_stats.size(); i++) {
         if (io_stats[i].empty()) {
@@ -91,17 +115,19 @@
             continue;
         }
 
-        if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
-            refresh_uid = true;
-            u.name = std::to_string(u.uid);
-        } else {
-            u.name = last_uid_io_stats[u.uid].name;
-        }
         uid_io_stats[u.uid] = u;
+        uid_io_stats[u.uid].name = std::to_string(u.uid);
+        uids.push_back(u.uid);
+        uid_names.push_back(&uid_io_stats[u.uid].name);
+        if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
+            refresh_uid_names = true;
+        } else {
+            uid_io_stats[u.uid].name = last_uid_io_stats[u.uid].name;
+        }
     }
 
-    if (refresh_uid) {
-        packagelist_parse(packagelist_parse_cb, &uid_io_stats);
+    if (!uids.empty() && refresh_uid_names) {
+        get_uid_names(uids, uid_names);
     }
 
     return uid_io_stats;
@@ -228,13 +254,13 @@
             last_uid_io_stats[uid.uid].io[BACKGROUND].write_bytes;
 
         usage.bytes[READ][FOREGROUND][charger_stat] +=
-            (fg_rd_delta < 0) ? uid.io[FOREGROUND].read_bytes : fg_rd_delta;
+            (fg_rd_delta < 0) ? 0 : fg_rd_delta;
         usage.bytes[READ][BACKGROUND][charger_stat] +=
-            (bg_rd_delta < 0) ? uid.io[BACKGROUND].read_bytes : bg_rd_delta;
+            (bg_rd_delta < 0) ? 0 : bg_rd_delta;
         usage.bytes[WRITE][FOREGROUND][charger_stat] +=
-            (fg_wr_delta < 0) ? uid.io[FOREGROUND].write_bytes : fg_wr_delta;
+            (fg_wr_delta < 0) ? 0 : fg_wr_delta;
         usage.bytes[WRITE][BACKGROUND][charger_stat] +=
-            (bg_wr_delta < 0) ? uid.io[BACKGROUND].write_bytes : bg_wr_delta;
+            (bg_wr_delta < 0) ? 0 : bg_wr_delta;
     }
 
     last_uid_io_stats = uid_io_stats;
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 8db8327..9c2cafa 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -40,6 +40,10 @@
     cflags: common_cflags,
     local_include_dirs: ["upstream-netbsd/include/"],
     symlinks: ["egrep", "fgrep"],
+
+    sanitize: {
+        integer_overflow: false,
+    },
 }
 
 cc_binary {
diff --git a/trusty/Android.bp b/trusty/Android.bp
index 386fbe6..2fb2e19 100644
--- a/trusty/Android.bp
+++ b/trusty/Android.bp
@@ -2,6 +2,5 @@
     "gatekeeper",
     "keymaster",
     "libtrusty",
-    "nvram",
     "storage/*",
 ]
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
index a9566a1..65b271a 100644
--- a/trusty/gatekeeper/Android.bp
+++ b/trusty/gatekeeper/Android.bp
@@ -22,6 +22,7 @@
 
 cc_library_shared {
     name: "gatekeeper.trusty",
+    vendor: true,
 
     relative_install_path: "hw",
 
@@ -43,4 +44,5 @@
         "libcutils",
         "libtrusty",
     ],
+    header_libs: ["libhardware_headers"],
 }
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 7735684..322a63d 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -25,6 +25,7 @@
 // and ECDSA keys.
 cc_binary {
     name: "trusty_keymaster_tipc",
+    vendor: true,
     srcs: [
         "trusty_keymaster_device.cpp",
         "trusty_keymaster_ipc.cpp",
@@ -46,6 +47,7 @@
 // keystore.trusty is the HAL used by keystore on Trusty devices.
 cc_library_shared {
     name: "keystore.trusty",
+    vendor: true,
     relative_install_path: "hw",
     srcs: [
         "module.cpp",
@@ -66,4 +68,5 @@
         "liblog",
         "libcutils",
     ],
+    header_libs: ["libhardware_headers"],
 }
diff --git a/trusty/keymaster/trusty_keymaster_ipc.cpp b/trusty/keymaster/trusty_keymaster_ipc.cpp
index fbd0eb3..686e7ae 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/trusty_keymaster_ipc.cpp
@@ -55,6 +55,11 @@
 
     size_t msg_size = in_size + sizeof(struct keymaster_message);
     struct keymaster_message* msg = reinterpret_cast<struct keymaster_message*>(malloc(msg_size));
+    if (!msg) {
+        ALOGE("failed to allocate msg buffer\n");
+        return -EINVAL;
+    }
+
     msg->cmd = cmd;
     memcpy(msg->payload, in, in_size);
 
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index 1a8db2f..c48deed 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -18,6 +18,7 @@
 
 cc_library {
     name: "libtrusty",
+    vendor: true,
 
     srcs: ["trusty.c"],
     export_include_dirs: ["include"],
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
index 6ec8c23..1e8467f 100644
--- a/trusty/libtrusty/tipc-test/Android.bp
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -14,12 +14,14 @@
 
 cc_test {
     name: "tipc-test",
-    static_executable: true,
+    vendor: true,
 
     srcs: ["tipc_test.c"],
     static_libs: [
-        "libc",
         "libtrusty",
+    ],
+    shared_libs: [
+        "libc",
         "liblog",
     ],
     gtest: false,
diff --git a/trusty/nvram/Android.bp b/trusty/nvram/Android.bp
deleted file mode 100644
index 15e6c3e..0000000
--- a/trusty/nvram/Android.bp
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-// Copyright (C) 2016 The Android Open-Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// nvram.trusty is the Trusty NVRAM HAL module.
-cc_library_shared {
-    name: "nvram.trusty",
-    relative_install_path: "hw",
-    srcs: [
-        "module.c",
-        "trusty_nvram_device.cpp",
-        "trusty_nvram_implementation.cpp",
-    ],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-        "-fvisibility=hidden",
-    ],
-    static_libs: ["libnvram-hal"],
-    shared_libs: [
-        "libtrusty",
-        "libnvram-messages",
-        "liblog",
-    ],
-}
-
-// nvram-wipe is a helper tool for clearing NVRAM state.
-cc_binary {
-    name: "nvram-wipe",
-    srcs: [
-        "nvram_wipe.cpp",
-        "trusty_nvram_implementation.cpp",
-    ],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-        "-fvisibility=hidden",
-    ],
-    static_libs: ["libnvram-hal"],
-    shared_libs: [
-        "libtrusty",
-        "libnvram-messages",
-        "liblog",
-    ],
-}
diff --git a/trusty/nvram/module.c b/trusty/nvram/module.c
deleted file mode 100644
index a2e64d3..0000000
--- a/trusty/nvram/module.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <hardware/nvram.h>
-
-// This function is defined in trusty_nvram_device.cpp.
-int trusty_nvram_open(const hw_module_t* module,
-                      const char* device_id,
-                      hw_device_t** device_ptr);
-
-static struct hw_module_methods_t nvram_module_methods = {
-    .open = trusty_nvram_open,
-};
-
-struct nvram_module HAL_MODULE_INFO_SYM
-    __attribute__((visibility("default"))) = {
-        .common = {.tag = HARDWARE_MODULE_TAG,
-                   .module_api_version = NVRAM_MODULE_API_VERSION_0_1,
-                   .hal_api_version = HARDWARE_HAL_API_VERSION,
-                   .id = NVRAM_HARDWARE_MODULE_ID,
-                   .name = "Trusty NVRAM HAL",
-                   .author = "The Android Open Source Project",
-                   .methods = &nvram_module_methods,
-                   .dso = 0,
-                   .reserved = {}},
-};
diff --git a/trusty/nvram/nvram_wipe.cpp b/trusty/nvram/nvram_wipe.cpp
deleted file mode 100644
index d0f4fad..0000000
--- a/trusty/nvram/nvram_wipe.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <nvram/messages/nvram_messages.h>
-
-#include "trusty_nvram_implementation.h"
-
-void usage(const char* program_name) {
-  fprintf(stderr, "Usage: %s [status|disable|wipe]\n", program_name);
-  exit(-1);
-}
-
-int main(int argc, char* argv[]) {
-  if (argc < 2) {
-    usage(argv[0]);
-  }
-
-  nvram::TrustyNvramImplementation nvram_proxy;
-  nvram::Request request;
-  nvram::Response response;
-
-  if (!strcmp(argv[1], "status")) {
-    request.payload.Activate<nvram::COMMAND_GET_INFO>();
-    nvram_proxy.Execute(request, &response);
-    const nvram::GetInfoResponse* get_info_response =
-        response.payload.get<nvram::COMMAND_GET_INFO>();
-    if (response.result == NV_RESULT_SUCCESS) {
-      int status = get_info_response && get_info_response->wipe_disabled;
-      printf("Wiping disabled: %d\n", status);
-      return status;
-    }
-  } else if (!strcmp(argv[1], "disable")) {
-    request.payload.Activate<nvram::COMMAND_DISABLE_WIPE>();
-    nvram_proxy.Execute(request, &response);
-  } else if (!strcmp(argv[1], "wipe")) {
-    request.payload.Activate<nvram::COMMAND_WIPE_STORAGE>();
-    nvram_proxy.Execute(request, &response);
-  } else {
-    usage(argv[0]);
-  }
-
-  if (response.result != NV_RESULT_SUCCESS) {
-    fprintf(stderr, "Command execution failure: %u\n", response.result);
-    return -1;
-  }
-
-  return 0;
-}
-
diff --git a/trusty/nvram/trusty_nvram_device.cpp b/trusty/nvram/trusty_nvram_device.cpp
deleted file mode 100644
index 82a1228..0000000
--- a/trusty/nvram/trusty_nvram_device.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-
-#include <nvram/hal/nvram_device_adapter.h>
-
-#include "trusty_nvram_implementation.h"
-
-extern "C" int trusty_nvram_open(const hw_module_t* module,
-                                 const char* device_id,
-                                 hw_device_t** device_ptr) {
-  if (strcmp(NVRAM_HARDWARE_DEVICE_ID, device_id) != 0) {
-    return -EINVAL;
-  }
-
-  nvram::NvramDeviceAdapter* adapter = new nvram::NvramDeviceAdapter(
-      module, new nvram::TrustyNvramImplementation);
-  *device_ptr = adapter->as_device();
-  return 0;
-}
diff --git a/trusty/nvram/trusty_nvram_implementation.cpp b/trusty/nvram/trusty_nvram_implementation.cpp
deleted file mode 100644
index 9215c85..0000000
--- a/trusty/nvram/trusty_nvram_implementation.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "TrustyNVRAM"
-
-#include "trusty_nvram_implementation.h"
-
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <hardware/nvram.h>
-#include <log/log.h>
-#include <trusty/tipc.h>
-
-#include <nvram/messages/blob.h>
-
-namespace nvram {
-namespace {
-
-// Character device to open for Trusty IPC connections.
-const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
-
-// App identifier of the NVRAM app.
-const char kTrustyNvramAppId[] = "com.android.trusty.nvram";
-
-}  // namespace
-
-TrustyNvramImplementation::~TrustyNvramImplementation() {
-  if (tipc_nvram_fd_ != -1) {
-    tipc_close(tipc_nvram_fd_);
-    tipc_nvram_fd_ = -1;
-  }
-}
-
-void TrustyNvramImplementation::Execute(const nvram::Request& request,
-                                        nvram::Response* response) {
-  if (!SendRequest(request, response)) {
-    response->result = NV_RESULT_INTERNAL_ERROR;
-  }
-}
-
-bool TrustyNvramImplementation::Connect() {
-  if (tipc_nvram_fd_ != -1) {
-    return true;
-  }
-
-  int rc = tipc_connect(kTrustyDeviceName, kTrustyNvramAppId);
-  if (rc < 0) {
-    ALOGE("Failed to connect to Trusty NVRAM app: %s\n", strerror(-rc));
-    return false;
-  }
-
-  tipc_nvram_fd_ = rc;
-  return true;
-}
-
-bool TrustyNvramImplementation::SendRequest(const nvram::Request& request,
-                                            nvram::Response* response) {
-  if (!Connect()) {
-    return false;
-  }
-
-  nvram::Blob request_buffer;
-  if (!nvram::Encode(request, &request_buffer)) {
-    ALOGE("Failed to encode NVRAM request.\n");
-    return false;
-  }
-
-  ssize_t rc =
-      write(tipc_nvram_fd_, request_buffer.data(), request_buffer.size());
-  if (rc < 0) {
-    ALOGE("Failed to send NVRAM request: %s\n", strerror(-rc));
-    return false;
-  }
-  if (static_cast<size_t>(rc) != request_buffer.size()) {
-    ALOGE("Failed to send full request buffer: %zd\n", rc);
-    return false;
-  }
-
-  rc = read(tipc_nvram_fd_, response_buffer_, sizeof(response_buffer_));
-  if (rc < 0) {
-    ALOGE("Failed to read NVRAM response: %s\n", strerror(-rc));
-    return false;
-  }
-
-  if (static_cast<size_t>(rc) >= sizeof(response_buffer_)) {
-    ALOGE("NVRAM response exceeds response buffer size.\n");
-    return false;
-  }
-
-  if (!nvram::Decode(response_buffer_, static_cast<size_t>(rc), response)) {
-    ALOGE("Failed to decode NVRAM response.\n");
-    return false;
-  }
-
-  return true;
-}
-
-}  // namespace nvram
diff --git a/trusty/nvram/trusty_nvram_implementation.h b/trusty/nvram/trusty_nvram_implementation.h
deleted file mode 100644
index 60758f7..0000000
--- a/trusty/nvram/trusty_nvram_implementation.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TRUSTY_NVRAM_TRUSTY_NVRAM_IMPLEMENTATION_H_
-#define TRUSTY_NVRAM_TRUSTY_NVRAM_IMPLEMENTATION_H_
-
-#include <stdint.h>
-
-#include <nvram/hal/nvram_device_adapter.h>
-#include <nvram/messages/nvram_messages.h>
-
-namespace nvram {
-
-// |TrustyNvramImplementation| proxies requests to the Trusty NVRAM app. It
-// serializes the request objects, sends it to the Trusty app and finally reads
-// back the result and decodes it.
-class TrustyNvramImplementation : public nvram::NvramImplementation {
- public:
-  ~TrustyNvramImplementation() override;
-
-  void Execute(const nvram::Request& request,
-               nvram::Response* response) override;
-
- private:
-  // Connects the IPC channel to the Trusty app if it is not already open.
-  // Returns true if the channel is open, false on errors.
-  bool Connect();
-
-  // Dispatches a command to the trust app. Returns true if successful (note
-  // that the response may still indicate an error on the Trusty side), false if
-  // there are any I/O or encoding/decoding errors.
-  bool SendRequest(const nvram::Request& request,
-                   nvram::Response* response);
-
-  // The file descriptor for the IPC connection to the Trusty app.
-  int tipc_nvram_fd_ = -1;
-
-  // Response buffer. This puts a hard size limit on the responses from the
-  // Trusty app. 4096 matches the maximum IPC message size currently supported
-  // by Trusty.
-  uint8_t response_buffer_[4096];
-};
-
-}  // namespace nvram
-
-#endif  // TRUSTY_NVRAM_TRUSTY_NVRAM_IMPLEMENTATION_H_
diff --git a/trusty/storage/interface/Android.bp b/trusty/storage/interface/Android.bp
index a551c37..18b4a5f 100644
--- a/trusty/storage/interface/Android.bp
+++ b/trusty/storage/interface/Android.bp
@@ -16,5 +16,6 @@
 
 cc_library_static {
     name: "libtrustystorageinterface",
+    vendor: true,
     export_include_dirs: ["include"],
 }
diff --git a/trusty/storage/lib/Android.bp b/trusty/storage/lib/Android.bp
index 5eb3f07..4e41674 100644
--- a/trusty/storage/lib/Android.bp
+++ b/trusty/storage/lib/Android.bp
@@ -16,16 +16,19 @@
 
 cc_library_static {
     name: "libtrustystorage",
+    vendor: true,
 
     srcs: ["storage.c"],
 
     export_include_dirs: ["include"],
 
     static_libs: [
-        "liblog",
         "libtrusty",
         "libtrustystorageinterface",
     ],
+    shared_libs: [
+        "liblog",
+    ],
 
     cflags: [
         "-fvisibility=hidden",
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index eb34df0..da8542d 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -16,6 +16,7 @@
 
 cc_binary {
     name: "storageproxyd",
+    vendor: true,
 
     srcs: [
         "ipc.c",
@@ -25,6 +26,7 @@
     ],
 
     shared_libs: ["liblog"],
+    header_libs: ["libcutils_headers"],
 
     static_libs: [
         "libtrustystorageinterface",
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 27e5891..41263e5 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -24,7 +24,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include <private/android_filesystem_config.h>
+#include <cutils/android_filesystem_config.h>
 
 #include "ipc.h"
 #include "log.h"
diff --git a/trusty/storage/tests/Android.bp b/trusty/storage/tests/Android.bp
index 1e4fced..536c3ca 100644
--- a/trusty/storage/tests/Android.bp
+++ b/trusty/storage/tests/Android.bp
@@ -16,6 +16,7 @@
 
 cc_test {
     name: "secure-storage-unit-test",
+    vendor: true,
 
     cflags: [
         "-g",
@@ -28,6 +29,8 @@
         "libtrustystorageinterface",
         "libtrustystorage",
         "libtrusty",
+    ],
+    shared_libs: [
         "liblog",
     ],