Merge "Update GSI keys for dynamic system image"
diff --git a/adb/types.h b/adb/types.h
index 0090c98..cd1366d 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -216,7 +216,10 @@
     // Add a nonempty block to the chain.
     // The end of the chain must be a complete block (i.e. end_offset_ == 0).
     void append(std::unique_ptr<const block_type> block) {
-        CHECK_NE(0ULL, block->size());
+        if (block->size() == 0) {
+            return;
+        }
+
         CHECK_EQ(0ULL, end_offset_);
         chain_length_ += block->size();
         chain_.emplace_back(std::move(block));
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 437450c..82ba0a1 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -48,6 +48,7 @@
 #define ATRACE_TAG ATRACE_TAG_BIONIC
 #include <utils/Trace.h>
 
+#include <unwindstack/DexFiles.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
@@ -566,7 +567,7 @@
 
   // TODO: Use seccomp to lock ourselves down.
   unwindstack::UnwinderFromPid unwinder(256, vm_pid);
-  if (!unwinder.Init()) {
+  if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
     LOG(FATAL) << "Failed to init unwinder object.";
   }
 
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 5f7ebc3..bbec612 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -42,6 +42,7 @@
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
+#include <unwindstack/DexFiles.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
@@ -80,12 +81,12 @@
     thread.pid = getpid();
     thread.tid = gettid();
     thread.thread_name = get_thread_name(gettid());
-    thread.registers.reset(
-        unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
+    unwindstack::ArchEnum arch = unwindstack::Regs::CurrentArch();
+    thread.registers.reset(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
 
     // TODO: Create this once and store it in a global?
     unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
-    if (unwinder.Init()) {
+    if (unwinder.Init(arch)) {
       dump_backtrace_thread(output_fd, &unwinder, thread);
     } else {
       async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Unable to init unwinder.");
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 4bdb9c8..47a7a8f 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -44,6 +44,7 @@
 #include <log/log.h>
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
+#include <unwindstack/DexFiles.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
@@ -649,7 +650,7 @@
   };
 
   unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid);
-  if (!unwinder.Init()) {
+  if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
     LOG(FATAL) << "Failed to init unwinder object.";
   }
 
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 827db96..f8f7eb3 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1407,7 +1407,7 @@
 
 int LocalImageSource::OpenFile(const std::string& name) const {
     auto path = find_item_given_name(name);
-    return open(path.c_str(), O_RDONLY);
+    return open(path.c_str(), O_RDONLY | O_BINARY);
 }
 
 static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index 85589cc..e3803d5 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -407,9 +407,25 @@
 #define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
 #endif
 
+    // f2fs: export FS_NOCOW_FL flag to user
+    uint32_t flags;
+    int error = ioctl(file_fd, FS_IOC_GETFLAGS, &flags);
+    if (error < 0) {
+        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+            PLOG(ERROR) << "Failed to get flags, not supported by kernel: " << file_path;
+        } else {
+            PLOG(ERROR) << "Failed to get flags: " << file_path;
+        }
+        return false;
+    }
+    if (!(flags & FS_NOCOW_FL)) {
+        LOG(ERROR) << "It is not pinned: " << file_path;
+        return false;
+    }
+
     // F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
     uint32_t moved_blocks_nr;
-    int error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
+    error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
     if (error < 0) {
         if ((errno == ENOTTY) || (errno == ENOTSUP)) {
             PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 56b5353..db27022 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -98,11 +98,12 @@
     return WriteToImageFile(fd, input);
 }
 
-SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
-                             const std::map<std::string, std::string>& images)
+ImageBuilder::ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
+                           const std::map<std::string, std::string>& images, bool sparsify)
     : metadata_(metadata),
       geometry_(metadata.geometry),
       block_size_(block_size),
+      sparsify_(sparsify),
       images_(images) {
     uint64_t total_size = GetTotalSuperPartitionSize(metadata);
     if (block_size % LP_SECTOR_SIZE != 0) {
@@ -144,11 +145,11 @@
     }
 }
 
-bool SparseBuilder::IsValid() const {
+bool ImageBuilder::IsValid() const {
     return device_images_.size() == metadata_.block_devices.size();
 }
 
-bool SparseBuilder::Export(const char* file) {
+bool ImageBuilder::Export(const char* file) {
     unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
     if (fd < 0) {
         PERROR << "open failed: " << file;
@@ -158,8 +159,8 @@
         LERROR << "Cannot export to a single image on retrofit builds.";
         return false;
     }
-    // No gzip compression; sparseify; no checksum.
-    int ret = sparse_file_write(device_images_[0].get(), fd, false, true, false);
+    // No gzip compression; no checksum.
+    int ret = sparse_file_write(device_images_[0].get(), fd, false, sparsify_, false);
     if (ret != 0) {
         LERROR << "sparse_file_write failed (error code " << ret << ")";
         return false;
@@ -167,7 +168,7 @@
     return true;
 }
 
-bool SparseBuilder::ExportFiles(const std::string& output_dir) {
+bool ImageBuilder::ExportFiles(const std::string& output_dir) {
     for (size_t i = 0; i < device_images_.size(); i++) {
         std::string name = GetBlockDevicePartitionName(metadata_.block_devices[i]);
         std::string file_name = "super_" + name + ".img";
@@ -179,8 +180,8 @@
             PERROR << "open failed: " << file_path;
             return false;
         }
-        // No gzip compression; sparseify; no checksum.
-        int ret = sparse_file_write(device_images_[i].get(), fd, false, true, false);
+        // No gzip compression; no checksum.
+        int ret = sparse_file_write(device_images_[i].get(), fd, false, sparsify_, false);
         if (ret != 0) {
             LERROR << "sparse_file_write failed (error code " << ret << ")";
             return false;
@@ -189,7 +190,7 @@
     return true;
 }
 
-bool SparseBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {
+bool ImageBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {
     uint32_t block;
     if (!SectorToBlock(sector, &block)) {
         return false;
@@ -203,7 +204,7 @@
     return true;
 }
 
-bool SparseBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
+bool ImageBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
     // The caller must ensure that the metadata has an alignment that is a
     // multiple of the block size. liblp will take care of the rest, ensuring
     // that all partitions are on an aligned boundary. Therefore all writes
@@ -218,11 +219,11 @@
     return true;
 }
 
-uint64_t SparseBuilder::BlockToSector(uint64_t block) const {
+uint64_t ImageBuilder::BlockToSector(uint64_t block) const {
     return (block * block_size_) / LP_SECTOR_SIZE;
 }
 
-bool SparseBuilder::Build() {
+bool ImageBuilder::Build() {
     if (sparse_file_add_fill(device_images_[0].get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
         LERROR << "Could not add initial sparse block for reserved zeroes";
         return false;
@@ -275,8 +276,8 @@
     return true;
 }
 
-bool SparseBuilder::AddPartitionImage(const LpMetadataPartition& partition,
-                                      const std::string& file) {
+bool ImageBuilder::AddPartitionImage(const LpMetadataPartition& partition,
+                                     const std::string& file) {
     // Track which extent we're processing.
     uint32_t extent_index = partition.first_extent_index;
 
@@ -371,7 +372,7 @@
     return true;
 }
 
-uint64_t SparseBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
+uint64_t ImageBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
     uint64_t sectors = 0;
     for (size_t i = 0; i < partition.num_extents; i++) {
         sectors += metadata_.extents[partition.first_extent_index + i].num_sectors;
@@ -386,7 +387,7 @@
 //
 // Without this, it would be more difficult to find the appropriate extent for
 // an output block. With this guarantee it is a linear walk.
-bool SparseBuilder::CheckExtentOrdering() {
+bool ImageBuilder::CheckExtentOrdering() {
     std::vector<uint64_t> last_sectors(metadata_.block_devices.size());
 
     for (const auto& extent : metadata_.extents) {
@@ -407,7 +408,7 @@
     return true;
 }
 
-int SparseBuilder::OpenImageFile(const std::string& file) {
+int ImageBuilder::OpenImageFile(const std::string& file) {
     android::base::unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC);
     if (source_fd < 0) {
         PERROR << "open image file failed: " << file;
@@ -437,15 +438,16 @@
     return temp_fds_.back().get();
 }
 
-bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
-                       const std::map<std::string, std::string>& images) {
-    SparseBuilder builder(metadata, block_size, images);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+                      const std::map<std::string, std::string>& images, bool sparsify) {
+    ImageBuilder builder(metadata, block_size, images, sparsify);
     return builder.IsValid() && builder.Build() && builder.Export(file);
 }
 
-bool WriteSplitSparseFiles(const std::string& output_dir, const LpMetadata& metadata,
-                           uint32_t block_size, const std::map<std::string, std::string>& images) {
-    SparseBuilder builder(metadata, block_size, images);
+bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
+                          uint32_t block_size, const std::map<std::string, std::string>& images,
+                          bool sparsify) {
+    ImageBuilder builder(metadata, block_size, images, sparsify);
     return builder.IsValid() && builder.Build() && builder.ExportFiles(output_dir);
 }
 
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index 44217a0..75060f9 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -32,13 +32,13 @@
 bool WriteToImageFile(const char* file, const LpMetadata& metadata);
 bool WriteToImageFile(int fd, const LpMetadata& metadata);
 
-// We use an object to build the sparse file since it requires that data
+// We use an object to build the image file since it requires that data
 // pointers be held alive until the sparse file is destroyed. It's easier
 // to do this when the data pointers are all in one place.
-class SparseBuilder {
+class ImageBuilder {
   public:
-    SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
-                  const std::map<std::string, std::string>& images);
+    ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
+                 const std::map<std::string, std::string>& images, bool sparsify);
 
     bool Build();
     bool Export(const char* file);
@@ -60,6 +60,7 @@
     const LpMetadata& metadata_;
     const LpMetadataGeometry& geometry_;
     uint32_t block_size_;
+    bool sparsify_;
 
     std::vector<SparsePtr> device_images_;
     std::string all_metadata_;
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 6348f55..5f782b0 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -72,8 +72,8 @@
 
 // Read/Write logical partition metadata to an image file, for diagnostics or
 // flashing.
-bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
-                       const std::map<std::string, std::string>& images);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+                      const std::map<std::string, std::string>& images, bool sparsify);
 bool WriteToImageFile(const char* file, const LpMetadata& metadata);
 std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file);
 std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
@@ -83,8 +83,9 @@
 // is intended for retrofit devices, and will generate one sparse file per
 // block device (each named super_<name>.img) and placed in the specified
 // output folder.
-bool WriteSplitSparseFiles(const std::string& output_dir, const LpMetadata& metadata,
-                           uint32_t block_size, const std::map<std::string, std::string>& images);
+bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
+                          uint32_t block_size, const std::map<std::string, std::string>& images,
+                          bool sparsify);
 
 // Helper to extract safe C++ strings from partition info.
 std::string GetPartitionName(const LpMetadataPartition& partition);
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 9f3314d..8fc02cb 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -598,7 +598,7 @@
     ASSERT_NE(exported, nullptr);
 
     // Build the sparse file.
-    SparseBuilder sparse(*exported.get(), 512, {});
+    ImageBuilder sparse(*exported.get(), 512, {}, true /* sparsify */);
     ASSERT_TRUE(sparse.IsValid());
     ASSERT_TRUE(sparse.Build());
 
diff --git a/init/Android.bp b/init/Android.bp
index 8292aa0..69ee34f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -61,6 +61,7 @@
     static_libs: [
         "libseccomp_policy",
         "libavb",
+        "libcgrouprc_format",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
@@ -82,6 +83,7 @@
         "liblogwrap",
         "liblp",
         "libprocessgroup",
+        "libprocessgroup_setup",
         "libselinux",
         "libutils",
     ],
diff --git a/init/init.cpp b/init/init.cpp
index cdec41c..0f44efd 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -45,6 +45,7 @@
 #include <libavb/libavb.h>
 #include <libgsi/libgsi.h>
 #include <processgroup/processgroup.h>
+#include <processgroup/setup.h>
 #include <selinux/android.h>
 
 #ifndef RECOVERY
@@ -358,7 +359,7 @@
     // Have to create <CGROUPS_RC_DIR> using make_dir function
     // for appropriate sepolicy to be set for it
     make_dir(android::base::Dirname(CGROUPS_RC_PATH), 0711);
-    if (!CgroupSetupCgroups()) {
+    if (!CgroupSetup()) {
         return ErrnoError() << "Failed to setup cgroups";
     }
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index fc5538c..467568c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -557,9 +557,8 @@
         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;
+            LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
+                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
         }
 
         break;
@@ -579,9 +578,8 @@
         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;
+            LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
+                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
         }
         socket.SendUint32(result);
         break;
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index ff19833..f5f9b2a 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -32,6 +32,9 @@
 #include <unwindstack/Regs.h>
 #include <unwindstack/RegsGetLocal.h>
 
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+#include <unwindstack/DexFiles.h>
+#endif
 #include <unwindstack/Unwinder.h>
 
 #include "BacktraceLog.h"
@@ -47,6 +50,14 @@
                                  regs, stack_map->process_memory());
   unwinder.SetResolveNames(stack_map->ResolveNames());
   stack_map->SetArch(regs->Arch());
+  if (stack_map->GetJitDebug() != nullptr) {
+    unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
+  }
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  if (stack_map->GetDexFiles() != nullptr) {
+    unwinder.SetDexFiles(stack_map->GetDexFiles(), regs->Arch());
+  }
+#endif
   unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
   if (error != nullptr) {
     switch (unwinder.LastErrorCode()) {
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 726fdfa..4518891 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -43,6 +43,13 @@
   // Create the process memory object.
   process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_);
 
+  // Create a JitDebug object for getting jit unwind information.
+  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_, search_libs_));
+#endif
+
   if (!stack_maps_->Parse()) {
     return false;
   }
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index 9bb9709..e19b605 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -27,6 +27,9 @@
 
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+#include <unwindstack/DexFiles.h>
+#endif
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
@@ -50,6 +53,12 @@
 
   const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
 
+  unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); }
+
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); }
+#endif
+
   void SetArch(unwindstack::ArchEnum arch) { arch_ = arch; }
 
  protected:
@@ -57,6 +66,11 @@
 
   std::unique_ptr<unwindstack::Maps> stack_maps_;
   std::shared_ptr<unwindstack::Memory> process_memory_;
+  std::unique_ptr<unwindstack::JitDebug> jit_debug_;
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  std::unique_ptr<unwindstack::DexFiles> dex_files_;
+#endif
+
   unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
 };
 
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index a27ecef..619bc56 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -235,6 +235,7 @@
     "libbase",
     "libjsoncpp",
     "libprocessgroup",
+    "libcgrouprc",
 ]
 
 cc_test {
@@ -249,7 +250,10 @@
     name: "libcutils_test_static",
     test_suites: ["device-tests"],
     defaults: ["libcutils_test_default"],
-    static_libs: ["libc"] + test_libraries,
+    static_libs: [
+        "libc",
+        "libcgrouprc_format",
+    ] + test_libraries,
     stl: "libc++_static",
 
     target: {
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index e35b91a..e67b458 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -73,6 +73,8 @@
 #ifndef __ANDROID_VNDK__
 using openFdType = int (*)();
 
+static openFdType openFd;
+
 openFdType initOpenAshmemFd() {
     openFdType openFd = nullptr;
     void* handle = dlopen("libashmemd_client.so", RTLD_NOW);
@@ -221,7 +223,10 @@
 
     int fd = -1;
 #ifndef __ANDROID_VNDK__
-    static auto openFd = initOpenAshmemFd();
+    if (!openFd) {
+        openFd = initOpenAshmemFd();
+    }
+
     if (openFd) {
         fd = openFd();
     }
@@ -480,3 +485,11 @@
 
     return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
 }
+
+void ashmem_init() {
+#ifndef __ANDROID_VNDK__
+    pthread_mutex_lock(&__ashmem_lock);
+    openFd = initOpenAshmemFd();
+    pthread_mutex_unlock(&__ashmem_lock);
+#endif  //__ANDROID_VNDK__
+}
diff --git a/libcutils/ashmem-host.cpp b/libcutils/ashmem-host.cpp
index bb990d5..32446d4 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -82,3 +82,5 @@
 
     return buf.st_size;
 }
+
+void ashmem_init() {}
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index d80caa6..abc5068 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -26,6 +26,7 @@
 int ashmem_pin_region(int fd, size_t offset, size_t len);
 int ashmem_unpin_region(int fd, size_t offset, size_t len);
 int ashmem_get_size_region(int fd);
+void ashmem_init();
 
 #ifdef __cplusplus
 }
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index b9f0dbf..66cb49f 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -24,6 +24,13 @@
         "libnativebridge",
         "libbase",
     ],
+    target: {
+        android: {
+            shared_libs: [
+                "libdl_android",
+            ],
+        },
+    },
     required: [
         "llndk.libraries.txt",
         "vndksp.libraries.txt",
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 07cbce9..78a02e5 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -31,6 +31,7 @@
     },
     shared_libs: [
         "libbase",
+        "libcgrouprc",
         "libjsoncpp",
     ],
     // for cutils/android_filesystem_config.h
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 1e66fa4..6cd6b6e 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -44,244 +44,42 @@
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
-static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
-static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
-
 static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
 static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
 static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.tasks";
 
-static bool Mkdir(const std::string& path, mode_t mode, const std::string& uid,
-                  const std::string& gid) {
-    if (mode == 0) {
-        mode = 0755;
-    }
-
-    if (mkdir(path.c_str(), mode) != 0) {
-        /* chmod in case the directory already exists */
-        if (errno == EEXIST) {
-            if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
-                // /acct is a special case when the directory already exists
-                // TODO: check if file mode is already what we want instead of using EROFS
-                if (errno != EROFS) {
-                    PLOG(ERROR) << "fchmodat() failed for " << path;
-                    return false;
-                }
-            }
-        } else {
-            PLOG(ERROR) << "mkdir() failed for " << path;
-            return false;
-        }
-    }
-
-    if (uid.empty()) {
-        return true;
-    }
-
-    passwd* uid_pwd = getpwnam(uid.c_str());
-    if (!uid_pwd) {
-        PLOG(ERROR) << "Unable to decode UID for '" << uid << "'";
-        return false;
-    }
-
-    uid_t pw_uid = uid_pwd->pw_uid;
-    gid_t gr_gid = -1;
-    if (!gid.empty()) {
-        group* gid_pwd = getgrnam(gid.c_str());
-        if (!gid_pwd) {
-            PLOG(ERROR) << "Unable to decode GID for '" << gid << "'";
-            return false;
-        }
-        gr_gid = gid_pwd->gr_gid;
-    }
-
-    if (lchown(path.c_str(), pw_uid, gr_gid) < 0) {
-        PLOG(ERROR) << "lchown() failed for " << path;
-        return false;
-    }
-
-    /* chown may have cleared S_ISUID and S_ISGID, chmod again */
-    if (mode & (S_ISUID | S_ISGID)) {
-        if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
-            PLOG(ERROR) << "fchmodat() failed for " << path;
-            return false;
-        }
-    }
-
-    return true;
+uint32_t CgroupController::version() const {
+    CHECK(HasValue());
+    return ACgroupController_getVersion(controller_);
 }
 
