Merge "Move restorecon() of /sys from init to ueventd."
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index f5d0f02..e533a00 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -125,7 +125,7 @@
 
 static int _adb_connect(const std::string& service, std::string* error) {
     D("_adb_connect: %s", service.c_str());
-    if (service.empty() || service.size() > MAX_PAYLOAD_V1) {
+    if (service.empty() || service.size() > MAX_PAYLOAD) {
         *error = android::base::StringPrintf("bad service name length (%zd)",
                                              service.size());
         return -1;
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index ca8729e..38e3116 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -31,7 +31,7 @@
 
 bool SendProtocolString(int fd, const std::string& s) {
     unsigned int length = s.size();
-    if (length > MAX_PAYLOAD_V1 - 4) {
+    if (length > MAX_PAYLOAD - 4) {
         errno = EMSGSIZE;
         return false;
     }
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 20c63b3..11c0ec9 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -74,13 +74,18 @@
 
     template <typename Fn>
     void PopAll(Fn fn) {
-        std::unique_lock<std::mutex> lock(mutex);
-        cv.wait(lock, [this]() { return !queue.empty(); });
+        std::vector<T> popped;
 
-        for (const T& t : queue) {
+        {
+            std::unique_lock<std::mutex> lock(mutex);
+            cv.wait(lock, [this]() { return !queue.empty(); });
+            popped = std::move(queue);
+            queue.clear();
+        }
+
+        for (const T& t : popped) {
             fn(t);
         }
-        queue.clear();
     }
 };
 
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 7e77b5e..e7f44c6 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -180,10 +180,6 @@
     if (port_count < 0) return "";
     return StringPrintf("/dev/bus/usb/%03u/%03u", libusb_get_bus_number(device), ports[0]);
 }
-
-static bool is_device_accessible(libusb_device* device) {
-    return access(get_device_dev_path(device).c_str(), R_OK | W_OK) == 0;
-}
 #endif
 
 static bool endpoint_is_output(uint8_t endpoint) {
@@ -377,9 +373,10 @@
     {
         std::unique_lock<std::mutex> lock(usb_handles_mutex);
         usb_handles[device_address] = std::move(result);
-    }
 
-    register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), writable);
+        register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(),
+                               writable);
+    }
     LOG(INFO) << "registered new usb device '" << device_serial << "'";
 }
 
@@ -388,18 +385,12 @@
 static void device_connected(libusb_device* device) {
 #if defined(__linux__)
     // Android's host linux libusb uses netlink instead of udev for device hotplug notification,
-    // which means we can get hotplug notifications before udev has updated ownership/perms on
-    // the device. Since we're not going to be able to link against the system's libudev any
-    // time soon, hack around this by checking for accessibility in a loop.
+    // which means we can get hotplug notifications before udev has updated ownership/perms on the
+    // device. Since we're not going to be able to link against the system's libudev any time soon,
+    // hack around this by inserting a sleep.
     auto thread = std::thread([device]() {
         std::string device_path = get_device_dev_path(device);
-        auto start = std::chrono::steady_clock::now();
-        while (std::chrono::steady_clock::now() - start < 500ms) {
-            if (is_device_accessible(device)) {
-                break;
-            }
-            std::this_thread::sleep_for(10ms);
-        }
+        std::this_thread::sleep_for(1s);
 
         process_device(device);
         if (--connecting_devices == 0) {
@@ -422,8 +413,10 @@
         if (!it->second->device_handle) {
             // If the handle is null, we were never able to open the device.
             unregister_usb_transport(it->second.get());
+            usb_handles.erase(it);
+        } else {
+            // Closure of the transport will erase the usb_handle.
         }
-        usb_handles.erase(it);
     }
 }
 
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 68ae4af..c9f1ee9 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -599,6 +599,13 @@
     std::string service_string = ShellServiceString(use_shell_protocol,
                                                     type_arg, command);
 
+    // Old devices can't handle a service string that's longer than MAX_PAYLOAD_V1.
+    // Use |use_shell_protocol| to determine whether to allow a command longer than that.
+    if (service_string.size() > MAX_PAYLOAD_V1 && !use_shell_protocol) {
+        fprintf(stderr, "error: shell command too long\n");
+        return 1;
+    }
+
     // Make local stdin raw if the device allocates a PTY, which happens if:
     //   1. We are explicitly asking for a PTY shell, or
     //   2. We don't specify shell type and are starting an interactive session.
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 14ad1ff..e0143c6 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -686,7 +686,7 @@
     }
 
     len = unhex(p->data, 4);
-    if ((len < 1) || (len > MAX_PAYLOAD_V1)) {
+    if ((len < 1) || (len > MAX_PAYLOAD)) {
         D("SS(%d): bad size (%d)", s->id, len);
         goto fail;
     }
diff --git a/adb/test_device.py b/adb/test_device.py
index 737d0c2..9e1a2ec 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -342,6 +342,13 @@
         out = self.device.shell(['echo', 'foo'])[0]
         self.assertEqual(out, 'foo' + self.device.linesep)
 
+    def test_shell_command_length(self):
+        # Devices that have shell_v2 should be able to handle long commands.
+        if self.device.has_shell_protocol():
+            rc, out, err = self.device.shell_nocheck(['echo', 'x' * 16384])
+            self.assertEqual(rc, 0)
+            self.assertTrue(out == ('x' * 16384 + '\n'))
+
     def test_shell_nocheck_failure(self):
         rc, out, _ = self.device.shell_nocheck(['false'])
         self.assertNotEqual(rc, 0)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 308ee8d..2bbbefd 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -603,15 +603,15 @@
 static void transport_unref(atransport* t) {
     CHECK(t != nullptr);
 
-    std::lock_guard<std::mutex> lock(transport_lock);
-    CHECK_GT(t->ref_count, 0u);
-    t->ref_count--;
-    if (t->ref_count == 0) {
+    size_t old_refcount = t->ref_count--;
+    CHECK_GT(old_refcount, 0u);
+
+    if (old_refcount == 1u) {
         D("transport: %s unref (kicking and closing)", t->serial);
         t->close(t);
         remove_transport(t);
     } else {
-        D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
+        D("transport: %s unref (count=%zu)", t->serial, old_refcount - 1);
     }
 }
 
diff --git a/adb/transport.h b/adb/transport.h
index 57fc988..4a89ed9 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -61,7 +61,7 @@
     // class in one go is a very large change. Given how bad our testing is,
     // it's better to do this piece by piece.
 
-    atransport(ConnectionState state = kCsOffline) : connection_state_(state) {
+    atransport(ConnectionState state = kCsOffline) : ref_count(0), connection_state_(state) {
         transport_fde = {};
         protocol_version = A_VERSION;
         max_payload = MAX_PAYLOAD;
@@ -88,7 +88,7 @@
     int fd = -1;
     int transport_socket = -1;
     fdevent transport_fde;
-    size_t ref_count = 0;
+    std::atomic<size_t> ref_count;
     uint32_t sync_token = 0;
     bool online = false;
     TransportType type = kTransportAny;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index a9e583f..6cedd92 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -388,6 +388,25 @@
     D("transport: qemu_socket_thread() exiting");
     return;
 }
+
+// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
+// goldfish) as the transport. This can either be explicitly set by the
+// service.adb.transport property, or be inferred from ro.kernel.qemu that is
+// set to "1" for ranchu/goldfish.
+static bool use_qemu_goldfish() {
+    // Legacy way to detect if adbd should use the goldfish pipe is to check for
+    // ro.kernel.qemu, keep that behaviour for backward compatibility.
+    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+        return true;
+    }
+    // If service.adb.transport is present and is set to "goldfish", use the
+    // QEMUD pipe.
+    if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
+        return true;
+    }
+    return false;
+}
+
 #endif  // !ADB_HOST
 
 void local_init(int port)
@@ -401,13 +420,7 @@
 #else
     // For the adbd daemon in the system image we need to distinguish
     // between the device, and the emulator.
-    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
-        // Running inside the emulator: use QEMUD pipe as the transport.
-        func = qemu_socket_thread;
-    } else {
-        // Running inside the device: use TCP socket as the transport.
-        func = server_socket_thread;
-    }
+    func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread;
     debug_name = "server";
 #endif // !ADB_HOST
 
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 558bc72..df7201d 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -44,6 +44,9 @@
 #include <private/android_filesystem_config.h>
 #include <procinfo/process.h>
 
+#define ATRACE_TAG ATRACE_TAG_BIONIC
+#include <utils/Trace.h>
+
 #include "backtrace.h"
 #include "tombstone.h"
 #include "utility.h"
@@ -101,6 +104,7 @@
 }
 
 static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data) {
+  ATRACE_CALL();
   android::base::unique_fd amfd(socket_local_client(
       "/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
   if (amfd.get() == -1) {
@@ -176,6 +180,7 @@
 }
 
 static void drop_capabilities() {
+  ATRACE_CALL();
   __user_cap_header_struct capheader;
   memset(&capheader, 0, sizeof(capheader));
   capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -194,6 +199,8 @@
 }
 
 int main(int argc, char** argv) {
+  atrace_begin(ATRACE_TAG, "before reparent");
+
   pid_t target = getppid();
   bool tombstoned_connected = false;
   unique_fd tombstoned_socket;
@@ -261,6 +268,8 @@
     PLOG(FATAL) << "parent died";
   }
 
+  atrace_end(ATRACE_TAG);
+
   // Reparent ourselves to init, so that the signal handler can waitpid on the
   // original process to avoid leaving a zombie for non-fatal dumps.
   pid_t forkpid = fork();
@@ -270,47 +279,60 @@
     exit(0);
   }
 
+  ATRACE_NAME("after reparent");
+
   // Die if we take too long.
   alarm(2);
 
   std::string attach_error;
 
-  // Seize the main thread.
-  if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
-    LOG(FATAL) << attach_error;
-  }
-
-  // Seize the siblings.
   std::map<pid_t, std::string> threads;
+
   {
-    std::set<pid_t> siblings;
-    if (!android::procinfo::GetProcessTids(target, &siblings)) {
-      PLOG(FATAL) << "failed to get process siblings";
+    ATRACE_NAME("ptrace");
+    // Seize the main thread.
+    if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
+      LOG(FATAL) << attach_error;
     }
 
-    // but not the already attached main thread.
-    siblings.erase(main_tid);
-    // or the handler pseudothread.
-    siblings.erase(pseudothread_tid);
+    // Seize the siblings.
+    {
+      std::set<pid_t> siblings;
+      if (!android::procinfo::GetProcessTids(target, &siblings)) {
+        PLOG(FATAL) << "failed to get process siblings";
+      }
 
-    for (pid_t sibling_tid : siblings) {
-      if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
-        LOG(WARNING) << attach_error;
-      } else {
-        threads.emplace(sibling_tid, get_thread_name(sibling_tid));
+      // but not the already attached main thread.
+      siblings.erase(main_tid);
+      // or the handler pseudothread.
+      siblings.erase(pseudothread_tid);
+
+      for (pid_t sibling_tid : siblings) {
+        if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
+          LOG(WARNING) << attach_error;
+        } else {
+          threads.emplace(sibling_tid, get_thread_name(sibling_tid));
+        }
       }
     }
   }
 
   // Collect the backtrace map, open files, and process/thread names, while we still have caps.
-  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid));
-  if (!backtrace_map) {
-    LOG(FATAL) << "failed to create backtrace map";
+  std::unique_ptr<BacktraceMap> backtrace_map;
+  {
+    ATRACE_NAME("backtrace map");
+    backtrace_map.reset(BacktraceMap::Create(main_tid));
+    if (!backtrace_map) {
+      LOG(FATAL) << "failed to create backtrace map";
+    }
   }
 
   // Collect the list of open files.
   OpenFilesList open_files;
-  populate_open_files_list(target, &open_files);
+  {
+    ATRACE_NAME("open files");
+    populate_open_files_list(target, &open_files);
+  }
 
   std::string process_name = get_process_name(main_tid);
   threads.emplace(main_tid, get_thread_name(main_tid));
@@ -318,9 +340,12 @@
   // Drop our capabilities now that we've attached to the threads we care about.
   drop_capabilities();
 
-  const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
-  LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
-  tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
+  {
+    ATRACE_NAME("tombstoned_connect");
+    const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
+    LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
+    tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
+  }
 
   // Write a '\1' to stdout to tell the crashing process to resume.
   // It also restores the value of PR_SET_DUMPABLE at this point.
@@ -349,9 +374,12 @@
   }
 
   siginfo_t siginfo = {};
-  if (!wait_for_signal(main_tid, &siginfo)) {
-    printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
-    exit(1);
+  {
+    ATRACE_NAME("wait_for_signal");
+    if (!wait_for_signal(main_tid, &siginfo)) {
+      printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
+      exit(1);
+    }
   }
 
   int signo = siginfo.si_signo;
@@ -373,8 +401,10 @@
 
   std::string amfd_data;
   if (backtrace) {
+    ATRACE_NAME("dump_backtrace");
     dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, process_name, threads, 0);
   } else {
+    ATRACE_NAME("engrave_tombstone");
     engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
                       process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
   }
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 9008b95..4660f3d 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -88,8 +88,12 @@
     }                                                                                       \
   } while (0)
 
+#define ASSERT_BACKTRACE_FRAME(result, frame_name)                        \
+  ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX \
+                       R"(/libc.so \()" frame_name R"(\+)")
+
 static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
