Merge changes Ic9957cb3,If7ae257c

* changes:
  fs_mgr: adb-remount-test in non overlayfs world
  fs_mgr: errno handling in fs_mgr_overlayfs_setup()
diff --git a/adb/Android.bp b/adb/Android.bp
index ae8e386..6234af1 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -24,7 +24,7 @@
         "-Wno-missing-field-initializers",
         "-Wvla",
     ],
-    rtti: true,
+    cpp_std: "experimental",
 
     use_version_lib: true,
 
diff --git a/adb/adb.h b/adb/adb.h
index e2911e8..cdd6346 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -149,6 +149,10 @@
 #endif
 
 #if !ADB_HOST
+asocket* daemon_service_to_socket(std::string_view name);
+#endif
+
+#if !ADB_HOST
 int init_jdwp(void);
 asocket* create_jdwp_service_socket();
 asocket* create_jdwp_tracker_service_socket();
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 95e66ae..c8e834e 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1275,6 +1275,42 @@
     return 0;
 }
 
+static int adb_connect_command_bidirectional(const std::string& command) {
+    std::string error;
+    int fd = adb_connect(command, &error);
+    if (fd < 0) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return 1;
+    }
+
+    static constexpr auto forward = [](int src, int sink, bool exit_on_end) {
+        char buf[4096];
+        while (true) {
+            int rc = adb_read(src, buf, sizeof(buf));
+            if (rc == 0) {
+                if (exit_on_end) {
+                    exit(0);
+                } else {
+                    adb_shutdown(sink, SHUT_WR);
+                }
+                return;
+            } else if (rc < 0) {
+                perror_exit("read failed");
+            }
+            if (!WriteFdExactly(sink, buf, rc)) {
+                perror_exit("write failed");
+            }
+        }
+    };
+
+    std::thread read(forward, fd, STDOUT_FILENO, true);
+    std::thread write(forward, STDIN_FILENO, fd, false);
+    read.join();
+    write.join();
+    adb_close(fd);
+    return 0;
+}
+
 static int adb_query_command(const std::string& command) {
     std::string result;
     std::string error;
@@ -1766,7 +1802,7 @@
         if (argc != 2) {
             error_exit("usage: adb raw SERVICE");
         }
-        return adb_connect_command(argv[1]);
+        return adb_connect_command_bidirectional(argv[1]);
     }
 
     /* "adb /?" is a common idiom under Windows */
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index b300fac..3182ddd 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -33,6 +33,7 @@
 #include <thread>
 
 #include <android-base/file.h>
+#include <android-base/parseint.h>
 #include <android-base/parsenetaddress.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -223,6 +224,105 @@
     WriteFdExactly(fd.get(), "spinning\n");
 }
 