-static bool ReadDescriptorsFromFile(const std::string& file_name,
-                                    std::map<std::string, CgroupDescriptor>* descriptors) {
-    std::vector<CgroupDescriptor> result;
-    std::string json_doc;
-
-    if (!android::base::ReadFileToString(file_name, &json_doc)) {
-        PLOG(ERROR) << "Failed to read task profiles from " << file_name;
-        return false;
-    }
-
-    Json::Reader reader;
-    Json::Value root;
-    if (!reader.parse(json_doc, root)) {
-        LOG(ERROR) << "Failed to parse cgroups description: " << reader.getFormattedErrorMessages();
-        return false;
-    }
-
-    if (root.isMember("Cgroups")) {
-        const Json::Value& cgroups = root["Cgroups"];
-        for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
-            std::string name = cgroups[i]["Controller"].asString();
-            auto iter = descriptors->find(name);
-            if (iter == descriptors->end()) {
-                descriptors->emplace(name, CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
-                                     std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
-                                     cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString()));
-            } else {
-                iter->second = CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
-                                     std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
-                                     cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString());
-            }
-        }
-    }
-
-    if (root.isMember("Cgroups2")) {
-        const Json::Value& cgroups2 = root["Cgroups2"];
-        auto iter = descriptors->find(CGROUPV2_CONTROLLER_NAME);
-        if (iter == descriptors->end()) {
-            descriptors->emplace(CGROUPV2_CONTROLLER_NAME, CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
-                                 std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
-                                 cgroups2["UID"].asString(), cgroups2["GID"].asString()));
-        } else {
-            iter->second = CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
-                                 std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
-                                 cgroups2["UID"].asString(), cgroups2["GID"].asString());
-        }
-    }
-
-    return true;
+const char* CgroupController::name() const {
+    CHECK(HasValue());
+    return ACgroupController_getName(controller_);
 }
 
-static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
-    // load system cgroup descriptors
-    if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {
-        return false;
-    }
-
-    // load vendor cgroup descriptors if the file exists
-    if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
-        !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
-        return false;
-    }
-
-    return true;
+const char* CgroupController::path() const {
+    CHECK(HasValue());
+    return ACgroupController_getPath(controller_);
 }
 
-// To avoid issues in sdk_mac build
-#if defined(__ANDROID__)
-
-static bool SetupCgroup(const CgroupDescriptor& descriptor) {
-    const CgroupController* controller = descriptor.controller();
-
-    // mkdir <path> [mode] [owner] [group]
-    if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
-        LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
-        return false;
-    }
-
-    int result;
-    if (controller->version() == 2) {
-        result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
-                       nullptr);
-    } else {
-        // Unfortunately historically cpuset controller was mounted using a mount command
-        // different from all other controllers. This results in controller attributes not
-        // to be prepended with controller name. For example this way instead of
-        // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
-        // the system currently expects.
-        if (!strcmp(controller->name(), "cpuset")) {
-            // mount cpuset none /dev/cpuset nodev noexec nosuid
-            result = mount("none", controller->path(), controller->name(),
-                           MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
-        } else {
-            // mount cgroup none <path> nodev noexec nosuid <controller>
-            result = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
-                           controller->name());
-        }
-    }
-
-    if (result < 0) {
-        PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
-        return false;
-    }
-
-    return true;
+bool CgroupController::HasValue() const {
+    return controller_ != nullptr;
 }
 
