Merge "Add libziparchive-based unzip."
diff --git a/adb/adb.cpp b/adb/adb.cpp
index a7706a0..bfb1144 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1257,6 +1257,10 @@
 void adb_notify_device_scan_complete() {
     {
         std::lock_guard<std::mutex> lock(init_mutex);
+        if (device_scan_complete) {
+            return;
+        }
+
         device_scan_complete = true;
     }
 
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index c1d5549..11c0ec9 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -17,7 +17,10 @@
 #ifndef _ADB_UTILS_H_
 #define _ADB_UTILS_H_
 
+#include <condition_variable>
+#include <mutex>
 #include <string>
+#include <vector>
 
 #include <android-base/macros.h>
 
@@ -53,4 +56,37 @@
 bool forward_targets_are_valid(const std::string& source, const std::string& dest,
                                std::string* error);
 
+// A thread-safe blocking queue.
+template <typename T>
+class BlockingQueue {
+    std::mutex mutex;
+    std::condition_variable cv;
+    std::vector<T> queue;
+
+  public:
+    void Push(const T& t) {
+        {
+            std::unique_lock<std::mutex> lock(mutex);
+            queue.push_back(t);
+        }
+        cv.notify_one();
+    }
+
+    template <typename Fn>
+    void PopAll(Fn fn) {
+        std::vector<T> popped;
+
+        {
+            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);
+        }
+    }
+};
+
 #endif
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 9477c56..e7f44c6 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -37,6 +37,7 @@
 #include <android-base/strings.h>
 
 #include "adb.h"
+#include "adb_utils.h"
 #include "transport.h"
 #include "usb.h"
 
@@ -179,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) {
@@ -376,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 << "'";
 }
 
@@ -389,20 +387,15 @@
     // 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.
-    ++connecting_devices;
+    // 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);
-        --connecting_devices;
+        if (--connecting_devices == 0) {
+            adb_notify_device_scan_complete();
+        }
     });
     thread.detach();
 #else
@@ -420,22 +413,40 @@
         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);
+    }
+}
+
+static auto& hotplug_queue = *new BlockingQueue<std::pair<libusb_hotplug_event, libusb_device*>>();
+static void hotplug_thread() {
+    adb_thread_setname("libusb hotplug");
+    while (true) {
+        hotplug_queue.PopAll([](std::pair<libusb_hotplug_event, libusb_device*> pair) {
+            libusb_hotplug_event event = pair.first;
+            libusb_device* device = pair.second;
+            if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+                device_connected(device);
+            } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
+                device_disconnected(device);
+            }
+        });
     }
 }
 
 static int hotplug_callback(libusb_context*, libusb_device* device, libusb_hotplug_event event,
                             void*) {
-    // We're called with the libusb lock taken. Call these on the main thread outside of this
+    // We're called with the libusb lock taken. Call these on a separate thread outside of this
     // function so that the usb_handle mutex is always taken before the libusb mutex.
-    fdevent_run_on_main_thread([device, event]() {
-        if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
-            device_connected(device);
-        } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
-            device_disconnected(device);
-        }
-    });
+    static std::once_flag once;
+    std::call_once(once, []() { std::thread(hotplug_thread).detach(); });
+
+    if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+        ++connecting_devices;
+    }
+    hotplug_queue.Push({event, device});
     return 0;
 }
 
@@ -457,13 +468,6 @@
         LOG(FATAL) << "failed to register libusb hotplug callback";
     }
 
-    // Wait for all of the connecting devices to finish.
-    while (connecting_devices != 0) {
-        std::this_thread::sleep_for(10ms);
-    }
-
-    adb_notify_device_scan_complete();
-
     // Spawn a thread for libusb_handle_events.
     std::thread([]() {
         adb_thread_setname("libusb");
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/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index c357fd6..cb7cbbe 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -28,7 +28,9 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 
@@ -117,6 +119,20 @@
     return false;
   }
 
+  std::string pipe_size_str;
+  int pipe_buffer_size = 1024 * 1024;
+  if (android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
+    pipe_size_str = android::base::Trim(pipe_size_str);
+
+    if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
+      LOG(FATAL) << "failed to parse pipe max size '" << pipe_size_str << "'";
+    }
+  }
+
+  if (fcntl(pipe_read.get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
+    PLOG(ERROR) << "failed to set pipe buffer size";
+  }
+
   if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
     PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
     return false;
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index cc78242..df7201d 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -41,8 +41,12 @@
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include <log/log.h>
+#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"
@@ -99,8 +103,10 @@
   return true;
 }
 
-static bool activity_manager_notify(int pid, int signal, const std::string& amfd_data) {
-  android::base::unique_fd amfd(socket_local_client("/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
+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) {
     PLOG(ERROR) << "unable to connect to activity manager";
     return false;
@@ -174,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;
@@ -192,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;
@@ -207,9 +216,14 @@
   action.sa_handler = signal_handler;
   debuggerd_register_handlers(&action);
 
+  sigset_t mask;
+  sigemptyset(&mask);
+  if (sigprocmask(SIG_SETMASK, &mask, nullptr) != 0) {
+    PLOG(FATAL) << "failed to set signal mask";
+  }
+
   if (argc != 4) {
     LOG(FATAL) << "Wrong number of args: " << argc << " (expected 4)";
-    return 1;
   }
 
   pid_t main_tid;
@@ -254,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();
@@ -263,47 +279,60 @@
     exit(0);
   }
 
+  ATRACE_NAME("after reparent");
+
   // Die if we take too long.
-  alarm(20);
+  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));
@@ -311,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.
@@ -342,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;
@@ -366,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);
   }
@@ -408,7 +445,10 @@
   }
 
   if (fatal_signal) {
-    activity_manager_notify(target, signo, amfd_data);
+    // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout.
+    if (target_info.name != "system_server" || target_info.uid != AID_SYSTEM) {
+      activity_manager_notify(target, signo, amfd_data);
+    }
   }
 
   // Close stdout before we notify tombstoned of completion.
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 8c00b76..da8ad37 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -89,7 +89,7 @@
   } while (0)
 
 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) {
@@ -119,6 +119,8 @@
     FAIL() << "failed to set pipe size: " << strerror(errno);
   }
 
+  ASSERT_GE(pipe_buffer_size, 1024 * 1024);
+
   if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
     FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
   }
@@ -134,7 +136,7 @@
            << ", received " << rc;
   }
 