+struct ServiceSocket : public asocket {
+    ServiceSocket() {
+        install_local_socket(this);
+        this->enqueue = [](asocket* self, apacket::payload_type data) {
+            return static_cast<ServiceSocket*>(self)->Enqueue(std::move(data));
+        };
+        this->ready = [](asocket* self) { return static_cast<ServiceSocket*>(self)->Ready(); };
+        this->close = [](asocket* self) { return static_cast<ServiceSocket*>(self)->Close(); };
+    }
+    virtual ~ServiceSocket() = default;
+
+    virtual int Enqueue(apacket::payload_type data) { return -1; }
+    virtual void Ready() {}
+    virtual void Close() {
+        if (peer) {
+            peer->peer = nullptr;
+            if (peer->shutdown) {
+                peer->shutdown(peer);
+            }
+            peer->close(peer);
+        }
+
+        remove_socket(this);
+        delete this;
+    }
+};
+
+struct SinkSocket : public ServiceSocket {
+    explicit SinkSocket(size_t byte_count) {
+        LOG(INFO) << "Creating new SinkSocket with capacity " << byte_count;
+        bytes_left_ = byte_count;
+    }
+
+    virtual ~SinkSocket() { LOG(INFO) << "SinkSocket destroyed"; }
+
+    virtual int Enqueue(apacket::payload_type data) override final {
+        if (bytes_left_ <= data.size()) {
+            // Done reading.
+            Close();
+            return -1;
+        }
+
+        bytes_left_ -= data.size();
+        return 0;
+    }
+
+    size_t bytes_left_;
+};
+
+struct SourceSocket : public ServiceSocket {
+    explicit SourceSocket(size_t byte_count) {
+        LOG(INFO) << "Creating new SourceSocket with capacity " << byte_count;
+        bytes_left_ = byte_count;
+    }
+
+    virtual ~SourceSocket() { LOG(INFO) << "SourceSocket destroyed"; }
+
+    void Ready() {
+        size_t len = std::min(bytes_left_, get_max_payload());
+        if (len == 0) {
+            Close();
+            return;
+        }
+
+        Block block(len);
+        memset(block.data(), 0, block.size());
+        peer->enqueue(peer, std::move(block));
+        bytes_left_ -= len;
+    }
+
+    int Enqueue(apacket::payload_type data) { return -1; }
+
+    size_t bytes_left_;
+};
+
+asocket* daemon_service_to_socket(std::string_view name) {
+    if (name == "jdwp") {
+        return create_jdwp_service_socket();
+    } else if (name == "track-jdwp") {
+        return create_jdwp_tracker_service_socket();
+    } else if (name.starts_with("sink:")) {
+        name.remove_prefix(strlen("sink:"));
+        uint64_t byte_count = 0;
+        if (!android::base::ParseUint(name.data(), &byte_count)) {
+            return nullptr;
+        }
+        return new SinkSocket(byte_count);
+    } else if (name.starts_with("source:")) {
+        name.remove_prefix(strlen("source:"));
+        uint64_t byte_count = 0;
+        if (!android::base::ParseUint(name.data(), &byte_count)) {
+            return nullptr;
+        }
+        return new SourceSocket(byte_count);
+    }
+
+    return nullptr;
+}
+
 unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
     if (!strncmp("dev:", name, 4)) {
         return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 8b07f74..1bd57c1 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -348,11 +348,8 @@
 
 asocket* create_local_service_socket(const char* name, atransport* transport) {
 #if !ADB_HOST
-    if (!strcmp(name, "jdwp")) {
-        return create_jdwp_service_socket();
-    }
-    if (!strcmp(name, "track-jdwp")) {
-        return create_jdwp_tracker_service_socket();
+    if (asocket* s = daemon_service_to_socket(name); s) {
+        return s;
     }
 #endif
     int fd = service_to_fd(name, transport);
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 8741654..f59a135 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -1305,11 +1305,7 @@
 void unregister_usb_transport(usb_handle* usb) {
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     transport_list.remove_if([usb](atransport* t) {
-        auto connection = t->connection();
-        if (auto usb_connection = dynamic_cast<UsbConnection*>(connection.get())) {
-            return usb_connection->handle_ == usb && t->GetConnectionState() == kCsNoPerm;
-        }
-        return false;
+        return t->GetUsbHandle() == usb && t->GetConnectionState() == kCsNoPerm;
     });
 }
 #endif
diff --git a/adb/transport.h b/adb/transport.h
index d593700..790004f 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -37,6 +37,7 @@
 
 #include "adb.h"
 #include "adb_unique_fd.h"
+#include "usb.h"
 
 typedef std::unordered_set<std::string> FeatureSet;
 
@@ -242,6 +243,9 @@
         return connection_;
     }
 
+    void SetUsbHandle(usb_handle* h) { usb_handle_ = h; }
+    usb_handle* GetUsbHandle() { return usb_handle_; }
+
     const TransportId id;
     size_t ref_count = 0;
     bool online = false;
@@ -333,6 +337,9 @@
     // The underlying connection object.
     std::shared_ptr<Connection> connection_ GUARDED_BY(mutex_);
 
+    // USB handle for the connection, if available.
+    usb_handle* usb_handle_ = nullptr;
+
     // A callback that will be invoked when the atransport needs to reconnect.
     ReconnectCallback reconnect_;
 
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index c471bf9..2e5918a 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -180,6 +180,7 @@
     auto connection = std::make_unique<UsbConnection>(h);
     t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(connection)));
     t->type = kTransportUsb;