-#else
+std::string CgroupController::GetTasksFilePath(const std::string& rel_path) const {
+    std::string tasks_path = path();
 
-// Stubs for non-Android targets.
-static bool SetupCgroup(const CgroupDescriptor&) {
-    return false;
+    if (!rel_path.empty()) {
+        tasks_path += "/" + rel_path;
+    }
+    return (version() == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
 }
 
-#endif
-
-// WARNING: This function should be called only from SetupCgroups and only once.
-// It intentionally leaks an FD, so additional invocation will result in additional leak.
-static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descriptors) {
-    // WARNING: We are intentionally leaking the FD to keep the file open forever.
-    // Let init keep the FD open to prevent file mappings from becoming invalid in
-    // case the file gets deleted somehow.
-    int fd = TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
-                                     S_IRUSR | S_IRGRP | S_IROTH));
-    if (fd < 0) {
-        PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
-        return false;
-    }
-
-    CgroupFile fl;
-    fl.version_ = CgroupFile::FILE_CURR_VERSION;
-    fl.controller_count_ = descriptors.size();
-    int ret = TEMP_FAILURE_RETRY(write(fd, &fl, sizeof(fl)));
-    if (ret < 0) {
-        PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
-        return false;
-    }
-
-    for (const auto& [name, descriptor] : descriptors) {
-        ret = TEMP_FAILURE_RETRY(write(fd, descriptor.controller(), sizeof(CgroupController)));
-        if (ret < 0) {
-            PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
-            return false;
-        }
-    }
-
-    return true;
-}
-
-CgroupController::CgroupController(uint32_t version, const std::string& name,
-                                   const std::string& path) {
-    version_ = version;
-    strncpy(name_, name.c_str(), sizeof(name_) - 1);
-    name_[sizeof(name_) - 1] = '\0';
-    strncpy(path_, path.c_str(), sizeof(path_) - 1);
-    path_[sizeof(path_) - 1] = '\0';
-}
-
-std::string CgroupController::GetTasksFilePath(const std::string& path) const {
-    std::string tasks_path = path_;
-
-    if (!path.empty()) {
-        tasks_path += "/" + path;
-    }
-    return (version_ == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
-}
-
-std::string CgroupController::GetProcsFilePath(const std::string& path, uid_t uid,
+std::string CgroupController::GetProcsFilePath(const std::string& rel_path, uid_t uid,
                                                pid_t pid) const {
-    std::string proc_path(path_);
-    proc_path.append("/").append(path);
+    std::string proc_path(path());
+    proc_path.append("/").append(rel_path);
     proc_path = regex_replace(proc_path, std::regex("<uid>"), std::to_string(uid));
     proc_path = regex_replace(proc_path, std::regex("<pid>"), std::to_string(pid));
 
@@ -302,7 +100,7 @@
         return true;
     }
 
-    std::string cg_tag = StringPrintf(":%s:", name_);
+    std::string cg_tag = StringPrintf(":%s:", name());
     size_t start_pos = content.find(cg_tag);
     if (start_pos == std::string::npos) {
         return false;
@@ -319,25 +117,12 @@
     return true;
 }
 
-CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,
-                                   const std::string& path, mode_t mode, const std::string& uid,
-                                   const std::string& gid)
-    : controller_(version, name, path), mode_(mode), uid_(uid), gid_(gid) {}
-
-CgroupMap::CgroupMap() : cg_file_data_(nullptr), cg_file_size_(0) {
+CgroupMap::CgroupMap() {
     if (!LoadRcFile()) {
         LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
     }
 }
 
-CgroupMap::~CgroupMap() {
-    if (cg_file_data_) {
-        munmap(cg_file_data_, cg_file_size_);
-        cg_file_data_ = nullptr;
-        cg_file_size_ = 0;
-    }
-}
-
 CgroupMap& CgroupMap::GetInstance() {
     // Deliberately leak this object to avoid a race between destruction on
     // process exit and concurrent access from another thread.
@@ -346,139 +131,46 @@
 }
 
 bool CgroupMap::LoadRcFile() {
-    struct stat sb;
-
-    if (cg_file_data_) {
-        // Data already initialized
-        return true;
+    if (!loaded_) {
+        loaded_ = (ACgroupFile_getVersion() != 0);
     }
-
-    unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_RDONLY | O_CLOEXEC)));
-    if (fd < 0) {
-        PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
-        return false;
-    }
-
-    if (fstat(fd, &sb) < 0) {
-        PLOG(ERROR) << "fstat() failed for " << CGROUPS_RC_PATH;
-        return false;
-    }
-
-    size_t file_size = sb.st_size;
-    if (file_size < sizeof(CgroupFile)) {
-        LOG(ERROR) << "Invalid file format " << CGROUPS_RC_PATH;
-        return false;
-    }
-
-    CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
-    if (file_data == MAP_FAILED) {
-        PLOG(ERROR) << "Failed to mmap " << CGROUPS_RC_PATH;
-        return false;
-    }
-
-    if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
-        LOG(ERROR) << CGROUPS_RC_PATH << " file version mismatch";
-        munmap(file_data, file_size);
-        return false;
-    }
-
-    if (file_size != sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController)) {
-        LOG(ERROR) << CGROUPS_RC_PATH << " file has invalid size";
-        munmap(file_data, file_size);
-        return false;
-    }
-
-    cg_file_data_ = file_data;
-    cg_file_size_ = file_size;
-
-    return true;
+    return loaded_;
 }
 
 void CgroupMap::Print() const {
-    if (!cg_file_data_) {
+    if (!loaded_) {
         LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
                    << "] failed, RC file was not initialized properly";
         return;
     }
-    LOG(INFO) << "File version = " << cg_file_data_->version_;
-    LOG(INFO) << "File controller count = " << cg_file_data_->controller_count_;
+    LOG(INFO) << "File version = " << ACgroupFile_getVersion();
+    LOG(INFO) << "File controller count = " << ACgroupFile_getControllerCount();
 
     LOG(INFO) << "Mounted cgroups:";
-    CgroupController* controller = (CgroupController*)(cg_file_data_ + 1);
-    for (int i = 0; i < cg_file_data_->controller_count_; i++, controller++) {
-        LOG(INFO) << "\t" << controller->name() << " ver " << controller->version() << " path "
-                  << controller->path();
+
+    auto controller_count = ACgroupFile_getControllerCount();
+    for (uint32_t i = 0; i < controller_count; ++i) {
+        const ACgroupController* controller = ACgroupFile_getController(i);
+        LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
+                  << ACgroupController_getVersion(controller) << " path "
+                  << ACgroupController_getPath(controller);
     }
 }
 
-bool CgroupMap::SetupCgroups() {
-    std::map<std::string, CgroupDescriptor> descriptors;
-
-    if (getpid() != 1) {
-        LOG(ERROR) << "Cgroup setup can be done only by init process";
-        return false;
-    }
-
-    // Make sure we do this only one time. No need for std::call_once because
-    // init is a single-threaded process
-    if (access(CGROUPS_RC_PATH, F_OK) == 0) {
-        LOG(WARNING) << "Attempt to call SetupCgroups more than once";
-        return true;
-    }
-
-    // load cgroups.json file
-    if (!ReadDescriptors(&descriptors)) {
-        LOG(ERROR) << "Failed to load cgroup description file";
-        return false;
-    }
-
-    // setup cgroups
-    for (const auto& [name, descriptor] : descriptors) {
-        if (!SetupCgroup(descriptor)) {
-            // issue a warning and proceed with the next cgroup
-            // TODO: mark the descriptor as invalid and skip it in WriteRcFile()
-            LOG(WARNING) << "Failed to setup " << name << " cgroup";
-        }
-    }
-
-    // mkdir <CGROUPS_RC_DIR> 0711 system system
-    if (!Mkdir(android::base::Dirname(CGROUPS_RC_PATH), 0711, "system", "system")) {
-        LOG(ERROR) << "Failed to create directory for " << CGROUPS_RC_PATH << " file";
-        return false;
-    }
-
-    // Generate <CGROUPS_RC_FILE> file which can be directly mmapped into
-    // process memory. This optimizes performance, memory usage
-    // and limits infrormation shared with unprivileged processes
-    // to the minimum subset of information from cgroups.json
-    if (!WriteRcFile(descriptors)) {
-        LOG(ERROR) << "Failed to write " << CGROUPS_RC_PATH << " file";
-        return false;
-    }
-
-    // chmod 0644 <CGROUPS_RC_PATH>
-    if (fchmodat(AT_FDCWD, CGROUPS_RC_PATH, 0644, AT_SYMLINK_NOFOLLOW) < 0) {
-        PLOG(ERROR) << "fchmodat() failed";
-        return false;
-    }
-
-    return true;
-}
-
-const CgroupController* CgroupMap::FindController(const std::string& name) const {
-    if (!cg_file_data_) {
+CgroupController CgroupMap::FindController(const std::string& name) const {
+    if (!loaded_) {
         LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
                    << "] failed, RC file was not initialized properly";
-        return nullptr;
+        return CgroupController(nullptr);
     }
 
-    // skip the file header to get to the first controller
-    CgroupController* controller = (CgroupController*)(cg_file_data_ + 1);
-    for (int i = 0; i < cg_file_data_->controller_count_; i++, controller++) {
-        if (name == controller->name()) {
-            return controller;
+    auto controller_count = ACgroupFile_getControllerCount();
+    for (uint32_t i = 0; i < controller_count; ++i) {
+        const ACgroupController* controller = ACgroupFile_getController(i);
+        if (name == ACgroupController_getName(controller)) {
+            return CgroupController(controller);
         }
     }
 
-    return nullptr;
+    return CgroupController(nullptr);
 }
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index 304ae15..d765e60 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -20,57 +20,30 @@
 #include <sys/types.h>
 
 #include <map>
+#include <memory>
 #include <mutex>
 #include <string>
+#include <vector>
 
-// Minimal controller description to be mmapped into process address space
+#include <android/cgrouprc.h>
+
+// Convenient wrapper of an ACgroupController pointer.
 class CgroupController {
   public:
-    CgroupController() {}
-    CgroupController(uint32_t version, const std::string& name, const std::string& path);
+    // Does not own controller
+    explicit CgroupController(const ACgroupController* controller) : controller_(controller) {}
 
-    uint32_t version() const { return version_; }
-    const char* name() const { return name_; }
-    const char* path() const { return path_; }
+    uint32_t version() const;
+    const char* name() const;
+    const char* path() const;
+
+    bool HasValue() const;
 
     std::string GetTasksFilePath(const std::string& path) const;
     std::string GetProcsFilePath(const std::string& path, uid_t uid, pid_t pid) const;
     bool GetTaskGroup(int tid, std::string* group) const;
-
   private:
-    static constexpr size_t CGROUP_NAME_BUF_SZ = 16;
-    static constexpr size_t CGROUP_PATH_BUF_SZ = 32;
-
-    uint32_t version_;
-    char name_[CGROUP_NAME_BUF_SZ];
-    char path_[CGROUP_PATH_BUF_SZ];
-};
-
-// Complete controller description for mounting cgroups
-class CgroupDescriptor {
-  public:
-    CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,
-                     mode_t mode, const std::string& uid, const std::string& gid);
-
-    const CgroupController* controller() const { return &controller_; }
-    mode_t mode() const { return mode_; }
-    std::string uid() const { return uid_; }
-    std::string gid() const { return gid_; }
-
-  private:
-    CgroupController controller_;
-    mode_t mode_;
-    std::string uid_;
-    std::string gid_;
-};
-
-struct CgroupFile {
-    static constexpr uint32_t FILE_VERSION_1 = 1;
-    static constexpr uint32_t FILE_CURR_VERSION = FILE_VERSION_1;
-
-    uint32_t version_;
-    uint32_t controller_count_;
-    CgroupController controllers_[];
+    const ACgroupController* controller_ = nullptr;
 };
 
 class CgroupMap {
@@ -79,16 +52,11 @@
     static bool SetupCgroups();
 
     static CgroupMap& GetInstance();
-
-    const CgroupController* FindController(const std::string& name) const;
+    CgroupController FindController(const std::string& name) const;
 
   private:
-    struct CgroupFile* cg_file_data_;
-    size_t cg_file_size_;
-
+    bool loaded_ = false;
     CgroupMap();
-    ~CgroupMap();
-
     bool LoadRcFile();
     void Print() const;
 };
diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp
new file mode 100644
index 0000000..6848620
--- /dev/null
+++ b/libprocessgroup/cgrouprc/Android.bp
@@ -0,0 +1,61 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library {
+    name: "libcgrouprc",
+    host_supported: true,
+    recovery_available: true,
+    // Do not ever mark this as vendor_available; otherwise, vendor modules
+    // that links to the static library will behave unexpectedly. All on-device
+    // modules should use libprocessgroup which links to the LL-NDK library
+    // defined below. The static library is built for tests.
+    vendor_available: false,
+    srcs: [
+        "cgroup_controller.cpp",
+        "cgroup_file.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    header_libs: [
+        "libprocessgroup_headers",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    static_libs: [
+        "libcgrouprc_format",
+    ],
+    stubs: {
+        symbol_file: "libcgrouprc.map.txt",
+        versions: ["29"],
+    },
+    target: {
+        linux: {
+            version_script: "libcgrouprc.map.txt",
+        },
+    },
+}
+
+llndk_library {
+    name: "libcgrouprc",
+    symbol_file: "libcgrouprc.map.txt",
+    export_include_dirs: [
+        "include",
+    ],
+}
diff --git a/libprocessgroup/cgrouprc/cgroup_controller.cpp b/libprocessgroup/cgrouprc/cgroup_controller.cpp
new file mode 100644
index 0000000..d064d31
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgroup_controller.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 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 <android-base/logging.h>
+#include <android/cgrouprc.h>
+
+#include "cgrouprc_internal.h"
+
+// All ACgroupController_* functions implicitly convert the pointer back
+// to the original CgroupController pointer before invoking the member functions.
+
+uint32_t ACgroupController_getVersion(const ACgroupController* controller) {
+    CHECK(controller != nullptr);
+    return controller->version();
+}
+
+const char* ACgroupController_getName(const ACgroupController* controller) {
+    CHECK(controller != nullptr);
+    return controller->name();
+}
+
+const char* ACgroupController_getPath(const ACgroupController* controller) {
+    CHECK(controller != nullptr);
+    return controller->path();
+}
diff --git a/libprocessgroup/cgrouprc/cgroup_file.cpp b/libprocessgroup/cgrouprc/cgroup_file.cpp
new file mode 100644
index 0000000..e26d841
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgroup_file.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 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 <sys/mman.h>
+#include <sys/stat.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <android/cgrouprc.h>
+#include <processgroup/processgroup.h>
+
+#include "cgrouprc_internal.h"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+using android::cgrouprc::format::CgroupController;
+using android::cgrouprc::format::CgroupFile;
+
+static CgroupFile* LoadRcFile() {
+    struct stat sb;
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
+        return nullptr;
+    }
+
+    if (fstat(fd, &sb) < 0) {
+        PLOG(ERROR) << "fstat() failed for " << CGROUPS_RC_PATH;
+        return nullptr;
+    }
+
+    size_t file_size = sb.st_size;
+    if (file_size < sizeof(CgroupFile)) {
+        LOG(ERROR) << "Invalid file format " << CGROUPS_RC_PATH;
+        return nullptr;
+    }
+
+    CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
+    if (file_data == MAP_FAILED) {
+        PLOG(ERROR) << "Failed to mmap " << CGROUPS_RC_PATH;
+        return nullptr;
+    }
+
+    if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
+        LOG(ERROR) << CGROUPS_RC_PATH << " file version mismatch";
+        munmap(file_data, file_size);
+        return nullptr;
+    }
+
+    auto expected = sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController);
+    if (file_size != expected) {
+        LOG(ERROR) << CGROUPS_RC_PATH << " file has invalid size, expected " << expected
+                   << ", actual " << file_size;
+        munmap(file_data, file_size);
+        return nullptr;
+    }
+
+    return file_data;
+}
+
+static CgroupFile* GetInstance() {
+    // Deliberately leak this object (not munmap) to avoid a race between destruction on
+    // process exit and concurrent access from another thread.
+    static auto* file = LoadRcFile();
+    return file;
+}
+
+uint32_t ACgroupFile_getVersion() {
+    auto file = GetInstance();
+    if (file == nullptr) return 0;
+    return file->version_;
+}
+
+uint32_t ACgroupFile_getControllerCount() {
+    auto file = GetInstance();
+    if (file == nullptr) return 0;
+    return file->controller_count_;
+}
+
+const ACgroupController* ACgroupFile_getController(uint32_t index) {
+    auto file = GetInstance();
+    if (file == nullptr) return nullptr;
+    CHECK(index < file->controller_count_);
+    // Although the object is not actually an ACgroupController object, all ACgroupController_*
+    // functions implicitly convert ACgroupController* back to CgroupController* before invoking
+    // member functions.
+    return static_cast<ACgroupController*>(&file->controllers_[index]);
+}
diff --git a/libprocessgroup/cgrouprc/cgrouprc_internal.h b/libprocessgroup/cgrouprc/cgrouprc_internal.h
new file mode 100644
index 0000000..cd02f03
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgrouprc_internal.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <android/cgrouprc.h>
+
+#include <processgroup/format/cgroup_controller.h>
+#include <processgroup/format/cgroup_file.h>
+
+struct ACgroupController : android::cgrouprc::format::CgroupController {};
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
new file mode 100644
index 0000000..4edd239
--- /dev/null
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+// For host builds, __INTRODUCED_IN is not defined.
+#ifndef __INTRODUCED_IN
+#define __INTRODUCED_IN(x)
+#endif
+
+struct ACgroupController;
+typedef struct ACgroupController ACgroupController;
+
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+
+// ACgroupFile
+
+/**
+ * Returns file version. See android::cgrouprc::format::CgroupFile for a list of valid versions
+ * for the file.
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupFile_getVersion() __INTRODUCED_IN(29);
+
+/**
+ * Returns the number of controllers.
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupFile_getControllerCount() __INTRODUCED_IN(29);
+
+/**
+ * Returns the controller at the given index.
+ * Returnss nullptr if the given index exceeds getControllerCount().
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) const ACgroupController* ACgroupFile_getController(
+        uint32_t index) __INTRODUCED_IN(29);
+
+// ACgroupController
+
+/**
+ * Returns the version of the given controller.
+ * If the given controller is null, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupController_getVersion(const ACgroupController*)
+        __INTRODUCED_IN(29);
+
+/**
+ * Returns the name of the given controller.
+ * If the given controller is null, return nullptr.
+ */
+__attribute__((warn_unused_result)) const char* ACgroupController_getName(const ACgroupController*)
+        __INTRODUCED_IN(29);
+
+/**
+ * Returns the path of the given controller.
+ * If the given controller is null, return nullptr.
+ */
+__attribute__((warn_unused_result)) const char* ACgroupController_getPath(const ACgroupController*)
+        __INTRODUCED_IN(29);
+
+__END_DECLS
+
+#endif
diff --git a/libprocessgroup/cgrouprc/libcgrouprc.map.txt b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
new file mode 100644
index 0000000..91df392
--- /dev/null
+++ b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
@@ -0,0 +1,11 @@
+LIBCGROUPRC { # introduced=29
+  global:
+    ACgroupFile_getVersion;
+    ACgroupFile_getControllerCount;
+    ACgroupFile_getController;
+    ACgroupController_getVersion;
+    ACgroupController_getName;
+    ACgroupController_getPath;
+  local:
+    *;
+};
diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp
new file mode 100644
index 0000000..dfbeed7
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+    name: "libcgrouprc_format",
+    host_supported: true,
+    recovery_available: true,
+    srcs: [
+        "cgroup_controller.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+}
diff --git a/libprocessgroup/cgrouprc_format/cgroup_controller.cpp b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
new file mode 100644
index 0000000..877eed8
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 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 <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+CgroupController::CgroupController(uint32_t version, const std::string& name,
+                                   const std::string& path) {
+    // strlcpy isn't available on host. Although there is an implementation
+    // in licutils, libcutils itself depends on libcgrouprc_format, causing
+    // a circular dependency.
+    version_ = version;
+    strncpy(name_, name.c_str(), sizeof(name_) - 1);
+    name_[sizeof(name_) - 1] = '\0';
+    strncpy(path_, path.c_str(), sizeof(path_) - 1);
+    path_[sizeof(path_) - 1] = '\0';
+}
+
+uint32_t CgroupController::version() const {
+    return version_;
+}
+
+const char* CgroupController::name() const {
+    return name_;
+}
+
+const char* CgroupController::path() const {
+    return path_;
+}
+
+}  // namespace format
+}  // namespace cgrouprc
+}  // namespace android
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
new file mode 100644
index 0000000..64c7532
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <string>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+// Minimal controller description to be mmapped into process address space
+struct CgroupController {
+  public:
+    CgroupController() {}
+    CgroupController(uint32_t version, const std::string& name, const std::string& path);
+
+    uint32_t version() const;
+    const char* name() const;
+    const char* path() const;
+
+  private:
+    static constexpr size_t CGROUP_NAME_BUF_SZ = 16;
+    static constexpr size_t CGROUP_PATH_BUF_SZ = 32;
+
+    uint32_t version_;
+    char name_[CGROUP_NAME_BUF_SZ];
+    char path_[CGROUP_PATH_BUF_SZ];
+};
+
+}  // namespace format
+}  // namespace cgrouprc
+}  // namespace android
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h
new file mode 100644
index 0000000..f1678a1
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+struct CgroupFile {
+    uint32_t version_;
+    uint32_t controller_count_;
+    CgroupController controllers_[];
+
+    static constexpr uint32_t FILE_VERSION_1 = 1;
+    static constexpr uint32_t FILE_CURR_VERSION = FILE_VERSION_1;
+};
+
+}  // namespace format
+}  // namespace cgrouprc
+}  // namespace android
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 46b8676..86e6035 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -26,7 +26,6 @@
 static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
 static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
 
-bool CgroupSetupCgroups();
 bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
 bool CgroupGetAttributePath(const std::string& attr_name, std::string* path);
 bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 8884650..abe63dd 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -55,19 +55,15 @@
 
 #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
 