-  ASSERT_EQ(InterceptStatus::kRegistered, response.status);
+  *status = response.status;
 }
 
 class CrasherTest : public ::testing::Test {
@@ -178,7 +180,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) {
@@ -596,7 +600,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;
@@ -628,7 +634,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,
@@ -659,7 +667,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;
@@ -683,3 +693,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.mk b/demangle/Android.mk
new file mode 100644
index 0000000..e3cfc2a
--- /dev/null
+++ b/demangle/Android.mk
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := demangle_fuzzer
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+    Demangler.cpp \
+    demangle_fuzzer.cpp \
+
+LOCAL_CFLAGS := \
+    -Wall \
+    -Werror \
+    -Wextra \
+
+include $(BUILD_FUZZ_TEST)
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index fb68119..f56a9be 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -22,7 +22,14 @@
 
 #include "Demangler.h"
 
-TEST(DemangleTest, VoidArgumentTest) {
+TEST(DemangleTest, IllegalArgumentModifiers) {
+  Demangler demangler;
+
+  ASSERT_EQ("_Zpp4FUNKK", demangler.Parse("_Zpp4FUNKK"));
+  ASSERT_EQ("_Zpp4FUNVV", demangler.Parse("_Zpp4FUNVV"));
+}
+
+TEST(DemangleTest, VoidArgument) {
   Demangler demangler;
 
   ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
@@ -189,6 +196,14 @@
   ASSERT_EQ("value(one, signed char)", demangler.Parse("_Z5value3onea"));
 }
 
+TEST(DemangleTest, FunctionStartsWithLPlusNumber) {
+  Demangler demangler;
+
+  ASSERT_EQ("value(char, int)", demangler.Parse("_ZL5valueci"));
+  ASSERT_EQ("abcdefjklmn(signed char)", demangler.Parse("_ZL11abcdefjklmna"));
+  ASSERT_EQ("value(one, signed char)", demangler.Parse("_ZL5value3onea"));
+}
+
 TEST(DemangleTest, StdTypes) {
   Demangler demangler;
 
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index 77cfd3b..c0a96aa 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -542,9 +542,8 @@
     } else {
       suffix = " volatile";
     }
-    if (name[-1] == 'K' || name[-1] == 'V') {
+    if (!cur_state_.suffixes.empty() && (name[-1] == 'K' || name[-1] == 'V')) {
       // Special case, const/volatile apply as a single entity.
-      assert(!cur_state_.suffixes.empty());
       size_t index = cur_state_.suffixes.size();
       cur_state_.suffixes[index-1].insert(0, suffix);
     } else {
@@ -699,6 +698,8 @@
 
   if (std::isdigit(*name)) {
     name = GetStringFromLength(name, &function_name_);
+  } else if (*name == 'L' && std::isdigit(name[1])) {
+    name = GetStringFromLength(name + 1, &function_name_);
   } else {
     name = AppendOperatorString(name);
     function_name_ = cur_state_.str;
@@ -723,7 +724,8 @@
       && static_cast<size_t>(cur_name - name) < max_length) {
     cur_name = (this->*parse_func_)(cur_name);
   }
-  if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty()) {
+  if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty() ||
+      !cur_state_.suffixes.empty()) {
     return name;
   }
 
diff --git a/demangle/demangle_fuzzer.cpp b/demangle/demangle_fuzzer.cpp
new file mode 100644
index 0000000..83fafc2
--- /dev/null
+++ b/demangle/demangle_fuzzer.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include "Demangler.h"
+
+extern "C" void LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  std::vector<char> data_str(size + 1);
+  memcpy(data_str.data(), data, size);
+  data_str[size] = '\0';
+
+  Demangler demangler;
+  std::string demangled_name = demangler.Parse(data_str.data());
+  if (size != 0 && data_str[0] != '\0' && demangled_name.empty()) {
+    abort();
+  }
+}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3e890c7..271ca95 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -53,6 +53,7 @@
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
@@ -1350,7 +1351,8 @@
     struct fastboot_buffer buf;
     const char* errMsg = nullptr;
     const struct fs_generator* gen = nullptr;
-    int fd;
+    TemporaryFile output;
+    unique_fd fd;
 
     unsigned int limit = INT_MAX;
     if (target_sparse_limit > 0 && target_sparse_limit < limit) {
@@ -1403,22 +1405,23 @@
         return;
     }
 
-    fd = make_temporary_fd();
-    if (fd == -1) return;
-
     unsigned eraseBlkSize, logicalBlkSize;
     eraseBlkSize = fb_get_flash_block_size(transport, "erase-block-size");
     logicalBlkSize = fb_get_flash_block_size(transport, "logical-block-size");
 
-    if (fs_generator_generate(gen, fd, size, initial_dir, eraseBlkSize, logicalBlkSize)) {
+    if (fs_generator_generate(gen, output.path, size, initial_dir,
+            eraseBlkSize, logicalBlkSize)) {
         fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
-        close(fd);
         return;
     }
 
-    if (!load_buf_fd(transport, fd, &buf)) {
+    fd.reset(open(output.path, O_RDONLY));
+    if (fd == -1) {
+        fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
+        return;
+    }
+    if (!load_buf_fd(transport, fd.release(), &buf)) {
         fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
-        close(fd);
         return;
     }
     flash_buf(partition, &buf);
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 5d9ccfe..99ca7dd 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -4,6 +4,7 @@
 #include "make_f2fs.h"
 
 #include <errno.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -11,12 +12,20 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/unique_fd.h>
 #include <ext4_utils/make_ext4fs.h>
 #include <sparse/sparse.h>
 
-static int generate_ext4_image(int fd, long long partSize, const std::string& initial_dir,
+using android::base::unique_fd;
+
+static int generate_ext4_image(const char* fileName, long long partSize, const std::string& initial_dir,
                                        unsigned eraseBlkSize, unsigned logicalBlkSize)
 {
+    unique_fd fd(open(fileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
+    if (fd == -1) {
+        fprintf(stderr, "Unable to open output file for EXT4 filesystem: %s\n", strerror(errno));
+        return -1;
+    }
     if (initial_dir.empty()) {
         make_ext4fs_sparse_fd_align(fd, partSize, NULL, NULL, eraseBlkSize, logicalBlkSize);
     } else {
@@ -27,11 +36,16 @@
 }
 
 #ifdef USE_F2FS
-static int generate_f2fs_image(int fd, long long partSize, const std::string& initial_dir,
+static int generate_f2fs_image(const char* fileName, long long partSize, const std::string& initial_dir,
                                unsigned /* unused */, unsigned /* unused */)
 {
     if (!initial_dir.empty()) {
-        fprintf(stderr, "Unable to set initial directory on F2FS filesystem\n");
+        fprintf(stderr, "Unable to set initial directory on F2FS filesystem: %s\n", strerror(errno));
+        return -1;
+    }
+    unique_fd fd(open(fileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
+    if (fd == -1) {
+        fprintf(stderr, "Unable to open output file for F2FS filesystem: %s\n", strerror(errno));
         return -1;
     }
     return make_f2fs_sparse_fd(fd, partSize, NULL, NULL);
@@ -42,7 +56,7 @@
     const char* fs_type;  //must match what fastboot reports for partition type
 
     //returns 0 or error value
-    int (*generate)(int fd, long long partSize, const std::string& initial_dir,
+    int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir,
                     unsigned eraseBlkSize, unsigned logicalBlkSize);
 
 } generators[] = {
@@ -61,8 +75,8 @@
     return nullptr;
 }
 
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
     const std::string& initial_dir, unsigned eraseBlkSize, unsigned logicalBlkSize)
 {
-    return gen->generate(tmpFileNo, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
+    return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
 }
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 0a5f5a4..c6baa7f 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -7,7 +7,7 @@
 struct fs_generator;
 
 const struct fs_generator* fs_get_generator(const std::string& fs_type);
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
     const std::string& initial_dir, unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0);
 
 #endif
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 73bdc7a..84981bf 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1195,7 +1195,7 @@
                 ret = -1;
                 continue;
             }
-            fprintf(zram_fp, "%d\n", fstab->recs[i].zram_size);
+            fprintf(zram_fp, "%u\n", fstab->recs[i].zram_size);
             fclose(zram_fp);
         }
 
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/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/devices.cpp b/init/devices.cpp
index 40f9740..c52d8f8 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -175,7 +175,7 @@
         if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
     }
 
-    if (access(path.c_str(), F_OK) == 0) {
+    if (!skip_restorecon_ && access(path.c_str(), F_OK) == 0) {
         LOG(VERBOSE) << "restorecon_recursive: " << path;
         if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
             PLOG(ERROR) << "selinux_android_restorecon(" << path << ") failed";
@@ -467,12 +467,13 @@
 
 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
                              std::vector<SysfsPermissions> sysfs_permissions,
-                             std::vector<Subsystem> subsystems)
+                             std::vector<Subsystem> subsystems, bool skip_restorecon)
     : dev_permissions_(std::move(dev_permissions)),
       sysfs_permissions_(std::move(sysfs_permissions)),
       subsystems_(std::move(subsystems)),
-      sehandle_(selinux_android_file_context_handle()) {}
+      sehandle_(selinux_android_file_context_handle()),
+      skip_restorecon_(skip_restorecon) {}
 
 DeviceHandler::DeviceHandler()
     : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
-                    std::vector<Subsystem>{}) {}
+                    std::vector<Subsystem>{}, false) {}
diff --git a/init/devices.h b/init/devices.h
index 50f49fc..09a0ce3 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -114,14 +114,21 @@
     DeviceHandler();
     DeviceHandler(std::vector<Permissions> dev_permissions,
                   std::vector<SysfsPermissions> sysfs_permissions,
-                  std::vector<Subsystem> subsystems);
+                  std::vector<Subsystem> subsystems, bool skip_restorecon);
     ~DeviceHandler(){};
 
     void HandleDeviceEvent(const Uevent& uevent);
+
+    void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
+
+    void HandlePlatformDeviceEvent(const Uevent& uevent);
+    void HandleBlockDeviceEvent(const Uevent& uevent) const;
+    void HandleGenericDeviceEvent(const Uevent& uevent) const;
+
     std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
+    void set_skip_restorecon(bool value) { skip_restorecon_ = value; }
 
   private:
-    void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
     std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
         const std::string& path, const std::vector<std::string>& links) const;
     void MakeDevice(const std::string& path, int block, int major, int minor,
@@ -129,15 +136,13 @@
     std::vector<std::string> GetCharacterDeviceSymlinks(const Uevent& uevent) const;
     void HandleDevice(const std::string& action, const std::string& devpath, int block, int major,
                       int minor, const std::vector<std::string>& links) const;
-    void HandlePlatformDeviceEvent(const Uevent& uevent);
-    void HandleBlockDeviceEvent(const Uevent& uevent) const;
-    void HandleGenericDeviceEvent(const Uevent& uevent) const;
 
     std::vector<Permissions> dev_permissions_;
     std::vector<SysfsPermissions> sysfs_permissions_;
     std::vector<Subsystem> subsystems_;
     PlatformDeviceList platform_devices_;
     selabel_handle* sehandle_;
+    bool skip_restorecon_;
 };
 
 // Exposed for testing
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 1471aeb..844c605 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -18,6 +18,7 @@
 
 #include <fcntl.h>
 #include <sys/sendfile.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #include <string>
@@ -103,14 +104,29 @@
     if (uevent.subsystem != "firmware" || uevent.action != "add") return;
 
     // Loading the firmware in a child means we can do that in parallel...
-    // (We ignore SIGCHLD rather than wait for our children.)
+    // We double fork instead of waiting for these processes.
     pid_t pid = fork();
-    if (pid == 0) {
-        Timer t;
-        ProcessFirmwareEvent(uevent);
-        LOG(INFO) << "loading " << uevent.path << " took " << t;
-        _exit(EXIT_SUCCESS);
-    } else if (pid == -1) {
+    if (pid == -1) {
         PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
+        return;
     }
+
+    if (pid == 0) {
+        pid = fork();
+        if (pid == -1) {
+            PLOG(ERROR) << "could not fork a sceond time to process firmware event for "
+                        << uevent.firmware;
+            _exit(EXIT_FAILURE);
+        }
+        if (pid == 0) {
+            Timer t;
+            ProcessFirmwareEvent(uevent);
+            LOG(INFO) << "loading " << uevent.path << " took " << t;
+            _exit(EXIT_SUCCESS);
+        }
+
+        _exit(EXIT_SUCCESS);
+    }
+
+    waitpid(pid, nullptr, 0);
 }
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/service.cpp b/init/service.cpp
index 7c931da..1a6474b 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.
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 27c5d23..01b8250 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -165,7 +165,7 @@
     return RegenerateUeventsForDir(d.get(), callback);
 }
 
-static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
+const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
 
 void UeventListener::RegenerateUevents(RegenerateCallback callback) const {
     for (const auto path : kRegenerationPaths) {
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
index ba31aaa..8e6f3b4 100644
--- a/init/uevent_listener.h
+++ b/init/uevent_listener.h
@@ -35,6 +35,8 @@
 using RegenerateCallback = std::function<RegenerationAction(const Uevent&)>;
 using PollCallback = std::function<void(const Uevent&)>;
 
+extern const char* kRegenerationPaths[3];
+
 class UeventListener {
   public:
     UeventListener();
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index bd21a3e..31e4106 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -22,10 +22,15 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/wait.h>
+
+#include <set>
+#include <thread>
 
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <selinux/android.h>
 #include <selinux/selinux.h>
 
 #include "devices.h"
@@ -35,6 +40,194 @@
 #include "ueventd_parser.h"
 #include "util.h"
 
+// At a high level, ueventd listens for uevent messages generated by the kernel through a netlink
+// socket.  When ueventd receives such a message it handles it by taking appropriate actions,
+// which can typically be creating a device node in /dev, setting file permissions, setting selinux
+// labels, etc.
+// Ueventd also handles loading of firmware that the kernel requests, and creates symlinks for block
+// and character devices.
+
+// When ueventd starts, it regenerates uevents for all currently registered devices by traversing
+// /sys and writing 'add' to each 'uevent' file that it finds.  This causes the kernel to generate
+// and resend uevent messages for all of the currently registered devices.  This is done, because
+// ueventd would not have been running when these devices were registered and therefore was unable
+// to receive their uevent messages and handle them appropriately.  This process is known as
+// 'cold boot'.
+
+// 'init' currently waits synchronously on the cold boot process of ueventd before it continues
+// its boot process.  For this reason, cold boot should be as quick as possible.  One way to achieve
+// a speed up here is to parallelize the handling of ueventd messages, which consume the bulk of the
+// time during cold boot.
+
+// Handling of uevent messages has two unique properties:
+// 1) It can be done in isolation; it doesn't need to read or write any status once it is started.
+// 2) It uses setegid() and setfscreatecon() so either care (aka locking) must be taken to ensure
+//    that no file system operations are done while the uevent process has an abnormal egid or
+//    fscreatecon or this handling must happen in a separate process.
+// Given the above two properties, it is best to fork() subprocesses to handle the uevents.  This
+// reduces the overhead and complexity that would be required in a solution with threads and locks.
+// In testing, a racy multithreaded solution has the same performance as the fork() solution, so
+// there is no reason to deal with the complexity of the former.
+
+// One other important caveat during the boot process is the handling of SELinux restorecon.
+// Since many devices have child devices, calling selinux_android_restorecon() recursively for each
+// device when its uevent is handled, results in multiple restorecon operations being done on a
+// given file.  It is more efficient to simply do restorecon recursively on /sys during cold boot,
+// than to do restorecon on each device as its uevent is handled.  This only applies to cold boot;
+// once that has completed, restorecon is done for each device as its uevent is handled.
+
+// With all of the above considered, the cold boot process has the below steps:
+// 1) ueventd regenerates uevents by doing the /sys traversal and listens to the netlink socket for
+//    the generated uevents.  It writes these uevents into a queue represented by a vector.
+//
+// 2) ueventd forks 'n' separate uevent handler subprocesses and has each of them to handle the
+//    uevents in the queue based on a starting offset (their process number) and a stride (the total
+//    number of processes).  Note that no IPC happens at this point and only const functions from
+//    DeviceHandler should be called from this context.
+//
+// 3) In parallel to the subprocesses handling the uevents, the main thread of ueventd calls
+//    selinux_android_restorecon() recursively on /sys/class, /sys/block, and /sys/devices.
+//
+// 4) Once the restorecon operation finishes, the main thread calls waitpid() to wait for all
+//    subprocess handlers to complete and exit.  Once this happens, it marks coldboot as having
+//    completed.
+//
+// At this point, ueventd is single threaded, poll()'s and then handles any future uevents.
+
+// Lastly, it should be noted that uevents that occur during the coldboot process are handled
+// without issue after the coldboot process completes.  This is because the uevent listener is
+// paused while the uevent handler and restorecon actions take place.  Once coldboot completes,
+// the uevent listener resumes in polling mode and will handle the uevents that occurred during
+// coldboot.
+
+class ColdBoot {
+  public:
+    ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler)
+        : uevent_listener_(uevent_listener),
+          device_handler_(device_handler),
+          num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
+
+    void Run();
+
+  private:
+    void UeventHandlerMain(unsigned int process_num, unsigned int total_processes);
+    void RegenerateUevents();
+    void ForkSubProcesses();
+    void DoRestoreCon();
+    void WaitForSubProcesses();
+
+    UeventListener& uevent_listener_;
+    DeviceHandler& device_handler_;
+
+    unsigned int num_handler_subprocesses_;
+    std::vector<Uevent> uevent_queue_;
+
+    std::set<pid_t> subprocess_pids_;
+};
+
+void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
+    for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {
+        auto& uevent = uevent_queue_[i];
+        if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
+            device_handler_.FixupSysPermissions(uevent.path, uevent.subsystem);
+        }
+
+        if (uevent.subsystem == "block") {
+            device_handler_.HandleBlockDeviceEvent(uevent);
+        } else {
+            device_handler_.HandleGenericDeviceEvent(uevent);
+        }
+    }
+    _exit(EXIT_SUCCESS);
+}
+
+void ColdBoot::RegenerateUevents() {
+    uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
+        HandleFirmwareEvent(uevent);
+
+        // This is the one mutable part of DeviceHandler, in which platform devices are
+        // added to a vector for later reference.  Since there is no communication after
+        // fork()'ing subprocess handlers, all platform devices must be in the vector before
+        // we fork, and therefore they must be handled in this loop.
+        if (uevent.subsystem == "platform") {
+            device_handler_.HandlePlatformDeviceEvent(uevent);
+        }
+
+        uevent_queue_.emplace_back(std::move(uevent));
+        return RegenerationAction::kContinue;
+    });
+}
+
+void ColdBoot::ForkSubProcesses() {
+    for (unsigned int i = 0; i < num_handler_subprocesses_; ++i) {
+        auto pid = fork();
+        if (pid < 0) {
+            PLOG(FATAL) << "fork() failed!";
+        }
+
+        if (pid == 0) {
+            UeventHandlerMain(i, num_handler_subprocesses_);
+        }
+
+        subprocess_pids_.emplace(pid);
+    }
+}
+
+void ColdBoot::DoRestoreCon() {
+    for (const char* path : kRegenerationPaths) {
+        selinux_android_restorecon(path, SELINUX_ANDROID_RESTORECON_RECURSE);
+    }
+    device_handler_.set_skip_restorecon(false);
+}
+
+void ColdBoot::WaitForSubProcesses() {
+    // Treat subprocesses that crash or get stuck the same as if ueventd itself has crashed or gets
+    // stuck.
+    //
+    // When a subprocess crashes, we fatally abort from ueventd.  init will restart ueventd when
+    // init reaps it, and the cold boot process will start again.  If this continues to fail, then
+    // since ueventd is marked as a critical service, init will reboot to recovery.
+    //
+    // When a subprocess gets stuck, keep ueventd spinning waiting for it.  init has a timeout for
+    // cold boot and will reboot to the bootloader if ueventd does not complete in time.
+    while (!subprocess_pids_.empty()) {
+        int status;
+        pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, 0));
+        if (pid == -1) {
+            PLOG(ERROR) << "waitpid() failed";
+            continue;
+        }
+
+        auto it = std::find(subprocess_pids_.begin(), subprocess_pids_.end(), pid);
+        if (it == subprocess_pids_.end()) continue;
+
+        if (WIFEXITED(status)) {
+            if (WEXITSTATUS(status) == EXIT_SUCCESS) {
+                subprocess_pids_.erase(it);
+            } else {
+                LOG(FATAL) << "subprocess exited with status " << WEXITSTATUS(status);
+            }
+        } else if (WIFSIGNALED(status)) {
+            LOG(FATAL) << "subprocess killed by signal " << WTERMSIG(status);
+        }
+    }
+}
+
+void ColdBoot::Run() {
+    Timer cold_boot_timer;
+
+    RegenerateUevents();
+
+    ForkSubProcesses();
+
+    DoRestoreCon();
+
+    WaitForSubProcesses();
+
+    close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+    LOG(INFO) << "Coldboot took " << cold_boot_timer;
+}
+
 DeviceHandler CreateDeviceHandler() {
     Parser parser;
 
@@ -64,11 +257,10 @@
     parser.ParseConfig("/ueventd." + hardware + ".rc");
 
     return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
-                         std::move(subsystems));
+                         std::move(subsystems), true);
 }
 