+    t->SetUsbHandle(h);
 }
 
 int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol) {
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 99231ac..ac138a3 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -23,7 +23,6 @@
     cflags: [
         "-Wall",
         "-Werror",
-        "-Wno-unused-variable",
     ],
 }
 
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 20d60ae..efc29bd 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -32,7 +32,6 @@
 
 // Updates |fstab| for slot_suffix. Returns true on success, false on error.
 bool fs_mgr_update_for_slotselect(Fstab* fstab) {
-    int n;
     std::string ab_suffix;
 
     for (auto& entry : *fstab) {
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index d8195ca..454258b 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -84,7 +84,6 @@
 // with potentially invalid metadata, or random partition data with metadata.
 static bool ValidateAndSerializeMetadata(const IPartitionOpener& opener, const LpMetadata& metadata,
                                          const std::string& slot_suffix, std::string* blob) {
-    const LpMetadataHeader& header = metadata.header;
     const LpMetadataGeometry& geometry = metadata.geometry;
 
     *blob = SerializeMetadata(metadata);
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 56bd6c4..71c04a6 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -116,7 +116,7 @@
     FIELD_BATTERY_RESISTANCE_UOHMS = 1448,
     FIELD_BATTERY_CURRENT_UA = 1449,
     FIELD_HARDWARE_LOCATION = 1450,
-    ACTION_BATTERY_CAUSED_SHUTDOWN = 1441,
+    ACTION_BATTERY_CAUSED_SHUTDOWN = 1451,
 };
 
 enum {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index a5e0dc9..4e0470e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -235,6 +235,8 @@
         "tests/files/offline/jit_map_arm/*",
         "tests/files/offline/gnu_debugdata_arm/*",
         "tests/files/offline/offset_arm/*",
+        "tests/files/offline/shared_lib_in_apk_arm64/*",
+        "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index e3b48ca..44ec5c1 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -29,6 +29,38 @@
 
 namespace unwindstack {
 
+bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
+  // One last attempt, see if the previous map is read-only with the
+  // same name and stretches across this map.
+  for (auto iter = maps_->begin(); iter != maps_->end(); ++iter) {
+    if (*iter == this) {
+      if (iter == maps_->begin()) {
+        return false;
+      }
+      --iter;
+      MapInfo* prev_map = *iter;
+      // Make sure this is a read-only map.
+      if (prev_map->flags != PROT_READ) {
+        return false;
+      }
+      uint64_t map_size = end - prev_map->end;
+      if (!memory->Init(name, prev_map->offset, map_size)) {
+        return false;
+      }
+      uint64_t max_size;
+      if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
+        return false;
+      }
+      if (!memory->Init(name, prev_map->offset, max_size)) {
+        return false;
+      }
+      elf_offset = offset - prev_map->offset;
+      return true;
+    }
+  }
+  return false;
+}
+
 Memory* MapInfo::GetFileMemory() {
   std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
   if (offset == 0) {
@@ -38,8 +70,12 @@
     return nullptr;
   }
 
-  // There are two possibilities when the offset is non-zero.
-  // - There is an elf file embedded in a file.
+  // These are the possibilities when the offset is non-zero.
+  // - There is an elf file embedded in a file, and the offset is the
+  //   the start of the elf in the file.
+  // - There is an elf file embedded in a file, and the offset is the
+  //   the start of the executable part of the file. The actual start
+  //   of the elf is in the read-only segment preceeding this map.
   // - The whole file is an elf file, and the offset needs to be saved.
   //
   // Map in just the part of the file for the map. If this is not
@@ -53,27 +89,41 @@
     return nullptr;
   }
 
-  uint64_t max_size;
-  if (!Elf::GetInfo(memory.get(), &max_size)) {
-    // Init as if the whole file is an elf.
-    if (memory->Init(name, 0)) {
-      elf_offset = offset;
-      return memory.release();
+  // Check if the start of this map is an embedded elf.
+  uint64_t max_size = 0;
+  uint64_t file_offset = offset;
+  if (Elf::GetInfo(memory.get(), &max_size)) {
+    if (max_size > map_size) {
+      if (memory->Init(name, file_offset, max_size)) {
+        return memory.release();
+      }
+      // Try to reinit using the default map_size.
+      if (memory->Init(name, file_offset, map_size)) {
+        return memory.release();
+      }
+      return nullptr;
     }
-    return nullptr;
+    return memory.release();
   }
 
-  if (max_size > map_size) {
-    if (memory->Init(name, offset, max_size)) {
-      return memory.release();
-    }
-    // Try to reinit using the default map_size.
-    if (memory->Init(name, offset, map_size)) {
-      return memory.release();
-    }
-    return nullptr;
+  // No elf at offset, try to init as if the whole file is an elf.
+  if (memory->Init(name, 0) && Elf::IsValidElf(memory.get())) {
+    elf_offset = offset;
+    return memory.release();
   }
-  return memory.release();
+
+  // See if the map previous to this one contains a read-only map
+  // that represents the real start of the elf data.
+  if (InitFileMemoryFromPreviousReadOnlyMap(memory.get())) {
+    return memory.release();
+  }
+
+  // Failed to find elf at start of file or at read-only map, return
+  // file object from the current map.
+  if (memory->Init(name, offset, map_size)) {
+    return memory.release();
+  }
+  return nullptr;
 }
 
 Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
@@ -110,29 +160,27 @@
     return nullptr;
   }
 
-  // Find the read-only map that has the same name and has an offset closest
-  // to the current offset but less than the offset of the current map.
-  // For shared libraries, there should be a r-x map that has a non-zero
-  // offset and then a r-- map that has a zero offset.
-  // For shared libraries loaded from an apk, there should be a r-x map that
-  // has a non-zero offset and then a r-- map that has a non-zero offset less
-  // than the offset from the r-x map.
-  uint64_t closest_offset = 0;
+  // Find the read-only map by looking at the previous map. The linker
+  // doesn't guarantee that this invariant will always be true. However,
+  // if that changes, there is likely something else that will change and
+  // break something.
   MapInfo* ro_map_info = nullptr;
-  for (auto map_info : *maps_) {
-    if (map_info->flags == PROT_READ && map_info->name == name && map_info->offset < offset &&
-        map_info->offset >= closest_offset) {
-      ro_map_info = map_info;
-      closest_offset = ro_map_info->offset;
+  for (auto iter = maps_->begin(); iter != maps_->end(); ++iter) {
+    if (*iter == this) {
+      if (iter != maps_->begin()) {
+        --iter;
+        ro_map_info = *iter;
+      }
+      break;
     }
   }
 
-  if (ro_map_info == nullptr) {
+  if (ro_map_info == nullptr || ro_map_info->name != name || ro_map_info->offset >= offset) {
     return nullptr;
   }
 
   // Make sure that relative pc values are corrected properly.
-  elf_offset = offset - closest_offset;
+  elf_offset = offset - ro_map_info->offset;
 
   MemoryRanges* ranges = new MemoryRanges;
   ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 9c6b552..cfdefd0 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -83,6 +83,7 @@
   void operator=(const MapInfo&) = delete;
 
   Memory* GetFileMemory();
+  bool InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory);
 
   // Protect the creation of the elf object.
   std::mutex mutex_;
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 0987bc1..943b3c9 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -59,16 +59,16 @@
   }
 
   static void SetUpTestCase() {
-    std::vector<uint8_t> buffer(1024);
-    memset(buffer.data(), 0, buffer.size());
+    std::vector<uint8_t> buffer(12288, 0);
     memcpy(buffer.data(), ELFMAG, SELFMAG);
     buffer[EI_CLASS] = ELFCLASS32;
-    ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+    ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), 1024));
 
     memset(buffer.data(), 0, buffer.size());
-    memcpy(&buffer[0x100], ELFMAG, SELFMAG);
-    buffer[0x100 + EI_CLASS] = ELFCLASS64;
-    ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+    memcpy(&buffer[0x1000], ELFMAG, SELFMAG);
+    buffer[0x1000 + EI_CLASS] = ELFCLASS64;
+    buffer[0x2000] = 0xff;
+    ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, buffer.data(), buffer.size()));
 
     InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32);
     InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