-                                 DebuggerdDumpType intercept_type) {
+                                 InterceptStatus* status, DebuggerdDumpType intercept_type) {
   intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
                                           ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
   if (intercept_fd->get() == -1) {
@@ -136,7 +140,7 @@
            << ", received " << rc;
   }
 
-  ASSERT_EQ(InterceptStatus::kRegistered, response.status);
+  *status = response.status;
 }
 
 class CrasherTest : public ::testing::Test {
@@ -180,7 +184,9 @@
     FAIL() << "crasher hasn't been started";
   }
 
-  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, intercept_type);
+  InterceptStatus status;
+  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &status, intercept_type);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
 }
 
 void CrasherTest::FinishIntercept(int* result) {
@@ -305,7 +311,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_BACKTRACE_FRAME(result, "tgkill");
 }
 
 TEST_F(CrasherTest, signal) {
@@ -441,7 +447,7 @@
   FinishIntercept(&intercept_result);
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+  /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
+  ASSERT_BACKTRACE_FRAME(result, "read");
 
   int status;
   ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
@@ -452,7 +458,7 @@
   FinishIntercept(&intercept_result);
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_BACKTRACE_FRAME(result, "tgkill");
 }
 
 TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
@@ -472,7 +478,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_BACKTRACE_FRAME(result, "tgkill");
 }
 
 TEST_F(CrasherTest, capabilities) {
@@ -529,7 +535,7 @@
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
   ASSERT_MATCH(result, R"(name: thread_name\s+>>> .+debuggerd_test(32|64) <<<)");
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_BACKTRACE_FRAME(result, "tgkill");
 }
 
 TEST_F(CrasherTest, fake_pid) {
@@ -560,7 +566,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_BACKTRACE_FRAME(result, "tgkill");
 }
 
 TEST(crash_dump, zombie) {
@@ -598,7 +604,9 @@
     pid_t pid = 123'456'789 + i;
 
     unique_fd intercept_fd, output_fd;
-    tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
+    InterceptStatus status;
+    tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+    ASSERT_EQ(InterceptStatus::kRegistered, status);
 
     {
       unique_fd tombstoned_socket, input_fd;
@@ -630,7 +638,9 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
+      InterceptStatus status;
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+      ASSERT_EQ(InterceptStatus::kRegistered, status);
 
       // Pretend to crash, and then immediately close the socket.
       unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
@@ -661,7 +671,9 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
+      InterceptStatus status;
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+      ASSERT_EQ(InterceptStatus::kRegistered, status);
 
       {
         unique_fd tombstoned_socket, input_fd;
@@ -685,3 +697,65 @@
     thread.join();
   }
 }
+
+TEST(tombstoned, java_trace_intercept_smoke) {
+  // Using a "real" PID is a little dangerous here - if the test fails
+  // or crashes, we might end up getting a bogus / unreliable stack
+  // trace.
+  const pid_t self = getpid();
+
+  unique_fd intercept_fd, output_fd;
+  InterceptStatus status;
+  tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+  // First connect to tombstoned requesting a native backtrace. This
+  // should result in a "regular" FD and not the installed intercept.
+  const char native[] = "native";
+  unique_fd tombstoned_socket, input_fd;
+  ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdNativeBacktrace));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), native, sizeof(native)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  // Then, connect to tombstoned asking for a java backtrace. This *should*
+  // trigger the intercept.
+  const char java[] = "java";
+  ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdJavaBacktrace));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), java, sizeof(java)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  char outbuf[sizeof(java)];
+  ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+  ASSERT_STREQ("java", outbuf);
+}
+
+TEST(tombstoned, multiple_intercepts) {
+  const pid_t fake_pid = 1'234'567;
+  unique_fd intercept_fd, output_fd;
+  InterceptStatus status;
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+  unique_fd intercept_fd_2, output_fd_2;
+  tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &status, kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, status);
+}
+
+TEST(tombstoned, intercept_any) {
+  const pid_t fake_pid = 1'234'567;
+
+  unique_fd intercept_fd, output_fd;
+  InterceptStatus status;
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+  const char any[] = "any";
+  unique_fd tombstoned_socket, input_fd;
+  ASSERT_TRUE(tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdAnyIntercept));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), any, sizeof(any)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  char outbuf[sizeof(any)];
+  ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+  ASSERT_STREQ("any", outbuf);
+}
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 0a9f000..b920ebc 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -23,7 +23,9 @@
 
 #include <array>
 #include <deque>
+#include <string>
 #include <unordered_map>
+#include <utility>
 
 #include <event2/event.h>
 #include <event2/listener.h>
@@ -75,23 +77,24 @@
     find_oldest_artifact();
   }
 