-int ueventd_main(int argc, char **argv)
-{
+int ueventd_main(int argc, char** argv) {
     /*
      * init sets the umask to 077 for forked processes. We need to
      * create files with exact permissions, without modification by
@@ -76,13 +268,6 @@
      */
     umask(000);
 
-    /* Prevent fire-and-forget children from becoming zombies.
-     * If we should need to wait() for some children in the future
-     * (as opposed to none right now), double-forking here instead
-     * of ignoring SIGCHLD may be the better solution.
-     */
-    signal(SIGCHLD, SIG_IGN);
-
     InitKernelLogging(argv);
 
     LOG(INFO) << "ueventd started!";
@@ -95,16 +280,8 @@
     UeventListener uevent_listener;
 
     if (access(COLDBOOT_DONE, F_OK) != 0) {
-        Timer t;
-
-        uevent_listener.RegenerateUevents([&device_handler](const Uevent& uevent) {
-            HandleFirmwareEvent(uevent);
-            device_handler.HandleDeviceEvent(uevent);
-            return RegenerationAction::kContinue;
-        });
-
-        close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
-        LOG(INFO) << "Coldboot took " << t;
+        ColdBoot cold_boot(uevent_listener, device_handler);
+        cold_boot.Run();
     }
 
     uevent_listener.DoPolling([&device_handler](const Uevent& uevent) {
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 7dd9227..a643a29 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -113,6 +113,7 @@
             static_libs: ["libasync_safe", "libcutils"],
         },
     },
+    whole_static_libs: ["libdemangle"],
 }
 
 cc_library_shared {
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 3545661..e46d353 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -27,6 +27,8 @@
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
+#include <demangle.h>
+
 #include "BacktraceLog.h"
 #include "thread_utils.h"
 #include "UnwindCurrent.h"
@@ -62,8 +64,7 @@
   if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
     return "";
   }
-  std::string func_name = GetFunctionNameRaw(pc, offset);
-  return func_name;
+  return demangle(GetFunctionNameRaw(pc, offset).c_str());
 }
 
 bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) {
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/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index 057be5d..bb1ce34 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -177,6 +177,12 @@
     return *this;
   }
 