@@ -84,13 +84,13 @@
 
   static TemporaryFile elf_;
 
-  static TemporaryFile elf_at_100_;
+  static TemporaryFile elf_at_1000_;
 
   static TemporaryFile elf32_at_map_;
   static TemporaryFile elf64_at_map_;
 };
 TemporaryFile MapInfoCreateMemoryTest::elf_;
-TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
+TemporaryFile MapInfoCreateMemoryTest::elf_at_1000_;
 TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
 TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
 
@@ -134,7 +134,7 @@
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_at_100_.path);
+  MapInfo info(nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -312,4 +312,43 @@
   }
 }
 
+TEST_F(MapInfoCreateMemoryTest, rosegment_from_file) {
+  Maps maps;
+  maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+  maps.Add(0x1000, 0x2000, 0x1000, PROT_READ, elf_at_1000_.path, 0);
+  maps.Add(0x2000, 0x3000, 0x2000, PROT_READ | PROT_EXEC, elf_at_1000_.path, 0);
+
+  MapInfo* map_info = maps.Find(0x2000);
+  ASSERT_TRUE(map_info != nullptr);
+
+  // Set up the size
+  Elf64_Ehdr ehdr;
+  ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr)));
+
+  // Will not give the elf memory, because the read-only entry does not
+  // extend over the executable segment.
+  std::unique_ptr<Memory> memory(map_info->CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  std::vector<uint8_t> buffer(0x100);
+  EXPECT_EQ(0x2000U, map_info->offset);
+  EXPECT_EQ(0U, map_info->elf_offset);
+  ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100));
+  EXPECT_EQ(0xffU, buffer[0]);
+
+  // Now init the elf data enough so that the file memory object will be used.
+  ehdr.e_shoff = 0x4000;
+  ehdr.e_shnum = 1;
+  ehdr.e_shentsize = 0x100;
+  ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET));
+  ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr)));
+
+  memory.reset(map_info->CreateMemory(process_memory_));
+  EXPECT_EQ(0x2000U, map_info->offset);
+  EXPECT_EQ(0x1000U, map_info->elf_offset);
+  Elf64_Ehdr ehdr_mem;
+  ASSERT_TRUE(memory->ReadFully(0, &ehdr_mem, sizeof(ehdr_mem)));
+  EXPECT_TRUE(memcmp(&ehdr, &ehdr_mem, sizeof(ehdr)) == 0);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index a65c077..dc015b4 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1239,4 +1239,79 @@
   EXPECT_EQ(0xffd4a718U, unwinder.frames()[7].sp);
 }
 
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_arm64) {
+  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_arm64/", ARCH_ARM64));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 000000000014ccbc (offset 0x39000)  linker64 (__dl_syscall+28)\n"
+      "  #01 pc 000000000005426c (offset 0x39000)  linker64 "
+      "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
+      "  #02 pc 00000000000008bc  vdso.so\n"
+      "  #03 pc 00000000000846f4 (offset 0x40000)  libc.so (abort+172)\n"
+      "  #04 pc 0000000000084ad4 (offset 0x40000)  libc.so (__assert2+36)\n"
+      "  #05 pc 000000000003d5b4 (offset 0x40000)  ANGLEPrebuilt.apk (ANGLEGetUtilityAPI+56)\n"
+      "  #06 pc 000000000007fe68 (offset 0x40000)  libc.so (__libc_init)\n",
+      frame_info);
+
+  EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x7e82b018bcULL, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7e7eeccad4ULL, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7dabf3dc40ULL, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x7dabc405b4ULL, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7dabf3dc50ULL, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x7e7eec7e68ULL, unwinder.frames()[6].pc);
+  EXPECT_EQ(0x7dabf3dc70ULL, unwinder.frames()[6].sp);
+  // Ignore top frame since the test code was modified to end in __libc_init.
+}
+
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_memory_only_arm64) {
+  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_memory_only_arm64/", ARCH_ARM64));
+  // Add the memory that represents the shared library.
+  MemoryOfflineParts* memory = reinterpret_cast<MemoryOfflineParts*>(process_memory_.get());
+  AddMemory(dir_ + "lib_mem.data", memory);
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 000000000014ccbc (offset 0x39000)  linker64 (__dl_syscall+28)\n"
+      "  #01 pc 000000000005426c (offset 0x39000)  linker64 "
+      "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
+      "  #02 pc 00000000000008bc  vdso.so\n"
+      "  #03 pc 00000000000846f4 (offset 0x40000)  libc.so (abort+172)\n"
+      "  #04 pc 0000000000084ad4 (offset 0x40000)  libc.so (__assert2+36)\n"
+      "  #05 pc 000000000003d5b4 (offset 0x2211000)  ANGLEPrebuilt.apk\n"
+      "  #06 pc 000000000007fe68 (offset 0x40000)  libc.so (__libc_init)\n",
+      frame_info);
+
+  EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x7e82b018bcULL, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7e7eeccad4ULL, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7dabf3dc40ULL, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x7dabc405b4ULL, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7dabf3dc50ULL, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x7e7eec7e68ULL, unwinder.frames()[6].pc);
+  EXPECT_EQ(0x7dabf3dc70ULL, unwinder.frames()[6].sp);
+  // Ignore top frame since the test code was modified to end in __libc_init.
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
new file mode 100644
index 0000000..0277359
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
new file mode 100644
index 0000000..20008fd
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
new file mode 100644
index 0000000..b90933b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt
new file mode 100644
index 0000000..c4fc067
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt
@@ -0,0 +1,7 @@
+7dabc03000-7dabc3f000 r--p 4000 00:00 0   ANGLEPrebuilt.apk
+7dabc3f000-7dabcf0000 r-xp 40000 00:00 0   ANGLEPrebuilt.apk
+7e7ee48000-7e7ee88000 r--p 0 00:00 0   libc.so
+7e7ee88000-7e7ef32000 r-xp 40000 00:00 0   libc.so
+7e82b01000-7e82b03000 r-xp 0 00:00 0   vdso.so
+7e82b03000-7e82b3c000 r--p 0 00:00 0   linker64
+7e82b3c000-7e82c77000 r-xp 39000 00:00 0   linker64
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt
new file mode 100644
index 0000000..1e2ea32
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7df8ca3c24
+x1: 0
+x2: ffffffff
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 20dd5829922a93ac
+x10: 7e82b57420
+x11: 4100
+x12: 7df8ca3b70
+x13: 7df8ca3b98
+x14: 73d015e5
+x15: 39a36122467299
+x16: 76ac
+x17: 0
+x18: 7df8cfc000
+x19: 7dabf3e7a0
+x20: 7df8ca3da0
+x21: 59616d61
+x22: 1
+x23: 7df8ca3c24
+x24: 1894
+x25: 62
+x26: 2
+x27: 0
+x28: 7dabf3e790
+x29: 7df8ca3d90
+sp: 7df8ca3bf0
+lr: 7e82b57270
+pc: 7e82c4fcbc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data
new file mode 100644
index 0000000..ec07e15
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data
new file mode 100644
index 0000000..825bb1a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
new file mode 100644
index 0000000..205ebd4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data
new file mode 100644
index 0000000..f39d127
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
new file mode 100644
index 0000000..20008fd
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
new file mode 100644
index 0000000..b90933b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt
new file mode 100644
index 0000000..386d57a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt
@@ -0,0 +1,7 @@
+7dabc03000-7dabc3f000 r--p 21d5000 00:00 0   ANGLEPrebuilt.apk
+7dabc3f000-7dabcf0000 r-xp 2211000 00:00 0   ANGLEPrebuilt.apk
+7e7ee48000-7e7ee88000 r--p 0 00:00 0   libc.so
+7e7ee88000-7e7ef32000 r-xp 40000 00:00 0   libc.so
+7e82b01000-7e82b03000 r-xp 0 00:00 0   vdso.so
+7e82b03000-7e82b3c000 r--p 0 00:00 0   linker64
+7e82b3c000-7e82c77000 r-xp 39000 00:00 0   linker64
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt
new file mode 100644
index 0000000..1e2ea32
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7df8ca3c24
+x1: 0
+x2: ffffffff
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 20dd5829922a93ac
+x10: 7e82b57420
+x11: 4100
+x12: 7df8ca3b70
+x13: 7df8ca3b98
+x14: 73d015e5
+x15: 39a36122467299
+x16: 76ac
+x17: 0
+x18: 7df8cfc000
+x19: 7dabf3e7a0
+x20: 7df8ca3da0
+x21: 59616d61
+x22: 1
+x23: 7df8ca3c24
+x24: 1894
+x25: 62
+x26: 2
+x27: 0
+x28: 7dabf3e790
+x29: 7df8ca3d90
+sp: 7df8ca3bf0
+lr: 7e82b57270
+pc: 7e82c4fcbc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data
new file mode 100644
index 0000000..ec07e15
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data
new file mode 100644
index 0000000..825bb1a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
new file mode 100644
index 0000000..205ebd4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 22ca7bf..e729453 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -35,7 +35,12 @@
 #include <unwindstack/Unwinder.h>
 
 static bool Attach(pid_t pid) {
-  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+  if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) {
+    ptrace(PTRACE_DETACH, pid, 0, 0);
     return false;
   }
 
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index c8a8ab5..652dbd1 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -50,7 +50,12 @@
 };
 
 static bool Attach(pid_t pid) {
-  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+  if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) {
+    ptrace(PTRACE_DETACH, pid, 0, 0);
     return false;
   }