Implement GetSharedLibraryInfoAddress

Summary:
This is the third patch to improve module loading in a series that started here (where I explain the motivation and solution): D62499

Add functions to read the r_debug location to know where the linked list of loaded libraries are so I can generate the `xfer:libraries-svr4` packet.
I'm also using this function to implement `GetSharedLibraryInfoAddress` that was "not implemented" for linux.
Most of this code was inspired by the current ds2 implementation here: https://github.com/facebook/ds2/blob/master/Sources/Target/POSIX/ELFProcess.cpp.

Reviewers: clayborg, xiaobai, labath

Reviewed By: clayborg, labath

Subscribers: emaste, krytarowski, mgorny, lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D62501

llvm-svn: 363458
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index 81d257e..7637237 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -288,7 +288,7 @@
                                        NativeDelegate &delegate,
                                        const ArchSpec &arch, MainLoop &mainloop,
                                        llvm::ArrayRef<::pid_t> tids)
-    : NativeProcessProtocol(pid, terminal_fd, delegate), m_arch(arch) {
+    : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch) {
   if (m_terminal_fd != -1) {
     Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
     assert(status.Success());
@@ -1389,11 +1389,6 @@
   return Status("not implemented");
 }
 
-lldb::addr_t NativeProcessLinux::GetSharedLibraryInfoAddress() {
-  // punt on this for now
-  return LLDB_INVALID_ADDRESS;
-}
-
 size_t NativeProcessLinux::UpdateThreads() {
   // The NativeProcessLinux monitoring threads are always up to date with
   // respect to thread state and they keep the thread list populated properly.
@@ -2082,18 +2077,3 @@
 
   return error;
 }
-
-llvm::Optional<uint64_t>
-NativeProcessLinux::GetAuxValue(enum AuxVector::EntryType type) {
-  if (m_aux_vector == nullptr) {
-    auto buffer_or_error = GetAuxvData();
-    if (!buffer_or_error)
-      return llvm::None;
-    DataExtractor auxv_data(buffer_or_error.get()->getBufferStart(),
-                            buffer_or_error.get()->getBufferSize(),
-                            GetByteOrder(), GetAddressByteSize());
-    m_aux_vector = llvm::make_unique<AuxVector>(auxv_data);
-  }
-
-  return m_aux_vector->GetAuxValue(type);
-}
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
index 0a67af0..1366f0b 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -12,7 +12,6 @@
 #include <csignal>
 #include <unordered_set>
 
-#include "Plugins/Process/Utility/AuxVector.h"
 #include "lldb/Host/Debug.h"
 #include "lldb/Host/HostThread.h"
 #include "lldb/Host/linux/Support.h"
@@ -22,8 +21,8 @@
 #include "lldb/lldb-types.h"
 
 #include "NativeThreadLinux.h"
+#include "Plugins/Process/POSIX/NativeProcessELF.h"
 #include "ProcessorTrace.h"
-#include "lldb/Host/common/NativeProcessProtocol.h"
 
 namespace lldb_private {
 class Status;
@@ -37,7 +36,7 @@
 /// for debugging.
 ///
 /// Changes in the inferior process state are broadcasted.
-class NativeProcessLinux : public NativeProcessProtocol {
+class NativeProcessLinux : public NativeProcessELF {
 public:
   class Factory : public NativeProcessProtocol::Factory {
   public:
@@ -77,8 +76,6 @@
 
   Status DeallocateMemory(lldb::addr_t addr) override;
 
-  lldb::addr_t GetSharedLibraryInfoAddress() override;
-
   size_t UpdateThreads() override;
 
   const ArchSpec &GetArchitecture() const override { return m_arch; }
@@ -103,8 +100,6 @@
     return getProcFile(GetID(), "auxv");
   }
 
-  llvm::Optional<uint64_t> GetAuxValue(enum AuxVector::EntryType type);
-
   lldb::user_id_t StartTrace(const TraceOptions &config,
                              Status &error) override;
 
@@ -135,7 +130,6 @@
 private:
   MainLoop::SignalHandleUP m_sigchld_handle;
   ArchSpec m_arch;
-  std::unique_ptr<AuxVector> m_aux_vector;
 
   LazyBool m_supports_mem_region = eLazyBoolCalculate;
   std::vector<std::pair<MemoryRegionInfo, FileSpec>> m_mem_region_cache;
diff --git a/lldb/source/Plugins/Process/POSIX/CMakeLists.txt b/lldb/source/Plugins/Process/POSIX/CMakeLists.txt
index c0f6d5a..fd74f81 100644
--- a/lldb/source/Plugins/Process/POSIX/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/POSIX/CMakeLists.txt
@@ -1,9 +1,11 @@
 add_lldb_library(lldbPluginProcessPOSIX PLUGIN
   CrashReason.cpp
+  NativeProcessELF.cpp
   ProcessMessage.cpp
   ProcessPOSIXLog.cpp
 
   LINK_LIBS
+    lldbPluginProcessUtility
     lldbUtility
   LINK_COMPONENTS
     Support
diff --git a/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp b/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp
new file mode 100644
index 0000000..559b16c
--- /dev/null
+++ b/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp
@@ -0,0 +1,110 @@
+//===-- NativeProcessELF.cpp ---------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeProcessELF.h"
+
+#include "lldb/Utility/DataExtractor.h"
+
+namespace lldb_private {
+
+llvm::Optional<uint64_t>
+NativeProcessELF::GetAuxValue(enum AuxVector::EntryType type) {
+  if (m_aux_vector == nullptr) {
+    auto buffer_or_error = GetAuxvData();
+    if (!buffer_or_error)
+      return llvm::None;
+    DataExtractor auxv_data(buffer_or_error.get()->getBufferStart(),
+                            buffer_or_error.get()->getBufferSize(),
+                            GetByteOrder(), GetAddressByteSize());
+    m_aux_vector = llvm::make_unique<AuxVector>(auxv_data);
+  }
+
+  return m_aux_vector->GetAuxValue(type);
+}
+
+lldb::addr_t NativeProcessELF::GetSharedLibraryInfoAddress() {
+  if (!m_shared_library_info_addr.hasValue()) {
+    if (GetAddressByteSize() == 8)
+      m_shared_library_info_addr =
+          GetELFImageInfoAddress<llvm::ELF::Elf64_Ehdr, llvm::ELF::Elf64_Phdr,
+                                 llvm::ELF::Elf64_Dyn>();
+    else
+      m_shared_library_info_addr =
+          GetELFImageInfoAddress<llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr,
+                                 llvm::ELF::Elf32_Dyn>();
+  }
+
+  return m_shared_library_info_addr.getValue();
+}
+
+template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN>
+lldb::addr_t NativeProcessELF::GetELFImageInfoAddress() {
+  llvm::Optional<uint64_t> maybe_phdr_addr =
+      GetAuxValue(AuxVector::AUXV_AT_PHDR);
+  llvm::Optional<uint64_t> maybe_phdr_entry_size =
+      GetAuxValue(AuxVector::AUXV_AT_PHENT);
+  llvm::Optional<uint64_t> maybe_phdr_num_entries =
+      GetAuxValue(AuxVector::AUXV_AT_PHNUM);
+  if (!maybe_phdr_addr || !maybe_phdr_entry_size || !maybe_phdr_num_entries)
+    return LLDB_INVALID_ADDRESS;
+  lldb::addr_t phdr_addr = *maybe_phdr_addr;
+  size_t phdr_entry_size = *maybe_phdr_entry_size;
+  size_t phdr_num_entries = *maybe_phdr_num_entries;
+
+  // Find the PT_DYNAMIC segment (.dynamic section) in the program header and
+  // what the load bias by calculating the difference of the program header
+  // load address and its virtual address.
+  lldb::offset_t load_bias;
+  bool found_load_bias = false;
+  lldb::addr_t dynamic_section_addr = 0;
+  uint64_t dynamic_section_size = 0;
+  bool found_dynamic_section = false;
+  ELF_PHDR phdr_entry;
+  for (size_t i = 0; i < phdr_num_entries; i++) {
+    size_t bytes_read;
+    auto error = ReadMemory(phdr_addr + i * phdr_entry_size, &phdr_entry,
+                            sizeof(phdr_entry), bytes_read);
+    if (!error.Success())
+      return LLDB_INVALID_ADDRESS;
+    if (phdr_entry.p_type == llvm::ELF::PT_PHDR) {
+      load_bias = phdr_addr - phdr_entry.p_vaddr;
+      found_load_bias = true;
+    }
+
+    if (phdr_entry.p_type == llvm::ELF::PT_DYNAMIC) {
+      dynamic_section_addr = phdr_entry.p_vaddr;
+      dynamic_section_size = phdr_entry.p_memsz;
+      found_dynamic_section = true;
+    }
+  }
+
+  if (!found_load_bias || !found_dynamic_section)
+    return LLDB_INVALID_ADDRESS;
+
+  // Find the DT_DEBUG entry in the .dynamic section
+  dynamic_section_addr += load_bias;
+  ELF_DYN dynamic_entry;
+  size_t dynamic_num_entries = dynamic_section_size / sizeof(dynamic_entry);
+  for (size_t i = 0; i < dynamic_num_entries; i++) {
+    size_t bytes_read;
+    auto error = ReadMemory(dynamic_section_addr + i * sizeof(dynamic_entry),
+                            &dynamic_entry, sizeof(dynamic_entry), bytes_read);
+    if (!error.Success())
+      return LLDB_INVALID_ADDRESS;
+    // Return the &DT_DEBUG->d_ptr which points to r_debug which contains the
+    // link_map.
+    if (dynamic_entry.d_tag == llvm::ELF::DT_DEBUG) {
+      return dynamic_section_addr + i * sizeof(dynamic_entry) +
+             sizeof(dynamic_entry.d_tag);
+    }
+  }
+
+  return LLDB_INVALID_ADDRESS;
+}
+
+} // namespace lldb_private
\ No newline at end of file
diff --git a/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h b/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h
new file mode 100644
index 0000000..84dc8d0
--- /dev/null
+++ b/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h
@@ -0,0 +1,46 @@
+//===-- NativeProcessELF.h ------------------------------------ -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_NativeProcessELF_H_
+#define liblldb_NativeProcessELF_H_
+
+#include "Plugins/Process/Utility/AuxVector.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "llvm/BinaryFormat/ELF.h"
+
+namespace lldb_private {
+
+/// \class NativeProcessELF
+/// Abstract class that extends \a NativeProcessProtocol with ELF specific
+/// logic. Meant to be subclassed by ELF based NativeProcess* implementations.
+class NativeProcessELF : public NativeProcessProtocol {
+  using NativeProcessProtocol::NativeProcessProtocol;
+
+protected:
+  template <typename T> struct ELFLinkMap {
+    T l_addr;
+    T l_name;
+    T l_ld;
+    T l_next;
+    T l_prev;
+  };
+
+  llvm::Optional<uint64_t> GetAuxValue(enum AuxVector::EntryType type);
+
+  lldb::addr_t GetSharedLibraryInfoAddress() override;
+
+  template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN>
+  lldb::addr_t GetELFImageInfoAddress();
+
+  std::unique_ptr<AuxVector> m_aux_vector;
+  llvm::Optional<lldb::addr_t> m_shared_library_info_addr;
+};
+
+} // namespace lldb_private
+
+#endif
\ No newline at end of file