+  android_log_event_list& operator<<(bool value) {
+    int retval = android_log_write_int32(ctx, value ? 1 : 0);
+    if (retval < 0) ret = retval;
+    return *this;
+  }
+
   android_log_event_list& operator<<(int64_t value) {
     int retval = android_log_write_int64(ctx, value);
     if (retval < 0) ret = retval;
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 32ed6c3..eb2b902 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -50,6 +50,7 @@
         "DwarfCfa.cpp",
         "DwarfMemory.cpp",
         "DwarfOp.cpp",
+        "DwarfSection.cpp",
         "Elf.cpp",
         "ElfInterface.cpp",
         "ElfInterfaceArm.cpp",
@@ -98,6 +99,8 @@
         "tests/DwarfMemoryTest.cpp",
         "tests/DwarfOpLogTest.cpp",
         "tests/DwarfOpTest.cpp",
+        "tests/DwarfSectionTest.cpp",
+        "tests/DwarfSectionImplTest.cpp",
         "tests/ElfInterfaceArmTest.cpp",
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
@@ -123,6 +126,10 @@
         "liblog",
     ],
 
+    static_libs: [
+        "libgmock",
+    ],
+
     target: {
         linux: {
             host_ldlibs: [
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index ce7da4a..42ebae1 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -63,7 +63,7 @@
   typedef typename std::make_signed<AddressType>::type SignedType;
 
  public:
-  DwarfCfa(DwarfMemory* memory, const DwarfFDE* fde) : memory_(memory), fde_(fde) {}
+  DwarfCfa(DwarfMemory* memory, const DwarfFde* fde) : memory_(memory), fde_(fde) {}
   virtual ~DwarfCfa() = default;
 
   bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
@@ -88,7 +88,7 @@
  private:
   DwarfError last_error_;
   DwarfMemory* memory_;
-  const DwarfFDE* fde_;
+  const DwarfFde* fde_;
 
   AddressType cur_pc_;
   const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h
index 824c307..c00f17d 100644
--- a/libunwindstack/DwarfError.h
+++ b/libunwindstack/DwarfError.h
@@ -27,6 +27,8 @@
   DWARF_ERROR_STACK_INDEX_NOT_VALID,
   DWARF_ERROR_NOT_IMPLEMENTED,
   DWARF_ERROR_TOO_MANY_ITERATIONS,
+  DWARF_ERROR_CFA_NOT_DEFINED,
+  DWARF_ERROR_UNSUPPORTED_VERSION,
 };
 
 #endif  // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index ed6537a..0f4b36d 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -37,7 +37,7 @@
 class DwarfMemory;
 class Memory;
 template <typename AddressType>
-class RegsTmpl;
+class RegsImpl;
 
 template <typename AddressType>
 class DwarfOp {
@@ -67,7 +67,7 @@
   AddressType StackAt(size_t index) { return stack_[index]; }
   size_t StackSize() { return stack_.size(); }
 
-  void set_regs(RegsTmpl<AddressType>* regs) { regs_ = regs; }
+  void set_regs(RegsImpl<AddressType>* regs) { regs_ = regs; }
 
   DwarfError last_error() { return last_error_; }
 
@@ -91,7 +91,7 @@
   DwarfMemory* memory_;
   Memory* regular_memory_;
 
-  RegsTmpl<AddressType>* regs_;
+  RegsImpl<AddressType>* regs_;
   bool is_register_ = false;
   DwarfError last_error_ = DWARF_ERROR_NONE;
   uint8_t cur_op_;
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
new file mode 100644
index 0000000..4c98aa3
--- /dev/null
+++ b/libunwindstack/DwarfSection.cpp
@@ -0,0 +1,543 @@
+/*
+ * 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 "DwarfCfa.h"
+#include "DwarfError.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "DwarfSection.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+#include "Memory.h"
+#include "Regs.h"
+
+const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
+  uint64_t fde_offset;
+  if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
+    return nullptr;
+  }
+  const DwarfFde* fde = GetFdeFromOffset(fde_offset);
+  // Guaranteed pc >= pc_start, need to check pc in the fde range.
+  if (pc < fde->pc_end) {
+    return fde;
+  }
+  last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+  return nullptr;
+}
+
+bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+  const DwarfFde* fde = GetFdeFromPc(pc);
+  if (fde == nullptr || fde->cie == nullptr) {
+    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    return false;
+  }
+
+  // Now get the location information for this pc.
+  dwarf_loc_regs_t loc_regs;
+  if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+    return false;
+  }
+
+  // Now eval the actual registers.
+  return Eval(fde->cie, process_memory, loc_regs, regs);
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uint8_t version,
+                                                   Memory* regular_memory, AddressType* value) {
+  DwarfOp<AddressType> op(&memory_, regular_memory);
+
+  // Need to evaluate the op data.
+  uint64_t start = loc.values[1];
+  uint64_t end = start + loc.values[0];
+  if (!op.Eval(start, end, version)) {
+    last_error_ = op.last_error();
+    return false;
+  }
+  if (op.StackSize() == 0) {
+    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    return false;
+  }
+  // We don't support an expression that evaluates to a register number.
+  if (op.is_register()) {
+    last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+    return false;
+  }
+  *value = op.StackAt(0);
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
+                                         const dwarf_loc_regs_t& loc_regs, Regs* regs) {
+  RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
+  if (cie->return_address_register >= cur_regs->total_regs()) {
+    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+
+  // Get the cfa value;
+  auto cfa_entry = loc_regs.find(CFA_REG);
+  if (cfa_entry == loc_regs.end()) {
+    last_error_ = DWARF_ERROR_CFA_NOT_DEFINED;
+    return false;
+  }
+
+  AddressType prev_pc = regs->pc();
+  AddressType prev_cfa = regs->sp();
+
+  AddressType cfa;
+  const DwarfLocation* loc = &cfa_entry->second;
+  // Only a few location types are valid for the cfa.
+  switch (loc->type) {
+    case DWARF_LOCATION_REGISTER:
+      if (loc->values[0] >= cur_regs->total_regs()) {
+        last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+        return false;
+      }
+      // If the stack pointer register is the CFA, and the stack
+      // pointer register does not have any associated location
+      // information, use the current cfa value.
+      if (regs->sp_reg() == loc->values[0] && loc_regs.count(regs->sp_reg()) == 0) {
+        cfa = prev_cfa;
+      } else {
+        cfa = (*cur_regs)[loc->values[0]];
+      }
+      cfa += loc->values[1];
+      break;
+    case DWARF_LOCATION_EXPRESSION:
+    case DWARF_LOCATION_VAL_EXPRESSION: {
+      AddressType value;
+      if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
+        return false;
+      }
+      if (loc->type == DWARF_LOCATION_EXPRESSION) {
+        if (!regular_memory->Read(value, &cfa, sizeof(AddressType))) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+      } else {
+        cfa = value;
+      }
+      break;
+    }
+    default:
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+  }
+
+  // This code is not guaranteed to work in cases where a register location
+  // is a double indirection to the actual value. For example, if r3 is set
+  // to r5 + 4, and r5 is set to CFA + 4, then this won't necessarily work
+  // because it does not guarantee that r5 is evaluated before r3.
+  // Check that this case does not exist, and error if it does.
+  bool return_address_undefined = false;
+  for (const auto& entry : loc_regs) {
+    uint16_t reg = entry.first;
+    // Already handled the CFA register.
+    if (reg == CFA_REG) continue;
+
+    if (reg >= cur_regs->total_regs()) {
+      // Skip this unknown register.
+      continue;
+    }
+
+    const DwarfLocation* loc = &entry.second;
+    switch (loc->type) {
+      case DWARF_LOCATION_OFFSET:
+        if (!regular_memory->Read(cfa + loc->values[0], &(*cur_regs)[reg], sizeof(AddressType))) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+        break;
+      case DWARF_LOCATION_VAL_OFFSET:
+        (*cur_regs)[reg] = cfa + loc->values[0];
+        break;
+      case DWARF_LOCATION_REGISTER: {
+        uint16_t cur_reg = loc->values[0];
+        if (cur_reg >= cur_regs->total_regs()) {
+          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          return false;
+        }
+        if (loc_regs.find(cur_reg) != loc_regs.end()) {
+          // This is a double indirection, a register definition references
+          // another register which is also defined as something other
+          // than a register.
+          log(0,
+              "Invalid indirection: register %d references register %d which is "
+              "not a plain register.\n",
+              reg, cur_reg);
+          last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+          return false;
+        }
+        (*cur_regs)[reg] = (*cur_regs)[cur_reg] + loc->values[1];
+        break;
+      }
+      case DWARF_LOCATION_EXPRESSION:
+      case DWARF_LOCATION_VAL_EXPRESSION: {
+        AddressType value;
+        if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
+          return false;
+        }
+        if (loc->type == DWARF_LOCATION_EXPRESSION) {
+          if (!regular_memory->Read(value, &(*cur_regs)[reg], sizeof(AddressType))) {
+            last_error_ = DWARF_ERROR_MEMORY_INVALID;
+            return false;
+          }
+        } else {
+          (*cur_regs)[reg] = value;
+        }
+        break;
+      }
+      case DWARF_LOCATION_UNDEFINED:
+        if (reg == cie->return_address_register) {
+          return_address_undefined = true;
+        }
+      default:
+        break;
+    }
+  }
+
+  // Find the return address location.
+  if (return_address_undefined) {
+    cur_regs->set_pc(0);
+  } else {
+    cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
+  }
+  cur_regs->set_sp(cfa);
+  // Stop if the cfa and pc are the same.
+  return prev_cfa != cfa || prev_pc != cur_regs->pc();
+}
+
+template <typename AddressType>
+const DwarfCie* DwarfSectionImpl<AddressType>::GetCie(uint64_t offset) {
+  auto cie_entry = cie_entries_.find(offset);
+  if (cie_entry != cie_entries_.end()) {
+    return &cie_entry->second;
+  }
+  DwarfCie* cie = &cie_entries_[offset];
+  memory_.set_cur_offset(offset);
+  if (!FillInCie(cie)) {
+    // Erase the cached entry.
+    cie_entries_.erase(offset);
+    return nullptr;
+  }
+  return cie;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
+  uint32_t length32;
+  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  if (length32 == static_cast<uint32_t>(-1)) {
+    // 64 bit Cie
+    uint64_t length64;
+    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+
+    cie->cfa_instructions_end = memory_.cur_offset() + length64;
+    cie->fde_address_encoding = DW_EH_PE_sdata8;
+
+    uint64_t cie_id;
+    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (!IsCie64(cie_id)) {
+      // This is not a Cie, something has gone horribly wrong.
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+  } else {
+    // 32 bit Cie
+    cie->cfa_instructions_end = memory_.cur_offset() + length32;
+    cie->fde_address_encoding = DW_EH_PE_sdata4;
+
+    uint32_t cie_id;
+    if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (!IsCie32(cie_id)) {
+      // This is not a Cie, something has gone horribly wrong.
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+  }
+
+  if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
+    // Unrecognized version.
+    last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+    return false;
+  }
+
+  // Read the augmentation string.
+  char aug_value;
+  do {
+    if (!memory_.ReadBytes(&aug_value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    cie->augmentation_string.push_back(aug_value);
+  } while (aug_value != '\0');
+
+  if (cie->version == 4) {
+    // Skip the Address Size field since we only use it for validation.
+    memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+    // Segment Size
+    if (!memory_.ReadBytes(&cie->segment_size, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+  }
+
+  // Code Alignment Factor
+  if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  // Data Alignment Factor
+  if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  if (cie->version == 1) {
+    // Return Address is a single byte.
+    uint8_t return_address_register;
+    if (!memory_.ReadBytes(&return_address_register, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    cie->return_address_register = return_address_register;
+  } else if (!memory_.ReadULEB128(&cie->return_address_register)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  if (cie->augmentation_string[0] != 'z') {
+    cie->cfa_instructions_offset = memory_.cur_offset();
+    return true;
+  }
+
+  uint64_t aug_length;
+  if (!memory_.ReadULEB128(&aug_length)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
+
+  for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
+    switch (cie->augmentation_string[i]) {
+      case 'L':
+        if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+        break;
+      case 'P': {
+        uint8_t encoding;
+        if (!memory_.ReadBytes(&encoding, 1)) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+        if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+      } break;
+      case 'R':
+        if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
+          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          return false;
+        }
+        break;
+    }
+  }
+  return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
+  auto fde_entry = fde_entries_.find(offset);
+  if (fde_entry != fde_entries_.end()) {
+    return &fde_entry->second;
+  }
+  DwarfFde* fde = &fde_entries_[offset];
+  memory_.set_cur_offset(offset);
+  if (!FillInFde(fde)) {
+    fde_entries_.erase(offset);
+    return nullptr;
+  }
+  return fde;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
+  uint32_t length32;
+  if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  if (length32 == static_cast<uint32_t>(-1)) {
+    // 64 bit Fde.
+    uint64_t length64;
+    if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    fde->cfa_instructions_end = memory_.cur_offset() + length64;
+
+    uint64_t value64;
+    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (IsCie64(value64)) {
+      // This is a Cie, this means something has gone wrong.
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+
+    // Get the Cie pointer, which is necessary to properly read the rest of
+    // of the Fde information.
+    fde->cie_offset = GetCieOffsetFromFde64(value64);
+  } else {
+    // 32 bit Fde.
+    fde->cfa_instructions_end = memory_.cur_offset() + length32;
+
+    uint32_t value32;
+    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (IsCie32(value32)) {
+      // This is a Cie, this means something has gone wrong.
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+
+    // Get the Cie pointer, which is necessary to properly read the rest of
+    // of the Fde information.
+    fde->cie_offset = GetCieOffsetFromFde32(value32);
+  }
+  uint64_t cur_offset = memory_.cur_offset();
+
+  const DwarfCie* cie = GetCie(fde->cie_offset);
+  if (cie == nullptr) {
+    return false;
+  }
+  fde->cie = cie;
+
+  if (cie->segment_size != 0) {
+    // Skip over the segment selector for now.
+    cur_offset += cie->segment_size;
+  }
+  memory_.set_cur_offset(cur_offset);
+
+  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  fde->pc_start = AdjustPcFromFde(fde->pc_start);
+
+  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_end)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  fde->pc_end += fde->pc_start;
+  if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
+    // Augmentation Size
+    uint64_t aug_length;
+    if (!memory_.ReadULEB128(&aug_length)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    uint64_t cur_offset = memory_.cur_offset();
+
+    if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+
+    // Set our position to after all of the augmentation data.
+    memory_.set_cur_offset(cur_offset + aug_length);
+  }
+  fde->cfa_instructions_offset = memory_.cur_offset();
+
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
+                                                       dwarf_loc_regs_t* loc_regs) {
+  DwarfCfa<AddressType> cfa(&memory_, fde);
+
+  // Look for the cached copy of the cie data.
+  auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
+  if (reg_entry == cie_loc_regs_.end()) {
+    if (!cfa.GetLocationInfo(pc, fde->cie->cfa_instructions_offset, fde->cie->cfa_instructions_end,
+                             loc_regs)) {
+      last_error_ = cfa.last_error();
+      return false;
+    }
+    cie_loc_regs_[fde->cie_offset] = *loc_regs;
+  }
+  cfa.set_cie_loc_regs(&cie_loc_regs_[fde->cie_offset]);
+  if (!cfa.GetLocationInfo(pc, fde->cfa_instructions_offset, fde->cfa_instructions_end, loc_regs)) {
+    last_error_ = cfa.last_error();
+    return false;
+  }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, uint64_t load_bias,
+                                        const DwarfFde* fde) {
+  DwarfCfa<AddressType> cfa(&memory_, fde);
+
+  // Always print the cie information.
+  const DwarfCie* cie = fde->cie;
+  if (!cfa.Log(indent, pc, load_bias, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
+    last_error_ = cfa.last_error();
+    return false;
+  }
+  if (!cfa.Log(indent, pc, load_bias, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
+    last_error_ = cfa.last_error();
+    return false;
+  }
+  return true;
+}
+
+// Explicitly instantiate DwarfSectionImpl
+template class DwarfSectionImpl<uint32_t>;
+template class DwarfSectionImpl<uint64_t>;
diff --git a/libunwindstack/DwarfSection.h b/libunwindstack/DwarfSection.h
new file mode 100644
index 0000000..e617601
--- /dev/null
+++ b/libunwindstack/DwarfSection.h
@@ -0,0 +1,137 @@
+/*
+ * 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_SECTION_H
+#define _LIBUNWINDSTACK_DWARF_SECTION_H
+
+#include <stdint.h>
+
+#include <iterator>
+#include <unordered_map>
+
+#include "DwarfError.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+
+// Forward declarations.
+class Memory;
+class Regs;
+
+class DwarfSection {
+ public:
+  DwarfSection(Memory* memory) : memory_(memory) {}
+  virtual ~DwarfSection() = default;
+
+  class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
+   public:
+    iterator(DwarfSection* section, size_t index) : section_(section), index_(index) {}
+
+    iterator& operator++() {
+      index_++;
+      return *this;
+    }
+    iterator& operator++(int increment) {
+      index_ += increment;
+      return *this;
+    }
+    iterator& operator--() {
+      index_--;
+      return *this;
+    }
+    iterator& operator--(int decrement) {
+      index_ -= decrement;
+      return *this;
+    }
+
+    bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
+    bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
+
+    const DwarfFde* operator*() { return section_->GetFdeFromIndex(index_); }
+
+   private:
+    DwarfSection* section_ = nullptr;
+    size_t index_ = 0;
+  };
+
+  iterator begin() { return iterator(this, 0); }
+  iterator end() { return iterator(this, fde_count_); }
+
+  DwarfError last_error() { return last_error_; }
+
+  virtual bool Init(uint64_t offset, uint64_t size) = 0;
+
+  virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*) = 0;
+
+  virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
+
+  virtual bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) = 0;
+
+  virtual const DwarfFde* GetFdeFromIndex(size_t index) = 0;
+
+  const DwarfFde* GetFdeFromPc(uint64_t pc);
+
+  virtual const DwarfFde* GetFdeFromOffset(uint64_t fde_offset) = 0;
+
+  virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
+
+  virtual bool IsCie32(uint32_t value32) = 0;
+
+  virtual bool IsCie64(uint64_t value64) = 0;
+
+  virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
+
+  virtual uint64_t GetCieOffsetFromFde64(uint64_t pointer) = 0;
+
+  virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
+
+  bool Step(uint64_t pc, Regs* regs, Memory* process_memory);
+
+ protected:
+  DwarfMemory memory_;
+  DwarfError last_error_ = DWARF_ERROR_NONE;
+
+  uint64_t fde_count_;
+  std::unordered_map<uint64_t, DwarfFde> fde_entries_;
+  std::unordered_map<uint64_t, DwarfCie> cie_entries_;
+  std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
+};
+
+template <typename AddressType>
+class DwarfSectionImpl : public DwarfSection {
+ public:
+  DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
+  virtual ~DwarfSectionImpl() = default;
+
+  bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
+            Regs* regs) override;
+
+  const DwarfCie* GetCie(uint64_t offset);
+  bool FillInCie(DwarfCie* cie);
+
+  const DwarfFde* GetFdeFromOffset(uint64_t offset) override;
+  bool FillInFde(DwarfFde* fde);
+
+  bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
+
+  bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) override;
+
+ protected:
+  bool EvalExpression(const DwarfLocation& loc, uint8_t version, Memory* regular_memory,
+                      AddressType* value);
+};
+
+#endif  // _LIBUNWINDSTACK_DWARF_SECTION_H
diff --git a/libunwindstack/DwarfStructs.h b/libunwindstack/DwarfStructs.h
index 57aac88..87182c9 100644
--- a/libunwindstack/DwarfStructs.h
+++ b/libunwindstack/DwarfStructs.h
@@ -23,7 +23,7 @@
 
 #include "DwarfEncoding.h"
 
-struct DwarfCIE {
+struct DwarfCie {
   uint8_t version = 0;
   uint8_t fde_address_encoding = DW_EH_PE_absptr;
   uint8_t lsda_encoding = DW_EH_PE_omit;
@@ -37,14 +37,14 @@
   uint64_t return_address_register = 0;
 };
 
-struct DwarfFDE {
+struct DwarfFde {
   uint64_t cie_offset = 0;
   uint64_t cfa_instructions_offset = 0;
   uint64_t cfa_instructions_end = 0;
   uint64_t pc_start = 0;
   uint64_t pc_end = 0;
   uint64_t lsda_address = 0;
-  const DwarfCIE* cie = nullptr;
+  const DwarfCie* cie = nullptr;
 };
 
 constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index adb6522..e7d10b2 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -30,7 +30,7 @@
 #include "User.h"
 
 template <typename AddressType>
-uint64_t RegsTmpl<AddressType>::GetRelPc(Elf* elf, const MapInfo* map_info) {
+uint64_t RegsImpl<AddressType>::GetRelPc(Elf* elf, const MapInfo* map_info) {
   uint64_t load_bias = 0;
   if (elf->valid()) {
     load_bias = elf->interface()->load_bias();
@@ -40,7 +40,7 @@
 }
 
 template <typename AddressType>
-bool RegsTmpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
+bool RegsImpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
   switch (return_loc_.type) {
   case LOCATION_REGISTER:
     assert(return_loc_.value < total_regs_);
@@ -59,9 +59,8 @@
   }
 }
 
-RegsArm::RegsArm() : RegsTmpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP,
-                                        Location(LOCATION_REGISTER, ARM_REG_LR)) {
-}
+RegsArm::RegsArm()
+    : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
 
 uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
@@ -89,9 +88,8 @@
   return rel_pc - 4;
 }
 
-RegsArm64::RegsArm64() : RegsTmpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP,
-                                            Location(LOCATION_REGISTER, ARM64_REG_LR)) {
-}
+RegsArm64::RegsArm64()
+    : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
 
 uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
@@ -104,9 +102,8 @@
   return rel_pc - 4;
 }
 
-RegsX86::RegsX86() : RegsTmpl<uint32_t>(X86_REG_LAST, X86_REG_SP,
-                                        Location(LOCATION_SP_OFFSET, -4)) {
-}
+RegsX86::RegsX86()
+    : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
 
 uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
@@ -119,9 +116,8 @@
   return rel_pc - 1;
 }
 
-RegsX86_64::RegsX86_64() : RegsTmpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP,
-                                              Location(LOCATION_SP_OFFSET, -8)) {
-}
+RegsX86_64::RegsX86_64()
+    : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
 
 uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
diff --git a/libunwindstack/Regs.h b/libunwindstack/Regs.h
index 718fc85..8f5a721 100644
--- a/libunwindstack/Regs.h
+++ b/libunwindstack/Regs.h
@@ -66,11 +66,11 @@
 };
 
 template <typename AddressType>
-class RegsTmpl : public Regs {
+class RegsImpl : public Regs {
  public:
-  RegsTmpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc)
+  RegsImpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc)
       : Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {}
-  virtual ~RegsTmpl() = default;
+  virtual ~RegsImpl() = default;
 
   uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) override;
 
@@ -92,7 +92,7 @@
   std::vector<AddressType> regs_;
 };
 
-class RegsArm : public RegsTmpl<uint32_t> {
+class RegsArm : public RegsImpl<uint32_t> {
  public:
   RegsArm();
   virtual ~RegsArm() = default;
@@ -100,7 +100,7 @@
   uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
 };
 
-class RegsArm64 : public RegsTmpl<uint64_t> {
+class RegsArm64 : public RegsImpl<uint64_t> {
  public:
   RegsArm64();
   virtual ~RegsArm64() = default;
@@ -108,7 +108,7 @@
   uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
 };
 
-class RegsX86 : public RegsTmpl<uint32_t> {
+class RegsX86 : public RegsImpl<uint32_t> {
  public:
   RegsX86();
   virtual ~RegsX86() = default;
@@ -116,7 +116,7 @@
   uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
 };
 
-class RegsX86_64 : public RegsTmpl<uint64_t> {
+class RegsX86_64 : public RegsImpl<uint64_t> {
  public:
   RegsX86_64();
   virtual ~RegsX86_64() = default;
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index 3185bc3..967c2c2 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -60,8 +60,8 @@
   MemoryFake memory_;
   std::unique_ptr<DwarfMemory> dmem_;
   std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
-  DwarfCIE cie_;
-  DwarfFDE fde_;
+  DwarfCie cie_;
+  DwarfFde fde_;
 };
 TYPED_TEST_CASE_P(DwarfCfaLogTest);
 
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 6cf028a..687af7d 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -57,8 +57,8 @@
   MemoryFake memory_;
   std::unique_ptr<DwarfMemory> dmem_;
   std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
-  DwarfCIE cie_;
-  DwarfFDE fde_;
+  DwarfCie cie_;
+  DwarfFde fde_;
 };
 TYPED_TEST_CASE_P(DwarfCfaTest);
 
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 520c545..20c488a 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -25,21 +25,9 @@
 #include "DwarfMemory.h"
 #include "DwarfOp.h"
 #include "Log.h"
-#include "Regs.h"
 
 #include "MemoryFake.h"
-
-template <typename TypeParam>
-class RegsFake : public RegsTmpl<TypeParam> {
- public:
-  RegsFake(uint16_t total_regs, uint16_t sp_reg)
-      : RegsTmpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
-  virtual ~RegsFake() = default;
-
-  uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
-  uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
-  bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
-};
+#include "RegsFake.h"
 
 template <typename TypeParam>
 class DwarfOpTest : public ::testing::Test {
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
new file mode 100644
index 0000000..71b114a
--- /dev/null
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -0,0 +1,832 @@
+/*
+ * 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 "DwarfSection.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+template <typename TypeParam>
+class MockDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
+ public:
+  MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
+  virtual ~MockDwarfSectionImpl() = default;
+
+  MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+
+  MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
+
+  MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
+
+  MOCK_METHOD1(IsCie32, bool(uint32_t));
+
+  MOCK_METHOD1(IsCie64, bool(uint64_t));
+
+  MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
+
+  MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+
+  MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
+
+  void TestSetCachedCieEntry(uint64_t offset, const DwarfCie& cie) {
+    this->cie_entries_[offset] = cie;
+  }
+  void TestClearCachedCieEntry() { this->cie_entries_.clear(); }
+
+  void TestSetCachedFdeEntry(uint64_t offset, const DwarfFde& fde) {
+    this->fde_entries_[offset] = fde;
+  }
+  void TestClearCachedFdeEntry() { this->fde_entries_.clear(); }
+
+  void TestSetCachedCieLocRegs(uint64_t offset, const dwarf_loc_regs_t& loc_regs) {
+    this->cie_loc_regs_[offset] = loc_regs;
+  }
+  void TestClearCachedCieLocRegs() { this->cie_loc_regs_.clear(); }
+
+  void TestClearError() { this->last_error_ = DWARF_ERROR_NONE; }
+};
+
+template <typename TypeParam>
+class DwarfSectionImplTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    section_ = new MockDwarfSectionImpl<TypeParam>(&memory_);
+    ResetLogs();
+  }
+
+  void TearDown() override { delete section_; }
+
+  MemoryFake memory_;
+  MockDwarfSectionImpl<TypeParam>* section_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfSectionImplTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  TypeParam cfa_value = 0x12345;
+  this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x12345U, regs.sp());
+  EXPECT_EQ(0x20U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x80000000U, regs.sp());
+  EXPECT_EQ(0x20U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
+  DwarfCie cie{.return_address_register = 60};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+  this->section_->TestClearError();
+  loc_regs.erase(CFA_REG);
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+  this->section_->TestClearError();
+  loc_regs.erase(CFA_REG);
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+  this->section_->TestClearError();
+  loc_regs.erase(CFA_REG);
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[9] = 0x3000;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {9, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x20U, regs.pc());
+  EXPECT_EQ(0x2000U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[6] = 0x4000;
+  regs[9] = 0x3000;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {6, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x20U, regs.pc());
+  EXPECT_EQ(0x4000U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 0}};
+  loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {10, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  if (sizeof(TypeParam) == sizeof(uint64_t)) {
+    this->memory_.SetData64(0x2150, 0x12345678abcdef00ULL);
+  } else {
+    this->memory_.SetData32(0x2150, 0x12345678);
+  }
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[3] = 0x234;
+  regs[5] = 0x10;
+  regs[8] = 0x2100;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x100, 0}};
+  loc_regs[2] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x50, 0}};
+  loc_regs[3] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x10U, regs.pc());
+  EXPECT_EQ(0x2100U, regs.sp());
+  EXPECT_EQ(0x2200U, regs[1]);
+  EXPECT_EQ(0x234U, regs[3]);
+  if (sizeof(TypeParam) == sizeof(uint64_t)) {
+    EXPECT_EQ(0x12345678abcdef00ULL, regs[2]);
+  } else {
+    EXPECT_EQ(0x12345678U, regs[2]);
+  }
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0U, regs.pc());
+  EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x20U, regs.pc());
+  EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x20;
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  // This should not result in any errors.
+  loc_regs[20] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x20U, regs.pc());
+  EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[8] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  TypeParam cfa_value = 0x12345;
+  this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x3000U, regs.sp());
+  EXPECT_EQ(0x12345U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[8] = 0x3000;
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x3000U, regs.sp());
+  EXPECT_EQ(0x80000000U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
+  DwarfCie cie{.version = 3, .return_address_register = 5};
+  RegsFake<TypeParam> regs(10, 9);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0x100;
+  regs[8] = 0x2000;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  EXPECT_EQ(0x2000U, regs.sp());
+  EXPECT_EQ(0x100U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_fail_should_not_cache) {
+  ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_32_version_check) {
+  this->memory_.SetData32(0x5000, 0x100);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 0x1);
+  this->memory_.SetData8(0x5009, '\0');
+  this->memory_.SetData8(0x500a, 4);
+  this->memory_.SetData8(0x500b, 8);
+  this->memory_.SetData8(0x500c, 0x20);
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  const DwarfCie* cie = this->section_->GetCie(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x500dU, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, cie->return_address_register);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->section_->last_error());
+
+  this->section_->TestClearCachedCieEntry();
+  // Set version to 0, 2, 5 and verify we fail.
+  this->memory_.SetData8(0x5008, 0x0);
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+
+  this->memory_.SetData8(0x5008, 0x2);
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+
+  this->memory_.SetData8(0x5008, 0x5);
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_negative_data_alignment_factor) {
+  this->memory_.SetData32(0x5000, 0x100);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 0x1);
+  this->memory_.SetData8(0x5009, '\0');
+  this->memory_.SetData8(0x500a, 4);
+  this->memory_.SetMemory(0x500b, std::vector<uint8_t>{0xfc, 0xff, 0xff, 0xff, 0x7f});
+  this->memory_.SetData8(0x5010, 0x20);
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  const DwarfCie* cie = this->section_->GetCie(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x5011U, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(-4, cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_64_no_augment) {
+  this->memory_.SetData32(0x8000, 0xffffffff);
+  this->memory_.SetData64(0x8004, 0x200);
+  this->memory_.SetData64(0x800c, 0xffffffff);
+  this->memory_.SetData8(0x8014, 0x1);
+  this->memory_.SetData8(0x8015, '\0');
+  this->memory_.SetData8(0x8016, 4);
+  this->memory_.SetData8(0x8017, 8);
+  this->memory_.SetData8(0x8018, 0x20);
+
+  EXPECT_CALL(*this->section_, IsCie64(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  const DwarfCie* cie = this->section_->GetCie(0x8000);
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x8019U, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x820cU, cie->cfa_instructions_end);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_augment) {
+  this->memory_.SetData32(0x5000, 0x100);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 0x1);
+  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
+  this->memory_.SetData8(0x500e, 4);
+  this->memory_.SetData8(0x500f, 8);
+  this->memory_.SetData8(0x5010, 0x10);
+  // Augment length.
+  this->memory_.SetData8(0x5011, 0xf);
+  // L data.
+  this->memory_.SetData8(0x5012, DW_EH_PE_textrel | DW_EH_PE_udata2);
+  // P data.
+  this->memory_.SetData8(0x5013, DW_EH_PE_udata4);
+  this->memory_.SetData32(0x5014, 0x12345678);
+  // R data.
+  this->memory_.SetData8(0x5018, DW_EH_PE_udata2);
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  const DwarfCie* cie = this->section_->GetCie(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_udata2, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_textrel | DW_EH_PE_udata2, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(5U, cie->augmentation_string.size());
+  EXPECT_EQ('z', cie->augmentation_string[0]);
+  EXPECT_EQ('L', cie->augmentation_string[1]);
+  EXPECT_EQ('P', cie->augmentation_string[2]);
+  EXPECT_EQ('R', cie->augmentation_string[3]);
+  EXPECT_EQ('\0', cie->augmentation_string[4]);
+  EXPECT_EQ(0x12345678U, cie->personality_handler);
+  EXPECT_EQ(0x5021U, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x10U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_3) {
+  this->memory_.SetData32(0x5000, 0x100);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 0x3);
+  this->memory_.SetData8(0x5009, '\0');
+  this->memory_.SetData8(0x500a, 4);
+  this->memory_.SetData8(0x500b, 8);
+  this->memory_.SetMemory(0x500c, std::vector<uint8_t>{0x81, 0x03});
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  const DwarfCie* cie = this->section_->GetCie(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(3U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x500eU, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x181U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_4) {
+  this->memory_.SetData32(0x5000, 0x100);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 0x4);
+  this->memory_.SetData8(0x5009, '\0');
+  this->memory_.SetData8(0x500a, 4);
+  this->memory_.SetData8(0x500b, 0x13);
+  this->memory_.SetData8(0x500c, 4);
+  this->memory_.SetData8(0x500d, 8);
+  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x81, 0x03});
+
+  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+  const DwarfCie* cie = this->section_->GetCie(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(4U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0x13U, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x5010U, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x181U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
+  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment) {
+  this->memory_.SetData32(0x4000, 0x20);
+  this->memory_.SetData32(0x4004, 0x8000);
+  this->memory_.SetData32(0x4008, 0x5000);
+  this->memory_.SetData32(0x400c, 0x100);
+
+  EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
+  DwarfCie cie{};
+  cie.fde_address_encoding = DW_EH_PE_udata4;
+  this->section_->TestSetCachedCieEntry(0x8000, cie);
+  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(0x4010U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x4024U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x5000U, fde->pc_start);
+  EXPECT_EQ(0x5100U, fde->pc_end);
+  EXPECT_EQ(0x8000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment_non_zero_segment_size) {
+  this->memory_.SetData32(0x4000, 0x30);
+  this->memory_.SetData32(0x4004, 0x8000);
+  this->memory_.SetData32(0x4018, 0x5000);
+  this->memory_.SetData32(0x401c, 0x100);
+
+  EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
+  DwarfCie cie{};
+  cie.fde_address_encoding = DW_EH_PE_udata4;
+  cie.segment_size = 0x10;
+  this->section_->TestSetCachedCieEntry(0x8000, cie);
+  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(0x4020U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x4034U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x5000U, fde->pc_start);
+  EXPECT_EQ(0x5100U, fde->pc_end);
+  EXPECT_EQ(0x8000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_augment) {
+  this->memory_.SetData32(0x4000, 0x100);
+  this->memory_.SetData32(0x4004, 0x8000);
+  this->memory_.SetData32(0x4008, 0x5000);
+  this->memory_.SetData32(0x400c, 0x100);
+  this->memory_.SetMemory(0x4010, std::vector<uint8_t>{0x82, 0x01});
+  this->memory_.SetData16(0x4012, 0x1234);
+
+  EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
+  DwarfCie cie{};
+  cie.fde_address_encoding = DW_EH_PE_udata4;
+  cie.augmentation_string.push_back('z');
+  cie.lsda_encoding = DW_EH_PE_udata2;
+  this->section_->TestSetCachedCieEntry(0x8000, cie);
+  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(0x4094U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x4104U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x5000U, fde->pc_start);
+  EXPECT_EQ(0x5100U, fde->pc_end);
+  EXPECT_EQ(0x8000U, fde->cie_offset);
+  EXPECT_EQ(0x1234U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_64_no_augment) {
+  this->memory_.SetData32(0x4000, 0xffffffff);
+  this->memory_.SetData64(0x4004, 0x100);
+  this->memory_.SetData64(0x400c, 0x12345678);
+  this->memory_.SetData32(0x4014, 0x5000);
+  this->memory_.SetData32(0x4018, 0x100);
+
+  EXPECT_CALL(*this->section_, IsCie64(0x12345678)).WillOnce(::testing::Return(false));
+  EXPECT_CALL(*this->section_, GetCieOffsetFromFde64(0x12345678))
+      .WillOnce(::testing::Return(0x12345678));
+  DwarfCie cie{};
+  cie.fde_address_encoding = DW_EH_PE_udata4;
+  this->section_->TestSetCachedCieEntry(0x12345678, cie);
+  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(0x401cU, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x410cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0x5000U, fde->pc_start);
+  EXPECT_EQ(0x5100U, fde->pc_end);
+  EXPECT_EQ(0x12345678U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_cached) {
+  DwarfCie cie{};
+  cie.fde_address_encoding = DW_EH_PE_udata4;
+  cie.augmentation_string.push_back('z');
+  cie.lsda_encoding = DW_EH_PE_udata2;
+
+  DwarfFde fde_cached{};
+  fde_cached.cfa_instructions_offset = 0x1000;
+  fde_cached.cfa_instructions_end = 0x1100;
+  fde_cached.pc_start = 0x9000;
+  fde_cached.pc_end = 0x9400;
+  fde_cached.cie_offset = 0x30000;
+  fde_cached.cie = &cie;
+  this->section_->TestSetCachedFdeEntry(0x6000, fde_cached);
+
+  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x6000);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_EQ(&cie, fde->cie);
+  EXPECT_EQ(0x1000U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x1100U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x9000U, fde->pc_start);
+  EXPECT_EQ(0x9400U, fde->pc_end);
+  EXPECT_EQ(0x30000U, fde->cie_offset);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
+  DwarfCie cie{};
+  cie.cfa_instructions_offset = 0x3000;
+  cie.cfa_instructions_end = 0x3002;
+  DwarfFde fde{};
+  fde.cie = &cie;
+  fde.cie_offset = 0x8000;
+  fde.cfa_instructions_offset = 0x6000;
+  fde.cfa_instructions_end = 0x6002;
+
+  this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x09, 0x02, 0x01});
+  this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
+
+  dwarf_loc_regs_t loc_regs;
+  ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+  ASSERT_EQ(2U, loc_regs.size());
+
+  auto entry = loc_regs.find(2);
+  ASSERT_NE(entry, loc_regs.end());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+  ASSERT_EQ(1U, entry->second.values[0]);
+
+  entry = loc_regs.find(4);
+  ASSERT_NE(entry, loc_regs.end());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+  ASSERT_EQ(3U, entry->second.values[0]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_cached) {
+  DwarfCie cie{};
+  cie.cfa_instructions_offset = 0x3000;
+  cie.cfa_instructions_end = 0x3002;
+  DwarfFde fde{};
+  fde.cie = &cie;
+  fde.cie_offset = 0x8000;
+  fde.cfa_instructions_offset = 0x6000;
+  fde.cfa_instructions_end = 0x6002;
+
+  dwarf_loc_regs_t cie_loc_regs{{6, {DWARF_LOCATION_REGISTER, {4, 0}}}};
+  this->section_->TestSetCachedCieLocRegs(0x8000, cie_loc_regs);
+  this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
+
+  dwarf_loc_regs_t loc_regs;
+  ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+  ASSERT_EQ(2U, loc_regs.size());
+
+  auto entry = loc_regs.find(6);
+  ASSERT_NE(entry, loc_regs.end());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+  ASSERT_EQ(4U, entry->second.values[0]);
+
+  entry = loc_regs.find(4);
+  ASSERT_NE(entry, loc_regs.end());
+  ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+  ASSERT_EQ(3U, entry->second.values[0]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Log) {
+  DwarfCie cie{};
+  cie.cfa_instructions_offset = 0x5000;
+  cie.cfa_instructions_end = 0x5001;
+  DwarfFde fde{};
+  fde.cie = &cie;
+  fde.cfa_instructions_offset = 0x6000;
+  fde.cfa_instructions_end = 0x6001;
+
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
+  this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
+  ASSERT_TRUE(this->section_->Log(2, 0x1000, 0x1000, &fde));
+
+  ASSERT_EQ(
+      "4 unwind     DW_CFA_nop\n"
+      "4 unwind     Raw Data: 0x00\n"
+      "4 unwind     DW_CFA_restore register(2)\n"
+      "4 unwind     Raw Data: 0xc2\n",
+      GetFakeLogPrint());
+  ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_CASE_P(
+    DwarfSectionImplTest, Eval_cfa_expr_eval_fail, Eval_cfa_expr_no_stack,
+    Eval_cfa_expr_is_register, Eval_cfa_expr, Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa,
+    Eval_cfa_bad, Eval_cfa_register_prev, Eval_cfa_register_from_value, Eval_double_indirection,
+    Eval_invalid_register, Eval_different_reg_locations, Eval_return_address_undefined,
+    Eval_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+    Eval_same_cfa_same_pc, GetCie_fail_should_not_cache, GetCie_32_version_check,
+    GetCie_negative_data_alignment_factor, GetCie_64_no_augment, GetCie_augment, GetCie_version_3,
+    GetCie_version_4, GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
+    GetFdeFromOffset_32_no_augment_non_zero_segment_size, GetFdeFromOffset_32_augment,
+    GetFdeFromOffset_64_no_augment, GetFdeFromOffset_cached, GetCfaLocationInfo_cie_not_cached,
+    GetCfaLocationInfo_cie_cached, Log);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
new file mode 100644
index 0000000..20f0e2a
--- /dev/null
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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 "DwarfSection.h"
+
+#include "MemoryFake.h"
+
+class MockDwarfSection : public DwarfSection {
+ public:
+  MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
+  virtual ~MockDwarfSection() = default;
+
+  MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
+
+  MOCK_METHOD4(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*));
+
+  MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
+
+  MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+
+  MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
+
+  MOCK_METHOD1(GetFdeFromOffset, const DwarfFde*(uint64_t));
+
+  MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
+
+  MOCK_METHOD1(IsCie32, bool(uint32_t));
+
+  MOCK_METHOD1(IsCie64, bool(uint64_t));
+
+  MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
+
+  MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+
+  MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
+};
+
+class DwarfSectionTest : public ::testing::Test {
+ protected:
+  MemoryFake memory_;
+};
+
+TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_from_pc) {
+  MockDwarfSection mock_section(&memory_);
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(false));
+
+  // Verify nullptr when GetFdeOffsetFromPc fails.
+  ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
+}
+
+TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_fde_pc_end) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfFde fde{};
+  fde.pc_end = 0x500;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  // Verify nullptr when GetFdeOffsetFromPc fails.
+  ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
+}
+
+TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_pass) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfFde fde{};
+  fde.pc_end = 0x2000;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  // Verify nullptr when GetFdeOffsetFromPc fails.
+  ASSERT_EQ(&fde, mock_section.GetFdeFromPc(0x1000));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_fde) {
+  MockDwarfSection mock_section(&memory_);
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(false));
+
+  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_cie_null) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfFde fde{};
+  fde.pc_end = 0x2000;
+  fde.cie = nullptr;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfCie cie{};
+  DwarfFde fde{};
+  fde.pc_end = 0x2000;
+  fde.cie = &cie;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+      .WillOnce(::testing::Return(false));
+
+  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+TEST_F(DwarfSectionTest, Step_pass) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfCie cie{};
+  DwarfFde fde{};
+  fde.pc_end = 0x2000;
+  fde.cie = &cie;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+      .WillOnce(::testing::Return(true));
+
+  MemoryFake process;
+  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr))
+      .WillOnce(::testing::Return(true));
+
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process));
+}
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
new file mode 100644
index 0000000..ff030c8
--- /dev/null
+++ b/libunwindstack/tests/RegsFake.h
@@ -0,0 +1,36 @@
+/*
+ * 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_TESTS_REGS_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
+
+#include <stdint.h>
+
+#include "Regs.h"
+
+template <typename TypeParam>
+class RegsFake : public RegsImpl<TypeParam> {
+ public:
+  RegsFake(uint16_t total_regs, uint16_t sp_reg)
+      : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+  virtual ~RegsFake() = default;
+
+  uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
+  uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+  bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+};
+
+#endif  // _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 0dac278..3056373 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -48,13 +48,13 @@
 };
 
 template <typename TypeParam>
-class RegsTestTmpl : public RegsTmpl<TypeParam> {
+class RegsTestImpl : public RegsImpl<TypeParam> {
  public:
-  RegsTestTmpl(uint16_t total_regs, uint16_t regs_sp)
-      : RegsTmpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
-  RegsTestTmpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
-      : RegsTmpl<TypeParam>(total_regs, regs_sp, return_loc) {}
-  virtual ~RegsTestTmpl() = default;
+  RegsTestImpl(uint16_t total_regs, uint16_t regs_sp)
+      : RegsImpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+  RegsTestImpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
+      : RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {}
+  virtual ~RegsTestImpl() = default;
 
   uint64_t GetAdjustedPc(uint64_t, Elf*) { return 0; }
 };
@@ -80,7 +80,7 @@
 };
 
 TEST_F(RegsTest, regs32) {
-  RegsTestTmpl<uint32_t> regs32(50, 10);
+  RegsTestImpl<uint32_t> regs32(50, 10);
   ASSERT_EQ(50U, regs32.total_regs());
   ASSERT_EQ(10U, regs32.sp_reg());
 
@@ -103,7 +103,7 @@
 }
 
 TEST_F(RegsTest, regs64) {
-  RegsTestTmpl<uint64_t> regs64(30, 12);
+  RegsTestImpl<uint64_t> regs64(30, 12);
   ASSERT_EQ(30U, regs64.total_regs());
   ASSERT_EQ(12U, regs64.sp_reg());
 
@@ -127,7 +127,7 @@
 
 template <typename AddressType>
 void RegsTest::regs_rel_pc() {
-  RegsTestTmpl<AddressType> regs(30, 12);
+  RegsTestImpl<AddressType> regs(30, 12);
 
   elf_interface_->set_load_bias(0);
   MapInfo map_info{.start = 0x1000, .end = 0x2000};
@@ -147,7 +147,7 @@
 
 template <typename AddressType>
 void RegsTest::regs_return_address_register() {
-  RegsTestTmpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
+  RegsTestImpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
 
   regs[5] = 0x12345;
   uint64_t value;
@@ -164,7 +164,7 @@
 }
 
 TEST_F(RegsTest, regs32_return_address_sp_offset) {
-  RegsTestTmpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
+  RegsTestImpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
 
   regs.set_sp(0x2002);
   memory_->SetData32(0x2000, 0x12345678);
@@ -174,7 +174,7 @@
 }
 
 TEST_F(RegsTest, regs64_return_address_sp_offset) {
-  RegsTestTmpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
+  RegsTestImpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
 
   regs.set_sp(0x2008);
   memory_->SetData64(0x2000, 0x12345678aabbccddULL);
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/zip_archive.cc b/libziparchive/zip_archive.cc
index 6860c4e..a64b3b1 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -49,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
@@ -484,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;
@@ -495,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;
 }
@@ -565,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
@@ -940,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 */
@@ -972,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];
@@ -981,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 ")",
@@ -1045,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;
   }
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/logcat/logcatd.rc b/logcat/logcatd.rc
index 06cc90d..07040b0 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -61,3 +61,4 @@
     user logd
     group log
     writepid /dev/cpuset/system-background/tasks
+    oom_score_adjust -600
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 8aecca1..94f64e0 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -27,6 +27,16 @@
     # Set the security context of /postinstall if present.
     restorecon /postinstall
 
+    # Mount cgroup mount point for cpu accounting
+    mount cgroup none /acct cpuacct
+    mkdir /acct/uid
+
+    # root memory control cgroup, used by lmkd
+    mkdir /dev/memcg 0700 root system
+    mount cgroup none /dev/memcg memory
+    # app mem cgroups, used by activity manager, lmkd and zygote
+    mkdir /dev/memcg/apps/ 0755 system system
+
     start ueventd
 
 on init
@@ -43,10 +53,6 @@
     # Link /vendor to /system/vendor for devices without a vendor partition.
     symlink /system/vendor /vendor
 
-    # Mount cgroup mount point for cpu accounting
-    mount cgroup none /acct cpuacct
-    mkdir /acct/uid
-
     # Create energy-aware scheduler tuning nodes
     mkdir /dev/stune
     mount cgroup none /dev/stune schedtune
@@ -99,12 +105,6 @@
     symlink /storage/self/primary /mnt/sdcard
     symlink /mnt/user/0/primary /mnt/runtime/default/self/primary
 
-    # root memory control cgroup, used by lmkd
-    mkdir /dev/memcg 0700 root system
-    mount cgroup none /dev/memcg memory
-    # app mem cgroups, used by activity manager, lmkd and zygote
-    mkdir /dev/memcg/apps/ 0755 system system
-
     write /proc/sys/kernel/panic_on_oops 1
     write /proc/sys/kernel/hung_task_timeout_secs 0
     write /proc/cpu/alignment 4
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>