-bool CgroupSetupCgroups() {
-    return CgroupMap::SetupCgroups();
-}
-
 bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) {
-    const CgroupController* controller = CgroupMap::GetInstance().FindController(cgroup_name);
+    auto controller = CgroupMap::GetInstance().FindController(cgroup_name);
 
-    if (controller == nullptr) {
+    if (!controller.HasValue()) {
         return false;
     }
 
     if (path) {
-        *path = controller->path();
+        *path = controller.path();
     }
 
     return true;
@@ -111,7 +107,7 @@
 
 static bool isMemoryCgroupSupported() {
     std::string cgroup_name;
-    static bool memcg_supported = (CgroupMap::GetInstance().FindController("memory") != nullptr);
+    static bool memcg_supported = CgroupMap::GetInstance().FindController("memory").HasValue();
 
     return memcg_supported;
 }
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 1eefada..c7d0cca 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -152,21 +152,21 @@
 }
 
 bool cpusets_enabled() {
-    static bool enabled = (CgroupMap::GetInstance().FindController("cpuset") != nullptr);
+    static bool enabled = (CgroupMap::GetInstance().FindController("cpuset").HasValue());
     return enabled;
 }
 
 bool schedboost_enabled() {
-    static bool enabled = (CgroupMap::GetInstance().FindController("schedtune") != nullptr);
+    static bool enabled = (CgroupMap::GetInstance().FindController("schedtune").HasValue());
     return enabled;
 }
 
 static int getCGroupSubsys(int tid, const char* subsys, std::string& subgroup) {
-    const CgroupController* controller = CgroupMap::GetInstance().FindController(subsys);
+    auto controller = CgroupMap::GetInstance().FindController(subsys);
 
-    if (!controller) return -1;
+    if (!controller.HasValue()) return -1;
 
-    if (!controller->GetTaskGroup(tid, &subgroup)) {
+    if (!controller.GetTaskGroup(tid, &subgroup)) {
         LOG(ERROR) << "Failed to find cgroup for tid " << tid;
         return -1;
     }
diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp
new file mode 100644
index 0000000..f6fc066
--- /dev/null
+++ b/libprocessgroup/setup/Android.bp
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_shared {
+    name: "libprocessgroup_setup",
+    recovery_available: true,
+    srcs: [
+        "cgroup_map_write.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcgrouprc",
+        "libjsoncpp",
+    ],
+    static_libs: [
+        "libcgrouprc_format",
+    ],
+    header_libs: [
+        "libprocessgroup_headers",
+    ],
+    export_header_lib_headers: [
+        "libprocessgroup_headers",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/libprocessgroup/setup/cgroup_descriptor.h b/libprocessgroup/setup/cgroup_descriptor.h
new file mode 100644
index 0000000..597060e
--- /dev/null
+++ b/libprocessgroup/setup/cgroup_descriptor.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+
+// Complete controller description for mounting cgroups
+class CgroupDescriptor {
+  public:
+    CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,
+                     mode_t mode, const std::string& uid, const std::string& gid);
+
+    const format::CgroupController* controller() const { return &controller_; }
+    mode_t mode() const { return mode_; }
+    std::string uid() const { return uid_; }
+    std::string gid() const { return gid_; }
+
+  private:
+    format::CgroupController controller_;
+    mode_t mode_ = 0;
+    std::string uid_;
+    std::string gid_;
+};
+
+}  // namespace cgrouprc
+}  // namespace android
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
new file mode 100644
index 0000000..da60948
--- /dev/null
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "libprocessgroup"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <regex>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <json/reader.h>
+#include <json/value.h>
+#include <processgroup/format/cgroup_file.h>
+#include <processgroup/processgroup.h>
+#include <processgroup/setup.h>
+
+#include "cgroup_descriptor.h"
+
+using android::base::GetBoolProperty;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+namespace cgrouprc {
+
+static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
+static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
+
+static bool Mkdir(const std::string& path, mode_t mode, const std::string& uid,
+                  const std::string& gid) {
+    if (mode == 0) {
+        mode = 0755;
+    }
+
+    if (mkdir(path.c_str(), mode) != 0) {
+        /* chmod in case the directory already exists */
+        if (errno == EEXIST) {
+            if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+                // /acct is a special case when the directory already exists
+                // TODO: check if file mode is already what we want instead of using EROFS
+                if (errno != EROFS) {
+                    PLOG(ERROR) << "fchmodat() failed for " << path;
+                    return false;
+                }
+            }
+        } else {
+            PLOG(ERROR) << "mkdir() failed for " << path;
+            return false;
+        }
+    }
+
+    if (uid.empty()) {
+        return true;
+    }
+
+    passwd* uid_pwd = getpwnam(uid.c_str());
+    if (!uid_pwd) {
+        PLOG(ERROR) << "Unable to decode UID for '" << uid << "'";
+        return false;
+    }
+
+    uid_t pw_uid = uid_pwd->pw_uid;
+    gid_t gr_gid = -1;
+    if (!gid.empty()) {
+        group* gid_pwd = getgrnam(gid.c_str());
+        if (!gid_pwd) {
+            PLOG(ERROR) << "Unable to decode GID for '" << gid << "'";
+            return false;
+        }
+        gr_gid = gid_pwd->gr_gid;
+    }
+
+    if (lchown(path.c_str(), pw_uid, gr_gid) < 0) {
+        PLOG(ERROR) << "lchown() failed for " << path;
+        return false;
+    }
+
+    /* chown may have cleared S_ISUID and S_ISGID, chmod again */
+    if (mode & (S_ISUID | S_ISGID)) {
+        if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+            PLOG(ERROR) << "fchmodat() failed for " << path;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool ReadDescriptorsFromFile(const std::string& file_name,
+                                    std::map<std::string, CgroupDescriptor>* descriptors) {
+    std::vector<CgroupDescriptor> result;
+    std::string json_doc;
+
+    if (!android::base::ReadFileToString(file_name, &json_doc)) {
+        PLOG(ERROR) << "Failed to read task profiles from " << file_name;
+        return false;
+    }
+
+    Json::Reader reader;
+    Json::Value root;
+    if (!reader.parse(json_doc, root)) {
+        LOG(ERROR) << "Failed to parse cgroups description: " << reader.getFormattedErrorMessages();
+        return false;
+    }
+
+    if (root.isMember("Cgroups")) {
+        const Json::Value& cgroups = root["Cgroups"];
+        for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
+            std::string name = cgroups[i]["Controller"].asString();
+            auto iter = descriptors->find(name);
+            if (iter == descriptors->end()) {
+                descriptors->emplace(
+                        name, CgroupDescriptor(
+                                      1, name, cgroups[i]["Path"].asString(),
+                                      std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+                                      cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString()));
+            } else {
+                iter->second = CgroupDescriptor(
+                        1, name, cgroups[i]["Path"].asString(),
+                        std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+                        cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString());
+            }
+        }
+    }
+
+    if (root.isMember("Cgroups2")) {
+        const Json::Value& cgroups2 = root["Cgroups2"];
+        auto iter = descriptors->find(CGROUPV2_CONTROLLER_NAME);
+        if (iter == descriptors->end()) {
+            descriptors->emplace(
+                    CGROUPV2_CONTROLLER_NAME,
+                    CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+                                     std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+                                     cgroups2["UID"].asString(), cgroups2["GID"].asString()));
+        } else {
+            iter->second =
+                    CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+                                     std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+                                     cgroups2["UID"].asString(), cgroups2["GID"].asString());
+        }
+    }
+
+    return true;
+}
+
+static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
+    // load system cgroup descriptors
+    if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {
+        return false;
+    }
+
+    // load vendor cgroup descriptors if the file exists
+    if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
+        !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
+        return false;
+    }
+
+    return true;
+}
+
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+
+static bool SetupCgroup(const CgroupDescriptor& descriptor) {
+    const format::CgroupController* controller = descriptor.controller();
+
+    // mkdir <path> [mode] [owner] [group]
+    if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
+        LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+        return false;
+    }
+
+    int result;
+    if (controller->version() == 2) {
+        result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+                       nullptr);
+    } else {
+        // Unfortunately historically cpuset controller was mounted using a mount command
+        // different from all other controllers. This results in controller attributes not
+        // to be prepended with controller name. For example this way instead of
+        // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
+        // the system currently expects.
+        if (!strcmp(controller->name(), "cpuset")) {
+            // mount cpuset none /dev/cpuset nodev noexec nosuid
+            result = mount("none", controller->path(), controller->name(),
+                           MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
+        } else {
+            // mount cgroup none <path> nodev noexec nosuid <controller>
+            result = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+                           controller->name());
+        }
+    }
+
+    if (result < 0) {
+        PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
+        return false;
+    }
+
+    return true;
+}
+
+#else
+
+// Stubs for non-Android targets.
+static bool SetupCgroup(const CgroupDescriptor&) {
+    return false;
+}
+
+#endif
+
+static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descriptors) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
+                                         S_IRUSR | S_IRGRP | S_IROTH)));
+    if (fd < 0) {
+        PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
+        return false;
+    }
+
+    format::CgroupFile fl;
+    fl.version_ = format::CgroupFile::FILE_CURR_VERSION;
+    fl.controller_count_ = descriptors.size();
+    int ret = TEMP_FAILURE_RETRY(write(fd, &fl, sizeof(fl)));
+    if (ret < 0) {
+        PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
+        return false;
+    }
+
+    for (const auto& [name, descriptor] : descriptors) {
+        ret = TEMP_FAILURE_RETRY(
+                write(fd, descriptor.controller(), sizeof(format::CgroupController)));
+        if (ret < 0) {
+            PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,
+                                   const std::string& path, mode_t mode, const std::string& uid,
+                                   const std::string& gid)
+    : controller_(version, name, path), mode_(mode), uid_(uid), gid_(gid) {}
+
+}  // namespace cgrouprc
+}  // namespace android
+
+bool CgroupSetup() {
+    using namespace android::cgrouprc;
+
+    std::map<std::string, CgroupDescriptor> descriptors;
+
+    if (getpid() != 1) {
+        LOG(ERROR) << "Cgroup setup can be done only by init process";
+        return false;
+    }
+
+    // Make sure we do this only one time. No need for std::call_once because
+    // init is a single-threaded process
+    if (access(CGROUPS_RC_PATH, F_OK) == 0) {
+        LOG(WARNING) << "Attempt to call SetupCgroups more than once";
+        return true;
+    }
+
+    // load cgroups.json file
+    if (!ReadDescriptors(&descriptors)) {
+        LOG(ERROR) << "Failed to load cgroup description file";
+        return false;
+    }
+
+    // setup cgroups
+    for (const auto& [name, descriptor] : descriptors) {
+        if (!SetupCgroup(descriptor)) {
+            // issue a warning and proceed with the next cgroup
+            // TODO: mark the descriptor as invalid and skip it in WriteRcFile()
+            LOG(WARNING) << "Failed to setup " << name << " cgroup";
+        }
+    }
+
+    // mkdir <CGROUPS_RC_DIR> 0711 system system
+    if (!Mkdir(android::base::Dirname(CGROUPS_RC_PATH), 0711, "system", "system")) {
+        LOG(ERROR) << "Failed to create directory for " << CGROUPS_RC_PATH << " file";
+        return false;
+    }
+
+    // Generate <CGROUPS_RC_FILE> file which can be directly mmapped into
+    // process memory. This optimizes performance, memory usage
+    // and limits infrormation shared with unprivileged processes
+    // to the minimum subset of information from cgroups.json
+    if (!WriteRcFile(descriptors)) {
+        LOG(ERROR) << "Failed to write " << CGROUPS_RC_PATH << " file";
+        return false;
+    }
+
+    // chmod 0644 <CGROUPS_RC_PATH>
+    if (fchmodat(AT_FDCWD, CGROUPS_RC_PATH, 0644, AT_SYMLINK_NOFOLLOW) < 0) {
+        PLOG(ERROR) << "fchmodat() failed";
+        return false;
+    }
+
+    return true;
+}
diff --git a/libprocessgroup/setup/include/processgroup/setup.h b/libprocessgroup/setup/include/processgroup/setup.h
new file mode 100644
index 0000000..6ea1979
--- /dev/null
+++ b/libprocessgroup/setup/include/processgroup/setup.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+bool CgroupSetup();
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 9362c03..4b45c87 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -46,7 +46,7 @@
 
 bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
     std::string subgroup;
-    if (!controller_->GetTaskGroup(tid, &subgroup)) {
+    if (!controller()->GetTaskGroup(tid, &subgroup)) {
         return false;
     }
 
@@ -55,9 +55,10 @@
     }
 
     if (subgroup.empty()) {
-        *path = StringPrintf("%s/%s", controller_->path(), file_name_.c_str());
+        *path = StringPrintf("%s/%s", controller()->path(), file_name_.c_str());
     } else {
-        *path = StringPrintf("%s/%s/%s", controller_->path(), subgroup.c_str(), file_name_.c_str());
+        *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
+                             file_name_.c_str());
     }
     return true;
 }
@@ -135,7 +136,7 @@
     return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
 }
 
-SetCgroupAction::SetCgroupAction(const CgroupController* c, const std::string& p)
+SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
     : controller_(c), path_(p) {
 #ifdef CACHE_FILE_DESCRIPTORS
     // cache file descriptor only if path is app independent
@@ -145,7 +146,7 @@
         return;
     }
 
-    std::string tasks_path = c->GetTasksFilePath(p.c_str());
+    std::string tasks_path = c.GetTasksFilePath(p);
 
     if (access(tasks_path.c_str(), W_OK) != 0) {
         // file is not accessible
@@ -199,7 +200,7 @@
     }
 
     // this is app-dependent path, file descriptor is not cached
-    std::string procs_path = controller_->GetProcsFilePath(path_, uid, pid);
+    std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
         PLOG(WARNING) << "Failed to open " << procs_path;
@@ -212,7 +213,7 @@
 
     return true;
 #else
-    std::string procs_path = controller_->GetProcsFilePath(path_, uid, pid);
+    std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
         // no permissions to access the file, ignore
@@ -247,7 +248,7 @@
     LOG(ERROR) << "Application profile can't be applied to a thread";
     return false;
 #else
-    std::string tasks_path = controller_->GetTasksFilePath(path_);
+    std::string tasks_path = controller()->GetTasksFilePath(path_);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
         // no permissions to access the file, ignore
