Fix handling of ro segments for embedded libs.

When a shared library is loaded directly from an apk, the new way the
linker splits a shared library into a read-only and execute segment
broke unwinding. Modify the code to handle this case.

Other changes:
- Modify the algorithm for finding read-only map entries. Before, the code
  would search the entire map for the closest offset. Now it simply looks
  at the previous map. I did this because the old code was too lenient and
  might still work even if the linker changes. I want this to break if the
  linker behavior changes so that I can analyze the change.
- Update the tools to use PTRACE_SEIZE instead of PTRACE_ATTACH since
  PTRACE_ATTACH doesn't work in all cases.
- Small refactor of the GetFileMemory function.
- Add new unit test cases and new offline unwind test cases.

Bug: 120618231

Test: Ran new unit tests, ran original failing test.
Change-Id: I4bade55cf33220d52f1d5e9b0cbbbcc8419669d4
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,