-  unique_fd get_output_fd() {
+  std::pair<unique_fd, std::string> get_output() {
     unique_fd result;
-    char buf[PATH_MAX];
-    snprintf(buf, sizeof(buf), "%s%02d", file_name_prefix_.c_str(), next_artifact_);
+    std::string file_name = StringPrintf("%s%02d", file_name_prefix_.c_str(), next_artifact_);
+
     // Unlink and create the file, instead of using O_TRUNC, to avoid two processes
     // interleaving their output in case we ever get into that situation.
-    if (unlinkat(dir_fd_, buf, 0) != 0 && errno != ENOENT) {
-      PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << buf;
+    if (unlinkat(dir_fd_, file_name.c_str(), 0) != 0 && errno != ENOENT) {
+      PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << "/" << file_name;
     }
 
-    result.reset(openat(dir_fd_, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
+    result.reset(openat(dir_fd_, file_name.c_str(),
+                        O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
     if (result == -1) {
-      PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << buf;
+      PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << "/" << file_name;
     }
 
     next_artifact_ = (next_artifact_ + 1) % max_artifacts_;
-    return result;
+    return {std::move(result), dir_path_ + "/" + file_name};
   }
 
   bool maybe_enqueue_crash(Crash* crash) {
@@ -124,8 +127,7 @@
     time_t oldest_time = std::numeric_limits<time_t>::max();
 
     for (size_t i = 0; i < max_artifacts_; ++i) {
-      std::string path = android::base::StringPrintf("%s/%s%02zu", dir_path_.c_str(),
-                                                     file_name_prefix_.c_str(), i);
+      std::string path = StringPrintf("%s/%s%02zu", dir_path_.c_str(), file_name_prefix_.c_str(), i);
       struct stat st;
       if (stat(path.c_str(), &st) != 0) {
         if (errno == ENOENT) {
@@ -163,7 +165,7 @@
 };
 
 // Whether java trace dumps are produced via tombstoned.
-static constexpr bool kJavaTraceDumpsEnabled = false;
+static constexpr bool kJavaTraceDumpsEnabled = true;
 
 /* static */ CrashQueue* const CrashQueue::tombstone =
     new CrashQueue("/data/tombstones", "tombstone_" /* file_name_prefix */, 10 /* max_artifacts */,
@@ -183,6 +185,7 @@
   unique_fd crash_fd;
   pid_t crash_pid;
   event* crash_event = nullptr;
+  std::string crash_path;
 
   DebuggerdDumpType crash_type;
 };
@@ -203,7 +206,7 @@
 static void perform_request(Crash* crash) {
   unique_fd output_fd;
   if (!intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd)) {
-    output_fd = get_crash_queue(crash)->get_output_fd();
+    std::tie(output_fd, crash->crash_path) = get_crash_queue(crash)->get_output();
   }
 
   TombstonedCrashPacket response = {
@@ -341,6 +344,10 @@
     goto fail;
   }
 
+  if (!crash->crash_path.empty()) {
+    LOG(ERROR) << "Tombstone written to: " << crash->crash_path;
+  }
+
 fail:
   CrashQueue* queue = get_crash_queue(crash);
   delete crash;
diff --git a/demangle/Android.bp b/demangle/Android.bp
index 96ab57d..e55c886 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -29,6 +29,7 @@
 cc_library {
     name: "libdemangle",
     defaults: ["libdemangle_defaults"],
+    vendor_available: true,
 
     srcs: [
         "Demangler.cpp",
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 70cd608..7a41b14 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -337,16 +337,8 @@
 
     dirent* dp;
     while ((dp = readdir(fstabdir.get())) != NULL) {
-        // skip over name and compatible
-        if (dp->d_type != DT_DIR) {
-            continue;
-        }
-
-        // skip if its not 'vendor', 'odm' or 'system'
-        if (strcmp(dp->d_name, "odm") && strcmp(dp->d_name, "system") &&
-            strcmp(dp->d_name, "vendor")) {
-            continue;
-        }
+        // skip over name, compatible and .
+        if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
 
         // create <dev> <mnt_point>  <type>  <mnt_flags>  <fsmgr_flags>\n
         std::vector<std::string> fstab_entry;
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5fa10bc..0177fdf 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -689,27 +689,55 @@
     return read_verity_state(fstab->verity_loc, offset, mode);
 }
 
-static void update_verity_table_blk_device(char *blk_device, char **table)
-{
-    std::string result, word;
+// Update the verity table using the actual block device path.
+// Two cases:
+// Case-1: verity table is shared for devices with different by-name prefix.
+// Example:
+//   verity table token:       /dev/block/bootdevice/by-name/vendor
+//   blk_device-1 (non-A/B):   /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+//   blk_device-2 (A/B):       /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor_a
+//
+// Case-2: append A/B suffix in the verity table.
+// Example:
+//   verity table token: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+//   blk_device:         /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor_a
+static void update_verity_table_blk_device(const std::string& blk_device, char** table,
+                                           bool slot_select) {
+    bool updated = false;
+    std::string result, ab_suffix;
     auto tokens = android::base::Split(*table, " ");
 
+    // If slot_select is set, it means blk_device is already updated with ab_suffix.
+    if (slot_select) ab_suffix = fs_mgr_get_slot_suffix();
+
     for (const auto& token : tokens) {
-        if (android::base::StartsWith(token, "/dev/block/") &&
-            android::base::StartsWith(blk_device, token.c_str())) {
-            word = blk_device;
+        std::string new_token;
+        if (android::base::StartsWith(token, "/dev/block/")) {
+            if (token == blk_device) return;  // no need to update if they're already the same.
+            std::size_t found1 = blk_device.find("by-name");
+            std::size_t found2 = token.find("by-name");
+            if (found1 != std::string::npos && found2 != std::string::npos &&
+                blk_device.substr(found1) == token.substr(found2) + ab_suffix) {
+                new_token = blk_device;
+            }
+        }
+
+        if (!new_token.empty()) {
+            updated = true;
+            LINFO << "Verity table: updated block device from '" << token << "' to '" << new_token
+                  << "'";
         } else {
-            word = token;
+            new_token = token;
         }
 
         if (result.empty()) {
-            result = word;
+            result = new_token;
         } else {
-            result += " " + word;
+            result += " " + new_token;
         }
     }
 
-    if (result.empty()) {
+    if (!updated) {
         return;
     }
 
@@ -825,10 +853,9 @@
     LINFO << "Enabling dm-verity for " << mount_point.c_str()
           << " (mode " << params.mode << ")";
 
-    if (fstab->fs_mgr_flags & MF_SLOTSELECT) {
-        // Update the verity params using the actual block device path
-        update_verity_table_blk_device(fstab->blk_device, &params.table);
-    }
+    // Update the verity params using the actual block device path
+    update_verity_table_blk_device(fstab->blk_device, &params.table,
+                                   fstab->fs_mgr_flags & MF_SLOTSELECT);
 
     // load the verity mapping table
     if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
index 31fc2df..ece8693 100644
--- a/include/ziparchive/zip_archive.h
+++ b/include/ziparchive/zip_archive.h
@@ -71,8 +71,17 @@
   // Modification time. The zipfile format specifies
   // that the first two little endian bytes contain the time
   // and the last two little endian bytes contain the date.
+  // See `GetModificationTime`.
+  // TODO: should be overridden by extra time field, if present.
   uint32_t mod_time;
 
+  // Returns `mod_time` as a broken-down struct tm.
+  struct tm GetModificationTime() const;
+
+  // Suggested Unix mode for this entry, from the zip archive if created on
+  // Unix, or a default otherwise.
+  mode_t unix_mode;
+
   // 1 if this entry contains a data descriptor segment, 0
   // otherwise.
   uint8_t has_data_descriptor;
diff --git a/init/Android.bp b/init/Android.bp
index af1e9d3..fce424e 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -20,7 +20,6 @@
     sanitize: {
         misc_undefined: ["integer"],
     },
-    tidy_checks: ["-misc-forwarding-reference-overload"],
     cppflags: [
         "-DLOG_UEVENTS=0",
         "-Wall",
diff --git a/init/Android.mk b/init/Android.mk
index 489d076..6cd47f4 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -40,8 +40,6 @@
 # --
 
 include $(CLEAR_VARS)
-# b/38002385, work around clang-tidy segmentation fault.
-LOCAL_TIDY_CHECKS := -misc-forwarding-reference-overload
 LOCAL_CPPFLAGS := $(init_cflags)
 LOCAL_SRC_FILES:= \
     bootchart.cpp \
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 8a7d9a2..0f2b1f3 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -60,9 +60,8 @@
     virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
 
     bool need_dm_verity_;
-    // Device tree fstab entries.
+
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
-    // Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
     std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
     DeviceHandler device_handler_;
@@ -115,12 +114,10 @@
         LOG(ERROR) << "Failed to read fstab from device tree";
         return;
     }
-    for (auto mount_point : {"/system", "/vendor", "/odm"}) {
-        fstab_rec* fstab_rec =
-            fs_mgr_get_entry_for_mount_point(device_tree_fstab_.get(), mount_point);
-        if (fstab_rec != nullptr) {
-            mount_fstab_recs_.push_back(fstab_rec);
-        }
+    // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
+    // for easier manipulation later, e.g., range-base for loop.
+    for (int i = 0; i < device_tree_fstab_->num_entries; i++) {
+        mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]);
     }
 }
 
@@ -420,7 +417,7 @@
 
 // Public functions
 // ----------------
-// Mounts /system, /vendor, and/or /odm if they are present in the fstab provided by device tree.
+// Mounts partitions specified by fstab in device tree.
 bool DoFirstStageMount() {
     // Skips first stage mount if we're in recovery mode.
     if (IsRecoveryMode()) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 3490544..b43da4e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,6 +39,7 @@
 #include <sys/_system_properties.h>
 
 #include <memory>
+#include <queue>
 #include <vector>
 
 #include <android-base/file.h>
@@ -162,7 +163,7 @@
     return true;
 }
 
-uint32_t property_set(const std::string& name, const std::string& value) {
+static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
     size_t valuelen = value.size();
 
     if (!is_legal_property_name(name)) {
@@ -176,12 +177,6 @@
         return PROP_ERROR_INVALID_VALUE;
     }
 
-    if (name == "selinux.restorecon_recursive" && valuelen > 0) {
-        if (selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
-            LOG(ERROR) << "Failed to restorecon_recursive " << value;
-        }
-    }
-
     prop_info* pi = (prop_info*) __system_property_find(name.c_str());
     if (pi != nullptr) {
         // ro.* properties are actually "write-once".
@@ -210,6 +205,85 @@
     return PROP_SUCCESS;
 }
 
+typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+
+struct PropertyChildInfo {
+    pid_t pid;
+    PropertyAsyncFunc func;
+    std::string name;
+    std::string value;
+};
+
+static std::queue<PropertyChildInfo> property_children;
+
+static void PropertyChildLaunch() {
+    auto& info = property_children.front();
+    pid_t pid = fork();
+    if (pid < 0) {
+        LOG(ERROR) << "Failed to fork for property_set_async";
+        while (!property_children.empty()) {
+            property_children.pop();
+        }
+        return;
+    }
+    if (pid != 0) {
+        info.pid = pid;
+    } else {
+        if (info.func(info.name, info.value) != 0) {
+            LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
+                       << "\") failed";
+        }
+        exit(0);
+    }
+}
+
+bool PropertyChildReap(pid_t pid) {
+    if (property_children.empty()) {
+        return false;
+    }
+    auto& info = property_children.front();
+    if (info.pid != pid) {
+        return false;
+    }
+    if (PropertySetImpl(info.name, info.value) != PROP_SUCCESS) {
+        LOG(ERROR) << "Failed to set async property " << info.name;
+    }
+    property_children.pop();
+    if (!property_children.empty()) {
+        PropertyChildLaunch();
+    }
+    return true;
+}
+
+static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
+                                 PropertyAsyncFunc func) {
+    if (value.empty()) {
+        return PropertySetImpl(name, value);
+    }
+
+    PropertyChildInfo info;
+    info.func = func;
+    info.name = name;
+    info.value = value;
+    property_children.push(info);
+    if (property_children.size() == 1) {
+        PropertyChildLaunch();
+    }
+    return PROP_SUCCESS;
+}
+
+static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
+    return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+}
+
+uint32_t property_set(const std::string& name, const std::string& value) {
+    if (name == "selinux.restorecon_recursive") {
+        return PropertySetAsync(name, value, RestoreconRecursiveAsync);
+    }
+
+    return PropertySetImpl(name, value);
+}
+
 class SocketConnection {
  public:
   SocketConnection(int socket, const struct ucred& cred)
diff --git a/init/property_service.h b/init/property_service.h
index 9a5b6f6..9251722 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -26,6 +26,8 @@
     const char* name;
 };
 
+extern bool PropertyChildReap(pid_t pid);
+
 void property_init(void);
 void property_load_boot_defaults(void);
 void load_persist_props(void);
diff --git a/init/service.cpp b/init/service.cpp
index 7c931da..b73ddfb 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -209,17 +209,6 @@
 }
 
 void Service::KillProcessGroup(int signal) {
-    // We ignore reporting errors of ESRCH as this commonly happens in the below case,
-    // 1) Terminate() is called, which sends SIGTERM to the process
-    // 2) The process successfully exits
-    // 3) ReapOneProcess() is called, which calls waitpid(-1, ...) which removes the pid entry.
-    // 4) Reap() is called, which sends SIGKILL, but the pid no longer exists.
-    // TODO: sigaction for SIGCHLD reports the pid of the exiting process,
-    // we should do this kill with that pid first before calling waitpid().
-    if (kill(-pid_, signal) == -1 && errno != ESRCH) {
-        PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
-    }
-
     // If we've already seen a successful result from killProcessGroup*(), then we have removed
     // the cgroup already and calling these functions a second time will simply result in an error.
     // This is true regardless of which signal was sent.
@@ -1096,6 +1085,8 @@
     } else if (pid == -1) {
         PLOG(ERROR) << "waitpid failed";
         return false;
+    } else if (PropertyChildReap(pid)) {
+        return true;
     }
 
     Service* svc = FindServiceByPid(pid);
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index e29a844..7e38535 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -340,7 +340,7 @@
     return 0;
 }
 
-static void set_timerslack_ns(int tid, unsigned long long slack) {
+static void set_timerslack_ns(int tid, unsigned long slack) {
     // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
     // TODO: once we've backported this, log if the open(2) fails.
     if (__sys_supports_timerslack) {
@@ -348,7 +348,7 @@
         snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
         int fd = open(buf, O_WRONLY | O_CLOEXEC);
         if (fd != -1) {
-            int len = snprintf(buf, sizeof(buf), "%llu", slack);
+            int len = snprintf(buf, sizeof(buf), "%lu", slack);
             if (write(fd, buf, len) != len) {
                 SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
             }
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index e7c0beb..1c84744 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -502,7 +502,10 @@
 std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
   UnreachableMemoryInfo info;
   if (!GetUnreachableMemory(info, limit)) {
-    return "Failed to get unreachable memory\n";
+    return "Failed to get unreachable memory\n"
+           "If you are trying to get unreachable memory from a system app\n"
+           "(like com.android.systemui), disable selinux first using\n"
+           "setenforce 0\n";
   }
 
   return info.ToString(log_contents);
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
index f28d726..1856e5c 100644
--- a/libpixelflinger/codeflinger/mips64_disassem.c
+++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -555,6 +555,7 @@
     } else {
         vprintf(fmt, argp);
     }
+    va_end(argp);
 }
 
 /*
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
index 3007b15..83a9740 100644
--- a/libpixelflinger/codeflinger/mips_disassem.c
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -562,6 +562,7 @@
     } else {
         vprintf(fmt, argp);
     }
+    va_end(argp);
 }
 
 
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 27b4065..f5d4e1c 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -27,10 +27,12 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <chrono>
 #include <memory>
 #include <mutex>
+#include <set>
 #include <thread>
 
 #include <android-base/logging.h>
@@ -258,6 +260,12 @@
         return -errno;
     }
 
+    // We separate all of the pids in the cgroup into those pids that are also the leaders of
+    // process groups (stored in the pgids set) and those that are not (stored in the pids set).
+    std::set<pid_t> pgids;
+    pgids.emplace(initialPid);
+    std::set<pid_t> pids;
+
     int ret;
     pid_t pid;
     int processes = 0;
@@ -269,8 +277,40 @@
             LOG(WARNING) << "Yikes, we've been told to kill pid 0!  How about we don't do that?";
             continue;
         }
+        pid_t pgid = getpgid(pid);
+        if (pgid == -1) PLOG(ERROR) << "getpgid(" << pid << ") failed";
+        if (pgid == pid) {
+            pgids.emplace(pid);
+        } else {
+            pids.emplace(pid);
+        }
+    }
+
+    // Erase all pids that will be killed when we kill the process groups.
+    for (auto it = pids.begin(); it != pids.end();) {
+        pid_t pgid = getpgid(pid);
+        if (pgids.count(pgid) == 1) {
+            it = pids.erase(it);
+        } else {
+            ++it;
+        }
+    }
+
+    // Kill all process groups.
+    for (const auto pgid : pgids) {
+        LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
+                     << " as part of process cgroup " << initialPid;
+
+        if (kill(-pgid, signal) == -1) {
+            PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
+        }
+    }
+
+    // Kill remaining pids.
+    for (const auto pid : pids) {
         LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
                      << initialPid;
+
         if (kill(pid, signal) == -1) {
             PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
         }
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index eb2b902..d7e949b 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -48,6 +48,8 @@
     srcs: [
         "ArmExidx.cpp",
         "DwarfCfa.cpp",
+        "DwarfDebugFrame.cpp",
+        "DwarfEhFrame.cpp",
         "DwarfMemory.cpp",
         "DwarfOp.cpp",
         "DwarfSection.cpp",
@@ -96,6 +98,8 @@
         "tests/ArmExidxExtractTest.cpp",
         "tests/DwarfCfaLogTest.cpp",
         "tests/DwarfCfaTest.cpp",
+        "tests/DwarfDebugFrameTest.cpp",
+        "tests/DwarfEhFrameTest.cpp",
         "tests/DwarfMemoryTest.cpp",
         "tests/DwarfOpLogTest.cpp",
         "tests/DwarfOpTest.cpp",
diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp
new file mode 100644
index 0000000..3ac02fc
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.cpp
@@ -0,0 +1,311 @@
+/*
+ * 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 "DwarfDebugFrame.h"
+#include "DwarfMemory.h"
+#include "DwarfSection.h"
+#include "DwarfStructs.h"
+#include "Memory.h"
+
+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>;
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
new file mode 100644
index 0000000..320368c
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -0,0 +1,76 @@
+/*
+ * 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_DWARF_DEBUG_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "DwarfSection.h"
+
+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) {}
+  virtual ~DwarfDebugFrame() = 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 == 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 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_;
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrame.cpp
new file mode 100644
index 0000000..045fb36
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "DwarfEhFrame.h"
+#include "DwarfMemory.h"
+#include "DwarfSection.h"
+#include "DwarfStructs.h"
+#include "Memory.h"
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+  uint8_t data[4];
+
+  memory_.clear_func_offset();
+  memory_.clear_text_offset();
+  memory_.set_data_offset(offset);
+  memory_.set_cur_offset(offset);
+
+  // Read the first four bytes all at once.
+  if (!memory_.ReadBytes(data, 4)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  version_ = data[0];
+  if (version_ != 1) {
+    // Unknown version.
+    last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+    return false;
+  }
+
+  ptr_encoding_ = data[1];
+  uint8_t fde_count_encoding = data[2];
+  table_encoding_ = data[3];
+  table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
+
+  memory_.set_pc_offset(memory_.cur_offset());
+  if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  memory_.set_pc_offset(memory_.cur_offset());
+  if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  entries_offset_ = memory_.cur_offset();
+  entries_end_ = offset + size;
+  entries_data_offset_ = offset;
+  cur_entries_offset_ = entries_offset_;
+
+  return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfEhFrame<AddressType>::GetFdeFromIndex(size_t index) {
+  const FdeInfo* info = GetFdeInfoFromIndex(index);
+  if (info == nullptr) {
+    return nullptr;
+  }
+  return this->GetFdeFromOffset(info->offset);
+}
+
+template <typename AddressType>
+const typename DwarfEhFrame<AddressType>::FdeInfo* DwarfEhFrame<AddressType>::GetFdeInfoFromIndex(
+    size_t index) {
+  auto entry = fde_info_.find(index);
+  if (entry != fde_info_.end()) {
+    return &fde_info_[index];
+  }
+  FdeInfo* info = &fde_info_[index];
+
+  memory_.set_data_offset(entries_data_offset_);
+  memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
+  memory_.set_pc_offset(memory_.cur_offset());
+  uint64_t value;
+  if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+      !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    fde_info_.erase(index);
+    return nullptr;
+  }
+  info->pc = value;
+  return info;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
+                                                   uint64_t total_entries) {
+  assert(fde_count_ > 0);
+  assert(total_entries <= fde_count_);
+
+  size_t first = 0;
+  size_t last = total_entries;
+  while (first < last) {
+    size_t current = (first + last) / 2;
+    const FdeInfo* info = GetFdeInfoFromIndex(current);
+    if (pc == info->pc) {
+      *fde_offset = info->offset;
+      return true;
+    }
+    if (pc < info->pc) {
+      last = current;
+    } else {
+      first = current + 1;
+    }
+  }
+  if (last != 0) {
+    const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
+    *fde_offset = info->offset;
+    return true;
+  }
+  return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
+  assert(fde_count_ != 0);
+  last_error_ = DWARF_ERROR_NONE;
+  // We can do a binary search if the pc is in the range of the elements
+  // that have already been cached.
+  if (!fde_info_.empty()) {
+    const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
+    if (pc >= info->pc) {
+      *fde_offset = info->offset;
+      return true;
+    }
+    if (pc < info->pc) {
+      return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
+    }
+  }
+
+  if (cur_entries_offset_ == 0) {
+    // All entries read, or error encountered.
+    return false;
+  }
+
+  memory_.set_data_offset(entries_data_offset_);
+  memory_.set_cur_offset(cur_entries_offset_);
+  cur_entries_offset_ = 0;
+
+  FdeInfo* prev_info = nullptr;
+  for (size_t current = fde_info_.size();
+       current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
+    memory_.set_pc_offset(memory_.cur_offset());
+    uint64_t value;
+    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+
+    FdeInfo* info = &fde_info_[current];
+    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+      fde_info_.erase(current);
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    info->pc = value;
+
+    if (pc < info->pc) {
+      if (prev_info == nullptr) {
+        return false;
+      }
+      cur_entries_offset_ = memory_.cur_offset();
+      *fde_offset = prev_info->offset;
+      return true;
+    }
+    prev_info = info;
+  }
+
+  if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
+    *fde_offset = prev_info->offset;
+    return true;
+  }
+  return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+  if (fde_count_ == 0) {
+    return false;
+  }
+
+  if (table_entry_size_ > 0) {
+    // Do a binary search since the size of each table entry is fixed.
+    return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
+  } else {
+    // Do a sequential search since each table entry size is variable.
+    return GetFdeOffsetSequential(pc, fde_offset);
+  }
+}
+
+// Explicitly instantiate DwarfEhFrame.
+template class DwarfEhFrame<uint32_t>;
+template class DwarfEhFrame<uint64_t>;
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
new file mode 100644
index 0000000..e6909ed
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.h
@@ -0,0 +1,89 @@
+/*
+ * 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_H
+#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+
+#include <stdint.h>
+
+#include "DwarfSection.h"
+
+// 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;
+  }
+
+  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+    return memory_.cur_offset() - pointer - 8;
+  }
+
+  uint64_t AdjustPcFromFde(uint64_t pc) override {
+    // The eh_frame uses relative pcs.
+    return pc + memory_.cur_offset();
+  }
+
+  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_;
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 087457c..bfa7944 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -20,10 +20,33 @@
 #include <memory>
 #include <string>
 
+#include "DwarfDebugFrame.h"
+#include "DwarfEhFrame.h"
 #include "ElfInterface.h"
 #include "Memory.h"
 #include "Regs.h"
 
+template <typename AddressType>
+void ElfInterface::InitHeadersWithTemplate() {
+  if (eh_frame_offset_ != 0) {
+    eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
+    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) {
+      eh_frame_.reset(nullptr);
+      eh_frame_offset_ = 0;
+      eh_frame_size_ = static_cast<uint64_t>(-1);
+    }
+  }
+
+  if (debug_frame_offset_ != 0) {
+    debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
+    if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) {
+      debug_frame_.reset(nullptr);
+      debug_frame_offset_ = 0;
+      debug_frame_size_ = static_cast<uint64_t>(-1);
+    }
+  }
+}
+
 template <typename EhdrType, typename PhdrType, typename ShdrType>
 bool ElfInterface::ReadAllHeaders() {
   EhdrType ehdr;
@@ -210,6 +233,9 @@
 }
 
 // Instantiate all of the needed template functions.
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
+
 template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
 template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
 
diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/ElfInterface.h
index 944146c..1cc8aa0 100644
--- a/libunwindstack/ElfInterface.h
+++ b/libunwindstack/ElfInterface.h
@@ -25,6 +25,8 @@
 #include <unordered_map>
 #include <vector>
 
+#include "DwarfSection.h"
+
 // Forward declarations.
 class Memory;
 class Regs;
@@ -68,10 +70,18 @@
   uint64_t dynamic_size() { return dynamic_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_; }
+  uint64_t debug_frame_size() { return debug_frame_size_; }
   uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; }
   uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; }
 
+  DwarfSection* eh_frame() { return eh_frame_.get(); }
+  DwarfSection* debug_frame() { return debug_frame_.get(); }
+
  protected:
+  template <typename AddressType>
+  void InitHeadersWithTemplate();
+
   template <typename EhdrType, typename PhdrType, typename ShdrType>
   bool ReadAllHeaders();
 
@@ -105,6 +115,9 @@
 
   uint8_t soname_type_ = SONAME_UNKNOWN;
   std::string soname_;
+
+  std::unique_ptr<DwarfSection> eh_frame_;
+  std::unique_ptr<DwarfSection> debug_frame_;
 };
 
 class ElfInterface32 : public ElfInterface {
@@ -116,8 +129,7 @@
     return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
   }
 
-  void InitHeaders() override {
-  }
+  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
 
   bool GetSoname(std::string* soname) override {
     return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
@@ -137,8 +149,7 @@
     return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
   }
 
-  void InitHeaders() override {
-  }
+  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
 
   bool GetSoname(std::string* soname) override {
     return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
new file mode 100644
index 0000000..94a9974
--- /dev/null
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -0,0 +1,456 @@
+/*
+ * 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 "DwarfDebugFrame.h"
+#include "DwarfEncoding.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+template <typename TypeParam>
+class MockDwarfDebugFrame : public DwarfDebugFrame<TypeParam> {
+ public:
+  MockDwarfDebugFrame(Memory* memory) : DwarfDebugFrame<TypeParam>(memory) {}
+  ~MockDwarfDebugFrame() = default;
+
+  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+  void TestSetOffset(uint64_t offset) { this->offset_ = offset; }
+  void TestSetEndOffset(uint64_t offset) { this->end_offset_ = offset; }
+  void TestPushFdeInfo(const typename DwarfDebugFrame<TypeParam>::FdeInfo& info) {
+    this->fdes_.push_back(info);
+  }
+
+  uint64_t TestGetFdeCount() { return this->fde_count_; }
+  uint8_t TestGetOffset() { return this->offset_; }
+  uint8_t TestGetEndOffset() { return this->end_offset_; }
+  void TestGetFdeInfo(size_t index, typename DwarfDebugFrame<TypeParam>::FdeInfo* info) {
+    *info = this->fdes_[index];
+  }
+};
+
+template <typename TypeParam>
+class DwarfDebugFrameTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    debug_frame_ = new MockDwarfDebugFrame<TypeParam>(&memory_);
+    ResetLogs();
+  }
+
+  void TearDown() override { delete debug_frame_; }
+
+  MemoryFake memory_;
+  MockDwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfDebugFrameTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init32) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 1);
+  this->memory_.SetData8(0x5009, '\0');
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0);
+  this->memory_.SetData32(0x5108, 0x1500);
+  this->memory_.SetData32(0x510c, 0x200);
+
+  this->memory_.SetData32(0x5200, 0xfc);
+  this->memory_.SetData32(0x5204, 0);
+  this->memory_.SetData32(0x5208, 0x2500);
+  this->memory_.SetData32(0x520c, 0x300);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x5300, 0xfc);
+  this->memory_.SetData32(0x5304, 0xffffffff);
+  this->memory_.SetData8(0x5308, 1);
+  this->memory_.SetData8(0x5309, '\0');
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5400, 0xfc);
+  this->memory_.SetData32(0x5404, 0x300);
+  this->memory_.SetData32(0x5408, 0x3500);
+  this->memory_.SetData32(0x540c, 0x400);
+
+  this->memory_.SetData32(0x5500, 0xfc);
+  this->memory_.SetData32(0x5504, 0x300);
+  this->memory_.SetData32(0x5508, 0x4500);
+  this->memory_.SetData32(0x550c, 0x500);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
+
+  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+  this->debug_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x1500U, info.start);
+  EXPECT_EQ(0x1700U, info.end);
+
+  this->debug_frame_->TestGetFdeInfo(1, &info);
+  EXPECT_EQ(0x5200U, info.offset);
+  EXPECT_EQ(0x2500U, info.start);
+  EXPECT_EQ(0x2800U, info.end);
+
+  this->debug_frame_->TestGetFdeInfo(2, &info);
+  EXPECT_EQ(0x5400U, info.offset);
+  EXPECT_EQ(0x3500U, info.start);
+  EXPECT_EQ(0x3900U, info.end);
+
+  this->debug_frame_->TestGetFdeInfo(3, &info);
+  EXPECT_EQ(0x5500U, info.offset);
+  EXPECT_EQ(0x4500U, info.start);
+  EXPECT_EQ(0x4a00U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init32_fde_not_following_cie) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 1);
+  this->memory_.SetData8(0x5009, '\0');
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0x1000);
+  this->memory_.SetData32(0x5108, 0x1500);
+  this->memory_.SetData32(0x510c, 0x200);
+
+  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init64) {
+  // CIE 64 information.
+  this->memory_.SetData32(0x5000, 0xffffffff);
+  this->memory_.SetData64(0x5004, 0xf4);
+  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
+  this->memory_.SetData8(0x5014, 1);
+  this->memory_.SetData8(0x5015, '\0');
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x5100, 0xffffffff);
+  this->memory_.SetData64(0x5104, 0xf4);
+  this->memory_.SetData64(0x510c, 0);
+  this->memory_.SetData64(0x5114, 0x1500);
+  this->memory_.SetData64(0x511c, 0x200);
+
+  this->memory_.SetData32(0x5200, 0xffffffff);
+  this->memory_.SetData64(0x5204, 0xf4);
+  this->memory_.SetData64(0x520c, 0);
+  this->memory_.SetData64(0x5214, 0x2500);
+  this->memory_.SetData64(0x521c, 0x300);
+
+  // CIE 64 information.
+  this->memory_.SetData32(0x5300, 0xffffffff);
+  this->memory_.SetData64(0x5304, 0xf4);
+  this->memory_.SetData64(0x530c, 0xffffffffffffffffULL);
+  this->memory_.SetData8(0x5314, 1);
+  this->memory_.SetData8(0x5315, '\0');
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x5400, 0xffffffff);
+  this->memory_.SetData64(0x5404, 0xf4);
+  this->memory_.SetData64(0x540c, 0x300);
+  this->memory_.SetData64(0x5414, 0x3500);
+  this->memory_.SetData64(0x541c, 0x400);
+
+  this->memory_.SetData32(0x5500, 0xffffffff);
+  this->memory_.SetData64(0x5504, 0xf4);
+  this->memory_.SetData64(0x550c, 0x300);
+  this->memory_.SetData64(0x5514, 0x4500);
+  this->memory_.SetData64(0x551c, 0x500);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
+
+  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+  this->debug_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x1500U, info.start);
+  EXPECT_EQ(0x1700U, info.end);
+
+  this->debug_frame_->TestGetFdeInfo(1, &info);
+  EXPECT_EQ(0x5200U, info.offset);
+  EXPECT_EQ(0x2500U, info.start);
+  EXPECT_EQ(0x2800U, info.end);
+
+  this->debug_frame_->TestGetFdeInfo(2, &info);
+  EXPECT_EQ(0x5400U, info.offset);
+  EXPECT_EQ(0x3500U, info.start);
+  EXPECT_EQ(0x3900U, info.end);
+
+  this->debug_frame_->TestGetFdeInfo(3, &info);
+  EXPECT_EQ(0x5500U, info.offset);
+  EXPECT_EQ(0x4500U, info.start);
+  EXPECT_EQ(0x4a00U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init64_fde_not_following_cie) {
+  // CIE 64 information.
+  this->memory_.SetData32(0x5000, 0xffffffff);
+  this->memory_.SetData64(0x5004, 0xf4);
+  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
+  this->memory_.SetData8(0x5014, 1);
+  this->memory_.SetData8(0x5015, '\0');
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x5100, 0xffffffff);
+  this->memory_.SetData64(0x5104, 0xf4);
+  this->memory_.SetData64(0x510c, 0x1000);
+  this->memory_.SetData64(0x5114, 0x1500);
+  this->memory_.SetData64(0x511c, 0x200);
+
+  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 1);
+  // Augment string.
+  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'});
+  // Code alignment factor.
+  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00});
+  // Data alignment factor.
+  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+  // Return address register
+  this->memory_.SetData8(0x5014, 0x84);
+  // Augmentation length
+  this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00});
+  // R data.
+  this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0);
+  this->memory_.SetData16(0x5108, 0x1500);
+  this->memory_.SetData16(0x510a, 0x200);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+  ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  this->debug_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x1500U, info.start);
+  EXPECT_EQ(0x1700U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init_version4) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 4);
+  // Augment string.
+  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
+  // Address size.
+  this->memory_.SetData8(0x500e, 4);
+  // Segment size.
+  this->memory_.SetData8(0x500f, 0);
+  // Code alignment factor.
+  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00});
+  // Data alignment factor.
+  this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+  // Return address register
+  this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10});
+  // Augmentation length
+  this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00});
+  // L data.
+  this->memory_.SetData8(0x501a, 0x10);
+  // P data.
+  this->memory_.SetData8(0x501b, DW_EH_PE_udata4);
+  this->memory_.SetData32(0x501c, 0x100);
+  // R data.
+  this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0);
+  this->memory_.SetData16(0x5108, 0x1500);
+  this->memory_.SetData16(0x510a, 0x200);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+  ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  this->debug_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x1500U, info.start);
+  EXPECT_EQ(0x1700U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeOffsetFromPc) {
+  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  for (size_t i = 0; i < 9; i++) {
+    info.start = 0x1000 * (i + 1);
+    info.end = 0x1000 * (i + 2) - 0x10;
+    info.offset = 0x5000 + i * 0x20;
+    this->debug_frame_->TestPushFdeInfo(info);
+  }
+
+  this->debug_frame_->TestSetFdeCount(0);
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+
+  this->debug_frame_->TestSetFdeCount(9);
+  ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+  // Odd number of elements.
+  for (size_t i = 0; i < 9; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
+                                                                             << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+        << "Failed at index " << i;
+    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+  }
+
+  // Even number of elements.
+  this->debug_frame_->TestSetFdeCount(10);
+  info.start = 0xa000;
+  info.end = 0xaff0;
+  info.offset = 0x5120;
+  this->debug_frame_->TestPushFdeInfo(info);
+
+  for (size_t i = 0; i < 10; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
+                                                                             << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+        << "Failed at index " << i;
+    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+  }
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde32) {
+  this->debug_frame_->TestSetOffset(0x4000);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0xf000, 0x100);
+  this->memory_.SetData32(0xf004, 0xffffffff);
+  this->memory_.SetData8(0xf008, 0x1);
+  this->memory_.SetData8(0xf009, '\0');
+  this->memory_.SetData8(0xf00a, 4);
+  this->memory_.SetData8(0xf00b, 8);
+  this->memory_.SetData8(0xf00c, 0x20);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x14000, 0x20);
+  this->memory_.SetData32(0x14004, 0xb000);
+  this->memory_.SetData32(0x14008, 0x9000);
+  this->memory_.SetData32(0x1400c, 0x100);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x14000);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x9000U, fde->pc_start);
+  EXPECT_EQ(0x9100U, 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(DwarfDebugFrameTest, GetCieFde64) {
+  this->debug_frame_->TestSetOffset(0x2000);
+
+  // CIE 64 information.
+  this->memory_.SetData32(0x6000, 0xffffffff);
+  this->memory_.SetData64(0x6004, 0x100);
+  this->memory_.SetData64(0x600c, 0xffffffffffffffffULL);
+  this->memory_.SetData8(0x6014, 0x1);
+  this->memory_.SetData8(0x6015, '\0');
+  this->memory_.SetData8(0x6016, 4);
+  this->memory_.SetData8(0x6017, 8);
+  this->memory_.SetData8(0x6018, 0x20);
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x8000, 0xffffffff);
+  this->memory_.SetData64(0x8004, 0x200);
+  this->memory_.SetData64(0x800c, 0x4000);
+  this->memory_.SetData64(0x8014, 0x5000);
+  this->memory_.SetData64(0x801c, 0x300);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x8000);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0x5000U, fde->pc_start);
+  EXPECT_EQ(0x5300U, fde->pc_end);
+  EXPECT_EQ(0x6000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+  EXPECT_EQ(0U, fde->cie->segment_size);
+  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+  EXPECT_EQ(0U, fde->cie->personality_handler);
+  EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+  EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+  EXPECT_EQ(8, fde->cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, 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> DwarfDebugFrameTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
new file mode 100644
index 0000000..4e538d4
--- /dev/null
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -0,0 +1,409 @@
+/*
+ * 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 "DwarfEhFrame.h"
+#include "DwarfEncoding.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+template <typename TypeParam>
+class MockDwarfEhFrame : public DwarfEhFrame<TypeParam> {
+ public:
+  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;
+  }
+
+  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 DwarfEhFrameTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    eh_frame_ = new MockDwarfEhFrame<TypeParam>(&memory_);
+    ResetLogs();
+  }
+
+  void TearDown() override { delete eh_frame_; }
+
+  MemoryFake memory_;
+  MockDwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfEhFrameTest);
+
+// 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);
+
+  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(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, 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(0x1380U, info->pc);
+  EXPECT_EQ(0x1540U, info->offset);
+}
+
+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);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x3340U, info->pc);
+  EXPECT_EQ(0x3500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, 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(0x340U, 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(0x340U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetBinary_verify) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  typename DwarfEhFrame<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(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(0x340, &fde_offset));
+  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
+  EXPECT_EQ(0x500U, fde_offset);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &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(0x440, &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) {
+  // 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(0x1d00cU, fde->pc_start);
+  EXPECT_EQ(0x1d10cU, 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(DwarfEhFrameTest, 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(0xd01cU, fde->pc_start);
+  EXPECT_EQ(0xd31cU, fde->pc_end);
+  EXPECT_EQ(0x6000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+  EXPECT_EQ(0U, fde->cie->segment_size);
+  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+  EXPECT_EQ(0U, fde->cie->personality_handler);
+  EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+  EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+  EXPECT_EQ(8, fde->cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, 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);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index c31903d..81cdaf5 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -67,6 +67,18 @@
   template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
   void SonameSize();
 
+  template <typename ElfType>
+  void InitHeadersEhFrameTest();
+
+  template <typename ElfType>
+  void InitHeadersDebugFrame();
+
+  template <typename ElfType>
+  void InitHeadersEhFrameFail();
+
+  template <typename ElfType>
+  void InitHeadersDebugFrameFail();
+
   MemoryFake memory_;
 };
 
@@ -571,3 +583,138 @@
 TEST_F(ElfInterfaceTest, elf64_soname_size) {
   SonameSize<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
 }
+
+class MockElfInterface32 : public ElfInterface32 {
+ public:
+  MockElfInterface32(Memory* memory) : ElfInterface32(memory) {}
+  virtual ~MockElfInterface32() = default;
+
+  void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+  void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+
+  void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+  void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+class MockElfInterface64 : public ElfInterface64 {
+ public:
+  MockElfInterface64(Memory* memory) : ElfInterface64(memory) {}
+  virtual ~MockElfInterface64() = default;
+
+  void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+  void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+
+  void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+  void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersEhFrameTest() {
+  ElfType elf(&memory_);
+
+  elf.TestSetEhFrameOffset(0x10000);
+  elf.TestSetEhFrameSize(0);
+  elf.TestSetDebugFrameOffset(0);
+  elf.TestSetDebugFrameSize(0);
+
+  memory_.SetMemory(0x10000,
+                    std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata2, DW_EH_PE_udata2});
+  memory_.SetData32(0x10004, 0x500);
+  memory_.SetData32(0x10008, 250);
+
+  elf.InitHeaders();
+
+  EXPECT_FALSE(elf.eh_frame() == nullptr);
+  EXPECT_TRUE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame32) {
+  InitHeadersEhFrameTest<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame64) {
+  InitHeadersEhFrameTest<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersDebugFrame() {
+  ElfType elf(&memory_);
+
+  elf.TestSetEhFrameOffset(0);
+  elf.TestSetEhFrameSize(0);
+  elf.TestSetDebugFrameOffset(0x5000);
+  elf.TestSetDebugFrameSize(0x200);
+
+  memory_.SetData32(0x5000, 0xfc);
+  memory_.SetData32(0x5004, 0xffffffff);
+  memory_.SetData8(0x5008, 1);
+  memory_.SetData8(0x5009, '\0');
+
+  memory_.SetData32(0x5100, 0xfc);
+  memory_.SetData32(0x5104, 0);
+  memory_.SetData32(0x5108, 0x1500);
+  memory_.SetData32(0x510c, 0x200);
+
+  elf.InitHeaders();
+
+  EXPECT_TRUE(elf.eh_frame() == nullptr);
+  EXPECT_FALSE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame32) {
+  InitHeadersDebugFrame<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame64) {
+  InitHeadersDebugFrame<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersEhFrameFail() {
+  ElfType elf(&memory_);
+
+  elf.TestSetEhFrameOffset(0x1000);
+  elf.TestSetEhFrameSize(0x100);
+  elf.TestSetDebugFrameOffset(0);
+  elf.TestSetDebugFrameSize(0);
+
+  elf.InitHeaders();
+
+  EXPECT_TRUE(elf.eh_frame() == nullptr);
+  EXPECT_EQ(0U, elf.eh_frame_offset());
+  EXPECT_EQ(static_cast<uint64_t>(-1), elf.eh_frame_size());
+  EXPECT_TRUE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame32_fail) {
+  InitHeadersEhFrameFail<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame64_fail) {
+  InitHeadersEhFrameFail<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersDebugFrameFail() {
+  ElfType elf(&memory_);
+
+  elf.TestSetEhFrameOffset(0);
+  elf.TestSetEhFrameSize(0);
+  elf.TestSetDebugFrameOffset(0x1000);
+  elf.TestSetDebugFrameSize(0x100);
+
+  elf.InitHeaders();
+
+  EXPECT_TRUE(elf.eh_frame() == nullptr);
+  EXPECT_TRUE(elf.debug_frame() == nullptr);
+  EXPECT_EQ(0U, elf.debug_frame_offset());
+  EXPECT_EQ(static_cast<uint64_t>(-1), elf.debug_frame_size());
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame32_fail) {
+  InitHeadersDebugFrameFail<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) {
+  InitHeadersDebugFrameFail<MockElfInterface64>();
+}
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index 98cd2c6..84af293 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -47,9 +47,11 @@
 #ifndef _WIN32
     if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error
         ALOGE("%s: Failed to format string", __FUNCTION__);
+        va_end(arglist);
         return;
     }
 #else
+    va_end(arglist);
     return;
 #endif
 
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 287a99c..1084d59 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -52,17 +52,25 @@
     ],
 }
 
-
 cc_library {
     name: "libziparchive",
     host_supported: true,
-    vendor_available:true,
+    vendor_available: true,
 
-    defaults: ["libziparchive_defaults", "libziparchive_flags"],
-    shared_libs: ["liblog", "libbase"],
+    defaults: [
+        "libziparchive_defaults",
+        "libziparchive_flags",
+    ],
+    shared_libs: [
+        "liblog",
+        "libbase",
+    ],
     target: {
         android: {
-            shared_libs: ["libz", "libutils"],
+            shared_libs: [
+                "libz",
+                "libutils",
+            ],
         },
         host: {
             static_libs: ["libutils"],
@@ -88,7 +96,10 @@
     name: "libziparchive-host",
     host_supported: true,
     device_supported: false,
-    defaults: ["libziparchive_defaults", "libziparchive_flags"],
+    defaults: [
+        "libziparchive_defaults",
+        "libziparchive_flags",
+    ],
     shared_libs: ["libz-host"],
     static_libs: ["libutils"],
 }
@@ -150,3 +161,13 @@
         },
     },
 }
+
+cc_binary {
+    name: "unzip",
+    defaults: ["libziparchive_flags"],
+    srcs: ["unzip.cpp"],
+    shared_libs: [
+        "libbase",
+        "libziparchive",
+    ],
+}
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
new file mode 100644
index 0000000..6756007
--- /dev/null
+++ b/libziparchive/unzip.cpp
@@ -0,0 +1,345 @@
+/*
+ * 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 <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <ziparchive/zip_archive.h>
+
+enum OverwriteMode {
+  kAlways,
+  kNever,
+  kPrompt,
+};
+
+static OverwriteMode overwrite_mode = kPrompt;
+static const char* flag_d = nullptr;
+static bool flag_l = false;
+static bool flag_p = false;
+static bool flag_q = false;
+static bool flag_v = false;
+static const char* archive_name = nullptr;
+static std::set<std::string> includes;
+static std::set<std::string> excludes;
+static uint64_t total_uncompressed_length = 0;
+static uint64_t total_compressed_length = 0;
+static size_t file_count = 0;
+
+static bool Filter(const std::string& name) {
+  if (!excludes.empty() && excludes.find(name) != excludes.end()) return true;
+  if (!includes.empty() && includes.find(name) == includes.end()) return true;
+  return false;
+}
+
+static bool MakeDirectoryHierarchy(const std::string& path) {
+  // stat rather than lstat because a symbolic link to a directory is fine too.
+  struct stat sb;
+  if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return true;
+
+  // Ensure the parent directories exist first.
+  if (!MakeDirectoryHierarchy(android::base::Dirname(path))) return false;
+
+  // Then try to create this directory.
+  return (mkdir(path.c_str(), 0777) != -1);
+}
+
+static int CompressionRatio(int64_t uncompressed, int64_t compressed) {
+  if (uncompressed == 0) return 0;
+  return (100LL * (uncompressed - compressed)) / uncompressed;
+}
+
+static void MaybeShowHeader() {
+  if (!flag_q) printf("Archive:  %s\n", archive_name);
+  if (flag_v) {
+    printf(
+        " Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
+        "--------  ------  ------- ---- ---------- ----- --------  ----\n");
+  } else if (flag_l) {
+    printf(
+        "  Length      Date    Time    Name\n"
+        "---------  ---------- -----   ----\n");
+  }
+}
+
+static void MaybeShowFooter() {
+  if (flag_v) {
+    printf(
+        "--------          -------  ---                            -------\n"
+        "%8" PRId64 "         %8" PRId64 " %3d%%                            %zu file%s\n",
+        total_uncompressed_length, total_compressed_length,
+        CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
+        (file_count == 1) ? "" : "s");
+  } else if (flag_l) {
+    printf(
+        "---------                     -------\n"
+        "%9" PRId64 "                     %zu file%s\n",
+        total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
+  }
+}
+
+static bool PromptOverwrite(const std::string& dst) {
+  // TODO: [r]ename not implemented because it doesn't seem useful.
+  printf("replace %s? [y]es, [n]o, [A]ll, [N]one: ", dst.c_str());
+  fflush(stdout);
+  while (true) {
+    char* line = nullptr;
+    size_t n;
+    if (getline(&line, &n, stdin) == -1) {
+      error(1, 0, "(EOF/read error; assuming [N]one...)");
+      overwrite_mode = kNever;
+      return false;
+    }
+    if (n == 0) continue;
+    char cmd = line[0];
+    free(line);
+    switch (cmd) {
+      case 'y':
+        return true;
+      case 'n':
+        return false;
+      case 'A':
+        overwrite_mode = kAlways;
+        return true;
+      case 'N':
+        overwrite_mode = kNever;
+        return false;
+    }
+  }
+}
+
+static void ExtractToPipe(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+  // We need to extract to memory because ExtractEntryToFile insists on
+  // being able to seek and truncate, and you can't do that with stdout.
+  uint8_t* buffer = new uint8_t[entry.uncompressed_length];
+  int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length);
+  if (err < 0) {
+    error(1, 0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
+  }
+  if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) {
+    error(1, errno, "failed to write %s to stdout", name.c_str());
+  }
+  delete[] buffer;
+}
+
+static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+  // Bad filename?
+  if (android::base::StartsWith(name, "/") || android::base::StartsWith(name, "../") ||
+      name.find("/../") != std::string::npos) {
+    error(1, 0, "bad filename %s", name.c_str());
+  }
+
+  // Where are we actually extracting to (for human-readable output)?
+  std::string dst;
+  if (flag_d) {
+    dst = flag_d;
+    if (!android::base::EndsWith(dst, "/")) dst += '/';
+  }
+  dst += name;
+
+  // Ensure the directory hierarchy exists.
+  if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
+    error(1, errno, "couldn't create directory hierarchy for %s", dst.c_str());
+  }
+
+  // An entry in a zip file can just be a directory itself.
+  if (android::base::EndsWith(name, "/")) {
+    if (mkdir(name.c_str(), entry.unix_mode) == -1) {
+      // If the directory already exists, that's fine.
+      if (errno == EEXIST) {
+        struct stat sb;
+        if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return;
+      }
+      error(1, errno, "couldn't extract directory %s", dst.c_str());
+    }
+    return;
+  }
+
+  // Create the file.
+  int fd = open(name.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC | O_EXCL, entry.unix_mode);
+  if (fd == -1 && errno == EEXIST) {
+    if (overwrite_mode == kNever) return;
+    if (overwrite_mode == kPrompt && !PromptOverwrite(dst)) return;
+    // Either overwrite_mode is kAlways or the user consented to this specific case.
+    fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode);
+  }
+  if (fd == -1) error(1, errno, "couldn't create file %s", dst.c_str());
+
+  // Actually extract into the file.
+  if (!flag_q) printf("  inflating: %s\n", dst.c_str());
+  int err = ExtractEntryToFile(zah, &entry, fd);
+  if (err < 0) error(1, 0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
+  close(fd);
+}
+
+static void ListOne(const ZipEntry& entry, const std::string& name) {
+  tm t = entry.GetModificationTime();
+  char time[32];
+  snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
+           t.tm_mday, t.tm_hour, t.tm_min);
+  if (flag_v) {
+    printf("%8d  %s  %7d %3d%% %s %08x  %s\n", entry.uncompressed_length,
+           (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
+           CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
+           name.c_str());
+  } else {
+    printf("%9d  %s   %s\n", entry.uncompressed_length, time, name.c_str());
+  }
+}
+
+static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+  if (flag_l || flag_v) {
+    // -l or -lv or -lq or -v.
+    ListOne(entry, name);
+  } else {
+    // Actually extract.
+    if (flag_p) {
+      ExtractToPipe(zah, entry, name);
+    } else {
+      ExtractOne(zah, entry, name);
+    }
+  }
+  total_uncompressed_length += entry.uncompressed_length;
+  total_compressed_length += entry.compressed_length;
+  ++file_count;
+}
+
+static void ProcessAll(ZipArchiveHandle zah) {
+  MaybeShowHeader();
+
+  // libziparchive iteration order doesn't match the central directory.
+  // We could sort, but that would cost extra and wouldn't match either.
+  void* cookie;
+  int err = StartIteration(zah, &cookie, nullptr, nullptr);
+  if (err != 0) {
+    error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
+  }
+
+  ZipEntry entry;
+  ZipString string;
+  while ((err = Next(cookie, &entry, &string)) >= 0) {
+    std::string name(string.name, string.name + string.name_length);
+    if (!Filter(name)) ProcessOne(zah, entry, name);
+  }
+
+  if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
+  EndIteration(cookie);
+
+  MaybeShowFooter();
+}
+
+static void ShowHelp(bool full) {
+  fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
+  if (!full) exit(EXIT_FAILURE);
+
+  printf(
+      "\n"
+      "Extract FILEs from ZIP archive. Default is all files.\n"
+      "\n"
+      "-d DIR	Extract into DIR\n"
+      "-l	List contents (-lq excludes archive name, -lv is verbose)\n"
+      "-n	Never overwrite files (default: prompt)\n"
+      "-o	Always overwrite files\n"
+      "-p	Pipe to stdout\n"
+      "-q	Quiet\n"
+      "-v	List contents verbosely\n"
+      "-x FILE	Exclude files\n");
+  exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char* argv[]) {
+  static struct option opts[] = {
+      {"help", no_argument, 0, 'h'},
+  };
+  bool saw_x = false;
+  int opt;
+  while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
+    switch (opt) {
+      case 'd':
+        flag_d = optarg;
+        break;
+      case 'h':
+        ShowHelp(true);
+        break;
+      case 'l':
+        flag_l = true;
+        break;
+      case 'n':
+        overwrite_mode = kNever;
+        break;
+      case 'o':
+        overwrite_mode = kAlways;
+        break;
+      case 'p':
+        flag_p = flag_q = true;
+        break;
+      case 'q':
+        flag_q = true;
+        break;
+      case 'v':
+        flag_v = true;
+        break;
+      case 'x':
+        saw_x = true;
+        break;
+      case 1:
+        // -x swallows all following arguments, so we use '-' in the getopt
+        // string and collect files here.
+        if (!archive_name) {
+          archive_name = optarg;
+        } else if (saw_x) {
+          excludes.insert(optarg);
+        } else {
+          includes.insert(optarg);
+        }
+        break;
+      default:
+        ShowHelp(false);
+    }
+  }
+
+  if (!archive_name) error(1, 0, "missing archive filename");
+
+  // We can't support "-" to unzip from stdin because libziparchive relies on mmap.
+  ZipArchiveHandle zah;
+  int32_t err;
+  if ((err = OpenArchive(archive_name, &zah)) != 0) {
+    error(1, 0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
+  }
+
+  // Implement -d by changing into that directory.
+  // We'll create implicit directories based on paths in the zip file, but we
+  // require that the -d directory already exists.
+  if (flag_d && chdir(flag_d) == -1) error(1, errno, "couldn't chdir to %s", flag_d);
+
+  ProcessAll(zah);
+
+  CloseArchive(zah);
+  return 0;
+}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 246575f..a64b3b1 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -27,6 +27,7 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
 #include <memory>
@@ -48,6 +49,10 @@
 
 using android::base::get_unaligned;
 
+// Used to turn on crc checks - verify that the content CRC matches the values
+// specified in the local file header and the central directory.
+static const bool kCrcChecksEnabled = false;
+
 // This is for windows. If we don't open a file in binary mode, weird
 // things will happen.
 #ifndef O_BINARY
@@ -483,8 +488,7 @@
   delete archive;
 }
 
-static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip,
-                                             ZipEntry *entry) {
+static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) {
   uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
   if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
     return kIoError;
@@ -494,9 +498,17 @@
   const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
   const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
 
-  entry->crc32 = descriptor->crc32;
-  entry->compressed_length = descriptor->compressed_size;
-  entry->uncompressed_length = descriptor->uncompressed_size;
+  // Validate that the values in the data descriptor match those in the central
+  // directory.
+  if (entry->compressed_length != descriptor->compressed_size ||
+      entry->uncompressed_length != descriptor->uncompressed_size ||
+      entry->crc32 != descriptor->crc32) {
+    ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+          "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+          entry->compressed_length, entry->uncompressed_length, entry->crc32,
+          descriptor->compressed_size, descriptor->uncompressed_size, descriptor->crc32);
+    return kInconsistentInformation;
+  }
 
   return 0;
 }
@@ -564,12 +576,22 @@
   // Paranoia: Match the values specified in the local file header
   // to those specified in the central directory.
 
-  // Verify that the central directory and local file header agree on the use of a trailing
-  // Data Descriptor.
+  // Warn if central directory and local file header don't agree on the use
+  // of a trailing Data Descriptor. The reference implementation is inconsistent
+  // and appears to use the LFH value during extraction (unzip) but the CD value
+  // while displayng information about archives (zipinfo). The spec remains
+  // silent on this inconsistency as well.
+  //
+  // For now, always use the version from the LFH but make sure that the values
+  // specified in the central directory match those in the data descriptor.
+  //
+  // NOTE: It's also worth noting that unzip *does* warn about inconsistencies in
+  // bit 11 (EFS: The language encoding flag, marking that filename and comment are
+  // encoded using UTF-8). This implementation does not check for the presence of
+  // that flag and always enforces that entry names are valid UTF-8.
   if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
-    ALOGW("Zip: gpb flag mismatch. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
+    ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
           cdr->gpb_flags, lfh->gpb_flags);
-    return kInconsistentInformation;
   }
 
   // If there is no trailing data descriptor, verify that the central directory and local file
@@ -589,6 +611,13 @@
     data->has_data_descriptor = 1;
   }
 
+  // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
+  if ((cdr->version_made_by >> 8) == 3) {
+    data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
+  } else {
+    data->unix_mode = 0777;
+  }
+
   // Check that the local file header name matches the declared
   // name in the central directory.
   if (lfh->file_name_length == nameLen) {
@@ -932,6 +961,7 @@
 
   const uint32_t uncompressed_length = entry->uncompressed_length;
 
+  uint64_t crc = 0;
   uint32_t compressed_length = entry->compressed_length;
   do {
     /* read as much as we can */
@@ -964,6 +994,8 @@
       if (!writer->Append(&write_buf[0], write_size)) {
         // The file might have declared a bogus length.
         return kInconsistentInformation;
+      } else {
+        crc = crc32(crc, &write_buf[0], write_size);
       }
 
       zstream.next_out = &write_buf[0];
@@ -973,8 +1005,13 @@
 
   assert(zerr == Z_STREAM_END);     /* other errors should've been caught */
 
-  // stream.adler holds the crc32 value for such streams.
-  *crc_out = zstream.adler;
+  // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
+  // "feature" of zlib to tell it there won't be a zlib file header. zlib
+  // doesn't bother calculating the checksum in that scenario. We just do
+  // it ourselves above because there are no additional gains to be made by
+  // having zlib calculate it for us, since they do it by calling crc32 in
+  // the same manner that we have above.
+  *crc_out = crc;
 
   if (zstream.total_out != uncompressed_length || compressed_length != 0) {
     ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
@@ -1037,15 +1074,14 @@
   }
 
   if (!return_value && entry->has_data_descriptor) {
-    return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
+    return_value = ValidateDataDescriptor(archive->mapped_zip, entry);
     if (return_value) {
       return return_value;
     }
   }
 
-  // TODO: Fix this check by passing the right flags to inflate2 so that
-  // it calculates the CRC for us.
-  if (entry->crc32 != crc && false) {
+  // Validate that the CRC matches the calculated value.
+  if (kCrcChecksEnabled && (entry->crc32 != static_cast<uint32_t>(crc))) {
     ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
     return kInconsistentInformation;
   }
@@ -1227,3 +1263,17 @@
   }
   return true;
 }
+
+tm ZipEntry::GetModificationTime() const {
+  tm t = {};
+
+  t.tm_hour = (mod_time >> 11) & 0x1f;
+  t.tm_min = (mod_time >> 5) & 0x3f;
+  t.tm_sec = (mod_time & 0x1f) << 1;
+
+  t.tm_year = ((mod_time >> 25) & 0x7f) + 80;
+  t.tm_mon = ((mod_time >> 21) & 0xf) - 1;
+  t.tm_mday = (mod_time >> 16) & 0x1f;
+
+  return t;
+}
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
index ca42509..bc1ebb4 100644
--- a/libziparchive/zip_archive_common.h
+++ b/libziparchive/zip_archive_common.h
@@ -73,7 +73,7 @@
 
   // The start of record signature. Must be |kSignature|.
   uint32_t record_signature;
-  // Tool version. Ignored by this implementation.
+  // Source tool version. Top byte gives source OS.
   uint16_t version_made_by;
   // Tool version. Ignored by this implementation.
   uint16_t version_needed;
@@ -106,7 +106,7 @@
   uint16_t file_start_disk;
   // File attributes. Ignored by this implementation.
   uint16_t internal_file_attributes;
-  // File attributes. Ignored by this implementation.
+  // File attributes. For archives created on Unix, the top bits are the mode.
   uint32_t external_file_attributes;
   // The offset to the local file header for this entry, from the
   // beginning of this archive.
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 9dd6cc0..42167dd 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -632,6 +632,96 @@
   CloseArchive(handle);
 }
 
+// Generated using the following Java program:
+//     public static void main(String[] foo) throws Exception {
+//       FileOutputStream fos = new
+//       FileOutputStream("/tmp/data_descriptor.zip");
+//       ZipOutputStream zos = new ZipOutputStream(fos);
+//       ZipEntry ze = new ZipEntry("name");
+//       ze.setMethod(ZipEntry.DEFLATED);
+//       zos.putNextEntry(ze);
+//       zos.write("abdcdefghijk".getBytes());
+//       zos.closeEntry();
+//       zos.close();
+//     }
+//
+// cat /tmp/data_descriptor.zip | xxd -i
+//
+static const std::vector<uint8_t> kDataDescriptorZipFile{
+    0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
+    0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
+    //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
+    0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+    0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
+    0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
+    0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
+    0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+// The offsets of the data descriptor in this file, so we can mess with
+// them later in the test.
+static constexpr uint32_t kDataDescriptorOffset = 48;
+static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
+static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
+
+static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
+                                 std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
+  TemporaryFile tmp_file;
+  ASSERT_NE(-1, tmp_file.fd);
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle));
+
+  // This function expects a variant of kDataDescriptorZipFile, for look for
+  // an entry whose name is "name" and whose size is 12 (contents =
+  // "abdcdefghijk").
+  ZipEntry entry;
+  ZipString empty_name;
+  SetZipString(&empty_name, "name");
+
+  ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+  ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
+
+  entry_out->resize(12);
+  (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, ValidDataDescriptors) {
+  std::vector<uint8_t> entry;
+  int32_t error_code = 0;
+  ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
+
+  ASSERT_EQ(0, error_code);
+  ASSERT_EQ(12u, entry.size());
+  ASSERT_EQ('a', entry[0]);
+  ASSERT_EQ('k', entry[11]);
+}
+
+TEST(ziparchive, InvalidDataDescriptors) {
+  std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
+  invalid_csize[kCSizeOffset] = 0xfe;
+
+  std::vector<uint8_t> entry;
+  int32_t error_code = 0;
+  ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+  ASSERT_GT(0, error_code);
+  ASSERT_STREQ("Inconsistent information", ErrorCodeString(error_code));
+
+  std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
+  invalid_csize[kSizeOffset] = 0xfe;
+
+  error_code = 0;
+  entry.clear();
+  ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+  ASSERT_GT(0, error_code);
+  ASSERT_STREQ("Inconsistent information", ErrorCodeString(error_code));
+}
+
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
 
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 5b526a4..9ad0252 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -135,17 +135,6 @@
   CloseArchive(handle);
 }
 
-static void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
-  memset(tm, 0, sizeof(struct tm));
-  tm->tm_hour = (zip_time >> 11) & 0x1f;
-  tm->tm_min = (zip_time >> 5) & 0x3f;
-  tm->tm_sec = (zip_time & 0x1f) << 1;
-
-  tm->tm_year = ((zip_time >> 25) & 0x7f) + 80;
-  tm->tm_mon = ((zip_time >> 21) & 0xf) - 1;
-  tm->tm_mday = (zip_time >> 16) & 0x1f;
-}
-
 static struct tm MakeTm() {
   struct tm tm;
   memset(&tm, 0, sizeof(struct tm));
@@ -177,8 +166,7 @@
   ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
   EXPECT_EQ(0, data.offset & 0x03);
 
-  struct tm mod;
-  ConvertZipTimeToTm(data.mod_time, &mod);
+  struct tm mod = data.GetModificationTime();
   EXPECT_EQ(tm.tm_sec, mod.tm_sec);
   EXPECT_EQ(tm.tm_min, mod.tm_min);
   EXPECT_EQ(tm.tm_hour, mod.tm_hour);
@@ -228,8 +216,7 @@
   ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
   EXPECT_EQ(0, data.offset & 0xfff);
 
-  struct tm mod;
-  ConvertZipTimeToTm(data.mod_time, &mod);
+  struct tm mod = data.GetModificationTime();
   EXPECT_EQ(tm.tm_sec, mod.tm_sec);
   EXPECT_EQ(tm.tm_min, mod.tm_min);
   EXPECT_EQ(tm.tm_hour, mod.tm_hour);
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index cd80212..4397b14 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -933,8 +933,12 @@
 }
 
 #ifdef __ANDROID__
-static inline int32_t get4LE(const char* src) {
-    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+static inline uint32_t get4LE(const uint8_t* src) {
+  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+static inline uint32_t get4LE(const char* src) {
+  return get4LE(reinterpret_cast<const uint8_t*>(src));
 }
 #endif
 
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 4f4fc5d..6d35fed 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -3,6 +3,7 @@
     required: [
         "bzip2",
         "grep",
+        "grep_vendor",
         "gzip",
         "mkshrc",
         "mkshrc_vendor",
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 1c9fb20..8db8327 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -28,8 +28,8 @@
 }
 
 // We build BSD grep separately, so it can provide egrep and fgrep too.
-cc_binary {
-    name: "grep",
+cc_defaults {
+    name: "grep_common",
     srcs: [
         "upstream-netbsd/usr.bin/grep/fastgrep.c",
         "upstream-netbsd/usr.bin/grep/file.c",
@@ -40,5 +40,19 @@
     cflags: common_cflags,
     local_include_dirs: ["upstream-netbsd/include/"],
     symlinks: ["egrep", "fgrep"],
+}
 
+cc_binary {
+    name: "grep",
+    defaults: ["grep_common"],
+}
+
+// Build vendor grep.
+// TODO: Add vendor_available to "grep" module and remove "grep_vendor" module
+//       when vendor_available is fully supported.
+cc_binary {
+    name: "grep_vendor",
+    stem: "grep",
+    vendor: true,
+    defaults: ["grep_common"],
 }
diff --git a/toolbox/upstream-netbsd/include/sys/mtio.h b/toolbox/upstream-netbsd/include/sys/mtio.h
deleted file mode 100644
index 8fb5655..0000000
--- a/toolbox/upstream-netbsd/include/sys/mtio.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <linux/mtio.h>
diff --git a/tzdatacheck/Android.bp b/tzdatacheck/Android.bp
deleted file mode 100644
index 00ad141..0000000
--- a/tzdatacheck/Android.bp
+++ /dev/null
@@ -1,14 +0,0 @@
-// ========================================================
-// Executable
-// ========================================================
-cc_binary {
-    name: "tzdatacheck",
-    host_supported: true,
-    srcs: ["tzdatacheck.cpp"],
-    shared_libs: [
-        "libbase",
-        "libcutils",
-        "liblog",
-    ],
-    cflags: ["-Werror"],
-}
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
deleted file mode 100644
index 8fcd17f..0000000
--- a/tzdatacheck/tzdatacheck.cpp
+++ /dev/null
@@ -1,601 +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.
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <ftw.h>
-#include <libgen.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <iostream>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "android-base/logging.h"
-
-// The name of the directory that holds a staged time zone update distro. If this exists it should
-// replace the one in CURRENT_DIR_NAME.
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* STAGED_DIR_NAME = "/staged";
-
-// The name of the directory that holds the (optional) installed time zone update distro.
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* CURRENT_DIR_NAME = "/current";
-
-// The name of a file in the staged dir that indicates the staged operation is an "uninstall".
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* UNINSTALL_TOMBSTONE_FILE_NAME = "/STAGED_UNINSTALL_TOMBSTONE";
-
-// The name of the file containing the distro version information.
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char* DISTRO_VERSION_FILENAME = "/distro_version";
-
-// distro_version is an ASCII file consisting of 17 bytes in the form: AAA.BBB|CCCCC|DDD
-// AAA.BBB is the major/minor version of the distro format (e.g. 001.001),
-// CCCCC is the rules version (e.g. 2016g)
-// DDD is the android revision for this rules version to allow for distro corrections (e.g. 001)
-// We only need the first 13 to determine if it is suitable for the device.
-static const int DISTRO_VERSION_LENGTH = 13;
-
-// The major version of the distro format supported by this code as a null-terminated char[].
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char SUPPORTED_DISTRO_MAJOR_VERSION[] = "001";
-
-// The length of the distro format major version excluding the \0
-static const size_t SUPPORTED_DISTRO_MAJOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MAJOR_VERSION) - 1;
-
-// The minor version of the distro format supported by this code as a null-terminated char[].
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char SUPPORTED_DISTRO_MINOR_VERSION[] = "001";
-
-// The length of the distro format minor version excluding the \0
-static const size_t SUPPORTED_DISTRO_MINOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MINOR_VERSION) - 1;
-
-// The length of the distro format version. e.g. 001.001
-static const size_t SUPPORTED_DISTRO_VERSION_LEN =
-        SUPPORTED_DISTRO_MAJOR_VERSION_LEN + SUPPORTED_DISTRO_MINOR_VERSION_LEN + 1;
-
-// The length of the IANA rules version bytes. e.g. 2016a
-static const size_t RULES_VERSION_LEN = 5;
-
-// Distro version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
-static const size_t DISTRO_VERSION_RULES_IDX = 8;
-
-// See also libcore.tzdata.shared2.TimeZoneDistro.
-static const char* TZDATA_FILENAME = "/tzdata";
-
-// tzdata file header (as much as we need for the version):
-// byte[11] tzdata_version  -- e.g. "tzdata2012f"
-static const int TZ_HEADER_LENGTH = 11;
-
-static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
-static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
-
-static void usage() {
-    std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
-            "\n"
-            "Checks whether any timezone update distro in DATA_TZ_DIR is compatible with the\n"
-            "current Android release and better than or the same as base system timezone rules in\n"
-            "SYSTEM_TZ_DIR. If the timezone rules in SYSTEM_TZ_DIR are a higher version than the\n"
-            "one in DATA_TZ_DIR the DATA_TZ_DIR is renamed and then deleted.\n";
-    exit(1);
-}
-
-/*
- * Opens a file and fills buffer with the first byteCount bytes from the file.
- * If the file does not exist or cannot be opened or is too short then false is returned.
- * If the bytes were read successfully then true is returned.
- */
-static bool readBytes(const std::string& fileName, char* buffer, size_t byteCount) {
-    FILE* file = fopen(fileName.c_str(), "r");
-    if (file == nullptr) {
-        if (errno != ENOENT) {
-            PLOG(WARNING) << "Error opening file " << fileName;
-        }
-        return false;
-    }
-    size_t bytesRead = fread(buffer, 1, byteCount, file);
-    fclose(file);
-    if (bytesRead != byteCount) {
-        LOG(WARNING) << fileName << " is too small. " << byteCount << " bytes required";
-        return false;
-    }
-    return true;
-}
-
-/*
- * Checks the contents of headerBytes. Returns true if it is valid (starts with "tzdata"), false
- * otherwise.
- */
-static bool checkValidTzDataHeader(const std::string& fileName, const char* headerBytes) {
-    if (strncmp("tzdata", headerBytes, 6) != 0) {
-        LOG(WARNING) << fileName << " does not start with the expected bytes (tzdata)";
-        return false;
-    }
-    return true;
-}
-
-static bool checkDigits(const char* buffer, const size_t count, size_t* i) {
-    for (size_t j = 0; j < count; j++) {
-      char toCheck = buffer[(*i)++];
-      if (!isdigit(toCheck)) {
-        return false;
-      }
-    }
-    return true;
-}
-
-static bool checkValidDistroVersion(const char* buffer) {
-    // See DISTRO_VERSION_LENGTH comments above for a description of the format.
-    size_t i = 0;
-    if (!checkDigits(buffer, 3, &i)) {
-      return false;
-    }
-    if (buffer[i++] != '.') {
-      return false;
-    }
-    if (!checkDigits(buffer, 3, &i)) {
-      return false;
-    }
-    if (buffer[i++] != '|') {
-      return false;
-    }
-    if (!checkDigits(buffer, 4, &i)) {
-      return false;
-    }
-    // Ignore the last character. It is assumed to be a letter but we don't check because it's not
-    // obvious what would happen at 'z'.
-    return true;
-}
-
-/* Return the parent directory of dirName. */
-static std::string getParentDir(const std::string& dirName) {
-    std::unique_ptr<char> mutable_dirname(strdup(dirName.c_str()));
-    return dirname(mutable_dirname.get());
-}
-
-/* Deletes a single file, symlink or directory. Called from nftw(). */
-static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct FTW*) {
-    LOG(DEBUG) << "Inspecting " << fpath;
-    switch (typeflag) {
-    case FTW_F:
-    case FTW_SL:
-        LOG(DEBUG) << "Unlinking " << fpath;
-        if (unlink(fpath)) {
-            PLOG(WARNING) << "Failed to unlink file/symlink " << fpath;
-        }
-        break;
-    case FTW_D:
-    case FTW_DP:
-        LOG(DEBUG) << "Removing dir " << fpath;
-        if (rmdir(fpath)) {
-            PLOG(WARNING) << "Failed to remove dir " << fpath;
-        }
-        break;
-    default:
-        LOG(WARNING) << "Unsupported file type " << fpath << ": " << typeflag;
-        break;
-    }
-    return 0;
-}
-
-enum PathStatus { ERR, NONE, IS_DIR, IS_REG, UNKNOWN };
-
-static PathStatus checkPath(const std::string& path) {
-    struct stat buf;
-    if (stat(path.c_str(), &buf) != 0) {
-        if (errno != ENOENT) {
-            PLOG(WARNING) << "Unable to stat " << path;
-            return ERR;
-        }
-        return NONE;
-    }
-    return S_ISDIR(buf.st_mode) ? IS_DIR : S_ISREG(buf.st_mode) ? IS_REG : UNKNOWN;
-}
-
-/*
- * Deletes fileToDelete and returns true if it is successful. If fileToDelete is not a file or
- * cannot be accessed this method returns false.
- */
-static bool deleteFile(const std::string& fileToDelete) {
-    // Check whether the file exists.
-    PathStatus pathStatus = checkPath(fileToDelete);
-    if (pathStatus == NONE) {
-        LOG(INFO) << "Path " << fileToDelete << " does not exist";
-        return true;
-    }
-    if (pathStatus != IS_REG) {
-        LOG(WARNING) << "Path " << fileToDelete << " failed to stat() or is not a file.";
-        return false;
-    }
-
-    // Attempt the deletion.
-    int rc = unlink(fileToDelete.c_str());
-    if (rc != 0) {
-        PLOG(WARNING) << "unlink() failed for " << fileToDelete;
-    }
-    return rc == 0;
-}
-
-/*
- * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
- * of the way. If dirToDelete does not exist this function does nothing and returns true. If
- * dirToDelete is not a directory or cannot be accessed this method returns false.
- *
- * During deletion, this function first renames the directory to a temporary name. If the temporary
- * directory cannot be created, or the directory cannot be renamed, false is returned. After the
- * rename, deletion of files and subdirs beneath the directory is performed on a "best effort"
- * basis. Symlinks beneath the directory are not followed.
- */
-static bool deleteDir(const std::string& dirToDelete) {
-    // Check whether the dir exists.
-    int pathStatus = checkPath(dirToDelete);
-    if (pathStatus == NONE) {
-        LOG(INFO) << "Path " << dirToDelete << " does not exist";
-        return true;
-    }
-    if (pathStatus != IS_DIR) {
-        LOG(WARNING) << "Path " << dirToDelete << " failed to stat() or is not a directory.";
-        return false;
-    }
-
-    // First, rename dirToDelete.
-
-    std::string tempDirNameTemplate = getParentDir(dirToDelete);
-    tempDirNameTemplate += "/tempXXXXXX";
-
-    // Create an empty directory with the temporary name. For this we need a non-const char*.
-    std::vector<char> tempDirName(tempDirNameTemplate.length() + 1);
-    strcpy(&tempDirName[0], tempDirNameTemplate.c_str());
-    if (mkdtemp(&tempDirName[0]) == nullptr) {
-        PLOG(WARNING) << "Unable to create a temporary directory: " << tempDirNameTemplate;
-        return false;
-    }
-
-    // Rename dirToDelete to tempDirName (replacing the empty tempDirName directory created above).
-    int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
-    if (rc == -1) {
-        PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
-                << &tempDirName[0];
-        return false;
-    }
-
-    // Recursively delete contents of tempDirName.
-
-    rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
-            FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
-    if (rc == -1) {
-        LOG(INFO) << "Could not delete directory: " << &tempDirName[0];
-    }
-    return true;
-}
-
-/*
- * Deletes the ConfigInstaller metadata directory.
- * TODO(nfuller). http://b/31008728 Remove this when ConfigInstaller is no longer used.
- */
-static void deleteConfigUpdaterMetadataDir(const char* dataZoneInfoDir) {
-    // Delete the update metadata
-    std::string dataUpdatesDirName(dataZoneInfoDir);
-    dataUpdatesDirName += "/updates";
-    LOG(INFO) << "Removing: " << dataUpdatesDirName;
-    if (!deleteDir(dataUpdatesDirName)) {
-        LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
-                << " was not successful";
-    }
-}
-
-/*
- * Deletes the timezone update distro directory.
- */
-static void deleteUpdateDistroDir(const std::string& distroDirName) {
-    LOG(INFO) << "Removing: " << distroDirName;
-    if (!deleteDir(distroDirName)) {
-        LOG(WARNING) << "Deletion of distro dir " << distroDirName << " was not successful";
-    }
-}
-
-static void handleStagedUninstall(const std::string& dataStagedDirName,
-                                  const std::string& dataCurrentDirName,
-                                  const PathStatus dataCurrentDirStatus) {
-    LOG(INFO) << "Staged operation is an uninstall.";
-
-    // Delete the current install directory.
-    switch (dataCurrentDirStatus) {
-        case NONE:
-            // This is unexpected: No uninstall should be staged if there is nothing to
-            // uninstall. Carry on anyway.
-            LOG(WARNING) << "No current install to delete.";
-            break;
-        case IS_DIR:
-            // This is normal. Delete the current install dir.
-            if (!deleteDir(dataCurrentDirName)) {
-                LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
-                             << " was not successful";
-                // If this happens we don't know whether we were able to delete or not. We don't
-                // delete the staged operation so it will be retried next boot unless overridden.
-                return;
-            }
-            break;
-        case IS_REG:
-        default:
-            // This is unexpected: We can try to delete the unexpected file and carry on.
-            LOG(WARNING) << "Current distro dir " << dataCurrentDirName
-                         << " is not actually a directory. Attempting deletion.";
-            if (!deleteFile(dataCurrentDirName)) {
-                LOG(WARNING) << "Could not delete " << dataCurrentDirName;
-                return;
-            }
-            break;
-    }
-
-    // Delete the staged uninstall dir.
-    if (!deleteDir(dataStagedDirName)) {
-        LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
-                     << " was not successful";
-        // If this happens we don't know whether we were able to delete the staged operation
-        // or not.
-        return;
-    }
-    LOG(INFO) << "Staged uninstall complete.";
-}
-
-static void handleStagedInstall(const std::string& dataStagedDirName,
-                                const std::string& dataCurrentDirName,
-                                const PathStatus dataCurrentDirStatus) {
-    LOG(INFO) << "Staged operation is an install.";
-
-    switch (dataCurrentDirStatus) {
-        case NONE:
-            // This is expected: This is the first install.
-            LOG(INFO) << "No current install to replace.";
-            break;
-        case IS_DIR:
-            // This is expected: We are replacing an existing install.
-            // Delete the current dir so we can replace it.
-            if (!deleteDir(dataCurrentDirName)) {
-                LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
-                             << " was not successful";
-                // If this happens, we cannot proceed.
-                return;
-            }
-            break;
-        case IS_REG:
-        default:
-            // This is unexpected: We can try to delete the unexpected file and carry on.
-            LOG(WARNING) << "Current distro dir " << dataCurrentDirName
-                         << " is not actually a directory. Attempting deletion.";
-            if (!deleteFile(dataCurrentDirName)) {
-                LOG(WARNING) << "Could not delete " << dataCurrentDirName;
-                return;
-            }
-            break;
-    }
-
-    // Move the staged dir so it is the new current dir, completing the install.
-    LOG(INFO) << "Moving " << dataStagedDirName << " to " << dataCurrentDirName;
-    int rc = rename(dataStagedDirName.c_str(), dataCurrentDirName.c_str());
-    if (rc == -1) {
-        PLOG(WARNING) << "Unable to rename directory from " << dataStagedDirName << " to "
-                      << &dataCurrentDirName[0];
-        return;
-    }
-
-    LOG(INFO) << "Staged install complete.";
-}
-/*
- * Process a staged operation if there is one.
- */
-static void processStagedOperation(const std::string& dataStagedDirName,
-                                   const std::string& dataCurrentDirName) {
-    PathStatus dataStagedDirStatus = checkPath(dataStagedDirName);
-
-    // Exit early for the common case.
-    if (dataStagedDirStatus == NONE) {
-        LOG(DEBUG) << "No staged time zone operation.";
-        return;
-    }
-
-    // Check known directory names are in a good starting state.
-    if (dataStagedDirStatus != IS_DIR) {
-        LOG(WARNING) << "Staged distro dir " << dataStagedDirName
-                     << " could not be accessed or is not a directory."
-                     << " stagedDirStatus=" << dataStagedDirStatus;
-        return;
-    }
-
-    // dataStagedDirStatus == IS_DIR.
-
-    // Work out whether there is anything currently installed.
-    PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
-    if (dataCurrentDirStatus == ERR) {
-        LOG(WARNING) << "Current install dir " << dataCurrentDirName << " could not be accessed"
-                     << " dataCurrentDirStatus=" << dataCurrentDirStatus;
-        return;
-    }
-
-    // We must perform the staged operation.
-
-    // Check to see if the staged directory contains an uninstall or an install operation.
-    std::string uninstallTombStoneFile(dataStagedDirName);
-    uninstallTombStoneFile += UNINSTALL_TOMBSTONE_FILE_NAME;
-    int uninstallTombStoneFileStatus = checkPath(uninstallTombStoneFile);
-    if (uninstallTombStoneFileStatus != IS_REG && uninstallTombStoneFileStatus != NONE) {
-        // Error case.
-        LOG(WARNING) << "Unable to determine if the staged operation is an uninstall.";
-        return;
-    }
-    if (uninstallTombStoneFileStatus == IS_REG) {
-        handleStagedUninstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
-    } else {
-        // uninstallTombStoneFileStatus == NONE meaning this is a staged install.
-        handleStagedInstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
-    }
-}
-
-/*
- * After a platform update it is likely that timezone data found on the system partition will be
- * newer than the version found in the data partition. This tool detects this case and removes the
- * version in /data.
- *
- * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
- * paths for the metadata and current timezone data must match.
- *
- * Typically on device the two args will be:
- *   /system/usr/share/zoneinfo /data/misc/zoneinfo
- *
- * See usage() for usage notes.
- */
-int main(int argc, char* argv[]) {
-    if (argc != 3) {
-        usage();
-        return 1;
-    }
-
-    const char* systemZoneInfoDir = argv[1];
-    const char* dataZoneInfoDir = argv[2];
-
-    std::string dataStagedDirName(dataZoneInfoDir);
-    dataStagedDirName += STAGED_DIR_NAME;
-
-    std::string dataCurrentDirName(dataZoneInfoDir);
-    dataCurrentDirName += CURRENT_DIR_NAME;
-
-    // Check for an process any staged operation.
-    // If the staged operation could not be handled we still have to validate the current installed
-    // directory so we do not check for errors and do not quit early.
-    processStagedOperation(dataStagedDirName, dataCurrentDirName);
-
-    // Check the distro directory exists. If it does not, exit quickly: nothing to do.
-    PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
-    if (dataCurrentDirStatus == NONE) {
-        LOG(INFO) << "timezone distro dir " << dataCurrentDirName
-                << " does not exist. No action required.";
-        return 0;
-    }
-
-    // If the distro directory path is not a directory or we can't stat() the path, exit with a
-    // warning: either there's a problem accessing storage or the world is not as it should be;
-    // nothing to do.
-    if (dataCurrentDirStatus != IS_DIR) {
-        LOG(WARNING) << "Current distro dir " << dataCurrentDirName
-                << " could not be accessed or is not a directory. result=" << dataCurrentDirStatus;
-        return 2;
-    }
-
-    // Check the installed distro version.
-    std::string distroVersionFileName(dataCurrentDirName);
-    distroVersionFileName += DISTRO_VERSION_FILENAME;
-    std::vector<char> distroVersion;
-    distroVersion.reserve(DISTRO_VERSION_LENGTH);
-    bool distroVersionReadOk =
-            readBytes(distroVersionFileName, distroVersion.data(), DISTRO_VERSION_LENGTH);
-    if (!distroVersionReadOk) {
-        LOG(WARNING) << "distro version file " << distroVersionFileName
-                << " does not exist or is too short. Deleting distro dir.";
-        // Implies the contents of the data partition is corrupt in some way. Try to clean up.
-        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-        deleteUpdateDistroDir(dataCurrentDirName);
-        return 3;
-    }
-
-    if (!checkValidDistroVersion(distroVersion.data())) {
-        LOG(WARNING) << "distro version file " << distroVersionFileName
-                << " is not valid. Deleting distro dir.";
-        // Implies the contents of the data partition is corrupt in some way. Try to clean up.
-        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-        deleteUpdateDistroDir(dataCurrentDirName);
-        return 4;
-    }
-
-    std::string actualDistroVersion =
-            std::string(distroVersion.data(), SUPPORTED_DISTRO_VERSION_LEN);
-    // Check the first 3 bytes of the distro version: these are the major version (e.g. 001).
-    // It must match the one we support exactly to be ok.
-    if (strncmp(
-            &distroVersion[0],
-            SUPPORTED_DISTRO_MAJOR_VERSION,
-            SUPPORTED_DISTRO_MAJOR_VERSION_LEN) != 0) {
-
-        LOG(INFO) << "distro version file " << distroVersionFileName
-                << " major version is not the required version " << SUPPORTED_DISTRO_MAJOR_VERSION
-                << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
-        // This implies there has been an OTA and the installed distro is not compatible with the
-        // new version of Android. Remove the installed distro.
-        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-        deleteUpdateDistroDir(dataCurrentDirName);
-        return 5;
-    }
-
-    // Check the last 3 bytes of the distro version: these are the minor version (e.g. 001).
-    // If the version in the distro is < the minor version required by this device it cannot be
-    // used.
-    if (strncmp(
-            &distroVersion[4],
-            SUPPORTED_DISTRO_MINOR_VERSION,
-            SUPPORTED_DISTRO_MINOR_VERSION_LEN) < 0) {
-
-        LOG(INFO) << "distro version file " << distroVersionFileName
-                << " minor version is not the required version " << SUPPORTED_DISTRO_MINOR_VERSION
-                << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
-        // This implies there has been an OTA and the installed distro is not compatible with the
-        // new version of Android. Remove the installed distro.
-        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-        deleteUpdateDistroDir(dataCurrentDirName);
-        return 5;
-    }
-
-    // Read the system rules version out of the /system tzdata file.
-    std::string systemTzDataFileName(systemZoneInfoDir);
-    systemTzDataFileName += TZDATA_FILENAME;
-    std::vector<char> systemTzDataHeader;
-    systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
-    bool systemFileExists =
-            readBytes(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
-    if (!systemFileExists) {
-        // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
-        LOG(WARNING) << systemTzDataFileName << " does not exist or could not be opened";
-        return 6;
-    }
-    if (!checkValidTzDataHeader(systemTzDataFileName, systemTzDataHeader.data())) {
-        // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
-        LOG(WARNING) << systemTzDataFileName << " does not have a valid header.";
-        return 7;
-    }
-
-    // Compare the distro rules version against the system rules version.
-    if (strncmp(
-            &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
-            &distroVersion[DISTRO_VERSION_RULES_IDX],
-            RULES_VERSION_LEN) <= 0) {
-        LOG(INFO) << "Found an installed distro but it is valid. No action taken.";
-        // Implies there is an installed update, but it is good.
-        return 0;
-    }
-
-    // Implies there has been an OTA and the system version of the timezone rules is now newer
-    // than the version installed in /data. Remove the installed distro.
-    LOG(INFO) << "timezone distro in " << dataCurrentDirName << " is older than data in "
-            << systemTzDataFileName << "; fixing...";
-
-    deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-    deleteUpdateDistroDir(dataCurrentDirName);
-    return 0;
-}