@@ -326,8 +327,8 @@
         std::string file_attr = attr[i]["File"].asString();
 
         if (attributes_.find(name) == attributes_.end()) {
-            const CgroupController* controller = cg_map.FindController(controller_name);
-            if (controller) {
+            auto controller = cg_map.FindController(controller_name);
+            if (controller.HasValue()) {
                 attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
             } else {
                 LOG(WARNING) << "Controller " << controller_name << " is not found";
@@ -355,8 +356,8 @@
                 std::string controller_name = params_val["Controller"].asString();
                 std::string path = params_val["Path"].asString();
 
-                const CgroupController* controller = cg_map.FindController(controller_name);
-                if (controller) {
+                auto controller = cg_map.FindController(controller_name);
+                if (controller.HasValue()) {
                     profile->Add(std::make_unique<SetCgroupAction>(controller, path));
                 } else {
                     LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 9ee81c1..37cc305 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -27,16 +27,16 @@
 
 class ProfileAttribute {
   public:
-    ProfileAttribute(const CgroupController* controller, const std::string& file_name)
+    ProfileAttribute(const CgroupController& controller, const std::string& file_name)
         : controller_(controller), file_name_(file_name) {}
 
-    const CgroupController* controller() const { return controller_; }
+    const CgroupController* controller() const { return &controller_; }
     const std::string& file_name() const { return file_name_; }
 
     bool GetPathForTask(int tid, std::string* path) const;
 
   private:
-    const CgroupController* controller_;
+    CgroupController controller_;
     std::string file_name_;
 };
 
@@ -106,16 +106,16 @@
 // Set cgroup profile element
 class SetCgroupAction : public ProfileAction {
   public:
-    SetCgroupAction(const CgroupController* c, const std::string& p);
+    SetCgroupAction(const CgroupController& c, const std::string& p);
 
     virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     virtual bool ExecuteForTask(int tid) const;
 
-    const CgroupController* controller() const { return controller_; }
+    const CgroupController* controller() const { return &controller_; }
     std::string path() const { return path_; }
 
   private:
-    const CgroupController* controller_;
+    CgroupController controller_;
     std::string path_;
 #ifdef CACHE_FILE_DESCRIPTORS
     android::base::unique_fd fd_;
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ce25108..b7650a1 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -49,6 +49,7 @@
     srcs: [
         "ArmExidx.cpp",
         "DexFile.cpp",
+        "DexFiles.cpp",
         "DwarfCfa.cpp",
         "DwarfEhFrameWithHdr.cpp",
         "DwarfMemory.cpp",
@@ -91,6 +92,7 @@
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
             exclude_srcs: [
                 "DexFile.cpp",
+                "DexFiles.cpp",
             ],
             exclude_shared_libs: [
                 "libdexfile_support",
@@ -100,6 +102,7 @@
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
             exclude_srcs: [
                 "DexFile.cpp",
+                "DexFiles.cpp",
             ],
             exclude_shared_libs: [
                 "libdexfile_support",
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index d8d5a24..eaf867f 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -35,31 +35,22 @@
 std::unique_ptr<DexFile> DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory,
                                          MapInfo* info) {
   if (!info->name.empty()) {
-    std::unique_ptr<DexFile> dex_file_from_file =
+    std::unique_ptr<DexFile> dex_file =
         DexFileFromFile::Create(dex_file_offset_in_memory - info->start + info->offset, info->name);
-    if (dex_file_from_file) {
-      dex_file_from_file->addr_ = dex_file_offset_in_memory;
-      return dex_file_from_file;
+    if (dex_file) {
+      return dex_file;
     }
   }
-  std::unique_ptr<DexFile> dex_file_from_memory =
-      DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name);
-  if (dex_file_from_memory) {
-    dex_file_from_memory->addr_ = dex_file_offset_in_memory;
-    return dex_file_from_memory;
-  }
-  return nullptr;
+  return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name);
 }
 
-bool DexFile::GetFunctionName(uint64_t dex_pc, std::string* method_name, uint64_t* method_offset) {
-  uint64_t dex_offset = dex_pc - addr_;
+bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
+                                   uint64_t* method_offset) {
   art_api::dex::MethodInfo method_info = GetMethodInfoForOffset(dex_offset, false);
   if (method_info.offset == 0) {
     return false;
   }
-  if (method_name != nullptr) {
-    *method_name = method_info.name;
-  }
+  *method_name = method_info.name;
   *method_offset = dex_offset - method_info.offset;
   return true;
 }
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index 1448a4b..ca658e6 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -29,22 +29,17 @@
 
 namespace unwindstack {
 
-class Memory;
-struct MapInfo;
-
 class DexFile : protected art_api::dex::DexFile {
  public:
   virtual ~DexFile() = default;
 
-  bool GetFunctionName(uint64_t dex_pc, std::string* method_name, uint64_t* method_offset);
+  bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
 
   static std::unique_ptr<DexFile> Create(uint64_t dex_file_offset_in_memory, Memory* memory,
                                          MapInfo* info);
 
  protected:
   DexFile(art_api::dex::DexFile&& art_dex_file) : art_api::dex::DexFile(std::move(art_dex_file)) {}
-
-  uint64_t addr_ = 0;
 };
 
 class DexFileFromFile : public DexFile {
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
new file mode 100644
index 0000000..63a77e5
--- /dev/null
+++ b/libunwindstack/DexFiles.cpp
@@ -0,0 +1,179 @@
+/*
+ * 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 <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+
+#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) : Global(memory) {}
+
+DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+    : Global(memory, search_libs) {}
+
+DexFiles::~DexFiles() {}
+
+void DexFiles::ProcessArch() {
+  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;
+}
+
+bool DexFiles::ReadVariableData(uint64_t ptr_offset) {
+  entry_addr_ = (this->*read_entry_ptr_func_)(ptr_offset);
+  return entry_addr_ != 0;
+}
+
+void DexFiles::Init(Maps* maps) {
+  if (initialized_) {
+    return;
+  }
+  initialized_ = true;
+  entry_addr_ = 0;
+
+  FindAndReadVariable(maps, "__dex_debug_descriptor");
+}
+
+DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
+  // Lock while processing the data.
+  DexFile* dex_file;
+  auto entry = files_.find(dex_file_offset);
+  if (entry == files_.end()) {
+    std::unique_ptr<DexFile> new_dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
+    dex_file = new_dex_file.get();
+    files_[dex_file_offset] = std::move(new_dex_file);
+  } else {
+    dex_file = entry->second.get();
+  }
+  return dex_file;
+}
+
+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::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/Elf.cpp b/libunwindstack/Elf.cpp
index 5b402ed..4b93abb 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -243,24 +243,6 @@
   return false;
 }
 
-bool Elf::GetTextRange(uint64_t* addr, uint64_t* size) {
-  if (!valid_) {
-    return false;
-  }
-
-  if (interface_->GetTextRange(addr, size)) {
-    *addr += load_bias_;
-    return true;
-  }
-
-  if (gnu_debugdata_interface_ != nullptr && gnu_debugdata_interface_->GetTextRange(addr, size)) {
-    *addr += load_bias_;
-    return true;
-  }
-
-  return false;
-}
-
 ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
   if (!IsValidElf(memory)) {
     return nullptr;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index e09a2ae..dee8eb3 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -69,15 +69,6 @@
   return false;
 }
 
-bool ElfInterface::GetTextRange(uint64_t* addr, uint64_t* size) {
-  if (text_size_ != 0) {
-    *addr = text_addr_;
-    *size = text_size_;
-    return true;
-  }
-  return false;
-}
-
 Memory* ElfInterface::CreateGnuDebugdataMemory() {
   if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
     return nullptr;
@@ -285,7 +276,7 @@
         if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
           return "";
         }
-        std::string build_id(hdr.n_descsz - 1, '\0');
+        std::string build_id(hdr.n_descsz, '\0');
         if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
           return build_id;
         }
@@ -339,26 +330,29 @@
       }
       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 || shdr.sh_type == SHT_NOBITS) {
+    } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) {
       // Look for the .debug_frame and .gnu_debugdata.
       if (shdr.sh_name < sec_size) {
         std::string name;
         if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+          uint64_t* offset_ptr = nullptr;
+          uint64_t* size_ptr = nullptr;
           if (name == ".debug_frame") {
-            debug_frame_offset_ = shdr.sh_offset;
-            debug_frame_size_ = shdr.sh_size;
+            offset_ptr = &debug_frame_offset_;
+            size_ptr = &debug_frame_size_;
           } else if (name == ".gnu_debugdata") {
-            gnu_debugdata_offset_ = shdr.sh_offset;
-            gnu_debugdata_size_ = shdr.sh_size;
+            offset_ptr = &gnu_debugdata_offset_;
+            size_ptr = &gnu_debugdata_size_;
           } else if (name == ".eh_frame") {
-            eh_frame_offset_ = shdr.sh_offset;
-            eh_frame_size_ = shdr.sh_size;
+            offset_ptr = &eh_frame_offset_;
+            size_ptr = &eh_frame_size_;
           } else if (eh_frame_hdr_offset_ == 0 && name == ".eh_frame_hdr") {
-            eh_frame_hdr_offset_ = shdr.sh_offset;
-            eh_frame_hdr_size_ = shdr.sh_size;
-          } else if (name == ".text") {
-            text_addr_ = shdr.sh_addr;
-            text_size_ = shdr.sh_size;
+            offset_ptr = &eh_frame_hdr_offset_;
+            size_ptr = &eh_frame_hdr_size_;
+          }
+          if (offset_ptr != nullptr) {
+            *offset_ptr = shdr.sh_offset;
+            *size_ptr = shdr.sh_size;
           }
         }
       }
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index 71665a1..20bc4b9 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -16,13 +16,8 @@
 
 #include <stdint.h>
 #include <sys/mman.h>
-#include <cstddef>
 
-#include <atomic>
-#include <deque>
-#include <map>
 #include <memory>
-#include <unordered_set>
 #include <vector>
 
 #include <unwindstack/Elf.h>
@@ -30,334 +25,197 @@
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 
-#if !defined(NO_LIBDEXFILE_SUPPORT)
-#include <DexFile.h>
-#endif
-
 // This implements the JIT Compilation Interface.
 // See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
 
 namespace unwindstack {
 
-// 32-bit platforms may differ in alignment of uint64_t.
-struct Uint64_P {
-  uint64_t value;
+struct JITCodeEntry32Pack {
+  uint32_t next;
+  uint32_t prev;
+  uint32_t symfile_addr;
+  uint64_t symfile_size;
 } __attribute__((packed));
-struct Uint64_A {
-  uint64_t value;
-} __attribute__((aligned(8)));
 
-// Wrapper around other memory object which protects us against data races.
-// It will check seqlock after every read, and fail if the seqlock changed.
-// This ensues that the read memory has not been partially modified.
-struct JitMemory : public Memory {
-  size_t Read(uint64_t addr, void* dst, size_t size) override;
-
-  Memory* parent_ = nullptr;
-  uint64_t seqlock_addr_ = 0;
-  uint32_t expected_seqlock_ = 0;
-  bool failed_due_to_race_ = false;
+struct JITCodeEntry32Pad {
+  uint32_t next;
+  uint32_t prev;
+  uint32_t symfile_addr;
+  uint32_t pad;
+  uint64_t symfile_size;
 };
 
-template <typename Symfile>
-struct JitCacheEntry {
-  // PC memory range described by this entry.
-  uint64_t addr_ = 0;
-  uint64_t size_ = 0;
-  std::unique_ptr<Symfile> symfile_;
-
-  bool Init(Maps* maps, JitMemory* memory, uint64_t addr, uint64_t size);
+struct JITCodeEntry64 {
+  uint64_t next;
+  uint64_t prev;
+  uint64_t symfile_addr;
+  uint64_t symfile_size;
 };
 
-template <typename Symfile, typename PointerT, typename Uint64_T>
-class JitDebugImpl : public JitDebug<Symfile>, public Global {
- public:
-  static constexpr const char* kDescriptorExtMagic = "Android1";
-  static constexpr int kMaxRaceRetries = 16;
+struct JITDescriptorHeader {
+  uint32_t version;
+  uint32_t action_flag;
+};
 
-  struct JITCodeEntry {
-    PointerT next;
-    PointerT prev;
-    PointerT symfile_addr;
-    Uint64_T symfile_size;
-  };
+struct JITDescriptor32 {
+  JITDescriptorHeader header;
+  uint32_t relevant_entry;
+  uint32_t first_entry;
+};
 
-  struct JITDescriptor {
-    uint32_t version;
-    uint32_t action_flag;
-    PointerT relevant_entry;
-    PointerT first_entry;
-  };
+struct JITDescriptor64 {
+  JITDescriptorHeader header;
+  uint64_t relevant_entry;
+  uint64_t first_entry;
+};
 
-  // Android-specific extensions.
-  struct JITDescriptorExt {
-    JITDescriptor desc;
-    uint8_t magic[8];
-    uint32_t flags;
-    uint32_t sizeof_descriptor;
-    uint32_t sizeof_entry;
-    uint32_t action_seqlock;
-    uint64_t action_timestamp;
-  };
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : Global(memory) {}
 
-  JitDebugImpl(ArchEnum arch, std::shared_ptr<Memory>& memory,
-               std::vector<std::string>& search_libs)
-      : Global(memory, search_libs) {
-    SetArch(arch);
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+    : Global(memory, search_libs) {}
+
+JitDebug::~JitDebug() {
+  for (auto* elf : elf_list_) {
+    delete elf;
+  }
+}
+
+uint64_t JitDebug::ReadDescriptor32(uint64_t addr) {
+  JITDescriptor32 desc;
+  if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
+    return 0;
   }
 
-  Symfile* Get(Maps* maps, uint64_t pc) override;
-  virtual bool ReadVariableData(uint64_t offset);
-  virtual void ProcessArch() {}
-  bool Update(Maps* maps);
-  bool Read(Maps* maps, JitMemory* memory);
+  if (desc.header.version != 1 || desc.first_entry == 0) {
+    // Either unknown version, or no jit entries.
+    return 0;
+  }
 
-  bool initialized_ = false;
-  uint64_t descriptor_addr_ = 0;  // Non-zero if we have found (non-empty) descriptor.
-  uint64_t seqlock_addr_ = 0;     // Re-read entries if the value at this address changes.
-  uint32_t last_seqlock_ = ~0u;   // The value of seqlock when we last read the entries.
+  return desc.first_entry;
+}
 
-  std::deque<JitCacheEntry<Symfile>> entries_;
+uint64_t JitDebug::ReadDescriptor64(uint64_t addr) {
+  JITDescriptor64 desc;
+  if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
+    return 0;
+  }
 
-  std::mutex lock_;
-};
+  if (desc.header.version != 1 || desc.first_entry == 0) {
+    // Either unknown version, or no jit entries.
+    return 0;
+  }
 
-template <typename Symfile>
-std::unique_ptr<JitDebug<Symfile>> JitDebug<Symfile>::Create(ArchEnum arch,
-                                                             std::shared_ptr<Memory>& memory,
-                                                             std::vector<std::string> search_libs) {
-  typedef JitDebugImpl<Symfile, uint32_t, Uint64_P> JitDebugImpl32P;
-  typedef JitDebugImpl<Symfile, uint32_t, Uint64_A> JitDebugImpl32A;
-  typedef JitDebugImpl<Symfile, uint64_t, Uint64_A> JitDebugImpl64A;
-  switch (arch) {
+  return desc.first_entry;
+}
+
+uint64_t JitDebug::ReadEntry32Pack(uint64_t* start, uint64_t* size) {
+  JITCodeEntry32Pack code;
+  if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+    return 0;
+  }
+
+  *start = code.symfile_addr;
+  *size = code.symfile_size;
+  return code.next;
+}
+
+uint64_t JitDebug::ReadEntry32Pad(uint64_t* start, uint64_t* size) {
+  JITCodeEntry32Pad code;
+  if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+    return 0;
+  }
+
+  *start = code.symfile_addr;
+  *size = code.symfile_size;
+  return code.next;
+}
+
+uint64_t JitDebug::ReadEntry64(uint64_t* start, uint64_t* size) {
+  JITCodeEntry64 code;
+  if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+    return 0;
+  }
+
+  *start = code.symfile_addr;
+  *size = code.symfile_size;
+  return code.next;
+}
+
+void JitDebug::ProcessArch() {
+  switch (arch()) {
     case ARCH_X86:
-      static_assert(sizeof(typename JitDebugImpl32P::JITCodeEntry) == 20, "layout");
-      static_assert(sizeof(typename JitDebugImpl32P::JITDescriptor) == 16, "layout");
-      static_assert(sizeof(typename JitDebugImpl32P::JITDescriptorExt) == 48, "layout");
-      return std::unique_ptr<JitDebug>(new JitDebugImpl32P(arch, memory, search_libs));
+      read_descriptor_func_ = &JitDebug::ReadDescriptor32;
+      read_entry_func_ = &JitDebug::ReadEntry32Pack;
       break;
+
     case ARCH_ARM:
     case ARCH_MIPS:
-      static_assert(sizeof(typename JitDebugImpl32A::JITCodeEntry) == 24, "layout");
-      static_assert(sizeof(typename JitDebugImpl32A::JITDescriptor) == 16, "layout");
-      static_assert(sizeof(typename JitDebugImpl32A::JITDescriptorExt) == 48, "layout");
-      return std::unique_ptr<JitDebug>(new JitDebugImpl32A(arch, memory, search_libs));
+      read_descriptor_func_ = &JitDebug::ReadDescriptor32;
+      read_entry_func_ = &JitDebug::ReadEntry32Pad;
       break;
+
     case ARCH_ARM64:
     case ARCH_X86_64:
     case ARCH_MIPS64:
-      static_assert(sizeof(typename JitDebugImpl64A::JITCodeEntry) == 32, "layout");
-      static_assert(sizeof(typename JitDebugImpl64A::JITDescriptor) == 24, "layout");
-      static_assert(sizeof(typename JitDebugImpl64A::JITDescriptorExt) == 56, "layout");
-      return std::unique_ptr<JitDebug>(new JitDebugImpl64A(arch, memory, search_libs));
+      read_descriptor_func_ = &JitDebug::ReadDescriptor64;
+      read_entry_func_ = &JitDebug::ReadEntry64;
       break;
-    default:
+    case ARCH_UNKNOWN:
       abort();
   }
 }
 
-size_t JitMemory::Read(uint64_t addr, void* dst, size_t size) {
-  if (!parent_->ReadFully(addr, dst, size)) {
-    return 0;
-  }
-  // This is required for memory synchronization if the we are working with local memory.
-  // For other types of memory (e.g. remote) this is no-op and has no significant effect.
-  std::atomic_thread_fence(std::memory_order_acquire);
-  uint32_t seen_seqlock;
-  if (!parent_->Read32(seqlock_addr_, &seen_seqlock)) {
-    return 0;
-  }
-  if (seen_seqlock != expected_seqlock_) {
-    failed_due_to_race_ = true;
-    return 0;
-  }
-  return size;
+bool JitDebug::ReadVariableData(uint64_t ptr) {
+  entry_addr_ = (this->*read_descriptor_func_)(ptr);
+  return entry_addr_ != 0;
 }
 
-template <typename Symfile, typename PointerT, typename Uint64_T>
-bool JitDebugImpl<Symfile, PointerT, Uint64_T>::ReadVariableData(uint64_t addr) {
-  JITDescriptor desc;
-  if (!this->memory_->ReadFully(addr, &desc, sizeof(desc))) {
-    return false;
+void JitDebug::Init(Maps* maps) {
+  if (initialized_) {
+    return;
   }
-  if (desc.version != 1) {
-    return false;
-  }
-  if (desc.first_entry == 0) {
-    return false;  // There could be multiple descriptors. Ignore empty ones.
-  }
-  descriptor_addr_ = addr;
-  JITDescriptorExt desc_ext;
-  if (this->memory_->ReadFully(addr, &desc_ext, sizeof(desc_ext)) &&
-      memcmp(desc_ext.magic, kDescriptorExtMagic, 8) == 0) {
-    seqlock_addr_ = descriptor_addr_ + offsetof(JITDescriptorExt, action_seqlock);
-  } else {
-    // In the absence of Android-specific fields, use the head pointer instead.
-    seqlock_addr_ = descriptor_addr_ + offsetof(JITDescriptor, first_entry);
-  }
-  return true;
+  // Regardless of what happens below, consider the init finished.
+  initialized_ = true;
+
+  FindAndReadVariable(maps, "__jit_debug_descriptor");
 }
 
-template <typename Symfile>
-static const char* GetDescriptorName();
-
-template <>
-const char* GetDescriptorName<Elf>() {
-  return "__jit_debug_descriptor";
-}
-
-template <typename Symfile, typename PointerT, typename Uint64_T>
-Symfile* JitDebugImpl<Symfile, PointerT, Uint64_T>::Get(Maps* maps, uint64_t pc) {
+Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) {
+  // Use a single lock, this object should be used so infrequently that
+  // a fine grain lock is unnecessary.
   std::lock_guard<std::mutex> guard(lock_);
   if (!initialized_) {
-    FindAndReadVariable(maps, GetDescriptorName<Symfile>());
-    initialized_ = true;
+    Init(maps);
   }
 
-  if (descriptor_addr_ == 0) {
-    return nullptr;
-  }
-
-  if (!Update(maps)) {
-    return nullptr;
-  }
-
-  Symfile* fallback = nullptr;
-  for (auto& entry : entries_) {
-    // Skip entries which are obviously not relevant (if we know the PC range).
-    if (entry.size_ == 0 || (entry.addr_ <= pc && (pc - entry.addr_) < entry.size_)) {
-      // Double check the entry contains the PC in case there are overlapping entries.
-      // This is might happen for native-code due to GC and for DEX due to data sharing.
-      std::string method_name;
-      uint64_t method_offset;
-      if (entry.symfile_->GetFunctionName(pc, &method_name, &method_offset)) {
-        return entry.symfile_.get();
-      }
-      fallback = entry.symfile_.get();  // Tests don't have any symbols.
-    }
-  }
-  return fallback;  // Not found.
-}
-
-// Update JIT entries if needed.  It will retry if there are data races.
-template <typename Symfile, typename PointerT, typename Uint64_T>
-bool JitDebugImpl<Symfile, PointerT, Uint64_T>::Update(Maps* maps) {
-  // We might need to retry the whole read in the presence of data races.
-  for (int i = 0; i < kMaxRaceRetries; i++) {
-    // Read the seqlock (counter which is incremented before and after any modification).
-    uint32_t seqlock = 0;
-    if (!this->memory_->Read32(seqlock_addr_, &seqlock)) {
-      return false;  // Failed to read seqlock.
-    }
-
-    // Check if anything changed since the last time we checked.
-    if (last_seqlock_ != seqlock) {
-      // Create memory wrapper to allow us to read the entries safely even in a live process.
-      JitMemory safe_memory;
-      safe_memory.parent_ = this->memory_.get();
-      safe_memory.seqlock_addr_ = seqlock_addr_;
-      safe_memory.expected_seqlock_ = seqlock;
-      std::atomic_thread_fence(std::memory_order_acquire);
-
-      // Add all entries to our cache.
-      if (!Read(maps, &safe_memory)) {
-        if (safe_memory.failed_due_to_race_) {
-          sleep(0);
-          continue;  // Try again (there was a data race).
-        } else {
-          return false;  // Proper failure (we could not read the data).
-        }
-      }
-      last_seqlock_ = seqlock;
-    }
-    return true;
-  }
-  return false;  // Too many retries.
-}
-
-// Read all JIT entries.  It might randomly fail due to data races.
-template <typename Symfile, typename PointerT, typename Uint64_T>
-bool JitDebugImpl<Symfile, PointerT, Uint64_T>::Read(Maps* maps, JitMemory* memory) {
-  std::unordered_set<uint64_t> seen_entry_addr;
-
-  // Read and verify the descriptor (must be after we have read the initial seqlock).
-  JITDescriptor desc;
-  if (!(memory->ReadFully(descriptor_addr_, &desc, sizeof(desc)))) {
-    return false;
-  }
-
-  entries_.clear();
-  JITCodeEntry entry;
-  for (uint64_t entry_addr = desc.first_entry; entry_addr != 0; entry_addr = entry.next) {
-    // Check for infinite loops in the lined list.
-    if (!seen_entry_addr.emplace(entry_addr).second) {
-      return true;  // TODO: Fail when seening infinite loop.
-    }
-
-    // Read the entry (while checking for data races).
-    if (!memory->ReadFully(entry_addr, &entry, sizeof(entry))) {
-      return false;
-    }
-
-    // Copy and load the symfile.
-    entries_.emplace_back(JitCacheEntry<Symfile>());
-    if (!entries_.back().Init(maps, memory, entry.symfile_addr, entry.symfile_size.value)) {
-      return false;
+  // Search the existing elf object first.
+  for (Elf* elf : elf_list_) {
+    if (elf->IsValidPc(pc)) {
+      return elf;
     }
   }
 
-  return true;
-}
+  while (entry_addr_ != 0) {
+    uint64_t start;
+    uint64_t size;
+    entry_addr_ = (this->*read_entry_func_)(&start, &size);
 
-// Copy and load ELF file.
-template <>
-bool JitCacheEntry<Elf>::Init(Maps*, JitMemory* memory, uint64_t addr, uint64_t size) {
-  // Make a copy of the in-memory symbol file (while checking for data races).
-  std::unique_ptr<MemoryBuffer> buffer(new MemoryBuffer());
-  buffer->Resize(size);
-  if (!memory->ReadFully(addr, buffer->GetPtr(0), buffer->Size())) {
-    return false;
+    Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
+    elf->Init();
+    if (!elf->valid()) {
+      // The data is not formatted in a way we understand, do not attempt
+      // to process any other entries.
+      entry_addr_ = 0;
+      delete elf;
+      return nullptr;
+    }
+    elf_list_.push_back(elf);
+
+    if (elf->IsValidPc(pc)) {
+      return elf;
+    }
   }
-
-  // Load and validate the ELF file.
-  symfile_.reset(new Elf(buffer.release()));
-  symfile_->Init();
-  if (!symfile_->valid()) {
-    return false;
-  }
-
-  symfile_->GetTextRange(&addr_, &size_);
-  return true;
+  return nullptr;
 }
 
-template std::unique_ptr<JitDebug<Elf>> JitDebug<Elf>::Create(ArchEnum, std::shared_ptr<Memory>&,
-                                                              std::vector<std::string>);
-
-#if !defined(NO_LIBDEXFILE_SUPPORT)
-
-template <>
-const char* GetDescriptorName<DexFile>() {
-  return "__dex_debug_descriptor";
-}
-
-// Copy and load DEX file.
-template <>
-bool JitCacheEntry<DexFile>::Init(Maps* maps, JitMemory* memory, uint64_t addr, uint64_t) {
-  MapInfo* info = maps->Find(addr);
-  if (info == nullptr) {
-    return false;
-  }
-  symfile_ = DexFile::Create(addr, memory, info);
-  if (symfile_ == nullptr) {
-    return false;
-  }
-  return true;
-}
-
-template std::unique_ptr<JitDebug<DexFile>> JitDebug<DexFile>::Create(ArchEnum,
-                                                                      std::shared_ptr<Memory>&,
-                                                                      std::vector<std::string>);
-
-#endif
-
 }  // namespace unwindstack
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 73c5a04..3f2e1c1 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -36,38 +36,11 @@
 #include <unwindstack/Unwinder.h>
 
 #if !defined(NO_LIBDEXFILE_SUPPORT)
-#include <DexFile.h>
+#include <unwindstack/DexFiles.h>
 #endif
 
 namespace unwindstack {
 
-Unwinder::Unwinder(size_t max_frames, Maps* maps, Regs* regs,
-                   std::shared_ptr<Memory> process_memory)
-    : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
-  frames_.reserve(max_frames);
-  if (regs != nullptr) {
-    ArchEnum arch = regs_->Arch();
-
-    jit_debug_ = JitDebug<Elf>::Create(arch, process_memory_);
-#if !defined(NO_LIBDEXFILE_SUPPORT)
-    dex_files_ = JitDebug<DexFile>::Create(arch, process_memory_);
-#endif
-  }
-}
-
-void Unwinder::SetRegs(Regs* regs) {
-  regs_ = regs;
-
-  if (jit_debug_ == nullptr) {
-    ArchEnum arch = regs_->Arch();
-
-    jit_debug_ = JitDebug<Elf>::Create(arch, process_memory_);
-#if !defined(NO_LIBDEXFILE_SUPPORT)
-    dex_files_ = JitDebug<DexFile>::Create(arch, process_memory_);
-#endif
-  }
-}
-
 // Inject extra 'virtual' frame that represents the dex pc data.
 // The dex pc is a magic register defined in the Mterp interpreter,
 // and thus it will be restored/observed in the frame after it.
@@ -111,7 +84,8 @@
     return;
   }
 
-  dex_files_->GetFunctionName(maps_, dex_pc, &frame->function_name, &frame->function_offset);
+  dex_files_->GetMethodInformation(maps_, info, dex_pc, &frame->function_name,
+                                   &frame->function_offset);
 #endif
 }
 
@@ -211,7 +185,7 @@
       // using the jit debug information.
       if (!elf->valid() && jit_debug_ != nullptr) {
         uint64_t adjusted_jit_pc = regs_->pc() - pc_adjustment;
-        Elf* jit_elf = jit_debug_->Get(maps_, adjusted_jit_pc);
+        Elf* jit_elf = jit_debug_->GetElf(maps_, adjusted_jit_pc);
         if (jit_elf != nullptr) {
           // The jit debug information requires a non relative adjusted pc.
           step_pc = adjusted_jit_pc;
@@ -356,7 +330,19 @@
   return FormatFrame(frames_[frame_num]);
 }
 
-bool UnwinderFromPid::Init() {
+void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
+  jit_debug->SetArch(arch);
+  jit_debug_ = jit_debug;
+}
+
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) {
+  dex_files->SetArch(arch);
+  dex_files_ = dex_files;
+}
+#endif
+
+bool UnwinderFromPid::Init(ArchEnum arch) {
   if (pid_ == getpid()) {
     maps_ptr_.reset(new LocalMaps());
   } else {
@@ -369,6 +355,15 @@
 
   process_memory_ = Memory::CreateProcessMemoryCached(pid_);
 
+  jit_debug_ptr_.reset(new JitDebug(process_memory_));
+  jit_debug_ = jit_debug_ptr_.get();
+  SetJitDebug(jit_debug_, arch);
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  dex_files_ptr_.reset(new DexFiles(process_memory_));
+  dex_files_ = dex_files_ptr_.get();
+  SetDexFiles(dex_files_, arch);
+#endif
+
   return true;
 }
 
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
new file mode 100644
index 0000000..67a9640
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -0,0 +1,79 @@
+/*
+ * 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_DEX_FILES_H
+#define _LIBUNWINDSTACK_DEX_FILES_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <unwindstack/Global.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class DexFile;
+class Maps;
+struct MapInfo;
+enum ArchEnum : uint8_t;
+
+class DexFiles : public Global {
+ public:
+  explicit DexFiles(std::shared_ptr<Memory>& memory);
+  DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
+  virtual ~DexFiles();
+
+  DexFile* GetDexFile(uint64_t dex_file_offset, MapInfo* info);
+
+  void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name,
+                            uint64_t* method_offset);
+
+ 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();
+
+  bool ReadVariableData(uint64_t ptr_offset) override;
+
+  void ProcessArch() override;
+
+  std::mutex lock_;
+  bool initialized_ = false;
+  std::unordered_map<uint64_t, std::unique_ptr<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
+
+#endif  // _LIBUNWINDSTACK_DEX_FILES_H
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 25a665d..ac94f10 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -78,8 +78,6 @@
 
   bool IsValidPc(uint64_t pc);
 
-  bool GetTextRange(uint64_t* addr, uint64_t* size);
-
   void GetLastError(ErrorData* data);
   ErrorCode GetLastErrorCode();
   uint64_t GetLastErrorAddress();
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 945c277..dbd917d 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -68,8 +68,6 @@
 
   virtual bool IsValidPc(uint64_t pc);
 
-  bool GetTextRange(uint64_t* addr, uint64_t* size);
-
   Memory* CreateGnuDebugdataMemory();
 
   Memory* memory() { return memory_; }
@@ -158,9 +156,6 @@
   uint64_t gnu_build_id_offset_ = 0;
   uint64_t gnu_build_id_size_ = 0;
 
-  uint64_t text_addr_ = 0;
-  uint64_t text_size_ = 0;
-
   uint8_t soname_type_ = SONAME_UNKNOWN;
   std::string soname_;
 
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
index 0c3ded9..8b7b4b5 100644
--- a/libunwindstack/include/unwindstack/JitDebug.h
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -19,7 +19,6 @@
 
 #include <stdint.h>
 
-#include <map>
 #include <memory>
 #include <mutex>
 #include <string>
@@ -31,24 +30,40 @@
 namespace unwindstack {
 
 // Forward declarations.
+class Elf;
 class Maps;
 enum ArchEnum : uint8_t;
 
-template <typename Symfile>
-class JitDebug {
+class JitDebug : public Global {
  public:
-  static std::unique_ptr<JitDebug> Create(ArchEnum arch, std::shared_ptr<Memory>& memory,
-                                          std::vector<std::string> search_libs = {});
-  virtual ~JitDebug() {}
+  explicit JitDebug(std::shared_ptr<Memory>& memory);
+  JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
+  virtual ~JitDebug();
 
-  // Find symbol file for given pc.
-  virtual Symfile* Get(Maps* maps, uint64_t pc) = 0;
+  Elf* GetElf(Maps* maps, uint64_t pc);
 
-  // Find symbol for given pc.
-  bool GetFunctionName(Maps* maps, uint64_t pc, std::string* name, uint64_t* offset) {
-    Symfile* file = Get(maps, pc);
-    return file != nullptr && file->GetFunctionName(pc, name, offset);
-  }
+ private:
+  void Init(Maps* maps);
+
+  uint64_t (JitDebug::*read_descriptor_func_)(uint64_t) = nullptr;
+  uint64_t (JitDebug::*read_entry_func_)(uint64_t*, uint64_t*) = nullptr;
+
+  uint64_t ReadDescriptor32(uint64_t);
+  uint64_t ReadDescriptor64(uint64_t);
+
+  uint64_t ReadEntry32Pack(uint64_t* start, uint64_t* size);
+  uint64_t ReadEntry32Pad(uint64_t* start, uint64_t* size);
+  uint64_t ReadEntry64(uint64_t* start, uint64_t* size);
+
+  bool ReadVariableData(uint64_t ptr_offset) override;
+
+  void ProcessArch() override;
+
+  uint64_t entry_addr_ = 0;
+  bool initialized_ = false;
+  std::vector<Elf*> elf_list_;
+
+  std::mutex lock_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index a04d7c3..8b01654 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -24,6 +24,7 @@
 #include <string>
 #include <vector>
 
+#include <unwindstack/DexFiles.h>
 #include <unwindstack/Error.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
@@ -33,7 +34,6 @@
 namespace unwindstack {
 
 // Forward declarations.
-class DexFile;
 class Elf;
 enum ArchEnum : uint8_t;
 
@@ -63,14 +63,14 @@
 
 class Unwinder {
  public:
-  Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory);
+  Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory)
+      : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
+    frames_.reserve(max_frames);
+  }
   Unwinder(size_t max_frames, Maps* maps, std::shared_ptr<Memory> process_memory)
-      : Unwinder(max_frames, maps, nullptr, process_memory) {}
-
-  Unwinder(const Unwinder&) = delete;
-  Unwinder& operator=(const Unwinder&) = delete;
-  Unwinder(Unwinder&&) = default;
-  Unwinder& operator=(Unwinder&&) = default;
+      : max_frames_(max_frames), maps_(maps), process_memory_(process_memory) {
+    frames_.reserve(max_frames);
+  }
 
   virtual ~Unwinder() = default;
 
@@ -90,7 +90,9 @@
   std::string FormatFrame(size_t frame_num);
   std::string FormatFrame(const FrameData& frame);
 
-  void SetRegs(Regs* regs);
+  void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
+
+  void SetRegs(Regs* regs) { regs_ = regs; }
   Maps* GetMaps() { return maps_; }
   std::shared_ptr<Memory>& GetProcessMemory() { return process_memory_; }
 
@@ -105,6 +107,10 @@
 
   void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
 
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
+#endif
+
   ErrorCode LastErrorCode() { return last_error_.code; }
   uint64_t LastErrorAddress() { return last_error_.address; }
 
@@ -120,9 +126,9 @@
   Regs* regs_;
   std::vector<FrameData> frames_;
   std::shared_ptr<Memory> process_memory_;
-  std::unique_ptr<JitDebug<Elf>> jit_debug_;
+  JitDebug* jit_debug_ = nullptr;
 #if !defined(NO_LIBDEXFILE_SUPPORT)
-  std::unique_ptr<JitDebug<DexFile>> dex_files_;
+  DexFiles* dex_files_ = nullptr;
 #endif
   bool resolve_names_ = true;
   bool embedded_soname_ = true;
@@ -135,11 +141,15 @@
   UnwinderFromPid(size_t max_frames, pid_t pid) : Unwinder(max_frames), pid_(pid) {}
   virtual ~UnwinderFromPid() = default;
 
-  bool Init();
+  bool Init(ArchEnum arch);
 
  private:
   pid_t pid_;
   std::unique_ptr<Maps> maps_ptr_;
+  std::unique_ptr<JitDebug> jit_debug_ptr_;
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  std::unique_ptr<DexFiles> dex_files_ptr_;
+#endif
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index df7b31d..0149a42 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -177,11 +177,11 @@
 
   std::string method;
   uint64_t method_offset;
-  ASSERT_TRUE(dex_file->GetFunctionName(0x4102, &method, &method_offset));
+  ASSERT_TRUE(dex_file->GetMethodInformation(0x102, &method, &method_offset));
   EXPECT_EQ("Main.<init>", method);
   EXPECT_EQ(2U, method_offset);
 
-  ASSERT_TRUE(dex_file->GetFunctionName(0x4118, &method, &method_offset));
+  ASSERT_TRUE(dex_file->GetMethodInformation(0x118, &method, &method_offset));
   EXPECT_EQ("Main.main", method);
   EXPECT_EQ(0U, method_offset);
 }
@@ -195,9 +195,9 @@
 
   std::string method;
   uint64_t method_offset;
-  EXPECT_FALSE(dex_file->GetFunctionName(0x100000, &method, &method_offset));
+  EXPECT_FALSE(dex_file->GetMethodInformation(0x100000, &method, &method_offset));
 
-  EXPECT_FALSE(dex_file->GetFunctionName(0x98, &method, &method_offset));
+  EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index 655dcc8..1ea9e5c 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -22,8 +22,8 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/DexFiles.h>
 #include <unwindstack/Elf.h>
-#include <unwindstack/JitDebug.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
@@ -32,10 +32,6 @@
 #include "ElfFake.h"
 #include "MemoryFake.h"
 
-#if !defined(NO_LIBDEXFILE_SUPPORT)
-#include <DexFile.h>
-#endif
-
 namespace unwindstack {
 
 class DexFilesTest : public ::testing::Test {
@@ -52,7 +48,8 @@
   }
 
   void Init(ArchEnum arch) {
-    dex_files_ = JitDebug<DexFile>::Create(arch, process_memory_);
+    dex_files_.reset(new DexFiles(process_memory_));
+    dex_files_->SetArch(arch);
 
     maps_.reset(
         new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf\n"
@@ -89,11 +86,10 @@
     Init(ARCH_ARM);
   }
 
-  void WriteDescriptor32(uint64_t addr, uint32_t entry);
-  void WriteDescriptor64(uint64_t addr, uint64_t entry);
-  void WriteEntry32Pack(uint64_t addr, uint32_t next, uint32_t prev, uint32_t dex);
-  void WriteEntry32Pad(uint64_t addr, uint32_t next, uint32_t prev, uint32_t dex);
-  void WriteEntry64(uint64_t addr, uint64_t next, uint64_t prev, uint64_t dex);
+  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 kMapGlobalNonReadable = 2;
@@ -105,70 +101,40 @@
 
   std::shared_ptr<Memory> process_memory_;
   MemoryFake* memory_;
-  std::unique_ptr<JitDebug<DexFile>> dex_files_;
+  std::unique_ptr<DexFiles> dex_files_;
   std::unique_ptr<BufferMaps> maps_;
 };
 
-void DexFilesTest::WriteDescriptor32(uint64_t addr, uint32_t entry) {
-  // Format of the 32 bit JITDescriptor structure:
-  //   uint32_t version
-  memory_->SetData32(addr, 1);
-  //   uint32_t action_flag
-  memory_->SetData32(addr + 4, 0);
-  //   uint32_t relevant_entry
-  memory_->SetData32(addr + 8, 0);
-  //   uint32_t first_entry
-  memory_->SetData32(addr + 12, entry);
+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 entry) {
-  // Format of the 64 bit JITDescriptor structure:
-  //   uint32_t version
-  memory_->SetData32(addr, 1);
-  //   uint32_t action_flag
-  memory_->SetData32(addr + 4, 0);
-  //   uint64_t relevant_entry
-  memory_->SetData64(addr + 8, 0);
-  //   uint64_t first_entry
-  memory_->SetData64(addr + 16, entry);
+void DexFilesTest::WriteDescriptor64(uint64_t addr, uint64_t head) {
+  //   void* first_entry_
+  memory_->SetData64(addr + 16, head);
 }
 
-void DexFilesTest::WriteEntry32Pack(uint64_t addr, uint32_t next, uint32_t prev, uint32_t dex) {
-  // Format of the 32 bit JITCodeEntry structure:
+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(addr, next);
+  memory_->SetData32(entry_addr, next);
   //   uint32_t prev
-  memory_->SetData32(addr + 4, prev);
-  //   uint32_t dex
-  memory_->SetData32(addr + 8, dex);
-  //   uint64_t symfile_size
-  memory_->SetData64(addr + 12, sizeof(kDexData) * sizeof(uint32_t));
+  memory_->SetData32(entry_addr + 4, prev);
+  //   uint32_t dex_file
+  memory_->SetData32(entry_addr + 8, dex_file);
 }
 
-void DexFilesTest::WriteEntry32Pad(uint64_t addr, uint32_t next, uint32_t prev, uint32_t dex) {
-  // Format of the 32 bit JITCodeEntry structure:
-  //   uint32_t next
-  memory_->SetData32(addr, next);
-  //   uint32_t prev
-  memory_->SetData32(addr + 4, prev);
-  //   uint32_t dex
-  memory_->SetData32(addr + 8, dex);
-  //   uint32_t pad
-  memory_->SetData32(addr + 12, 0);
-  //   uint64_t symfile_size
-  memory_->SetData64(addr + 16, sizeof(kDexData) * sizeof(uint32_t));
-}
-
-void DexFilesTest::WriteEntry64(uint64_t addr, uint64_t next, uint64_t prev, uint64_t dex) {
-  // Format of the 64 bit JITCodeEntry structure:
+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(addr, next);
+  memory_->SetData64(entry_addr, next);
   //   uint64_t prev
-  memory_->SetData64(addr + 8, prev);
-  //   uint64_t dex
-  memory_->SetData64(addr + 16, dex);
-  //   uint64_t symfile_size
-  memory_->SetData64(addr + 24, sizeof(kDexData) * sizeof(uint32_t));
+  memory_->SetData64(entry_addr + 8, prev);
+  //   uint64_t dex_file
+  memory_->SetData64(entry_addr + 16, dex_file);
 }
 
 void DexFilesTest::WriteDex(uint64_t dex_file) {
@@ -178,8 +144,9 @@
 TEST_F(DexFilesTest, get_method_information_invalid) {
   std::string method_name = "nothing";
   uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFileEntries);
 
-  dex_files_->GetFunctionName(maps_.get(), 0, &method_name, &method_offset);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0, &method_name, &method_offset);
   EXPECT_EQ("nothing", method_name);
   EXPECT_EQ(0x124U, method_offset);
 }
@@ -187,12 +154,13 @@
 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);
-  WriteEntry32Pad(0x200000, 0, 0, 0x300000);
+  WriteEntry32(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
-  dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("Main.<init>", method_name);
   EXPECT_EQ(0U, method_offset);
 }
@@ -202,12 +170,13 @@
 
   std::string method_name = "nothing";
   uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
 
   WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0, 0, 0x301000);
   WriteDex(0x301000);
 
-  dex_files_->GetFunctionName(maps_.get(), 0x301102, &method_name, &method_offset);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x301102, &method_name, &method_offset);
   EXPECT_EQ("Main.<init>", method_name);
   EXPECT_EQ(2U, method_offset);
 }
@@ -215,14 +184,14 @@
 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);
-  WriteEntry32Pad(0x200000, 0x200100, 0, 0x100000);
-  WriteDex(0x100000);
-  WriteEntry32Pad(0x200100, 0, 0x200000, 0x300000);
+  WriteEntry32(0x200000, 0x200100, 0, 0x100000);
+  WriteEntry32(0x200100, 0, 0x200000, 0x300000);
   WriteDex(0x300000);
 
-  dex_files_->GetFunctionName(maps_.get(), 0x300104, &method_name, &method_offset);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
   EXPECT_EQ("Main.<init>", method_name);
   EXPECT_EQ(4U, method_offset);
 }
@@ -232,14 +201,14 @@
 
   std::string method_name = "nothing";
   uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
 
   WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0x200100, 0, 0x100000);
-  WriteDex(0x100000);
   WriteEntry64(0x200100, 0, 0x200000, 0x300000);
   WriteDex(0x300000);
 
-  dex_files_->GetFunctionName(maps_.get(), 0x300106, &method_name, &method_offset);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300106, &method_name, &method_offset);
   EXPECT_EQ("Main.<init>", method_name);
   EXPECT_EQ(6U, method_offset);
 }
@@ -247,18 +216,19 @@
 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);
-  WriteEntry32Pad(0x200000, 0, 0, 0x300000);
+  WriteEntry32(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
-  dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
+  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_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("Main.<init>", method_name);
   EXPECT_EQ(0U, method_offset);
 }
@@ -266,24 +236,26 @@
 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);
-  WriteEntry32Pad(0x200000, 0x200100, 0, 0x100000);
-  WriteDex(0x100000);
-  WriteEntry32Pad(0x200100, 0, 0x200000, 0x300000);
+  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_ = JitDebug<DexFile>::Create(ARCH_ARM, process_memory_, libs);
+  dex_files_.reset(new DexFiles(process_memory_, libs));
+  dex_files_->SetArch(ARCH_ARM);
 
-  dex_files_->GetFunctionName(maps_.get(), 0x300104, &method_name, &method_offset);
+  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_ = JitDebug<DexFile>::Create(ARCH_ARM, process_memory_, libs);
+  dex_files_.reset(new DexFiles(process_memory_, libs));
+  dex_files_->SetArch(ARCH_ARM);
   // Set the rw map to the same name or this will not scan this entry.
   map_info = maps_->Get(kMapGlobalRw);
   map_info->name = "/system/lib/libart.so";
@@ -291,7 +263,7 @@
   // DexFiles object.
   libs.clear();
 
-  dex_files_->GetFunctionName(maps_.get(), 0x300104, &method_name, &method_offset);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
   EXPECT_EQ("Main.<init>", method_name);
   EXPECT_EQ(4U, method_offset);
 }
@@ -299,24 +271,26 @@
 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(0xa800, 0);
 
   WriteDescriptor32(0xf800, 0x200000);
-  WriteEntry32Pad(0x200000, 0, 0, 0x300000);
+  WriteEntry32(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
-  dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
+  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_ = JitDebug<DexFile>::Create(ARCH_ARM, process_memory_);
+  dex_files_.reset(new DexFiles(process_memory_));
+  dex_files_->SetArch(ARCH_ARM);
   method_name = "fail";
   method_offset = 0x123;
   WriteDescriptor32(0xa800, 0x100000);
-  dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("fail", method_name);
   EXPECT_EQ(0x123U, method_offset);
 }
@@ -326,6 +300,7 @@
 
   std::string method_name = "nothing";
   uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
 
   // First global variable found, but value is zero.
   WriteDescriptor64(0xa800, 0);
@@ -334,16 +309,17 @@
   WriteEntry64(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
-  dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
+  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_ = JitDebug<DexFile>::Create(ARCH_ARM64, process_memory_);
+  dex_files_.reset(new DexFiles(process_memory_));
+  dex_files_->SetArch(ARCH_ARM64);
   method_name = "fail";
   method_offset = 0x123;
   WriteDescriptor64(0xa800, 0x100000);
-  dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("fail", method_name);
   EXPECT_EQ(0x123U, method_offset);
 }
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index d895863..cdc927a 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -1192,14 +1192,16 @@
   char note_section[128];
   Nhdr note_header = {};
   note_header.n_namesz = 4;  // "GNU"
-  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_descsz = 7;  // "BUILDID"
   note_header.n_type = NT_GNU_BUILD_ID;
   memcpy(&note_section, &note_header, sizeof(note_header));
   size_t note_offset = sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
   memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
   note_offset += sizeof("GNU");
-  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
-  note_offset += sizeof("BUILDID");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
 
   Shdr shdr = {};
   shdr.sh_type = SHT_NOTE;
@@ -1244,24 +1246,27 @@
   char note_section[128];
   Nhdr note_header = {};
   note_header.n_namesz = 8;  // "WRONG" aligned to 4
-  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_descsz = 7;  // "BUILDID"
   note_header.n_type = NT_GNU_BUILD_ID;
   memcpy(&note_section, &note_header, sizeof(note_header));
   size_t note_offset = sizeof(note_header);
   memcpy(&note_section[note_offset], "WRONG", sizeof("WRONG"));
   note_offset += 8;
-  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
-  note_offset += sizeof("BUILDID");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
 
   note_header.n_namesz = 4;  // "GNU"
-  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_descsz = 7;  // "BUILDID"
   note_header.n_type = NT_GNU_BUILD_ID;
   memcpy(&note_section[note_offset], &note_header, sizeof(note_header));
   note_offset += sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
   memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
   note_offset += sizeof("GNU");
-  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
-  note_offset += sizeof("BUILDID");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
 
   Shdr shdr = {};
   shdr.sh_type = SHT_NOTE;
@@ -1306,14 +1311,16 @@
   char note_section[128];
   Nhdr note_header = {};
   note_header.n_namesz = 4;  // "GNU"
-  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_descsz = 7;  // "BUILDID"
   note_header.n_type = NT_GNU_BUILD_ID;
   memcpy(&note_section, &note_header, sizeof(note_header));
   size_t note_offset = sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
   memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
   note_offset += sizeof("GNU");
-  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
-  note_offset += sizeof("BUILDID");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
 
   Shdr shdr = {};
   shdr.sh_type = SHT_NOTE;
@@ -1358,14 +1365,16 @@
   char note_section[128];
   Nhdr note_header = {};
   note_header.n_namesz = 4;  // "GNU"
-  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_descsz = 7;  // "BUILDID"
   note_header.n_type = NT_GNU_BUILD_ID;
   memcpy(&note_section, &note_header, sizeof(note_header));
   size_t note_offset = sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
   memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
   note_offset += sizeof("GNU");
-  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
-  note_offset += sizeof("BUILDID");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
 
   Shdr shdr = {};
   shdr.sh_type = SHT_NOTE;
@@ -1410,14 +1419,16 @@
   char note_section[128];
   Nhdr note_header = {};
   note_header.n_namesz = 4;  // "GNU"
-  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_descsz = 7;  // "BUILDID"
   note_header.n_type = NT_GNU_BUILD_ID;
   memcpy(&note_section, &note_header, sizeof(note_header));
   size_t note_offset = sizeof(note_header);
+  // The note information contains the GNU and trailing '\0'.
   memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
   note_offset += sizeof("GNU");
-  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
-  note_offset += sizeof("BUILDID");
+  // This part of the note does not contain any trailing '\0'.
+  memcpy(&note_section[note_offset], "BUILDID", 7);
+  note_offset += 8;
 
   Shdr shdr = {};
   shdr.sh_type = SHT_NOTE;
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
index 438194a..b1ca111 100644
--- a/libunwindstack/tests/JitDebugTest.cpp
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -46,7 +46,8 @@
   }
 
   void Init(ArchEnum arch) {
-    jit_debug_ = JitDebug<Elf>::Create(arch, process_memory_);
+    jit_debug_.reset(new JitDebug(process_memory_));
+    jit_debug_->SetArch(arch);
 
     maps_.reset(
         new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf1\n"
@@ -61,12 +62,6 @@
                        "200000-210000 rw-p 0002000 00:00 0 /fake/elf4\n"));
     ASSERT_TRUE(maps_->Parse());
 
-    // Ensure all memory of the ELF file is initialized,
-    // otherwise reads within it may fail.
-    for (uint64_t addr = 0x4000; addr < 0x6000; addr += 8) {
-      memory_->SetData64(addr, 0);
-    }
-
     MapInfo* map_info = maps_->Get(3);
     ASSERT_TRUE(map_info != nullptr);
     CreateFakeElf(map_info);
@@ -99,7 +94,7 @@
     ehdr.e_shstrndx = 1;
     ehdr.e_shoff = sh_offset;
     ehdr.e_shentsize = sizeof(ShdrType);
-    ehdr.e_shnum = 4;
+    ehdr.e_shnum = 3;
     memory_->SetMemory(offset, &ehdr, sizeof(ehdr));
 
     ShdrType shdr;
@@ -115,7 +110,6 @@
     shdr.sh_size = 0x100;
     memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
     memory_->SetMemory(offset + 0x500, ".debug_frame");
-    memory_->SetMemory(offset + 0x550, ".text");
 
     sh_offset += sizeof(shdr);
     memset(&shdr, 0, sizeof(shdr));
@@ -126,15 +120,6 @@
     shdr.sh_size = 0x200;
     memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
 
-    sh_offset += sizeof(shdr);
-    memset(&shdr, 0, sizeof(shdr));
-    shdr.sh_type = SHT_NOBITS;
-    shdr.sh_name = 0x50;
-    shdr.sh_addr = pc;
-    shdr.sh_offset = 0;
-    shdr.sh_size = size;
-    memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
-
     // Now add a single cie/fde.
     uint64_t dwarf_offset = offset + 0x600;
     if (class_type == ELFCLASS32) {
@@ -183,7 +168,7 @@
 
   std::shared_ptr<Memory> process_memory_;
   MemoryFake* memory_;
-  std::unique_ptr<JitDebug<Elf>> jit_debug_;
+  std::unique_ptr<JitDebug> jit_debug_;
   std::unique_ptr<BufferMaps> maps_;
 };
 
@@ -253,20 +238,20 @@
 }
 
 TEST_F(JitDebugTest, get_elf_invalid) {
-  Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf == nullptr);
 }
 
 TEST_F(JitDebugTest, get_elf_no_global_variable) {
   maps_.reset(new BufferMaps(""));
-  Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf == nullptr);
 }
 
 TEST_F(JitDebugTest, get_elf_no_valid_descriptor_in_memory) {
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
 
-  Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf == nullptr);
 }
 
@@ -275,7 +260,7 @@
 
   WriteDescriptor32(0xf800, 0x200000);
 
-  Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf == nullptr);
 }
 
@@ -284,7 +269,7 @@
 
   WriteDescriptor32(0xf800, 0);
 
-  Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf == nullptr);
 }
 
@@ -295,7 +280,7 @@
   // Set the version to an invalid value.
   memory_->SetData32(0xf800, 2);
 
-  Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf == nullptr);
 }
 
@@ -305,18 +290,12 @@
   WriteDescriptor32(0xf800, 0x200000);
   WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
 
-  Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf != nullptr);
-  uint64_t text_addr;
-  uint64_t text_size;
-  ASSERT_TRUE(elf->GetTextRange(&text_addr, &text_size));
-  ASSERT_EQ(text_addr, 0x1500u);
-  ASSERT_EQ(text_size, 0x200u);
 
   // Clear the memory and verify all of the data is cached.
   memory_->Clear();
-  WriteDescriptor32(0xf800, 0x200000);
-  Elf* elf2 = jit_debug_->Get(maps_.get(), 0x1500);
+  Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf2 != nullptr);
   EXPECT_EQ(elf, elf2);
 }
@@ -330,15 +309,16 @@
   WriteDescriptor32(0x12800, 0x201000);
   WriteEntry32Pad(0x201000, 0, 0, 0x5000, 0x1000);
 
-  ASSERT_TRUE(jit_debug_->Get(maps_.get(), 0x1500) != nullptr);
-  ASSERT_TRUE(jit_debug_->Get(maps_.get(), 0x2000) == nullptr);
+  ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
+  ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) == nullptr);
 
   // Now clear the descriptor entry for the first one.
   WriteDescriptor32(0xf800, 0);
-  jit_debug_ = JitDebug<Elf>::Create(ARCH_ARM, process_memory_);
+  jit_debug_.reset(new JitDebug(process_memory_));
+  jit_debug_->SetArch(ARCH_ARM);
 
-  ASSERT_TRUE(jit_debug_->Get(maps_.get(), 0x1500) == nullptr);
-  ASSERT_TRUE(jit_debug_->Get(maps_.get(), 0x2000) != nullptr);
+  ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
+  ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) != nullptr);
 }
 
 TEST_F(JitDebugTest, get_elf_x86) {
@@ -349,14 +329,13 @@
   WriteDescriptor32(0xf800, 0x200000);
   WriteEntry32Pack(0x200000, 0, 0, 0x4000, 0x1000);
 
-  jit_debug_ = JitDebug<Elf>::Create(ARCH_X86, process_memory_);
-  Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
+  jit_debug_->SetArch(ARCH_X86);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf != nullptr);
 
   // Clear the memory and verify all of the data is cached.
   memory_->Clear();
-  WriteDescriptor32(0xf800, 0x200000);
-  Elf* elf2 = jit_debug_->Get(maps_.get(), 0x1500);
+  Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf2 != nullptr);
   EXPECT_EQ(elf, elf2);
 }
@@ -369,13 +348,12 @@
   WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
 
-  Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf != nullptr);
 
   // Clear the memory and verify all of the data is cached.
   memory_->Clear();
-  WriteDescriptor64(0xf800, 0x200000);
-  Elf* elf2 = jit_debug_->Get(maps_.get(), 0x1500);
+  Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf2 != nullptr);
   EXPECT_EQ(elf, elf2);
 }
@@ -388,21 +366,20 @@
   WriteEntry32Pad(0x200000, 0, 0x200100, 0x4000, 0x1000);
   WriteEntry32Pad(0x200100, 0x200100, 0, 0x5000, 0x1000);
 
-  Elf* elf_2 = jit_debug_->Get(maps_.get(), 0x2400);
+  Elf* elf_2 = jit_debug_->GetElf(maps_.get(), 0x2400);
   ASSERT_TRUE(elf_2 != nullptr);
 
-  Elf* elf_1 = jit_debug_->Get(maps_.get(), 0x1600);
+  Elf* elf_1 = jit_debug_->GetElf(maps_.get(), 0x1600);
   ASSERT_TRUE(elf_1 != nullptr);
 
   // Clear the memory and verify all of the data is cached.
   memory_->Clear();
-  WriteDescriptor32(0xf800, 0x200000);
-  EXPECT_EQ(elf_1, jit_debug_->Get(maps_.get(), 0x1500));
-  EXPECT_EQ(elf_1, jit_debug_->Get(maps_.get(), 0x16ff));
-  EXPECT_EQ(elf_2, jit_debug_->Get(maps_.get(), 0x2300));
-  EXPECT_EQ(elf_2, jit_debug_->Get(maps_.get(), 0x26ff));
-  EXPECT_EQ(nullptr, jit_debug_->Get(maps_.get(), 0x1700));
-  EXPECT_EQ(nullptr, jit_debug_->Get(maps_.get(), 0x2700));
+  EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x1500));
+  EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x16ff));
+  EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x2300));
+  EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x26ff));
+  EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x1700));
+  EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x2700));
 }
 
 TEST_F(JitDebugTest, get_elf_search_libs) {
@@ -413,19 +390,21 @@
 
   // Only search a given named list of libs.
   std::vector<std::string> libs{"libart.so"};
-  jit_debug_ = JitDebug<Elf>::Create(ARCH_ARM, process_memory_, libs);
-  EXPECT_TRUE(jit_debug_->Get(maps_.get(), 0x1500) == nullptr);
+  jit_debug_.reset(new JitDebug(process_memory_, libs));
+  jit_debug_->SetArch(ARCH_ARM);
+  EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
 
   // Change the name of the map that includes the value and verify this works.
   MapInfo* map_info = maps_->Get(5);
   map_info->name = "/system/lib/libart.so";
   map_info = maps_->Get(6);
   map_info->name = "/system/lib/libart.so";
-  jit_debug_ = JitDebug<Elf>::Create(ARCH_ARM, process_memory_);
+  jit_debug_.reset(new JitDebug(process_memory_, libs));
   // Make sure that clearing our copy of the libs doesn't affect the
   // JitDebug object.
   libs.clear();
-  EXPECT_TRUE(jit_debug_->Get(maps_.get(), 0x1500) != nullptr);
+  jit_debug_->SetArch(ARCH_ARM);
+  EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 0867561..02ba9c8 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -204,6 +204,7 @@
 TEST_F(UnwindOfflineTest, pc_straddle_arm) {
   ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
 
+  std::unique_ptr<Regs> regs_copy(regs_->Clone());
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
 
@@ -223,6 +224,22 @@
   EXPECT_EQ(0xe9c86730U, unwinder.frames()[2].sp);
   EXPECT_EQ(0xf3367147U, unwinder.frames()[3].pc);
   EXPECT_EQ(0xe9c86778U, unwinder.frames()[3].sp);
+
+  // Display build ids now.
+  unwinder.SetRegs(regs_copy.get());
+  unwinder.SetDisplayBuildID(true);
+  unwinder.Unwind();
+
+  frame_info = DumpFrames(unwinder);
+  ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0001a9f8  libc.so (abort+64) (BuildId: 2dd0d4ba881322a0edabeed94808048c)\n"
+      "  #01 pc 00006a1b  libbase.so (android::base::DefaultAborter(char const*)+6) (BuildId: "
+      "ed43842c239cac1a618e600ea91c4cbd)\n"
+      "  #02 pc 00007441  libbase.so (android::base::LogMessage::~LogMessage()+748) (BuildId: "
+      "ed43842c239cac1a618e600ea91c4cbd)\n"
+      "  #03 pc 00015147  /does/not/exist/libhidlbase.so\n",
+      frame_info);
 }
 
 TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
@@ -290,7 +307,9 @@
   }
   process_memory_.reset(memory);
 
+  JitDebug jit_debug(process_memory_);
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.SetJitDebug(&jit_debug, regs_->Arch());
   unwinder.Unwind();
 
   std::string frame_info(DumpFrames(unwinder));
@@ -590,7 +609,9 @@
   }
   process_memory_.reset(memory);
 
+  JitDebug jit_debug(process_memory_);
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.SetJitDebug(&jit_debug, regs_->Arch());
   unwinder.Unwind();
 
   std::string frame_info(DumpFrames(unwinder));
@@ -911,7 +932,9 @@
   LeakType* leak_data = reinterpret_cast<LeakType*>(data);
 
   std::unique_ptr<Regs> regs_copy(leak_data->regs->Clone());
+  JitDebug jit_debug(leak_data->process_memory);
   Unwinder unwinder(128, leak_data->maps, regs_copy.get(), leak_data->process_memory);
+  unwinder.SetJitDebug(&jit_debug, regs_copy->Arch());
   unwinder.Unwind();
   ASSERT_EQ(76U, unwinder.NumFrames());
 }
@@ -1032,7 +1055,9 @@
   }
   process_memory_.reset(memory);
 
+  JitDebug jit_debug(process_memory_);
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.SetJitDebug(&jit_debug, regs_->Arch());
   unwinder.Unwind();
 
   std::string frame_info(DumpFrames(unwinder));
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 5e7e6bf..4e38015 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -170,7 +170,7 @@
     unwinder.reset(new Unwinder(512, maps.get(), regs.get(), process_memory));
   } else {
     UnwinderFromPid* unwinder_from_pid = new UnwinderFromPid(512, getpid());
-    ASSERT_TRUE(unwinder_from_pid->Init());
+    ASSERT_TRUE(unwinder_from_pid->Init(regs->Arch()));
     unwinder_from_pid->SetRegs(regs.get());
     unwinder.reset(unwinder_from_pid);
   }
@@ -283,7 +283,7 @@
   ASSERT_TRUE(regs.get() != nullptr);
 
   UnwinderFromPid unwinder(512, pid);
-  ASSERT_TRUE(unwinder.Init());
+  ASSERT_TRUE(unwinder.Init(regs->Arch()));
   unwinder.SetRegs(regs.get());
 
   VerifyUnwind(&unwinder, kFunctionOrder);
@@ -335,7 +335,7 @@
   ASSERT_TRUE(regs.get() != nullptr);
 
   UnwinderFromPid unwinder(512, *pid);
-  ASSERT_TRUE(unwinder.Init());
+  ASSERT_TRUE(unwinder.Init(regs->Arch()));
   unwinder.SetRegs(regs.get());
 
   VerifyUnwind(&unwinder, kFunctionOrder);
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index cad95f8..1812e50 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>
@@ -89,7 +90,7 @@
   printf("\n");
 
   unwindstack::UnwinderFromPid unwinder(1024, pid);
-  if (!unwinder.Init()) {
+  if (!unwinder.Init(regs->Arch())) {
     printf("Failed to init unwinder object.\n");
     return;
   }
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 86f3163..4f67d67 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -248,7 +248,7 @@
   // Do an unwind so we know how much of the stack to save, and what
   // elf files are involved.
   unwindstack::UnwinderFromPid unwinder(1024, pid);
-  if (!unwinder.Init()) {
+  if (!unwinder.Init(regs->Arch())) {
     printf("Unable to init unwinder object.\n");
     return 1;
   }
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index bfa2508..546c15c 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -9,7 +9,10 @@
     ],
     local_include_dirs: ["include/vndksupport"],
     export_include_dirs: ["include"],
-    shared_libs: ["liblog"],
+    shared_libs: [
+        "libdl_android",
+        "liblog",
+    ],
     version_script: "libvndksupport.map.txt",
     stubs: {
         symbol_file: "libvndksupport.map.txt",
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 0710d0a..96dbba1 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -102,21 +102,9 @@
 }
 
 static uint32_t ComputeHash(const ZipString& name) {
-#if !defined(_WIN32)
   return std::hash<std::string_view>{}(
-      std::string_view(reinterpret_cast<const char*>(name.name), name.name_length));
-#else
-  // Remove this code path once the windows compiler knows how to compile the above statement.
-  uint32_t hash = 0;
-  uint16_t len = name.name_length;
-  const uint8_t* str = name.name;
-
-  while (len--) {
-    hash = hash * 31 + *str++;
-  }
-
-  return hash;
-#endif
+             std::string_view(reinterpret_cast<const char*>(name.name), name.name_length)) &
+         UINT32_MAX;
 }
 
 static bool isZipStringEqual(const uint8_t* start, const ZipString& zip_string,
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 5d307b8..462ae8b 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -89,7 +89,7 @@
 
 EXPORT_GLOBAL_GCOV_OPTIONS :=
 ifeq ($(NATIVE_COVERAGE),true)
-  EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
+  EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
 endif
 
 # Put it here instead of in init.rc module definition,
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index 0fccd31..7324ba9 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -100,6 +100,7 @@
 namespace.media.link.default.shared_libs  = libandroid.so
 namespace.media.link.default.shared_libs += libbinder_ndk.so
 namespace.media.link.default.shared_libs += libc.so
+namespace.media.link.default.shared_libs += libcgrouprc.so
 namespace.media.link.default.shared_libs += libdl.so
 namespace.media.link.default.shared_libs += liblog.so
 namespace.media.link.default.shared_libs += libmediametrics.so
@@ -143,6 +144,7 @@
 namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
 namespace.resolv.links = default
 namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 84651fb..45e80e1 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -212,6 +212,7 @@
 namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
 namespace.resolv.links = default
 namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 272485f..a762ba8 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -154,6 +154,7 @@
 namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
 namespace.resolv.links = default
 namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
@@ -476,6 +477,7 @@
 namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
 namespace.resolv.links = default
 namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
diff --git a/rootdir/fsverity_init.sh b/rootdir/fsverity_init.sh
index 29e4519..4fee15f 100644
--- a/rootdir/fsverity_init.sh
+++ b/rootdir/fsverity_init.sh
@@ -24,6 +24,9 @@
     log -p e -t fsverity_init "Failed to load $cert"
 done
 
-# Prevent future key links to .fs-verity keyring
-/system/bin/mini-keyctl restrict_keyring .fs-verity ||
-  log -p e -t fsverity_init "Failed to restrict .fs-verity keyring"
+DEBUGGABLE=$(getprop ro.debuggable)
+if [ $DEBUGGABLE != "1" ]; then
+  # Prevent future key links to .fs-verity keyring
+  /system/bin/mini-keyctl restrict_keyring .fs-verity ||
+    log -p e -t fsverity_init "Failed to restrict .fs-verity keyring"
+fi
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 1f852ff..5289976 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -56,14 +56,6 @@
     defaults: ["toolbox_binary_defaults"],
 }
 
-// We only want 'r' on userdebug and eng builds.
-cc_binary {
-    name: "r",
-    defaults: ["toolbox_defaults"],
-    srcs: ["r.c"],
-    vendor_available: true,
-}
-
 // We build BSD grep separately (but see http://b/111849261).
 cc_defaults {
     name: "grep_common",
diff --git a/toolbox/r.c b/toolbox/r.c
deleted file mode 100644
index b96cdb2..0000000
--- a/toolbox/r.c
+++ /dev/null
@@ -1,102 +0,0 @@
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#if __LP64__
-#define strtoptr strtoull
-#else
-#define strtoptr strtoul
-#endif
-
-static int usage()
-{
-    fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
-    return -1;
-}
-
-int main(int argc, char *argv[])
-{
-    if(argc < 2) return usage();
-
-    int width = 4;
-    if(!strcmp(argv[1], "-b")) {
-        width = 1;
-        argc--;
-        argv++;
-    } else if(!strcmp(argv[1], "-s")) {
-        width = 2;
-        argc--;
-        argv++;
-    }
-
-    if(argc < 2) return usage();
-    uintptr_t addr = strtoptr(argv[1], 0, 16);
-
-    uintptr_t endaddr = 0;
-    char* end = strchr(argv[1], '-');
-    if (end)
-        endaddr = strtoptr(end + 1, 0, 16);
-
-    if (!endaddr)
-        endaddr = addr + width - 1;
-
-    if (endaddr <= addr) {
-        fprintf(stderr, "end address <= start address\n");
-        return -1;
-    }
-
-    bool set = false;
-    uint32_t value = 0;
-    if(argc > 2) {
-        set = true;
-        value = strtoul(argv[2], 0, 16);
-    }
-
-    int fd = open("/dev/mem", O_RDWR | O_SYNC);
-    if(fd < 0) {
-        fprintf(stderr,"cannot open /dev/mem\n");
-        return -1;
-    }
-
-    off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
-    size_t mmap_size = endaddr - mmap_start + 1;
-    mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
-
-    void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE,
-                        MAP_SHARED, fd, mmap_start);
-
-    if(page == MAP_FAILED){
-        fprintf(stderr,"cannot mmap region\n");
-        return -1;
-    }
-
-    while (addr <= endaddr) {
-        switch(width){
-        case 4: {
-            uint32_t* x = (uint32_t*) (((uintptr_t) page) + (addr & 4095));
-            if(set) *x = value;
-            fprintf(stderr,"%08"PRIxPTR": %08x\n", addr, *x);
-            break;
-        }
-        case 2: {
-            uint16_t* x = (uint16_t*) (((uintptr_t) page) + (addr & 4095));
-            if(set) *x = value;
-            fprintf(stderr,"%08"PRIxPTR": %04x\n", addr, *x);
-            break;
-        }
-        case 1: {
-            uint8_t* x = (uint8_t*) (((uintptr_t) page) + (addr & 4095));
-            if(set) *x = value;
-            fprintf(stderr,"%08"PRIxPTR": %02x\n", addr, *x);
-            break;
-        }
-        }
-        addr += width;
-    }
-    return 0;
-}
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 0a0ecec..00e3dbc 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -19,8 +19,13 @@
 # to pull in the baseline set of Trusty specific modules.
 #
 
+# For gatekeeper, we include the generic -service and -impl to use legacy
+# HAL loading of gatekeeper.trusty.
+
 PRODUCT_PACKAGES += \
 	android.hardware.keymaster@3.0-service.trusty \
+	android.hardware.gatekeeper@1.0-service \
+	android.hardware.gatekeeper@1.0-impl \
 	gatekeeper.trusty
 
 PRODUCT_PROPERTY_OVERRIDES += \