Merge "Revert "If enablefilecrypto or init_user0 fails, reboot into recovery.""
diff --git a/adb/Android.bp b/adb/Android.bp
index 41f7b89..0858a6c 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -13,39 +13,39 @@
 // limitations under the License.
 
 python_binary_host {
-  name: "adb_integration_test_adb",
-  main: "test_adb.py",
-  srcs: [
-    "test_adb.py",
-  ],
-  libs: [
-    "adb_py",
-  ],
-  version: {
-    py2: {
-      enabled: true,
+    name: "adb_integration_test_adb",
+    main: "test_adb.py",
+    srcs: [
+        "test_adb.py",
+    ],
+    libs: [
+        "adb_py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
     },
-    py3: {
-      enabled: false,
-    },
-  },
 }
 
 python_binary_host {
-  name: "adb_integration_test_device",
-  main: "test_device.py",
-  srcs: [
-    "test_device.py",
-  ],
-  libs: [
-    "adb_py",
-  ],
-  version: {
-    py2: {
-      enabled: true,
+    name: "adb_integration_test_device",
+    main: "test_device.py",
+    srcs: [
+        "test_device.py",
+    ],
+    libs: [
+        "adb_py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
     },
-    py3: {
-      enabled: false,
-    },
-  },
 }
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 0e38897..c4df5c4 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -42,7 +42,6 @@
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/parsenetaddress.h>
-#include <android-base/quick_exit.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
@@ -105,31 +104,27 @@
 }
 
 uint32_t calculate_apacket_checksum(const apacket* p) {
-    const unsigned char* x = reinterpret_cast<const unsigned char*>(p->data);
     uint32_t sum = 0;
-    size_t count = p->msg.data_length;
-
-    while (count-- > 0) {
-        sum += *x++;
+    for (size_t i = 0; i < p->msg.data_length; ++i) {
+        sum += static_cast<uint8_t>(p->payload[i]);
     }
-
     return sum;
 }
 
 apacket* get_apacket(void)
 {
-    apacket* p = reinterpret_cast<apacket*>(malloc(sizeof(apacket)));
+    apacket* p = new apacket();
     if (p == nullptr) {
       fatal("failed to allocate an apacket");
     }
 
-    memset(p, 0, sizeof(apacket) - MAX_PAYLOAD);
+    memset(&p->msg, 0, sizeof(p->msg));
     return p;
 }
 
 void put_apacket(apacket *p)
 {
-    free(p);
+    delete p;
 }
 
 void handle_online(atransport *t)
@@ -155,8 +150,7 @@
 #define DUMPMAX 32
 void print_packet(const char *label, apacket *p)
 {
-    char *tag;
-    char *x;
+    const char* tag;
     unsigned count;
 
     switch(p->msg.command){
@@ -173,15 +167,15 @@
     fprintf(stderr, "%s: %s %08x %08x %04x \"",
             label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length);
     count = p->msg.data_length;
-    x = (char*) p->data;
-    if(count > DUMPMAX) {
+    const char* x = p->payload.data();
+    if (count > DUMPMAX) {
         count = DUMPMAX;
         tag = "\n";
     } else {
         tag = "\"\n";
     }
-    while(count-- > 0){
-        if((*x >= ' ') && (*x < 127)) {
+    while (count-- > 0) {
+        if ((*x >= ' ') && (*x < 127)) {
             fputc(*x, stderr);
         } else {
             fputc('.', stderr);
@@ -254,8 +248,8 @@
                    << connection_str.length() << ")";
     }
 
-    memcpy(cp->data, connection_str.c_str(), connection_str.length());
-    cp->msg.data_length = connection_str.length();
+    cp->payload = std::move(connection_str);
+    cp->msg.data_length = cp->payload.size();
 
     send_packet(cp, t);
 }
@@ -329,9 +323,7 @@
     }
 
     t->update_version(p->msg.arg0, p->msg.arg1);
-    std::string banner(reinterpret_cast<const char*>(p->data),
-                       p->msg.data_length);
-    parse_banner(banner, t);
+    parse_banner(p->payload, t);
 
 #if ADB_HOST
     handle_online(t);
@@ -354,6 +346,7 @@
             ((char*) (&(p->msg.command)))[2],
             ((char*) (&(p->msg.command)))[3]);
     print_packet("recv", p);
+    CHECK_EQ(p->payload.size(), p->msg.data_length);
 
     switch(p->msg.command){
     case A_SYNC:
@@ -380,11 +373,11 @@
                 if (t->GetConnectionState() == kCsOffline) {
                     t->SetConnectionState(kCsUnauthorized);
                 }
-                send_auth_response(p->data, p->msg.data_length, t);
+                send_auth_response(p->payload.data(), p->msg.data_length, t);
                 break;
 #else
             case ADB_AUTH_SIGNATURE:
-                if (adbd_auth_verify(t->token, sizeof(t->token), p->data, p->msg.data_length)) {
+                if (adbd_auth_verify(t->token, sizeof(t->token), p->payload)) {
                     adbd_auth_verified(t);
                     t->failed_auth_attempts = 0;
                 } else {
@@ -394,7 +387,7 @@
                 break;
 
             case ADB_AUTH_RSAPUBLICKEY:
-                adbd_auth_confirm_key(p->data, p->msg.data_length, t);
+                adbd_auth_confirm_key(p->payload.data(), p->msg.data_length, t);
                 break;
 #endif
             default:
@@ -406,9 +399,7 @@
 
     case A_OPEN: /* OPEN(local-id, 0, "destination") */
         if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
-            char *name = (char*) p->data;
-            name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
-            asocket* s = create_local_service_socket(name, t);
+            asocket* s = create_local_service_socket(p->payload.c_str(), t);
             if (s == nullptr) {
                 send_close(0, p->msg.arg0, t);
             } else {
@@ -474,13 +465,10 @@
             asocket* s = find_local_socket(p->msg.arg1, p->msg.arg0);
             if (s) {
                 unsigned rid = p->msg.arg0;
-                p->len = p->msg.data_length;
-
-                if (s->enqueue(s, p) == 0) {
+                if (s->enqueue(s, std::move(p->payload)) == 0) {
                     D("Enqueue the socket");
                     send_ready(s->id, rid, t);
                 }
-                return;
             }
         }
         break;
@@ -1070,7 +1058,7 @@
         SendOkay(reply_fd);
 
         // Rely on process exit to close the socket for us.
-        android::base::quick_exit(0);
+        exit(0);
     }
 
     // "transport:" is used for switching transport with a specified serial number
diff --git a/adb/adb.h b/adb/adb.h
index b5d6bcb..a6d0463 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -73,13 +73,8 @@
 };
 
 struct apacket {
-    apacket* next;
-
-    size_t len;
-    char* ptr;
-
     amessage msg;
-    char data[MAX_PAYLOAD];
+    std::string payload;
 };
 
 uint32_t calculate_apacket_checksum(const apacket* packet);
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index a6f224f..715e04f 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -49,7 +49,7 @@
 void adbd_auth_verified(atransport *t);
 
 void adbd_cloexec_auth_socket();
-bool adbd_auth_verify(const char* token, size_t token_size, const char* sig, int sig_len);
+bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig);
 void adbd_auth_confirm_key(const char* data, size_t len, atransport* t);
 
 void send_auth_request(atransport *t);
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index 365bf77..c3aef16 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -299,20 +299,25 @@
     return result;
 }
 
-static int adb_auth_sign(RSA* key, const char* token, size_t token_size, char* sig) {
+static std::string adb_auth_sign(RSA* key, const char* token, size_t token_size) {
     if (token_size != TOKEN_SIZE) {
         D("Unexpected token size %zd", token_size);
         return 0;
     }
 
+    std::string result;
+    result.resize(MAX_PAYLOAD);
+
     unsigned int len;
     if (!RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
-                  reinterpret_cast<uint8_t*>(sig), &len, key)) {
-        return 0;
+                  reinterpret_cast<uint8_t*>(&result[0]), &len, key)) {
+        return std::string();
     }
 
+    result.resize(len);
+
     D("adb_auth_sign len=%d", len);
-    return (int)len;
+    return result;
 }
 
 std::string adb_auth_get_userkey() {
@@ -446,13 +451,14 @@
     }
 
     apacket* p = get_apacket();
-    memcpy(p->data, key.c_str(), key.size() + 1);
-
     p->msg.command = A_AUTH;
     p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
 
+    p->payload = std::move(key);
+
     // adbd expects a null-terminated string.
-    p->msg.data_length = key.size() + 1;
+    p->payload.push_back('\0');
+    p->msg.data_length = p->payload.size();
     send_packet(p, t);
 }
 
@@ -467,8 +473,8 @@
     LOG(INFO) << "Calling send_auth_response";
     apacket* p = get_apacket();
 
-    int ret = adb_auth_sign(key.get(), token, token_size, p->data);
-    if (!ret) {
+    std::string result = adb_auth_sign(key.get(), token, token_size);
+    if (result.empty()) {
         D("Error signing the token");
         put_apacket(p);
         return;
@@ -476,6 +482,7 @@
 
     p->msg.command = A_AUTH;
     p->msg.arg0 = ADB_AUTH_SIGNATURE;
-    p->msg.data_length = ret;
+    p->payload = std::move(result);
+    p->msg.data_length = p->payload.size();
     send_packet(p, t);
 }
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index eac923d..a8ec5fb 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -42,7 +42,11 @@
                const char* message) {
     android::base::StderrLogger(id, severity, tag, file, line, message);
 #if !ADB_HOST
-    gLogdLogger(id, severity, tag, file, line, message);
+    // Only print logs of INFO or higher to logcat, so that `adb logcat` with adbd tracing on
+    // doesn't result in exponential logging.
+    if (severity >= android::base::INFO) {
+        gLogdLogger(id, severity, tag, file, line, message);
+    }
 #endif
 }
 
@@ -137,11 +141,15 @@
             // -1 is used for the special values "1" and "all" that enable all
             // tracing.
             adb_trace_mask = ~0;
-            return;
+            break;
         } else {
             adb_trace_mask |= 1 << flag->second;
         }
     }
+
+    if (adb_trace_mask != 0) {
+        android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+    }
 }
 
 void adb_trace_init(char** argv) {
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index fc6560c..1d2c8c7 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -43,11 +43,11 @@
 #define VLOG_IS_ON(TAG) \
     ((adb_trace_mask & (1 << (TAG))) != 0)
 
-#define VLOG(TAG)         \
+#define VLOG(TAG)                 \
     if (LIKELY(!VLOG_IS_ON(TAG))) \
-        ;                 \
-    else                  \
-        LOG(INFO)
+        ;                         \
+    else                          \
+        LOG(DEBUG)
 
 // You must define TRACE_TAG before using this macro.
 #define D(...) \
diff --git a/adb/adbd_auth.cpp b/adb/adbd_auth.cpp
index 3488ad1..3fd2b31 100644
--- a/adb/adbd_auth.cpp
+++ b/adb/adbd_auth.cpp
@@ -46,7 +46,7 @@
 
 bool auth_required = true;
 
-bool adbd_auth_verify(const char* token, size_t token_size, const char* sig, int sig_len) {
+bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig) {
     static constexpr const char* key_paths[] = { "/adb_keys", "/data/misc/adb/adb_keys", nullptr };
 
     for (const auto& path : key_paths) {
@@ -80,7 +80,8 @@
 
                 bool verified =
                     (RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
-                                reinterpret_cast<const uint8_t*>(sig), sig_len, key) == 1);
+                                reinterpret_cast<const uint8_t*>(sig.c_str()), sig.size(),
+                                key) == 1);
                 RSA_free(key);
                 if (verified) return true;
             }
@@ -210,10 +211,10 @@
     }
 
     apacket* p = get_apacket();
-    memcpy(p->data, t->token, sizeof(t->token));
     p->msg.command = A_AUTH;
     p->msg.arg0 = ADB_AUTH_TOKEN;
     p->msg.data_length = sizeof(t->token);
+    p->payload.assign(t->token, t->token + sizeof(t->token));
     send_packet(p, t);
 }
 
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index f0d0ce7..31cb853 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -28,7 +28,6 @@
 #include <android-base/errors.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/quick_exit.h>
 #include <android-base/stringprintf.h>
 
 #include "adb.h"
@@ -61,7 +60,7 @@
 static BOOL WINAPI ctrlc_handler(DWORD type) {
     // TODO: Consider trying to kill a starting up adb server (if we're in
     // launch_server) by calling GenerateConsoleCtrlEvent().
-    android::base::quick_exit(STATUS_CONTROL_C_EXIT);
+    exit(STATUS_CONTROL_C_EXIT);
     return TRUE;
 }
 #endif
@@ -76,6 +75,12 @@
     usb_cleanup();
 }
 
+static void intentionally_leak() {
+    void* p = ::operator new(1);
+    // The analyzer is upset about this leaking. NOLINTNEXTLINE
+    LOG(INFO) << "leaking pointer " << p;
+}
+
 int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd) {
 #if defined(_WIN32)
     // adb start-server starts us up with stdout and stderr hooked up to
@@ -95,16 +100,21 @@
     SetConsoleCtrlHandler(ctrlc_handler, TRUE);
 #else
     signal(SIGINT, [](int) {
-        fdevent_run_on_main_thread([]() { android::base::quick_exit(0); });
+        fdevent_run_on_main_thread([]() { exit(0); });
     });
 #endif
 
+    char* leak = getenv("ADB_LEAK");
+    if (leak && strcmp(leak, "1") == 0) {
+        intentionally_leak();
+    }
+
     if (is_daemon) {
         close_stdin();
         setup_daemon_logging();
     }
 
-    android::base::at_quick_exit(adb_server_cleanup);
+    atexit(adb_server_cleanup);
 
     init_transport_registration();
     init_mdns_transport_discovery();
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 18f585d..46c3f58 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -19,6 +19,7 @@
 #include "sysdeps.h"
 
 #include <stdint.h>
+#include <stdlib.h>
 
 #include <atomic>
 #include <chrono>
@@ -33,7 +34,6 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/quick_exit.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index b28de4b..d285561 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -26,6 +26,7 @@
 #include <unistd.h>
 
 #include <atomic>
+#include <deque>
 #include <functional>
 #include <list>
 #include <mutex>
@@ -81,7 +82,7 @@
 
 static auto& run_queue_notify_fd = *new unique_fd();
 static auto& run_queue_mutex = *new std::mutex();
-static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::vector<std::function<void()>>();
+static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::deque<std::function<void()>>();
 
 void check_main_thread() {
     if (main_thread_valid) {
@@ -359,11 +360,21 @@
 }
 #endif // !ADB_HOST
 
-static void fdevent_run_flush() REQUIRES(run_queue_mutex) {
-    for (auto& f : run_queue) {
-        f();
+static void fdevent_run_flush() EXCLUDES(run_queue_mutex) {
+    // We need to be careful around reentrancy here, since a function we call can queue up another
+    // function.
+    while (true) {
+        std::function<void()> fn;
+        {
+            std::lock_guard<std::mutex> lock(run_queue_mutex);
+            if (run_queue.empty()) {
+                break;
+            }
+            fn = run_queue.front();
+            run_queue.pop_front();
+        }
+        fn();
     }
-    run_queue.clear();
 }
 
 static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
@@ -377,22 +388,23 @@
         PLOG(FATAL) << "failed to empty run queue notify fd";
     }
 
-    std::lock_guard<std::mutex> lock(run_queue_mutex);
     fdevent_run_flush();
 }
 
 static void fdevent_run_setup() {
-    std::lock_guard<std::mutex> lock(run_queue_mutex);
-    CHECK(run_queue_notify_fd.get() == -1);
-    int s[2];
-    if (adb_socketpair(s) != 0) {
-        PLOG(FATAL) << "failed to create run queue notify socketpair";
-    }
+    {
+        std::lock_guard<std::mutex> lock(run_queue_mutex);
+        CHECK(run_queue_notify_fd.get() == -1);
+        int s[2];
+        if (adb_socketpair(s) != 0) {
+            PLOG(FATAL) << "failed to create run queue notify socketpair";
+        }
 
-    run_queue_notify_fd.reset(s[0]);
-    fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
-    CHECK(fde != nullptr);
-    fdevent_add(fde, FDE_READ);
+        run_queue_notify_fd.reset(s[0]);
+        fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
+        CHECK(fde != nullptr);
+        fdevent_add(fde, FDE_READ);
+    }
 
     fdevent_run_flush();
 }
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 86e0209..63cc4d1 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -194,3 +194,31 @@
         ASSERT_EQ(i, vec[i]);
     }
 }
+
+static std::function<void()> make_appender(std::vector<int>* vec, int value) {
+    return [vec, value]() {
+        check_main_thread();
+        if (value == 100) {
+            return;
+        }
+
+        vec->push_back(value);
+        fdevent_run_on_main_thread(make_appender(vec, value + 1));
+    };
+}
+
+TEST_F(FdeventTest, run_on_main_thread_reentrant) {
+    std::vector<int> vec;
+
+    PrepareThread();
+    std::thread thread(fdevent_loop);
+
+    fdevent_run_on_main_thread(make_appender(&vec, 0));
+
+    TerminateThread(thread);
+
+    ASSERT_EQ(100u, vec.size());
+    for (int i = 0; i < 100; ++i) {
+        ASSERT_EQ(i, vec[i]);
+    }
+}
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index f0dff06..6f5396a 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -124,9 +124,6 @@
  ** for each JDWP process, we record its pid and its connected socket
  **/
 
-// PIDs are transmitted as 4 hex digits in ascii.
-static constexpr size_t PID_LEN = 4;
-
 static void jdwp_process_event(int socket, unsigned events, void* _proc);
 static void jdwp_process_list_updated(void);
 
@@ -174,7 +171,7 @@
         _jdwp_list.remove_if(pred);
     }
 
-    int pid = -1;
+    int32_t pid = -1;
     int socket = -1;
     fdevent* fde = nullptr;
 
@@ -221,17 +218,9 @@
 
     if (events & FDE_READ) {
         if (proc->pid < 0) {
-            /* read the PID as a 4-hexchar string */
-            char buf[PID_LEN + 1];
-            ssize_t rc = TEMP_FAILURE_RETRY(recv(socket, buf, PID_LEN, 0));
-            if (rc != PID_LEN) {
-                D("failed to read jdwp pid: %s", strerror(errno));
-                goto CloseProcess;
-            }
-            buf[PID_LEN] = '\0';
-
-            if (sscanf(buf, "%04x", &proc->pid) != 1) {
-                D("could not decode JDWP %p PID number: '%s'", proc, buf);
+            ssize_t rc = TEMP_FAILURE_RETRY(recv(socket, &proc->pid, sizeof(proc->pid), 0));
+            if (rc != sizeof(proc->pid)) {
+                D("failed to read jdwp pid: rc = %zd, errno = %s", rc, strerror(errno));
                 goto CloseProcess;
             }
 
@@ -453,7 +442,7 @@
  **/
 
 struct JdwpSocket : public asocket {
-    bool pass;
+    bool pass = false;
 };
 
 static void jdwp_socket_close(asocket* s) {
@@ -467,13 +456,12 @@
     }
 
     remove_socket(s);
-    free(s);
+    delete s;
 }
 
-static int jdwp_socket_enqueue(asocket* s, apacket* p) {
+static int jdwp_socket_enqueue(asocket* s, std::string) {
     /* you can't write to this asocket */
     D("LS(%d): JDWP socket received data?", s->id);
-    put_apacket(p);
     s->peer->close(s->peer);
     return -1;
 }
@@ -486,9 +474,11 @@
      * on the second one, close the connection
      */
     if (!jdwp->pass) {
-        apacket* p = get_apacket();
-        p->len = jdwp_process_list((char*)p->data, s->get_max_payload());
-        peer->enqueue(peer, p);
+        std::string data;
+        data.resize(s->get_max_payload());
+        size_t len = jdwp_process_list(&data[0], data.size());
+        data.resize(len);
+        peer->enqueue(peer, std::move(data));
         jdwp->pass = true;
     } else {
         peer->close(peer);
@@ -496,7 +486,7 @@
 }
 
 asocket* create_jdwp_service_socket(void) {
-    JdwpSocket* s = reinterpret_cast<JdwpSocket*>(calloc(sizeof(*s), 1));
+    JdwpSocket* s = new JdwpSocket();
 
     if (!s) {
         fatal("failed to allocate JdwpSocket");
@@ -524,17 +514,14 @@
 static std::vector<std::unique_ptr<JdwpTracker>> _jdwp_trackers;
 
 static void jdwp_process_list_updated(void) {
-    char buffer[1024];
-    int len = jdwp_process_list_msg(buffer, sizeof(buffer));
+    std::string data;
+    data.resize(1024);
+    data.resize(jdwp_process_list_msg(&data[0], data.size()));
 
     for (auto& t : _jdwp_trackers) {
-        apacket* p = get_apacket();
-        memcpy(p->data, buffer, len);
-        p->len = len;
-
         if (t->peer) {
             // The tracker might not have been connected yet.
-            t->peer->enqueue(t->peer, p);
+            t->peer->enqueue(t->peer, data);
         }
     }
 }
@@ -560,17 +547,17 @@
     JdwpTracker* t = (JdwpTracker*)s;
 
     if (t->need_initial) {
-        apacket* p = get_apacket();
+        std::string data;
+        data.resize(s->get_max_payload());
+        data.resize(jdwp_process_list_msg(&data[0], data.size()));
         t->need_initial = false;
-        p->len = jdwp_process_list_msg((char*)p->data, s->get_max_payload());
-        s->peer->enqueue(s->peer, p);
+        s->peer->enqueue(s->peer, std::move(data));
     }
 }
 
-static int jdwp_tracker_enqueue(asocket* s, apacket* p) {
+static int jdwp_tracker_enqueue(asocket* s, std::string) {
     /* you can't write to this socket */
     D("LS(%d): JDWP tracker received data?", s->id);
-    put_apacket(p);
     s->peer->close(s->peer);
     return -1;
 }
diff --git a/adb/range.h b/adb/range.h
new file mode 100644
index 0000000..7a0b822
--- /dev/null
+++ b/adb/range.h
@@ -0,0 +1,65 @@
+#pragma once
+
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <android-base/logging.h>
+
+struct Range {
+    explicit Range(std::string data) : data_(std::move(data)) {}
+
+    Range(const Range& copy) = delete;
+    Range& operator=(const Range& copy) = delete;
+
+    Range(Range&& move) = default;
+    Range& operator=(Range&& move) = default;
+
+    bool empty() const {
+        return size() == 0;
+    }
+
+    size_t size() const {
+        return data_.size() - begin_offset_ - end_offset_;
+    };
+
+    void drop_front(size_t n) {
+        CHECK_GE(size(), n);
+        begin_offset_ += n;
+    }
+
+    void drop_end(size_t n) {
+        CHECK_GE(size(), n);
+        end_offset_ += n;
+    }
+
+    char* data() {
+        return &data_[0] + begin_offset_;
+    }
+
+    std::string::iterator begin() {
+        return data_.begin() + begin_offset_;
+    }
+
+    std::string::iterator end() {
+        return data_.end() - end_offset_;
+    }
+
+    std::string data_;
+    size_t begin_offset_ = 0;
+    size_t end_offset_ = 0;
+};
diff --git a/adb/remount_service.cpp b/adb/remount_service.cpp
index 32ed28f..a4c7a5a 100644
--- a/adb/remount_service.cpp
+++ b/adb/remount_service.cpp
@@ -62,9 +62,9 @@
 
 // The proc entry for / is full of lies, so check fstab instead.
 // /proc/mounts lists rootfs and /dev/root, neither of which is what we want.
-static std::string find_mount(const char* dir) {
-    if (strcmp(dir, "/") == 0) {
-       return find_fstab_mount(dir);
+static std::string find_mount(const char* dir, bool is_root) {
+    if (is_root) {
+        return find_fstab_mount(dir);
     } else {
        return find_proc_mount(dir);
     }
@@ -86,17 +86,29 @@
     if (!directory_exists(dir)) {
         return true;
     }
-    std::string dev = find_mount(dir);
-    if (dev.empty()) {
+    bool is_root = strcmp(dir, "/") == 0;
+    std::string dev = find_mount(dir, is_root);
+    // Even if the device for the root is not found, we still try to remount it
+    // as rw. This typically only happens when running Android in a container:
+    // the root will almost always be in a loop device, which is dynamic, so
+    // it's not convenient to put in the fstab.
+    if (dev.empty() && !is_root) {
         return true;
     }
-    if (!make_block_device_writable(dev)) {
+    if (!dev.empty() && !make_block_device_writable(dev)) {
         WriteFdFmt(fd, "remount of %s failed; couldn't make block device %s writable: %s\n",
                    dir, dev.c_str(), strerror(errno));
         return false;
     }
+    if (mount(dev.c_str(), dir, "none", MS_REMOUNT | MS_BIND, nullptr) == -1) {
+        // This is useful for cases where the superblock is already marked as
+        // read-write, but the mount itself is read-only, such as containers
+        // where the remount with just MS_REMOUNT is forbidden by the kernel.
+        WriteFdFmt(fd, "remount of the %s mount failed: %s.\n", dir, strerror(errno));
+        return false;
+    }
     if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
-        WriteFdFmt(fd, "remount of %s failed: %s\n", dir, strerror(errno));
+        WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno));
         return false;
     }
     return true;
diff --git a/adb/services.cpp b/adb/services.cpp
index 6dc71cf..fe74eb6 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -296,6 +296,7 @@
         void* arg = strdup(name + 7);
         if (arg == NULL) return -1;
         ret = create_service_thread("reboot", reboot_service, arg);
+        if (ret < 0) free(arg);
     } else if(!strncmp(name, "root:", 5)) {
         ret = create_service_thread("root", restart_root_service, nullptr);
     } else if(!strncmp(name, "unroot:", 7)) {
diff --git a/adb/socket.h b/adb/socket.h
index 64d05a9..2f09080 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -19,9 +19,12 @@
 
 #include <stddef.h>
 
+#include <deque>
 #include <memory>
+#include <string>
 
 #include "fdevent.h"
+#include "range.h"
 
 struct apacket;
 class atransport;
@@ -31,43 +34,38 @@
  * remote asocket is bound to the protocol engine.
  */
 struct asocket {
-    /* chain pointers for the local/remote list of
-     * asockets that this asocket lives in
-     */
-    asocket* next;
-    asocket* prev;
-
     /* the unique identifier for this asocket
      */
-    unsigned id;
+    unsigned id = 0;
 
     /* flag: set when the socket's peer has closed
      * but packets are still queued for delivery
      */
-    int closing;
+    int closing = 0;
 
     // flag: set when the socket failed to write, so the socket will not wait to
     // write packets and close directly.
-    bool has_write_error;
+    bool has_write_error = 0;
 
     /* flag: quit adbd when both ends close the
      * local service socket
      */
-    int exit_on_close;
+    int exit_on_close = 0;
 
     // the asocket we are connected to
-    asocket* peer;
+    asocket* peer = nullptr;
 
     /* For local asockets, the fde is used to bind
      * us to our fd event system.  For remote asockets
      * these fields are not used.
      */
-    fdevent fde;
-    int fd;
+    fdevent fde = {};
+    int fd = 0;
 
-    // queue of apackets waiting to be written
-    apacket* pkt_first;
-    apacket* pkt_last;
+    // queue of data waiting to be written
+    std::deque<Range> packet_queue;
+
+    std::string smart_socket_data;
 
     /* enqueue is called by our peer when it has data
      * for us.  It should return 0 if we can accept more
@@ -75,27 +73,27 @@
      * peer->ready() when we once again are ready to
      * receive data.
      */
-    int (*enqueue)(asocket* s, apacket* pkt);
+    int (*enqueue)(asocket* s, std::string data) = nullptr;
 
     /* ready is called by the peer when it is ready for
      * us to send data via enqueue again
      */
-    void (*ready)(asocket* s);
+    void (*ready)(asocket* s) = nullptr;
 
     /* shutdown is called by the peer before it goes away.
      * the socket should not do any further calls on its peer.
      * Always followed by a call to close. Optional, i.e. can be NULL.
      */
-    void (*shutdown)(asocket* s);
+    void (*shutdown)(asocket* s) = nullptr;
 
     /* close is called by the peer when it has gone away.
      * we are not allowed to make any further calls on the
      * peer once our close method is called.
      */
-    void (*close)(asocket* s);
+    void (*close)(asocket* s) = nullptr;
 
     /* A socket is bound to atransport */
-    atransport* transport;
+    atransport* transport = nullptr;
 
     size_t get_max_payload() const;
 };
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index f7c66db..04ad6f3 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -114,10 +114,10 @@
     ASSERT_TRUE(s != nullptr);
     arg->bytes_written = 0;
     while (true) {
-        apacket* p = get_apacket();
-        p->len = sizeof(p->data);
-        arg->bytes_written += p->len;
-        int ret = s->enqueue(s, p);
+        std::string data;
+        data.resize(MAX_PAYLOAD);
+        arg->bytes_written += data.size();
+        int ret = s->enqueue(s, std::move(data));
         if (ret == 1) {
             // The writer has one packet waiting to send.
             break;
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index c53fbb4..0007fed 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -37,32 +37,28 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "range.h"
 #include "transport.h"
 
 static std::recursive_mutex& local_socket_list_lock = *new std::recursive_mutex();
 static unsigned local_socket_next_id = 1;
 
-static asocket local_socket_list = {
-    .next = &local_socket_list, .prev = &local_socket_list,
-};
+static auto& local_socket_list = *new std::vector<asocket*>();
 
 /* the the list of currently closing local sockets.
 ** these have no peer anymore, but still packets to
 ** write to their fd.
 */
-static asocket local_socket_closing_list = {
-    .next = &local_socket_closing_list, .prev = &local_socket_closing_list,
-};
+static auto& local_socket_closing_list = *new std::vector<asocket*>();
 
 // Parse the global list of sockets to find one with id |local_id|.
 // If |peer_id| is not 0, also check that it is connected to a peer
 // with id |peer_id|. Returns an asocket handle on success, NULL on failure.
 asocket* find_local_socket(unsigned local_id, unsigned peer_id) {
-    asocket* s;
-    asocket* result = NULL;
+    asocket* result = nullptr;
 
     std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
-    for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
+    for (asocket* s : local_socket_list) {
         if (s->id != local_id) {
             continue;
         }
@@ -75,13 +71,6 @@
     return result;
 }
 
-static void insert_local_socket(asocket* s, asocket* list) {
-    s->next = list;
-    s->prev = s->next->prev;
-    s->prev->next = s;
-    s->next->prev = s;
-}
-
 void install_local_socket(asocket* s) {
     std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
 
@@ -92,29 +81,24 @@
         fatal("local socket id overflow");
     }
 
-    insert_local_socket(s, &local_socket_list);
+    local_socket_list.push_back(s);
 }
 
 void remove_socket(asocket* s) {
     std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
-    if (s->prev && s->next) {
-        s->prev->next = s->next;
-        s->next->prev = s->prev;
-        s->next = 0;
-        s->prev = 0;
-        s->id = 0;
+    for (auto list : { &local_socket_list, &local_socket_closing_list }) {
+        list->erase(std::remove_if(list->begin(), list->end(), [s](asocket* x) { return x == s; }),
+                    list->end());
     }
 }
 
 void close_all_sockets(atransport* t) {
-    asocket* s;
-
     /* this is a little gross, but since s->close() *will* modify
     ** the list out from under you, your options are limited.
     */
     std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
 restart:
-    for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
+    for (asocket* s : local_socket_list) {
         if (s->transport == t || (s->peer && s->peer->transport == t)) {
             s->close(s);
             goto restart;
@@ -122,55 +106,47 @@
     }
 }
 
-static int local_socket_enqueue(asocket* s, apacket* p) {
-    D("LS(%d): enqueue %zu", s->id, p->len);
+static int local_socket_enqueue(asocket* s, std::string data) {
+    D("LS(%d): enqueue %zu", s->id, data.size());
 
-    p->ptr = p->data;
+    Range r(std::move(data));
 
     /* if there is already data queue'd, we will receive
     ** events when it's time to write.  just add this to
     ** the tail
     */
-    if (s->pkt_first) {
+    if (!s->packet_queue.empty()) {
         goto enqueue;
     }
 
     /* write as much as we can, until we
     ** would block or there is an error/eof
     */
-    while (p->len > 0) {
-        int r = adb_write(s->fd, p->ptr, p->len);
-        if (r > 0) {
-            p->len -= r;
-            p->ptr += r;
+    while (!r.empty()) {
+        int rc = adb_write(s->fd, r.data(), r.size());
+        if (rc > 0) {
+            r.drop_front(rc);
             continue;
         }
-        if ((r == 0) || (errno != EAGAIN)) {
+
+        if (rc == 0 || errno != EAGAIN) {
             D("LS(%d): not ready, errno=%d: %s", s->id, errno, strerror(errno));
-            put_apacket(p);
             s->has_write_error = true;
             s->close(s);
             return 1; /* not ready (error) */
         } else {
+            // errno == EAGAIN
             break;
         }
     }
 
-    if (p->len == 0) {
-        put_apacket(p);
+    if (r.empty()) {
         return 0; /* ready for more data */
     }
 
 enqueue:
-    p->next = 0;
-    if (s->pkt_first) {
-        s->pkt_last->next = p;
-    } else {
-        s->pkt_first = p;
-    }
-    s->pkt_last = p;
-
     /* make sure we are notified when we can drain the queue */
+    s->packet_queue.push_back(std::move(r));
     fdevent_add(&s->fde, FDE_WRITE);
 
     return 1; /* not ready (backlog) */
@@ -184,7 +160,6 @@
 
 // be sure to hold the socket list lock when calling this
 static void local_socket_destroy(asocket* s) {
-    apacket *p, *n;
     int exit_on_close = s->exit_on_close;
 
     D("LS(%d): destroying fde.fd=%d", s->id, s->fde.fd);
@@ -194,14 +169,8 @@
     */
     fdevent_remove(&s->fde);
 
-    /* dispose of any unwritten data */
-    for (p = s->pkt_first; p; p = n) {
-        D("LS(%d): discarding %zu bytes", s->id, p->len);
-        n = p->next;
-        put_apacket(p);
-    }
     remove_socket(s);
-    free(s);
+    delete s;
 
     if (exit_on_close) {
         D("local_socket_destroy: exiting");
@@ -229,7 +198,7 @@
     /* If we are already closing, or if there are no
     ** pending packets, destroy immediately
     */
-    if (s->closing || s->has_write_error || s->pkt_first == NULL) {
+    if (s->closing || s->has_write_error || s->packet_queue.empty()) {
         int id = s->id;
         local_socket_destroy(s);
         D("LS(%d): closed", id);
@@ -243,7 +212,7 @@
     fdevent_del(&s->fde, FDE_READ);
     remove_socket(s);
     D("LS(%d): put on socket_closing_list fd=%d", s->id, s->fd);
-    insert_local_socket(s, &local_socket_closing_list);
+    local_socket_closing_list.push_back(s);
     CHECK_EQ(FDE_WRITE, s->fde.state & FDE_WRITE);
 }
 
@@ -255,35 +224,30 @@
     ** in order to simplify the code.
     */
     if (ev & FDE_WRITE) {
-        apacket* p;
-        while ((p = s->pkt_first) != nullptr) {
-            while (p->len > 0) {
-                int r = adb_write(fd, p->ptr, p->len);
-                if (r == -1) {
+        while (!s->packet_queue.empty()) {
+            Range& r = s->packet_queue.front();
+            while (!r.empty()) {
+                int rc = adb_write(fd, r.data(), r.size());
+                if (rc == -1) {
                     /* returning here is ok because FDE_READ will
                     ** be processed in the next iteration loop
                     */
                     if (errno == EAGAIN) {
                         return;
                     }
-                } else if (r > 0) {
-                    p->ptr += r;
-                    p->len -= r;
+                } else if (rc > 0) {
+                    r.drop_front(rc);
                     continue;
                 }
 
-                D(" closing after write because r=%d and errno is %d", r, errno);
+                D(" closing after write because rc=%d and errno is %d", rc, errno);
                 s->has_write_error = true;
                 s->close(s);
                 return;
             }
 
-            if (p->len == 0) {
-                s->pkt_first = p->next;
-                if (s->pkt_first == 0) {
-                    s->pkt_last = 0;
-                }
-                put_apacket(p);
+            if (r.empty()) {
+                s->packet_queue.pop_front();
             }
         }
 
@@ -305,9 +269,10 @@
     }
 
     if (ev & FDE_READ) {
-        apacket* p = get_apacket();
-        char* x = p->data;
         const size_t max_payload = s->get_max_payload();
+        std::string data;
+        data.resize(max_payload);
+        char* x = &data[0];
         size_t avail = max_payload;
         int r = 0;
         int is_eof = 0;
@@ -332,16 +297,15 @@
         }
         D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d", s->id, s->fd, r, is_eof,
           s->fde.force_eof);
-        if ((avail == max_payload) || (s->peer == 0)) {
-            put_apacket(p);
-        } else {
-            p->len = max_payload - avail;
+
+        if (avail != max_payload && s->peer) {
+            data.resize(max_payload - avail);
 
             // s->peer->enqueue() may call s->close() and free s,
             // so save variables for debug printing below.
             unsigned saved_id = s->id;
             int saved_fd = s->fd;
-            r = s->peer->enqueue(s->peer, p);
+            r = s->peer->enqueue(s->peer, std::move(data));
             D("LS(%u): fd=%d post peer->enqueue(). r=%d", saved_id, saved_fd, r);
 
             if (r < 0) {
@@ -383,10 +347,7 @@
 }
 
 asocket* create_local_socket(int fd) {
-    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
-    if (s == NULL) {
-        fatal("cannot allocate socket");
-    }
+    asocket* s = new asocket();
     s->fd = fd;
     s->enqueue = local_socket_enqueue;
     s->ready = local_socket_ready;
@@ -445,12 +406,22 @@
 }
 #endif /* ADB_HOST */
 
-static int remote_socket_enqueue(asocket* s, apacket* p) {
+static int remote_socket_enqueue(asocket* s, std::string data) {
     D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d", s->id, s->fd, s->peer->fd);
+    apacket* p = get_apacket();
+
     p->msg.command = A_WRTE;
     p->msg.arg0 = s->peer->id;
     p->msg.arg1 = s->id;
-    p->msg.data_length = p->len;
+
+    if (data.size() > MAX_PAYLOAD) {
+        put_apacket(p);
+        return -1;
+    }
+
+    p->payload = std::move(data);
+    p->msg.data_length = p->payload.size();
+
     send_packet(p, s->transport);
     return 1;
 }
@@ -485,7 +456,7 @@
     D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d", s->id, s->fd,
       s->peer ? s->peer->fd : -1);
     D("RS(%d): closed", s->id);
-    free(s);
+    delete s;
 }
 
 // Create a remote socket to exchange packets with a remote service through transport
@@ -496,11 +467,7 @@
     if (id == 0) {
         fatal("invalid remote socket id (0)");
     }
-    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
-
-    if (s == NULL) {
-        fatal("cannot allocate socket");
-    }
+    asocket* s = new asocket();
     s->id = id;
     s->enqueue = remote_socket_enqueue;
     s->ready = remote_socket_ready;
@@ -515,17 +482,20 @@
 void connect_to_remote(asocket* s, const char* destination) {
     D("Connect_to_remote call RS(%d) fd=%d", s->id, s->fd);
     apacket* p = get_apacket();
-    size_t len = strlen(destination) + 1;
-
-    if (len > (s->get_max_payload() - 1)) {
-        fatal("destination oversized");
-    }
 
     D("LS(%d): connect('%s')", s->id, destination);
     p->msg.command = A_OPEN;
     p->msg.arg0 = s->id;
-    p->msg.data_length = len;
-    strcpy((char*)p->data, destination);
+
+    // adbd expects a null-terminated string.
+    p->payload = destination;
+    p->payload.push_back('\0');
+    p->msg.data_length = p->payload.size();
+
+    if (p->msg.data_length > s->get_max_payload()) {
+        fatal("destination oversized");
+    }
+
     send_packet(p, s->transport);
 }
 
@@ -550,7 +520,7 @@
     s->close(s);
 }
 
-static unsigned unhex(char* s, int len) {
+static unsigned unhex(const char* s, int len) {
     unsigned n = 0, c;
 
     while (len-- > 0) {
@@ -654,8 +624,7 @@
 
 #endif  // ADB_HOST
 
-static int smart_socket_enqueue(asocket* s, apacket* p) {
-    unsigned len;
+static int smart_socket_enqueue(asocket* s, std::string data) {
 #if ADB_HOST
     char* service = nullptr;
     char* serial = nullptr;
@@ -663,49 +632,38 @@
     TransportType type = kTransportAny;
 #endif
 
-    D("SS(%d): enqueue %zu", s->id, p->len);
+    D("SS(%d): enqueue %zu", s->id, data.size());
 
-    if (s->pkt_first == 0) {
-        s->pkt_first = p;
-        s->pkt_last = p;
+    if (s->smart_socket_data.empty()) {
+        s->smart_socket_data = std::move(data);
     } else {
-        if ((s->pkt_first->len + p->len) > s->get_max_payload()) {
-            D("SS(%d): overflow", s->id);
-            put_apacket(p);
-            goto fail;
-        }
-
-        memcpy(s->pkt_first->data + s->pkt_first->len, p->data, p->len);
-        s->pkt_first->len += p->len;
-        put_apacket(p);
-
-        p = s->pkt_first;
+        std::copy(data.begin(), data.end(), std::back_inserter(s->smart_socket_data));
     }
 
     /* don't bother if we can't decode the length */
-    if (p->len < 4) {
+    if (s->smart_socket_data.size() < 4) {
         return 0;
     }
 
-    len = unhex(p->data, 4);
-    if ((len < 1) || (len > MAX_PAYLOAD)) {
-        D("SS(%d): bad size (%d)", s->id, len);
+    uint32_t len = unhex(s->smart_socket_data.data(), 4);
+    if (len == 0 || len > MAX_PAYLOAD) {
+        D("SS(%d): bad size (%u)", s->id, len);
         goto fail;
     }
 
-    D("SS(%d): len is %d", s->id, len);
+    D("SS(%d): len is %u", s->id, len);
     /* can't do anything until we have the full header */
-    if ((len + 4) > p->len) {
-        D("SS(%d): waiting for %zu more bytes", s->id, len + 4 - p->len);
+    if ((len + 4) > s->smart_socket_data.size()) {
+        D("SS(%d): waiting for %zu more bytes", s->id, len + 4 - s->smart_socket_data.size());
         return 0;
     }
 
-    p->data[len + 4] = 0;
+    s->smart_socket_data[len + 4] = 0;
 
-    D("SS(%d): '%s'", s->id, (char*)(p->data + 4));
+    D("SS(%d): '%s'", s->id, (char*)(s->smart_socket_data.data() + 4));
 
 #if ADB_HOST
-    service = (char*)p->data + 4;
+    service = &s->smart_socket_data[4];
     if (!strncmp(service, "host-serial:", strlen("host-serial:"))) {
         char* serial_end;
         service += strlen("host-serial:");
@@ -753,7 +711,7 @@
         }
         if (!strncmp(service, "transport", strlen("transport"))) {
             D("SS(%d): okay transport", s->id);
-            p->len = 0;
+            s->smart_socket_data.clear();
             return 0;
         }
 
@@ -824,7 +782,7 @@
     /* give him our transport and upref it */
     s->peer->transport = s->transport;
 
-    connect_to_remote(s->peer, (char*)(p->data + 4));
+    connect_to_remote(s->peer, s->smart_socket_data.data() + 4);
     s->peer = 0;
     s->close(s);
     return 1;
@@ -844,21 +802,17 @@
 
 static void smart_socket_close(asocket* s) {
     D("SS(%d): closed", s->id);
-    if (s->pkt_first) {
-        put_apacket(s->pkt_first);
-    }
     if (s->peer) {
         s->peer->peer = 0;
         s->peer->close(s->peer);
         s->peer = 0;
     }
-    free(s);
+    delete s;
 }
 
 static asocket* create_smart_socket(void) {
     D("Creating smart socket");
-    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
-    if (s == NULL) fatal("cannot allocate socket");
+    asocket* s = new asocket();
     s->enqueue = smart_socket_enqueue;
     s->ready = smart_socket_ready;
     s->shutdown = NULL;
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 5acaaec..6b1a00b 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -34,7 +34,6 @@
 
 #include <android-base/logging.h>
 #include <android-base/parsenetaddress.h>
-#include <android-base/quick_exit.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/thread_annotations.h>
@@ -72,7 +71,14 @@
         return false;
     }
 
-    if (!ReadFdExactly(fd_.get(), &packet->data, packet->msg.data_length)) {
+    if (packet->msg.data_length > MAX_PAYLOAD) {
+        D("remote local: read overflow (data length = %" PRIu32 ")", packet->msg.data_length);
+        return false;
+    }
+
+    packet->payload.resize(packet->msg.data_length);
+
+    if (!ReadFdExactly(fd_.get(), &packet->payload[0], packet->payload.size())) {
         D("remote local: terminated (data)");
         return false;
     }
@@ -81,13 +87,18 @@
 }
 
 bool FdConnection::Write(apacket* packet) {
-    uint32_t length = packet->msg.data_length;
-
-    if (!WriteFdExactly(fd_.get(), &packet->msg, sizeof(amessage) + length)) {
+    if (!WriteFdExactly(fd_.get(), &packet->msg, sizeof(packet->msg))) {
         D("remote local: write terminated");
         return false;
     }
 
+    if (packet->msg.data_length) {
+        if (!WriteFdExactly(fd_.get(), &packet->payload[0], packet->msg.data_length)) {
+            D("remote local: write terminated");
+            return false;
+        }
+    }
+
     return true;
 }
 
@@ -128,7 +139,7 @@
 
     std::string result = android::base::StringPrintf("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ", name,
                                                      func, cmd, arg0, arg1, len);
-    result += dump_hex(p->data, len);
+    result += dump_hex(p->payload.data(), p->payload.size());
     return result;
 }
 
@@ -186,9 +197,10 @@
         apacket* p = 0;
         if (read_packet(fd, t->serial, &p)) {
             D("%s: failed to read packet from transport socket on fd %d", t->serial, fd);
-        } else {
-            handle_packet(p, (atransport*)_t);
+            return;
         }
+
+        handle_packet(p, (atransport*)_t);
     }
 }
 
@@ -238,6 +250,7 @@
     p->msg.arg0 = 1;
     p->msg.arg1 = ++(t->sync_token);
     p->msg.magic = A_SYNC ^ 0xffffffff;
+    D("sending SYNC packet (len = %u, payload.size() = %zu)", p->msg.data_length, p->payload.size());
     if (write_packet(t->fd, t->serial, &p)) {
         put_apacket(p);
         D("%s: failed to write SYNC packet", t->serial);
@@ -331,6 +344,13 @@
             if (active) {
                 D("%s: transport got packet, sending to remote", t->serial);
                 ATRACE_NAME("write_transport write_remote");
+
+                // Allow sending the payload's implicit null terminator.
+                if (p->msg.data_length != p->payload.size()) {
+                    LOG(FATAL) << "packet data length doesn't match payload: msg.data_length = "
+                               << p->msg.data_length << ", payload.size() = " << p->payload.size();
+                }
+
                 if (t->Write(p) != 0) {
                     D("%s: remote write failed for transport", t->serial);
                     put_apacket(p);
@@ -373,9 +393,9 @@
  */
 struct device_tracker {
     asocket socket;
-    bool update_needed;
-    bool long_output;
-    device_tracker* next;
+    bool update_needed = false;
+    bool long_output = false;
+    device_tracker* next = nullptr;
 };
 
 /* linked list of all device trackers */
@@ -406,24 +426,25 @@
         peer->close(peer);
     }
     device_tracker_remove(tracker);
-    free(tracker);
+    delete tracker;
 }
 
-static int device_tracker_enqueue(asocket* socket, apacket* p) {
+static int device_tracker_enqueue(asocket* socket, std::string) {
     /* you can't read from a device tracker, close immediately */
-    put_apacket(p);
     device_tracker_close(socket);
     return -1;
 }
 
 static int device_tracker_send(device_tracker* tracker, const std::string& string) {
-    apacket* p = get_apacket();
     asocket* peer = tracker->socket.peer;
 
-    snprintf(reinterpret_cast<char*>(p->data), 5, "%04x", static_cast<int>(string.size()));
-    memcpy(&p->data[4], string.data(), string.size());
-    p->len = 4 + string.size();
-    return peer->enqueue(peer, p);
+    std::string data;
+    data.resize(4 + string.size());
+    char buf[5];
+    snprintf(buf, sizeof(buf), "%04x", static_cast<int>(string.size()));
+    memcpy(&data[0], buf, 4);
+    memcpy(&data[4], string.data(), string.size());
+    return peer->enqueue(peer, std::move(data));
 }
 
 static void device_tracker_ready(asocket* socket) {
@@ -440,7 +461,7 @@
 }
 
 asocket* create_device_tracker(bool long_output) {
-    device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
+    device_tracker* tracker = new device_tracker();
     if (tracker == nullptr) fatal("cannot allocate device tracker");
 
     D("device tracker %p created", tracker);
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 73e8e15..d7565f6 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -61,9 +61,12 @@
 static int UsbReadPayload(usb_handle* h, apacket* p) {
     D("UsbReadPayload(%d)", p->msg.data_length);
 
+    if (p->msg.data_length > MAX_PAYLOAD) {
+        return -1;
+    }
+
 #if CHECK_PACKET_OVERFLOW
     size_t usb_packet_size = usb_get_max_packet_size(h);
-    CHECK_EQ(0ULL, sizeof(p->data) % usb_packet_size);
 
     // Round the data length up to the nearest packet size boundary.
     // The device won't send a zero packet for packet size aligned payloads,
@@ -73,10 +76,18 @@
     if (rem_size) {
         len += usb_packet_size - rem_size;
     }
-    CHECK_LE(len, sizeof(p->data));
-    return usb_read(h, &p->data, len);
+
+    p->payload.resize(len);
+    int rc = usb_read(h, &p->payload[0], p->payload.size());
+    if (rc != static_cast<int>(p->msg.data_length)) {
+        return -1;
+    }
+
+    p->payload.resize(rc);
+    return rc;
 #else
-    return usb_read(h, &p->data, p->msg.data_length);
+    p->payload.resize(p->msg.data_length);
+    return usb_read(h, &p->payload[0], p->payload.size());
 #endif
 }
 
@@ -116,7 +127,13 @@
     }
 
     if (p->msg.data_length) {
-        if (usb_read(usb, p->data, p->msg.data_length)) {
+        if (p->msg.data_length > MAX_PAYLOAD) {
+            PLOG(ERROR) << "remote usb: read overflow (data length = " << p->msg.data_length << ")";
+            return -1;
+        }
+
+        p->payload.resize(p->msg.data_length);
+        if (usb_read(usb, &p->payload[0], p->payload.size())) {
             PLOG(ERROR) << "remote usb: terminated (data)";
             return -1;
         }
@@ -143,7 +160,7 @@
         return false;
     }
 
-    if (packet->msg.data_length != 0 && usb_write(handle_, &packet->data, size) != 0) {
+    if (packet->msg.data_length != 0 && usb_write(handle_, packet->payload.data(), size) != 0) {
         PLOG(ERROR) << "remote usb: 2 - write terminated";
         return false;
     }
diff --git a/base/Android.bp b/base/Android.bp
index 01800af..5d70d47 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -15,8 +15,12 @@
 //
 
 cc_defaults {
-    name: "libbase_defaults",
-    cflags: ["-Wall", "-Werror", "-Wextra"],
+    name: "libbase_cflags_defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
 }
 
 cc_library_headers {
@@ -35,16 +39,11 @@
     },
 }
 
-cc_library {
-    name: "libbase",
-    defaults: ["libbase_defaults"],
-    vendor_available: true,
-    host_supported: true,
-    vndk: {
-        enabled: true,
-        support_system_process: true,
-    },
+cc_defaults {
+    name: "libbase_defaults",
+    defaults: ["libbase_cflags_defaults"],
     srcs: [
+        "chrono_utils.cpp",
         "file.cpp",
         "logging.cpp",
         "parsenetaddress.cpp",
@@ -54,11 +53,6 @@
         "test_utils.cpp",
     ],
 
-    header_libs: [
-        "libbase_headers",
-    ],
-    export_header_lib_headers: ["libbase_headers"],
-
     shared_libs: ["liblog"],
     target: {
         android: {
@@ -72,14 +66,12 @@
         },
         linux: {
             srcs: [
-                "chrono_utils.cpp",
                 "errors_unix.cpp",
             ],
             cppflags: ["-Wexit-time-destructors"],
         },
         darwin: {
             srcs: [
-                "chrono_utils.cpp",
                 "errors_unix.cpp",
             ],
             cppflags: ["-Wexit-time-destructors"],
@@ -97,11 +89,34 @@
     },
 }
 
+cc_library {
+    name: "libbase",
+    defaults: ["libbase_defaults"],
+    vendor_available: true,
+    host_supported: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
+    header_libs: [
+        "libbase_headers",
+    ],
+    export_header_lib_headers: ["libbase_headers"],
+}
+
+cc_library_static {
+    name: "libbase_ndk",
+    defaults: ["libbase_defaults"],
+    sdk_version: "current",
+    stl: "c++_static",
+    export_include_dirs: ["include"],
+}
+
 // Tests
 // ------------------------------------------------------------------------------
 cc_test {
     name: "libbase_test",
-    defaults: ["libbase_defaults"],
+    defaults: ["libbase_cflags_defaults"],
     host_supported: true,
     srcs: [
         "endian_test.cpp",
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
index dbe5483..19080a5 100644
--- a/base/chrono_utils.cpp
+++ b/base/chrono_utils.cpp
@@ -28,7 +28,7 @@
   return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
                                 std::chrono::nanoseconds(ts.tv_nsec));
 #else
-  // Darwin does not support clock_gettime.
+  // Darwin and Windows do not support clock_gettime.
   return boot_clock::time_point();
 #endif  // __linux__
 }
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
index 7679d4c..c3396ee 100644
--- a/base/include/android-base/chrono_utils.h
+++ b/base/include/android-base/chrono_utils.h
@@ -20,7 +20,9 @@
 #include <chrono>
 #include <sstream>
 
+#if __cplusplus > 201103L  // C++14
 using namespace std::chrono_literals;
+#endif
 
 namespace android {
 namespace base {
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index c11acb1..4d9fa34 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -67,8 +67,8 @@
 // TODO: string_view
 bool EndsWith(const std::string& s, const char* suffix);
 bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
-bool EndsWith(const std::string& s, const std::string& prefix);
-bool EndsWithIgnoreCase(const std::string& s, const std::string& prefix);
+bool EndsWith(const std::string& s, const std::string& suffix);
+bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix);
 
 // Tests whether 'lhs' equals 'rhs', ignoring case.
 bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
diff --git a/base/logging.cpp b/base/logging.cpp
index 0f2012a..1f7bc2a 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -45,7 +45,7 @@
 
 // Headers for LogMessage::LogLine.
 #ifdef __ANDROID__
-#include <log/log.h>
+#include <android/log.h>
 #include <android/set_abort_message.h>
 #else
 #include <sys/types.h>
diff --git a/base/properties.cpp b/base/properties.cpp
index cde4d69..6cf43f9 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -23,12 +23,11 @@
 
 #include <algorithm>
 #include <chrono>
+#include <limits>
 #include <string>
 
 #include <android-base/parseint.h>
 
-using namespace std::chrono_literals;
-
 namespace android {
 namespace base {
 
@@ -109,7 +108,7 @@
 static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
   auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
   auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
-  ts.tv_sec = s.count();
+  ts.tv_sec = std::min<std::chrono::seconds::rep>(s.count(), std::numeric_limits<time_t>::max());
   ts.tv_nsec = ns.count();
 }
 
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index 2c87018..dd9ba88 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -66,6 +66,9 @@
     shared_libs: ["liblogcat"],
     init_rc: ["bootstat.rc"],
     product_variables: {
+        pdk: {
+            enabled: false,
+        },
         debuggable: {
             init_rc: ["bootstat-debug.rc"],
         },
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 5323524..7e6f24d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -16,7 +16,7 @@
 
 cc_library_headers {
     name: "libdebuggerd_common_headers",
-    export_include_dirs: ["common/include"]
+    export_include_dirs: ["common/include"],
 }
 
 cc_library_shared {
@@ -39,7 +39,7 @@
     ],
 
     export_header_lib_headers: ["libdebuggerd_common_headers"],
-    export_include_dirs: ["tombstoned/include"]
+    export_include_dirs: ["tombstoned/include"],
 }
 
 // Utility library to tombstoned and get an output fd.
@@ -60,7 +60,7 @@
     ],
 
     export_header_lib_headers: ["libdebuggerd_common_headers"],
-    export_include_dirs: ["tombstoned/include"]
+    export_include_dirs: ["tombstoned/include"],
 }
 
 // Core implementation, linked into libdebuggerd_handler and the dynamic linker.
@@ -186,7 +186,10 @@
                 "client/debuggerd_client_test.cpp",
                 "debuggerd_test.cpp",
             ],
-            static_libs: ["libasync_safe", "libtombstoned_client_static"],
+            static_libs: [
+                "libasync_safe",
+                "libtombstoned_client_static",
+            ],
         },
     },
 
@@ -197,7 +200,7 @@
         "libdebuggerd_client",
         "liblog",
         "libminijail",
-        "libnativehelper"
+        "libnativehelper",
     ],
 
     static_libs: [
@@ -298,7 +301,7 @@
         "liblog",
     ],
 
-    init_rc: ["tombstoned/tombstoned.rc"]
+    init_rc: ["tombstoned/tombstoned.rc"],
 }
 
 subdirs = [
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 3bc1742..c03b41d 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -20,4 +20,5 @@
 LOCAL_SRC_FILES_arm64 := seccomp_policy/crash_dump.arm64.policy
 LOCAL_SRC_FILES_x86 := seccomp_policy/crash_dump.x86.policy
 LOCAL_SRC_FILES_x86_64 := seccomp_policy/crash_dump.x86_64.policy
+LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
 include $(BUILD_PREBUILT)
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index f8b4bad..397ff2f 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -20,6 +20,7 @@
 #include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/ptrace.h>
+#include <sys/resource.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -570,7 +571,7 @@
 static const char* const kDebuggerdSeccompPolicy =
     "/system/etc/seccomp_policy/crash_dump." ABI_STRING ".policy";
 
-pid_t seccomp_fork() {
+static pid_t seccomp_fork_impl(void (*prejail)()) {
   unique_fd policy_fd(open(kDebuggerdSeccompPolicy, O_RDONLY | O_CLOEXEC));
   if (policy_fd == -1) {
     LOG(FATAL) << "failed to open policy " << kDebuggerdSeccompPolicy;
@@ -607,10 +608,18 @@
     continue;
   }
 
+  if (prejail) {
+    prejail();
+  }
+
   minijail_enter(jail.get());
   return result;
 }
 
+static pid_t seccomp_fork() {
+  return seccomp_fork_impl(nullptr);
+}
+
 TEST_F(CrasherTest, seccomp_crash) {
   int intercept_result;
   unique_fd output_fd;
@@ -628,6 +637,46 @@
   ASSERT_BACKTRACE_FRAME(result, "abort");
 }
 
+static pid_t seccomp_fork_rlimit() {
+  return seccomp_fork_impl([]() {
+    struct rlimit rlim = {
+        .rlim_cur = 512 * 1024 * 1024,
+        .rlim_max = 512 * 1024 * 1024,
+    };
+
+    if (setrlimit(RLIMIT_AS, &rlim) != 0) {
+      raise(SIGINT);
+    }
+  });
+}
+
+TEST_F(CrasherTest, seccomp_crash_oom) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  StartProcess(
+      []() {
+        std::vector<void*> vec;
+        for (int i = 0; i < 512; ++i) {
+          char* buf = static_cast<char*>(malloc(1024 * 1024));
+          if (!buf) {
+            abort();
+          }
+          memset(buf, 0xff, 1024 * 1024);
+          vec.push_back(buf);
+        }
+      },
+      &seccomp_fork_rlimit);
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  // We can't actually generate a backtrace, just make sure that the process terminates.
+}
+
 __attribute__((noinline)) extern "C" bool raise_debugger_signal(DebuggerdDumpType dump_type) {
   siginfo_t siginfo;
   siginfo.si_code = SI_QUEUE;
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 5fddddc..dea2e17 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -37,6 +37,7 @@
 
 #include <atomic>
 #include <memory>
+#include <mutex>
 
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
@@ -55,7 +56,7 @@
 using android::base::unique_fd;
 using unwindstack::Regs;
 
-extern "C" void __linker_enable_fallback_allocator();
+extern "C" bool __linker_enable_fallback_allocator();
 extern "C" void __linker_disable_fallback_allocator();
 
 // This is incredibly sketchy to do inside of a signal handler, especially when libbacktrace
@@ -65,7 +66,11 @@
 // This isn't the default method of dumping because it can fail in cases such as address space
 // exhaustion.
 static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) {
-  __linker_enable_fallback_allocator();
+  if (!__linker_enable_fallback_allocator()) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
+    return;
+  }
+
   {
     std::unique_ptr<Regs> regs;
 
@@ -84,7 +89,11 @@
 
 static void debuggerd_fallback_tombstone(int output_fd, ucontext_t* ucontext, siginfo_t* siginfo,
                                          void* abort_message) {
-  __linker_enable_fallback_allocator();
+  if (!__linker_enable_fallback_allocator()) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
+    return;
+  }
+
   engrave_tombstone_ucontext(output_fd, reinterpret_cast<uintptr_t>(abort_message), siginfo,
                              ucontext);
   __linker_disable_fallback_allocator();
@@ -116,7 +125,7 @@
   closedir(dir);
 }
 
-static bool forward_output(int src_fd, int dst_fd) {
+static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
   // Make sure the thread actually got the signal.
   struct pollfd pfd = {
     .fd = src_fd, .events = POLLIN,
@@ -127,6 +136,18 @@
     return false;
   }
 
+  pid_t tid;
+  if (TEMP_FAILURE_RETRY(read(src_fd, &tid, sizeof(tid))) != sizeof(tid)) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to read tid");
+    return false;
+  }
+
+  if (tid != expected_tid) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "received tid %d, expected %d", tid,
+                          expected_tid);
+    return false;
+  }
+
   while (true) {
     char buf[512];
     ssize_t rc = TEMP_FAILURE_RETRY(read(src_fd, buf, sizeof(buf)));
@@ -144,16 +165,54 @@
   }
 }
 
+struct __attribute__((__packed__)) packed_thread_output {
+  int32_t tid;
+  int32_t fd;
+};
+
+static uint64_t pack_thread_fd(pid_t tid, int fd) {
+  packed_thread_output packed = {.tid = tid, .fd = fd};
+  uint64_t result;
+  static_assert(sizeof(packed) == sizeof(result));
+  memcpy(&result, &packed, sizeof(packed));
+  return result;
+}
+
+static std::pair<pid_t, int> unpack_thread_fd(uint64_t value) {
+  packed_thread_output result;
+  memcpy(&result, &value, sizeof(value));
+  return std::make_pair(result.tid, result.fd);
+}
+
 static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
-  static std::atomic<int> trace_output_fd(-1);
+  static std::atomic<uint64_t> trace_output(pack_thread_fd(-1, -1));
 
   if (info->si_value.sival_int == ~0) {
     // Asked to dump by the original signal recipient.
-    debuggerd_fallback_trace(trace_output_fd, ucontext);
+    uint64_t val = trace_output.load();
+    auto [tid, fd] = unpack_thread_fd(val);
+    if (tid != gettid()) {
+      // We received some other thread's info request?
+      async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                            "thread %d received output fd for thread %d?", gettid(), tid);
+      return;
+    }
 
-    int tmp = trace_output_fd.load();
-    trace_output_fd.store(-1);
-    close(tmp);
+    if (!trace_output.compare_exchange_strong(val, pack_thread_fd(-1, -1))) {
+      // Presumably, the timeout in forward_output expired, and the main thread moved on.
+      // If this happened, the main thread closed our fd for us, so just return.
+      async_safe_format_log(ANDROID_LOG_ERROR, "libc", "cmpxchg for thread %d failed", gettid());
+      return;
+    }
+
+    // Write our tid to the output fd to let the main thread know that we're working.
+    if (TEMP_FAILURE_RETRY(write(fd, &tid, sizeof(tid))) == sizeof(tid)) {
+      debuggerd_fallback_trace(fd, ucontext);
+    } else {
+      async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write to output fd");
+    }
+
+    close(fd);
     return;
   }
 
@@ -189,7 +248,14 @@
           return false;
         }
 
-        trace_output_fd.store(pipe_write.get());
+        uint64_t expected = pack_thread_fd(-1, -1);
+        if (!trace_output.compare_exchange_strong(expected,
+                                                  pack_thread_fd(tid, pipe_write.release()))) {
+          auto [tid, fd] = unpack_thread_fd(expected);
+          async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                                "thread %d is already outputting to fd %d?", tid, fd);
+          return false;
+        }
 
         siginfo_t siginfo = {};
         siginfo.si_code = SI_QUEUE;
@@ -203,12 +269,20 @@
           return false;
         }
 
-        bool success = forward_output(pipe_read.get(), output_fd);
-        if (success) {
-          // The signaled thread has closed trace_output_fd already.
-          (void)pipe_write.release();
-        } else {
-          trace_output_fd.store(-1);
+        bool success = forward_output(pipe_read.get(), output_fd, tid);
+        if (!success) {
+          async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                                "timeout expired while waiting for thread %d to dump", tid);
+        }
+
+        // Regardless of whether the poll succeeds, check to see if the thread took fd ownership.
+        uint64_t post_wait = trace_output.exchange(pack_thread_fd(-1, -1));
+        if (post_wait != pack_thread_fd(-1, -1)) {
+          auto [tid, fd] = unpack_thread_fd(post_wait);
+          if (fd != -1) {
+            async_safe_format_log(ANDROID_LOG_ERROR, "libc", "closing fd %d for thread %d", fd, tid);
+            close(fd);
+          }
         }
 
         return true;
@@ -225,11 +299,13 @@
 static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
   // Only allow one thread to handle a crash at a time (this can happen multiple times without
   // exit, since tombstones can be requested without a real crash happening.)
-  static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
-  int ret = pthread_mutex_lock(&crash_mutex);
-  if (ret != 0) {
-    async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
-    return;
+  static std::recursive_mutex crash_mutex;
+  static int lock_count;
+
+  crash_mutex.lock();
+  if (lock_count++ > 0) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "recursed signal handler call, exiting");
+    _exit(1);
   }
 
   unique_fd tombstone_socket, output_fd;
@@ -240,7 +316,8 @@
     tombstoned_notify_completion(tombstone_socket.get());
   }
 
-  pthread_mutex_unlock(&crash_mutex);
+  --lock_count;
+  crash_mutex.unlock();
 }
 
 extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 0f049fd..7b04e71 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -68,7 +68,7 @@
 class Memory;
 }
 
-void dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const char* fmt, ...);
+void dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const std::string&);
 
 void read_with_default(const char* path, char* buf, size_t len, const char* default_value);
 
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index 7c8a0ea..be39582 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -201,7 +201,7 @@
   }
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, memory_mock_.get(), 0x12345678, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x12345678, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -221,7 +221,7 @@
   memory_mock_->SetReadData(buffer, sizeof(buffer));
   memory_mock_->SetPartialReadAmount(96);
 
-  dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -240,7 +240,7 @@
   }
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -253,7 +253,7 @@
 }
 
 TEST_F(DumpMemoryTest, memory_unreadable) {
-  dump_memory(&log_, memory_mock_.get(), 0xa2345678, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0xa2345678, "memory near pc");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -309,7 +309,7 @@
   }
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -329,7 +329,7 @@
   memory_mock_->SetReadData(buffer, sizeof(buffer));
   memory_mock_->SetPartialReadAmount(102);
 
-  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -354,7 +354,7 @@
   memory_mock_->SetReadData(buffer, sizeof(buffer));
   memory_mock_->SetPartialReadAmount(45);
 
-  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -380,7 +380,7 @@
   memset(buffer, 0, sizeof(buffer));
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, memory_mock_.get(), 0x1000, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x1000, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -434,7 +434,7 @@
   memset(buffer, 0, sizeof(buffer));
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, memory_mock_.get(), 0, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -452,13 +452,13 @@
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
 #if defined(__LP64__)
-  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1");
-  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1");
-  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL, "memory near r1");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 32, "memory near r1");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 216, "memory near r1");
 #else
-  dump_memory(&log_, memory_mock_.get(), 0xffff0000, "memory near %.2s:", "r1");
-  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1");
-  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000, "memory near r1");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 32, "memory near r1");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 220, "memory near r1");
 #endif
 
   std::string tombstone_contents;
@@ -477,9 +477,9 @@
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
 #if defined(__LP64__)
-  dump_memory(&log_, memory_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xfffffffffffffff0, "memory near r1");
 #else
-  dump_memory(&log_, memory_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xfffffff0, "memory near r1");
 #endif
 
   std::string tombstone_contents;
@@ -500,9 +500,9 @@
   memory_mock_->SetReadData(buffer, sizeof(buffer));
 
 #if defined(__LP64__)
-  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 224, "memory near r4");
 #else
-  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 224, "memory near r4");
 #endif
 
   std::string tombstone_contents;
@@ -562,7 +562,7 @@
 
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   uintptr_t addr = 0x10000020 + page_size - 120;
-  dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -621,7 +621,7 @@
 
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   uintptr_t addr = 0x10000020 + page_size - 192;
-  dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -679,7 +679,7 @@
   memory_mock_->SetPartialReadAmount(0);
 
   uintptr_t addr = 0x10000020;
-  dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -739,7 +739,7 @@
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   uintptr_t addr = 0x10000020 + page_size - 256;
 
-  dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 7d85602..140ef6d 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -65,6 +65,8 @@
 using unwindstack::Memory;
 using unwindstack::Regs;
 
+using namespace std::literals::string_literals;
+
 #define STACK_WORDS 16
 
 static void dump_header_info(log_t* log) {
@@ -148,8 +150,9 @@
 
     backtrace_map_t map;
     backtrace_map->FillIn(stack_data[i], &map);
-    if (BacktraceMap::IsValid(map) && !map.name.empty()) {
-      line += "  " + map.name;
+    std::string map_name{map.Name()};
+    if (BacktraceMap::IsValid(map) && !map_name.empty()) {
+      line += "  " + map_name;
       uint64_t offset = 0;
       std::string func_name = backtrace_map->GetFunctionName(stack_data[i], &offset);
       if (!func_name.empty()) {
@@ -382,9 +385,16 @@
   print_register_row(log, special_row);
 }
 
-void dump_memory_and_code(log_t* log, Memory* memory, Regs* regs) {
-  regs->IterateRegisters([log, memory](const char* name, uint64_t value) {
-    dump_memory(log, memory, value, "memory near %s:", name);
+void dump_memory_and_code(log_t* log, BacktraceMap* map, Memory* memory, Regs* regs) {
+  regs->IterateRegisters([log, map, memory](const char* reg_name, uint64_t reg_value) {
+    std::string label{"memory near "s + reg_name};
+    if (map) {
+      backtrace_map_t map_info;
+      map->FillIn(reg_value, &map_info);
+      std::string map_name{map_info.Name()};
+      if (!map_name.empty()) label += " (" + map_info.Name() + ")";
+    }
+    dump_memory(log, memory, reg_value, label);
   });
 }
 
@@ -423,7 +433,7 @@
   }
 
   if (primary_thread) {
-    dump_memory_and_code(log, process_memory, thread_info.registers.get());
+    dump_memory_and_code(log, map, process_memory, thread_info.registers.get());
     if (map) {
       uint64_t addr = 0;
       siginfo_t* si = thread_info.siginfo;
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 3ac98f5..d153865 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -124,13 +124,7 @@
 #define MEMORY_BYTES_TO_DUMP 256
 #define MEMORY_BYTES_PER_LINE 16
 
-void dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const char* fmt, ...) {
-  std::string log_msg;
-  va_list ap;
-  va_start(ap, fmt);
-  android::base::StringAppendV(&log_msg, fmt, ap);
-  va_end(ap);
-
+void dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const std::string& label) {
   // Align the address to sizeof(long) and start 32 bytes before the address.
   addr &= ~(sizeof(long) - 1);
   if (addr >= 4128) {
@@ -147,7 +141,7 @@
     return;
   }
 
-  _LOG(log, logtype::MEMORY, "\n%s\n", log_msg.c_str());
+  _LOG(log, logtype::MEMORY, "\n%s:\n", label.c_str());
 
   // Dump 256 bytes
   uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
diff --git a/debuggerd/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy
index c64e288..254330d 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm.policy
@@ -22,15 +22,15 @@
 process_vm_readv: 1
 tgkill: 1
 rt_sigprocmask: 1
+rt_sigaction: 1
 rt_tgsigqueueinfo: 1
 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
 madvise: 1
-mprotect: arg2 in PROT_READ|PROT_WRITE
+mprotect: arg2 in 0x1|0x2
 munmap: 1
 getuid32: 1
 fstat64: 1
-mmap2: arg2 in PROT_READ|PROT_WRITE
-sigaction: 1
+mmap2: arg2 in 0x1|0x2
 geteuid32: 1
 getgid32: 1
 getegid32: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 0c689bb..9b3ef09 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -21,15 +21,15 @@
 process_vm_readv: 1
 tgkill: 1
 rt_sigprocmask: 1
+rt_sigaction: 1
 rt_tgsigqueueinfo: 1
 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
 madvise: 1
-mprotect: arg2 in PROT_READ|PROT_WRITE
+mprotect: arg2 in 0x1|0x2
 munmap: 1
 getuid: 1
 fstat: 1
-mmap: arg2 in PROT_READ|PROT_WRITE
-rt_sigaction: 1
+mmap: arg2 in 0x1|0x2
 geteuid: 1
 getgid: 1
 getegid: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index dadffac..2ef31b0 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -29,11 +29,20 @@
 
 tgkill: 1
 rt_sigprocmask: 1
+rt_sigaction: 1
 rt_tgsigqueueinfo: 1
 
 #define PR_SET_VMA 0x53564d41
 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA
 
+#if 0
+libminijail on vendor partitions older than P does not have constants from <sys/mman.h>.
+Define the values of PROT_READ and PROT_WRITE ourselves to maintain backwards compatibility.
+#else
+#define PROT_READ 0x1
+#define PROT_WRITE 0x2
+#endif
+
 madvise: 1
 mprotect: arg2 in PROT_READ|PROT_WRITE
 munmap: 1
@@ -42,12 +51,10 @@
 getuid: 1
 fstat: 1
 mmap: arg2 in PROT_READ|PROT_WRITE
-rt_sigaction: 1
 #else
 getuid32: 1
 fstat64: 1
 mmap2: arg2 in PROT_READ|PROT_WRITE
-sigaction: 1
 #endif
 
 // Needed for logging.
diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy
index c64e288..254330d 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86.policy
@@ -22,15 +22,15 @@
 process_vm_readv: 1
 tgkill: 1
 rt_sigprocmask: 1
+rt_sigaction: 1
 rt_tgsigqueueinfo: 1
 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
 madvise: 1
-mprotect: arg2 in PROT_READ|PROT_WRITE
+mprotect: arg2 in 0x1|0x2
 munmap: 1
 getuid32: 1
 fstat64: 1
-mmap2: arg2 in PROT_READ|PROT_WRITE
-sigaction: 1
+mmap2: arg2 in 0x1|0x2
 geteuid32: 1
 getgid32: 1
 getegid32: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86_64.policy b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
index 0c689bb..9b3ef09 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86_64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
@@ -21,15 +21,15 @@
 process_vm_readv: 1
 tgkill: 1
 rt_sigprocmask: 1
+rt_sigaction: 1
 rt_tgsigqueueinfo: 1
 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
 madvise: 1
-mprotect: arg2 in PROT_READ|PROT_WRITE
+mprotect: arg2 in 0x1|0x2
 munmap: 1
 getuid: 1
 fstat: 1
-mmap: arg2 in PROT_READ|PROT_WRITE
-rt_sigaction: 1
+mmap: arg2 in 0x1|0x2
 geteuid: 1
 getgid: 1
 getegid: 1
diff --git a/demangle/Android.bp b/demangle/Android.bp
index 89b8772..8d5b135 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -42,11 +42,11 @@
     ],
 
     local_include_dirs: [
-       "include",
+        "include",
     ],
 
     export_include_dirs: [
-       "include",
+        "include",
     ],
 }
 
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index 46a6f76..1787031 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -509,6 +509,29 @@
   ASSERT_EQ("_ZTH_N3oneE", demangler.Parse("_ZTH_N3oneE"));
 }
 
+TEST(DemangleTest, r_value_reference) {
+  Demangler demangler;
+  ASSERT_EQ(
+      "android::SurfaceComposerClient::Transaction::merge(android::SurfaceComposerClient::"
+      "Transaction&&)",
+      demangler.Parse("_ZN7android21SurfaceComposerClient11Transaction5mergeEOS1_"));
+}
+
+TEST(DemangleTest, initial_St) {
+  Demangler demangler;
+  EXPECT_EQ("std::state", demangler.Parse("_ZSt5state"));
+  EXPECT_EQ("std::_In::ward", demangler.Parse("_ZNSt3_In4wardE"));
+  EXPECT_EQ("std::__terminate(void (*)())", demangler.Parse("_ZSt11__terminatePFvvE"));
+}
+
+TEST(DemangleTest, cfi) {
+  Demangler demangler;
+  EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*)",
+            demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB"));
+  EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*) [clone .cfi]",
+            demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB.cfi"));
+}
+
 TEST(DemangleTest, demangle) {
   std::string str;
 
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index af2816c..7a3aa81 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -580,6 +580,10 @@
     }
     return name + 1;
 
+  case 'O':
+    cur_state_.suffixes.push_back("&&");
+    return name + 1;
+
   case 'K':
   case 'V': {
     const char* suffix;
@@ -701,6 +705,9 @@
         cur_state_.str.clear();
       }
       return name;
+    } else if (strcmp(name, ".cfi") == 0) {
+      function_suffix_ += " [clone .cfi]";
+      return name + 4;
     }
   }
   return nullptr;
@@ -816,6 +823,16 @@
     return name + 1;
   }
 
+  if (*name == 'S') {
+    name++;
+    if (*name == 't') {
+      function_name_ = "std::";
+      name++;
+    } else {
+      return nullptr;
+    }
+  }
+
   if (std::isdigit(*name)) {
     name = GetStringFromLength(name, &function_name_);
   } else if (*name == 'L' && std::isdigit(name[1])) {
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index a2b80ad..9aab0ba 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -32,11 +32,14 @@
 #include <unistd.h>
 
 #include <memory>
+#include <string>
 #include <thread>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/android_filesystem_config.h>
 #include <cutils/android_reboot.h>
@@ -775,6 +778,22 @@
     }
 }
 
+static bool call_vdc(const std::vector<std::string>& args) {
+    std::vector<char const*> argv;
+    argv.emplace_back("/system/bin/vdc");
+    for (auto& arg : args) {
+        argv.emplace_back(arg.c_str());
+    }
+    LOG(INFO) << "Calling: " << android::base::Join(argv, ' ');
+    int ret = android_fork_execvp(4, const_cast<char**>(argv.data()), nullptr, false, true);
+    if (ret != 0) {
+        LOG(ERROR) << "vdc returned error code: " << ret;
+        return false;
+    }
+    LOG(DEBUG) << "vdc finished successfully";
+    return true;
+}
+
 /* When multiple fstab records share the same mount_point, it will
  * try to mount each one in turn, and ignore any duplicates after a
  * first successful mount.
@@ -881,6 +900,13 @@
                     LERROR << "Only one encryptable/encrypted partition supported";
                 }
                 encryptable = status;
+                if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
+                    if (!call_vdc(
+                            {"cryptfs", "encryptFstab", fstab->recs[attempted_idx].mount_point})) {
+                        LERROR << "Encryption failed";
+                        return FS_MGR_MNTALL_FAIL;
+                    }
+                }
             }
 
             /* Success!  Go get the next one */
@@ -955,7 +981,11 @@
             encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
         } else if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
                    should_use_metadata_encryption(&fstab->recs[attempted_idx])) {
+            if (!call_vdc({"cryptfs", "mountFstab", fstab->recs[attempted_idx].mount_point})) {
+                ++error_count;
+            }
             encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
+            continue;
         } else {
             // fs_options might be null so we cannot use PERROR << directly.
             // Use StringPrintf to output "(null)" instead.
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 1c01d8c..9b7405a 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -39,6 +39,7 @@
     char *key_loc;
     char* key_dir;
     char *verity_loc;
+    char *sysfs_path;
     long long part_length;
     char *label;
     int partnum;
@@ -104,6 +105,7 @@
     {"quota", MF_QUOTA},
     {"eraseblk=", MF_ERASEBLKSIZE},
     {"logicalblk=", MF_LOGICALBLKSIZE},
+    {"sysfs_path=", MF_SYSFS},
     {"defaults", 0},
     {0, 0},
 };
@@ -341,6 +343,9 @@
                     unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
                     if (val >= 4096 && (val & (val - 1)) == 0)
                         flag_vals->logical_blk_size = val;
+                } else if ((fl[i].flag == MF_SYSFS) && flag_vals) {
+                    /* The path to trigger device gc by idle-maint of vold. */
+                    flag_vals->sysfs_path = strdup(strchr(p, '=') + 1);
                 }
                 break;
             }
@@ -615,6 +620,7 @@
         fstab->recs[cnt].file_names_mode = flag_vals.file_names_mode;
         fstab->recs[cnt].erase_blk_size = flag_vals.erase_blk_size;
         fstab->recs[cnt].logical_blk_size = flag_vals.logical_blk_size;
+        fstab->recs[cnt].sysfs_path = flag_vals.sysfs_path;
         cnt++;
     }
     /* If an A/B partition, modify block device to be the real block device */
@@ -787,6 +793,7 @@
         free(fstab->recs[i].key_loc);
         free(fstab->recs[i].key_dir);
         free(fstab->recs[i].label);
+        free(fstab->recs[i].sysfs_path);
     }
 
     /* Free the fstab_recs array created by calloc(3) */
@@ -922,3 +929,8 @@
 int fs_mgr_is_quota(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_QUOTA;
 }
+
+int fs_mgr_has_sysfs_path(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_SYSFS;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 724156d..ade0cc4 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -109,7 +109,8 @@
 #define MF_ERASEBLKSIZE     0x800000
 #define MF_LOGICALBLKSIZE  0X1000000
 #define MF_AVB             0X2000000
-#define MF_KEYDIRECTORY 0X4000000
+#define MF_KEYDIRECTORY    0X4000000
+#define MF_SYSFS           0X8000000
 
 #define DM_BUF_SIZE 4096
 
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 94aacfd..8c585dd 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -56,6 +56,7 @@
     unsigned int file_names_mode;
     unsigned int erase_blk_size;
     unsigned int logical_blk_size;
+    char* sysfs_path;
 };
 
 struct fstab* fs_mgr_read_fstab_default();
@@ -83,6 +84,7 @@
 int fs_mgr_is_nofail(const struct fstab_rec* fstab);
 int fs_mgr_is_latemount(const struct fstab_rec* fstab);
 int fs_mgr_is_quota(const struct fstab_rec* fstab);
+int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
 
 std::string fs_mgr_get_slot_suffix();
 
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 676ee41..08b8b26 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -42,7 +42,6 @@
 #define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
 #define FAKE_BATTERY_CAPACITY 42
 #define FAKE_BATTERY_TEMPERATURE 424
-#define ALWAYS_PLUGGED_CAPACITY 100
 #define MILLION 1.0e6
 #define DEFAULT_VBUS_VOLTAGE 5000000
 
@@ -81,8 +80,11 @@
     props->batteryTechnology.clear();
 }
 
-BatteryMonitor::BatteryMonitor() : mHealthdConfig(nullptr), mBatteryDevicePresent(false),
-    mAlwaysPluggedDevice(false), mBatteryFixedCapacity(0), mBatteryFixedTemperature(0) {
+BatteryMonitor::BatteryMonitor()
+    : mHealthdConfig(nullptr),
+      mBatteryDevicePresent(false),
+      mBatteryFixedCapacity(0),
+      mBatteryFixedTemperature(0) {
     initBatteryProperties(&props);
 }
 
@@ -223,15 +225,6 @@
         mBatteryFixedTemperature :
         getIntField(mHealthdConfig->batteryTemperaturePath);
 
-    // For devices which do not have battery and are always plugged
-    // into power souce.
-    if (mAlwaysPluggedDevice) {
-        props.chargerAcOnline = true;
-        props.batteryPresent = true;
-        props.batteryStatus = BATTERY_STATUS_CHARGING;
-        props.batteryHealth = BATTERY_HEALTH_GOOD;
-    }
-
     std::string buf;
 
     if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
@@ -405,11 +398,7 @@
         break;
 
     case BATTERY_PROP_BATTERY_STATUS:
-        if (mAlwaysPluggedDevice) {
-            val->valueInt64 = BATTERY_STATUS_CHARGING;
-        } else {
-            val->valueInt64 = getChargeStatus();
-        }
+        val->valueInt64 = getChargeStatus();
         ret = NO_ERROR;
         break;
 
@@ -628,9 +617,6 @@
         KLOG_WARNING(LOG_TAG, "No battery devices found\n");
         hc->periodic_chores_interval_fast = -1;
         hc->periodic_chores_interval_slow = -1;
-        mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
-        mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
-        mAlwaysPluggedDevice = true;
     } else {
         if (mHealthdConfig->batteryStatusPath.isEmpty())
             KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 8865a7d..194e667 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -48,7 +48,6 @@
     struct healthd_config *mHealthdConfig;
     Vector<String8> mChargerNames;
     bool mBatteryDevicePresent;
-    bool mAlwaysPluggedDevice;
     int mBatteryFixedCapacity;
     int mBatteryFixedTemperature;
     struct BatteryProperties props;
diff --git a/init/Android.bp b/init/Android.bp
index 6c80ee6..31c8efb 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -56,7 +56,7 @@
         },
         uml: {
             cppflags: ["-DUSER_MODE_LINUX"],
-        }
+        },
     },
     static_libs: [
         "libbootloader_message",
@@ -93,6 +93,8 @@
     defaults: ["init_defaults"],
     srcs: [
         "action.cpp",
+        "action_manager.cpp",
+        "action_parser.cpp",
         "bootchart.cpp",
         "builtins.cpp",
         "capabilities.cpp",
@@ -192,4 +194,67 @@
     static_libs: ["libinit"],
 }
 
+// Host Verifier
+// ------------------------------------------------------------------------------
+
+genrule {
+    name: "generated_stub_builtin_function_map",
+    out: ["generated_stub_builtin_function_map.h"],
+    srcs: ["builtins.cpp"],
+    cmd: "sed -n '/Builtin-function-map start/{:a;n;/Builtin-function-map end/q;p;ba}' $(in) | sed -e 's/do_[^}]*/do_stub/g' > $(out)",
+}
+
+cc_binary {
+    name: "host_init_verifier",
+    host_supported: true,
+    cpp_std: "experimental",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wno-unused-parameter",
+        "-Werror",
+    ],
+    static_libs: [
+        "libbase",
+        "libselinux",
+    ],
+    whole_static_libs: ["libcap"],
+    shared_libs: [
+        "libprotobuf-cpp-lite",
+        "libhidl-gen-utils",
+        "libprocessgroup",
+        "liblog",
+        "libcutils",
+    ],
+    srcs: [
+        "action.cpp",
+        "action_manager.cpp",
+        "action_parser.cpp",
+        "capabilities.cpp",
+        "descriptors.cpp",
+        "import_parser.cpp",
+        "host_init_parser.cpp",
+        "host_init_stubs.cpp",
+        "parser.cpp",
+        "rlimit_parser.cpp",
+        "tokenizer.cpp",
+        "service.cpp",
+        "subcontext.cpp",
+        "subcontext.proto",
+        "util.cpp",
+    ],
+    proto: {
+        type: "lite",
+    },
+    generated_headers: ["generated_stub_builtin_function_map"],
+    target: {
+        android: {
+            enabled: false,
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
 subdirs = ["*"]
diff --git a/init/README.md b/init/README.md
index d7edf21..5c2352b 100644
--- a/init/README.md
+++ b/init/README.md
@@ -10,7 +10,11 @@
 whitespace from breaking text into multiple tokens.  The backslash,
 when it is the last character on a line, may be used for line-folding.
 
-Lines which start with a # (leading whitespace allowed) are comments.
+Lines which start with a `#` (leading whitespace allowed) are comments.
+
+System properties can be expanded using the syntax
+`${property.name}`. This also works in contexts where concatenation is
+required, such as `import /init.recovery.${ro.hardware}.rc`.
 
 Actions and Services implicitly declare a new section.  All commands
 or options belong to the section most recently declared.  Commands
diff --git a/init/action.cpp b/init/action.cpp
index ba03e66..f782b51 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,14 +18,17 @@
 
 #include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
-#include <android-base/properties.h>
 #include <android-base/strings.h>
 
-#include "stable_properties.h"
 #include "util.h"
 
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+#else
+#include "host_init_stubs.h"
+#endif
+
 using android::base::Join;
-using android::base::StartsWith;
 
 namespace android {
 namespace init {
@@ -70,8 +73,15 @@
     return Join(args_, ' ');
 }
 
-Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line)
-    : oneshot_(oneshot), subcontext_(subcontext), filename_(filename), line_(line) {}
+Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line,
+               const std::string& event_trigger,
+               const std::map<std::string, std::string>& property_triggers)
+    : property_triggers_(property_triggers),
+      event_trigger_(event_trigger),
+      oneshot_(oneshot),
+      subcontext_(subcontext),
+      filename_(filename),
+      line_(line) {}
 
 const KeywordFunctionMap* Action::function_map_ = nullptr;
 
@@ -135,85 +145,6 @@
     }
 }
 
-static bool IsActionableProperty(Subcontext* subcontext, const std::string& prop_name) {
-    static bool enabled =
-        android::base::GetBoolProperty("ro.actionable_compatible_property.enabled", false);
-
-    if (subcontext == nullptr || !enabled) {
-        return true;
-    }
-
-    if (kExportedActionableProperties.count(prop_name) == 1) {
-        return true;
-    }
-    for (const auto& prefix : kPartnerPrefixes) {
-        if (android::base::StartsWith(prop_name, prefix)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-Result<Success> Action::ParsePropertyTrigger(const std::string& trigger) {
-    const static std::string prop_str("property:");
-    std::string prop_name(trigger.substr(prop_str.length()));
-    size_t equal_pos = prop_name.find('=');
-    if (equal_pos == std::string::npos) {
-        return Error() << "property trigger found without matching '='";
-    }
-
-    std::string prop_value(prop_name.substr(equal_pos + 1));
-    prop_name.erase(equal_pos);
-
-    if (!IsActionableProperty(subcontext_, prop_name)) {
-        return Error() << "unexported property tigger found: " << prop_name;
-    }
-
-    if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
-        return Error() << "multiple property triggers found for same property";
-    }
-    return Success();
-}
-
-Result<Success> Action::InitTriggers(const std::vector<std::string>& args) {
-    const static std::string prop_str("property:");
-    for (std::size_t i = 0; i < args.size(); ++i) {
-        if (args[i].empty()) {
-            return Error() << "empty trigger is not valid";
-        }
-
-        if (i % 2) {
-            if (args[i] != "&&") {
-                return Error() << "&& is the only symbol allowed to concatenate actions";
-            } else {
-                continue;
-            }
-        }
-
-        if (!args[i].compare(0, prop_str.length(), prop_str)) {
-            if (auto result = ParsePropertyTrigger(args[i]); !result) {
-                return result;
-            }
-        } else {
-            if (!event_trigger_.empty()) {
-                return Error() << "multiple event triggers are not allowed";
-            }
-
-            event_trigger_ = args[i];
-        }
-    }
-
-    return Success();
-}
-
-Result<Success> Action::InitSingleTrigger(const std::string& trigger) {
-    std::vector<std::string> name_vector{trigger};
-    if (auto result = InitTriggers(name_vector); !result) {
-        return Error() << "InitTriggers() failed: " << result.error();
-    }
-    return Success();
-}
-
 // This function checks that all property triggers are satisfied, that is
 // for each (name, value) in property_triggers_, check that the current
 // value of the property 'name' == value.
@@ -281,142 +212,5 @@
     }
 }
 
-ActionManager::ActionManager() : current_command_(0) {
-}
-
-ActionManager& ActionManager::GetInstance() {
-    static ActionManager instance;
-    return instance;
-}
-
-void ActionManager::AddAction(std::unique_ptr<Action> action) {
-    actions_.emplace_back(std::move(action));
-}
-
-void ActionManager::QueueEventTrigger(const std::string& trigger) {
-    event_queue_.emplace(trigger);
-}
-
-void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
-    event_queue_.emplace(std::make_pair(name, value));
-}
-
-void ActionManager::QueueAllPropertyActions() {
-    QueuePropertyChange("", "");
-}
-
-void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
-    auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0);
-    std::vector<std::string> name_vector{name};
-
-    if (auto result = action->InitSingleTrigger(name); !result) {
-        LOG(ERROR) << "Cannot queue BuiltinAction for " << name << ": " << result.error();
-        return;
-    }
-
-    action->AddCommand(func, name_vector, 0);
-
-    event_queue_.emplace(action.get());
-    actions_.emplace_back(std::move(action));
-}
-
-void ActionManager::ExecuteOneCommand() {
-    // Loop through the event queue until we have an action to execute
-    while (current_executing_actions_.empty() && !event_queue_.empty()) {
-        for (const auto& action : actions_) {
-            if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
-                           event_queue_.front())) {
-                current_executing_actions_.emplace(action.get());
-            }
-        }
-        event_queue_.pop();
-    }
-
-    if (current_executing_actions_.empty()) {
-        return;
-    }
-
-    auto action = current_executing_actions_.front();
-
-    if (current_command_ == 0) {
-        std::string trigger_name = action->BuildTriggersString();
-        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
-                  << ":" << action->line() << ")";
-    }
-
-    action->ExecuteOneCommand(current_command_);
-
-    // If this was the last command in the current action, then remove
-    // the action from the executing list.
-    // If this action was oneshot, then also remove it from actions_.
-    ++current_command_;
-    if (current_command_ == action->NumCommands()) {
-        current_executing_actions_.pop();
-        current_command_ = 0;
-        if (action->oneshot()) {
-            auto eraser = [&action] (std::unique_ptr<Action>& a) {
-                return a.get() == action;
-            };
-            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
-        }
-    }
-}
-
-bool ActionManager::HasMoreCommands() const {
-    return !current_executing_actions_.empty() || !event_queue_.empty();
-}
-
-void ActionManager::DumpState() const {
-    for (const auto& a : actions_) {
-        a->DumpState();
-    }
-}
-
-void ActionManager::ClearQueue() {
-    // We are shutting down so don't claim the oneshot builtin actions back
-    current_executing_actions_ = {};
-    event_queue_ = {};
-    current_command_ = 0;
-}
-
-Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
-                                           const std::string& filename, int line) {
-    std::vector<std::string> triggers(args.begin() + 1, args.end());
-    if (triggers.size() < 1) {
-        return Error() << "Actions must have a trigger";
-    }
-
-    Subcontext* action_subcontext = nullptr;
-    if (subcontexts_) {
-        for (auto& subcontext : *subcontexts_) {
-            if (StartsWith(filename, subcontext.path_prefix())) {
-                action_subcontext = &subcontext;
-                break;
-            }
-        }
-    }
-
-    auto action = std::make_unique<Action>(false, action_subcontext, filename, line);
-
-    if (auto result = action->InitTriggers(triggers); !result) {
-        return Error() << "InitTriggers() failed: " << result.error();
-    }
-
-    action_ = std::move(action);
-    return Success();
-}
-
-Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
-    return action_ ? action_->AddCommand(std::move(args), line) : Success();
-}
-
-Result<Success> ActionParser::EndSection() {
-    if (action_ && action_->NumCommands() > 0) {
-        action_manager_->AddAction(std::move(action_));
-    }
-
-    return Success();
-}
-
 }  // namespace init
 }  // namespace android
diff --git a/init/action.h b/init/action.h
index 1bfc6c7..4f063cc 100644
--- a/init/action.h
+++ b/init/action.h
@@ -25,7 +25,6 @@
 
 #include "builtins.h"
 #include "keyword_map.h"
-#include "parser.h"
 #include "result.h"
 #include "subcontext.h"
 
@@ -58,12 +57,12 @@
 
 class Action {
   public:
-    Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line);
+    Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line,
+           const std::string& event_trigger,
+           const std::map<std::string, std::string>& property_triggers);
 
     Result<Success> AddCommand(const std::vector<std::string>& args, int line);
     void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
-    Result<Success> InitTriggers(const std::vector<std::string>& args);
-    Result<Success> InitSingleTrigger(const std::string& trigger);
     std::size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
@@ -84,7 +83,6 @@
     void ExecuteCommand(const Command& command) const;
     bool CheckPropertyTriggers(const std::string& name = "",
                                const std::string& value = "") const;
-    Result<Success> ParsePropertyTrigger(const std::string& trigger);
 
     std::map<std::string, std::string> property_triggers_;
     std::string event_trigger_;
@@ -96,48 +94,6 @@
     static const KeywordFunctionMap* function_map_;
 };
 
-class ActionManager {
-  public:
-    static ActionManager& GetInstance();
-
-    // Exposed for testing
-    ActionManager();
-
-    void AddAction(std::unique_ptr<Action> action);
-    void QueueEventTrigger(const std::string& trigger);
-    void QueuePropertyChange(const std::string& name, const std::string& value);
-    void QueueAllPropertyActions();
-    void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
-    void ExecuteOneCommand();
-    bool HasMoreCommands() const;
-    void DumpState() const;
-    void ClearQueue();
-
-  private:
-    ActionManager(ActionManager const&) = delete;
-    void operator=(ActionManager const&) = delete;
-
-    std::vector<std::unique_ptr<Action>> actions_;
-    std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
-    std::queue<const Action*> current_executing_actions_;
-    std::size_t current_command_;
-};
-
-class ActionParser : public SectionParser {
-  public:
-    ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
-        : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
-    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                 int line) override;
-    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
-    Result<Success> EndSection() override;
-
-  private:
-    ActionManager* action_manager_;
-    std::vector<Subcontext>* subcontexts_;
-    std::unique_ptr<Action> action_;
-};
-
 }  // namespace init
 }  // namespace android
 
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
new file mode 100644
index 0000000..22977bb
--- /dev/null
+++ b/init/action_manager.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "action_manager.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace init {
+
+ActionManager::ActionManager() : current_command_(0) {}
+
+ActionManager& ActionManager::GetInstance() {
+    static ActionManager instance;
+    return instance;
+}
+
+void ActionManager::AddAction(std::unique_ptr<Action> action) {
+    actions_.emplace_back(std::move(action));
+}
+
+void ActionManager::QueueEventTrigger(const std::string& trigger) {
+    event_queue_.emplace(trigger);
+}
+
+void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
+    event_queue_.emplace(std::make_pair(name, value));
+}
+
+void ActionManager::QueueAllPropertyActions() {
+    QueuePropertyChange("", "");
+}
+
+void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
+    auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
+                                           std::map<std::string, std::string>{});
+    std::vector<std::string> name_vector{name};
+
+    action->AddCommand(func, name_vector, 0);
+
+    event_queue_.emplace(action.get());
+    actions_.emplace_back(std::move(action));
+}
+
+void ActionManager::ExecuteOneCommand() {
+    // Loop through the event queue until we have an action to execute
+    while (current_executing_actions_.empty() && !event_queue_.empty()) {
+        for (const auto& action : actions_) {
+            if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
+                           event_queue_.front())) {
+                current_executing_actions_.emplace(action.get());
+            }
+        }
+        event_queue_.pop();
+    }
+
+    if (current_executing_actions_.empty()) {
+        return;
+    }
+
+    auto action = current_executing_actions_.front();
+
+    if (current_command_ == 0) {
+        std::string trigger_name = action->BuildTriggersString();
+        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
+                  << ":" << action->line() << ")";
+    }
+
+    action->ExecuteOneCommand(current_command_);
+
+    // If this was the last command in the current action, then remove
+    // the action from the executing list.
+    // If this action was oneshot, then also remove it from actions_.
+    ++current_command_;
+    if (current_command_ == action->NumCommands()) {
+        current_executing_actions_.pop();
+        current_command_ = 0;
+        if (action->oneshot()) {
+            auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
+            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
+        }
+    }
+}
+
+bool ActionManager::HasMoreCommands() const {
+    return !current_executing_actions_.empty() || !event_queue_.empty();
+}
+
+void ActionManager::DumpState() const {
+    for (const auto& a : actions_) {
+        a->DumpState();
+    }
+}
+
+void ActionManager::ClearQueue() {
+    // We are shutting down so don't claim the oneshot builtin actions back
+    current_executing_actions_ = {};
+    event_queue_ = {};
+    current_command_ = 0;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/action_manager.h b/init/action_manager.h
new file mode 100644
index 0000000..5f47a6d
--- /dev/null
+++ b/init/action_manager.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_ACTION_MANAGER_H
+#define _INIT_ACTION_MANAGER_H
+
+#include <string>
+#include <vector>
+
+#include "action.h"
+#include "builtins.h"
+
+namespace android {
+namespace init {
+
+class ActionManager {
+  public:
+    static ActionManager& GetInstance();
+
+    // Exposed for testing
+    ActionManager();
+
+    void AddAction(std::unique_ptr<Action> action);
+    void QueueEventTrigger(const std::string& trigger);
+    void QueuePropertyChange(const std::string& name, const std::string& value);
+    void QueueAllPropertyActions();
+    void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
+    void ExecuteOneCommand();
+    bool HasMoreCommands() const;
+    void DumpState() const;
+    void ClearQueue();
+
+  private:
+    ActionManager(ActionManager const&) = delete;
+    void operator=(ActionManager const&) = delete;
+
+    std::vector<std::unique_ptr<Action>> actions_;
+    std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
+    std::queue<const Action*> current_executing_actions_;
+    std::size_t current_command_;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
new file mode 100644
index 0000000..a2c9671
--- /dev/null
+++ b/init/action_parser.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "action_parser.h"
+
+#include <android-base/strings.h>
+
+#include "stable_properties.h"
+
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+#else
+#include "host_init_stubs.h"
+#endif
+
+using android::base::GetBoolProperty;
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+namespace {
+
+bool IsActionableProperty(Subcontext* subcontext, const std::string& prop_name) {
+    static bool enabled = GetBoolProperty("ro.actionable_compatible_property.enabled", false);
+
+    if (subcontext == nullptr || !enabled) {
+        return true;
+    }
+
+    if (kExportedActionableProperties.count(prop_name) == 1) {
+        return true;
+    }
+    for (const auto& prefix : kPartnerPrefixes) {
+        if (android::base::StartsWith(prop_name, prefix)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
+                                     std::map<std::string, std::string>* property_triggers) {
+    const static std::string prop_str("property:");
+    std::string prop_name(trigger.substr(prop_str.length()));
+    size_t equal_pos = prop_name.find('=');
+    if (equal_pos == std::string::npos) {
+        return Error() << "property trigger found without matching '='";
+    }
+
+    std::string prop_value(prop_name.substr(equal_pos + 1));
+    prop_name.erase(equal_pos);
+
+    if (!IsActionableProperty(subcontext, prop_name)) {
+        return Error() << "unexported property tigger found: " << prop_name;
+    }
+
+    if (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) {
+        return Error() << "multiple property triggers found for same property";
+    }
+    return Success();
+}
+
+Result<Success> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
+                              std::string* event_trigger,
+                              std::map<std::string, std::string>* property_triggers) {
+    const static std::string prop_str("property:");
+    for (std::size_t i = 0; i < args.size(); ++i) {
+        if (args[i].empty()) {
+            return Error() << "empty trigger is not valid";
+        }
+
+        if (i % 2) {
+            if (args[i] != "&&") {
+                return Error() << "&& is the only symbol allowed to concatenate actions";
+            } else {
+                continue;
+            }
+        }
+
+        if (!args[i].compare(0, prop_str.length(), prop_str)) {
+            if (auto result = ParsePropertyTrigger(args[i], subcontext, property_triggers);
+                !result) {
+                return result;
+            }
+        } else {
+            if (!event_trigger->empty()) {
+                return Error() << "multiple event triggers are not allowed";
+            }
+
+            *event_trigger = args[i];
+        }
+    }
+
+    return Success();
+}
+
+}  // namespace
+
+Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
+                                           const std::string& filename, int line) {
+    std::vector<std::string> triggers(args.begin() + 1, args.end());
+    if (triggers.size() < 1) {
+        return Error() << "Actions must have a trigger";
+    }
+
+    Subcontext* action_subcontext = nullptr;
+    if (subcontexts_) {
+        for (auto& subcontext : *subcontexts_) {
+            if (StartsWith(filename, subcontext.path_prefix())) {
+                action_subcontext = &subcontext;
+                break;
+            }
+        }
+    }
+
+    std::string event_trigger;
+    std::map<std::string, std::string> property_triggers;
+
+    if (auto result = ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);
+        !result) {
+        return Error() << "ParseTriggers() failed: " << result.error();
+    }
+
+    auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger,
+                                           property_triggers);
+
+    action_ = std::move(action);
+    return Success();
+}
+
+Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    return action_ ? action_->AddCommand(std::move(args), line) : Success();
+}
+
+Result<Success> ActionParser::EndSection() {
+    if (action_ && action_->NumCommands() > 0) {
+        action_manager_->AddAction(std::move(action_));
+    }
+
+    return Success();
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/action_parser.h b/init/action_parser.h
new file mode 100644
index 0000000..b7f7074
--- /dev/null
+++ b/init/action_parser.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_ACTION_PARSER_H
+#define _INIT_ACTION_PARSER_H
+
+#include <string>
+#include <vector>
+
+#include "action.h"
+#include "action_manager.h"
+#include "parser.h"
+#include "subcontext.h"
+
+namespace android {
+namespace init {
+
+class ActionParser : public SectionParser {
+  public:
+    ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
+        : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
+    Result<Success> EndSection() override;
+
+  private:
+    ActionManager* action_manager_;
+    std::vector<Subcontext>* subcontexts_;
+    std::unique_ptr<Action> action_;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 413d11e..fc74dda 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -58,7 +58,7 @@
 #include <selinux/selinux.h>
 #include <system/thread_defs.h>
 
-#include "action.h"
+#include "action_manager.h"
 #include "bootchart.h"
 #include "init.h"
 #include "parser.h"
@@ -514,8 +514,9 @@
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
 
-        // defaultcrypto detects file/block encryption. init flow is same for each.
-        ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
+        // Although encrypted, vold has already set the device up, so we do not need to
+        // do anything different from the nonencrypted case.
+        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
         return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
         if (e4crypt_install_keyring()) {
@@ -523,8 +524,9 @@
         }
         property_set("ro.crypto.type", "file");
 
-        // encrypt detects file/block encryption. init flow is same for each.
-        ActionManager::GetInstance().QueueEventTrigger("encrypt");
+        // Although encrypted, vold has already set the device up, so we do not need to
+        // do anything different from the nonencrypted case.
+        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
         return Success();
     } else if (code > 0) {
         Error() << "fs_mgr_mount_all() returned unexpected error " << code;
@@ -969,8 +971,8 @@
     const char* value = args[2].c_str();
     size_t value_len = strlen(value);
 
-    if (!is_legal_property_name(name)) {
-        return Error() << "is_legal_property_name(" << name << ") failed";
+    if (!IsLegalPropertyName(name)) {
+        return Error() << "IsLegalPropertyName(" << name << ") failed";
     }
     if (value_len >= PROP_VALUE_MAX) {
         return Error() << "value too long";
@@ -1003,6 +1005,7 @@
     return do_exec({std::move(exec_args), args.context});
 }
 
+// Builtin-function-map start
 const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
@@ -1030,6 +1033,10 @@
         {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
         {"loglevel",                {1,     1,    {false,  do_loglevel}}},
         {"mkdir",                   {1,     4,    {true,   do_mkdir}}},
+        // TODO: Do mount operations in vendor_init.
+        // mount_all is currently too complex to run in vendor_init as it queues action triggers,
+        // imports rc scripts, etc.  It should be simplified and run in vendor_init context.
+        // mount and umount are run in the same context as mount_all for symmetry.
         {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
         {"mount",                   {3,     kMax, {false,  do_mount}}},
         {"umount",                  {1,     1,    {false,  do_umount}}},
@@ -1056,6 +1063,7 @@
     // clang-format on
     return builtin_functions;
 }
+// Builtin-function-map end
 
 }  // namespace init
 }  // namespace android
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index 50987db..a91cd1d 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -14,7 +14,6 @@
 
 #include "capabilities.h"
 
-#include <sys/capability.h>
 #include <sys/prctl.h>
 
 #include <map>
@@ -72,10 +71,15 @@
 static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
 
 static bool ComputeCapAmbientSupported() {
+#if defined(__ANDROID__)
     return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= 0;
+#else
+    return true;
+#endif
 }
 
 static unsigned int ComputeLastValidCap() {
+#if defined(__ANDROID__)
     // Android does not support kernels < 3.8. 'CAP_WAKE_ALARM' has been present since 3.0, see
     // http://lxr.free-electrons.com/source/include/linux/capability.h?v=3.0#L360.
     unsigned int last_valid_cap = CAP_WAKE_ALARM;
@@ -83,6 +87,9 @@
 
     // |last_valid_cap| will be the first failing value.
     return last_valid_cap - 1;
+#else
+    return CAP_LAST_CAP;
+#endif
 }
 
 static bool DropBoundingSet(const CapSet& to_keep) {
@@ -139,6 +146,7 @@
 }
 
 static bool SetAmbientCaps(const CapSet& to_raise) {
+#if defined(__ANDROID__)
     for (size_t cap = 0; cap < to_raise.size(); ++cap) {
         if (to_raise.test(cap)) {
             if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) != 0) {
@@ -147,6 +155,7 @@
             }
         }
     }
+#endif
     return true;
 }
 
diff --git a/init/capabilities.h b/init/capabilities.h
index fc80c98..891e0ac 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -21,6 +21,17 @@
 #include <string>
 #include <type_traits>
 
+#if !defined(__ANDROID__)
+#ifndef CAP_BLOCK_SUSPEND
+#define CAP_BLOCK_SUSPEND 36
+#endif
+#ifndef CAP_AUDIT_READ
+#define CAP_AUDIT_READ 37
+#endif
+#undef CAP_LAST_CAP
+#define CAP_LAST_CAP CAP_AUDIT_READ
+#endif
+
 namespace android {
 namespace init {
 
diff --git a/init/host_init_parser.cpp b/init/host_init_parser.cpp
new file mode 100644
index 0000000..5232b7e
--- /dev/null
+++ b/init/host_init_parser.cpp
@@ -0,0 +1,82 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <pwd.h>
+
+#include <android-base/logging.h>
+
+#include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
+#include "parser.h"
+#include "result.h"
+#include "service.h"
+
+// The host passwd file won't have the Android entries, so we fake success here.
+passwd* getpwnam(const char* login) {  // NOLINT: implementing bad function.
+    char dummy_buf[] = "dummy";
+    static passwd dummy_passwd = {
+        .pw_name = dummy_buf,
+        .pw_dir = dummy_buf,
+        .pw_shell = dummy_buf,
+        .pw_uid = 123,
+        .pw_gid = 123,
+    };
+    return &dummy_passwd;
+}
+
+namespace android {
+namespace init {
+
+static Result<Success> do_stub(const BuiltinArguments& args) {
+    return Success();
+}
+
+#include "generated_stub_builtin_function_map.h"
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::StderrLogger);
+    if (argc != 2) {
+        LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
+        return -1;
+    }
+    const BuiltinFunctionMap function_map;
+    Action::set_function_map(&function_map);
+    ActionManager& am = ActionManager::GetInstance();
+    ServiceList& sl = ServiceList::GetInstance();
+    Parser parser;
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+
+    size_t num_errors = 0;
+    if (!parser.ParseConfig(argv[1], &num_errors)) {
+        LOG(ERROR) << "Failed to find script";
+        return -1;
+    }
+    if (num_errors > 0) {
+        LOG(ERROR) << "Parse failed with " << num_errors << " errors";
+        return -1;
+    }
+    LOG(INFO) << "Parse success!";
+    return 0;
+}
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    android::init::main(argc, argv);
+}
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
new file mode 100644
index 0000000..e6cc08a
--- /dev/null
+++ b/init/host_init_stubs.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host_init_stubs.h"
+
+// unistd.h
+int setgroups(size_t __size, const gid_t* __list) {
+    return 0;
+}
+
+namespace android {
+namespace base {
+
+std::string GetProperty(const std::string&, const std::string& default_value) {
+    return default_value;
+}
+
+bool GetBoolProperty(const std::string&, bool default_value) {
+    return default_value;
+}
+
+}  // namespace base
+}  // namespace android
+
+namespace android {
+namespace init {
+
+// init.h
+std::string default_console = "/dev/console";
+
+// property_service.h
+uint32_t (*property_set)(const std::string& name, const std::string& value) = nullptr;
+uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
+                           std::string*) {
+    return 0;
+}
+
+// selinux.h
+void SelabelInitialize() {}
+
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+    return false;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
new file mode 100644
index 0000000..f31ece6
--- /dev/null
+++ b/init/host_init_stubs.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_HOST_INIT_STUBS_H
+#define _INIT_HOST_INIT_STUBS_H
+
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <string>
+
+// sys/system_properties.h
+#define PROP_VALUE_MAX 92
+
+// unistd.h
+int setgroups(size_t __size, const gid_t* __list);
+
+// android-base/properties.h
+namespace android {
+namespace base {
+
+std::string GetProperty(const std::string& key, const std::string& default_value);
+bool GetBoolProperty(const std::string& key, bool default_value);
+
+}  // namespace base
+}  // namespace android
+
+namespace android {
+namespace init {
+
+// init.h
+extern std::string default_console;
+
+// property_service.h
+extern uint32_t (*property_set)(const std::string& name, const std::string& value);
+uint32_t HandlePropertySet(const std::string& name, const std::string& value,
+                           const std::string& source_context, const ucred& cr, std::string* error);
+
+// selinux.h
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/init.cpp b/init/init.cpp
index 79623c3..efb9c1d 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <paths.h>
+#include <pthread.h>
 #include <seccomp_policy.h>
 #include <signal.h>
 #include <stdlib.h>
@@ -34,6 +35,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/android_reboot.h>
 #include <keyutils.h>
@@ -44,6 +46,7 @@
 #include <memory>
 #include <optional>
 
+#include "action_parser.h"
 #include "import_parser.h"
 #include "init_first_stage.h"
 #include "keychords.h"
@@ -61,7 +64,10 @@
 
 using android::base::boot_clock;
 using android::base::GetProperty;
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
 using android::base::Timer;
+using android::base::Trim;
 
 namespace android {
 namespace init {
@@ -244,7 +250,7 @@
     return control_message_functions;
 }
 
-void handle_control_message(const std::string& msg, const std::string& name) {
+void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
     const auto& map = get_control_message_map();
     const auto it = map.find(msg);
 
@@ -253,6 +259,18 @@
         return;
     }
 
+    std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
+    std::string process_cmdline;
+    if (ReadFileToString(cmdline_path, &process_cmdline)) {
+        std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' ');
+        process_cmdline = Trim(process_cmdline);
+    } else {
+        process_cmdline = "unknown process";
+    }
+
+    LOG(INFO) << "Received control message '" << msg << "' for '" << name << "' from pid: " << pid
+              << " (" << process_cmdline << ")";
+
     const ControlMessageFunction& function = it->second;
 
     if (function.target == ControlTarget::SERVICE) {
@@ -491,6 +509,16 @@
     HandlePowerctlMessage("shutdown,container");
 }
 
+static void UnblockSigterm() {
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGTERM);
+
+    if (sigprocmask(SIG_UNBLOCK, &mask, nullptr) == -1) {
+        PLOG(FATAL) << "failed to unblock SIGTERM for PID " << getpid();
+    }
+}
+
 static void InstallSigtermHandler() {
     sigset_t mask;
     sigemptyset(&mask);
@@ -500,6 +528,12 @@
         PLOG(FATAL) << "failed to block SIGTERM";
     }
 
+    // Register a handler to unblock SIGTERM in the child processes.
+    const int result = pthread_atfork(nullptr, nullptr, &UnblockSigterm);
+    if (result != 0) {
+        LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
+    }
+
     sigterm_signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
     if (sigterm_signal_fd == -1) {
         PLOG(FATAL) << "failed to create signalfd for SIGTERM";
diff --git a/init/init.h b/init/init.h
index ff7bdeb..d4a0e96 100644
--- a/init/init.h
+++ b/init/init.h
@@ -17,10 +17,13 @@
 #ifndef _INIT_INIT_H
 #define _INIT_INIT_H
 
+#include <sys/types.h>
+
 #include <string>
 #include <vector>
 
 #include "action.h"
+#include "action_manager.h"
 #include "parser.h"
 #include "service.h"
 
@@ -35,7 +38,7 @@
 
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
 
-void handle_control_message(const std::string& msg, const std::string& arg);
+void HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);
 
 void property_changed(const std::string& name, const std::string& value);
 
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 6fa07e7..0f4cc68 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -56,6 +56,7 @@
     bool InitDevices();
 
   protected:
+    ListenerAction HandleBlockDevice(const std::string& name, const Uevent&);
     bool InitRequiredDevices();
     bool InitVerityDevice(const std::string& verity_device);
     bool MountPartitions();
@@ -209,6 +210,24 @@
     return true;
 }
 
+ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
+    // Matches partition name to create device nodes.
+    // Both required_devices_partition_names_ and uevent->partition_name have A/B
+    // suffix when A/B is used.
+    auto iter = required_devices_partition_names_.find(name);
+    if (iter != required_devices_partition_names_.end()) {
+        LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
+        required_devices_partition_names_.erase(iter);
+        device_handler_.HandleDeviceEvent(uevent);
+        if (required_devices_partition_names_.empty()) {
+            return ListenerAction::kStop;
+        } else {
+            return ListenerAction::kContinue;
+        }
+    }
+    return ListenerAction::kContinue;
+}
+
 ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent) {
     // Ignores everything that is not a block device.
     if (uevent.subsystem != "block") {
@@ -216,19 +235,11 @@
     }
 
     if (!uevent.partition_name.empty()) {
-        // Matches partition name to create device nodes.
-        // Both required_devices_partition_names_ and uevent->partition_name have A/B
-        // suffix when A/B is used.
-        auto iter = required_devices_partition_names_.find(uevent.partition_name);
-        if (iter != required_devices_partition_names_.end()) {
-            LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
-            required_devices_partition_names_.erase(iter);
-            device_handler_.HandleDeviceEvent(uevent);
-            if (required_devices_partition_names_.empty()) {
-                return ListenerAction::kStop;
-            } else {
-                return ListenerAction::kContinue;
-            }
+        return HandleBlockDevice(uevent.partition_name, uevent);
+    } else {
+        size_t base_idx = uevent.path.rfind('/');
+        if (base_idx != std::string::npos) {
+            return HandleBlockDevice(uevent.path.substr(base_idx + 1), uevent);
         }
     }
     // Not found a partition or find an unneeded partition, continue to find others.
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 268873c..0f9635f 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -21,6 +21,8 @@
 #include <gtest/gtest.h>
 
 #include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
 #include "builtins.h"
 #include "import_parser.h"
 #include "keyword_map.h"
diff --git a/init/parser.cpp b/init/parser.cpp
index 4c69bac..4453aaa 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -39,7 +39,7 @@
     line_callbacks_.emplace_back(prefix, callback);
 }
 
-void Parser::ParseData(const std::string& filename, const std::string& data) {
+void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
     // TODO: Use a parser with const input and remove this copy
     std::vector<char> data_copy(data.begin(), data.end());
     data_copy.push_back('\0');
@@ -57,6 +57,7 @@
         if (section_parser == nullptr) return;
 
         if (auto result = section_parser->EndSection(); !result) {
+            (*parse_errors)++;
             LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
         }
 
@@ -80,6 +81,7 @@
                         end_section();
 
                         if (auto result = callback(std::move(args)); !result) {
+                            (*parse_errors)++;
                             LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         }
                         break;
@@ -92,12 +94,14 @@
                     if (auto result =
                             section_parser->ParseSection(std::move(args), filename, state.line);
                         !result) {
+                        (*parse_errors)++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         section_parser = nullptr;
                     }
                 } else if (section_parser) {
                     if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
                         !result) {
+                        (*parse_errors)++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
                 }
@@ -110,7 +114,7 @@
     }
 }
 
-bool Parser::ParseConfigFile(const std::string& path) {
+bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
     LOG(INFO) << "Parsing file " << path << "...";
     android::base::Timer t;
     auto config_contents = ReadFile(path);
@@ -120,7 +124,7 @@
     }
 
     config_contents->push_back('\n');  // TODO: fix parse_config.
-    ParseData(path, *config_contents);
+    ParseData(path, *config_contents, parse_errors);
     for (const auto& [section_name, section_parser] : section_parsers_) {
         section_parser->EndFile();
     }
@@ -129,7 +133,7 @@
     return true;
 }
 
-bool Parser::ParseConfigDir(const std::string& path) {
+bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
     LOG(INFO) << "Parsing directory " << path << "...";
     std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
     if (!config_dir) {
@@ -149,7 +153,7 @@
     // Sort first so we load files in a consistent order (bug 31996208)
     std::sort(files.begin(), files.end());
     for (const auto& file : files) {
-        if (!ParseConfigFile(file)) {
+        if (!ParseConfigFile(file, parse_errors)) {
             LOG(ERROR) << "could not import file '" << file << "'";
         }
     }
@@ -157,10 +161,16 @@
 }
 
 bool Parser::ParseConfig(const std::string& path) {
+    size_t parse_errors;
+    return ParseConfig(path, &parse_errors);
+}
+
+bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
+    *parse_errors = 0;
     if (is_dir(path.c_str())) {
-        return ParseConfigDir(path);
+        return ParseConfigDir(path, parse_errors);
     }
-    return ParseConfigFile(path);
+    return ParseConfigFile(path, parse_errors);
 }
 
 }  // namespace init
diff --git a/init/parser.h b/init/parser.h
index 110a468..f6e237f 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -72,13 +72,14 @@
     Parser();
 
     bool ParseConfig(const std::string& path);
+    bool ParseConfig(const std::string& path, size_t* parse_errors);
     void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
     void AddSingleLineParser(const std::string& prefix, LineCallback callback);
 
   private:
-    void ParseData(const std::string& filename, const std::string& data);
-    bool ParseConfigFile(const std::string& path);
-    bool ParseConfigDir(const std::string& path);
+    void ParseData(const std::string& filename, const std::string& data, size_t* parse_errors);
+    bool ParseConfigFile(const std::string& path, size_t* parse_errors);
+    bool ParseConfigDir(const std::string& path, size_t* parse_errors);
 
     std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
     std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index ecd5baa..95ef35c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -59,8 +59,11 @@
 #include "init.h"
 #include "persistent_properties.h"
 #include "property_type.h"
+#include "subcontext.h"
 #include "util.h"
 
+using namespace std::literals;
+
 using android::base::ReadFileToString;
 using android::base::Split;
 using android::base::StartsWith;
@@ -117,48 +120,21 @@
     return has_access;
 }
 
-bool is_legal_property_name(const std::string& name) {
-    size_t namelen = name.size();
-
-    if (namelen < 1) return false;
-    if (name[0] == '.') return false;
-    if (name[namelen - 1] == '.') return false;
-
-    /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
-    /* Don't allow ".." to appear in a property name */
-    for (size_t i = 0; i < namelen; i++) {
-        if (name[i] == '.') {
-            // i=0 is guaranteed to never have a dot. See above.
-            if (name[i-1] == '.') return false;
-            continue;
-        }
-        if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
-        if (name[i] >= 'a' && name[i] <= 'z') continue;
-        if (name[i] >= 'A' && name[i] <= 'Z') continue;
-        if (name[i] >= '0' && name[i] <= '9') continue;
-        return false;
-    }
-
-    return true;
-}
-
-static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
+static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
     size_t valuelen = value.size();
 
-    if (!is_legal_property_name(name)) {
-        LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
+    if (!IsLegalPropertyName(name)) {
+        *error = "Illegal property name";
         return PROP_ERROR_INVALID_NAME;
     }
 
     if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
-        LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
-                   << "value too long";
+        *error = "Property value too long";
         return PROP_ERROR_INVALID_VALUE;
     }
 
     if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
-        LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
-                   << "value not a UTF8 encoded string";
+        *error = "Value is not a UTF8 encoded string";
         return PROP_ERROR_INVALID_VALUE;
     }
 
@@ -166,8 +142,7 @@
     if (pi != nullptr) {
         // ro.* properties are actually "write-once".
         if (StartsWith(name, "ro.")) {
-            LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
-                       << "property already set";
+            *error = "Read-only property was already set";
             return PROP_ERROR_READ_ONLY_PROPERTY;
         }
 
@@ -175,8 +150,7 @@
     } else {
         int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
         if (rc < 0) {
-            LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
-                       << "__system_property_add failed";
+            *error = "__system_property_add failed";
             return PROP_ERROR_SET_FAILED;
         }
     }
@@ -230,8 +204,10 @@
     if (info.pid != pid) {
         return false;
     }
-    if (PropertySetImpl(info.name, info.value) != PROP_SUCCESS) {
-        LOG(ERROR) << "Failed to set async property " << info.name;
+    std::string error;
+    if (PropertySet(info.name, info.value, &error) != PROP_SUCCESS) {
+        LOG(ERROR) << "Failed to set async property " << info.name << " to " << info.value << ": "
+                   << error;
     }
     property_children.pop();
     if (!property_children.empty()) {
@@ -241,9 +217,9 @@
 }
 
 static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
-                                 PropertyAsyncFunc func) {
+                                 PropertyAsyncFunc func, std::string* error) {
     if (value.empty()) {
-        return PropertySetImpl(name, value);
+        return PropertySet(name, value, error);
     }
 
     PropertyChildInfo info;
@@ -261,30 +237,27 @@
     return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
 }
 
-uint32_t PropertySet(const std::string& name, const std::string& value) {
-    if (name == "selinux.restorecon_recursive") {
-        return PropertySetAsync(name, value, RestoreconRecursiveAsync);
-    }
-
-    return PropertySetImpl(name, value);
-}
-
 uint32_t InitPropertySet(const std::string& name, const std::string& value) {
     if (StartsWith(name, "ctl.")) {
-        LOG(ERROR) << "Do not set ctl. properties from init; call the Service functions directly";
+        LOG(ERROR) << "InitPropertySet: Do not set ctl. properties from init; call the Service "
+                      "functions directly";
+        return PROP_ERROR_INVALID_NAME;
+    }
+    if (name == "selinux.restorecon_recursive") {
+        LOG(ERROR) << "InitPropertySet: Do not set selinux.restorecon_recursive from init; use the "
+                      "restorecon builtin directly";
         return PROP_ERROR_INVALID_NAME;
     }
 
-    const char* type = nullptr;
-    property_info_area->GetPropertyInfo(name.c_str(), nullptr, &type);
-
-    if (type == nullptr || !CheckType(type, value)) {
-        LOG(ERROR) << "property_set: name: '" << name << "' type check failed, type: '"
-                   << (type ?: "(null)") << "' value: '" << value << "'";
-        return PROP_ERROR_INVALID_VALUE;
+    uint32_t result = 0;
+    ucred cr = {.pid = 1, .uid = 0, .gid = 0};
+    std::string error;
+    result = HandlePropertySet(name, value, kInitContext.c_str(), cr, &error);
+    if (result != PROP_SUCCESS) {
+        LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
     }
 
-    return PropertySet(name, value);
+    return result;
 }
 
 class SocketConnection {
@@ -415,9 +388,9 @@
 
 // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
-                           const std::string& source_context, const ucred& cr) {
-    if (!is_legal_property_name(name)) {
-        LOG(ERROR) << "PropertySet: illegal property name \"" << name << "\"";
+                           const std::string& source_context, const ucred& cr, std::string* error) {
+    if (!IsLegalPropertyName(name)) {
+        *error = "Illegal property name";
         return PROP_ERROR_INVALID_NAME;
     }
 
@@ -430,13 +403,11 @@
         const char* type = nullptr;
         property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
         if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) {
-            LOG(ERROR) << "PropertySet: Unable to " << (name.c_str() + 4) << " service ctl ["
-                       << value << "]"
-                       << " uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid;
+            *error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str());
             return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
         }
 
-        handle_control_message(name.c_str() + 4, value.c_str());
+        HandleControlMessage(name.c_str() + 4, value, cr.pid);
         return PROP_SUCCESS;
     }
 
@@ -445,13 +416,13 @@
     property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);
 
     if (!CheckMacPerms(name, target_context, source_context.c_str(), cr)) {
-        LOG(ERROR) << "PropertySet: permission denied uid:" << cr.uid << " name:" << name;
+        *error = "SELinux permission check failed";
         return PROP_ERROR_PERMISSION_DENIED;
     }
 
     if (type == nullptr || !CheckType(type, value)) {
-        LOG(ERROR) << "PropertySet: name: '" << name << "' type check failed, type: '"
-                   << (type ?: "(null)") << "' value: '" << value << "'";
+        *error = StringPrintf("Property type check failed, value doesn't match expected type '%s'",
+                              (type ?: "(null)"));
         return PROP_ERROR_INVALID_VALUE;
     }
 
@@ -470,7 +441,11 @@
                   << process_log_string;
     }
 
-    return PropertySet(name, value);
+    if (name == "selinux.restorecon_recursive") {
+        return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
+    }
+
+    return PropertySet(name, value, error);
 }
 
 static void handle_property_set_fd() {
@@ -513,7 +488,16 @@
         prop_name[PROP_NAME_MAX-1] = 0;
         prop_value[PROP_VALUE_MAX-1] = 0;
 
-        HandlePropertySet(prop_value, prop_value, socket.source_context(), socket.cred());
+        const auto& cr = socket.cred();
+        std::string error;
+        uint32_t result =
+            HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
+        if (result != PROP_SUCCESS) {
+            LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
+                       << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
+                       << error;
+        }
+
         break;
       }
 
@@ -527,7 +511,14 @@
           return;
         }
 
-        auto result = HandlePropertySet(name, value, socket.source_context(), socket.cred());
+        const auto& cr = socket.cred();
+        std::string error;
+        uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
+        if (result != PROP_SUCCESS) {
+            LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
+                       << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
+                       << error;
+        }
         socket.SendUint32(result);
         break;
       }
@@ -545,11 +536,17 @@
  * Filter is used to decide which properties to load: NULL loads all keys,
  * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
  */
-static void load_properties(char *data, const char *filter)
-{
+static void LoadProperties(char* data, const char* filter, const char* filename) {
     char *key, *value, *eol, *sol, *tmp, *fn;
     size_t flen = 0;
 
+    const char* context = kInitContext.c_str();
+    for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
+        if (StartsWith(filename, path_prefix)) {
+            context = secontext;
+        }
+    }
+
     if (filter) {
         flen = strlen(filter);
     }
@@ -596,7 +593,21 @@
                 }
             }
 
-            property_set(key, value);
+            if (StartsWith(key, "ctl.") || key == "sys.powerctl"s ||
+                key == "selinux.restorecon_recursive"s) {
+                LOG(ERROR) << "Ignoring disallowed property '" << key
+                           << "' with special meaning in prop file '" << filename << "'";
+                continue;
+            }
+
+            uint32_t result = 0;
+            ucred cr = {.pid = 1, .uid = 0, .gid = 0};
+            std::string error;
+            result = HandlePropertySet(key, value, context, cr, &error);
+            if (result != PROP_SUCCESS) {
+                LOG(ERROR) << "Unable to set property '" << key << "' to '" << value
+                           << "' in property file '" << filename << "': " << error;
+            }
         }
     }
 }
@@ -612,7 +623,8 @@
         return false;
     }
     file_contents->push_back('\n');
-    load_properties(file_contents->data(), filter);
+
+    LoadProperties(file_contents->data(), filter, filename);
     LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
     return true;
 }
diff --git a/init/property_service.h b/init/property_service.h
index 8161b40..29eaaa9 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -32,7 +32,7 @@
 extern uint32_t (*property_set)(const std::string& name, const std::string& value);
 
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
-                           const std::string& source_context, const ucred& cr);
+                           const std::string& source_context, const ucred& cr, std::string* error);
 
 extern bool PropertyChildReap(pid_t pid);
 
@@ -41,7 +41,6 @@
 void load_persist_props(void);
 void load_system_props(void);
 void start_property_service(void);
-bool is_legal_property_name(const std::string& name);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index 95dd340..c038aff 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -45,11 +45,13 @@
   // ...so we can send it a malformed request.
   uint32_t msg = PROP_MSG_SETPROP2;
   uint32_t size = 0xffffffff;
-  uint32_t data = 0xdeadbeef;
 
   ASSERT_EQ(static_cast<ssize_t>(sizeof(msg)), send(fd, &msg, sizeof(msg), 0));
   ASSERT_EQ(static_cast<ssize_t>(sizeof(size)), send(fd, &size, sizeof(size), 0));
-  ASSERT_EQ(static_cast<ssize_t>(sizeof(data)), send(fd, &data, sizeof(data), 0));
+  uint32_t result = 0;
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(result)),
+            TEMP_FAILURE_RETRY(recv(fd, &result, sizeof(result), MSG_WAITALL)));
+  EXPECT_EQ(static_cast<uint32_t>(PROP_ERROR_READ_DATA), result);
   ASSERT_EQ(0, close(fd));
 }
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index a88a42d..03ed55a 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -20,6 +20,7 @@
 #include <fcntl.h>
 #include <linux/fs.h>
 #include <mntent.h>
+#include <semaphore.h>
 #include <sys/capability.h>
 #include <sys/cdefs.h>
 #include <sys/ioctl.h>
@@ -50,6 +51,7 @@
 #include <private/android_filesystem_config.h>
 #include <selinux/selinux.h>
 
+#include "action_manager.h"
 #include "capabilities.h"
 #include "init.h"
 #include "property_service.h"
@@ -88,12 +90,13 @@
           mnt_opts_(entry.mnt_opts) {}
 
     bool Umount(bool force) {
+        LOG(INFO) << "Unmounting " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
         int r = umount2(mnt_dir_.c_str(), force ? MNT_FORCE : 0);
         if (r == 0) {
-            LOG(INFO) << "umounted " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
+            LOG(INFO) << "Umounted " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
             return true;
         } else {
-            PLOG(WARNING) << "cannot umount " << mnt_fsname_ << ":" << mnt_dir_ << " opts "
+            PLOG(WARNING) << "Cannot umount " << mnt_fsname_ << ":" << mnt_dir_ << " opts "
                           << mnt_opts_;
             return false;
         }
@@ -327,39 +330,9 @@
     return stat;
 }
 
-void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
-              bool runFsck) {
+void RebootThread(unsigned int cmd, std::chrono::milliseconds shutdown_timeout, bool runFsck,
+                  sem_t* reboot_semaphore) {
     Timer t;
-    LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
-
-    // Ensure last reboot reason is reduced to canonical
-    // alias reported in bootloader or system boot reason.
-    size_t skip = 0;
-    std::vector<std::string> reasons = Split(reason, ",");
-    if (reasons.size() >= 2 && reasons[0] == "reboot" &&
-        (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
-         reasons[1] == "hard" || reasons[1] == "warm")) {
-        skip = strlen("reboot,");
-    }
-    property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
-    sync();
-
-    bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
-
-    auto shutdown_timeout = 0ms;
-    if (!SHUTDOWN_ZERO_TIMEOUT) {
-        if (is_thermal_shutdown) {
-            constexpr unsigned int thermal_shutdown_timeout = 1;
-            shutdown_timeout = std::chrono::seconds(thermal_shutdown_timeout);
-        } else {
-            constexpr unsigned int shutdown_timeout_default = 6;
-            auto shutdown_timeout_property = android::base::GetUintProperty(
-                "ro.build.shutdown_timeout", shutdown_timeout_default);
-            shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
-        }
-    }
-    LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
-
     // keep debugging tools until non critical ones are all gone.
     const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
     // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
@@ -383,7 +356,7 @@
     }
 
     // remaining operations (specifically fsck) may take a substantial duration
-    if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
+    if (cmd == ANDROID_RB_POWEROFF || cmd == ANDROID_RB_THERMOFF) {
         TurnOffBacklight();
     }
 
@@ -460,8 +433,77 @@
     UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
     // Follow what linux shutdown is doing: one more sync with little bit delay
     sync();
-    if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
+    if (cmd != ANDROID_RB_THERMOFF) std::this_thread::sleep_for(100ms);
     LogShutdownTime(stat, &t);
+
+    if (reboot_semaphore != nullptr) {
+        sem_post(reboot_semaphore);
+    }
+}
+
+void RunRebootThread(unsigned int cmd, std::chrono::milliseconds shutdown_timeout) {
+    sem_t reboot_semaphore;
+    timespec shutdown_timeout_timespec;
+
+    if (sem_init(&reboot_semaphore, false, 0) == -1 ||
+        clock_gettime(CLOCK_REALTIME, &shutdown_timeout_timespec) == -1) {
+        // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
+        return;
+    }
+
+    std::thread reboot_thread(&RebootThread, cmd, shutdown_timeout, false, &reboot_semaphore);
+    reboot_thread.detach();
+
+    // One extra second than the timeout passed to the thread as there is a final Umount pass
+    // after the timeout is reached.
+    shutdown_timeout_timespec.tv_sec += 1 + shutdown_timeout.count() / 1000;
+
+    int sem_return = 0;
+    while ((sem_return = sem_timedwait(&reboot_semaphore, &shutdown_timeout_timespec)) == -1 &&
+           errno == EINTR) {
+    }
+
+    if (sem_return == -1) {
+        LOG(ERROR) << "Reboot thread timed out";
+    }
+}
+
+void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
+              bool runFsck) {
+    LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
+
+    // Ensure last reboot reason is reduced to canonical
+    // alias reported in bootloader or system boot reason.
+    size_t skip = 0;
+    std::vector<std::string> reasons = Split(reason, ",");
+    if (reasons.size() >= 2 && reasons[0] == "reboot" &&
+        (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
+         reasons[1] == "hard" || reasons[1] == "warm")) {
+        skip = strlen("reboot,");
+    }
+    property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
+    sync();
+
+    auto shutdown_timeout = 0ms;
+    if (!SHUTDOWN_ZERO_TIMEOUT) {
+        if (cmd == ANDROID_RB_THERMOFF) {
+            constexpr auto kThermalShutdownTimeout = 1s;
+            shutdown_timeout = kThermalShutdownTimeout;
+        } else {
+            constexpr unsigned int kShutdownTimeoutDefault = 6;
+            auto shutdown_timeout_property = android::base::GetUintProperty(
+                "ro.build.shutdown_timeout", kShutdownTimeoutDefault);
+            shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
+        }
+    }
+    LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
+
+    if (runFsck) {
+        RebootThread(cmd, shutdown_timeout, true, nullptr);
+    } else {
+        RunRebootThread(cmd, shutdown_timeout);
+    }
+
     // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
     RebootSystem(cmd, rebootTarget);
     abort();
diff --git a/init/service.cpp b/init/service.cpp
index a4e33f7..8130e73 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -24,7 +24,6 @@
 #include <sys/prctl.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
-#include <sys/system_properties.h>
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <termios.h>
@@ -33,8 +32,6 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
-#include <android-base/properties.h>
-#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <hidl-util/FQName.h>
@@ -42,15 +39,23 @@
 #include <selinux/selinux.h>
 #include <system/thread_defs.h>
 
-#include "init.h"
-#include "property_service.h"
 #include "rlimit_parser.h"
 #include "util.h"
 
+#if defined(__ANDROID__)
+#include <sys/system_properties.h>
+
+#include <android-base/properties.h>
+
+#include "init.h"
+#include "property_service.h"
+#else
+#include "host_init_stubs.h"
+#endif
+
 using android::base::boot_clock;
 using android::base::GetProperty;
 using android::base::Join;
-using android::base::make_scope_guard;
 using android::base::ParseInt;
 using android::base::StartsWith;
 using android::base::StringPrintf;
@@ -189,7 +194,8 @@
       capabilities_(capabilities),
       namespace_flags_(namespace_flags),
       seclabel_(seclabel),
-      onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0),
+      onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
+                 "onrestart", {}),
       keychord_id_(0),
       ioprio_class_(IoSchedClass_NONE),
       ioprio_pri_(0),
@@ -199,9 +205,7 @@
       soft_limit_in_bytes_(-1),
       limit_in_bytes_(-1),
       start_order_(0),
-      args_(args) {
-    onrestart_.InitSingleTrigger("onrestart");
-}
+      args_(args) {}
 
 void Service::NotifyStateChange(const std::string& new_state) const {
     if ((flags_ & SVC_TEMPORARY) != 0) {
@@ -1165,7 +1169,7 @@
     // Property values can contain any characters, but may only be a certain length.
     // (The latter restriction is needed because `start` and `stop` work by writing
     // the service name to the "ctl.start" and "ctl.stop" properties.)
-    return is_legal_property_name("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
+    return IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
 }
 
 }  // namespace init
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index f3b643a..c1846f7 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -27,12 +27,14 @@
 #include <selinux/android.h>
 
 #include "action.h"
-#include "property_service.h"
-#include "selinux.h"
 #include "util.h"
 
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
+#if defined(__ANDROID__)
+#include "property_service.h"
+#include "selinux.h"
+#else
+#include "host_init_stubs.h"
+#endif
 
 using android::base::GetExecutablePath;
 using android::base::Join;
@@ -47,6 +49,11 @@
 const std::string kInitContext = "u:r:init:s0";
 const std::string kVendorContext = "u:r:vendor_init:s0";
 
+const char* const paths_and_secontexts[2][2] = {
+    {"/vendor", kVendorContext.c_str()},
+    {"/odm", kVendorContext.c_str()},
+};
+
 namespace {
 
 constexpr size_t kBufferSize = 4096;
@@ -83,7 +90,7 @@
 
 uint32_t SubcontextPropertySet(const std::string& name, const std::string& value) {
     properties_to_set.emplace_back(name, value);
-    return PROP_SUCCESS;
+    return 0;
 }
 
 class SubcontextProcess {
@@ -295,7 +302,11 @@
 
     for (const auto& property : subcontext_reply->properties_to_set()) {
         ucred cr = {.pid = pid_, .uid = 0, .gid = 0};
-        HandlePropertySet(property.name(), property.value(), context_, cr);
+        std::string error;
+        if (HandlePropertySet(property.name(), property.value(), context_, cr, &error) != 0) {
+            LOG(ERROR) << "Subcontext init could not set '" << property.name() << "' to '"
+                       << property.value() << "': " << error;
+        }
     }
 
     if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
@@ -343,9 +354,6 @@
 static std::vector<Subcontext> subcontexts;
 
 std::vector<Subcontext>* InitializeSubcontexts() {
-    static const char* const paths_and_secontexts[][2] = {
-        {"/vendor", kVendorContext.c_str()},
-    };
     for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
         subcontexts.emplace_back(path_prefix, secontext);
     }
diff --git a/init/subcontext.h b/init/subcontext.h
index 5601b80..22d7d43 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -33,6 +33,7 @@
 
 extern const std::string kInitContext;
 extern const std::string kVendorContext;
+extern const char* const paths_and_secontexts[2][2];
 
 class Subcontext {
   public:
diff --git a/init/test_service/Android.bp b/init/test_service/Android.bp
index 6399699..8bd16a7 100644
--- a/init/test_service/Android.bp
+++ b/init/test_service/Android.bp
@@ -17,7 +17,10 @@
 cc_binary {
     name: "test_service",
     srcs: ["test_service.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: ["libbase"],
     init_rc: ["test_service.rc"],
 }
diff --git a/init/util.cpp b/init/util.cpp
index d80cb1e..4455b2e 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -33,7 +33,6 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -42,7 +41,14 @@
 #include <selinux/android.h>
 
 #include "reboot.h"
+
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+
 #include "selinux.h"
+#else
+#include "host_init_stubs.h"
+#endif
 
 #ifdef _INIT_INIT_H
 #error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
@@ -409,5 +415,30 @@
     return false;
 }
 
+bool IsLegalPropertyName(const std::string& name) {
+    size_t namelen = name.size();
+
+    if (namelen < 1) return false;
+    if (name[0] == '.') return false;
+    if (name[namelen - 1] == '.') return false;
+
+    /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
+    /* Don't allow ".." to appear in a property name */
+    for (size_t i = 0; i < namelen; i++) {
+        if (name[i] == '.') {
+            // i=0 is guaranteed to never have a dot. See above.
+            if (name[i - 1] == '.') return false;
+            continue;
+        }
+        if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
+        if (name[i] >= 'a' && name[i] <= 'z') continue;
+        if (name[i] >= 'A' && name[i] <= 'Z') continue;
+        if (name[i] >= '0' && name[i] <= '9') continue;
+        return false;
+    }
+
+    return true;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/util.h b/init/util.h
index 2cfcf6c..07e4864 100644
--- a/init/util.h
+++ b/init/util.h
@@ -62,6 +62,8 @@
 bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
 bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
 
+bool IsLegalPropertyName(const std::string& name);
+
 }  // namespace init
 }  // namespace android
 
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index 29ffe32..ae1481f 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -19,7 +19,7 @@
         "FuseBuffer.cc",
         "FuseBridgeLoop.cc",
         "EpollController.cc",
-    ]
+    ],
 }
 
 cc_test {
@@ -31,5 +31,5 @@
         "tests/FuseAppLoopTest.cc",
         "tests/FuseBridgeLoopTest.cc",
         "tests/FuseBufferTest.cc",
-    ]
+    ],
 }
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 9a12f0d..8a2afea 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -16,7 +16,11 @@
 
 cc_defaults {
     name: "libasyncio_defaults",
-    cflags: ["-Wall", "-Werror", "-Wextra"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
 }
 
 cc_library {
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 10a4e46..4bd01d2 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -27,7 +27,6 @@
 
     include_dirs: ["external/libunwind/include/tdep"],
 
-
     target: {
         darwin: {
             enabled: false,
@@ -41,7 +40,7 @@
         lib64: {
             suffix: "64",
         },
-    }
+    },
 }
 
 libbacktrace_sources = [
@@ -83,6 +82,9 @@
     target: {
         darwin: {
             enabled: true,
+            shared_libs: [
+                "libbase",
+            ],
         },
         linux: {
             srcs: libbacktrace_sources,
@@ -103,7 +105,7 @@
                 "art/runtime",
             ],
 
-            header_libs: [ "jni_headers", ],
+            header_libs: ["jni_headers"],
         },
         android: {
             static_libs: ["libasync_safe"],
@@ -111,7 +113,7 @@
         vendor: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
             exclude_shared_libs: ["libdexfile"],
-        }
+        },
     },
     whole_static_libs: ["libdemangle"],
 }
@@ -127,7 +129,7 @@
     srcs: ["backtrace_testlib.cpp"],
 
     shared_libs: [
-      "libunwindstack",
+        "libunwindstack",
     ],
 }
 
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index dec241c..6445a7c 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -85,14 +85,12 @@
 std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
   std::string map_name;
   if (BacktraceMap::IsValid(frame->map)) {
+    map_name = frame->map.Name();
     if (!frame->map.name.empty()) {
-      map_name = frame->map.name.c_str();
       if (map_name[0] == '[' && map_name[map_name.size() - 1] == ']') {
         map_name.resize(map_name.size() - 1);
         map_name += StringPrintf(":%" PRIPTR "]", frame->map.start);
       }
-    } else {
-      map_name = StringPrintf("<anonymous:%" PRIPTR ">", frame->map.start);
     }
   } else {
     map_name = "<unknown>";
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index c8a500c..bdae140 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -24,11 +24,21 @@
 
 #include <log/log.h>
 
-#include <backtrace/backtrace_constants.h>
+#include <android-base/stringprintf.h>
+#include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
+#include <backtrace/backtrace_constants.h>
 
 #include "thread_utils.h"
 
+using android::base::StringPrintf;
+
+std::string backtrace_map_t::Name() const {
+  if (!name.empty()) return name;
+  if (start == 0 && end == 0) return "";
+  return StringPrintf("<anonymous:%" PRIPTR ">", start);
+}
+
 BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) {
   if (pid_ < 0) {
     pid_ = getpid();
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 7e2e6d0..711a12a 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -52,6 +52,7 @@
   auto process_memory = stack_map->process_memory();
   unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
                                  regs, stack_map->process_memory());
+  unwinder.SetResolveNames(stack_map->ResolveNames());
   if (stack_map->GetJitDebug() != nullptr) {
     unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
   }
@@ -127,6 +128,22 @@
   return true;
 }
 
+bool Backtrace::UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
+                              const backtrace_stackinfo_t& stack,
+                              std::vector<backtrace_frame_data_t>* frames,
+                              BacktraceUnwindError* error) {
+  UnwindStackOfflineMap* offline_map = reinterpret_cast<UnwindStackOfflineMap*>(back_map);
+  // Create the process memory from the stack data since this will almost
+  // always be different each unwind.
+  if (!offline_map->CreateProcessMemory(stack)) {
+    if (error != nullptr) {
+      error->error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    }
+    return false;
+  }
+  return Backtrace::Unwind(regs, back_map, frames, 0U, nullptr, error);
+}
+
 UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
     : BacktraceCurrent(pid, tid, map) {}
 
@@ -220,12 +237,12 @@
 Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
                                     const std::vector<backtrace_map_t>& maps,
                                     const backtrace_stackinfo_t& stack) {
-  BacktraceMap* map = BacktraceMap::CreateOffline(pid, maps, stack);
-  if (map == nullptr) {
+  std::unique_ptr<UnwindStackOfflineMap> map(
+      reinterpret_cast<UnwindStackOfflineMap*>(BacktraceMap::CreateOffline(pid, maps)));
+  if (map.get() == nullptr || !map->CreateProcessMemory(stack)) {
     return nullptr;
   }
-
-  return new UnwindStackOffline(arch, pid, tid, map, false);
+  return new UnwindStackOffline(arch, pid, tid, map.release(), false);
 }
 
 Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map) {
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 1622e30..9c6fed4 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -46,7 +46,7 @@
   std::vector<std::string> search_libs_{"libart.so", "libartd.so"};
   jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
 #if !defined(NO_LIBDEXFILE_SUPPORT)
-  dex_files_.reset(new unwindstack::DexFiles(process_memory_));
+  dex_files_.reset(new unwindstack::DexFiles(process_memory_, search_libs_));
 #endif
 
   if (!stack_maps_->Parse()) {
@@ -127,12 +127,7 @@
   return false;
 }
 
-bool UnwindStackOfflineMap::Build(const std::vector<backtrace_map_t>& backtrace_maps,
-                                  const backtrace_stackinfo_t& stack) {
-  if (stack.start >= stack.end) {
-    return false;
-  }
-
+bool UnwindStackOfflineMap::Build(const std::vector<backtrace_map_t>& backtrace_maps) {
   for (const backtrace_map_t& map : backtrace_maps) {
     maps_.push_back(map);
   }
@@ -145,6 +140,13 @@
   for (const backtrace_map_t& map : maps_) {
     maps->Add(map.start, map.end, map.offset, map.flags, map.name, map.load_bias);
   }
+  return true;
+}
+
+bool UnwindStackOfflineMap::CreateProcessMemory(const backtrace_stackinfo_t& stack) {
+  if (stack.start >= stack.end) {
+    return false;
+  }
 
   // Create the process memory from the stack data.
   uint64_t size = stack.end - stack.start;
@@ -154,7 +156,6 @@
   std::shared_ptr<unwindstack::Memory> shared_memory(memory);
 
   process_memory_.reset(new unwindstack::MemoryRange(shared_memory, 0, size, stack.start));
-
   return true;
 }
 
@@ -182,10 +183,9 @@
 //-------------------------------------------------------------------------
 // BacktraceMap create offline function.
 //-------------------------------------------------------------------------
-BacktraceMap* BacktraceMap::CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps,
-                                          const backtrace_stackinfo_t& stack) {
+BacktraceMap* BacktraceMap::CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps) {
   UnwindStackOfflineMap* map = new UnwindStackOfflineMap(pid);
-  if (!map->Build(maps, stack)) {
+  if (!map->Build(maps)) {
     delete map;
     return nullptr;
   }
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index 94cbfb2..ec0d9c1 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -76,7 +76,9 @@
 
   bool Build() override;
 
-  bool Build(const std::vector<backtrace_map_t>& maps, const backtrace_stackinfo_t& stack);
+  bool Build(const std::vector<backtrace_map_t>& maps);
+
+  bool CreateProcessMemory(const backtrace_stackinfo_t& stack);
 };
 
 #endif  // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 7a37015..a088207 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -151,6 +151,11 @@
                      std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
                      std::vector<std::string>* skip_names, BacktraceUnwindError* error = nullptr);
 
+  static bool UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
+                            const backtrace_stackinfo_t& stack_info,
+                            std::vector<backtrace_frame_data_t>* frames,
+                            BacktraceUnwindError* error = nullptr);
+
   // Get the function name and offset into the function given the pc.
   // If the string is empty, then no valid function name was found,
   // or the pc is not in any valid map.
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index e19c413..473d195 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -48,6 +48,9 @@
   uint64_t load_bias = 0;
   int flags = 0;
   std::string name;
+
+  // Returns `name` if non-empty, or `<anonymous:0x...>` otherwise.
+  std::string Name() const;
 };
 
 namespace unwindstack {
@@ -61,8 +64,7 @@
   // is unsupported.
   static BacktraceMap* Create(pid_t pid, bool uncached = false);
 
-  static BacktraceMap* CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps,
-                                     const backtrace_stackinfo_t& stack);
+  static BacktraceMap* CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps);
 
   virtual ~BacktraceMap();
 
@@ -151,6 +153,13 @@
 
   const std::vector<std::string>& GetSuffixesToIgnore() { return suffixes_to_ignore_; }
 
+  // Disabling the resolving of names results in the function name being
+  // set to an empty string and the function offset being set to zero
+  // in the frame data when unwinding.
+  void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
+
+  bool ResolveNames() { return resolve_names_; }
+
  protected:
   BacktraceMap(pid_t pid);
 
@@ -161,6 +170,7 @@
   pid_t pid_;
   std::deque<backtrace_map_t> maps_;
   std::vector<std::string> suffixes_to_ignore_;
+  bool resolve_names_ = true;
 };
 
 class ScopedBacktraceMapIteratorLock {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index a993d41..5ea981f 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -202,16 +202,6 @@
                                            CAP_MASK_LONG(CAP_NET_RAW),
                                               "vendor/bin/hw/android.hardware.wifi@1.0-service" },
 
-    // A non-privileged zygote that spawns isolated processes for web rendering.
-    { 0750,  AID_ROOT,      AID_ROOT,      CAP_MASK_LONG(CAP_SETUID) |
-                                           CAP_MASK_LONG(CAP_SETGID) |
-                                           CAP_MASK_LONG(CAP_SETPCAP),
-                                              "system/bin/webview_zygote32" },
-    { 0750,  AID_ROOT,      AID_ROOT,      CAP_MASK_LONG(CAP_SETUID) |
-                                           CAP_MASK_LONG(CAP_SETGID) |
-                                           CAP_MASK_LONG(CAP_SETPCAP),
-                                              "system/bin/webview_zygote64" },
-
     // generic defaults
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index fcbdc9b..b2779b2 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -72,7 +72,8 @@
 #define ATRACE_TAG_DATABASE         (1<<20)
 #define ATRACE_TAG_NETWORK          (1<<21)
 #define ATRACE_TAG_ADB              (1<<22)
-#define ATRACE_TAG_LAST             ATRACE_TAG_ADB
+#define ATRACE_TAG_VIBRATOR         (1<<23)
+#define ATRACE_TAG_LAST             ATRACE_TAG_VIBRATOR
 
 // Reserved for initialization.
 #define ATRACE_TAG_NOT_READY        (1ULL<<63)
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index fc354f1..5d17698 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -55,6 +55,9 @@
  */
 
 #define AID_ROOT 0 /* traditional unix root user */
+/* The following are for LTP and should only be used for testing */
+#define AID_DAEMON 1 /* traditional unix daemon owner */
+#define AID_BIN 2    /* traditional unix binaries owner */
 
 #define AID_SYSTEM 1000 /* system server */
 
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index 27255c2..4da8215 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -25,7 +25,6 @@
 void atrace_set_tracing_enabled(bool enabled)
 {
     atomic_store_explicit(&atrace_is_enabled, enabled, memory_order_release);
-    atomic_store_explicit(&atrace_is_ready, false, memory_order_release);
     atrace_update_tags();
 }
 
@@ -35,17 +34,18 @@
     if (atrace_marker_fd == -1) {
         ALOGE("Error opening trace file: %s (%d)", strerror(errno), errno);
         atrace_enabled_tags = 0;
-        return;
+        goto done;
     }
+
     atrace_enabled_tags = atrace_get_property();
+
+done:
+    atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
 }
 
 void atrace_setup()
 {
-    if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
-        pthread_once(&atrace_once_control, atrace_init_once);
-    }
-    atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
+    pthread_once(&atrace_once_control, atrace_init_once);
 }
 
 void atrace_begin_body(const char* name)
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index f32330a..f95c6c5 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -70,7 +70,7 @@
     for (int i = 0; i < count; i++) {
         snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
         property_get(buf, value, "");
-        if (strcmp(value, cmdline) == 0) {
+        if (strcmp(value, "*") == 0 || strcmp(value, cmdline) == 0) {
             return true;
         }
     }
diff --git a/libion/Android.bp b/libion/Android.bp
index 6d9fae0..2f73d92 100644
--- a/libion/Android.bp
+++ b/libion/Android.bp
@@ -1,4 +1,3 @@
-
 cc_library {
     name: "libion",
     vendor_available: true,
diff --git a/libion/ion_test.c b/libion/ion_test.c
index b7d5583..f3874ae 100644
--- a/libion/ion_test.c
+++ b/libion/ion_test.c
@@ -250,7 +250,7 @@
         case 'p':
             prot = 0;
             prot |= strstr(optarg, "MAP_PRIVATE") ? MAP_PRIVATE : 0;
-            prot |= strstr(optarg, "MAP_SHARED") ? MAP_PRIVATE : 0;
+            prot |= strstr(optarg, "MAP_SHARED") ? MAP_SHARED : 0;
             break;
         case 'f':
             alloc_flags = atol(optarg);
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index ade2821..28c87e4 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -171,6 +171,36 @@
 #endif
     ;
 
+#ifndef log_id_t_defined
+#define log_id_t_defined
+typedef enum log_id {
+  LOG_ID_MIN = 0,
+
+  LOG_ID_MAIN = 0,
+  LOG_ID_RADIO = 1,
+  LOG_ID_EVENTS = 2,
+  LOG_ID_SYSTEM = 3,
+  LOG_ID_CRASH = 4,
+  LOG_ID_STATS = 5,
+  LOG_ID_SECURITY = 6,
+  LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
+
+  LOG_ID_MAX
+} log_id_t;
+#endif
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_buf_write(int bufID, int prio, const char* tag,
+                            const char* text);
+int __android_log_buf_print(int bufID, int prio, const char* tag,
+                            const char* fmt, ...)
+#if defined(__GNUC__)
+    __attribute__((__format__(printf, 4, 5)))
+#endif
+    ;
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/liblog/include_vndk/log/log_event_list.h b/liblog/include_vndk/log/log_event_list.h
new file mode 100644
index 0000000..cbd3091
--- /dev/null
+++ b/liblog/include_vndk/log/log_event_list.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2005-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.
+ */
+
+/* Special log_event_list.h file for VNDK linking modules */
+
+#ifndef _LIBS_LOG_EVENT_LIST_H
+#define _LIBS_LOG_EVENT_LIST_H
+
+#include <stdint.h>
+
+#include <log/log_id.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
+
+/*
+ * The opaque context used to manipulate lists of events.
+ */
+#ifndef __android_log_context_defined
+#define __android_log_context_defined
+typedef struct android_log_context_internal* android_log_context;
+#endif
+
+/*
+ * Creates a context associated with an event tag to write elements to
+ * the list of events.
+ */
+android_log_context create_android_logger(uint32_t tag);
+
+/* All lists must be braced by a begin and end call */
+/*
+ * NB: If the first level braces are missing when specifying multiple
+ *     elements, we will manufacturer a list to embrace it for your API
+ *     convenience. For a single element, it will remain solitary.
+ */
+int android_log_write_list_begin(android_log_context ctx);
+int android_log_write_list_end(android_log_context ctx);
+
+int android_log_write_int32(android_log_context ctx, int32_t value);
+int android_log_write_int64(android_log_context ctx, int64_t value);
+int android_log_write_string8(android_log_context ctx, const char* value);
+int android_log_write_string8_len(android_log_context ctx, const char* value,
+                                  size_t maxlen);
+int android_log_write_float32(android_log_context ctx, float value);
+
+/* Submit the composed list context to the specified logger id */
+/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
+int android_log_write_list(android_log_context ctx, log_id_t id);
+
+/* Finished with reader or writer context */
+int android_log_destroy(android_log_context* ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_EVENT_LIST_H */
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 3c4c1f1..66670fe 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -3,8 +3,8 @@
     android_name_to_log_id; # vndk
     android_log_id_to_name; # vndk
     __android_log_assert;
-    __android_log_buf_print; # vndk
-    __android_log_buf_write; # vndk
+    __android_log_buf_print;
+    __android_log_buf_write;
     __android_log_print;
     __android_log_vprint;
     __android_log_write;
@@ -35,6 +35,17 @@
     android_logger_get_statistics; # vndk
     __android_log_error_write; # vndk
     __android_log_is_loggable;
+    create_android_logger; #vndk
+    android_log_destroy; #vndk
+    android_log_write_list_begin; #vndk
+    android_log_write_list_end; #vndk
+    android_log_write_int32; #vndk
+    android_log_write_int64; #vndk
+    android_log_write_string8; #vndk
+    android_log_write_string8_len; #vndk
+    android_log_write_float32; #vndk
+    android_log_write_list; #vndk
+
 };
 
 LIBLOG_O {
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 8b76a65..caca377 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -16,11 +16,11 @@
         },
         host: {
             shared_libs: ["liblog"],
-        }
+        },
     },
 }
 
-cc_library_shared {
+cc_library {
     name: "libmemunreachable",
     defaults: ["libmemunreachable_defaults"],
     srcs: [
@@ -88,14 +88,14 @@
 cc_test {
     name: "memunreachable_binder_test",
     defaults: ["libmemunreachable_defaults"],
+    test_suites: ["vts"],
     srcs: [
         "tests/Binder_test.cpp",
-        "tests/MemUnreachable_test.cpp",
     ],
+    static_libs: ["libmemunreachable"],
     shared_libs: [
         "libbinder",
         "libhwbinder",
-        "libmemunreachable",
         "libutils",
     ],
 }
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
index aca2a82..61a1d24 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -98,6 +98,7 @@
     return (*reinterpret_cast<std::function<int()>*>(arg))();
   };
 
+  // See README.md for why we create the child process this way
   child_pid_ = clone(proxy, stack_->top(), CLONE_VM | CLONE_FS | CLONE_FILES /*|CLONE_UNTRACED*/,
                      reinterpret_cast<void*>(&func_));
   if (child_pid_ < 0) {
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
index 61a47de..ae8fa94 100644
--- a/libmemunreachable/README.md
+++ b/libmemunreachable/README.md
@@ -36,7 +36,7 @@
 
  1. *Original process*: Leak detection is requested by calling `GetUnreachableMemory()`
  2. Allocations are disabled using `malloc_disable()`
- 3. The collection process is spawned.  The collection process is similar to a normal `fork()` child process, except that it shares the address space of the parent - any writes by the original process are visible to the collection process, and vice-versa.
+ 3. The collection process is spawned.  The collection process, created using clone, is similar to a normal `fork()` child process, except that it shares the address space of the parent - any writes by the original process are visible to the collection process, and vice-versa. If we forked instead of using clone, the address space might get out of sync with observed post-ptrace thread state, since it takes some time to pause the parent.
  4. *Collection process*: All threads in the original process are paused with `ptrace()`.
  5. Registers contents, active stack areas, and memory mapping information are collected.
  6. *Original process*: Allocations are re-enabled using `malloc_enable()`, but all threads are still paused with `ptrace()`.
diff --git a/libmemunreachable/tests/Binder_test.cpp b/libmemunreachable/tests/Binder_test.cpp
index 6e85d5a..eaf7652 100644
--- a/libmemunreachable/tests/Binder_test.cpp
+++ b/libmemunreachable/tests/Binder_test.cpp
@@ -33,6 +33,9 @@
 
 static const String16 service_name("test.libmemunreachable_binder");
 
+// Provides a service that will hold a strong reference to any remote binder
+// object, so that the test can verify that a remote strong reference is
+// visible to libmemunreachable.
 class BinderService : public BBinder {
  public:
   BinderService() = default;
@@ -55,6 +58,8 @@
   ~BinderObject() = default;
 };
 
+// Forks a subprocess that registers a BinderService with the global binder
+// servicemanager.  Requires root permissions.
 class ServiceProcess {
  public:
   ServiceProcess() : child_(0) {}
@@ -97,6 +102,7 @@
       fprintf(stderr, "Failed to get service manager\n");
       return 1;
     }
+    // This step requires root permissions
     if (sm->addService(service_name, new BinderService()) != OK) {
       fprintf(stderr, "Failed to add test service\n");
       return 1;
@@ -110,12 +116,18 @@
   pid_t child_;
 };
 
-class BinderTest : public ::testing::Test {
+class MemunreachableBinderTest : public ::testing::Test {
  protected:
   ServiceProcess service_process_;
 };
 
-TEST_F(BinderTest, binder) {
+// Tests that a local binder object with a remote strong reference is visible
+// through the libmemunreachable BinderReferences interface, which uses the
+// getBinderKernelReferences method in libbinder.  Starts a BinderService
+// through ServiceProcess as a remote service to hold the strong reference.
+TEST_F(MemunreachableBinderTest, binder) {
+  ASSERT_EQ(static_cast<uid_t>(0), getuid()) << "This test must be run as root.";
+
   ServiceProcess service_process;
   ASSERT_TRUE(service_process.Run());
 
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 089f3b8..6e63b74 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -1,9 +1,8 @@
-
 cc_library_headers {
     name: "libnativebridge-dummy-headers",
 
     host_supported: true,
-    export_include_dirs=["include"],
+    export_include_dirs: ["include"],
 }
 
 cc_library {
@@ -11,9 +10,12 @@
 
     host_supported: true,
     srcs: ["native_bridge.cc"],
-    shared_libs: ["liblog", "libbase"],
+    shared_libs: [
+        "liblog",
+        "libbase",
+    ],
 
-    export_include_dirs=["include"],
+    export_include_dirs: ["include"],
 
     cflags: [
         "-Werror",
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index 2d33704..5cf88b0 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -17,31 +17,34 @@
 cc_library {
     name: "libfoo.oem1",
     srcs: ["test.cpp"],
-    cflags : ["-DLIBNAME=\"libfoo.oem1.so\""],
+    cflags: ["-DLIBNAME=\"libfoo.oem1.so\""],
     shared_libs: [
         "libbase",
     ],
 }
+
 cc_library {
     name: "libbar.oem1",
     srcs: ["test.cpp"],
-    cflags : ["-DLIBNAME=\"libbar.oem1.so\""],
+    cflags: ["-DLIBNAME=\"libbar.oem1.so\""],
     shared_libs: [
         "libbase",
     ],
 }
+
 cc_library {
     name: "libfoo.oem2",
     srcs: ["test.cpp"],
-    cflags : ["-DLIBNAME=\"libfoo.oem2.so\""],
+    cflags: ["-DLIBNAME=\"libfoo.oem2.so\""],
     shared_libs: [
         "libbase",
     ],
 }
+
 cc_library {
     name: "libbar.oem2",
     srcs: ["test.cpp"],
-    cflags : ["-DLIBNAME=\"libbar.oem2.so\""],
+    cflags: ["-DLIBNAME=\"libbar.oem2.so\""],
     shared_libs: [
         "libbase",
     ],
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index 3d202fc..27693b3 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -2,7 +2,10 @@
 
     name: "libpackagelistparser",
     srcs: ["packagelistparser.c"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: ["liblog"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 1974f2c..b0bc497 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -1,7 +1,7 @@
 cc_library {
     srcs: ["processgroup.cpp"],
     name: "libprocessgroup",
-    defaults: ["linux_bionic_supported"],
+    host_supported: true,
     shared_libs: ["libbase"],
     export_include_dirs: ["include"],
     cflags: [
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 8526b3a..6dfa697 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -22,6 +22,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -33,15 +34,19 @@
 #include <memory>
 #include <mutex>
 #include <set>
+#include <string>
 #include <thread>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/unique_fd.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <private/android_filesystem_config.h>
 
 #include <processgroup/processgroup.h>
 
+using android::base::StartsWith;
+using android::base::StringPrintf;
 using android::base::WriteStringToFile;
 
 using namespace std::chrono_literals;
@@ -50,171 +55,58 @@
 #define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
 #define ACCT_CGROUP_PATH "/acct"
 
-#define PROCESSGROUP_UID_PREFIX "uid_"
-#define PROCESSGROUP_PID_PREFIX "pid_"
 #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
-#define PROCESSGROUP_MAX_UID_LEN 11
-#define PROCESSGROUP_MAX_PID_LEN 11
-#define PROCESSGROUP_MAX_PATH_LEN \
-        ((sizeof(MEM_CGROUP_PATH) > sizeof(ACCT_CGROUP_PATH) ? \
-          sizeof(MEM_CGROUP_PATH) : sizeof(ACCT_CGROUP_PATH)) + \
-         sizeof(PROCESSGROUP_UID_PREFIX) + 1 + \
-         PROCESSGROUP_MAX_UID_LEN + \
-         sizeof(PROCESSGROUP_PID_PREFIX) + 1 + \
-         PROCESSGROUP_MAX_PID_LEN + \
-         sizeof(PROCESSGROUP_CGROUP_PROCS_FILE) + \
-         1)
 
 std::once_flag init_path_flag;
 
-class ProcessGroup {
-  public:
-    ProcessGroup() : buf_ptr_(buf_), buf_len_(0) {}
-
-    bool Open(uid_t uid, int pid);
-
-    // Return positive number and sets *pid = next pid in process cgroup on success
-    // Returns 0 if there are no pids left in the process cgroup
-    // Returns -errno if an error was encountered
-    int GetOneAppProcess(pid_t* pid);
-
-  private:
-    // Returns positive number of bytes filled on success
-    // Returns 0 if there was nothing to read
-    // Returns -errno if an error was encountered
-    int RefillBuffer();
-
-    android::base::unique_fd fd_;
-    char buf_[128];
-    char* buf_ptr_;
-    size_t buf_len_;
-};
-
-static const char* getCgroupRootPath() {
-    static const char* cgroup_root_path = NULL;
+static const std::string& GetCgroupRootPath() {
+    static std::string cgroup_root_path;
     std::call_once(init_path_flag, [&]() {
             // Check if mem cgroup is mounted, only then check for write-access to avoid
             // SELinux denials
-            cgroup_root_path = access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
-                    ACCT_CGROUP_PATH : MEM_CGROUP_PATH;
+            cgroup_root_path =
+                (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ? ACCT_CGROUP_PATH
+                                                                                 : MEM_CGROUP_PATH);
             });
     return cgroup_root_path;
 }
 
-static int convertUidToPath(char *path, size_t size, uid_t uid)
-{
-    return snprintf(path, size, "%s/%s%d",
-            getCgroupRootPath(),
-            PROCESSGROUP_UID_PREFIX,
-            uid);
+static std::string ConvertUidToPath(uid_t uid) {
+    return StringPrintf("%s/uid_%d", GetCgroupRootPath().c_str(), uid);
 }
 
-static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid)
-{
-    return snprintf(path, size, "%s/%s%d/%s%d",
-            getCgroupRootPath(),
-            PROCESSGROUP_UID_PREFIX,
-            uid,
-            PROCESSGROUP_PID_PREFIX,
-            pid);
+static std::string ConvertUidPidToPath(uid_t uid, int pid) {
+    return StringPrintf("%s/uid_%d/pid_%d", GetCgroupRootPath().c_str(), uid, pid);
 }
 
-bool ProcessGroup::Open(uid_t uid, int pid) {
-    char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
-    convertUidPidToPath(path, sizeof(path), uid, pid);
-    strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
-
-    int fd = open(path, O_RDONLY);
-    if (fd < 0) return false;
-
-    fd_.reset(fd);
-
-    LOG(VERBOSE) << "Initialized context for " << path;
-
-    return true;
-}
-
-int ProcessGroup::RefillBuffer() {
-    memmove(buf_, buf_ptr_, buf_len_);
-    buf_ptr_ = buf_;
-
-    ssize_t ret = read(fd_, buf_ptr_ + buf_len_, sizeof(buf_) - buf_len_ - 1);
-    if (ret < 0) {
-        return -errno;
-    } else if (ret == 0) {
-        return 0;
-    }
-
-    buf_len_ += ret;
-    buf_[buf_len_] = 0;
-    LOG(VERBOSE) << "Read " << ret << " to buffer: " << buf_;
-
-    assert(buf_len_ <= sizeof(buf_));
-
-    return ret;
-}
-
-int ProcessGroup::GetOneAppProcess(pid_t* out_pid) {
-    *out_pid = 0;
-
-    char* eptr;
-    while ((eptr = static_cast<char*>(memchr(buf_ptr_, '\n', buf_len_))) == nullptr) {
-        int ret = RefillBuffer();
-        if (ret <= 0) return ret;
-    }
-
-    *eptr = '\0';
-    char* pid_eptr = nullptr;
-    errno = 0;
-    long pid = strtol(buf_ptr_, &pid_eptr, 10);
-    if (errno != 0) {
-        return -errno;
-    }
-    if (pid_eptr != eptr) {
-        errno = EINVAL;
-        return -errno;
-    }
-
-    buf_len_ -= (eptr - buf_ptr_) + 1;
-    buf_ptr_ = eptr + 1;
-
-    *out_pid = static_cast<pid_t>(pid);
-    return 1;
-}
-
-static int removeProcessGroup(uid_t uid, int pid)
-{
+static int RemoveProcessGroup(uid_t uid, int pid) {
     int ret;
-    char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
 
-    convertUidPidToPath(path, sizeof(path), uid, pid);
-    ret = rmdir(path);
+    auto uid_pid_path = ConvertUidPidToPath(uid, pid);
+    ret = rmdir(uid_pid_path.c_str());
 
-    convertUidToPath(path, sizeof(path), uid);
-    rmdir(path);
+    auto uid_path = ConvertUidToPath(uid);
+    rmdir(uid_path.c_str());
 
     return ret;
 }
 
-static void removeUidProcessGroups(const char *uid_path)
-{
-    std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path), closedir);
+static void RemoveUidProcessGroups(const std::string& uid_path) {
+    std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
     if (uid != NULL) {
         dirent* dir;
         while ((dir = readdir(uid.get())) != nullptr) {
-            char path[PROCESSGROUP_MAX_PATH_LEN];
-
             if (dir->d_type != DT_DIR) {
                 continue;
             }
 
-            if (strncmp(dir->d_name, PROCESSGROUP_PID_PREFIX, strlen(PROCESSGROUP_PID_PREFIX))) {
+            if (!StartsWith(dir->d_name, "pid_")) {
                 continue;
             }
 
-            snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
+            auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
             LOG(VERBOSE) << "Removing " << path;
-            if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
+            if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
         }
     }
 }
@@ -222,38 +114,38 @@
 void removeAllProcessGroups()
 {
     LOG(VERBOSE) << "removeAllProcessGroups()";
-    const char* cgroup_root_path = getCgroupRootPath();
-    std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
+    const auto& cgroup_root_path = GetCgroupRootPath();
+    std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
     if (root == NULL) {
         PLOG(ERROR) << "Failed to open " << cgroup_root_path;
     } else {
         dirent* dir;
         while ((dir = readdir(root.get())) != nullptr) {
-            char path[PROCESSGROUP_MAX_PATH_LEN];
-
             if (dir->d_type != DT_DIR) {
                 continue;
             }
-            if (strncmp(dir->d_name, PROCESSGROUP_UID_PREFIX, strlen(PROCESSGROUP_UID_PREFIX))) {
+
+            if (!StartsWith(dir->d_name, "uid_")) {
                 continue;
             }
 
-            snprintf(path, sizeof(path), "%s/%s", cgroup_root_path, dir->d_name);
-            removeUidProcessGroups(path);
+            auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
+            RemoveUidProcessGroups(path);
             LOG(VERBOSE) << "Removing " << path;
-            if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
+            if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
         }
     }
 }
 
 // Returns number of processes killed on success
 // Returns 0 if there are no processes in the process cgroup left to kill
-// Returns -errno on error
-static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
-    ProcessGroup process_group;
-    if (!process_group.Open(uid, initialPid)) {
+// Returns -1 on error
+static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
+    auto path = ConvertUidPidToPath(uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
+    std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
+    if (!fd) {
         PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
-        return -errno;
+        return -1;
     }
 
     // We separate all of the pids in the cgroup into those pids that are also the leaders of
@@ -262,10 +154,9 @@
     pgids.emplace(initialPid);
     std::set<pid_t> pids;
 
-    int ret;
     pid_t pid;
     int processes = 0;
-    while ((ret = process_group.GetOneAppProcess(&pid)) > 0 && pid >= 0) {
+    while (fscanf(fd.get(), "%d\n", &pid) == 1 && pid >= 0) {
         processes++;
         if (pid == 0) {
             // Should never happen...  but if it does, trying to kill this
@@ -312,15 +203,15 @@
         }
     }
 
-    return ret >= 0 ? processes : ret;
+    return feof(fd.get()) ? processes : -1;
 }
 
-static int killProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
+static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
     std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
 
     int retry = retries;
     int processes;
-    while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
+    while ((processes = DoKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
         LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
         if (retry > 0) {
             std::this_thread::sleep_for(5ms);
@@ -350,7 +241,7 @@
             LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
                       << " in " << static_cast<int>(ms) << "ms";
         }
-        return removeProcessGroup(uid, initialPid);
+        return RemoveProcessGroup(uid, initialPid);
     } else {
         if (retries > 0) {
             LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
@@ -362,22 +253,21 @@
 }
 
 int killProcessGroup(uid_t uid, int initialPid, int signal) {
-    return killProcessGroup(uid, initialPid, signal, 40 /*retries*/);
+    return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/);
 }
 
 int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
-    return killProcessGroup(uid, initialPid, signal, 0 /*retries*/);
+    return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/);
 }
 
-static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
-{
-    if (mkdir(path, mode) == -1 && errno != EEXIST) {
+static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+    if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
         return false;
     }
 
-    if (chown(path, uid, gid) == -1) {
+    if (chown(path.c_str(), uid, gid) == -1) {
         int saved_errno = errno;
-        rmdir(path);
+        rmdir(path.c_str());
         errno = saved_errno;
         return false;
     }
@@ -387,42 +277,38 @@
 
 int createProcessGroup(uid_t uid, int initialPid)
 {
-    char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
+    auto uid_path = ConvertUidToPath(uid);
 
-    convertUidToPath(path, sizeof(path), uid);
-
-    if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
-        PLOG(ERROR) << "Failed to make and chown " << path;
+    if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
+        PLOG(ERROR) << "Failed to make and chown " << uid_path;
         return -errno;
     }
 
-    convertUidPidToPath(path, sizeof(path), uid, initialPid);
+    auto uid_pid_path = ConvertUidPidToPath(uid, initialPid);
 
-    if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
-        PLOG(ERROR) << "Failed to make and chown " << path;
+    if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
+        PLOG(ERROR) << "Failed to make and chown " << uid_pid_path;
         return -errno;
     }
 
-    strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
+    auto uid_pid_procs_file = uid_pid_path + PROCESSGROUP_CGROUP_PROCS_FILE;
 
     int ret = 0;
-    if (!WriteStringToFile(std::to_string(initialPid), path)) {
+    if (!WriteStringToFile(std::to_string(initialPid), uid_pid_procs_file)) {
         ret = -errno;
-        PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << path;
+        PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << uid_pid_procs_file;
     }
 
     return ret;
 }
 
-static bool setProcessGroupValue(uid_t uid, int pid, const char* fileName, int64_t value) {
-    char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
-    if (strcmp(getCgroupRootPath(), MEM_CGROUP_PATH)) {
-        PLOG(ERROR) << "Memcg is not mounted." << path;
+static bool SetProcessGroupValue(uid_t uid, int pid, const std::string& file_name, int64_t value) {
+    if (GetCgroupRootPath() != MEM_CGROUP_PATH) {
+        PLOG(ERROR) << "Memcg is not mounted.";
         return false;
     }
 
-    convertUidPidToPath(path, sizeof(path), uid, pid);
-    strlcat(path, fileName, sizeof(path));
+    auto path = ConvertUidPidToPath(uid, pid) + file_name;
 
     if (!WriteStringToFile(std::to_string(value), path)) {
         PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
@@ -432,13 +318,13 @@
 }
 
 bool setProcessGroupSwappiness(uid_t uid, int pid, int swappiness) {
-    return setProcessGroupValue(uid, pid, "/memory.swappiness", swappiness);
+    return SetProcessGroupValue(uid, pid, "/memory.swappiness", swappiness);
 }
 
 bool setProcessGroupSoftLimit(uid_t uid, int pid, int64_t soft_limit_in_bytes) {
-    return setProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes);
+    return SetProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes);
 }
 
 bool setProcessGroupLimit(uid_t uid, int pid, int64_t limit_in_bytes) {
-    return setProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes);
+    return SetProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes);
 }
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 1cfabd5..83b0a7f 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -16,7 +16,11 @@
 
 cc_defaults {
     name: "libprocinfo_defaults",
-    cflags: ["-Wall", "-Werror", "-Wextra"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
 }
 
 cc_library {
@@ -65,7 +69,10 @@
         },
     },
 
-    shared_libs: ["libbase", "libprocinfo"],
+    shared_libs: [
+        "libbase",
+        "libprocinfo",
+    ],
 
     compile_multilib: "both",
     multilib: {
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index 846a585..82bf1bc 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -11,5 +11,5 @@
         windows: {
             enabled: true,
         },
-    }
+    },
 }
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 74dfaa5..124c70e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -78,7 +78,10 @@
     target: {
         // Always disable optimizations for host to make it easier to debug.
         host: {
-            cflags: ["-O0", "-g"],
+            cflags: [
+                "-O0",
+                "-g",
+            ],
         },
         vendor: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
@@ -128,6 +131,15 @@
         "DexFile.cpp",
         "DexFiles.cpp",
     ],
+    target: {
+        // Always disable optimizations for host to make it easier to debug.
+        host: {
+            cflags: [
+                "-O0",
+                "-g",
+            ],
+        },
+    },
 
     shared_libs: [
         "libbase",
@@ -151,8 +163,13 @@
     vendor_available: false,
     defaults: ["libunwindstack_flags"],
 
+    shared: {
+        enabled: false,
+    },
+
     srcs: [
         "tests/DexFileTest.cpp",
+        "tests/DexFilesTest.cpp",
     ],
     local_include_dirs: ["include"],
     allow_undefined_symbols: true,
@@ -160,6 +177,7 @@
     shared_libs: [
         "libbase",
         "libunwindstack",
+        "libdexfile",
     ],
 
     // libdexfile will eventually properly export headers, for now include
@@ -237,10 +255,11 @@
         "tests/files/elf64.xz",
         "tests/files/offline/bad_eh_frame_hdr_arm64/*",
         "tests/files/offline/debug_frame_first_x86/*",
-        "tests/files/offline/jit_debug_arm32/*",
-        "tests/files/offline/jit_debug_x86_32/*",
-        "tests/files/offline/gnu_debugdata_arm32/*",
-        "tests/files/offline/straddle_arm32/*",
+        "tests/files/offline/eh_frame_hdr_begin_x86_64/*",
+        "tests/files/offline/jit_debug_arm/*",
+        "tests/files/offline/jit_debug_x86/*",
+        "tests/files/offline/gnu_debugdata_arm/*",
+        "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
 }
@@ -295,6 +314,15 @@
     ],
 }
 
+cc_binary {
+    name: "unwind_reg_info",
+    defaults: ["libunwindstack_tools"],
+
+    srcs: [
+        "tools/unwind_reg_info.cpp",
+    ],
+}
+
 // Generates the elf data for use in the tests for .gnu_debugdata frames.
 // Once these files are generated, use the xz command to compress the data.
 cc_binary_host {
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 65638ae..6e397e3 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -22,12 +22,12 @@
 #include <android-base/stringprintf.h>
 
 #include <unwindstack/Log.h>
+#include <unwindstack/MachineArm.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/RegsArm.h>
 
 #include "ArmExidx.h"
 #include "Check.h"
-#include "MachineArm.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index be6c2f7..b18b0ce 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -24,7 +24,7 @@
 
 #include <android-base/unique_fd.h>
 
-#include <dex/code_item_accessors-no_art-inl.h>
+#include <dex/code_item_accessors-inl.h>
 #include <dex/compact_dex_file.h>
 #include <dex/dex_file-inl.h>
 #include <dex/dex_file_loader.h>
@@ -52,10 +52,20 @@
   return nullptr;
 }
 
-void DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
+DexFileFromFile::~DexFileFromFile() {
+  if (size_ != 0) {
+    munmap(mapped_memory_, size_);
+  }
+}
+
+bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
                                    uint64_t* method_offset) {
   if (dex_file_ == nullptr) {
-    return;
+    return false;
+  }
+
+  if (!dex_file_->IsInDataSection(dex_file_->Begin() + dex_offset)) {
+    return false;  // The DEX offset is not within the bytecode of this dex file.
   }
 
   for (uint32_t i = 0; i < dex_file_->NumClassDefs(); ++i) {
@@ -82,16 +92,11 @@
       if (offset <= dex_offset && dex_offset < offset + size) {
         *method_name = dex_file_->PrettyMethod(it.GetMemberIndex(), false);
         *method_offset = dex_offset - offset;
-        return;
+        return true;
       }
     }
   }
-}
-
-DexFileFromFile::~DexFileFromFile() {
-  if (size_ != 0) {
-    munmap(mapped_memory_, size_);
-  }
+  return false;
 }
 
 bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
@@ -139,25 +144,40 @@
 }
 
 bool DexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, Memory* memory) {
-  art::DexFile::Header header;
-  if (!memory->ReadFully(dex_file_offset_in_memory, &header, sizeof(header))) {
+  memory_.resize(sizeof(art::DexFile::Header));
+  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
     return false;
   }
 
-  if (!art::StandardDexFile::IsMagicValid(header.magic_) &&
-      !art::CompactDexFile::IsMagicValid(header.magic_)) {
+  art::DexFile::Header* header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
+  uint32_t file_size = header->file_size_;
+  if (art::CompactDexFile::IsMagicValid(header->magic_)) {
+    // Compact dex file store data section separately so that it can be shared.
+    // Therefore we need to extend the read memory range to include it.
+    // TODO: This might be wasteful as we might read data in between as well.
+    //       In practice, this should be fine, as such sharing only happens on disk.
+    uint32_t computed_file_size;
+    if (__builtin_add_overflow(header->data_off_, header->data_size_, &computed_file_size)) {
+      return false;
+    }
+    if (computed_file_size > file_size) {
+      file_size = computed_file_size;
+    }
+  } else if (!art::StandardDexFile::IsMagicValid(header->magic_)) {
     return false;
   }
 
-  memory_.resize(header.file_size_);
-  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), header.file_size_)) {
+  memory_.resize(file_size);
+  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
     return false;
   }
 
+  header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
+
   art::DexFileLoader loader;
   std::string error_msg;
   auto dex =
-      loader.Open(memory_.data(), header.file_size_, "", 0, nullptr, false, false, &error_msg);
+      loader.Open(memory_.data(), header->file_size_, "", 0, nullptr, false, false, &error_msg);
   dex_file_.reset(dex.release());
   return dex_file_ != nullptr;
 }
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index 22e98df..3ce2f1e 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -32,7 +32,7 @@
   DexFile() = default;
   virtual ~DexFile() = default;
 
-  void GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
+  bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
 
   static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info);
 
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index fe6d3c6..430f6c5 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -24,23 +24,140 @@
 
 #include <unwindstack/DexFiles.h>
 #include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 
 #include "DexFile.h"
 
 namespace unwindstack {
 
+struct DEXFileEntry32 {
+  uint32_t next;
+  uint32_t prev;
+  uint32_t dex_file;
+};
+
+struct DEXFileEntry64 {
+  uint64_t next;
+  uint64_t prev;
+  uint64_t dex_file;
+};
+
 DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : memory_(memory) {}
 
+DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+    : memory_(memory), search_libs_(search_libs) {}
+
 DexFiles::~DexFiles() {
   for (auto& entry : files_) {
     delete entry.second;
   }
 }
 
+void DexFiles::SetArch(ArchEnum arch) {
+  switch (arch) {
+    case ARCH_ARM:
+    case ARCH_MIPS:
+    case ARCH_X86:
+      read_entry_ptr_func_ = &DexFiles::ReadEntryPtr32;
+      read_entry_func_ = &DexFiles::ReadEntry32;
+      break;
+
+    case ARCH_ARM64:
+    case ARCH_MIPS64:
+    case ARCH_X86_64:
+      read_entry_ptr_func_ = &DexFiles::ReadEntryPtr64;
+      read_entry_func_ = &DexFiles::ReadEntry64;
+      break;
+
+    case ARCH_UNKNOWN:
+      abort();
+  }
+}
+
+uint64_t DexFiles::ReadEntryPtr32(uint64_t addr) {
+  uint32_t entry;
+  const uint32_t field_offset = 12;  // offset of first_entry_ in the descriptor struct.
+  if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) {
+    return 0;
+  }
+  return entry;
+}
+
+uint64_t DexFiles::ReadEntryPtr64(uint64_t addr) {
+  uint64_t entry;
+  const uint32_t field_offset = 16;  // offset of first_entry_ in the descriptor struct.
+  if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) {
+    return 0;
+  }
+  return entry;
+}
+
+bool DexFiles::ReadEntry32() {
+  DEXFileEntry32 entry;
+  if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
+    entry_addr_ = 0;
+    return false;
+  }
+
+  addrs_.push_back(entry.dex_file);
+  entry_addr_ = entry.next;
+  return true;
+}
+
+bool DexFiles::ReadEntry64() {
+  DEXFileEntry64 entry;
+  if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
+    entry_addr_ = 0;
+    return false;
+  }
+
+  addrs_.push_back(entry.dex_file);
+  entry_addr_ = entry.next;
+  return true;
+}
+
+void DexFiles::Init(Maps* maps) {
+  if (initialized_) {
+    return;
+  }
+  initialized_ = true;
+  entry_addr_ = 0;
+
+  const std::string dex_debug_name("__dex_debug_descriptor");
+  for (MapInfo* info : *maps) {
+    if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
+      continue;
+    }
+
+    if (!search_libs_.empty()) {
+      bool found = false;
+      const char* lib = basename(info->name.c_str());
+      for (const std::string& name : search_libs_) {
+        if (name == lib) {
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        continue;
+      }
+    }
+
+    Elf* elf = info->GetElf(memory_, true);
+    uint64_t ptr;
+    // Find first non-empty list (libart might be loaded multiple times).
+    if (elf->GetGlobalVariable(dex_debug_name, &ptr) && ptr != 0) {
+      entry_addr_ = (this->*read_entry_ptr_func_)(ptr + info->start);
+      if (entry_addr_ != 0) {
+        break;
+      }
+    }
+  }
+}
+
 DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
   // Lock while processing the data.
-  std::lock_guard<std::mutex> guard(files_lock_);
   DexFile* dex_file;
   auto entry = files_.find(dex_file_offset);
   if (entry == files_.end()) {
@@ -52,14 +169,38 @@
   return dex_file;
 }
 
-void DexFiles::GetMethodInformation(uint64_t dex_file_offset, uint64_t dex_offset, MapInfo* info,
-                                    std::string* method_name, uint64_t* method_offset) {
-  DexFile* dex_file = GetDexFile(dex_file_offset, info);
-  if (dex_file != nullptr) {
-    dex_file->GetMethodInformation(dex_offset, method_name, method_offset);
+bool DexFiles::GetAddr(size_t index, uint64_t* addr) {
+  if (index < addrs_.size()) {
+    *addr = addrs_[index];
+    return true;
   }
+  if (entry_addr_ != 0 && (this->*read_entry_func_)()) {
+    *addr = addrs_.back();
+    return true;
+  }
+  return false;
 }
 
-void DexFiles::SetArch(ArchEnum) {}
+void DexFiles::GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc,
+                                    std::string* method_name, uint64_t* method_offset) {
+  std::lock_guard<std::mutex> guard(lock_);
+  if (!initialized_) {
+    Init(maps);
+  }
+
+  size_t index = 0;
+  uint64_t addr;
+  while (GetAddr(index++, &addr)) {
+    if (addr < info->start || addr >= info->end) {
+      continue;
+    }
+
+    DexFile* dex_file = GetDexFile(addr, info);
+    if (dex_file != nullptr &&
+        dex_file->GetMethodInformation(dex_pc - addr, method_name, method_offset)) {
+      break;
+    }
+  }
+}
 
 }  // namespace unwindstack
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
index 2589c89..7a41e45 100644
--- a/libunwindstack/DwarfEhFrame.h
+++ b/libunwindstack/DwarfEhFrame.h
@@ -46,4 +46,4 @@
 
 }  // namespace unwindstack
 
-#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
+#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index a131abe..9a49013 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -109,7 +109,7 @@
     fde_info_.erase(index);
     return nullptr;
   }
-  info->pc = value + 4;
+  info->pc = value;
   return info;
 }
 
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
index dcf04e6..5bc60b9 100644
--- a/libunwindstack/DwarfOp.cpp
+++ b/libunwindstack/DwarfOp.cpp
@@ -36,13 +36,45 @@
 constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
 
 template <typename AddressType>
-bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end, uint8_t dwarf_version) {
-  uint32_t iterations = 0;
+bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end) {
   is_register_ = false;
   stack_.clear();
   memory_->set_cur_offset(start);
+  dex_pc_set_ = false;
+
+  // Unroll the first Decode calls to be able to check for a special
+  // sequence of ops and values that indicate this is the dex pc.
+  // The pattern is:
+  //   OP_const4u (0x0c)  'D' 'E' 'X' '1'
+  //   OP_drop (0x13)
+  if (memory_->cur_offset() < end) {
+    if (!Decode()) {
+      return false;
+    }
+  } else {
+    return true;
+  }
+  bool check_for_drop;
+  if (cur_op_ == 0x0c && operands_.back() == 0x31584544) {
+    check_for_drop = true;
+  } else {
+    check_for_drop = false;
+  }
+  if (memory_->cur_offset() < end) {
+    if (!Decode()) {
+      return false;
+    }
+  } else {
+    return true;
+  }
+
+  if (check_for_drop && cur_op_ == 0x13) {
+    dex_pc_set_ = true;
+  }
+
+  uint32_t iterations = 2;
   while (memory_->cur_offset() < end) {
-    if (!Decode(dwarf_version)) {
+    if (!Decode()) {
       return false;
     }
     // To protect against a branch that creates an infinite loop,
@@ -56,7 +88,7 @@
 }
 
 template <typename AddressType>
-bool DwarfOp<AddressType>::Decode(uint8_t dwarf_version) {
+bool DwarfOp<AddressType>::Decode() {
   last_error_.code = DWARF_ERROR_NONE;
   if (!memory_->ReadBytes(&cur_op_, 1)) {
     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
@@ -71,12 +103,6 @@
     return false;
   }
 
-  // Check for an unsupported opcode.
-  if (dwarf_version < op->supported_version) {
-    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
-    return false;
-  }
-
   // Make sure that the required number of stack elements is available.
   if (stack_.size() < op->num_required_stack_values) {
     last_error_.code = DWARF_ERROR_STACK_INDEX_NOT_VALID;
@@ -434,22 +460,22 @@
 template <typename AddressType>
 bool DwarfOp<AddressType>::op_breg() {
   uint16_t reg = cur_op() - 0x70;
-  if (reg >= regs_->total_regs()) {
+  if (reg >= regs_info_->Total()) {
     last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
-  stack_.push_front((*regs_)[reg] + OperandAt(0));
+  stack_.push_front(regs_info_->Get(reg) + OperandAt(0));
   return true;
 }
 
 template <typename AddressType>
 bool DwarfOp<AddressType>::op_bregx() {
   AddressType reg = OperandAt(0);
-  if (reg >= regs_->total_regs()) {
+  if (reg >= regs_info_->Total()) {
     last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
-  stack_.push_front((*regs_)[reg] + OperandAt(1));
+  stack_.push_front(regs_info_->Get(reg) + OperandAt(1));
   return true;
 }
 
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index 40b7b23..4c69b3d 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -27,16 +27,10 @@
 #include <unwindstack/DwarfError.h>
 
 #include "DwarfEncoding.h"
+#include "RegsInfo.h"
 
 namespace unwindstack {
 
-enum DwarfVersion : uint8_t {
-  DWARF_VERSION_2 = 2,
-  DWARF_VERSION_3 = 3,
-  DWARF_VERSION_4 = 4,
-  DWARF_VERSION_MAX = DWARF_VERSION_4,
-};
-
 // Forward declarations.
 class DwarfMemory;
 class Memory;
@@ -51,7 +45,6 @@
   struct OpCallback {
     const char* name;
     bool (DwarfOp::*handle_func)();
-    uint8_t supported_version;
     uint8_t num_required_stack_values;
     uint8_t num_operands;
     uint8_t operands[2];
@@ -62,21 +55,23 @@
       : memory_(memory), regular_memory_(regular_memory) {}
   virtual ~DwarfOp() = default;
 
-  bool Decode(uint8_t dwarf_version);
+  bool Decode();
 
-  bool Eval(uint64_t start, uint64_t end, uint8_t dwarf_version);
+  bool Eval(uint64_t start, uint64_t end);
 
   void GetLogInfo(uint64_t start, uint64_t end, std::vector<std::string>* lines);
 
   AddressType StackAt(size_t index) { return stack_[index]; }
   size_t StackSize() { return stack_.size(); }
 
-  void set_regs(RegsImpl<AddressType>* regs) { regs_ = regs; }
+  void set_regs_info(RegsInfo<AddressType>* regs_info) { regs_info_ = regs_info; }
 
   const DwarfErrorData& last_error() { return last_error_; }
   DwarfErrorCode LastErrorCode() { return last_error_.code; }
   uint64_t LastErrorAddress() { return last_error_.address; }
 
+  bool dex_pc_set() { return dex_pc_set_; }
+
   bool is_register() { return is_register_; }
 
   uint8_t cur_op() { return cur_op_; }
@@ -97,7 +92,8 @@
   DwarfMemory* memory_;
   Memory* regular_memory_;
 
-  RegsImpl<AddressType>* regs_;
+  RegsInfo<AddressType>* regs_info_;
+  bool dex_pc_set_ = false;
   bool is_register_ = false;
   DwarfErrorData last_error_{DWARF_ERROR_NONE, 0};
   uint8_t cur_op_;
@@ -148,35 +144,32 @@
   bool op_not_implemented();
 
   constexpr static OpCallback kCallbackTable[256] = {
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x00 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x01 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x02 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x00 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x01 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x02 illegal op
       {
           // 0x03 DW_OP_addr
           "DW_OP_addr",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_absptr},
       },
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x04 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x05 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x04 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x05 illegal op
       {
           // 0x06 DW_OP_deref
           "DW_OP_deref",
           &DwarfOp::op_deref,
-          DWARF_VERSION_2,
           1,
           0,
           {},
       },
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x07 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x07 illegal op
       {
           // 0x08 DW_OP_const1u
           "DW_OP_const1u",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata1},
@@ -185,7 +178,6 @@
           // 0x09 DW_OP_const1s
           "DW_OP_const1s",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata1},
@@ -194,7 +186,6 @@
           // 0x0a DW_OP_const2u
           "DW_OP_const2u",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata2},
@@ -203,7 +194,6 @@
           // 0x0b DW_OP_const2s
           "DW_OP_const2s",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata2},
@@ -212,7 +202,6 @@
           // 0x0c DW_OP_const4u
           "DW_OP_const4u",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata4},
@@ -221,7 +210,6 @@
           // 0x0d DW_OP_const4s
           "DW_OP_const4s",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata4},
@@ -230,7 +218,6 @@
           // 0x0e DW_OP_const8u
           "DW_OP_const8u",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata8},
@@ -239,7 +226,6 @@
           // 0x0f DW_OP_const8s
           "DW_OP_const8s",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata8},
@@ -248,7 +234,6 @@
           // 0x10 DW_OP_constu
           "DW_OP_constu",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_uleb128},
@@ -257,7 +242,6 @@
           // 0x11 DW_OP_consts
           "DW_OP_consts",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -266,7 +250,6 @@
           // 0x12 DW_OP_dup
           "DW_OP_dup",
           &DwarfOp::op_dup,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -275,7 +258,6 @@
           // 0x13 DW_OP_drop
           "DW_OP_drop",
           &DwarfOp::op_drop,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -284,7 +266,6 @@
           // 0x14 DW_OP_over
           "DW_OP_over",
           &DwarfOp::op_over,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -293,7 +274,6 @@
           // 0x15 DW_OP_pick
           "DW_OP_pick",
           &DwarfOp::op_pick,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata1},
@@ -302,7 +282,6 @@
           // 0x16 DW_OP_swap
           "DW_OP_swap",
           &DwarfOp::op_swap,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -311,7 +290,6 @@
           // 0x17 DW_OP_rot
           "DW_OP_rot",
           &DwarfOp::op_rot,
-          DWARF_VERSION_2,
           3,
           0,
           {},
@@ -320,7 +298,6 @@
           // 0x18 DW_OP_xderef
           "DW_OP_xderef",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -329,7 +306,6 @@
           // 0x19 DW_OP_abs
           "DW_OP_abs",
           &DwarfOp::op_abs,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -338,7 +314,6 @@
           // 0x1a DW_OP_and
           "DW_OP_and",
           &DwarfOp::op_and,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -347,7 +322,6 @@
           // 0x1b DW_OP_div
           "DW_OP_div",
           &DwarfOp::op_div,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -356,7 +330,6 @@
           // 0x1c DW_OP_minus
           "DW_OP_minus",
           &DwarfOp::op_minus,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -365,7 +338,6 @@
           // 0x1d DW_OP_mod
           "DW_OP_mod",
           &DwarfOp::op_mod,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -374,7 +346,6 @@
           // 0x1e DW_OP_mul
           "DW_OP_mul",
           &DwarfOp::op_mul,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -383,7 +354,6 @@
           // 0x1f DW_OP_neg
           "DW_OP_neg",
           &DwarfOp::op_neg,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -392,7 +362,6 @@
           // 0x20 DW_OP_not
           "DW_OP_not",
           &DwarfOp::op_not,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -401,7 +370,6 @@
           // 0x21 DW_OP_or
           "DW_OP_or",
           &DwarfOp::op_or,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -410,7 +378,6 @@
           // 0x22 DW_OP_plus
           "DW_OP_plus",
           &DwarfOp::op_plus,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -419,7 +386,6 @@
           // 0x23 DW_OP_plus_uconst
           "DW_OP_plus_uconst",
           &DwarfOp::op_plus_uconst,
-          DWARF_VERSION_2,
           1,
           1,
           {DW_EH_PE_uleb128},
@@ -428,7 +394,6 @@
           // 0x24 DW_OP_shl
           "DW_OP_shl",
           &DwarfOp::op_shl,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -437,7 +402,6 @@
           // 0x25 DW_OP_shr
           "DW_OP_shr",
           &DwarfOp::op_shr,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -446,7 +410,6 @@
           // 0x26 DW_OP_shra
           "DW_OP_shra",
           &DwarfOp::op_shra,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -455,7 +418,6 @@
           // 0x27 DW_OP_xor
           "DW_OP_xor",
           &DwarfOp::op_xor,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -464,7 +426,6 @@
           // 0x28 DW_OP_bra
           "DW_OP_bra",
           &DwarfOp::op_bra,
-          DWARF_VERSION_2,
           1,
           1,
           {DW_EH_PE_sdata2},
@@ -473,7 +434,6 @@
           // 0x29 DW_OP_eq
           "DW_OP_eq",
           &DwarfOp::op_eq,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -482,7 +442,6 @@
           // 0x2a DW_OP_ge
           "DW_OP_ge",
           &DwarfOp::op_ge,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -491,7 +450,6 @@
           // 0x2b DW_OP_gt
           "DW_OP_gt",
           &DwarfOp::op_gt,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -500,7 +458,6 @@
           // 0x2c DW_OP_le
           "DW_OP_le",
           &DwarfOp::op_le,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -509,7 +466,6 @@
           // 0x2d DW_OP_lt
           "DW_OP_lt",
           &DwarfOp::op_lt,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -518,7 +474,6 @@
           // 0x2e DW_OP_ne
           "DW_OP_ne",
           &DwarfOp::op_ne,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -527,7 +482,6 @@
           // 0x2f DW_OP_skip
           "DW_OP_skip",
           &DwarfOp::op_skip,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata2},
@@ -536,7 +490,6 @@
           // 0x30 DW_OP_lit0
           "DW_OP_lit0",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -545,7 +498,6 @@
           // 0x31 DW_OP_lit1
           "DW_OP_lit1",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -554,7 +506,6 @@
           // 0x32 DW_OP_lit2
           "DW_OP_lit2",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -563,7 +514,6 @@
           // 0x33 DW_OP_lit3
           "DW_OP_lit3",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -572,7 +522,6 @@
           // 0x34 DW_OP_lit4
           "DW_OP_lit4",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -581,7 +530,6 @@
           // 0x35 DW_OP_lit5
           "DW_OP_lit5",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -590,7 +538,6 @@
           // 0x36 DW_OP_lit6
           "DW_OP_lit6",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -599,7 +546,6 @@
           // 0x37 DW_OP_lit7
           "DW_OP_lit7",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -608,7 +554,6 @@
           // 0x38 DW_OP_lit8
           "DW_OP_lit8",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -617,7 +562,6 @@
           // 0x39 DW_OP_lit9
           "DW_OP_lit9",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -626,7 +570,6 @@
           // 0x3a DW_OP_lit10
           "DW_OP_lit10",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -635,7 +578,6 @@
           // 0x3b DW_OP_lit11
           "DW_OP_lit11",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -644,7 +586,6 @@
           // 0x3c DW_OP_lit12
           "DW_OP_lit12",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -653,7 +594,6 @@
           // 0x3d DW_OP_lit13
           "DW_OP_lit13",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -662,7 +602,6 @@
           // 0x3e DW_OP_lit14
           "DW_OP_lit14",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -671,7 +610,6 @@
           // 0x3f DW_OP_lit15
           "DW_OP_lit15",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -680,7 +618,6 @@
           // 0x40 DW_OP_lit16
           "DW_OP_lit16",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -689,7 +626,6 @@
           // 0x41 DW_OP_lit17
           "DW_OP_lit17",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -698,7 +634,6 @@
           // 0x42 DW_OP_lit18
           "DW_OP_lit18",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -707,7 +642,6 @@
           // 0x43 DW_OP_lit19
           "DW_OP_lit19",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -716,7 +650,6 @@
           // 0x44 DW_OP_lit20
           "DW_OP_lit20",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -725,7 +658,6 @@
           // 0x45 DW_OP_lit21
           "DW_OP_lit21",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -734,7 +666,6 @@
           // 0x46 DW_OP_lit22
           "DW_OP_lit22",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -743,7 +674,6 @@
           // 0x47 DW_OP_lit23
           "DW_OP_lit23",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -752,7 +682,6 @@
           // 0x48 DW_OP_lit24
           "DW_OP_lit24",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -761,7 +690,6 @@
           // 0x49 DW_OP_lit25
           "DW_OP_lit25",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -770,7 +698,6 @@
           // 0x4a DW_OP_lit26
           "DW_OP_lit26",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -779,7 +706,6 @@
           // 0x4b DW_OP_lit27
           "DW_OP_lit27",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -788,7 +714,6 @@
           // 0x4c DW_OP_lit28
           "DW_OP_lit28",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -797,7 +722,6 @@
           // 0x4d DW_OP_lit29
           "DW_OP_lit29",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -806,7 +730,6 @@
           // 0x4e DW_OP_lit30
           "DW_OP_lit30",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -815,7 +738,6 @@
           // 0x4f DW_OP_lit31
           "DW_OP_lit31",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -824,7 +746,6 @@
           // 0x50 DW_OP_reg0
           "DW_OP_reg0",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -833,7 +754,6 @@
           // 0x51 DW_OP_reg1
           "DW_OP_reg1",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -842,7 +762,6 @@
           // 0x52 DW_OP_reg2
           "DW_OP_reg2",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -851,7 +770,6 @@
           // 0x53 DW_OP_reg3
           "DW_OP_reg3",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -860,7 +778,6 @@
           // 0x54 DW_OP_reg4
           "DW_OP_reg4",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -869,7 +786,6 @@
           // 0x55 DW_OP_reg5
           "DW_OP_reg5",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -878,7 +794,6 @@
           // 0x56 DW_OP_reg6
           "DW_OP_reg6",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -887,7 +802,6 @@
           // 0x57 DW_OP_reg7
           "DW_OP_reg7",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -896,7 +810,6 @@
           // 0x58 DW_OP_reg8
           "DW_OP_reg8",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -905,7 +818,6 @@
           // 0x59 DW_OP_reg9
           "DW_OP_reg9",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -914,7 +826,6 @@
           // 0x5a DW_OP_reg10
           "DW_OP_reg10",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -923,7 +834,6 @@
           // 0x5b DW_OP_reg11
           "DW_OP_reg11",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -932,7 +842,6 @@
           // 0x5c DW_OP_reg12
           "DW_OP_reg12",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -941,7 +850,6 @@
           // 0x5d DW_OP_reg13
           "DW_OP_reg13",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -950,7 +858,6 @@
           // 0x5e DW_OP_reg14
           "DW_OP_reg14",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -959,7 +866,6 @@
           // 0x5f DW_OP_reg15
           "DW_OP_reg15",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -968,7 +874,6 @@
           // 0x60 DW_OP_reg16
           "DW_OP_reg16",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -977,7 +882,6 @@
           // 0x61 DW_OP_reg17
           "DW_OP_reg17",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -986,7 +890,6 @@
           // 0x62 DW_OP_reg18
           "DW_OP_reg18",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -995,7 +898,6 @@
           // 0x63 DW_OP_reg19
           "DW_OP_reg19",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1004,7 +906,6 @@
           // 0x64 DW_OP_reg20
           "DW_OP_reg20",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1013,7 +914,6 @@
           // 0x65 DW_OP_reg21
           "DW_OP_reg21",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1022,7 +922,6 @@
           // 0x66 DW_OP_reg22
           "DW_OP_reg22",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1031,7 +930,6 @@
           // 0x67 DW_OP_reg23
           "DW_OP_reg23",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1040,7 +938,6 @@
           // 0x68 DW_OP_reg24
           "DW_OP_reg24",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1049,7 +946,6 @@
           // 0x69 DW_OP_reg25
           "DW_OP_reg25",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1058,7 +954,6 @@
           // 0x6a DW_OP_reg26
           "DW_OP_reg26",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1067,7 +962,6 @@
           // 0x6b DW_OP_reg27
           "DW_OP_reg27",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1076,7 +970,6 @@
           // 0x6c DW_OP_reg28
           "DW_OP_reg28",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1085,7 +978,6 @@
           // 0x6d DW_OP_reg29
           "DW_OP_reg29",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1094,7 +986,6 @@
           // 0x6e DW_OP_reg30
           "DW_OP_reg30",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1103,7 +994,6 @@
           // 0x6f DW_OP_reg31
           "DW_OP_reg31",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1112,7 +1002,6 @@
           // 0x70 DW_OP_breg0
           "DW_OP_breg0",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1121,7 +1010,6 @@
           // 0x71 DW_OP_breg1
           "DW_OP_breg1",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1130,7 +1018,6 @@
           // 0x72 DW_OP_breg2
           "DW_OP_breg2",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1139,7 +1026,6 @@
           // 0x73 DW_OP_breg3
           "DW_OP_breg3",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1148,7 +1034,6 @@
           // 0x74 DW_OP_breg4
           "DW_OP_breg4",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1157,7 +1042,6 @@
           // 0x75 DW_OP_breg5
           "DW_OP_breg5",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1166,7 +1050,6 @@
           // 0x76 DW_OP_breg6
           "DW_OP_breg6",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1175,7 +1058,6 @@
           // 0x77 DW_OP_breg7
           "DW_OP_breg7",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1184,7 +1066,6 @@
           // 0x78 DW_OP_breg8
           "DW_OP_breg8",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1193,7 +1074,6 @@
           // 0x79 DW_OP_breg9
           "DW_OP_breg9",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1202,7 +1082,6 @@
           // 0x7a DW_OP_breg10
           "DW_OP_breg10",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1211,7 +1090,6 @@
           // 0x7b DW_OP_breg11
           "DW_OP_breg11",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1220,7 +1098,6 @@
           // 0x7c DW_OP_breg12
           "DW_OP_breg12",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1229,7 +1106,6 @@
           // 0x7d DW_OP_breg13
           "DW_OP_breg13",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1238,7 +1114,6 @@
           // 0x7e DW_OP_breg14
           "DW_OP_breg14",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1247,7 +1122,6 @@
           // 0x7f DW_OP_breg15
           "DW_OP_breg15",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1256,7 +1130,6 @@
           // 0x80 DW_OP_breg16
           "DW_OP_breg16",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1265,7 +1138,6 @@
           // 0x81 DW_OP_breg17
           "DW_OP_breg17",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1274,7 +1146,6 @@
           // 0x82 DW_OP_breg18
           "DW_OP_breg18",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1283,7 +1154,6 @@
           // 0x83 DW_OP_breg19
           "DW_OP_breg19",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1292,7 +1162,6 @@
           // 0x84 DW_OP_breg20
           "DW_OP_breg20",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1301,7 +1170,6 @@
           // 0x85 DW_OP_breg21
           "DW_OP_breg21",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1310,7 +1178,6 @@
           // 0x86 DW_OP_breg22
           "DW_OP_breg22",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1319,7 +1186,6 @@
           // 0x87 DW_OP_breg23
           "DW_OP_breg23",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1328,7 +1194,6 @@
           // 0x88 DW_OP_breg24
           "DW_OP_breg24",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1337,7 +1202,6 @@
           // 0x89 DW_OP_breg25
           "DW_OP_breg25",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1346,7 +1210,6 @@
           // 0x8a DW_OP_breg26
           "DW_OP_breg26",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1355,7 +1218,6 @@
           // 0x8b DW_OP_breg27
           "DW_OP_breg27",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1364,7 +1226,6 @@
           // 0x8c DW_OP_breg28
           "DW_OP_breg28",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1373,7 +1234,6 @@
           // 0x8d DW_OP_breg29
           "DW_OP_breg29",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1382,7 +1242,6 @@
           // 0x8e DW_OP_breg30
           "DW_OP_breg30",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1391,7 +1250,6 @@
           // 0x8f DW_OP_breg31
           "DW_OP_breg31",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1400,7 +1258,6 @@
           // 0x90 DW_OP_regx
           "DW_OP_regx",
           &DwarfOp::op_regx,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_uleb128},
@@ -1409,7 +1266,6 @@
           // 0x91 DW_OP_fbreg
           "DW_OP_fbreg",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1418,7 +1274,6 @@
           // 0x92 DW_OP_bregx
           "DW_OP_bregx",
           &DwarfOp::op_bregx,
-          DWARF_VERSION_2,
           0,
           2,
           {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
@@ -1427,7 +1282,6 @@
           // 0x93 DW_OP_piece
           "DW_OP_piece",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_uleb128},
@@ -1436,7 +1290,6 @@
           // 0x94 DW_OP_deref_size
           "DW_OP_deref_size",
           &DwarfOp::op_deref_size,
-          DWARF_VERSION_2,
           1,
           1,
           {DW_EH_PE_udata1},
@@ -1445,7 +1298,6 @@
           // 0x95 DW_OP_xderef_size
           "DW_OP_xderef_size",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata1},
@@ -1454,7 +1306,6 @@
           // 0x96 DW_OP_nop
           "DW_OP_nop",
           &DwarfOp::op_nop,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1463,7 +1314,6 @@
           // 0x97 DW_OP_push_object_address
           "DW_OP_push_object_address",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           0,
           {},
@@ -1472,7 +1322,6 @@
           // 0x98 DW_OP_call2
           "DW_OP_call2",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           1,
           {DW_EH_PE_udata2},
@@ -1481,7 +1330,6 @@
           // 0x99 DW_OP_call4
           "DW_OP_call4",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           1,
           {DW_EH_PE_udata4},
@@ -1490,7 +1338,6 @@
           // 0x9a DW_OP_call_ref
           "DW_OP_call_ref",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           0,  // Has a different sized operand (4 bytes or 8 bytes).
           {},
@@ -1499,7 +1346,6 @@
           // 0x9b DW_OP_form_tls_address
           "DW_OP_form_tls_address",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           0,
           {},
@@ -1508,7 +1354,6 @@
           // 0x9c DW_OP_call_frame_cfa
           "DW_OP_call_frame_cfa",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           0,
           {},
@@ -1517,7 +1362,6 @@
           // 0x9d DW_OP_bit_piece
           "DW_OP_bit_piece",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           2,
           {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
@@ -1526,7 +1370,6 @@
           // 0x9e DW_OP_implicit_value
           "DW_OP_implicit_value",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_4,
           0,
           1,
           {DW_EH_PE_uleb128},
@@ -1535,107 +1378,106 @@
           // 0x9f DW_OP_stack_value
           "DW_OP_stack_value",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_4,
           1,
           0,
           {},
       },
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xaa illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xab illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xac illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xad illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xae illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xaf illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xba illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbc illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbd illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbe illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbf illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xca illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcc illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcd illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xce illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcf illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xda illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdc illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdd illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xde illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdf illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe0 DW_OP_lo_user
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xea illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xeb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xec illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xed illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xee illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xef illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfa illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfc illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfd illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfe illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xff DW_OP_hi_user
+      {nullptr, nullptr, 0, 0, {}},  // 0xa0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xaa illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xab illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xac illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xad illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xae illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xaf illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xba illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbc illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbd illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbe illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbf illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xca illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xcb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xcc illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xcd illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xce illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xcf illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xda illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xdb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xdc illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xdd illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xde illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xdf illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe0 DW_OP_lo_user
+      {nullptr, nullptr, 0, 0, {}},  // 0xe1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xea illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xeb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xec illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xed illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xee illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xef illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfa illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfc illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfd illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfe illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xff DW_OP_hi_user
   };
 };
 
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 4e94f88..7649798 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -26,16 +26,14 @@
 #include <unwindstack/Regs.h>
 
 #include "DwarfCfa.h"
-#include "DwarfEncoding.h"
-#include "DwarfOp.h"
-
 #include "DwarfDebugFrame.h"
 #include "DwarfEhFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfOp.h"
+#include "RegsInfo.h"
 
 namespace unwindstack {
 
-constexpr uint64_t DEX_PC_REG = 0x20444558;
-
 DwarfSection::DwarfSection(Memory* memory) : memory_(memory) {}
 
 const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
@@ -75,14 +73,17 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uint8_t version,
-                                                   Memory* regular_memory, AddressType* value) {
+bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, Memory* regular_memory,
+                                                   AddressType* value,
+                                                   RegsInfo<AddressType>* regs_info,
+                                                   bool* is_dex_pc) {
   DwarfOp<AddressType> op(&memory_, regular_memory);
+  op.set_regs_info(regs_info);
 
   // 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)) {
+  uint64_t end = loc.values[1];
+  uint64_t start = end - loc.values[0];
+  if (!op.Eval(start, end)) {
     last_error_ = op.last_error();
     return false;
   }
@@ -96,6 +97,9 @@
     return false;
   }
   *value = op.StackAt(0);
+  if (is_dex_pc != nullptr && op.dex_pc_set()) {
+    *is_dex_pc = true;
+  }
   return true;
 }
 
@@ -103,12 +107,10 @@
 struct EvalInfo {
   const dwarf_loc_regs_t* loc_regs;
   const DwarfCie* cie;
-  RegsImpl<AddressType>* cur_regs;
   Memory* regular_memory;
   AddressType cfa;
   bool return_address_undefined = false;
-  uint64_t reg_map = 0;
-  AddressType reg_values[64];
+  RegsInfo<AddressType> regs_info;
 };
 
 template <typename AddressType>
@@ -129,32 +131,18 @@
       break;
     case DWARF_LOCATION_REGISTER: {
       uint32_t cur_reg = loc->values[0];
-      if (cur_reg >= eval_info->cur_regs->total_regs()) {
+      if (cur_reg >= eval_info->regs_info.Total()) {
         last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
         return false;
       }
-      AddressType* cur_reg_ptr = &(*eval_info->cur_regs)[cur_reg];
-      const auto& entry = eval_info->loc_regs->find(cur_reg);
-      if (entry != eval_info->loc_regs->end()) {
-        if (!(eval_info->reg_map & (1 << cur_reg))) {
-          eval_info->reg_map |= 1 << cur_reg;
-          eval_info->reg_values[cur_reg] = *cur_reg_ptr;
-          if (!EvalRegister(&entry->second, cur_reg, cur_reg_ptr, eval_info)) {
-            return false;
-          }
-        }
-
-        // Use the register value from before any evaluations.
-        *reg_ptr = eval_info->reg_values[cur_reg] + loc->values[1];
-      } else {
-        *reg_ptr = *cur_reg_ptr + loc->values[1];
-      }
+      *reg_ptr = eval_info->regs_info.Get(cur_reg) + loc->values[1];
       break;
     }
     case DWARF_LOCATION_EXPRESSION:
     case DWARF_LOCATION_VAL_EXPRESSION: {
       AddressType value;
-      if (!EvalExpression(*loc, eval_info->cie->version, regular_memory, &value)) {
+      bool is_dex_pc = false;
+      if (!EvalExpression(*loc, regular_memory, &value, &eval_info->regs_info, &is_dex_pc)) {
         return false;
       }
       if (loc->type == DWARF_LOCATION_EXPRESSION) {
@@ -165,6 +153,9 @@
         }
       } else {
         *reg_ptr = value;
+        if (is_dex_pc) {
+          eval_info->regs_info.regs->set_dex_pc(value);
+        }
       }
       break;
     }
@@ -201,8 +192,10 @@
 
   AddressType prev_cfa = regs->sp();
 
-  EvalInfo<AddressType> eval_info{
-      .loc_regs = &loc_regs, .cie = cie, .regular_memory = regular_memory, .cur_regs = cur_regs};
+  EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
+                                  .cie = cie,
+                                  .regular_memory = regular_memory,
+                                  .regs_info = RegsInfo<AddressType>(cur_regs)};
   const DwarfLocation* loc = &cfa_entry->second;
   // Only a few location types are valid for the cfa.
   switch (loc->type) {
@@ -224,7 +217,7 @@
     case DWARF_LOCATION_EXPRESSION:
     case DWARF_LOCATION_VAL_EXPRESSION: {
       AddressType value;
-      if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
+      if (!EvalExpression(*loc, regular_memory, &value, &eval_info.regs_info, nullptr)) {
         return false;
       }
       if (loc->type == DWARF_LOCATION_EXPRESSION) {
@@ -249,28 +242,15 @@
     if (reg == CFA_REG) continue;
 
     AddressType* reg_ptr;
-    AddressType dex_pc = 0;
-    if (reg == DEX_PC_REG) {
-      // Special register that indicates this is a dex pc.
-      dex_pc = 0;
-      reg_ptr = &dex_pc;
-    } else if (reg >= cur_regs->total_regs() || eval_info.reg_map & (1 << reg)) {
-      // Skip this unknown register, or a register that has already been
-      // processed.
+    if (reg >= cur_regs->total_regs()) {
+      // Skip this unknown register.
       continue;
-    } else {
-      reg_ptr = &(*cur_regs)[reg];
-      eval_info.reg_map |= 1 << reg;
-      eval_info.reg_values[reg] = *reg_ptr;
     }
 
+    reg_ptr = eval_info.regs_info.Save(reg);
     if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
       return false;
     }
-
-    if (reg == DEX_PC_REG) {
-      cur_regs->set_dex_pc(dex_pc);
-    }
   }
 
   // Find the return address location.
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index dbf772e..02f8a9a 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -20,6 +20,7 @@
 #include <memory>
 #include <mutex>
 #include <string>
+#include <utility>
 
 #define LOG_TAG "unwind"
 #include <log/log.h>
@@ -36,7 +37,7 @@
 namespace unwindstack {
 
 bool Elf::cache_enabled_;
-std::unordered_map<std::string, std::shared_ptr<Elf>>* Elf::cache_;
+std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* Elf::cache_;
 std::mutex* Elf::cache_lock_;
 
 bool Elf::Init(bool init_gnu_debugdata) {
@@ -308,7 +309,7 @@
 void Elf::SetCachingEnabled(bool enable) {
   if (!cache_enabled_ && enable) {
     cache_enabled_ = true;
-    cache_ = new std::unordered_map<std::string, std::shared_ptr<Elf>>;
+    cache_ = new std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>;
     cache_lock_ = new std::mutex;
   } else if (cache_enabled_ && !enable) {
     cache_enabled_ = false;
@@ -326,18 +327,54 @@
 }
 
 void Elf::CacheAdd(MapInfo* info) {
-  if (info->offset == 0) {
-    (*cache_)[info->name] = info->elf;
-  } else {
-    std::string name(info->name + ':' + std::to_string(info->offset));
-    (*cache_)[name] = info->elf;
+  // If elf_offset != 0, then cache both name:offset and name.
+  // The cached name is used to do lookups if multiple maps for the same
+  // named elf file exist.
+  // For example, if there are two maps boot.odex:1000 and boot.odex:2000
+  // where each reference the entire boot.odex, the cache will properly
+  // use the same cached elf object.
+
+  if (info->offset == 0 || info->elf_offset != 0) {
+    (*cache_)[info->name] = std::make_pair(info->elf, true);
+  }
+
+  if (info->offset != 0) {
+    // The second element in the pair indicates whether elf_offset should
+    // be set to offset when getting out of the cache.
+    (*cache_)[info->name + ':' + std::to_string(info->offset)] =
+        std::make_pair(info->elf, info->elf_offset != 0);
   }
 }
 
-bool Elf::CacheGet(const std::string& name, std::shared_ptr<Elf>* elf) {
+bool Elf::CacheAfterCreateMemory(MapInfo* info) {
+  if (info->name.empty() || info->offset == 0 || info->elf_offset == 0) {
+    return false;
+  }
+
+  auto entry = cache_->find(info->name);
+  if (entry == cache_->end()) {
+    return false;
+  }
+
+  // In this case, the whole file is the elf, and the name has already
+  // been cached. Add an entry at name:offset to get this directly out
+  // of the cache next time.
+  info->elf = entry->second.first;
+  (*cache_)[info->name + ':' + std::to_string(info->offset)] = std::make_pair(info->elf, true);
+  return true;
+}
+
+bool Elf::CacheGet(MapInfo* info) {
+  std::string name(info->name);
+  if (info->offset != 0) {
+    name += ':' + std::to_string(info->offset);
+  }
   auto entry = cache_->find(name);
   if (entry != cache_->end()) {
-    *elf = entry->second;
+    info->elf = entry->second.first;
+    if (entry->second.second) {
+      info->elf_offset = info->offset;
+    }
     return true;
   }
   return false;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index e413081..10afe33 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -19,6 +19,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 
 #include <7zCrc.h>
 #include <Xz.h>
@@ -322,19 +323,13 @@
   // Skip the first header, it's always going to be NULL.
   offset += ehdr.e_shentsize;
   for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
-    if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
+    if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
       last_error_.code = ERROR_MEMORY_INVALID;
-      last_error_.address =
-          offset + reinterpret_cast<uintptr_t>(&shdr.sh_type) - reinterpret_cast<uintptr_t>(&shdr);
+      last_error_.address = offset;
       return false;
     }
 
     if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
-      if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset;
-        return false;
-      }
       // Need to go get the information about the section that contains
       // the string terminated names.
       ShdrType str_shdr;
@@ -343,39 +338,19 @@
         return false;
       }
       uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
-      if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_type, sizeof(str_shdr.sh_type))) {
+      if (!memory_->Read(str_offset, &str_shdr, sizeof(str_shdr))) {
         last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = str_offset + reinterpret_cast<uintptr_t>(&str_shdr.sh_type) -
-                              reinterpret_cast<uintptr_t>(&str_shdr);
+        last_error_.address = str_offset;
         return false;
       }
       if (str_shdr.sh_type != SHT_STRTAB) {
         last_error_.code = ERROR_UNWIND_INFO;
         return false;
       }
-      if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_offset,
-                              sizeof(str_shdr.sh_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = str_offset + reinterpret_cast<uintptr_t>(&str_shdr.sh_offset) -
-                              reinterpret_cast<uintptr_t>(&str_shdr);
-        return false;
-      }
-      if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_size, sizeof(str_shdr.sh_size))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = str_offset + reinterpret_cast<uintptr_t>(&str_shdr.sh_size) -
-                              reinterpret_cast<uintptr_t>(&str_shdr);
-        return false;
-      }
       symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
                                      str_shdr.sh_offset, str_shdr.sh_size));
     } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) {
       // Look for the .debug_frame and .gnu_debugdata.
-      if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&shdr.sh_name) -
-                              reinterpret_cast<uintptr_t>(&shdr);
-        return false;
-      }
       if (shdr.sh_name < sec_size) {
         std::string name;
         if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
@@ -394,14 +369,16 @@
             offset_ptr = &eh_frame_hdr_offset_;
             size_ptr = &eh_frame_hdr_size_;
           }
-          if (offset_ptr != nullptr &&
-              memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
-              memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+          if (offset_ptr != nullptr) {
             *offset_ptr = shdr.sh_offset;
             *size_ptr = shdr.sh_size;
           }
         }
       }
+    } else if (shdr.sh_type == SHT_STRTAB) {
+      // In order to read soname, keep track of address to offset mapping.
+      strtabs_.push_back(std::make_pair<uint64_t, uint64_t>(static_cast<uint64_t>(shdr.sh_addr),
+                                                            static_cast<uint64_t>(shdr.sh_offset)));
     }
   }
   return true;
@@ -420,7 +397,7 @@
   soname_type_ = SONAME_INVALID;
 
   uint64_t soname_offset = 0;
-  uint64_t strtab_offset = 0;
+  uint64_t strtab_addr = 0;
   uint64_t strtab_size = 0;
 
   // Find the soname location from the dynamic headers section.
@@ -435,7 +412,7 @@
     }
 
     if (dyn.d_tag == DT_STRTAB) {
-      strtab_offset = dyn.d_un.d_ptr;
+      strtab_addr = dyn.d_un.d_ptr;
     } else if (dyn.d_tag == DT_STRSZ) {
       strtab_size = dyn.d_un.d_val;
     } else if (dyn.d_tag == DT_SONAME) {
@@ -445,16 +422,22 @@
     }
   }
 
-  soname_offset += strtab_offset;
-  if (soname_offset >= strtab_offset + strtab_size) {
-    return false;
+  // Need to map the strtab address to the real offset.
+  for (const auto& entry : strtabs_) {
+    if (entry.first == strtab_addr) {
+      soname_offset = entry.second + soname_offset;
+      if (soname_offset >= entry.second + strtab_size) {
+        return false;
+      }
+      if (!memory_->ReadString(soname_offset, &soname_)) {
+        return false;
+      }
+      soname_type_ = SONAME_VALID;
+      *soname = soname_;
+      return true;
+    }
   }
-  if (!memory_->ReadString(soname_offset, &soname_)) {
-    return false;
-  }
-  soname_type_ = SONAME_VALID;
-  *soname = soname_;
-  return true;
+  return false;
 }
 
 template <typename SymType>
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index 616d1b1..dfb8e8f 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -17,12 +17,12 @@
 #include <elf.h>
 #include <stdint.h>
 
+#include <unwindstack/MachineArm.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/RegsArm.h>
 
 #include "ArmExidx.h"
 #include "ElfInterfaceArm.h"
-#include "MachineArm.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index d1dc0e6..0c319ec 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -172,7 +172,7 @@
   // Regardless of what happens below, consider the init finished.
   initialized_ = true;
 
-  std::string descriptor_name("__jit_debug_descriptor");
+  const std::string descriptor_name("__jit_debug_descriptor");
   for (MapInfo* info : *maps) {
     if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
       continue;
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 0c15335..39378a3 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -117,23 +117,15 @@
   if (Elf::CachingEnabled() && !name.empty()) {
     Elf::CacheLock();
     locked = true;
-    if (offset != 0) {
-      std::string hash(name + ':' + std::to_string(offset));
-      if (Elf::CacheGet(hash, &elf)) {
-        Elf::CacheUnlock();
-        return elf.get();
-      }
-    } else if (Elf::CacheGet(name, &elf)) {
+    if (Elf::CacheGet(this)) {
       Elf::CacheUnlock();
       return elf.get();
     }
   }
 
   Memory* memory = CreateMemory(process_memory);
-  if (locked && offset != 0 && elf_offset != 0) {
-    // In this case, the whole file is the elf, need to see if the elf
-    // data was cached.
-    if (Elf::CacheGet(name, &elf)) {
+  if (locked) {
+    if (Elf::CacheAfterCreateMemory(this)) {
       delete memory;
       Elf::CacheUnlock();
       return elf.get();
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index 7feafad..c7dec52 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -25,17 +25,16 @@
 #include <unwindstack/Regs.h>
 #include <unwindstack/RegsArm.h>
 #include <unwindstack/RegsArm64.h>
-#include <unwindstack/RegsX86.h>
-#include <unwindstack/RegsX86_64.h>
 #include <unwindstack/RegsMips.h>
 #include <unwindstack/RegsMips64.h>
-
-#include "UserArm.h"
-#include "UserArm64.h"
-#include "UserX86.h"
-#include "UserX86_64.h"
-#include "UserMips.h"
-#include "UserMips64.h"
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/UserArm.h>
+#include <unwindstack/UserArm64.h>
+#include <unwindstack/UserMips.h>
+#include <unwindstack/UserMips64.h>
+#include <unwindstack/UserX86.h>
+#include <unwindstack/UserX86_64.h>
 
 namespace unwindstack {
 
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index d05c3e2..5502ce1 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -19,13 +19,12 @@
 #include <functional>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/MachineArm.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/RegsArm.h>
-
-#include "MachineArm.h"
-#include "UcontextArm.h"
-#include "UserArm.h"
+#include <unwindstack/UcontextArm.h>
+#include <unwindstack/UserArm.h>
 
 namespace unwindstack {
 
@@ -36,26 +35,25 @@
   return ARCH_ARM;
 }
 
-uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
+uint64_t RegsArm::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
   uint64_t load_bias = elf->GetLoadBias();
   if (rel_pc < load_bias) {
-    return rel_pc;
+    return 0;
   }
   uint64_t adjusted_rel_pc = rel_pc - load_bias;
-
   if (adjusted_rel_pc < 5) {
-    return rel_pc;
+    return 0;
   }
 
   if (adjusted_rel_pc & 1) {
     // This is a thumb instruction, it could be 2 or 4 bytes.
     uint32_t value;
-    if (rel_pc < 5 || !elf->memory()->ReadFully(adjusted_rel_pc - 5, &value, sizeof(value)) ||
+    if (!elf->memory()->ReadFully(adjusted_rel_pc - 5, &value, sizeof(value)) ||
         (value & 0xe000f000) != 0xe000f000) {
-      return rel_pc - 2;
+      return 2;
     }
   }
-  return rel_pc - 4;
+  return 4;
 }
 
 void RegsArm::SetFromRaw() {
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index 2077bc5..cc6f5ce 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -19,13 +19,12 @@
 #include <functional>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/MachineArm64.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/RegsArm64.h>
-
-#include "MachineArm64.h"
-#include "UcontextArm64.h"
-#include "UserArm64.h"
+#include <unwindstack/UcontextArm64.h>
+#include <unwindstack/UserArm64.h>
 
 namespace unwindstack {
 
@@ -36,15 +35,11 @@
   return ARCH_ARM64;
 }
 
-uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
+uint64_t RegsArm64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid() || rel_pc < 4) {
+    return 0;
   }
-
-  if (rel_pc < 4) {
-    return rel_pc;
-  }
-  return rel_pc - 4;
+  return 4;
 }
 
 void RegsArm64::SetFromRaw() {
diff --git a/libunwindstack/RegsInfo.h b/libunwindstack/RegsInfo.h
new file mode 100644
index 0000000..47825f5
--- /dev/null
+++ b/libunwindstack/RegsInfo.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_INFO_H
+#define _LIBUNWINDSTACK_REGS_INFO_H
+
+#include <stdint.h>
+
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+template <typename AddressType>
+struct RegsInfo {
+  RegsInfo(RegsImpl<AddressType>* regs) : regs(regs) {}
+
+  RegsImpl<AddressType>* regs = nullptr;
+  uint64_t saved_reg_map = 0;
+  AddressType saved_regs[64];
+
+  inline AddressType Get(uint32_t reg) {
+    if (IsSaved(reg)) {
+      return saved_regs[reg];
+    }
+    return (*regs)[reg];
+  }
+
+  inline AddressType* Save(uint32_t reg) {
+    if (reg > sizeof(saved_regs) / sizeof(AddressType)) {
+      // This should never happen as since all currently supported
+      // architectures have the total number of registers < 64.
+      abort();
+    }
+    saved_reg_map |= 1 << reg;
+    saved_regs[reg] = (*regs)[reg];
+    return &(*regs)[reg];
+  }
+
+  inline bool IsSaved(uint32_t reg) {
+    if (reg > sizeof(saved_regs) / sizeof(AddressType)) {
+      // This should never happen as since all currently supported
+      // architectures have the total number of registers < 64.
+      abort();
+    }
+    return saved_reg_map & (1 << reg);
+  }
+
+  inline uint16_t Total() { return regs->total_regs(); }
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_INFO_H
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index 44cde05..5d20bef 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -19,13 +19,12 @@
 #include <functional>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/MachineMips.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/RegsMips.h>
-
-#include "MachineMips.h"
-#include "UcontextMips.h"
-#include "UserMips.h"
+#include <unwindstack/UcontextMips.h>
+#include <unwindstack/UserMips.h>
 
 namespace unwindstack {
 
@@ -36,16 +35,12 @@
   return ARCH_MIPS;
 }
 
-uint64_t RegsMips::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
+uint64_t RegsMips::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid() || rel_pc < 8) {
+    return 0;
   }
-
-  // For now, just assuming no compact branches
-  if (rel_pc < 8) {
-    return rel_pc;
-  }
-  return rel_pc - 8;
+  // For now, just assume no compact branches
+  return 8;
 }
 
 void RegsMips::SetFromRaw() {
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
index b4e5246..4a03538 100644
--- a/libunwindstack/RegsMips64.cpp
+++ b/libunwindstack/RegsMips64.cpp
@@ -19,13 +19,12 @@
 #include <functional>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/MachineMips64.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/RegsMips64.h>
-
-#include "MachineMips64.h"
-#include "UcontextMips64.h"
-#include "UserMips64.h"
+#include <unwindstack/UcontextMips64.h>
+#include <unwindstack/UserMips64.h>
 
 namespace unwindstack {
 
@@ -37,16 +36,12 @@
   return ARCH_MIPS64;
 }
 
-uint64_t RegsMips64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
+uint64_t RegsMips64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid() || rel_pc < 8) {
+    return 0;
   }
-
-  // For now, just assuming no compact branches
-  if (rel_pc < 8) {
-    return rel_pc;
-  }
-  return rel_pc - 8;
+  // For now, just assume no compact branches
+  return 8;
 }
 
 void RegsMips64::SetFromRaw() {
diff --git a/libunwindstack/RegsX86.cpp b/libunwindstack/RegsX86.cpp
index ef2f3de..573cb23 100644
--- a/libunwindstack/RegsX86.cpp
+++ b/libunwindstack/RegsX86.cpp
@@ -19,13 +19,12 @@
 #include <functional>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/MachineX86.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/RegsX86.h>
-
-#include "MachineX86.h"
-#include "UcontextX86.h"
-#include "UserX86.h"
+#include <unwindstack/UcontextX86.h>
+#include <unwindstack/UserX86.h>
 
 namespace unwindstack {
 
@@ -36,15 +35,11 @@
   return ARCH_X86;
 }
 
-uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
-  }
-
-  if (rel_pc == 0) {
+uint64_t RegsX86::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid() || rel_pc == 0) {
     return 0;
   }
-  return rel_pc - 1;
+  return 1;
 }
 
 void RegsX86::SetFromRaw() {
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index 70921f8..3175a90 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -19,13 +19,12 @@
 #include <functional>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/MachineX86_64.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/RegsX86_64.h>
-
-#include "MachineX86_64.h"
-#include "UcontextX86_64.h"
-#include "UserX86_64.h"
+#include <unwindstack/UcontextX86_64.h>
+#include <unwindstack/UserX86_64.h>
 
 namespace unwindstack {
 
@@ -36,16 +35,11 @@
   return ARCH_X86_64;
 }
 
-uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
-  }
-
-  if (rel_pc == 0) {
+uint64_t RegsX86_64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid() || rel_pc == 0) {
     return 0;
   }
-
-  return rel_pc - 1;
+  return 1;
 }
 
 void RegsX86_64::SetFromRaw() {
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index db8278e..7da6994 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -48,72 +48,55 @@
   size_t frame_num = frames_.size();
   frames_.resize(frame_num + 1);
   FrameData* frame = &frames_.at(frame_num);
+  frame->num = frame_num;
 
   uint64_t dex_pc = regs_->dex_pc();
   frame->pc = dex_pc;
   frame->sp = regs_->sp();
 
-  auto it = maps_->begin();
-  uint64_t rel_dex_pc;
-  MapInfo* info;
-  for (; it != maps_->end(); ++it) {
-    auto entry = *it;
-    if (dex_pc >= entry->start && dex_pc < entry->end) {
-      info = entry;
-      rel_dex_pc = dex_pc - entry->start;
-      frame->map_start = entry->start;
-      frame->map_end = entry->end;
-      frame->map_offset = entry->offset;
-      frame->map_load_bias = entry->load_bias;
-      frame->map_flags = entry->flags;
-      frame->map_name = entry->name;
-      frame->rel_pc = rel_dex_pc;
-      break;
-    }
-  }
-
-  if (it == maps_->end() || ++it == maps_->end()) {
+  MapInfo* info = maps_->Find(dex_pc);
+  if (info != nullptr) {
+    frame->map_start = info->start;
+    frame->map_end = info->end;
+    frame->map_offset = info->offset;
+    frame->map_load_bias = info->load_bias;
+    frame->map_flags = info->flags;
+    frame->map_name = info->name;
+    frame->rel_pc = dex_pc - info->start;
+  } else {
+    frame->rel_pc = dex_pc;
     return;
   }
 
-  auto entry = *it;
-  unwindstack::Elf* elf = entry->GetElf(process_memory_, true);
-  if (!elf->valid()) {
-    return;
-  }
-
-  // Adjust the relative dex by the offset.
-  rel_dex_pc += entry->elf_offset;
-
-  uint64_t dex_offset;
-  if (!elf->GetFunctionName(rel_dex_pc, &frame->function_name, &dex_offset)) {
-    return;
-  }
-  frame->function_offset = dex_offset;
-  if (frame->function_name != "$dexfile") {
+  if (!resolve_names_) {
     return;
   }
 
 #if !defined(NO_LIBDEXFILE_SUPPORT)
-  dex_files_->GetMethodInformation(dex_pc - dex_offset, dex_offset, info, &frame->function_name,
+  if (dex_files_ == nullptr) {
+    return;
+  }
+
+  // dex_files_->GetMethodInformation(dex_pc - dex_offset, dex_offset, info, &frame->function_name,
+  dex_files_->GetMethodInformation(maps_, info, dex_pc, &frame->function_name,
                                    &frame->function_offset);
 #endif
 }
 
-void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t func_pc) {
+void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
+                           uint64_t pc_adjustment) {
   size_t frame_num = frames_.size();
   frames_.resize(frame_num + 1);
   FrameData* frame = &frames_.at(frame_num);
   frame->num = frame_num;
   frame->sp = regs_->sp();
-  frame->rel_pc = adjusted_rel_pc;
+  frame->rel_pc = rel_pc - pc_adjustment;
+  frame->pc = regs_->pc() - pc_adjustment;
 
   if (map_info == nullptr) {
-    frame->pc = regs_->pc();
     return;
   }
 
-  frame->pc = map_info->start + adjusted_rel_pc;
   frame->map_name = map_info->name;
   frame->map_offset = map_info->offset;
   frame->map_start = map_info->start;
@@ -121,7 +104,8 @@
   frame->map_flags = map_info->flags;
   frame->map_load_bias = elf->GetLoadBias();
 
-  if (!elf->GetFunctionName(func_pc, &frame->function_name, &frame->function_offset)) {
+  if (!resolve_names_ ||
+      !elf->GetFunctionName(func_pc, &frame->function_name, &frame->function_offset)) {
     frame->function_name = "";
     frame->function_offset = 0;
   }
@@ -156,13 +140,12 @@
 
     MapInfo* map_info = maps_->Find(regs_->pc());
     uint64_t rel_pc;
-    uint64_t adjusted_pc;
-    uint64_t adjusted_rel_pc;
+    uint64_t pc_adjustment = 0;
+    uint64_t step_pc;
     Elf* elf;
     if (map_info == nullptr) {
       rel_pc = regs_->pc();
-      adjusted_rel_pc = rel_pc;
-      adjusted_pc = rel_pc;
+      step_pc = rel_pc;
       last_error_.code = ERROR_INVALID_MAP;
     } else {
       if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
@@ -171,21 +154,20 @@
       elf = map_info->GetElf(process_memory_, true);
       rel_pc = elf->GetRelPc(regs_->pc(), map_info);
       if (adjust_pc) {
-        adjusted_pc = regs_->GetAdjustedPc(rel_pc, elf);
+        pc_adjustment = regs_->GetPcAdjustment(rel_pc, elf);
       } else {
-        adjusted_pc = rel_pc;
+        pc_adjustment = 0;
       }
-      adjusted_rel_pc = adjusted_pc;
+      step_pc = rel_pc - pc_adjustment;
 
       // If the pc is in an invalid elf file, try and get an Elf object
       // using the jit debug information.
       if (!elf->valid() && jit_debug_ != nullptr) {
-        uint64_t adjusted_jit_pc = regs_->pc() - (rel_pc - adjusted_pc);
+        uint64_t adjusted_jit_pc = regs_->pc() - pc_adjustment;
         Elf* jit_elf = jit_debug_->GetElf(maps_, adjusted_jit_pc);
         if (jit_elf != nullptr) {
           // The jit debug information requires a non relative adjusted pc.
-          adjusted_pc = adjusted_jit_pc;
-          adjusted_rel_pc = adjusted_pc - map_info->start;
+          step_pc = adjusted_jit_pc;
           elf = jit_elf;
         }
       }
@@ -197,9 +179,11 @@
       if (regs_->dex_pc() != 0) {
         // Add a frame to represent the dex file.
         FillInDexFrame();
+        // Clear the dex pc so that we don't repeat this frame later.
+        regs_->set_dex_pc(0);
       }
 
-      FillInFrame(map_info, elf, adjusted_rel_pc, adjusted_pc);
+      FillInFrame(map_info, elf, rel_pc, step_pc, pc_adjustment);
 
       // Once a frame is added, stop skipping frames.
       initial_map_names_to_skip = nullptr;
@@ -227,8 +211,8 @@
           in_device_map = true;
         } else {
           bool finished;
-          stepped = elf->Step(rel_pc, adjusted_pc, map_info->elf_offset, regs_,
-                              process_memory_.get(), &finished);
+          stepped = elf->Step(rel_pc, step_pc, map_info->elf_offset, regs_, process_memory_.get(),
+                              &finished);
           elf->GetLastError(&last_error_);
           if (stepped && finished) {
             break;
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index 50c9c32..26f5d35 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -23,6 +23,7 @@
 #include <mutex>
 #include <string>
 #include <unordered_map>
+#include <vector>
 
 namespace unwindstack {
 
@@ -36,19 +37,40 @@
 class DexFiles {
  public:
   explicit DexFiles(std::shared_ptr<Memory>& memory);
+  DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
   ~DexFiles();
 
   DexFile* GetDexFile(uint64_t dex_file_offset, MapInfo* info);
 
-  void GetMethodInformation(uint64_t dex_file_offset, uint64_t dex_offset, MapInfo* info,
-                            std::string* method_name, uint64_t* method_offset);
+  void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name,
+                            uint64_t* method_offset);
 
   void SetArch(ArchEnum arch);
 
  private:
+  void Init(Maps* maps);
+
+  bool GetAddr(size_t index, uint64_t* addr);
+
+  uint64_t ReadEntryPtr32(uint64_t addr);
+
+  uint64_t ReadEntryPtr64(uint64_t addr);
+
+  bool ReadEntry32();
+
+  bool ReadEntry64();
+
   std::shared_ptr<Memory> memory_;
-  std::mutex files_lock_;
+  std::vector<std::string> search_libs_;
+
+  std::mutex lock_;
+  bool initialized_ = false;
   std::unordered_map<uint64_t, DexFile*> files_;
+
+  uint64_t entry_addr_ = 0;
+  uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr;
+  bool (DexFiles::*read_entry_func_)() = nullptr;
+  std::vector<uint64_t> addrs_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 03f40d6..da91fd0 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -32,6 +32,8 @@
 // Forward declarations.
 class Memory;
 class Regs;
+template <typename AddressType>
+struct RegsInfo;
 
 class DwarfSection {
  public:
@@ -149,8 +151,8 @@
   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);
+  bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
+                      RegsInfo<AddressType>* regs_info, bool* is_dex_pc);
 
   bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
 
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index a874709..385973e 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -23,6 +23,7 @@
 #include <mutex>
 #include <string>
 #include <unordered_map>
+#include <utility>
 
 #include <unwindstack/ElfInterface.h>
 #include <unwindstack/Memory.h>
@@ -103,7 +104,8 @@
   static void CacheLock();
   static void CacheUnlock();
   static void CacheAdd(MapInfo* info);
-  static bool CacheGet(const std::string& name, std::shared_ptr<Elf>* elf);
+  static bool CacheGet(MapInfo* info);
+  static bool CacheAfterCreateMemory(MapInfo* info);
 
  protected:
   bool valid_ = false;
@@ -120,7 +122,7 @@
   std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
 
   static bool cache_enabled_;
-  static std::unordered_map<std::string, std::shared_ptr<Elf>>* cache_;
+  static std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* cache_;
   static std::mutex* cache_lock_;
 };
 
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index ea9ec9d..3a221bc 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -157,6 +157,7 @@
   ElfInterface* gnu_debugdata_interface_ = nullptr;
 
   std::vector<Symbols*> symbols_;
+  std::vector<std::pair<uint64_t, uint64_t>> strtabs_;
 };
 
 class ElfInterface32 : public ElfInterface {
diff --git a/libunwindstack/MachineArm.h b/libunwindstack/include/unwindstack/MachineArm.h
similarity index 100%
rename from libunwindstack/MachineArm.h
rename to libunwindstack/include/unwindstack/MachineArm.h
diff --git a/libunwindstack/MachineArm64.h b/libunwindstack/include/unwindstack/MachineArm64.h
similarity index 100%
rename from libunwindstack/MachineArm64.h
rename to libunwindstack/include/unwindstack/MachineArm64.h
diff --git a/libunwindstack/MachineMips.h b/libunwindstack/include/unwindstack/MachineMips.h
similarity index 100%
rename from libunwindstack/MachineMips.h
rename to libunwindstack/include/unwindstack/MachineMips.h
diff --git a/libunwindstack/MachineMips64.h b/libunwindstack/include/unwindstack/MachineMips64.h
similarity index 100%
rename from libunwindstack/MachineMips64.h
rename to libunwindstack/include/unwindstack/MachineMips64.h
diff --git a/libunwindstack/MachineX86.h b/libunwindstack/include/unwindstack/MachineX86.h
similarity index 100%
rename from libunwindstack/MachineX86.h
rename to libunwindstack/include/unwindstack/MachineX86.h
diff --git a/libunwindstack/MachineX86_64.h b/libunwindstack/include/unwindstack/MachineX86_64.h
similarity index 100%
rename from libunwindstack/MachineX86_64.h
rename to libunwindstack/include/unwindstack/MachineX86_64.h
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index a5ba7a0..b0e7ea1 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -60,7 +60,7 @@
   uint64_t dex_pc() { return dex_pc_; }
   void set_dex_pc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
 
-  virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
+  virtual uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) = 0;
 
   virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
 
diff --git a/libunwindstack/include/unwindstack/RegsArm.h b/libunwindstack/include/unwindstack/RegsArm.h
index b5d344b..5af90d3 100644
--- a/libunwindstack/include/unwindstack/RegsArm.h
+++ b/libunwindstack/include/unwindstack/RegsArm.h
@@ -36,7 +36,7 @@
 
   virtual ArchEnum Arch() override final;
 
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+  uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
 
   void SetFromRaw() override;
 
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
index 30e626c..cb05732 100644
--- a/libunwindstack/include/unwindstack/RegsArm64.h
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -36,7 +36,7 @@
 
   virtual ArchEnum Arch() override final;
 
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+  uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
 
   void SetFromRaw() override;
 
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
index 3fe6a9f..8e3c01f 100644
--- a/libunwindstack/include/unwindstack/RegsMips.h
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -36,7 +36,7 @@
 
   virtual ArchEnum Arch() override final;
 
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+  uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
 
   void SetFromRaw() override;
 
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
index 6b4bcdf..8c2d443 100644
--- a/libunwindstack/include/unwindstack/RegsMips64.h
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -36,7 +36,7 @@
 
   virtual ArchEnum Arch() override final;
 
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+  uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
 
   void SetFromRaw() override;
 
diff --git a/libunwindstack/include/unwindstack/RegsX86.h b/libunwindstack/include/unwindstack/RegsX86.h
index a695bbf..1bc145d 100644
--- a/libunwindstack/include/unwindstack/RegsX86.h
+++ b/libunwindstack/include/unwindstack/RegsX86.h
@@ -37,7 +37,7 @@
 
   virtual ArchEnum Arch() override final;
 
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+  uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
 
   void SetFromRaw() override;
 
diff --git a/libunwindstack/include/unwindstack/RegsX86_64.h b/libunwindstack/include/unwindstack/RegsX86_64.h
index 23a3f20..4cd45d4 100644
--- a/libunwindstack/include/unwindstack/RegsX86_64.h
+++ b/libunwindstack/include/unwindstack/RegsX86_64.h
@@ -37,7 +37,7 @@
 
   virtual ArchEnum Arch() override final;
 
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+  uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
 
   void SetFromRaw() override;
 
diff --git a/libunwindstack/UcontextArm.h b/libunwindstack/include/unwindstack/UcontextArm.h
similarity index 98%
rename from libunwindstack/UcontextArm.h
rename to libunwindstack/include/unwindstack/UcontextArm.h
index 8c94166..7d1ec3b 100644
--- a/libunwindstack/UcontextArm.h
+++ b/libunwindstack/include/unwindstack/UcontextArm.h
@@ -31,7 +31,7 @@
 
 #include <stdint.h>
 
-#include "MachineArm.h"
+#include <unwindstack/MachineArm.h>
 
 namespace unwindstack {
 
diff --git a/libunwindstack/UcontextArm64.h b/libunwindstack/include/unwindstack/UcontextArm64.h
similarity index 98%
rename from libunwindstack/UcontextArm64.h
rename to libunwindstack/include/unwindstack/UcontextArm64.h
index 655719f..a68be3b 100644
--- a/libunwindstack/UcontextArm64.h
+++ b/libunwindstack/include/unwindstack/UcontextArm64.h
@@ -31,7 +31,7 @@
 
 #include <stdint.h>
 
-#include "MachineArm64.h"
+#include <unwindstack/MachineArm64.h>
 
 namespace unwindstack {
 
diff --git a/libunwindstack/UcontextMips.h b/libunwindstack/include/unwindstack/UcontextMips.h
similarity index 97%
rename from libunwindstack/UcontextMips.h
rename to libunwindstack/include/unwindstack/UcontextMips.h
index 27185e7..02e33b6 100644
--- a/libunwindstack/UcontextMips.h
+++ b/libunwindstack/include/unwindstack/UcontextMips.h
@@ -31,7 +31,7 @@
 
 #include <stdint.h>
 
-#include "MachineMips.h"
+#include <unwindstack/MachineMips.h>
 
 namespace unwindstack {
 
diff --git a/libunwindstack/UcontextMips64.h b/libunwindstack/include/unwindstack/UcontextMips64.h
similarity index 96%
rename from libunwindstack/UcontextMips64.h
rename to libunwindstack/include/unwindstack/UcontextMips64.h
index 623bf3a..5b92a55 100644
--- a/libunwindstack/UcontextMips64.h
+++ b/libunwindstack/include/unwindstack/UcontextMips64.h
@@ -31,7 +31,7 @@
 
 #include <stdint.h>
 
-#include "MachineMips64.h"
+#include <unwindstack/MachineMips64.h>
 
 namespace unwindstack {
 
@@ -66,4 +66,4 @@
 
 }  // namespace unwindstack
 
-#endif  // _LIBUNWINDSTACK_UCONTEXT_MIPS6464_H
+#endif  // _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
diff --git a/libunwindstack/UcontextX86.h b/libunwindstack/include/unwindstack/UcontextX86.h
similarity index 98%
rename from libunwindstack/UcontextX86.h
rename to libunwindstack/include/unwindstack/UcontextX86.h
index f79d92b..c96ebb7 100644
--- a/libunwindstack/UcontextX86.h
+++ b/libunwindstack/include/unwindstack/UcontextX86.h
@@ -31,7 +31,7 @@
 
 #include <stdint.h>
 
-#include "MachineX86.h"
+#include <unwindstack/MachineX86.h>
 
 namespace unwindstack {
 
diff --git a/libunwindstack/UcontextX86_64.h b/libunwindstack/include/unwindstack/UcontextX86_64.h
similarity index 98%
rename from libunwindstack/UcontextX86_64.h
rename to libunwindstack/include/unwindstack/UcontextX86_64.h
index 2b8bdc4..4e163e5 100644
--- a/libunwindstack/UcontextX86_64.h
+++ b/libunwindstack/include/unwindstack/UcontextX86_64.h
@@ -31,7 +31,7 @@
 
 #include <stdint.h>
 
-#include "MachineX86_64.h"
+#include <unwindstack/MachineX86_64.h>
 
 namespace unwindstack {
 
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index e8af8b4..56b0581 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -45,14 +45,14 @@
   uint64_t sp;
 
   std::string function_name;
-  uint64_t function_offset;
+  uint64_t function_offset = 0;
 
   std::string map_name;
-  uint64_t map_offset;
-  uint64_t map_start;
-  uint64_t map_end;
-  uint64_t map_load_bias;
-  int map_flags;
+  uint64_t map_offset = 0;
+  uint64_t map_start = 0;
+  uint64_t map_end = 0;
+  uint64_t map_load_bias = 0;
+  int map_flags = 0;
 };
 
 class Unwinder {
@@ -75,6 +75,10 @@
 
   void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
 
+  // Disabling the resolving of names results in the function name being
+  // set to an empty string and the function offset being set to zero.
+  void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
+
 #if !defined(NO_LIBDEXFILE_SUPPORT)
   void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
 #endif
@@ -84,7 +88,8 @@
 
  private:
   void FillInDexFrame();
-  void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t adjusted_pc);
+  void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
+                   uint64_t pc_adjustment);
 
   size_t max_frames_;
   Maps* maps_;
@@ -95,6 +100,7 @@
 #if !defined(NO_LIBDEXFILE_SUPPORT)
   DexFiles* dex_files_ = nullptr;
 #endif
+  bool resolve_names_ = true;
   ErrorData last_error_;
 };
 
diff --git a/libunwindstack/UserArm.h b/libunwindstack/include/unwindstack/UserArm.h
similarity index 100%
rename from libunwindstack/UserArm.h
rename to libunwindstack/include/unwindstack/UserArm.h
diff --git a/libunwindstack/UserArm64.h b/libunwindstack/include/unwindstack/UserArm64.h
similarity index 100%
rename from libunwindstack/UserArm64.h
rename to libunwindstack/include/unwindstack/UserArm64.h
diff --git a/libunwindstack/UserMips.h b/libunwindstack/include/unwindstack/UserMips.h
similarity index 100%
rename from libunwindstack/UserMips.h
rename to libunwindstack/include/unwindstack/UserMips.h
diff --git a/libunwindstack/UserMips64.h b/libunwindstack/include/unwindstack/UserMips64.h
similarity index 100%
rename from libunwindstack/UserMips64.h
rename to libunwindstack/include/unwindstack/UserMips64.h
diff --git a/libunwindstack/UserX86.h b/libunwindstack/include/unwindstack/UserX86.h
similarity index 100%
rename from libunwindstack/UserX86.h
rename to libunwindstack/include/unwindstack/UserX86.h
diff --git a/libunwindstack/UserX86_64.h b/libunwindstack/include/unwindstack/UserX86_64.h
similarity index 100%
rename from libunwindstack/UserX86_64.h
rename to libunwindstack/include/unwindstack/UserX86_64.h
diff --git a/libunwindstack/tests/DexFileData.h b/libunwindstack/tests/DexFileData.h
new file mode 100644
index 0000000..6975c68
--- /dev/null
+++ b/libunwindstack/tests/DexFileData.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DEXFILESDATA_H
+#define _LIBUNWINDSTACK_DEXFILESDATA_H
+
+namespace unwindstack {
+
+// Borrowed from art/dex/dex_file_test.cc.
+static constexpr uint32_t kDexData[] = {
+    0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
+    0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
+    0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
+    0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
+    0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
+    0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
+    0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
+    0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
+    0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
+    0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
+    0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
+    0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
+    0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
+    0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
+    0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
+    0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
+    0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DEXFILESDATA_H
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index d1338cb..0b02c5b 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -25,38 +25,18 @@
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
 
-#include <dex/code_item_accessors-no_art-inl.h>
+#include <dex/code_item_accessors-inl.h>
 #include <dex/standard_dex_file.h>
 
 #include <gtest/gtest.h>
 
 #include "DexFile.h"
 
+#include "DexFileData.h"
 #include "MemoryFake.h"
 
 namespace unwindstack {
 
-// Borrowed from art/dex/dex_file_test.cc.
-static constexpr uint32_t kDexData[] = {
-    0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
-    0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
-    0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
-    0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
-    0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
-    0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
-    0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
-    0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
-    0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
-    0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
-    0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
-    0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
-    0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
-    0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
-    0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
-    0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
-    0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
-};
-
 TEST(DexFileTest, from_file_open_non_exist) {
   DexFileFromFile dex_file;
   ASSERT_FALSE(dex_file.Open(0, "/file/does/not/exist"));
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
new file mode 100644
index 0000000..d029bb0
--- /dev/null
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <string.h>
+
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "DexFileData.h"
+#include "ElfFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class DexFilesTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+
+    dex_files_.reset(new DexFiles(process_memory_));
+    dex_files_->SetArch(ARCH_ARM);
+
+    maps_.reset(
+        new BufferMaps("1000-4000 ---s 00000000 00:00 0\n"
+                       "4000-6000 r--s 00000000 00:00 0\n"
+                       "6000-8000 -w-s 00000000 00:00 0\n"
+                       "a000-c000 r-xp 00000000 00:00 0\n"
+                       "c000-f000 rwxp 00000000 00:00 0\n"
+                       "f000-11000 r-xp 00000000 00:00 0\n"
+                       "100000-110000 rw-p 0000000 00:00 0\n"
+                       "200000-210000 rw-p 0000000 00:00 0\n"
+                       "300000-400000 rw-p 0000000 00:00 0\n"));
+    ASSERT_TRUE(maps_->Parse());
+
+    // Global variable in a section that is not readable/executable.
+    MapInfo* map_info = maps_->Get(kMapGlobalNonReadableExectable);
+    ASSERT_TRUE(map_info != nullptr);
+    MemoryFake* memory = new MemoryFake;
+    ElfFake* elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
+    map_info->elf.reset(elf);
+
+    // Global variable not set by default.
+    map_info = maps_->Get(kMapGlobalSetToZero);
+    ASSERT_TRUE(map_info != nullptr);
+    memory = new MemoryFake;
+    elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
+    map_info->elf.reset(elf);
+
+    // Global variable set in this map.
+    map_info = maps_->Get(kMapGlobal);
+    ASSERT_TRUE(map_info != nullptr);
+    memory = new MemoryFake;
+    elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
+    map_info->elf.reset(elf);
+  }
+
+  void WriteDescriptor32(uint64_t addr, uint32_t head);
+  void WriteDescriptor64(uint64_t addr, uint64_t head);
+  void WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev, uint32_t dex_file);
+  void WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, uint64_t dex_file);
+  void WriteDex(uint64_t dex_file);
+
+  static constexpr size_t kMapGlobalNonReadableExectable = 3;
+  static constexpr size_t kMapGlobalSetToZero = 4;
+  static constexpr size_t kMapGlobal = 5;
+  static constexpr size_t kMapDexFileEntries = 7;
+  static constexpr size_t kMapDexFiles = 8;
+
+  std::shared_ptr<Memory> process_memory_;
+  MemoryFake* memory_;
+  std::unique_ptr<DexFiles> dex_files_;
+  std::unique_ptr<BufferMaps> maps_;
+};
+
+void DexFilesTest::WriteDescriptor32(uint64_t addr, uint32_t head) {
+  //   void* first_entry_
+  memory_->SetData32(addr + 12, head);
+}
+
+void DexFilesTest::WriteDescriptor64(uint64_t addr, uint64_t head) {
+  //   void* first_entry_
+  memory_->SetData64(addr + 16, head);
+}
+
+void DexFilesTest::WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev,
+                                uint32_t dex_file) {
+  // Format of the 32 bit DEXFileEntry structure:
+  //   uint32_t next
+  memory_->SetData32(entry_addr, next);
+  //   uint32_t prev
+  memory_->SetData32(entry_addr + 4, prev);
+  //   uint32_t dex_file
+  memory_->SetData32(entry_addr + 8, dex_file);
+}
+
+void DexFilesTest::WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev,
+                                uint64_t dex_file) {
+  // Format of the 64 bit DEXFileEntry structure:
+  //   uint64_t next
+  memory_->SetData64(entry_addr, next);
+  //   uint64_t prev
+  memory_->SetData64(entry_addr + 8, prev);
+  //   uint64_t dex_file
+  memory_->SetData64(entry_addr + 16, dex_file);
+}
+
+void DexFilesTest::WriteDex(uint64_t dex_file) {
+  memory_->SetMemory(dex_file, kDexData, sizeof(kDexData) * sizeof(uint32_t));
+}
+
+TEST_F(DexFilesTest, get_method_information_invalid) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFileEntries);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0, &method_name, &method_offset);
+  EXPECT_EQ("nothing", method_name);
+  EXPECT_EQ(0x124U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_32) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0, 0, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_64) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  dex_files_->SetArch(ARCH_ARM64);
+  WriteDescriptor64(0xf800, 0x200000);
+  WriteEntry64(0x200000, 0, 0, 0x301000);
+  WriteDex(0x301000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x301102, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(2U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_not_first_entry_32) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0x200100, 0, 0x100000);
+  WriteEntry32(0x200100, 0, 0x200000, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(4U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_not_first_entry_64) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  dex_files_->SetArch(ARCH_ARM64);
+  WriteDescriptor64(0xf800, 0x200000);
+  WriteEntry64(0x200000, 0x200100, 0, 0x100000);
+  WriteEntry64(0x200100, 0, 0x200000, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300106, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(6U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_cached) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0, 0, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+
+  // Clear all memory and make sure that data is acquired from the cache.
+  memory_->Clear();
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_search_libs) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0x200100, 0, 0x100000);
+  WriteEntry32(0x200100, 0, 0x200000, 0x300000);
+  WriteDex(0x300000);
+
+  // Only search a given named list of libs.
+  std::vector<std::string> libs{"libart.so"};
+  dex_files_.reset(new DexFiles(process_memory_, libs));
+  dex_files_->SetArch(ARCH_ARM);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+  EXPECT_EQ("nothing", method_name);
+  EXPECT_EQ(0x124U, method_offset);
+
+  MapInfo* map_info = maps_->Get(kMapGlobal);
+  map_info->name = "/system/lib/libart.so";
+  dex_files_.reset(new DexFiles(process_memory_, libs));
+  dex_files_->SetArch(ARCH_ARM);
+  // Make sure that clearing out copy of the libs doesn't affect the
+  // DexFiles object.
+  libs.clear();
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(4U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_global_skip_zero_32) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  // First global variable found, but value is zero.
+  WriteDescriptor32(0xc800, 0);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0, 0, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+
+  // Verify that second is ignored when first is set to non-zero
+  dex_files_.reset(new DexFiles(process_memory_));
+  dex_files_->SetArch(ARCH_ARM);
+  method_name = "fail";
+  method_offset = 0x123;
+  WriteDescriptor32(0xc800, 0x100000);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("fail", method_name);
+  EXPECT_EQ(0x123U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  // First global variable found, but value is zero.
+  WriteDescriptor64(0xc800, 0);
+
+  WriteDescriptor64(0xf800, 0x200000);
+  WriteEntry64(0x200000, 0, 0, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->SetArch(ARCH_ARM64);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+
+  // Verify that second is ignored when first is set to non-zero
+  dex_files_.reset(new DexFiles(process_memory_));
+  dex_files_->SetArch(ARCH_ARM64);
+  method_name = "fail";
+  method_offset = 0x123;
+  WriteDescriptor64(0xc800, 0x100000);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("fail", method_name);
+  EXPECT_EQ(0x123U, method_offset);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index a2ae5eb..4240419 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -134,7 +134,7 @@
 
   auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
   ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x1384U, info->pc);
+  EXPECT_EQ(0x1380U, info->pc);
   EXPECT_EQ(0x1540U, info->offset);
 }
 
@@ -149,7 +149,7 @@
 
   auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
   ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x3344U, info->pc);
+  EXPECT_EQ(0x3340U, info->pc);
   EXPECT_EQ(0x3500U, info->offset);
 }
 
@@ -163,7 +163,7 @@
 
   auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
   ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x344U, info->pc);
+  EXPECT_EQ(0x340U, info->pc);
   EXPECT_EQ(0x500U, info->offset);
 
   // Clear the memory so that this will fail if it doesn't read cached data.
@@ -171,7 +171,7 @@
 
   info = this->eh_frame_->GetFdeInfoFromIndex(2);
   ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x344U, info->pc);
+  EXPECT_EQ(0x340U, info->pc);
   EXPECT_EQ(0x500U, info->offset);
 }
 
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 036226d..6e15227 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -52,14 +52,14 @@
 
 TYPED_TEST_P(DwarfOpTest, decode) {
   // Memory error.
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
   EXPECT_EQ(0U, this->op_->LastErrorAddress());
 
   // No error.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
   this->mem_->set_cur_offset(0);
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_NONE, this->op_->LastErrorCode());
   ASSERT_EQ(0x96U, this->op_->cur_op());
   ASSERT_EQ(1U, this->mem_->cur_offset());
@@ -67,14 +67,14 @@
 
 TYPED_TEST_P(DwarfOpTest, eval) {
   // Memory error.
-  ASSERT_FALSE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Eval(0, 2));
   ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
   EXPECT_EQ(0U, this->op_->LastErrorAddress());
 
   // Register set.
   // Do this first, to verify that subsequent calls reset the value.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x50});
-  ASSERT_TRUE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 1));
   ASSERT_TRUE(this->op_->is_register());
   ASSERT_EQ(1U, this->mem_->cur_offset());
   ASSERT_EQ(1U, this->op_->StackSize());
@@ -85,7 +85,7 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Eval(0, 8, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 8));
   ASSERT_EQ(DWARF_ERROR_NONE, this->op_->LastErrorCode());
   ASSERT_FALSE(this->op_->is_register());
   ASSERT_EQ(8U, this->mem_->cur_offset());
@@ -97,7 +97,7 @@
 
   // Infinite loop.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x2f, 0xfd, 0xff});
-  ASSERT_FALSE(this->op_->Eval(0, 4, DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Eval(0, 4));
   ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->LastErrorCode());
   ASSERT_FALSE(this->op_->is_register());
   ASSERT_EQ(0U, this->op_->StackSize());
@@ -112,29 +112,7 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   for (size_t i = 0; i < opcode_buffer.size(); i++) {
-    ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
-    ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
-  }
-}
-
-TYPED_TEST_P(DwarfOpTest, illegal_in_version3) {
-  std::vector<uint8_t> opcode_buffer = {0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d};
-  this->op_memory_.SetMemory(0, opcode_buffer);
-
-  for (size_t i = 0; i < opcode_buffer.size(); i++) {
-    ASSERT_FALSE(this->op_->Decode(2));
-    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
-    ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
-  }
-}
-
-TYPED_TEST_P(DwarfOpTest, illegal_in_version4) {
-  std::vector<uint8_t> opcode_buffer = {0x9e, 0x9f};
-  this->op_memory_.SetMemory(0, opcode_buffer);
-
-  for (size_t i = 0; i < opcode_buffer.size(); i++) {
-    ASSERT_FALSE(this->op_->Decode(3));
+    ASSERT_FALSE(this->op_->Decode());
     ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
     ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
   }
@@ -174,12 +152,12 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // Push the stack values.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
+  ASSERT_TRUE(this->op_->Decode());
+  ASSERT_TRUE(this->op_->Decode());
 
   while (this->mem_->cur_offset() < opcode_buffer.size()) {
-    ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+    ASSERT_FALSE(this->op_->Decode());
     ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->LastErrorCode());
   }
 }
@@ -194,7 +172,7 @@
   }
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x03, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -217,17 +195,17 @@
   TypeParam value = 0x12345678;
   this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x06, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(value, this->op_->StackAt(0));
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
   ASSERT_EQ(0x12345678U, this->op_->LastErrorAddress());
 }
@@ -237,14 +215,14 @@
   TypeParam value = 0x12345678;
   this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   // Read all byte sizes up to the sizeof the type.
   for (size_t i = 1; i < sizeof(TypeParam); i++) {
     this->op_memory_.SetMemory(
         0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, static_cast<uint8_t>(i)});
-    ASSERT_TRUE(this->op_->Eval(0, 5, DWARF_VERSION_MAX)) << "Failed at size " << i;
+    ASSERT_TRUE(this->op_->Eval(0, 5)) << "Failed at size " << i;
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed at size " << i;
     ASSERT_EQ(0x94, this->op_->cur_op()) << "Failed at size " << i;
     TypeParam expected_value = 0;
@@ -254,17 +232,17 @@
 
   // Zero byte read.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, 0x00});
-  ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Eval(0, 5));
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 
   // Read too many bytes.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, sizeof(TypeParam) + 1});
-  ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Eval(0, 5));
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 
   // Force bad memory read.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x40, 0x94, 0x01});
-  ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Eval(0, 5));
   ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
   EXPECT_EQ(0x4010U, this->op_->LastErrorAddress());
 }
@@ -284,40 +262,40 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // const1u
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x08, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x12U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x08, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0xffU, this->op_->StackAt(0));
 
   // const2u
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0a, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x1245U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0a, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(0xff00U, this->op_->StackAt(0));
 
   // const4u
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0c, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0c, this->op_->cur_op());
   ASSERT_EQ(6U, this->op_->StackSize());
   ASSERT_EQ(0xff010203U, this->op_->StackAt(0));
 
   // const8u
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0e, this->op_->cur_op());
   ASSERT_EQ(7U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -326,7 +304,7 @@
     ASSERT_EQ(0x0102030405060708ULL, this->op_->StackAt(0));
   }
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0e, this->op_->cur_op());
   ASSERT_EQ(8U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -351,40 +329,40 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // const1s
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x09, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x12U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x09, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
 
   // const2s
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0b, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x3221U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0b, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-248), this->op_->StackAt(0));
 
   // const4s
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0d, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   ASSERT_EQ(0x12233445U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0d, this->op_->cur_op());
   ASSERT_EQ(6U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-16580095), this->op_->StackAt(0));
 
   // const8s
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0f, this->op_->cur_op());
   ASSERT_EQ(7U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -393,7 +371,7 @@
     ASSERT_EQ(0x1223344556677889ULL, this->op_->StackAt(0));
   }
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0f, this->op_->cur_op());
   ASSERT_EQ(8U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -414,28 +392,28 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // Single byte ULEB128
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x22U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x7fU, this->op_->StackAt(0));
 
   // Multi byte ULEB128
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x1122U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(0x3a22U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -444,7 +422,7 @@
     ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
   }
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(6U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -480,28 +458,28 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // Single byte SLEB128
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x22U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
 
   // Multi byte SLEB128
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x1122U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-1502), this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -510,7 +488,7 @@
     ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
   }
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(6U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -531,21 +509,21 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x12, this->op_->cur_op());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x12, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x15U, this->op_->StackAt(0));
   ASSERT_EQ(0x15U, this->op_->StackAt(1));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x12, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(0x23U, this->op_->StackAt(0));
@@ -565,21 +543,21 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x13, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x10U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x13, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x13, this->op_->cur_op());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
@@ -597,24 +575,24 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x14, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x1aU, this->op_->StackAt(0));
   ASSERT_EQ(0xedU, this->op_->StackAt(1));
   ASSERT_EQ(0x1aU, this->op_->StackAt(2));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x14, this->op_->cur_op());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
@@ -632,14 +610,14 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x15, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(0xedU, this->op_->StackAt(0));
@@ -647,7 +625,7 @@
   ASSERT_EQ(0xedU, this->op_->StackAt(2));
   ASSERT_EQ(0x1aU, this->op_->StackAt(3));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x15, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   ASSERT_EQ(0x1aU, this->op_->StackAt(0));
@@ -656,7 +634,7 @@
   ASSERT_EQ(0xedU, this->op_->StackAt(3));
   ASSERT_EQ(0x1aU, this->op_->StackAt(4));
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x15, this->op_->cur_op());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
@@ -672,23 +650,23 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0xabU, this->op_->StackAt(0));
   ASSERT_EQ(0x26U, this->op_->StackAt(1));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x16, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x26U, this->op_->StackAt(0));
   ASSERT_EQ(0xabU, this->op_->StackAt(1));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x16, this->op_->cur_op());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
@@ -706,28 +684,28 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x30U, this->op_->StackAt(0));
   ASSERT_EQ(0x20U, this->op_->StackAt(1));
   ASSERT_EQ(0x10U, this->op_->StackAt(2));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x17, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x20U, this->op_->StackAt(0));
@@ -756,30 +734,30 @@
   opcode_buffer.push_back(0x19);
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x10U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x19, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x10U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x19, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x1U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x19, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -808,56 +786,56 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   // Two positive values.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1b, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x10U, this->op_->StackAt(0));
 
   // Two negative values.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1b, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x04U, this->op_->StackAt(0));
 
   // One negative value, one positive value.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(4U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1b, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-4), this->op_->StackAt(0));
 
   // Divide by zero.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(4U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(5U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
@@ -874,19 +852,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1a, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x40U, this->op_->StackAt(0));
@@ -905,19 +883,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1c, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x44U, this->op_->StackAt(0));
@@ -938,29 +916,29 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1d, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x03U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
@@ -977,19 +955,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1e, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x120U, this->op_->StackAt(0));
@@ -1006,21 +984,21 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1f, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-72), this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1f, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x01U, this->op_->StackAt(0));
@@ -1037,21 +1015,21 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x20, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-5), this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x20, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x03U, this->op_->StackAt(0));
@@ -1070,19 +1048,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x21, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0xfcU, this->op_->StackAt(0));
@@ -1101,19 +1079,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x22, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x1f1U, this->op_->StackAt(0));
@@ -1128,13 +1106,13 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x23, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x28d0U, this->op_->StackAt(0));
@@ -1153,19 +1131,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x24, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x338U, this->op_->StackAt(0));
@@ -1184,19 +1162,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x25, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -1219,19 +1197,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x26, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-2), this->op_->StackAt(0));
@@ -1250,19 +1228,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x27, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x50U, this->op_->StackAt(0));
@@ -1283,48 +1261,48 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   // Push on a non-zero value with a positive branch.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
   uint64_t offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x28, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset + 0x102, this->mem_->cur_offset());
 
   // Push on a zero value with a positive branch.
   this->mem_->set_cur_offset(offset);
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
   offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x28, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset - 5, this->mem_->cur_offset());
 
   // Push on a non-zero value with a negative branch.
   this->mem_->set_cur_offset(offset);
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
   offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x28, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset - 4, this->mem_->cur_offset());
 
   // Push on a zero value with a negative branch.
   this->mem_->set_cur_offset(offset);
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
   offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x28, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset + 16, this->mem_->cur_offset());
@@ -1344,11 +1322,11 @@
     opcode_buffer[3] = opcode;
     this->op_memory_.SetMemory(0, opcode_buffer);
 
-    ASSERT_FALSE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+    ASSERT_FALSE(this->op_->Eval(0, 1));
     ASSERT_EQ(opcode, this->op_->cur_op());
     ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-    ASSERT_FALSE(this->op_->Eval(1, 4, DWARF_VERSION_MAX));
+    ASSERT_FALSE(this->op_->Eval(1, 4));
     ASSERT_EQ(opcode, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize());
     ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
@@ -1387,7 +1365,7 @@
     opcode_buffer[14] = expected[i];
     this->op_memory_.SetMemory(0, opcode_buffer);
 
-    ASSERT_TRUE(this->op_->Eval(0, 15, DWARF_VERSION_MAX))
+    ASSERT_TRUE(this->op_->Eval(0, 15))
         << "Op: 0x" << std::hex << static_cast<uint32_t>(expected[i]) << " failed";
 
     ASSERT_EQ(3U, this->op_->StackSize());
@@ -1407,14 +1385,14 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   uint64_t offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x2f, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset + 0x2010, this->mem_->cur_offset());
 
   this->mem_->set_cur_offset(offset);
   offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x2f, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset - 3, this->mem_->cur_offset());
@@ -1431,7 +1409,7 @@
 
   for (size_t i = 0; i < opcode_buffer.size(); i++) {
     uint32_t op = opcode_buffer[i];
-    ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+    ASSERT_TRUE(this->op_->Eval(i, i + 1)) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op - 0x30U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
@@ -1449,7 +1427,7 @@
 
   for (size_t i = 0; i < opcode_buffer.size(); i++) {
     uint32_t op = opcode_buffer[i];
-    ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+    ASSERT_TRUE(this->op_->Eval(i, i + 1)) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op, this->op_->cur_op());
     ASSERT_TRUE(this->op_->is_register()) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
@@ -1463,13 +1441,13 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 2));
   ASSERT_EQ(0x90, this->op_->cur_op());
   ASSERT_TRUE(this->op_->is_register());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x02U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Eval(2, 5, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(2, 5));
   ASSERT_EQ(0x90, this->op_->cur_op());
   ASSERT_TRUE(this->op_->is_register());
   ASSERT_EQ(1U, this->op_->StackSize());
@@ -1494,21 +1472,20 @@
   for (size_t i = 0; i < 32; i++) {
     regs[i] = i + 10;
   }
-  this->op_->set_regs(&regs);
+  RegsInfo<TypeParam> regs_info(&regs);
+  this->op_->set_regs_info(&regs_info);
 
   uint64_t offset = 0;
   for (uint32_t op = 0x70; op <= 0x8f; op++) {
     // Positive value added to register.
-    ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
-                                                                        << std::hex << op;
+    ASSERT_TRUE(this->op_->Eval(offset, offset + 2)) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op - 0x70 + 10 + 0x12, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
     offset += 2;
 
     // Negative value added to register.
-    ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
-                                                                        << std::hex << op;
+    ASSERT_TRUE(this->op_->Eval(offset, offset + 2)) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op - 0x70 + 10 - 2, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
@@ -1526,16 +1503,17 @@
   for (size_t i = 0; i < 16; i++) {
     regs[i] = i + 10;
   }
-  this->op_->set_regs(&regs);
+  RegsInfo<TypeParam> regs_info(&regs);
+  this->op_->set_regs_info(&regs_info);
 
   // Should pass since this references the last regsister.
-  ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 2));
   ASSERT_EQ(0x7fU, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x2bU, this->op_->StackAt(0));
 
   // Should fail since this references a non-existent register.
-  ASSERT_FALSE(this->op_->Eval(2, 4, DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Eval(2, 4));
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
@@ -1551,38 +1529,55 @@
   RegsImplFake<TypeParam> regs(10, 10);
   regs[5] = 0x45;
   regs[6] = 0x190;
-  this->op_->set_regs(&regs);
+  RegsInfo<TypeParam> regs_info(&regs);
+  this->op_->set_regs_info(&regs_info);
 
-  ASSERT_TRUE(this->op_->Eval(0, 3, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 3));
   ASSERT_EQ(0x92, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x65U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Eval(3, 7, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(3, 7));
   ASSERT_EQ(0x92, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x90U, this->op_->StackAt(0));
 
-  ASSERT_FALSE(this->op_->Eval(7, 12, DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Eval(7, 12));
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_nop) {
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x96, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, illegal_in_version3,
-                           illegal_in_version4, not_implemented, op_addr, op_deref, op_deref_size,
-                           const_unsigned, const_signed, const_uleb, const_sleb, op_dup, op_drop,
-                           op_over, op_pick, op_swap, op_rot, op_abs, op_and, op_div, op_minus,
-                           op_mod, op_mul, op_neg, op_not, op_or, op_plus, op_plus_uconst, op_shl,
-                           op_shr, op_shra, op_xor, op_bra, compare_opcode_stack_error,
-                           compare_opcodes, op_skip, op_lit, op_reg, op_regx, op_breg,
-                           op_breg_invalid_register, op_bregx, op_nop);
+TYPED_TEST_P(DwarfOpTest, is_dex_pc) {
+  // Special sequence that indicates this is a dex pc.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '1', 0x13});
+
+  ASSERT_TRUE(this->op_->Eval(0, 6));
+  EXPECT_TRUE(this->op_->dex_pc_set());
+
+  // Try without the last op.
+  ASSERT_TRUE(this->op_->Eval(0, 5));
+  EXPECT_FALSE(this->op_->dex_pc_set());
+
+  // Change the constant.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '2', 0x13});
+  ASSERT_TRUE(this->op_->Eval(0, 6));
+  EXPECT_FALSE(this->op_->dex_pc_set());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, not_implemented, op_addr,
+                           op_deref, op_deref_size, const_unsigned, const_signed, const_uleb,
+                           const_sleb, op_dup, op_drop, op_over, op_pick, op_swap, op_rot, op_abs,
+                           op_and, op_div, op_minus, op_mod, op_mul, op_neg, op_not, op_or, op_plus,
+                           op_plus_uconst, op_shl, op_shr, op_shra, op_xor, op_bra,
+                           compare_opcode_stack_error, compare_opcodes, op_skip, op_lit, op_reg,
+                           op_regx, op_breg, op_breg_invalid_register, op_bregx, op_nop, is_dex_pc);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 7e10935..37305b2 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -99,7 +99,7 @@
   regs.set_sp(0x2000);
   regs[5] = 0x20;
   regs[9] = 0x3000;
-  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
@@ -116,7 +116,7 @@
   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}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->LastErrorCode());
@@ -134,7 +134,7 @@
   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}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
   bool finished;
   ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_FALSE(finished);
@@ -152,7 +152,7 @@
   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}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
   bool finished;
   ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   ASSERT_FALSE(finished);
@@ -170,7 +170,7 @@
   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}};
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
   bool finished;
   ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->LastErrorCode());
@@ -322,7 +322,8 @@
   regs[0] = 0x10;
   regs[8] = 0x20;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  loc_regs[0x20444558] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 1}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x8, 0x5008}};
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '1', 0x13, 0x08, 0x11});
   bool finished;
   ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(0x10U, regs[0]);
@@ -462,7 +463,7 @@
   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}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
   bool finished;
   ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_FALSE(finished);
@@ -480,7 +481,7 @@
   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}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
   bool finished;
   ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_FALSE(finished);
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 0086c9e..89331ea 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -60,6 +60,7 @@
 
   void VerifyWithinSameMap(bool cache_enabled);
   void VerifySameMap(bool cache_enabled);
+  void VerifyWithinSameMapNeverReadAtZero(bool cache_enabled);
 
   static std::shared_ptr<Memory> memory_;
 };
@@ -198,4 +199,66 @@
   VerifyWithinSameMap(true);
 }
 
+// Verify that when reading from multiple non-zero offsets in the same map
+// that when cached, all of the elf objects are the same.
+void ElfCacheTest::VerifyWithinSameMapNeverReadAtZero(bool cache_enabled) {
+  if (!cache_enabled) {
+    Elf::SetCachingEnabled(false);
+  }
+
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  WriteElfFile(0, &tf, EM_ARM);
+  lseek(tf.fd, 0x500, SEEK_SET);
+  uint8_t value = 0;
+  write(tf.fd, &value, 1);
+  close(tf.fd);
+
+  uint64_t start = 0x1000;
+  uint64_t end = 0x20000;
+  // Multiple info sections at different offsets will have non-zero elf offsets.
+  MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
+  MapInfo info400_1(start, end, 0x400, 0x5, tf.path);
+  MapInfo info400_2(start, end, 0x400, 0x5, tf.path);
+
+  Elf* elf300_1 = info300_1.GetElf(memory_, true);
+  ASSERT_TRUE(elf300_1->valid());
+  EXPECT_EQ(ARCH_ARM, elf300_1->arch());
+  Elf* elf300_2 = info300_2.GetElf(memory_, true);
+  ASSERT_TRUE(elf300_2->valid());
+  EXPECT_EQ(ARCH_ARM, elf300_2->arch());
+  EXPECT_EQ(0x300U, info300_1.elf_offset);
+  EXPECT_EQ(0x300U, info300_2.elf_offset);
+  if (cache_enabled) {
+    EXPECT_EQ(elf300_1, elf300_2);
+  } else {
+    EXPECT_NE(elf300_1, elf300_2);
+  }
+
+  Elf* elf400_1 = info400_1.GetElf(memory_, true);
+  ASSERT_TRUE(elf400_1->valid());
+  EXPECT_EQ(ARCH_ARM, elf400_1->arch());
+  Elf* elf400_2 = info400_2.GetElf(memory_, true);
+  ASSERT_TRUE(elf400_2->valid());
+  EXPECT_EQ(ARCH_ARM, elf400_2->arch());
+  EXPECT_EQ(0x400U, info400_1.elf_offset);
+  EXPECT_EQ(0x400U, info400_2.elf_offset);
+  if (cache_enabled) {
+    EXPECT_EQ(elf400_1, elf400_2);
+    EXPECT_EQ(elf300_1, elf400_1);
+  } else {
+    EXPECT_NE(elf400_1, elf400_2);
+    EXPECT_NE(elf300_1, elf400_1);
+  }
+}
+
+TEST_F(ElfCacheTest, no_caching_valid_elf_offset_non_zero_never_read_at_zero) {
+  VerifyWithinSameMapNeverReadAtZero(false);
+}
+
+TEST_F(ElfCacheTest, caching_valid_elf_offset_non_zero_never_read_at_zero) {
+  VerifyWithinSameMapNeverReadAtZero(true);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 31d6a63..70a52ad 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -20,10 +20,10 @@
 
 #include <vector>
 
+#include <unwindstack/MachineArm.h>
 #include <unwindstack/RegsArm.h>
 
 #include "ElfInterfaceArm.h"
-#include "MachineArm.h"
 
 #include "ElfFake.h"
 #include "MemoryFake.h"
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 042c5fb..bf97e30 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -63,15 +63,28 @@
   template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
   void ManyPhdrs();
 
-  template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+  enum SonameTestEnum : uint8_t {
+    SONAME_NORMAL,
+    SONAME_DTNULL_AFTER,
+    SONAME_DTSIZE_SMALL,
+    SONAME_MISSING_MAP,
+  };
+
+  template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
+  void SonameInit(SonameTestEnum test_type = SONAME_NORMAL);
+
+  template <typename ElfInterfaceType>
   void Soname();
 
-  template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+  template <typename ElfInterfaceType>
   void SonameAfterDtNull();
 
-  template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+  template <typename ElfInterfaceType>
   void SonameSize();
 
+  template <typename ElfInterfaceType>
+  void SonameMissingMap();
+
   template <typename ElfType>
   void InitHeadersEhFrameTest();
 
@@ -465,17 +478,29 @@
   ASSERT_EQ(2U, elf_arm.total_entries());
 }
 
-template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
-void ElfInterfaceTest::Soname() {
-  std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
-
+template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
+void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) {
   Ehdr ehdr;
   memset(&ehdr, 0, sizeof(ehdr));
+  ehdr.e_shoff = 0x200;
+  ehdr.e_shnum = 2;
+  ehdr.e_shentsize = sizeof(Shdr);
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 1;
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
+  Shdr shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  if (test_type == SONAME_MISSING_MAP) {
+    shdr.sh_addr = 0x20100;
+  } else {
+    shdr.sh_addr = 0x10100;
+  }
+  shdr.sh_offset = 0x10000;
+  memory_.SetMemory(0x200 + sizeof(shdr), &shdr, sizeof(shdr));
+
   Phdr phdr;
   memset(&phdr, 0, sizeof(phdr));
   phdr.p_type = PT_DYNAMIC;
@@ -487,15 +512,25 @@
   Dyn dyn;
 
   dyn.d_tag = DT_STRTAB;
-  dyn.d_un.d_ptr = 0x10000;
+  dyn.d_un.d_ptr = 0x10100;
   memory_.SetMemory(offset, &dyn, sizeof(dyn));
   offset += sizeof(dyn);
 
   dyn.d_tag = DT_STRSZ;
-  dyn.d_un.d_val = 0x1000;
+  if (test_type == SONAME_DTSIZE_SMALL) {
+    dyn.d_un.d_val = 0x10;
+  } else {
+    dyn.d_un.d_val = 0x1000;
+  }
   memory_.SetMemory(offset, &dyn, sizeof(dyn));
   offset += sizeof(dyn);
 
+  if (test_type == SONAME_DTNULL_AFTER) {
+    dyn.d_tag = DT_NULL;
+    memory_.SetMemory(offset, &dyn, sizeof(dyn));
+    offset += sizeof(dyn);
+  }
+
   dyn.d_tag = DT_SONAME;
   dyn.d_un.d_val = 0x10;
   memory_.SetMemory(offset, &dyn, sizeof(dyn));
@@ -505,6 +540,11 @@
   memory_.SetMemory(offset, &dyn, sizeof(dyn));
 
   SetStringMemory(0x10010, "fake_soname.so");
+}
+
+template <typename ElfInterfaceType>
+void ElfInterfaceTest::Soname() {
+  std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
@@ -516,55 +556,19 @@
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname) {
-  Soname<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+  SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>();
+  Soname<ElfInterface32>();
 }
 
 TEST_F(ElfInterfaceTest, elf64_soname) {
-  Soname<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+  SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>();
+  Soname<ElfInterface64>();
 }
 
-template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+template <typename ElfInterfaceType>
 void ElfInterfaceTest::SonameAfterDtNull() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
-  ehdr.e_phoff = 0x100;
-  ehdr.e_phnum = 1;
-  ehdr.e_phentsize = sizeof(Phdr);
-  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
-
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
-  phdr.p_type = PT_DYNAMIC;
-  phdr.p_offset = 0x2000;
-  phdr.p_memsz = sizeof(Dyn) * 3;
-  memory_.SetMemory(0x100, &phdr, sizeof(phdr));
-
-  Dyn dyn;
-  uint64_t offset = 0x2000;
-
-  dyn.d_tag = DT_STRTAB;
-  dyn.d_un.d_ptr = 0x10000;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_STRSZ;
-  dyn.d_un.d_val = 0x1000;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_NULL;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_SONAME;
-  dyn.d_un.d_val = 0x10;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  SetStringMemory(0x10010, "fake_soname.so");
-
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
   EXPECT_EQ(0U, load_bias);
@@ -574,54 +578,19 @@
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
-  SonameAfterDtNull<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+  SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTNULL_AFTER);
+  SonameAfterDtNull<ElfInterface32>();
 }
 
 TEST_F(ElfInterfaceTest, elf64_soname_after_dt_null) {
-  SonameAfterDtNull<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+  SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTNULL_AFTER);
+  SonameAfterDtNull<ElfInterface64>();
 }
 
-template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+template <typename ElfInterfaceType>
 void ElfInterfaceTest::SonameSize() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
-  ehdr.e_phoff = 0x100;
-  ehdr.e_phnum = 1;
-  ehdr.e_phentsize = sizeof(Phdr);
-  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
-
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
-  phdr.p_type = PT_DYNAMIC;
-  phdr.p_offset = 0x2000;
-  phdr.p_memsz = sizeof(Dyn);
-  memory_.SetMemory(0x100, &phdr, sizeof(phdr));
-
-  Dyn dyn;
-  uint64_t offset = 0x2000;
-
-  dyn.d_tag = DT_STRTAB;
-  dyn.d_un.d_ptr = 0x10000;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_STRSZ;
-  dyn.d_un.d_val = 0x10;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_SONAME;
-  dyn.d_un.d_val = 0x10;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_NULL;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-
-  SetStringMemory(0x10010, "fake_soname.so");
-
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
   EXPECT_EQ(0U, load_bias);
@@ -631,11 +600,37 @@
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname_size) {
-  SonameSize<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+  SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTSIZE_SMALL);
+  SonameSize<ElfInterface32>();
 }
 
 TEST_F(ElfInterfaceTest, elf64_soname_size) {
-  SonameSize<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+  SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTSIZE_SMALL);
+  SonameSize<ElfInterface64>();
+}
+
+// Verify that there is no map from STRTAB in the dynamic section to a
+// STRTAB entry in the section headers.
+template <typename ElfInterfaceType>
+void ElfInterfaceTest::SonameMissingMap() {
+  std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
+
+  std::string name;
+  ASSERT_FALSE(elf->GetSoname(&name));
+}
+
+TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {
+  SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_MISSING_MAP);
+  SonameMissingMap<ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, elf64_soname_missing_map) {
+  SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_MISSING_MAP);
+  SonameMissingMap<ElfInterface64>();
 }
 
 template <typename ElfType>
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
index 37628f8..c1c45f8 100644
--- a/libunwindstack/tests/JitDebugTest.cpp
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -56,30 +56,30 @@
 
     MapInfo* map_info = maps_->Get(3);
     ASSERT_TRUE(map_info != nullptr);
-    elf_memories_.push_back(new MemoryFake);
-    ElfFake* elf = new ElfFake(elf_memories_.back());
+    MemoryFake* memory = new MemoryFake;
+    ElfFake* elf = new ElfFake(memory);
     elf->FakeSetValid(true);
-    ElfInterfaceFake* interface = new ElfInterfaceFake(elf_memories_.back());
+    ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
     elf->FakeSetInterface(interface);
     interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
     map_info->elf.reset(elf);
 
     map_info = maps_->Get(5);
     ASSERT_TRUE(map_info != nullptr);
-    elf_memories_.push_back(new MemoryFake);
-    elf = new ElfFake(elf_memories_.back());
+    memory = new MemoryFake;
+    elf = new ElfFake(memory);
     elf->FakeSetValid(true);
-    interface = new ElfInterfaceFake(elf_memories_.back());
+    interface = new ElfInterfaceFake(memory);
     elf->FakeSetInterface(interface);
     interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
     map_info->elf.reset(elf);
 
     map_info = maps_->Get(6);
     ASSERT_TRUE(map_info != nullptr);
-    elf_memories_.push_back(new MemoryFake);
-    elf = new ElfFake(elf_memories_.back());
+    memory = new MemoryFake;
+    elf = new ElfFake(memory);
     elf->FakeSetValid(true);
-    interface = new ElfInterfaceFake(elf_memories_.back());
+    interface = new ElfInterfaceFake(memory);
     elf->FakeSetInterface(interface);
     interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
     map_info->elf.reset(elf);
@@ -171,7 +171,6 @@
 
   std::shared_ptr<Memory> process_memory_;
   MemoryFake* memory_;
-  std::vector<MemoryFake*> elf_memories_;
   std::unique_ptr<JitDebug> jit_debug_;
   std::unique_ptr<BufferMaps> maps_;
 };
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index 8f7d913..ab23194 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -47,7 +47,7 @@
 
   bool Is32Bit() { return false; }
 
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; }
+  uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 2; }
 
   bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
 
@@ -56,6 +56,7 @@
   void FakeSetArch(ArchEnum arch) { fake_arch_ = arch; }
   void FakeSetPc(uint64_t pc) { fake_pc_ = pc; }
   void FakeSetSp(uint64_t sp) { fake_sp_ = sp; }
+  void FakeSetDexPc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
   void FakeSetReturnAddress(uint64_t return_address) { fake_return_address_ = return_address; }
   void FakeSetReturnAddressValid(bool valid) { fake_return_address_valid_ = valid; }
 
@@ -76,7 +77,7 @@
 
   ArchEnum Arch() override { return ARCH_UNKNOWN; }
 
-  uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+  uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 0; }
   void SetFromRaw() override {}
   bool SetPcFromReturnAddress(Memory*) override { return false; }
   bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index 8b5b31f..9a27dbd 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -24,20 +24,19 @@
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/ElfInterface.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineMips.h>
+#include <unwindstack/MachineMips64.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MachineX86_64.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/RegsArm.h>
 #include <unwindstack/RegsArm64.h>
-#include <unwindstack/RegsX86.h>
-#include <unwindstack/RegsX86_64.h>
 #include <unwindstack/RegsMips.h>
 #include <unwindstack/RegsMips64.h>
-
-#include "MachineArm.h"
-#include "MachineArm64.h"
-#include "MachineX86.h"
-#include "MachineX86_64.h"
-#include "MachineMips.h"
-#include "MachineMips64.h"
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
index ef9e61c..ecd4051 100644
--- a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -19,19 +19,18 @@
 #include <gtest/gtest.h>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineMips.h>
+#include <unwindstack/MachineMips64.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MachineX86_64.h>
 #include <unwindstack/RegsArm.h>
 #include <unwindstack/RegsArm64.h>
-#include <unwindstack/RegsX86.h>
-#include <unwindstack/RegsX86_64.h>
 #include <unwindstack/RegsMips.h>
 #include <unwindstack/RegsMips64.h>
-
-#include "MachineArm.h"
-#include "MachineArm64.h"
-#include "MachineX86.h"
-#include "MachineX86_64.h"
-#include "MachineMips.h"
-#include "MachineMips64.h"
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
 
 #include "MemoryFake.h"
 
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 7c06373..8b2f6c8 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -96,48 +96,48 @@
 
 TEST_F(RegsTest, rel_pc) {
   RegsArm64 arm64;
-  ASSERT_EQ(0xcU, arm64.GetAdjustedPc(0x10, elf_.get()));
-  ASSERT_EQ(0x0U, arm64.GetAdjustedPc(0x4, elf_.get()));
-  ASSERT_EQ(0x3U, arm64.GetAdjustedPc(0x3, elf_.get()));
-  ASSERT_EQ(0x2U, arm64.GetAdjustedPc(0x2, elf_.get()));
-  ASSERT_EQ(0x1U, arm64.GetAdjustedPc(0x1, elf_.get()));
-  ASSERT_EQ(0x0U, arm64.GetAdjustedPc(0x0, elf_.get()));
+  ASSERT_EQ(4U, arm64.GetPcAdjustment(0x10, elf_.get()));
+  ASSERT_EQ(4U, arm64.GetPcAdjustment(0x4, elf_.get()));
+  ASSERT_EQ(0U, arm64.GetPcAdjustment(0x3, elf_.get()));
+  ASSERT_EQ(0U, arm64.GetPcAdjustment(0x2, elf_.get()));
+  ASSERT_EQ(0U, arm64.GetPcAdjustment(0x1, elf_.get()));
+  ASSERT_EQ(0U, arm64.GetPcAdjustment(0x0, elf_.get()));
 
   RegsX86 x86;
-  ASSERT_EQ(0xffU,  x86.GetAdjustedPc(0x100, elf_.get()));
-  ASSERT_EQ(0x1U,  x86.GetAdjustedPc(0x2, elf_.get()));
-  ASSERT_EQ(0x0U,  x86.GetAdjustedPc(0x1, elf_.get()));
-  ASSERT_EQ(0x0U,  x86.GetAdjustedPc(0x0, elf_.get()));
+  ASSERT_EQ(1U, x86.GetPcAdjustment(0x100, elf_.get()));
+  ASSERT_EQ(1U, x86.GetPcAdjustment(0x2, elf_.get()));
+  ASSERT_EQ(1U, x86.GetPcAdjustment(0x1, elf_.get()));
+  ASSERT_EQ(0U, x86.GetPcAdjustment(0x0, elf_.get()));
 
   RegsX86_64 x86_64;
-  ASSERT_EQ(0xffU,  x86_64.GetAdjustedPc(0x100, elf_.get()));
-  ASSERT_EQ(0x1U,  x86_64.GetAdjustedPc(0x2, elf_.get()));
-  ASSERT_EQ(0x0U,  x86_64.GetAdjustedPc(0x1, elf_.get()));
-  ASSERT_EQ(0x0U,  x86_64.GetAdjustedPc(0x0, elf_.get()));
+  ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x100, elf_.get()));
+  ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x2, elf_.get()));
+  ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x1, elf_.get()));
+  ASSERT_EQ(0U, x86_64.GetPcAdjustment(0x0, elf_.get()));
 
   RegsMips mips;
-  ASSERT_EQ(0x8U, mips.GetAdjustedPc(0x10, elf_.get()));
-  ASSERT_EQ(0x0U, mips.GetAdjustedPc(0x8, elf_.get()));
-  ASSERT_EQ(0x7U, mips.GetAdjustedPc(0x7, elf_.get()));
-  ASSERT_EQ(0x6U, mips.GetAdjustedPc(0x6, elf_.get()));
-  ASSERT_EQ(0x5U, mips.GetAdjustedPc(0x5, elf_.get()));
-  ASSERT_EQ(0x4U, mips.GetAdjustedPc(0x4, elf_.get()));
-  ASSERT_EQ(0x3U, mips.GetAdjustedPc(0x3, elf_.get()));
-  ASSERT_EQ(0x2U, mips.GetAdjustedPc(0x2, elf_.get()));
-  ASSERT_EQ(0x1U, mips.GetAdjustedPc(0x1, elf_.get()));
-  ASSERT_EQ(0x0U, mips.GetAdjustedPc(0x0, elf_.get()));
+  ASSERT_EQ(8U, mips.GetPcAdjustment(0x10, elf_.get()));
+  ASSERT_EQ(8U, mips.GetPcAdjustment(0x8, elf_.get()));
+  ASSERT_EQ(0U, mips.GetPcAdjustment(0x7, elf_.get()));
+  ASSERT_EQ(0U, mips.GetPcAdjustment(0x6, elf_.get()));
+  ASSERT_EQ(0U, mips.GetPcAdjustment(0x5, elf_.get()));
+  ASSERT_EQ(0U, mips.GetPcAdjustment(0x4, elf_.get()));
+  ASSERT_EQ(0U, mips.GetPcAdjustment(0x3, elf_.get()));
+  ASSERT_EQ(0U, mips.GetPcAdjustment(0x2, elf_.get()));
+  ASSERT_EQ(0U, mips.GetPcAdjustment(0x1, elf_.get()));
+  ASSERT_EQ(0U, mips.GetPcAdjustment(0x0, elf_.get()));
 
   RegsMips64 mips64;
-  ASSERT_EQ(0x8U, mips64.GetAdjustedPc(0x10, elf_.get()));
-  ASSERT_EQ(0x0U, mips64.GetAdjustedPc(0x8, elf_.get()));
-  ASSERT_EQ(0x7U, mips64.GetAdjustedPc(0x7, elf_.get()));
-  ASSERT_EQ(0x6U, mips64.GetAdjustedPc(0x6, elf_.get()));
-  ASSERT_EQ(0x5U, mips64.GetAdjustedPc(0x5, elf_.get()));
-  ASSERT_EQ(0x4U, mips64.GetAdjustedPc(0x4, elf_.get()));
-  ASSERT_EQ(0x3U, mips64.GetAdjustedPc(0x3, elf_.get()));
-  ASSERT_EQ(0x2U, mips64.GetAdjustedPc(0x2, elf_.get()));
-  ASSERT_EQ(0x1U, mips64.GetAdjustedPc(0x1, elf_.get()));
-  ASSERT_EQ(0x0U, mips64.GetAdjustedPc(0x0, elf_.get()));
+  ASSERT_EQ(8U, mips64.GetPcAdjustment(0x10, elf_.get()));
+  ASSERT_EQ(8U, mips64.GetPcAdjustment(0x8, elf_.get()));
+  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x7, elf_.get()));
+  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x6, elf_.get()));
+  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x5, elf_.get()));
+  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x4, elf_.get()));
+  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x3, elf_.get()));
+  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x2, elf_.get()));
+  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x1, elf_.get()));
+  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x0, elf_.get()));
 }
 
 TEST_F(RegsTest, rel_pc_arm) {
@@ -145,34 +145,34 @@
 
   // Check fence posts.
   elf_->FakeSetLoadBias(0);
-  ASSERT_EQ(3U,  arm.GetAdjustedPc(0x5, elf_.get()));
-  ASSERT_EQ(4U,  arm.GetAdjustedPc(0x4, elf_.get()));
-  ASSERT_EQ(3U,  arm.GetAdjustedPc(0x3, elf_.get()));
-  ASSERT_EQ(2U,  arm.GetAdjustedPc(0x2, elf_.get()));
-  ASSERT_EQ(1U,  arm.GetAdjustedPc(0x1, elf_.get()));
-  ASSERT_EQ(0U,  arm.GetAdjustedPc(0x0, elf_.get()));
+  ASSERT_EQ(2U, arm.GetPcAdjustment(0x5, elf_.get()));
+  ASSERT_EQ(0U, arm.GetPcAdjustment(0x4, elf_.get()));
+  ASSERT_EQ(0U, arm.GetPcAdjustment(0x3, elf_.get()));
+  ASSERT_EQ(0U, arm.GetPcAdjustment(0x2, elf_.get()));
+  ASSERT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
+  ASSERT_EQ(0U, arm.GetPcAdjustment(0x0, elf_.get()));
 
   elf_->FakeSetLoadBias(0x100);
-  ASSERT_EQ(0xffU,  arm.GetAdjustedPc(0xff, elf_.get()));
-  ASSERT_EQ(0x103U,  arm.GetAdjustedPc(0x105, elf_.get()));
-  ASSERT_EQ(0x104U,  arm.GetAdjustedPc(0x104, elf_.get()));
-  ASSERT_EQ(0x103U,  arm.GetAdjustedPc(0x103, elf_.get()));
-  ASSERT_EQ(0x102U,  arm.GetAdjustedPc(0x102, elf_.get()));
-  ASSERT_EQ(0x101U,  arm.GetAdjustedPc(0x101, elf_.get()));
-  ASSERT_EQ(0x100U,  arm.GetAdjustedPc(0x100, elf_.get()));
+  ASSERT_EQ(0U, arm.GetPcAdjustment(0xff, elf_.get()));
+  ASSERT_EQ(2U, arm.GetPcAdjustment(0x105, elf_.get()));
+  ASSERT_EQ(0U, arm.GetPcAdjustment(0x104, elf_.get()));
+  ASSERT_EQ(0U, arm.GetPcAdjustment(0x103, elf_.get()));
+  ASSERT_EQ(0U, arm.GetPcAdjustment(0x102, elf_.get()));
+  ASSERT_EQ(0U, arm.GetPcAdjustment(0x101, elf_.get()));
+  ASSERT_EQ(0U, arm.GetPcAdjustment(0x100, elf_.get()));
 
   // Check thumb instructions handling.
   elf_->FakeSetLoadBias(0);
   memory_->SetData32(0x2000, 0);
-  ASSERT_EQ(0x2003U,  arm.GetAdjustedPc(0x2005, elf_.get()));
+  ASSERT_EQ(2U, arm.GetPcAdjustment(0x2005, elf_.get()));
   memory_->SetData32(0x2000, 0xe000f000);
-  ASSERT_EQ(0x2001U,  arm.GetAdjustedPc(0x2005, elf_.get()));
+  ASSERT_EQ(4U, arm.GetPcAdjustment(0x2005, elf_.get()));
 
   elf_->FakeSetLoadBias(0x400);
   memory_->SetData32(0x2100, 0);
-  ASSERT_EQ(0x2503U,  arm.GetAdjustedPc(0x2505, elf_.get()));
+  ASSERT_EQ(2U, arm.GetPcAdjustment(0x2505, elf_.get()));
   memory_->SetData32(0x2100, 0xf111f111);
-  ASSERT_EQ(0x2501U,  arm.GetAdjustedPc(0x2505, elf_.get()));
+  ASSERT_EQ(4U, arm.GetPcAdjustment(0x2505, elf_.get()));
 }
 
 TEST_F(RegsTest, elf_invalid) {
@@ -188,27 +188,27 @@
 
   regs_arm.set_pc(0x1500);
   EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
-  EXPECT_EQ(0x4fcU, regs_arm.GetAdjustedPc(0x500U, invalid_elf));
+  EXPECT_EQ(4U, regs_arm.GetPcAdjustment(0x500U, invalid_elf));
 
   regs_arm64.set_pc(0x1600);
   EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
-  EXPECT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, invalid_elf));
+  EXPECT_EQ(0U, regs_arm64.GetPcAdjustment(0x600U, invalid_elf));
 
   regs_x86.set_pc(0x1700);
   EXPECT_EQ(0x700U, invalid_elf->GetRelPc(regs_x86.pc(), &map_info));
-  EXPECT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, invalid_elf));
+  EXPECT_EQ(0U, regs_x86.GetPcAdjustment(0x700U, invalid_elf));
 
   regs_x86_64.set_pc(0x1800);
   EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info));
-  EXPECT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, invalid_elf));
+  EXPECT_EQ(0U, regs_x86_64.GetPcAdjustment(0x800U, invalid_elf));
 
   regs_mips.set_pc(0x1900);
   EXPECT_EQ(0x900U, invalid_elf->GetRelPc(regs_mips.pc(), &map_info));
-  EXPECT_EQ(0x900U, regs_mips.GetAdjustedPc(0x900U, invalid_elf));
+  EXPECT_EQ(0U, regs_mips.GetPcAdjustment(0x900U, invalid_elf));
 
   regs_mips64.set_pc(0x1a00);
   EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(regs_mips64.pc(), &map_info));
-  EXPECT_EQ(0xa00U, regs_mips64.GetAdjustedPc(0xa00U, invalid_elf));
+  EXPECT_EQ(0U, regs_mips64.GetPcAdjustment(0xa00U, invalid_elf));
 }
 
 TEST_F(RegsTest, arm_set_from_raw) {
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 09376ab..af4a5b5 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -23,24 +23,154 @@
 #include <gtest/gtest.h>
 
 #include <string>
+#include <unordered_map>
 #include <vector>
 
+#include <android-base/file.h>
+
 #include <unwindstack/JitDebug.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MachineX86_64.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/RegsArm.h>
 #include <unwindstack/RegsArm64.h>
 #include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
 #include <unwindstack/Unwinder.h>
 
-#include "MachineArm.h"
-#include "MachineArm64.h"
-#include "MachineX86.h"
-
 #include "ElfTestUtils.h"
 
 namespace unwindstack {
 
+class UnwindOfflineTest : public ::testing::Test {
+ protected:
+  void TearDown() override {
+    if (cwd_ != nullptr) {
+      ASSERT_EQ(0, chdir(cwd_));
+    }
+    free(cwd_);
+  }
+
+  void Init(const char* file_dir, ArchEnum arch) {
+    dir_ = TestGetFileDirectory() + "offline/" + file_dir;
+
+    std::string data;
+    ASSERT_TRUE(android::base::ReadFileToString((dir_ + "maps.txt"), &data));
+
+    maps_.reset(new BufferMaps(data.c_str()));
+    ASSERT_TRUE(maps_->Parse());
+
+    std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
+    ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
+    process_memory_.reset(stack_memory.release());
+
+    switch (arch) {
+      case ARCH_ARM: {
+        RegsArm* regs = new RegsArm;
+        regs_.reset(regs);
+        ReadRegs<uint32_t>(regs, arm_regs_);
+        break;
+      }
+      case ARCH_ARM64: {
+        RegsArm64* regs = new RegsArm64;
+        regs_.reset(regs);
+        ReadRegs<uint64_t>(regs, arm64_regs_);
+        break;
+      }
+      case ARCH_X86: {
+        RegsX86* regs = new RegsX86;
+        regs_.reset(regs);
+        ReadRegs<uint32_t>(regs, x86_regs_);
+        break;
+      }
+      case ARCH_X86_64: {
+        RegsX86_64* regs = new RegsX86_64;
+        regs_.reset(regs);
+        ReadRegs<uint64_t>(regs, x86_64_regs_);
+        break;
+      }
+      default:
+        ASSERT_TRUE(false) << "Unknown arch " << std::to_string(arch);
+    }
+    cwd_ = getcwd(nullptr, 0);
+    // Make dir_ an absolute directory.
+    if (dir_.empty() || dir_[0] != '/') {
+      dir_ = std::string(cwd_) + '/' + dir_;
+    }
+    ASSERT_EQ(0, chdir(dir_.c_str()));
+  }
+
+  template <typename AddressType>
+  void ReadRegs(RegsImpl<AddressType>* regs,
+                const std::unordered_map<std::string, uint32_t>& name_to_reg) {
+    FILE* fp = fopen((dir_ + "regs.txt").c_str(), "r");
+    ASSERT_TRUE(fp != nullptr);
+    while (!feof(fp)) {
+      uint64_t value;
+      char reg_name[100];
+      ASSERT_EQ(2, fscanf(fp, "%s %" SCNx64 "\n", reg_name, &value));
+      std::string name(reg_name);
+      if (!name.empty()) {
+        // Remove the : from the end.
+        name.resize(name.size() - 1);
+      }
+      auto entry = name_to_reg.find(name);
+      ASSERT_TRUE(entry != name_to_reg.end()) << "Unknown register named " << name;
+      (*regs)[entry->second] = value;
+    }
+    fclose(fp);
+    regs->SetFromRaw();
+  }
+
+  static std::unordered_map<std::string, uint32_t> arm_regs_;
+  static std::unordered_map<std::string, uint32_t> arm64_regs_;
+  static std::unordered_map<std::string, uint32_t> x86_regs_;
+  static std::unordered_map<std::string, uint32_t> x86_64_regs_;
+
+  char* cwd_ = nullptr;
+  std::string dir_;
+  std::unique_ptr<Regs> regs_;
+  std::unique_ptr<Maps> maps_;
+  std::shared_ptr<Memory> process_memory_;
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm_regs_ = {
+    {"r0", ARM_REG_R0},  {"r1", ARM_REG_R1}, {"r2", ARM_REG_R2},   {"r3", ARM_REG_R3},
+    {"r4", ARM_REG_R4},  {"r5", ARM_REG_R5}, {"r6", ARM_REG_R6},   {"r7", ARM_REG_R7},
+    {"r8", ARM_REG_R8},  {"r9", ARM_REG_R9}, {"r10", ARM_REG_R10}, {"r11", ARM_REG_R11},
+    {"ip", ARM_REG_R12}, {"sp", ARM_REG_SP}, {"lr", ARM_REG_LR},   {"pc", ARM_REG_PC},
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm64_regs_ = {
+    {"x0", ARM64_REG_R0},   {"x1", ARM64_REG_R1},   {"x2", ARM64_REG_R2},   {"x3", ARM64_REG_R3},
+    {"x4", ARM64_REG_R4},   {"x5", ARM64_REG_R5},   {"x6", ARM64_REG_R6},   {"x7", ARM64_REG_R7},
+    {"x8", ARM64_REG_R8},   {"x9", ARM64_REG_R9},   {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
+    {"x12", ARM64_REG_R12}, {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14}, {"x15", ARM64_REG_R15},
+    {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17}, {"x18", ARM64_REG_R18}, {"x19", ARM64_REG_R19},
+    {"x20", ARM64_REG_R20}, {"x21", ARM64_REG_R21}, {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
+    {"x24", ARM64_REG_R24}, {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26}, {"x27", ARM64_REG_R27},
+    {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29}, {"sp", ARM64_REG_SP},   {"lr", ARM64_REG_LR},
+    {"pc", ARM64_REG_PC},
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_regs_ = {
+    {"eax", X86_REG_EAX}, {"ebx", X86_REG_EBX}, {"ecx", X86_REG_ECX},
+    {"edx", X86_REG_EDX}, {"ebp", X86_REG_EBP}, {"edi", X86_REG_EDI},
+    {"esi", X86_REG_ESI}, {"esp", X86_REG_ESP}, {"eip", X86_REG_EIP},
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_64_regs_ = {
+    {"rax", X86_64_REG_RAX}, {"rbx", X86_64_REG_RBX}, {"rcx", X86_64_REG_RCX},
+    {"rdx", X86_64_REG_RDX}, {"r8", X86_64_REG_R8},   {"r9", X86_64_REG_R9},
+    {"r10", X86_64_REG_R10}, {"r11", X86_64_REG_R11}, {"r12", X86_64_REG_R12},
+    {"r13", X86_64_REG_R13}, {"r14", X86_64_REG_R14}, {"r15", X86_64_REG_R15},
+    {"rdi", X86_64_REG_RDI}, {"rsi", X86_64_REG_RSI}, {"rbp", X86_64_REG_RBP},
+    {"rsp", X86_64_REG_RSP}, {"rip", X86_64_REG_RIP},
+};
+
 static std::string DumpFrames(Unwinder& unwinder) {
   std::string str;
   for (size_t i = 0; i < unwinder.NumFrames(); i++) {
@@ -49,45 +179,11 @@
   return str;
 }
 
-TEST(UnwindOfflineTest, pc_straddle_arm32) {
-  std::string dir(TestGetFileDirectory() + "offline/straddle_arm32/");
+TEST_F(UnwindOfflineTest, pc_straddle_arm) {
+  Init("straddle_arm/", ARCH_ARM);
 
-  MemoryOffline* memory = new MemoryOffline;
-  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
-
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsArm regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_PC] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_SP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_LR] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_ARM, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  Unwinder unwinder(128, &maps, &regs, process_memory);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -97,45 +193,21 @@
       "  #02 pc 00007441  libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
       "  #03 pc 00015147  /does/not/exist/libhidlbase.so\n",
       frame_info);
+  EXPECT_EQ(0xf31ea9f8U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xe9c866f8U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xf2da0a1bU, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xe9c86728U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0xf2da1441U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xe9c86730U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0xf3367147U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xe9c86778U, unwinder.frames()[3].sp);
 }
 
-TEST(UnwindOfflineTest, pc_in_gnu_debugdata_arm32) {
-  std::string dir(TestGetFileDirectory() + "offline/gnu_debugdata_arm32/");
+TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
+  Init("gnu_debugdata_arm/", ARCH_ARM);
 
-  MemoryOffline* memory = new MemoryOffline;
-  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
-
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsArm regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_PC] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_SP] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_ARM, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  Unwinder unwinder(128, &maps, &regs, process_memory);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -145,49 +217,17 @@
       "  #01 pc 0006dce5  libandroid_runtime.so "
       "(_ZN7android14AndroidRuntime19javaCreateThreadEtcEPFiPvES1_PKcijPS1_)\n",
       frame_info);
+  EXPECT_EQ(0xf1f6dc49U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xd8fe6930U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xf1f6dce5U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xd8fe6958U, unwinder.frames()[1].sp);
 }
 
-TEST(UnwindOfflineTest, pc_straddle_arm64) {
-  std::string dir(TestGetFileDirectory() + "offline/straddle_arm64/");
+TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
+  Init("straddle_arm64/", ARCH_ARM64);
 
-  MemoryOffline* memory = new MemoryOffline;
-  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
-
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsArm64 regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_PC] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_SP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_LR] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "x29: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_R29] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_ARM64, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  Unwinder unwinder(128, &maps, &regs, process_memory);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -201,6 +241,18 @@
       "(_ZN11unwindstack37UnwindTest_remote_through_signal_Test8TestBodyEv+32)\n"
       "  #05 pc 0000000000455d70  libunwindstack_test (_ZN7testing4Test3RunEv+392)\n",
       frame_info);
+  EXPECT_EQ(0x64d09d4fd8U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7fe0d84040U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x64d09d5078U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7fe0d84070U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x64d09d508cU, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7fe0d84080U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x64d09d88fcU, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7fe0d84090U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x64d09d88d8U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7fe0d840f0U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x64d0a00d70U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7fe0d84110U, unwinder.frames()[5].sp);
 }
 
 static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
@@ -209,64 +261,22 @@
   parts->Add(memory);
 }
 
-TEST(UnwindOfflineTest, jit_debug_x86_32) {
-  std::string dir(TestGetFileDirectory() + "offline/jit_debug_x86_32/");
+TEST_F(UnwindOfflineTest, jit_debug_x86) {
+  Init("jit_debug_x86/", ARCH_X86);
 
   MemoryOfflineParts* memory = new MemoryOfflineParts;
-  AddMemory(dir + "descriptor.data", memory);
-  AddMemory(dir + "stack.data", memory);
+  AddMemory(dir_ + "descriptor.data", memory);
+  AddMemory(dir_ + "stack.data", memory);
   for (size_t i = 0; i < 7; i++) {
-    AddMemory(dir + "entry" + std::to_string(i) + ".data", memory);
-    AddMemory(dir + "jit" + std::to_string(i) + ".data", memory);
+    AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
+    AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
   }
+  process_memory_.reset(memory);
 
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsX86 regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "eax: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EAX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ebx: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EBX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ecx: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_ECX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "edx: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EDX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ebp: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EBP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "edi: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EDI] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "esi: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_ESI] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "esp: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_ESP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "eip: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EIP] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_X86, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  JitDebug jit_debug(process_memory);
-  Unwinder unwinder(128, &maps, &regs, process_memory);
-  unwinder.SetJitDebug(&jit_debug, regs.Arch());
+  JitDebug jit_debug(process_memory_);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.SetJitDebug(&jit_debug, regs_->Arch());
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -404,81 +414,163 @@
       "  #67 pc 00001a80  dalvikvm32 (main+1312)\n"
       "  #68 pc 00018275  libc.so\n",
       frame_info);
+  EXPECT_EQ(0xeb89bfb8U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xffeb5280U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xeb89af00U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xffeb52a0U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0xec6061a8U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xffeb5ce0U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0xee75be81U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xffeb5d30U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xf728e4d2U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xffeb5d60U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0xffeb5d80U, unwinder.frames()[5].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[6].pc);
+  EXPECT_EQ(0xffeb5e20U, unwinder.frames()[6].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[7].pc);
+  EXPECT_EQ(0xffeb5ec0U, unwinder.frames()[7].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[8].pc);
+  EXPECT_EQ(0xffeb5f40U, unwinder.frames()[8].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[9].pc);
+  EXPECT_EQ(0xffeb5fb0U, unwinder.frames()[9].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[10].pc);
+  EXPECT_EQ(0xffeb6110U, unwinder.frames()[10].sp);
+  EXPECT_EQ(0xee75be04U, unwinder.frames()[11].pc);
+  EXPECT_EQ(0xffeb6160U, unwinder.frames()[11].sp);
+  EXPECT_EQ(0xf728e4d2U, unwinder.frames()[12].pc);
+  EXPECT_EQ(0xffeb6180U, unwinder.frames()[12].sp);
+  EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[13].pc);
+  EXPECT_EQ(0xffeb61b0U, unwinder.frames()[13].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[14].pc);
+  EXPECT_EQ(0xffeb6250U, unwinder.frames()[14].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[15].pc);
+  EXPECT_EQ(0xffeb62f0U, unwinder.frames()[15].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[16].pc);
+  EXPECT_EQ(0xffeb6370U, unwinder.frames()[16].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[17].pc);
+  EXPECT_EQ(0xffeb63e0U, unwinder.frames()[17].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[18].pc);
+  EXPECT_EQ(0xffeb6530U, unwinder.frames()[18].sp);
+  EXPECT_EQ(0xee75bd3cU, unwinder.frames()[19].pc);
+  EXPECT_EQ(0xffeb6580U, unwinder.frames()[19].sp);
+  EXPECT_EQ(0xf728e4d2U, unwinder.frames()[20].pc);
+  EXPECT_EQ(0xffeb65b0U, unwinder.frames()[20].sp);
+  EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[21].pc);
+  EXPECT_EQ(0xffeb65e0U, unwinder.frames()[21].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[22].pc);
+  EXPECT_EQ(0xffeb6680U, unwinder.frames()[22].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[23].pc);
+  EXPECT_EQ(0xffeb6720U, unwinder.frames()[23].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[24].pc);
+  EXPECT_EQ(0xffeb67a0U, unwinder.frames()[24].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[25].pc);
+  EXPECT_EQ(0xffeb6810U, unwinder.frames()[25].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[26].pc);
+  EXPECT_EQ(0xffeb6960U, unwinder.frames()[26].sp);
+  EXPECT_EQ(0xee75bbdcU, unwinder.frames()[27].pc);
+  EXPECT_EQ(0xffeb69b0U, unwinder.frames()[27].sp);
+  EXPECT_EQ(0xf728e6a2U, unwinder.frames()[28].pc);
+  EXPECT_EQ(0xffeb69f0U, unwinder.frames()[28].sp);
+  EXPECT_EQ(0xf6d27acbU, unwinder.frames()[29].pc);
+  EXPECT_EQ(0xffeb6a20U, unwinder.frames()[29].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[30].pc);
+  EXPECT_EQ(0xffeb6ac0U, unwinder.frames()[30].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[31].pc);
+  EXPECT_EQ(0xffeb6b60U, unwinder.frames()[31].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[32].pc);
+  EXPECT_EQ(0xffeb6be0U, unwinder.frames()[32].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[33].pc);
+  EXPECT_EQ(0xffeb6c50U, unwinder.frames()[33].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[34].pc);
+  EXPECT_EQ(0xffeb6dd0U, unwinder.frames()[34].sp);
+  EXPECT_EQ(0xee75b625U, unwinder.frames()[35].pc);
+  EXPECT_EQ(0xffeb6e20U, unwinder.frames()[35].sp);
+  EXPECT_EQ(0xf728e4d2U, unwinder.frames()[36].pc);
+  EXPECT_EQ(0xffeb6e50U, unwinder.frames()[36].sp);
+  EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[37].pc);
+  EXPECT_EQ(0xffeb6e70U, unwinder.frames()[37].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[38].pc);
+  EXPECT_EQ(0xffeb6f10U, unwinder.frames()[38].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[39].pc);
+  EXPECT_EQ(0xffeb6fb0U, unwinder.frames()[39].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[40].pc);
+  EXPECT_EQ(0xffeb7030U, unwinder.frames()[40].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[41].pc);
+  EXPECT_EQ(0xffeb70a0U, unwinder.frames()[41].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[42].pc);
+  EXPECT_EQ(0xffeb71f0U, unwinder.frames()[42].sp);
+  EXPECT_EQ(0xee75aedcU, unwinder.frames()[43].pc);
+  EXPECT_EQ(0xffeb7240U, unwinder.frames()[43].sp);
+  EXPECT_EQ(0xf728e4d2U, unwinder.frames()[44].pc);
+  EXPECT_EQ(0xffeb72a0U, unwinder.frames()[44].sp);
+  EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[45].pc);
+  EXPECT_EQ(0xffeb72c0U, unwinder.frames()[45].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[46].pc);
+  EXPECT_EQ(0xffeb7360U, unwinder.frames()[46].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[47].pc);
+  EXPECT_EQ(0xffeb7400U, unwinder.frames()[47].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[48].pc);
+  EXPECT_EQ(0xffeb7480U, unwinder.frames()[48].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[49].pc);
+  EXPECT_EQ(0xffeb74f0U, unwinder.frames()[49].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[50].pc);
+  EXPECT_EQ(0xffeb7680U, unwinder.frames()[50].sp);
+  EXPECT_EQ(0xee756c22U, unwinder.frames()[51].pc);
+  EXPECT_EQ(0xffeb76d0U, unwinder.frames()[51].sp);
+  EXPECT_EQ(0xf728e6a2U, unwinder.frames()[52].pc);
+  EXPECT_EQ(0xffeb76f0U, unwinder.frames()[52].sp);
+  EXPECT_EQ(0xf6d27acbU, unwinder.frames()[53].pc);
+  EXPECT_EQ(0xffeb7710U, unwinder.frames()[53].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[54].pc);
+  EXPECT_EQ(0xffeb77b0U, unwinder.frames()[54].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[55].pc);
+  EXPECT_EQ(0xffeb7850U, unwinder.frames()[55].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[56].pc);
+  EXPECT_EQ(0xffeb78d0U, unwinder.frames()[56].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[57].pc);
+  EXPECT_EQ(0xffeb7940U, unwinder.frames()[57].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[58].pc);
+  EXPECT_EQ(0xffeb7a80U, unwinder.frames()[58].sp);
+  EXPECT_EQ(0xf728e6a2U, unwinder.frames()[59].pc);
+  EXPECT_EQ(0xffeb7ad0U, unwinder.frames()[59].sp);
+  EXPECT_EQ(0xf6d27acbU, unwinder.frames()[60].pc);
+  EXPECT_EQ(0xffeb7af0U, unwinder.frames()[60].sp);
+  EXPECT_EQ(0xf718bc95U, unwinder.frames()[61].pc);
+  EXPECT_EQ(0xffeb7b90U, unwinder.frames()[61].sp);
+  EXPECT_EQ(0xf718bb5aU, unwinder.frames()[62].pc);
+  EXPECT_EQ(0xffeb7c50U, unwinder.frames()[62].sp);
+  EXPECT_EQ(0xf706b3ddU, unwinder.frames()[63].pc);
+  EXPECT_EQ(0xffeb7d10U, unwinder.frames()[63].sp);
+  EXPECT_EQ(0xf6d6548cU, unwinder.frames()[64].pc);
+  EXPECT_EQ(0xffeb7d70U, unwinder.frames()[64].sp);
+  EXPECT_EQ(0xf6d5df06U, unwinder.frames()[65].pc);
+  EXPECT_EQ(0xffeb7df0U, unwinder.frames()[65].sp);
+  EXPECT_EQ(0x56574d8cU, unwinder.frames()[66].pc);
+  EXPECT_EQ(0xffeb7e40U, unwinder.frames()[66].sp);
+  EXPECT_EQ(0x56574a80U, unwinder.frames()[67].pc);
+  EXPECT_EQ(0xffeb7e70U, unwinder.frames()[67].sp);
+  EXPECT_EQ(0xf7363275U, unwinder.frames()[68].pc);
+  EXPECT_EQ(0xffeb7ef0U, unwinder.frames()[68].sp);
 }
 
-TEST(UnwindOfflineTest, jit_debug_arm32) {
-  std::string dir(TestGetFileDirectory() + "offline/jit_debug_arm32/");
+TEST_F(UnwindOfflineTest, jit_debug_arm) {
+  Init("jit_debug_arm/", ARCH_ARM);
 
   MemoryOfflineParts* memory = new MemoryOfflineParts;
-  AddMemory(dir + "descriptor.data", memory);
-  AddMemory(dir + "descriptor1.data", memory);
-  AddMemory(dir + "stack.data", memory);
+  AddMemory(dir_ + "descriptor.data", memory);
+  AddMemory(dir_ + "descriptor1.data", memory);
+  AddMemory(dir_ + "stack.data", memory);
   for (size_t i = 0; i < 7; i++) {
-    AddMemory(dir + "entry" + std::to_string(i) + ".data", memory);
-    AddMemory(dir + "jit" + std::to_string(i) + ".data", memory);
+    AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
+    AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
   }
+  process_memory_.reset(memory);
 
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsArm regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r0: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R0] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r1: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R1] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r2: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R2] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r3: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R3] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r4: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R4] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r5: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R5] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r6: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R6] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r7: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R7] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r8: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R8] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r9: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R9] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r10: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R10] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "r11: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R11] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ip: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_R12] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_SP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_LR] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
-  regs[ARM_REG_PC] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_ARM, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  JitDebug jit_debug(process_memory);
-  Unwinder unwinder(128, &maps, &regs, process_memory);
-  unwinder.SetJitDebug(&jit_debug, regs.Arch());
+  JitDebug jit_debug(process_memory_);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.SetJitDebug(&jit_debug, regs_->Arch());
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -623,52 +715,168 @@
       "  #74 pc 00001349  dalvikvm32 (main+896)\n"
       "  #75 pc 000850c9  libc.so\n",
       frame_info);
+  EXPECT_EQ(0xdfe66a5eU, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xff85d180U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xe044712dU, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xff85d200U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0xe27a7cb1U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xff85d290U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xff85d2b0U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xed761129U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xff85d2e8U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0xed3b97a9U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0xff85d370U, unwinder.frames()[5].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[6].pc);
+  EXPECT_EQ(0xff85d3d8U, unwinder.frames()[6].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[7].pc);
+  EXPECT_EQ(0xff85d428U, unwinder.frames()[7].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[8].pc);
+  EXPECT_EQ(0xff85d470U, unwinder.frames()[8].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[9].pc);
+  EXPECT_EQ(0xff85d4b0U, unwinder.frames()[9].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[10].pc);
+  EXPECT_EQ(0xff85d5d0U, unwinder.frames()[10].sp);
+  EXPECT_EQ(0xe27a7c31U, unwinder.frames()[11].pc);
+  EXPECT_EQ(0xff85d640U, unwinder.frames()[11].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[12].pc);
+  EXPECT_EQ(0xff85d660U, unwinder.frames()[12].sp);
+  EXPECT_EQ(0xed761129U, unwinder.frames()[13].pc);
+  EXPECT_EQ(0xff85d698U, unwinder.frames()[13].sp);
+  EXPECT_EQ(0xed3b97a9U, unwinder.frames()[14].pc);
+  EXPECT_EQ(0xff85d720U, unwinder.frames()[14].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[15].pc);
+  EXPECT_EQ(0xff85d788U, unwinder.frames()[15].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[16].pc);
+  EXPECT_EQ(0xff85d7d8U, unwinder.frames()[16].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[17].pc);
+  EXPECT_EQ(0xff85d820U, unwinder.frames()[17].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[18].pc);
+  EXPECT_EQ(0xff85d860U, unwinder.frames()[18].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[19].pc);
+  EXPECT_EQ(0xff85d970U, unwinder.frames()[19].sp);
+  EXPECT_EQ(0xe27a7b77U, unwinder.frames()[20].pc);
+  EXPECT_EQ(0xff85d9e0U, unwinder.frames()[20].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[21].pc);
+  EXPECT_EQ(0xff85da10U, unwinder.frames()[21].sp);
+  EXPECT_EQ(0xed761129U, unwinder.frames()[22].pc);
+  EXPECT_EQ(0xff85da48U, unwinder.frames()[22].sp);
+  EXPECT_EQ(0xed3b97a9U, unwinder.frames()[23].pc);
+  EXPECT_EQ(0xff85dad0U, unwinder.frames()[23].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[24].pc);
+  EXPECT_EQ(0xff85db38U, unwinder.frames()[24].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[25].pc);
+  EXPECT_EQ(0xff85db88U, unwinder.frames()[25].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[26].pc);
+  EXPECT_EQ(0xff85dbd0U, unwinder.frames()[26].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[27].pc);
+  EXPECT_EQ(0xff85dc10U, unwinder.frames()[27].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[28].pc);
+  EXPECT_EQ(0xff85dd20U, unwinder.frames()[28].sp);
+  EXPECT_EQ(0xe27a7a29U, unwinder.frames()[29].pc);
+  EXPECT_EQ(0xff85dd90U, unwinder.frames()[29].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[30].pc);
+  EXPECT_EQ(0xff85ddc0U, unwinder.frames()[30].sp);
+  EXPECT_EQ(0xed76122fU, unwinder.frames()[31].pc);
+  EXPECT_EQ(0xff85de08U, unwinder.frames()[31].sp);
+  EXPECT_EQ(0xed3b97bbU, unwinder.frames()[32].pc);
+  EXPECT_EQ(0xff85de90U, unwinder.frames()[32].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[33].pc);
+  EXPECT_EQ(0xff85def8U, unwinder.frames()[33].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[34].pc);
+  EXPECT_EQ(0xff85df48U, unwinder.frames()[34].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[35].pc);
+  EXPECT_EQ(0xff85df90U, unwinder.frames()[35].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[36].pc);
+  EXPECT_EQ(0xff85dfd0U, unwinder.frames()[36].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[37].pc);
+  EXPECT_EQ(0xff85e110U, unwinder.frames()[37].sp);
+  EXPECT_EQ(0xe27a739bU, unwinder.frames()[38].pc);
+  EXPECT_EQ(0xff85e180U, unwinder.frames()[38].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[39].pc);
+  EXPECT_EQ(0xff85e1b0U, unwinder.frames()[39].sp);
+  EXPECT_EQ(0xed761129U, unwinder.frames()[40].pc);
+  EXPECT_EQ(0xff85e1e0U, unwinder.frames()[40].sp);
+  EXPECT_EQ(0xed3b97a9U, unwinder.frames()[41].pc);
+  EXPECT_EQ(0xff85e268U, unwinder.frames()[41].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[42].pc);
+  EXPECT_EQ(0xff85e2d0U, unwinder.frames()[42].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[43].pc);
+  EXPECT_EQ(0xff85e320U, unwinder.frames()[43].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[44].pc);
+  EXPECT_EQ(0xff85e368U, unwinder.frames()[44].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[45].pc);
+  EXPECT_EQ(0xff85e3a8U, unwinder.frames()[45].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[46].pc);
+  EXPECT_EQ(0xff85e4c0U, unwinder.frames()[46].sp);
+  EXPECT_EQ(0xe27a6aa7U, unwinder.frames()[47].pc);
+  EXPECT_EQ(0xff85e530U, unwinder.frames()[47].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[48].pc);
+  EXPECT_EQ(0xff85e5a0U, unwinder.frames()[48].sp);
+  EXPECT_EQ(0xed761129U, unwinder.frames()[49].pc);
+  EXPECT_EQ(0xff85e5d8U, unwinder.frames()[49].sp);
+  EXPECT_EQ(0xed3b97a9U, unwinder.frames()[50].pc);
+  EXPECT_EQ(0xff85e660U, unwinder.frames()[50].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[51].pc);
+  EXPECT_EQ(0xff85e6c8U, unwinder.frames()[51].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[52].pc);
+  EXPECT_EQ(0xff85e718U, unwinder.frames()[52].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[53].pc);
+  EXPECT_EQ(0xff85e760U, unwinder.frames()[53].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[54].pc);
+  EXPECT_EQ(0xff85e7a0U, unwinder.frames()[54].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[55].pc);
+  EXPECT_EQ(0xff85e8f0U, unwinder.frames()[55].sp);
+  EXPECT_EQ(0xe27a1a99U, unwinder.frames()[56].pc);
+  EXPECT_EQ(0xff85e960U, unwinder.frames()[56].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[57].pc);
+  EXPECT_EQ(0xff85e990U, unwinder.frames()[57].sp);
+  EXPECT_EQ(0xed76122fU, unwinder.frames()[58].pc);
+  EXPECT_EQ(0xff85e9c8U, unwinder.frames()[58].sp);
+  EXPECT_EQ(0xed3b97bbU, unwinder.frames()[59].pc);
+  EXPECT_EQ(0xff85ea50U, unwinder.frames()[59].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[60].pc);
+  EXPECT_EQ(0xff85eab8U, unwinder.frames()[60].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[61].pc);
+  EXPECT_EQ(0xff85eb08U, unwinder.frames()[61].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[62].pc);
+  EXPECT_EQ(0xff85eb50U, unwinder.frames()[62].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[63].pc);
+  EXPECT_EQ(0xff85eb90U, unwinder.frames()[63].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[64].pc);
+  EXPECT_EQ(0xff85ec90U, unwinder.frames()[64].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[65].pc);
+  EXPECT_EQ(0xff85ed00U, unwinder.frames()[65].sp);
+  EXPECT_EQ(0xed76122fU, unwinder.frames()[66].pc);
+  EXPECT_EQ(0xff85ed38U, unwinder.frames()[66].sp);
+  EXPECT_EQ(0xed3b97bbU, unwinder.frames()[67].pc);
+  EXPECT_EQ(0xff85edc0U, unwinder.frames()[67].sp);
+  EXPECT_EQ(0xed6ac92dU, unwinder.frames()[68].pc);
+  EXPECT_EQ(0xff85ee28U, unwinder.frames()[68].sp);
+  EXPECT_EQ(0xed6ac6c3U, unwinder.frames()[69].pc);
+  EXPECT_EQ(0xff85eeb8U, unwinder.frames()[69].sp);
+  EXPECT_EQ(0xed602411U, unwinder.frames()[70].pc);
+  EXPECT_EQ(0xff85ef48U, unwinder.frames()[70].sp);
+  EXPECT_EQ(0xed3e0a9fU, unwinder.frames()[71].pc);
+  EXPECT_EQ(0xff85ef90U, unwinder.frames()[71].sp);
+  EXPECT_EQ(0xed3db9b9U, unwinder.frames()[72].pc);
+  EXPECT_EQ(0xff85f008U, unwinder.frames()[72].sp);
+  EXPECT_EQ(0xab0d459fU, unwinder.frames()[73].pc);
+  EXPECT_EQ(0xff85f038U, unwinder.frames()[73].sp);
+  EXPECT_EQ(0xab0d4349U, unwinder.frames()[74].pc);
+  EXPECT_EQ(0xff85f050U, unwinder.frames()[74].sp);
+  EXPECT_EQ(0xedb0d0c9U, unwinder.frames()[75].pc);
+  EXPECT_EQ(0xff85f0c0U, unwinder.frames()[75].sp);
 }
 
 // The eh_frame_hdr data is present but set to zero fdes. This should
 // fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
 // No .gnu_debugdata section in the elf file, so no symbols.
-TEST(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
-  std::string dir(TestGetFileDirectory() + "offline/bad_eh_frame_hdr_arm64/");
+TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
+  Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64);
 
-  MemoryOffline* memory = new MemoryOffline;
-  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
-
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsArm64 regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_PC] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_SP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_LR] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "x29: %" SCNx64 "\n", &reg_value));
-  regs[ARM64_REG_R29] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_ARM64, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  Unwinder unwinder(128, &maps, &regs, process_memory);
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -679,63 +887,25 @@
       "  #03 pc 0000000000000590  waiter64\n"
       "  #04 pc 00000000000a8e98  libc.so (__libc_init+88)\n",
       frame_info);
+  EXPECT_EQ(0x60a9fdf550U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7fdd141990U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x60a9fdf568U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7fdd1419a0U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x60a9fdf57cU, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7fdd1419b0U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x60a9fdf590U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7fdd1419c0U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7542d68e98U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7fdd1419d0U, unwinder.frames()[4].sp);
 }
 
 // The elf has bad eh_frame unwind information for the pcs. If eh_frame
 // is used first, the unwind will not match the expected output.
-TEST(UnwindOfflineTest, debug_frame_first_x86) {
-  std::string dir(TestGetFileDirectory() + "offline/debug_frame_first_x86/");
+TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
+  Init("debug_frame_first_x86/", ARCH_X86);
 
-  MemoryOffline* memory = new MemoryOffline;
-  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
-
-  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  RegsX86 regs;
-  uint64_t reg_value;
-  ASSERT_EQ(1, fscanf(fp, "eax: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EAX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ebx: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EBX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ecx: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_ECX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "edx: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EDX] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "ebp: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EBP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "edi: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EDI] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "esi: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_ESI] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "esp: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_ESP] = reg_value;
-  ASSERT_EQ(1, fscanf(fp, "eip: %" SCNx64 "\n", &reg_value));
-  regs[X86_REG_EIP] = reg_value;
-  regs.SetFromRaw();
-  fclose(fp);
-
-  fp = fopen((dir + "maps.txt").c_str(), "r");
-  ASSERT_TRUE(fp != nullptr);
-  // The file is guaranteed to be less than 4096 bytes.
-  std::vector<char> buffer(4096);
-  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
-  fclose(fp);
-
-  BufferMaps maps(buffer.data());
-  ASSERT_TRUE(maps.Parse());
-
-  ASSERT_EQ(ARCH_X86, regs.Arch());
-
-  std::shared_ptr<Memory> process_memory(memory);
-
-  char* cwd = getcwd(nullptr, 0);
-  ASSERT_EQ(0, chdir(dir.c_str()));
-  JitDebug jit_debug(process_memory);
-  Unwinder unwinder(128, &maps, &regs, process_memory);
-  unwinder.SetJitDebug(&jit_debug, regs.Arch());
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
-  ASSERT_EQ(0, chdir(cwd));
-  free(cwd);
 
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
@@ -746,6 +916,44 @@
       "  #03 pc 000006f7  waiter (main+23)\n"
       "  #04 pc 00018275  libc.so\n",
       frame_info);
+  EXPECT_EQ(0x56598685U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xffcf9e38U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x565986b7U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xffcf9e50U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x565986d7U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xffcf9e60U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x565986f7U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xffcf9e70U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xf744a275U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xffcf9e80U, unwinder.frames()[4].sp);
+}
+
+// Make sure that a pc that is at the beginning of an fde unwinds correctly.
+TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
+  Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64);
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0000000000000a80  unwind_test64 (calling3)\n"
+      "  #01 pc 0000000000000dd9  unwind_test64 (calling2+633)\n"
+      "  #02 pc 000000000000121e  unwind_test64 (calling1+638)\n"
+      "  #03 pc 00000000000013ed  unwind_test64 (main+13)\n"
+      "  #04 pc 00000000000202b0  libc.so\n",
+      frame_info);
+  EXPECT_EQ(0x561550b17a80U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7ffcc8596ce8U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x561550b17dd9U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7ffcc8596cf0U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x561550b1821eU, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7ffcc8596f40U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x561550b183edU, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7ffcc8597190U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7f4de62162b0U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7ffcc85971a0U, unwinder.frames()[4].sp);
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 45cf907..e44b225 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -98,6 +98,26 @@
     info = new MapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
     maps_.FakeAddMapInfo(info);
 
+    info = new MapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+    info->load_bias = 0;
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                       "/fake/fake_load_bias.so");
+    elf = new ElfFake(new MemoryFake);
+    info->elf.reset(elf);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    elf->FakeSetLoadBias(0x5000);
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                       "/fake/fake_offset.oat");
+    elf = new ElfFake(new MemoryFake);
+    info->elf.reset(elf);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    info->elf_offset = 0x8000;
+    maps_.FakeAddMapInfo(info);
+
     process_memory_.reset(new MemoryFake);
   }
 
@@ -176,6 +196,123 @@
   EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
 }
 
+TEST_F(UnwinderTest, multiple_frames_dont_resolve_names) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.SetResolveNames(false);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x100U, frame->rel_pc);
+  EXPECT_EQ(0x1100U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x200U, frame->rel_pc);
+  EXPECT_EQ(0x1200U, frame->pc);
+  EXPECT_EQ(0x10020U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, non_zero_load_bias) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.FakeSetPc(0xa5500);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x5500U, frame->rel_pc);
+  EXPECT_EQ(0xa5500U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake_load_bias.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa5000U, frame->map_start);
+  EXPECT_EQ(0xa6000U, frame->map_end);
+  EXPECT_EQ(0x5000U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, non_zero_elf_offset) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.FakeSetPc(0xa7500);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x8500U, frame->rel_pc);
+  EXPECT_EQ(0xa7500U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake_offset.oat", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa7000U, frame->map_start);
+  EXPECT_EQ(0xa8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
 TEST_F(UnwinderTest, non_zero_map_offset) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
 
@@ -666,6 +803,146 @@
   EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
 }
 
+TEST_F(UnwinderTest, dex_pc_in_map) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+  regs_.FakeSetDexPc(0xa3400);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(2U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0xa3400U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa3000U, frame->map_start);
+  EXPECT_EQ(0xa4000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_not_in_map) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+  regs_.FakeSetDexPc(0x50000);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(2U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x50000U, frame->rel_pc);
+  EXPECT_EQ(0x50000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_start);
+  EXPECT_EQ(0U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(0, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_multiple_frames) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+  regs_.FakeSetDexPc(0xa3400);
+  ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0xa3400U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa3000U, frame->map_start);
+  EXPECT_EQ(0xa4000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0x33400U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/fake/compressed.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x33000U, frame->map_start);
+  EXPECT_EQ(0x34000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
 // Verify format frame code.
 TEST_F(UnwinderTest, format_frame_static) {
   FrameData frame;
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so
new file mode 100644
index 0000000..46b6f45
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt
new file mode 100644
index 0000000..ac2e564
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt
@@ -0,0 +1,2 @@
+561550b17000-561550b1a000 r-xp 0 00:00 0   unwind_test64
+7f4de61f6000-7f4de638b000 r-xp 0 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt
new file mode 100644
index 0000000..38af274
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt
@@ -0,0 +1,11 @@
+rax: 92134c6fbbdc12ff
+rbx: 0
+rcx: 92134c6fbbdc1200
+rdx: 92134c6fbbdc1200
+r8: 561552153034
+r12: 561550b17930
+r13: 7ffcc8597270
+rsi: 561552153034
+rbp: 7ffcc8596f30
+rsp: 7ffcc8596ce8
+rip: 561550b17a80
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data
new file mode 100644
index 0000000..cc7882b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64 b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64
new file mode 100644
index 0000000..ab0ef8f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/libandroid_runtime.so b/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so
similarity index 100%
rename from libunwindstack/tests/files/offline/gnu_debugdata_arm32/libandroid_runtime.so
rename to libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/maps.txt b/libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/gnu_debugdata_arm32/maps.txt
rename to libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/regs.txt b/libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/gnu_debugdata_arm32/regs.txt
rename to libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/stack.data b/libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/gnu_debugdata_arm32/stack.data
rename to libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/137-cfi.odex b/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/137-cfi.odex
rename to libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/dalvikvm32
rename to libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/descriptor.data b/libunwindstack/tests/files/offline/jit_debug_arm/descriptor.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/descriptor.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/descriptor1.data b/libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/descriptor1.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry0.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/entry0.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry1.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/entry1.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry2.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry2.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/entry2.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry3.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry3.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/entry3.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry4.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry4.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/entry4.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry5.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry5.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/entry5.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry6.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry6.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/entry6.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit0.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/jit0.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit1.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/jit1.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit2.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit2.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/jit2.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit3.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit3.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/jit3.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit4.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit4.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/jit4.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit5.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit5.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/jit5.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit6.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit6.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/jit6.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/jit6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/libart.so b/libunwindstack/tests/files/offline/jit_debug_arm/libart.so
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/libart.so
rename to libunwindstack/tests/files/offline/jit_debug_arm/libart.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/libartd.so b/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/libartd.so
rename to libunwindstack/tests/files/offline/jit_debug_arm/libartd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/libarttestd.so
rename to libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/libc.so b/libunwindstack/tests/files/offline/jit_debug_arm/libc.so
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/libc.so
rename to libunwindstack/tests/files/offline/jit_debug_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/maps.txt
rename to libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/regs.txt b/libunwindstack/tests/files/offline/jit_debug_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/regs.txt
rename to libunwindstack/tests/files/offline/jit_debug_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/stack.data b/libunwindstack/tests/files/offline/jit_debug_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm32/stack.data
rename to libunwindstack/tests/files/offline/jit_debug_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex b/libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex
rename to libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32
rename to libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data b/libunwindstack/tests/files/offline/jit_debug_x86/descriptor.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry2.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry3.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry4.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry5.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry6.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit2.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit3.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit4.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit5.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit6.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/jit6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so b/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so
new file mode 100644
index 0000000..92ed991
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so
new file mode 100644
index 0000000..5efae02
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so b/libunwindstack/tests/files/offline/jit_debug_x86/libc.so
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so
rename to libunwindstack/tests/files/offline/jit_debug_x86/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt
rename to libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt b/libunwindstack/tests/files/offline/jit_debug_x86/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt
rename to libunwindstack/tests/files/offline/jit_debug_x86/regs.txt
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data b/libunwindstack/tests/files/offline/jit_debug_x86/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data
rename to libunwindstack/tests/files/offline/jit_debug_x86/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so
deleted file mode 100644
index 70352db..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_x86_32/libartd.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so
deleted file mode 100644
index e72e673..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/libbase.so b/libunwindstack/tests/files/offline/straddle_arm/libbase.so
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm32/libbase.so
rename to libunwindstack/tests/files/offline/straddle_arm/libbase.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/libc.so b/libunwindstack/tests/files/offline/straddle_arm/libc.so
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm32/libc.so
rename to libunwindstack/tests/files/offline/straddle_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/maps.txt b/libunwindstack/tests/files/offline/straddle_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm32/maps.txt
rename to libunwindstack/tests/files/offline/straddle_arm/maps.txt
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/regs.txt b/libunwindstack/tests/files/offline/straddle_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm32/regs.txt
rename to libunwindstack/tests/files/offline/straddle_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/straddle_arm32/stack.data b/libunwindstack/tests/files/offline/straddle_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm32/stack.data
rename to libunwindstack/tests/files/offline/straddle_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 07e48af..22ca7bf 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <unwindstack/DexFiles.h>
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
@@ -91,8 +92,13 @@
 
   auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
   unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory);
+
   unwindstack::JitDebug jit_debug(process_memory);
   unwinder.SetJitDebug(&jit_debug, regs->Arch());
+
+  unwindstack::DexFiles dex_files(process_memory);
+  unwinder.SetDexFiles(&dex_files, regs->Arch());
+
   unwinder.Unwind();
 
   // Print the frames.
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 7f2d11d..a0abcca 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -120,6 +120,11 @@
     return 1;
   }
 
+  std::string soname;
+  if (elf.GetSoname(&soname)) {
+    printf("Soname: %s\n", soname.c_str());
+  }
+
   ElfInterface* interface = elf.interface();
   if (elf.machine_type() == EM_ARM) {
     DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
new file mode 100644
index 0000000..47a4f91
--- /dev/null
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+
+#include "DwarfOp.h"
+
+namespace unwindstack {
+
+void PrintSignedValue(int64_t value) {
+  if (value < 0) {
+    printf("- %" PRId64, -value);
+  } else if (value > 0) {
+    printf("+ %" PRId64, value);
+  }
+}
+
+void PrintExpression(Memory* memory, uint8_t class_type, uint64_t end, uint64_t length) {
+  std::vector<std::string> lines;
+  DwarfMemory dwarf_memory(memory);
+  if (class_type == ELFCLASS32) {
+    DwarfOp<uint32_t> op(&dwarf_memory, nullptr);
+    op.GetLogInfo(end - length, end, &lines);
+  } else {
+    DwarfOp<uint64_t> op(&dwarf_memory, nullptr);
+    op.GetLogInfo(end - length, end, &lines);
+  }
+  for (auto& line : lines) {
+    printf("    %s\n", line.c_str());
+  }
+}
+
+void PrintRegInformation(DwarfSection* section, Memory* memory, uint64_t pc, uint8_t class_type) {
+  const DwarfFde* fde = section->GetFdeFromPc(pc);
+  if (fde == nullptr) {
+    printf("  No fde found.\n");
+    return;
+  }
+
+  dwarf_loc_regs_t regs;
+  if (!section->GetCfaLocationInfo(pc, fde, &regs)) {
+    printf("  Cannot get location information.\n");
+    return;
+  }
+
+  std::vector<std::pair<uint32_t, DwarfLocation>> loc_regs;
+  for (auto& loc : regs) {
+    loc_regs.push_back(loc);
+  }
+  std::sort(loc_regs.begin(), loc_regs.end(), [](auto a, auto b) {
+    if (a.first == CFA_REG) {
+      return true;
+    } else if (b.first == CFA_REG) {
+      return false;
+    }
+    return a.first < b.first;
+  });
+
+  for (auto& entry : loc_regs) {
+    const DwarfLocation* loc = &entry.second;
+    if (entry.first == CFA_REG) {
+      printf("  cfa = ");
+    } else {
+      printf("  r%d = ", entry.first);
+    }
+    switch (loc->type) {
+      case DWARF_LOCATION_OFFSET:
+        printf("[cfa ");
+        PrintSignedValue(loc->values[0]);
+        printf("]\n");
+        break;
+
+      case DWARF_LOCATION_VAL_OFFSET:
+        printf("cfa ");
+        PrintSignedValue(loc->values[0]);
+        printf("\n");
+        break;
+
+      case DWARF_LOCATION_REGISTER:
+        printf("r%" PRId64 " ", loc->values[0]);
+        PrintSignedValue(loc->values[1]);
+        printf("\n");
+        break;
+
+      case DWARF_LOCATION_EXPRESSION: {
+        printf("EXPRESSION\n");
+        PrintExpression(memory, class_type, loc->values[1], loc->values[0]);
+        break;
+      }
+
+      case DWARF_LOCATION_VAL_EXPRESSION: {
+        printf("VAL EXPRESSION\n");
+        PrintExpression(memory, class_type, loc->values[1], loc->values[0]);
+        break;
+      }
+
+      case DWARF_LOCATION_UNDEFINED:
+        printf("undefine\n");
+        break;
+
+      case DWARF_LOCATION_INVALID:
+        printf("INVALID\n");
+        break;
+    }
+  }
+}
+
+int GetInfo(const char* file, uint64_t pc) {
+  MemoryFileAtOffset* memory = new MemoryFileAtOffset;
+  if (!memory->Init(file, 0)) {
+    // Initializatation failed.
+    printf("Failed to init\n");
+    return 1;
+  }
+
+  Elf elf(memory);
+  if (!elf.Init(true) || !elf.valid()) {
+    printf("%s is not a valid elf file.\n", file);
+    return 1;
+  }
+
+  ElfInterface* interface = elf.interface();
+  uint64_t load_bias = elf.GetLoadBias();
+  if (pc < load_bias) {
+    printf("PC is less than load bias.\n");
+    return 1;
+  }
+
+  std::string soname;
+  if (elf.GetSoname(&soname)) {
+    printf("Soname: %s\n\n", soname.c_str());
+  }
+
+  printf("PC 0x%" PRIx64 ":\n", pc);
+
+  DwarfSection* section = interface->eh_frame();
+  if (section != nullptr) {
+    printf("\neh_frame:\n");
+    PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+  } else {
+    printf("\nno eh_frame information\n");
+  }
+
+  section = interface->debug_frame();
+  if (section != nullptr) {
+    printf("\ndebug_frame:\n");
+    PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+    printf("\n");
+  } else {
+    printf("\nno debug_frame information\n");
+  }
+
+  // If there is a gnu_debugdata interface, dump the information for that.
+  ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface();
+  if (gnu_debugdata_interface != nullptr) {
+    section = gnu_debugdata_interface->eh_frame();
+    if (section != nullptr) {
+      printf("\ngnu_debugdata (eh_frame):\n");
+      PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
+      printf("\n");
+    } else {
+      printf("\nno gnu_debugdata (eh_frame)\n");
+    }
+
+    section = gnu_debugdata_interface->debug_frame();
+    if (section != nullptr) {
+      printf("\ngnu_debugdata (debug_frame):\n");
+      PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
+      printf("\n");
+    } else {
+      printf("\nno gnu_debugdata (debug_frame)\n");
+    }
+  } else {
+    printf("\nno valid gnu_debugdata information\n");
+  }
+
+  return 0;
+}
+
+}  // namespace unwindstack
+
+int main(int argc, char** argv) {
+  if (argc != 3) {
+    printf("Usage: unwind_reg_info ELF_FILE PC\n");
+    printf("  ELF_FILE\n");
+    printf("    The path to an elf file.\n");
+    printf("  PC\n");
+    printf("    The pc for which the register information should be obtained.\n");
+    return 1;
+  }
+
+  struct stat st;
+  if (stat(argv[1], &st) == -1) {
+    printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+    return 1;
+  }
+  if (!S_ISREG(st.st_mode)) {
+    printf("%s is not a regular file.\n", argv[1]);
+    return 1;
+  }
+
+  uint64_t pc = 0;
+  char* end;
+  pc = strtoull(argv[2], &end, 16);
+  if (*end != '\0') {
+    printf("Malformed OFFSET value: %s\n", argv[2]);
+    return 1;
+  }
+
+  return unwindstack::GetInfo(argv[1], pc);
+}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 697e4cd..086dffe 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -71,6 +71,11 @@
     return 1;
   }
 
+  std::string soname;
+  if (elf.GetSoname(&soname)) {
+    printf("Soname: %s\n\n", soname.c_str());
+  }
+
   switch (elf.machine_type()) {
     case EM_ARM:
       printf("ABI: arm\n");
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index fa0191b..cb8d430 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -64,10 +64,6 @@
 // Some devices fail to send string descriptors if we attempt reading > 255 bytes
 #define MAX_STRING_DESCRIPTOR_LENGTH    255
 
-// From drivers/usb/core/devio.c
-// I don't know why this isn't in a kernel header
-#define MAX_USBFS_BUFFER_SIZE   16384
-
 #define MAX_USBFS_WD_COUNT      10
 
 struct usb_host_context {
@@ -664,10 +660,6 @@
 {
     struct usbdevfs_bulktransfer  ctrl;
 
-    // need to limit request size to avoid EINVAL
-    if (length > MAX_USBFS_BUFFER_SIZE)
-        length = MAX_USBFS_BUFFER_SIZE;
-
     memset(&ctrl, 0, sizeof(ctrl));
     ctrl.ep = endpoint;
     ctrl.len = length;
@@ -727,11 +719,7 @@
 
     urb->status = -1;
     urb->buffer = req->buffer;
-    // need to limit request size to avoid EINVAL
-    if (req->buffer_length > MAX_USBFS_BUFFER_SIZE)
-        urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
-    else
-        urb->buffer_length = req->buffer_length;
+    urb->buffer_length = req->buffer_length;
 
     do {
         res = ioctl(req->dev->fd, USBDEVFS_SUBMITURB, urb);
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 2be5d98..80dcdcb 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -20,12 +20,12 @@
     header_libs: [
         "liblog_headers",
         "libsystem_headers",
-        "libcutils_headers"
+        "libcutils_headers",
     ],
     export_header_lib_headers: [
         "liblog_headers",
         "libsystem_headers",
-        "libcutils_headers"
+        "libcutils_headers",
     ],
     export_include_dirs: ["include"],
 
@@ -52,7 +52,10 @@
     },
     host_supported: true,
 
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     include_dirs: ["external/safe-iop/include"],
     header_libs: [
         "libutils_headers",
@@ -193,7 +196,8 @@
     static_libs: ["libutils"],
     shared_libs: ["liblog"],
     srcs: ["SharedBufferTest.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
-
-subdirs = ["tests"]
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
index a3e7ffe..1390552 100644
--- a/libutils/tests/Android.bp
+++ b/libutils/tests/Android.bp
@@ -77,7 +77,10 @@
     host_supported: true,
     relative_install_path: "libutils_tests",
     srcs: ["Singleton_test1.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
 
 cc_test_library {
@@ -85,6 +88,9 @@
     host_supported: true,
     relative_install_path: "libutils_tests",
     srcs: ["Singleton_test2.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: ["libutils_tests_singleton1"],
 }
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index fec79b7..e73b366 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -3,7 +3,10 @@
 cc_library {
     name: "libvndksupport",
     srcs: ["linker.c"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     local_include_dirs: ["include/vndksupport"],
     export_include_dirs: ["include"],
     shared_libs: ["liblog"],
diff --git a/libvndksupport/tests/Android.bp b/libvndksupport/tests/Android.bp
index 5b467f8..2570cce 100644
--- a/libvndksupport/tests/Android.bp
+++ b/libvndksupport/tests/Android.bp
@@ -17,11 +17,14 @@
     srcs: [
         "linker_test.cpp",
     ],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 
     host_supported: false,
     shared_libs: [
         "libvndksupport",
         "libbase",
-    ]
+    ],
 }
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index b486a17..15471e0 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -615,11 +615,11 @@
     pid_remove(pid);
 
     if (r) {
-        ALOGE("kill(%d): errno=%d", procp->pid, errno);
+        ALOGE("kill(%d): errno=%d", pid, errno);
         return -1;
-    } else {
-        return tasksize;
     }
+
+    return tasksize;
 }
 
 /*
diff --git a/logcat/Android.bp b/logcat/Android.bp
index afc7a01..01beb53 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -68,8 +68,11 @@
     srcs: ["logpersist"],
     init_rc: ["logcatd.rc"],
     required: ["logcatd"],
-    symlinks: ["logpersist.stop", "logpersist.cat"],
+    symlinks: [
+        "logpersist.stop",
+        "logpersist.cat",
+    ],
     strip: {
         none: true,
-    }
+    },
 }
diff --git a/logd/Android.bp b/logd/Android.bp
index 68b79d3..5c79976 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -25,7 +25,7 @@
     "-DAUDITD_LOG_TAG=1003",
     "-DCHATTY_LOG_TAG=1004",
     "-DTAG_DEF_LOG_TAG=1005",
-    "-DLIBLOG_LOG_TAG=1006"
+    "-DLIBLOG_LOG_TAG=1006",
 ]
 
 cc_library_static {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 560f490..b8af2f0 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -198,7 +198,7 @@
 
 int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
                    pid_t tid, const char* msg, unsigned short len) {
-    if ((log_id >= LOG_ID_MAX) || (log_id < 0)) {
+    if (log_id >= LOG_ID_MAX) {
         return -EINVAL;
     }
 
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index ccb1aa6..f163f57 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -49,5 +49,5 @@
     ],
     cflags: [
         "-Werror",
-    ]
+    ],
 }
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index d8163ab..feb100e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -145,13 +145,21 @@
 # $(1): Input source file (ld.config.txt)
 # $(2): Output built module
 # $(3): VNDK version suffix
+# $(4): true if libz must be included in llndk not in vndk-sp
 define update_and_install_ld_config
+# If $(4) is true, move libz to llndk from vndk-sp.
+$(if $(filter true,$(4)),\
+  $(eval llndk_libraries_list := $(LLNDK_LIBRARIES) libz) \
+  $(eval vndksp_libraries_list := $(filter-out libz,$(VNDK_SAMEPROCESS_LIBRARIES))),\
+  $(eval llndk_libraries_list := $(LLNDK_LIBRARIES)) \
+  $(eval vndksp_libraries_list := $(VNDK_SAMEPROCESS_LIBRARIES)))
+
 llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
-  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(LLNDK_LIBRARIES))))
+  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(llndk_libraries_list))))
 private_llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
-  $(filter $(VNDK_PRIVATE_LIBRARIES),$(LLNDK_LIBRARIES))))
+  $(filter $(VNDK_PRIVATE_LIBRARIES),$(llndk_libraries_list))))
 vndk_sameprocess_libraries := $(call normalize-path-list,$(addsuffix .so,\
-  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_SAMEPROCESS_LIBRARIES))))
+  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(vndksp_libraries_list))))
 vndk_core_libraries := $(call normalize-path-list,$(addsuffix .so,\
   $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_CORE_LIBRARIES))))
 sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
@@ -180,6 +188,8 @@
 	$$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $$@
 	$$(hide) sed -i -e 's?%VNDK_VER%?$$(PRIVATE_VNDK_VERSION)?g' $$@
 
+llndk_libraries_list :=
+vndksp_libraries_list :=
 llndk_libraries :=
 private_llndk_libraries :=
 vndk_sameprocess_libraries :=
@@ -192,11 +202,12 @@
 # ld.config.txt
 #
 # For VNDK enforced devices that have defined BOARD_VNDK_VERSION, use
-# "ld.config.txt.in" as a source file. This configuration includes strict VNDK
+# "ld.config.txt" as a source file. This configuration includes strict VNDK
 # run-time restrictions for vendor process.
 # Other treblized devices, that have not defined BOARD_VNDK_VERSION or that
-# have set BOARD_VNDK_RUNTIME_DISABLE to true, use "ld.config.txt" as a source
-# file. This configuration does not have strict VNDK run-time restrictions.
+# have set BOARD_VNDK_RUNTIME_DISABLE to true, use "ld.config.vndk_lite.txt"
+# as a source file. This configuration does not have strict VNDK run-time
+# restrictions.
 # If the device is not treblized, use "ld.config.legacy.txt" for legacy
 # namespace configuration.
 include $(CLEAR_VARS)
@@ -216,7 +227,7 @@
 LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
 $(eval $(call update_and_install_ld_config,\
-  $(LOCAL_PATH)/etc/ld.config.txt.in,\
+  $(LOCAL_PATH)/etc/ld.config.txt,\
   $(LOCAL_BUILT_MODULE),\
   $(PLATFORM_VNDK_VERSION)))
 
@@ -225,9 +236,10 @@
 LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
 $(eval $(call update_and_install_ld_config,\
-  $(LOCAL_PATH)/etc/ld.config.txt,\
+  $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt,\
   $(LOCAL_BUILT_MODULE),\
-  $(if $(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION))))
+  $(if $(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION)),\
+  true))
 
 else
 # for legacy non-treblized devices
@@ -244,9 +256,9 @@
 #
 # This file is a temporary configuration file only for GSI. Originally GSI has
 # BOARD_VNDK_VERSION defined and has strict VNDK enforcing rule based on
-# "ld.config.txt.in". However for the devices, that have not defined
+# "ld.config.txt". However for the devices, that have not defined
 # BOARD_VNDK_VERSION, GSI provides this configuration file which is based on
-# "ld.config.txt".
+# "ld.config.vndk_lite.txt".
 # Do not install this file for the devices other than GSI.
 include $(CLEAR_VARS)
 LOCAL_MODULE := ld.config.noenforce.txt
@@ -255,9 +267,10 @@
 LOCAL_MODULE_STEM := $(LOCAL_MODULE)
 include $(BUILD_SYSTEM)/base_rules.mk
 $(eval $(call update_and_install_ld_config,\
-  $(LOCAL_PATH)/etc/ld.config.txt,\
+  $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt,\
   $(LOCAL_BUILT_MODULE),\
-  $(PLATFORM_VNDK_VERSION)))
+  $(PLATFORM_VNDK_VERSION),\
+  true))
 
 #######################################
 # llndk.libraries.txt
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 5d97a73..c8d87c8 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -30,21 +30,55 @@
 ###############################################################################
 # "default" namespace
 #
-# Framework-side code runs in this namespace. However, libs from other
-# partitions are also allowed temporarily.
+# Framework-side code runs in this namespace. Libs from /vendor partition
+# can't be loaded in this namespace.
 ###############################################################################
-namespace.default.isolated = false
+namespace.default.isolated = true
 
-namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /odm/${LIB}
-namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths = /system/${LIB}
+
+# We can't have entire /system/${LIB} as permitted paths because doing so
+# makes it possible to load libs in /system/${LIB}/vndk* directories by
+# their absolute paths (e.g. dlopen("/system/lib/vndk/libbase.so");).
+# VNDK libs are built with previous versions of Android and thus must not be
+# loaded into this namespace where libs built with the current version of
+# Android are loaded. Mixing the two types of libs in the same namespace can
+# cause unexpected problem.
+namespace.default.permitted.paths  = /system/${LIB}/drm
+namespace.default.permitted.paths += /system/${LIB}/extractors
+namespace.default.permitted.paths += /system/${LIB}/hw
+# These are where odex files are located. libart has to be able to dlopen the files
+namespace.default.permitted.paths += /system/framework
+namespace.default.permitted.paths += /system/app
+namespace.default.permitted.paths += /system/priv-app
+namespace.default.permitted.paths += /vendor/framework
+namespace.default.permitted.paths += /vendor/app
+namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /oem/app
+namespace.default.permitted.paths += /product/framework
+namespace.default.permitted.paths += /product/app
+namespace.default.permitted.paths += /product/priv-app
+namespace.default.permitted.paths += /data
+namespace.default.permitted.paths += /mnt/expand
 
 namespace.default.asan.search.paths  = /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/odm/${LIB}
-namespace.default.asan.search.paths +=           /odm/${LIB}
-namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
-namespace.default.asan.search.paths +=           /vendor/${LIB}
+
+namespace.default.asan.permitted.paths  = /data
+namespace.default.asan.permitted.paths += /system/${LIB}/drm
+namespace.default.asan.permitted.paths += /system/${LIB}/extractors
+namespace.default.asan.permitted.paths += /system/${LIB}/hw
+namespace.default.asan.permitted.paths += /system/framework
+namespace.default.asan.permitted.paths += /system/app
+namespace.default.asan.permitted.paths += /system/priv-app
+namespace.default.asan.permitted.paths += /vendor/framework
+namespace.default.asan.permitted.paths += /vendor/app
+namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /oem/app
+namespace.default.asan.permitted.paths += /product/framework
+namespace.default.asan.permitted.paths += /product/app
+namespace.default.asan.permitted.paths += /product/priv-app
+namespace.default.asan.permitted.paths += /mnt/expand
 
 ###############################################################################
 # "sphal" namespace
@@ -82,35 +116,10 @@
 # libs listed here can be used.
 namespace.sphal.links = default,vndk,rs
 
-# WARNING: only NDK libs can be listed here.
-namespace.sphal.link.default.shared_libs  = libc.so
-namespace.sphal.link.default.shared_libs += libEGL.so
-namespace.sphal.link.default.shared_libs += libGLESv1_CM.so
-namespace.sphal.link.default.shared_libs += libGLESv2.so
-namespace.sphal.link.default.shared_libs += libdl.so
-namespace.sphal.link.default.shared_libs += liblog.so
-namespace.sphal.link.default.shared_libs += libm.so
-namespace.sphal.link.default.shared_libs += libnativewindow.so
-namespace.sphal.link.default.shared_libs += libstdc++.so
-namespace.sphal.link.default.shared_libs += libsync.so
-namespace.sphal.link.default.shared_libs += libvndksupport.so
-namespace.sphal.link.default.shared_libs += libz.so
+namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
-# WARNING: only VNDK-SP libs can be listed here. DO NOT EDIT this line.
-namespace.sphal.link.vndk.shared_libs  = android.hardware.renderscript@1.0.so
-namespace.sphal.link.vndk.shared_libs += android.hardware.graphics.common@1.0.so
-namespace.sphal.link.vndk.shared_libs += android.hardware.graphics.mapper@2.0.so
-namespace.sphal.link.vndk.shared_libs += android.hidl.memory@1.0.so
-namespace.sphal.link.vndk.shared_libs += libbase.so
-namespace.sphal.link.vndk.shared_libs += libc++.so
-namespace.sphal.link.vndk.shared_libs += libcutils.so
-namespace.sphal.link.vndk.shared_libs += libhardware.so
-namespace.sphal.link.vndk.shared_libs += libhidlbase.so
-namespace.sphal.link.vndk.shared_libs += libhidlmemory.so
-namespace.sphal.link.vndk.shared_libs += libhidltransport.so
-namespace.sphal.link.vndk.shared_libs += libhwbinder.so
-namespace.sphal.link.vndk.shared_libs += libion.so
-namespace.sphal.link.vndk.shared_libs += libutils.so
+namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
 
 # Renderscript gets separate namespace
 namespace.sphal.link.rs.shared_libs = libRS_internal.so
@@ -155,38 +164,13 @@
 
 namespace.rs.links = default,vndk
 
-namespace.rs.link.default.shared_libs  = libc.so
-namespace.rs.link.default.shared_libs += libEGL.so
-namespace.rs.link.default.shared_libs += libGLESv1_CM.so
-namespace.rs.link.default.shared_libs += libGLESv2.so
-namespace.rs.link.default.shared_libs += libdl.so
-namespace.rs.link.default.shared_libs += liblog.so
-namespace.rs.link.default.shared_libs += libm.so
-namespace.rs.link.default.shared_libs += libnativewindow.so
-namespace.rs.link.default.shared_libs += libstdc++.so
-namespace.rs.link.default.shared_libs += libsync.so
-namespace.rs.link.default.shared_libs += libvndksupport.so
-namespace.rs.link.default.shared_libs += libz.so
-# These two libs are private LLNDK libs but are exceptionally visible
-# in this 'rs' namespace because RenderScript framework libraries
-# which are loaded into this namespace are using them.
-namespace.rs.link.default.shared_libs += libft2.so
-namespace.rs.link.default.shared_libs += libmediandk.so
+namespace.rs.link.default.shared_libs  =  %LLNDK_LIBRARIES%
+namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+# Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
+# namespace because RS framework libs are using them.
+namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
 
-namespace.rs.link.vndk.shared_libs  = android.hardware.renderscript@1.0.so
-namespace.rs.link.vndk.shared_libs += android.hardware.graphics.common@1.0.so
-namespace.rs.link.vndk.shared_libs += android.hardware.graphics.mapper@2.0.so
-namespace.rs.link.vndk.shared_libs += android.hidl.memory@1.0.so
-namespace.rs.link.vndk.shared_libs += libbase.so
-namespace.rs.link.vndk.shared_libs += libc++.so
-namespace.rs.link.vndk.shared_libs += libcutils.so
-namespace.rs.link.vndk.shared_libs += libhardware.so
-namespace.rs.link.vndk.shared_libs += libhidlbase.so
-namespace.rs.link.vndk.shared_libs += libhidlmemory.so
-namespace.rs.link.vndk.shared_libs += libhidltransport.so
-namespace.rs.link.vndk.shared_libs += libhwbinder.so
-namespace.rs.link.vndk.shared_libs += libion.so
-namespace.rs.link.vndk.shared_libs += libutils.so
+namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
 
 ###############################################################################
 # "vndk" namespace
@@ -204,6 +188,8 @@
 namespace.vndk.permitted.paths += /odm/${LIB}/egl
 namespace.vndk.permitted.paths += /vendor/${LIB}/hw
 namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
+namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
 
 namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
 namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
@@ -221,21 +207,15 @@
 namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
 namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/egl
 
+namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%/hw
+namespace.vndk.asan.permitted.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%/hw
+
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the default namespace. This is possible since their ABI is stable across
 # Android releases.
 namespace.vndk.links = default
-namespace.vndk.link.default.shared_libs  = android.hidl.memory@1.0-impl.so
-namespace.vndk.link.default.shared_libs += libEGL.so
-namespace.vndk.link.default.shared_libs += libc.so
-namespace.vndk.link.default.shared_libs += libdl.so
-namespace.vndk.link.default.shared_libs += liblog.so
-namespace.vndk.link.default.shared_libs += libm.so
-namespace.vndk.link.default.shared_libs += libnativewindow.so
-namespace.vndk.link.default.shared_libs += libstdc++.so
-namespace.vndk.link.default.shared_libs += libsync.so
-namespace.vndk.link.default.shared_libs += libvndksupport.so
-namespace.vndk.link.default.shared_libs += libz.so
+namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
 ###############################################################################
 # Namespace config for vendor processes. In O, no restriction is enforced for
@@ -244,35 +224,88 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
-namespace.default.isolated = false
+additional.namespaces = system,vndk
+
+###############################################################################
+# "default" namespace
+#
+# This is the default linker namespace for a vendor process (a process started
+# from /vendor/bin/*). The main executable and the libs under /vendor/lib[64]
+# are loaded directly into this namespace. However, other libs under the system
+# partition (VNDK and LLNDK libraries) are not loaded here but from the
+# separate namespace 'system'. The delegation to the system namespace is done
+# via the 'namespace.default.link.system.shared_libs' property below.
+###############################################################################
+namespace.default.isolated = true
+namespace.default.visible = true
 
 namespace.default.search.paths  = /odm/${LIB}
-namespace.default.search.paths += /odm/${LIB}/vndk
-namespace.default.search.paths += /odm/${LIB}/vndk-sp
 namespace.default.search.paths += /vendor/${LIB}
-namespace.default.search.paths += /vendor/${LIB}/vndk
-namespace.default.search.paths += /vendor/${LIB}/vndk-sp
 
-# Access to system libraries are allowed
-namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
-namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
-namespace.default.search.paths += /system/${LIB}
+namespace.default.permitted.paths  = /odm
+namespace.default.permitted.paths += /vendor
 
 namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.default.asan.search.paths +=           /odm/${LIB}
-namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk
-namespace.default.asan.search.paths +=           /odm/${LIB}/vndk
-namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp
-namespace.default.asan.search.paths +=           /odm/${LIB}/vndk-sp
 namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
 namespace.default.asan.search.paths +=           /vendor/${LIB}
-namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk
-namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk
-namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
-namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk-sp
-namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
-namespace.default.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
-namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
-namespace.default.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
-namespace.default.asan.search.paths += /data/asan/system/${LIB}
-namespace.default.asan.search.paths +=           /system/${LIB}
+
+namespace.default.asan.permitted.paths  = /data/asan/odm
+namespace.default.asan.permitted.paths +=           /odm
+namespace.default.asan.permitted.paths += /data/asan/vendor
+namespace.default.asan.permitted.paths +=           /vendor
+
+namespace.default.links = system,vndk
+namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
+namespace.default.link.vndk.shared_libs  = %VNDK_SAMEPROCESS_LIBRARIES%
+namespace.default.link.vndk.shared_libs += %VNDK_CORE_LIBRARIES%
+
+###############################################################################
+# "vndk" namespace
+#
+# This namespace is where VNDK and VNDK-SP libraries are loaded for
+# a vendor process.
+###############################################################################
+namespace.vndk.isolated = false
+
+namespace.vndk.search.paths  = /odm/${LIB}/vndk
+namespace.vndk.search.paths += /odm/${LIB}/vndk-sp
+namespace.vndk.search.paths += /vendor/${LIB}/vndk
+namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.search.paths += /system/${LIB}/vndk%VNDK_VER%
+
+namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk
+namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk
+namespace.vndk.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk
+namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
+namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
+
+# When these NDK libs are required inside this namespace, then it is redirected
+# to the system namespace. This is possible since their ABI is stable across
+# Android releases.
+namespace.vndk.links = system,default
+namespace.vndk.link.system.shared_libs  = %LLNDK_LIBRARIES%
+namespace.vndk.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.vndk.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "system" namespace
+#
+# This namespace is where system libs (VNDK and LLNDK libs) are loaded for
+# a vendor process.
+###############################################################################
+namespace.system.isolated = false
+
+namespace.system.search.paths = /system/${LIB}
+
+namespace.system.asan.search.paths  = /data/asan/system/${LIB}
+namespace.system.asan.search.paths +=           /system/${LIB}
diff --git a/rootdir/etc/ld.config.txt.in b/rootdir/etc/ld.config.txt.in
deleted file mode 100644
index c8d87c8..0000000
--- a/rootdir/etc/ld.config.txt.in
+++ /dev/null
@@ -1,311 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Bionic loader config file.
-#
-
-# Don't change the order here. The first pattern that matches with the
-# absolute path of an executable is selected.
-dir.system = /system/bin/
-dir.system = /system/xbin/
-
-dir.vendor = /odm/bin/
-dir.vendor = /vendor/bin/
-dir.vendor = /data/nativetest/odm
-dir.vendor = /data/nativetest64/odm
-dir.vendor = /data/benchmarktest/odm
-dir.vendor = /data/benchmarktest64/odm
-dir.vendor = /data/nativetest/vendor
-dir.vendor = /data/nativetest64/vendor
-dir.vendor = /data/benchmarktest/vendor
-dir.vendor = /data/benchmarktest64/vendor
-
-dir.system = /data/nativetest
-dir.system = /data/nativetest64
-dir.system = /data/benchmarktest
-dir.system = /data/benchmarktest64
-
-[system]
-additional.namespaces = sphal,vndk,rs
-
-###############################################################################
-# "default" namespace
-#
-# Framework-side code runs in this namespace. Libs from /vendor partition
-# can't be loaded in this namespace.
-###############################################################################
-namespace.default.isolated = true
-
-namespace.default.search.paths = /system/${LIB}
-
-# We can't have entire /system/${LIB} as permitted paths because doing so
-# makes it possible to load libs in /system/${LIB}/vndk* directories by
-# their absolute paths (e.g. dlopen("/system/lib/vndk/libbase.so");).
-# VNDK libs are built with previous versions of Android and thus must not be
-# loaded into this namespace where libs built with the current version of
-# Android are loaded. Mixing the two types of libs in the same namespace can
-# cause unexpected problem.
-namespace.default.permitted.paths  = /system/${LIB}/drm
-namespace.default.permitted.paths += /system/${LIB}/extractors
-namespace.default.permitted.paths += /system/${LIB}/hw
-# These are where odex files are located. libart has to be able to dlopen the files
-namespace.default.permitted.paths += /system/framework
-namespace.default.permitted.paths += /system/app
-namespace.default.permitted.paths += /system/priv-app
-namespace.default.permitted.paths += /vendor/framework
-namespace.default.permitted.paths += /vendor/app
-namespace.default.permitted.paths += /vendor/priv-app
-namespace.default.permitted.paths += /oem/app
-namespace.default.permitted.paths += /product/framework
-namespace.default.permitted.paths += /product/app
-namespace.default.permitted.paths += /product/priv-app
-namespace.default.permitted.paths += /data
-namespace.default.permitted.paths += /mnt/expand
-
-namespace.default.asan.search.paths  = /data/asan/system/${LIB}
-namespace.default.asan.search.paths +=           /system/${LIB}
-
-namespace.default.asan.permitted.paths  = /data
-namespace.default.asan.permitted.paths += /system/${LIB}/drm
-namespace.default.asan.permitted.paths += /system/${LIB}/extractors
-namespace.default.asan.permitted.paths += /system/${LIB}/hw
-namespace.default.asan.permitted.paths += /system/framework
-namespace.default.asan.permitted.paths += /system/app
-namespace.default.asan.permitted.paths += /system/priv-app
-namespace.default.asan.permitted.paths += /vendor/framework
-namespace.default.asan.permitted.paths += /vendor/app
-namespace.default.asan.permitted.paths += /vendor/priv-app
-namespace.default.asan.permitted.paths += /oem/app
-namespace.default.asan.permitted.paths += /product/framework
-namespace.default.asan.permitted.paths += /product/app
-namespace.default.asan.permitted.paths += /product/priv-app
-namespace.default.asan.permitted.paths += /mnt/expand
-
-###############################################################################
-# "sphal" namespace
-#
-# SP-HAL(Sameprocess-HAL)s are the only vendor libraries that are allowed to be
-# loaded inside system processes. libEGL_<chipset>.so, libGLESv2_<chipset>.so,
-# android.hardware.graphics.mapper@2.0-impl.so, etc are SP-HALs.
-#
-# This namespace is exclusivly for SP-HALs. When the framework tries to dynami-
-# cally load SP-HALs, android_dlopen_ext() is used to explicitly specifying
-# that they should be searched and loaded from this namespace.
-#
-# Note that there is no link from the default namespace to this namespace.
-###############################################################################
-namespace.sphal.isolated = true
-namespace.sphal.visible = true
-
-namespace.sphal.search.paths  = /odm/${LIB}
-namespace.sphal.search.paths += /vendor/${LIB}
-
-namespace.sphal.permitted.paths  = /odm/${LIB}
-namespace.sphal.permitted.paths += /vendor/${LIB}
-
-namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
-namespace.sphal.asan.search.paths +=           /odm/${LIB}
-namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}
-namespace.sphal.asan.search.paths +=           /vendor/${LIB}
-
-namespace.sphal.asan.permitted.paths  = /data/asan/odm/${LIB}
-namespace.sphal.asan.permitted.paths +=           /odm/${LIB}
-namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}
-namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}
-
-# Once in this namespace, access to libraries in /system/lib is restricted. Only
-# libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
-
-namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
-namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-
-# Renderscript gets separate namespace
-namespace.sphal.link.rs.shared_libs = libRS_internal.so
-
-###############################################################################
-# "rs" namespace
-#
-# This namespace is exclusively for Renderscript internal libraries.
-# This namespace has slightly looser restriction than the vndk namespace because
-# of the genuine characteristics of Renderscript; /data is in the permitted path
-# to load the compiled *.so file and libmediandk.so can be used here.
-###############################################################################
-namespace.rs.isolated = true
-namespace.rs.visible = true
-
-namespace.rs.search.paths  = /odm/${LIB}/vndk-sp
-namespace.rs.search.paths += /vendor/${LIB}/vndk-sp
-namespace.rs.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
-namespace.rs.search.paths += /odm/${LIB}
-namespace.rs.search.paths += /vendor/${LIB}
-
-namespace.rs.permitted.paths  = /odm/${LIB}
-namespace.rs.permitted.paths += /vendor/${LIB}
-namespace.rs.permitted.paths += /data
-
-namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
-namespace.rs.asan.search.paths +=           /odm/${LIB}/vndk-sp
-namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
-namespace.rs.asan.search.paths +=           /vendor/${LIB}/vndk-sp
-namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
-namespace.rs.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
-namespace.rs.asan.search.paths += /data/asan/odm/${LIB}
-namespace.rs.asan.search.paths +=           /odm/${LIB}
-namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}
-namespace.rs.asan.search.paths +=           /vendor/${LIB}
-
-namespace.rs.asan.permitted.paths  = /data/asan/odm/${LIB}
-namespace.rs.asan.permitted.paths +=           /odm/${LIB}
-namespace.rs.asan.permitted.paths += /data/asan/vendor/${LIB}
-namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
-namespace.rs.asan.permitted.paths += /data
-
-namespace.rs.links = default,vndk
-
-namespace.rs.link.default.shared_libs  =  %LLNDK_LIBRARIES%
-namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-# Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
-# namespace because RS framework libs are using them.
-namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
-
-namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-
-###############################################################################
-# "vndk" namespace
-#
-# This namespace is exclusively for vndk-sp libs.
-###############################################################################
-namespace.vndk.isolated = true
-namespace.vndk.visible = true
-
-namespace.vndk.search.paths  = /odm/${LIB}/vndk-sp
-namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
-namespace.vndk.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
-
-namespace.vndk.permitted.paths  = /odm/${LIB}/hw
-namespace.vndk.permitted.paths += /odm/${LIB}/egl
-namespace.vndk.permitted.paths += /vendor/${LIB}/hw
-namespace.vndk.permitted.paths += /vendor/${LIB}/egl
-# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
-namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
-
-namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
-namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
-
-namespace.vndk.asan.permitted.paths  = /data/asan/odm/${LIB}/hw
-namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/hw
-namespace.vndk.asan.permitted.paths += /data/asan/odm/${LIB}/egl
-namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/egl
-namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/hw
-namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/hw
-namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
-namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/egl
-
-namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%/hw
-namespace.vndk.asan.permitted.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%/hw
-
-# When these NDK libs are required inside this namespace, then it is redirected
-# to the default namespace. This is possible since their ABI is stable across
-# Android releases.
-namespace.vndk.links = default
-namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
-namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-###############################################################################
-# Namespace config for vendor processes. In O, no restriction is enforced for
-# them. However, in O-MR1, access to /system/${LIB} will not be allowed to
-# the default namespace. 'system' namespace will be added to give limited
-# (LL-NDK only) access.
-###############################################################################
-[vendor]
-additional.namespaces = system,vndk
-
-###############################################################################
-# "default" namespace
-#
-# This is the default linker namespace for a vendor process (a process started
-# from /vendor/bin/*). The main executable and the libs under /vendor/lib[64]
-# are loaded directly into this namespace. However, other libs under the system
-# partition (VNDK and LLNDK libraries) are not loaded here but from the
-# separate namespace 'system'. The delegation to the system namespace is done
-# via the 'namespace.default.link.system.shared_libs' property below.
-###############################################################################
-namespace.default.isolated = true
-namespace.default.visible = true
-
-namespace.default.search.paths  = /odm/${LIB}
-namespace.default.search.paths += /vendor/${LIB}
-
-namespace.default.permitted.paths  = /odm
-namespace.default.permitted.paths += /vendor
-
-namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
-namespace.default.asan.search.paths +=           /odm/${LIB}
-namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
-namespace.default.asan.search.paths +=           /vendor/${LIB}
-
-namespace.default.asan.permitted.paths  = /data/asan/odm
-namespace.default.asan.permitted.paths +=           /odm
-namespace.default.asan.permitted.paths += /data/asan/vendor
-namespace.default.asan.permitted.paths +=           /vendor
-
-namespace.default.links = system,vndk
-namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
-namespace.default.link.vndk.shared_libs  = %VNDK_SAMEPROCESS_LIBRARIES%
-namespace.default.link.vndk.shared_libs += %VNDK_CORE_LIBRARIES%
-
-###############################################################################
-# "vndk" namespace
-#
-# This namespace is where VNDK and VNDK-SP libraries are loaded for
-# a vendor process.
-###############################################################################
-namespace.vndk.isolated = false
-
-namespace.vndk.search.paths  = /odm/${LIB}/vndk
-namespace.vndk.search.paths += /odm/${LIB}/vndk-sp
-namespace.vndk.search.paths += /vendor/${LIB}/vndk
-namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
-namespace.vndk.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
-namespace.vndk.search.paths += /system/${LIB}/vndk%VNDK_VER%
-
-namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk
-namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk
-namespace.vndk.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk
-namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk
-namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
-namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
-namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
-namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
-
-# When these NDK libs are required inside this namespace, then it is redirected
-# to the system namespace. This is possible since their ABI is stable across
-# Android releases.
-namespace.vndk.links = system,default
-namespace.vndk.link.system.shared_libs  = %LLNDK_LIBRARIES%
-namespace.vndk.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-namespace.vndk.link.default.allow_all_shared_libs = true
-
-###############################################################################
-# "system" namespace
-#
-# This namespace is where system libs (VNDK and LLNDK libs) are loaded for
-# a vendor process.
-###############################################################################
-namespace.system.isolated = false
-
-namespace.system.search.paths = /system/${LIB}
-
-namespace.system.asan.search.paths  = /data/asan/system/${LIB}
-namespace.system.asan.search.paths +=           /system/${LIB}
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
new file mode 100644
index 0000000..5256cb1
--- /dev/null
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -0,0 +1,224 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Bionic loader config file.
+#
+
+# Don't change the order here. The first pattern that matches with the
+# absolute path of an executable is selected.
+dir.system = /system/bin/
+dir.system = /system/xbin/
+
+dir.vendor = /odm/bin/
+dir.vendor = /vendor/bin/
+dir.vendor = /data/nativetest/odm
+dir.vendor = /data/nativetest64/odm
+dir.vendor = /data/benchmarktest/odm
+dir.vendor = /data/benchmarktest64/odm
+dir.vendor = /data/nativetest/vendor
+dir.vendor = /data/nativetest64/vendor
+dir.vendor = /data/benchmarktest/vendor
+dir.vendor = /data/benchmarktest64/vendor
+
+dir.system = /data/nativetest
+dir.system = /data/nativetest64
+dir.system = /data/benchmarktest
+dir.system = /data/benchmarktest64
+
+[system]
+additional.namespaces = sphal,vndk,rs
+
+###############################################################################
+# "default" namespace
+#
+# Framework-side code runs in this namespace. However, libs from other
+# partitions are also allowed temporarily.
+###############################################################################
+namespace.default.isolated = false
+
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+
+namespace.default.asan.search.paths  = /data/asan/system/${LIB}
+namespace.default.asan.search.paths +=           /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths +=           /vendor/${LIB}
+
+###############################################################################
+# "sphal" namespace
+#
+# SP-HAL(Sameprocess-HAL)s are the only vendor libraries that are allowed to be
+# loaded inside system processes. libEGL_<chipset>.so, libGLESv2_<chipset>.so,
+# android.hardware.graphics.mapper@2.0-impl.so, etc are SP-HALs.
+#
+# This namespace is exclusivly for SP-HALs. When the framework tries to dynami-
+# cally load SP-HALs, android_dlopen_ext() is used to explicitly specifying
+# that they should be searched and loaded from this namespace.
+#
+# Note that there is no link from the default namespace to this namespace.
+###############################################################################
+namespace.sphal.isolated = true
+namespace.sphal.visible = true
+
+namespace.sphal.search.paths  = /odm/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}
+
+namespace.sphal.permitted.paths  = /odm/${LIB}
+namespace.sphal.permitted.paths += /vendor/${LIB}
+
+namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.search.paths +=           /odm/${LIB}
+namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.search.paths +=           /vendor/${LIB}
+
+namespace.sphal.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.permitted.paths +=           /odm/${LIB}
+namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}
+
+# Once in this namespace, access to libraries in /system/lib is restricted. Only
+# libs listed here can be used.
+namespace.sphal.links = default,vndk,rs
+
+namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
+
+###############################################################################
+# "rs" namespace
+#
+# This namespace is exclusively for Renderscript internal libraries.
+# This namespace has slightly looser restriction than the vndk namespace because
+# of the genuine characteristics of Renderscript; /data is in the permitted path
+# to load the compiled *.so file and libmediandk.so can be used here.
+###############################################################################
+namespace.rs.isolated = true
+namespace.rs.visible = true
+
+namespace.rs.search.paths  = /odm/${LIB}/vndk-sp
+namespace.rs.search.paths += /vendor/${LIB}/vndk-sp
+namespace.rs.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.search.paths += /odm/${LIB}
+namespace.rs.search.paths += /vendor/${LIB}
+
+namespace.rs.permitted.paths  = /odm/${LIB}
+namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /data
+
+namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
+namespace.rs.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.rs.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.asan.search.paths += /data/asan/odm/${LIB}
+namespace.rs.asan.search.paths +=           /odm/${LIB}
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.search.paths +=           /vendor/${LIB}
+
+namespace.rs.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.rs.asan.permitted.paths +=           /odm/${LIB}
+namespace.rs.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
+namespace.rs.asan.permitted.paths += /data
+
+namespace.rs.links = default,vndk
+
+namespace.rs.link.default.shared_libs  =  %LLNDK_LIBRARIES%
+namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+# Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
+# namespace because RS framework libs are using them.
+namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
+
+namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
+
+###############################################################################
+# "vndk" namespace
+#
+# This namespace is exclusively for vndk-sp libs.
+###############################################################################
+namespace.vndk.isolated = true
+namespace.vndk.visible = true
+
+namespace.vndk.search.paths  = /odm/${LIB}/vndk-sp
+namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+
+namespace.vndk.permitted.paths  = /odm/${LIB}/hw
+namespace.vndk.permitted.paths += /odm/${LIB}/egl
+namespace.vndk.permitted.paths += /vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
+namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
+
+namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
+
+namespace.vndk.asan.permitted.paths  = /data/asan/odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
+namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/egl
+
+namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%/hw
+namespace.vndk.asan.permitted.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%/hw
+
+# When these NDK libs are required inside this namespace, then it is redirected
+# to the default namespace. This is possible since their ABI is stable across
+# Android releases.
+namespace.vndk.links = default
+namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# Namespace config for vendor processes. In O, no restriction is enforced for
+# them. However, in O-MR1, access to /system/${LIB} will not be allowed to
+# the default namespace. 'system' namespace will be added to give limited
+# (LL-NDK only) access.
+###############################################################################
+[vendor]
+namespace.default.isolated = false
+
+namespace.default.search.paths  = /odm/${LIB}
+namespace.default.search.paths += /odm/${LIB}/vndk
+namespace.default.search.paths += /odm/${LIB}/vndk-sp
+namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /vendor/${LIB}/vndk
+namespace.default.search.paths += /vendor/${LIB}/vndk-sp
+
+# Access to system libraries are allowed
+namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
+namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.default.search.paths += /system/${LIB}
+
+namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk
+namespace.default.asan.search.paths +=           /odm/${LIB}/vndk
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp
+namespace.default.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths +=           /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk
+namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
+namespace.default.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.default.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.default.asan.search.paths += /data/asan/system/${LIB}
+namespace.default.asan.search.paths +=           /system/${LIB}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6a1872f..a213ffb 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -28,12 +28,12 @@
     restorecon /postinstall
 
     # Mount cgroup mount point for cpu accounting
-    mount cgroup none /acct cpuacct
+    mount cgroup none /acct nodev noexec nosuid cpuacct
     mkdir /acct/uid
 
     # root memory control cgroup, used by lmkd
     mkdir /dev/memcg 0700 root system
-    mount cgroup none /dev/memcg memory
+    mount cgroup none /dev/memcg nodev noexec nosuid memory
     # app mem cgroups, used by activity manager, lmkd and zygote
     mkdir /dev/memcg/apps/ 0755 system system
     # cgroup for system_server and surfaceflinger
@@ -59,7 +59,7 @@
 
     # Create energy-aware scheduler tuning nodes
     mkdir /dev/stune
-    mount cgroup none /dev/stune schedtune
+    mount cgroup none /dev/stune nodev noexec nosuid schedtune
     mkdir /dev/stune/foreground
     mkdir /dev/stune/background
     mkdir /dev/stune/top-app
@@ -82,10 +82,10 @@
 
     # Mount staging areas for devices managed by vold
     # See storage config details at http://source.android.com/tech/storage/
-    mount tmpfs tmpfs /mnt mode=0755,uid=0,gid=1000
+    mount tmpfs tmpfs /mnt nodev noexec nosuid mode=0755,uid=0,gid=1000
     restorecon_recursive /mnt
 
-    mount configfs none /config
+    mount configfs none /config nodev noexec nosuid
     chmod 0775 /config/sdcardfs
     chown system package_info /config/sdcardfs
 
@@ -155,7 +155,7 @@
 
     # Create cgroup mount points for process groups
     mkdir /dev/cpuctl
-    mount cgroup none /dev/cpuctl cpu
+    mount cgroup none /dev/cpuctl nodev noexec nosuid cpu
     chown system system /dev/cpuctl
     chown system system /dev/cpuctl/tasks
     chmod 0666 /dev/cpuctl/tasks
@@ -164,7 +164,7 @@
 
     # sets up initial cpusets for ActivityManager
     mkdir /dev/cpuset
-    mount cpuset none /dev/cpuset
+    mount cpuset none /dev/cpuset nodev noexec nosuid
 
     # this ensures that the cpusets are present and usable, but the device's
     # init.rc must actually set the correct cpus
@@ -219,17 +219,17 @@
     chmod 0644 /dev/xt_qtaguid
 
     mkdir /dev/cg2_bpf
-    mount cgroup2 cg2_bpf /dev/cg2_bpf
+    mount cgroup2 cg2_bpf /dev/cg2_bpf nodev noexec nosuid
     chown root root /dev/cg2_bpf
     chmod 0600 /dev/cg2_bpf
-    mount bpf bpf /sys/fs/bpf
+    mount bpf bpf /sys/fs/bpf nodev noexec nosuid
 
     # Create location for fs_mgr to store abbreviated output from filesystem
     # checker programs.
     mkdir /dev/fscklogs 0770 root system
 
     # pstore/ramoops previous console log
-    mount pstore pstore /sys/fs/pstore
+    mount pstore pstore /sys/fs/pstore nodev noexec nosuid
     chown system log /sys/fs/pstore/console-ramoops
     chmod 0440 /sys/fs/pstore/console-ramoops
     chown system log /sys/fs/pstore/console-ramoops-0
@@ -318,8 +318,9 @@
     start hwservicemanager
     start vndservicemanager
 
-    # once everything is setup, no need to modify /
-    mount rootfs rootfs / ro remount
+    # Once everything is setup, no need to modify /.
+    # The bind+ro combination avoids modifying any other mount flags.
+    mount rootfs rootfs / remount bind ro
     # Mount shared so changes propagate into child namespaces
     mount rootfs rootfs / shared rec
     # Mount default storage into root namespace
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 3168f40..f0681d2 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -18,10 +18,6 @@
     disabled
     seclabel u:r:adbd:s0
 
-# adbd on at boot in emulator
-on property:ro.kernel.qemu=1
-    start adbd
-
 on boot
     setprop sys.usb.configfs 0
 
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 322a63d..5428e73 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -31,7 +31,10 @@
         "trusty_keymaster_ipc.cpp",
         "trusty_keymaster_main.cpp",
     ],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: [
         "libcrypto",
         "libcutils",
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index c48deed..f6e9bee 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -22,7 +22,10 @@
 
     srcs: ["trusty.c"],
     export_include_dirs: ["include"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 
     shared_libs: ["liblog"],
 }
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
index 1e8467f..32499e3 100644
--- a/trusty/libtrusty/tipc-test/Android.bp
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -25,5 +25,8 @@
         "liblog",
     ],
     gtest: false,
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
diff --git a/trusty/storage/lib/Android.bp b/trusty/storage/lib/Android.bp
index 4e41674..2fba17e 100644
--- a/trusty/storage/lib/Android.bp
+++ b/trusty/storage/lib/Android.bp
@@ -34,5 +34,5 @@
         "-fvisibility=hidden",
         "-Wall",
         "-Werror",
-    ]
+    ],
 }
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index da8542d..b93facb 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -36,5 +36,5 @@
     cflags: [
         "-Wall",
         "-Werror",
-    ]
+    ],
 }
diff --git a/usbd/Android.bp b/usbd/Android.bp
index 4f9338f..3afa7a9 100644
--- a/usbd/Android.bp
+++ b/usbd/Android.bp
@@ -13,4 +13,3 @@
         "libcutils",
     ],
 }
-