Merge "adbd: receive jdwp pids from art as raw integers."
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index 5cf4c56..6f5396a 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -442,7 +442,7 @@
  **/
 
 struct JdwpSocket : public asocket {
-    bool pass;
+    bool pass = false;
 };
 
 static void jdwp_socket_close(asocket* s) {
@@ -456,7 +456,7 @@
     }
 
     remove_socket(s);
-    free(s);
+    delete s;
 }
 
 static int jdwp_socket_enqueue(asocket* s, std::string) {
@@ -486,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");
diff --git a/adb/socket.h b/adb/socket.h
index a1b52b3..2f09080 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -36,31 +36,31 @@
 struct asocket {
     /* 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 data waiting to be written
     std::deque<Range> packet_queue;
@@ -73,27 +73,27 @@
      * peer->ready() when we once again are ready to
      * receive data.
      */
-    int (*enqueue)(asocket* s, std::string data);
+    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/sockets.cpp b/adb/sockets.cpp
index e9c45b7..307cbfe 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -170,7 +170,7 @@
     fdevent_remove(&s->fde);
 
     remove_socket(s);
-    free(s);
+    delete s;
 
     if (exit_on_close) {
         D("local_socket_destroy: exiting");
@@ -347,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;
@@ -459,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
@@ -470,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;
@@ -811,13 +804,12 @@
         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 3b0669c..9ae1297 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -378,9 +378,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 */
@@ -411,7 +411,7 @@
         peer->close(peer);
     }
     device_tracker_remove(tracker);
-    free(tracker);
+    delete tracker;
 }
 
 static int device_tracker_enqueue(asocket* socket, std::string) {
@@ -446,7 +446,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/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 5fddddc..364fca5 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -55,7 +55,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 +65,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 +88,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 +124,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 +135,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 +164,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 +247,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 +268,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;
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/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/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 892fb48..34bfcef 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -248,6 +248,7 @@
         "tests/files/elf64.xz",
         "tests/files/offline/bad_eh_frame_hdr_arm64/*",
         "tests/files/offline/debug_frame_first_x86/*",
+        "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/*",
@@ -306,6 +307,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/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/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 644bfa8..5e32933 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -91,7 +91,7 @@
     return;
   }
 
-  frame->pc = map_info->start + adjusted_rel_pc;
+  frame->pc = map_info->start + adjusted_rel_pc - elf->GetLoadBias() - map_info->elf_offset;
   frame->map_name = map_info->name;
   frame->map_offset = map_info->offset;
   frame->map_start = map_info->start;
@@ -175,6 +175,8 @@
       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);
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/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index e499593..df262f5 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -23,23 +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 "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++) {
@@ -48,45 +179,11 @@
   return str;
 }
 
-TEST(UnwindOfflineTest, pc_straddle_arm) {
-  std::string dir(TestGetFileDirectory() + "offline/straddle_arm/");
+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;
@@ -98,43 +195,11 @@
       frame_info);
 }
 
-TEST(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
-  std::string dir(TestGetFileDirectory() + "offline/gnu_debugdata_arm/");
+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;
@@ -146,47 +211,11 @@
       frame_info);
 }
 
-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;
@@ -208,64 +237,22 @@
   parts->Add(memory);
 }
 
-TEST(UnwindOfflineTest, jit_debug_x86) {
-  std::string dir(TestGetFileDirectory() + "offline/jit_debug_x86/");
+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;
@@ -405,79 +392,23 @@
       frame_info);
 }
 
-TEST(UnwindOfflineTest, jit_debug_arm) {
-  std::string dir(TestGetFileDirectory() + "offline/jit_debug_arm/");
+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;
@@ -627,47 +558,11 @@
 // 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;
@@ -682,59 +577,11 @@
 
 // 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;
@@ -747,4 +594,22 @@
       frame_info);
 }
 
+// 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);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 09c6e04..7fbae4c 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -102,6 +102,22 @@
     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);
   }
 
@@ -180,6 +196,62 @@
   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));
 
@@ -752,6 +824,64 @@
   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/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
new file mode 100644
index 0000000..4d89087
--- /dev/null
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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;
+  }
+
+  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/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/rootdir/init.rc b/rootdir/init.rc
index 6a1872f..f008c17 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
@@ -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