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
diff --git a/lldb/unittests/Host/NativeProcessProtocolTest.cpp b/lldb/unittests/Host/NativeProcessProtocolTest.cpp
index 214d505..bf61951 100644
--- a/lldb/unittests/Host/NativeProcessProtocolTest.cpp
+++ b/lldb/unittests/Host/NativeProcessProtocolTest.cpp
@@ -6,6 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "TestingSupport/Host/NativeProcessTestUtils.h"
+
 #include "lldb/Host/common/NativeProcessProtocol.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gmock/gmock.h"
@@ -14,142 +16,10 @@
 using namespace lldb;
 using namespace testing;
 
-namespace {
-class MockDelegate : public NativeProcessProtocol::NativeDelegate {
-public:
-  MOCK_METHOD1(InitializeDelegate, void(NativeProcessProtocol *Process));
-  MOCK_METHOD2(ProcessStateChanged,
-               void(NativeProcessProtocol *Process, StateType State));
-  MOCK_METHOD1(DidExec, void(NativeProcessProtocol *Process));
-};
-
-// NB: This class doesn't use the override keyword to avoid
-// -Winconsistent-missing-override warnings from the compiler. The
-// inconsistency comes from the overriding definitions in the MOCK_*** macros.
-class MockProcess : public NativeProcessProtocol {
-public:
-  MockProcess(NativeDelegate &Delegate, const ArchSpec &Arch,
-              lldb::pid_t Pid = 1)
-      : NativeProcessProtocol(Pid, -1, Delegate), Arch(Arch) {}
-
-  MOCK_METHOD1(Resume, Status(const ResumeActionList &ResumeActions));
-  MOCK_METHOD0(Halt, Status());
-  MOCK_METHOD0(Detach, Status());
-  MOCK_METHOD1(Signal, Status(int Signo));
-  MOCK_METHOD0(Kill, Status());
-  MOCK_METHOD3(AllocateMemory,
-               Status(size_t Size, uint32_t Permissions, addr_t &Addr));
-  MOCK_METHOD1(DeallocateMemory, Status(addr_t Addr));
-  MOCK_METHOD0(GetSharedLibraryInfoAddress, addr_t());
-  MOCK_METHOD0(UpdateThreads, size_t());
-  MOCK_CONST_METHOD0(GetAuxvData,
-                     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>());
-  MOCK_METHOD2(GetLoadedModuleFileSpec,
-               Status(const char *ModulePath, FileSpec &Spec));
-  MOCK_METHOD2(GetFileLoadAddress,
-               Status(const llvm::StringRef &FileName, addr_t &Addr));
-
-  const ArchSpec &GetArchitecture() const /*override*/ { return Arch; }
-  Status SetBreakpoint(lldb::addr_t Addr, uint32_t Size,
-                       bool Hardware) /*override*/ {
-    if (Hardware)
-      return SetHardwareBreakpoint(Addr, Size);
-    else
-      return SetSoftwareBreakpoint(Addr, Size);
-  }
-
-  // Redirect base class Read/Write Memory methods to functions whose signatures
-  // are more mock-friendly.
-  Status ReadMemory(addr_t Addr, void *Buf, size_t Size,
-                    size_t &BytesRead) /*override*/;
-  Status WriteMemory(addr_t Addr, const void *Buf, size_t Size,
-                     size_t &BytesWritten) /*override*/;
-
-  MOCK_METHOD2(ReadMemory,
-               llvm::Expected<std::vector<uint8_t>>(addr_t Addr, size_t Size));
-  MOCK_METHOD2(WriteMemory,
-               llvm::Expected<size_t>(addr_t Addr,
-                                      llvm::ArrayRef<uint8_t> Data));
-
-  using NativeProcessProtocol::GetSoftwareBreakpointTrapOpcode;
-  llvm::Expected<std::vector<uint8_t>> ReadMemoryWithoutTrap(addr_t Addr,
-                                                             size_t Size);
-
-private:
-  ArchSpec Arch;
-};
-
-class FakeMemory {
-public:
-  FakeMemory(llvm::ArrayRef<uint8_t> Data) : Data(Data) {}
-  llvm::Expected<std::vector<uint8_t>> Read(addr_t Addr, size_t Size);
-  llvm::Expected<size_t> Write(addr_t Addr, llvm::ArrayRef<uint8_t> Chunk);
-
-private:
-  std::vector<uint8_t> Data;
-};
-} // namespace
-
-Status MockProcess::ReadMemory(addr_t Addr, void *Buf, size_t Size,
-                               size_t &BytesRead) {
-  auto ExpectedMemory = ReadMemory(Addr, Size);
-  if (!ExpectedMemory) {
-    BytesRead = 0;
-    return Status(ExpectedMemory.takeError());
-  }
-  BytesRead = ExpectedMemory->size();
-  assert(BytesRead <= Size);
-  std::memcpy(Buf, ExpectedMemory->data(), BytesRead);
-  return Status();
-}
-
-Status MockProcess::WriteMemory(addr_t Addr, const void *Buf, size_t Size,
-                                size_t &BytesWritten) {
-  auto ExpectedBytes = WriteMemory(
-      Addr, llvm::makeArrayRef(static_cast<const uint8_t *>(Buf), Size));
-  if (!ExpectedBytes) {
-    BytesWritten = 0;
-    return Status(ExpectedBytes.takeError());
-  }
-  BytesWritten = *ExpectedBytes;
-  return Status();
-}
-
-llvm::Expected<std::vector<uint8_t>>
-MockProcess::ReadMemoryWithoutTrap(addr_t Addr, size_t Size) {
-  std::vector<uint8_t> Data(Size, 0);
-  size_t BytesRead;
-  Status ST = NativeProcessProtocol::ReadMemoryWithoutTrap(
-      Addr, Data.data(), Data.size(), BytesRead);
-  if (ST.Fail())
-    return ST.ToError();
-  Data.resize(BytesRead);
-  return std::move(Data);
-}
-
-llvm::Expected<std::vector<uint8_t>> FakeMemory::Read(addr_t Addr,
-                                                      size_t Size) {
-  if (Addr >= Data.size())
-    return llvm::createStringError(llvm::inconvertibleErrorCode(),
-                                   "Address out of range.");
-  Size = std::min(Size, Data.size() - (size_t)Addr);
-  auto Begin = std::next(Data.begin(), Addr);
-  return std::vector<uint8_t>(Begin, std::next(Begin, Size));
-}
-
-llvm::Expected<size_t> FakeMemory::Write(addr_t Addr,
-                                         llvm::ArrayRef<uint8_t> Chunk) {
-  if (Addr >= Data.size())
-    return llvm::createStringError(llvm::inconvertibleErrorCode(),
-                                   "Address out of range.");
-  size_t Size = std::min(Chunk.size(), Data.size() - (size_t)Addr);
-  std::copy_n(Chunk.begin(), Size, &Data[Addr]);
-  return Size;
-}
-
 TEST(NativeProcessProtocolTest, SetBreakpoint) {
   NiceMock<MockDelegate> DummyDelegate;
-  MockProcess Process(DummyDelegate, ArchSpec("x86_64-pc-linux"));
+  MockProcess<NativeProcessProtocol> Process(DummyDelegate,
+                                             ArchSpec("x86_64-pc-linux"));
   auto Trap = cantFail(Process.GetSoftwareBreakpointTrapOpcode(1));
   InSequence S;
   EXPECT_CALL(Process, ReadMemory(0x47, 1))
@@ -162,7 +32,8 @@
 
 TEST(NativeProcessProtocolTest, SetBreakpointFailRead) {
   NiceMock<MockDelegate> DummyDelegate;
-  MockProcess Process(DummyDelegate, ArchSpec("x86_64-pc-linux"));
+  MockProcess<NativeProcessProtocol> Process(DummyDelegate,
+                                             ArchSpec("x86_64-pc-linux"));
   EXPECT_CALL(Process, ReadMemory(0x47, 1))
       .WillOnce(Return(ByMove(
           llvm::createStringError(llvm::inconvertibleErrorCode(), "Foo"))));
@@ -172,7 +43,8 @@
 
 TEST(NativeProcessProtocolTest, SetBreakpointFailWrite) {
   NiceMock<MockDelegate> DummyDelegate;
-  MockProcess Process(DummyDelegate, ArchSpec("x86_64-pc-linux"));
+  MockProcess<NativeProcessProtocol> Process(DummyDelegate,
+                                             ArchSpec("x86_64-pc-linux"));
   auto Trap = cantFail(Process.GetSoftwareBreakpointTrapOpcode(1));
   InSequence S;
   EXPECT_CALL(Process, ReadMemory(0x47, 1))
@@ -186,7 +58,8 @@
 
 TEST(NativeProcessProtocolTest, SetBreakpointFailVerify) {
   NiceMock<MockDelegate> DummyDelegate;
-  MockProcess Process(DummyDelegate, ArchSpec("x86_64-pc-linux"));
+  MockProcess<NativeProcessProtocol> Process(DummyDelegate,
+                                             ArchSpec("x86_64-pc-linux"));
   auto Trap = cantFail(Process.GetSoftwareBreakpointTrapOpcode(1));
   InSequence S;
   EXPECT_CALL(Process, ReadMemory(0x47, 1))
@@ -201,7 +74,8 @@
 
 TEST(NativeProcessProtocolTest, ReadMemoryWithoutTrap) {
   NiceMock<MockDelegate> DummyDelegate;
-  MockProcess Process(DummyDelegate, ArchSpec("aarch64-pc-linux"));
+  MockProcess<NativeProcessProtocol> Process(DummyDelegate,
+                                             ArchSpec("aarch64-pc-linux"));
   FakeMemory M{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}};
   EXPECT_CALL(Process, ReadMemory(_, _))
       .WillRepeatedly(Invoke(&M, &FakeMemory::Read));
diff --git a/lldb/unittests/Process/CMakeLists.txt b/lldb/unittests/Process/CMakeLists.txt
index 75db3be..a404569 100644
--- a/lldb/unittests/Process/CMakeLists.txt
+++ b/lldb/unittests/Process/CMakeLists.txt
@@ -1,5 +1,6 @@
 add_subdirectory(gdb-remote)
 if (CMAKE_SYSTEM_NAME MATCHES "Linux|Android")
   add_subdirectory(Linux)
+  add_subdirectory(POSIX)
 endif()
 add_subdirectory(minidump)
diff --git a/lldb/unittests/Process/POSIX/CMakeLists.txt b/lldb/unittests/Process/POSIX/CMakeLists.txt
new file mode 100644
index 0000000..53318d9
--- /dev/null
+++ b/lldb/unittests/Process/POSIX/CMakeLists.txt
@@ -0,0 +1,8 @@
+include_directories(${LLDB_SOURCE_DIR}/source/Plugins/Process/POSIX)
+
+add_lldb_unittest(ProcessPOSIXTest
+  NativeProcessELFTest.cpp
+
+  LINK_LIBS
+    lldbPluginProcessPOSIX
+  )
diff --git a/lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp b/lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp
new file mode 100644
index 0000000..9e91464
--- /dev/null
+++ b/lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp
@@ -0,0 +1,155 @@
+//===-- NativeProcessELFTest.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 "TestingSupport/Host/NativeProcessTestUtils.h"
+
+#include "Plugins/Process/POSIX/NativeProcessELF.h"
+#include "Plugins/Process/Utility/AuxVector.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataEncoder.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include "gmock/gmock.h"
+
+using namespace lldb_private;
+using namespace lldb;
+using namespace testing;
+
+namespace {
+class MockProcessELF : public MockProcess<NativeProcessELF> {
+public:
+  using MockProcess::MockProcess;
+  using NativeProcessELF::GetAuxValue;
+  using NativeProcessELF::GetELFImageInfoAddress;
+};
+
+std::unique_ptr<llvm::MemoryBuffer> CreateAuxvData(
+    MockProcessELF &process,
+    llvm::ArrayRef<std::pair<AuxVector::EntryType, uint32_t>> auxv_data) {
+  auto addr_size = process.GetAddressByteSize();
+  DataBufferSP buffer_sp(
+      new DataBufferHeap(auxv_data.size() * addr_size * 2, 0));
+  DataEncoder encoder(buffer_sp, process.GetByteOrder(), addr_size);
+  uint32_t offset = 0;
+  for (auto &pair : auxv_data) {
+    offset = encoder.PutAddress(offset, pair.first);
+    offset = encoder.PutAddress(offset, pair.second);
+  }
+  return llvm::MemoryBuffer::getMemBufferCopy(
+      llvm::toStringRef(buffer_sp->GetData()), "");
+}
+
+} // namespace
+
+TEST(NativeProcessELFTest, GetAuxValue) {
+  NiceMock<MockDelegate> DummyDelegate;
+  MockProcessELF process(DummyDelegate, ArchSpec("i386-pc-linux"));
+
+  uint64_t phdr_addr = 0x42;
+  auto auxv_buffer = CreateAuxvData(
+      process, {std::make_pair(AuxVector::AUXV_AT_PHDR, phdr_addr)});
+  EXPECT_CALL(process, GetAuxvData())
+      .WillOnce(Return(ByMove(std::move(auxv_buffer))));
+
+  ASSERT_EQ(phdr_addr, process.GetAuxValue(AuxVector::AUXV_AT_PHDR));
+}
+
+TEST(NativeProcessELFTest, GetELFImageInfoAddress) {
+  NiceMock<MockDelegate> DummyDelegate;
+  MockProcessELF process(DummyDelegate, ArchSpec("i386-pc-linux"));
+
+  uint32_t load_base = 0x1000;
+  uint32_t info_addr = 0x3741;
+  uint32_t phdr_addr = load_base + sizeof(llvm::ELF::Elf32_Ehdr);
+
+  auto auxv_buffer = CreateAuxvData(
+      process,
+      {std::make_pair(AuxVector::AUXV_AT_PHDR, phdr_addr),
+       std::make_pair(AuxVector::AUXV_AT_PHENT, sizeof(llvm::ELF::Elf32_Phdr)),
+       std::make_pair(AuxVector::AUXV_AT_PHNUM, 2)});
+  EXPECT_CALL(process, GetAuxvData())
+      .WillOnce(Return(ByMove(std::move(auxv_buffer))));
+
+  // We're going to set up a fake memory with 2 program headers and 1 entry in
+  // the dynamic section. For simplicity sake they will be contiguous in memory.
+  struct MemoryContents {
+    llvm::ELF::Elf32_Phdr phdr_load;
+    llvm::ELF::Elf32_Phdr phdr_dynamic;
+    llvm::ELF::Elf32_Dyn dyn_debug;
+  } MC;
+  // Setup the 2 program header entries
+  MC.phdr_load.p_type = llvm::ELF::PT_PHDR;
+  MC.phdr_load.p_vaddr = phdr_addr - load_base;
+
+  MC.phdr_dynamic.p_type = llvm::ELF::PT_DYNAMIC;
+  MC.phdr_dynamic.p_vaddr =
+      (phdr_addr + 2 * sizeof(llvm::ELF::Elf32_Phdr)) - load_base;
+  MC.phdr_dynamic.p_memsz = sizeof(llvm::ELF::Elf32_Dyn);
+
+  // Setup the single entry in the .dynamic section
+  MC.dyn_debug.d_tag = llvm::ELF::DT_DEBUG;
+  MC.dyn_debug.d_un.d_ptr = info_addr;
+
+  FakeMemory M(&MC, sizeof(MC), phdr_addr);
+  EXPECT_CALL(process, ReadMemory(_, _))
+      .WillRepeatedly(Invoke(&M, &FakeMemory::Read));
+
+  lldb::addr_t elf_info_addr = process.GetELFImageInfoAddress<
+      llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr, llvm::ELF::Elf32_Dyn>();
+
+  // Read the address at the elf_info_addr location to make sure we're reading
+  // the correct one.
+  lldb::offset_t info_addr_offset = elf_info_addr - phdr_addr;
+  DataExtractor mem_extractor(&MC, sizeof(MC), process.GetByteOrder(),
+                              process.GetAddressByteSize());
+  ASSERT_EQ(mem_extractor.GetAddress(&info_addr_offset), info_addr);
+}
+
+TEST(NativeProcessELFTest, GetELFImageInfoAddress_NoDebugEntry) {
+  NiceMock<MockDelegate> DummyDelegate;
+  MockProcessELF process(DummyDelegate, ArchSpec("i386-pc-linux"));
+
+  uint32_t phdr_addr = sizeof(llvm::ELF::Elf32_Ehdr);
+
+  auto auxv_buffer = CreateAuxvData(
+      process,
+      {std::make_pair(AuxVector::AUXV_AT_PHDR, phdr_addr),
+       std::make_pair(AuxVector::AUXV_AT_PHENT, sizeof(llvm::ELF::Elf32_Phdr)),
+       std::make_pair(AuxVector::AUXV_AT_PHNUM, 2)});
+  EXPECT_CALL(process, GetAuxvData())
+      .WillOnce(Return(ByMove(std::move(auxv_buffer))));
+
+  // We're going to set up a fake memory with 2 program headers and 1 entry in
+  // the dynamic section. For simplicity sake they will be contiguous in memory.
+  struct MemoryContents {
+    llvm::ELF::Elf32_Phdr phdr_load;
+    llvm::ELF::Elf32_Phdr phdr_dynamic;
+    llvm::ELF::Elf32_Dyn dyn_notdebug;
+  } MC;
+  // Setup the 2 program header entries
+  MC.phdr_load.p_type = llvm::ELF::PT_PHDR;
+  MC.phdr_load.p_vaddr = phdr_addr;
+
+  MC.phdr_dynamic.p_type = llvm::ELF::PT_DYNAMIC;
+  MC.phdr_dynamic.p_vaddr = (phdr_addr + 2 * sizeof(llvm::ELF::Elf32_Phdr));
+  MC.phdr_dynamic.p_memsz = sizeof(llvm::ELF::Elf32_Dyn);
+
+  // Setup the single entry in the .dynamic section
+  MC.dyn_notdebug.d_tag = llvm::ELF::DT_NULL;
+
+  FakeMemory M(&MC, sizeof(MC), phdr_addr);
+  EXPECT_CALL(process, ReadMemory(_, _))
+      .WillRepeatedly(Invoke(&M, &FakeMemory::Read));
+
+  lldb::addr_t elf_info_addr = process.GetELFImageInfoAddress<
+      llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr, llvm::ELF::Elf32_Dyn>();
+
+  ASSERT_EQ(elf_info_addr, LLDB_INVALID_ADDRESS);
+}
diff --git a/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h b/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h
new file mode 100644
index 0000000..c72aacd
--- /dev/null
+++ b/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h
@@ -0,0 +1,150 @@
+//===-- NativeProcessTestUtils.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_unittests_Host_NativeProcessTestUtils_h_
+#define lldb_unittests_Host_NativeProcessTestUtils_h_
+
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gmock/gmock.h"
+
+using namespace lldb_private;
+using namespace lldb;
+using namespace testing;
+
+namespace lldb_private {
+
+class MockDelegate : public NativeProcessProtocol::NativeDelegate {
+public:
+  MOCK_METHOD1(InitializeDelegate, void(NativeProcessProtocol *Process));
+  MOCK_METHOD2(ProcessStateChanged,
+               void(NativeProcessProtocol *Process, StateType State));
+  MOCK_METHOD1(DidExec, void(NativeProcessProtocol *Process));
+};
+
+// NB: This class doesn't use the override keyword to avoid
+// -Winconsistent-missing-override warnings from the compiler. The
+// inconsistency comes from the overriding definitions in the MOCK_*** macros.
+template <typename T> class MockProcess : public T {
+public:
+  MockProcess(NativeProcessProtocol::NativeDelegate &Delegate,
+              const ArchSpec &Arch, lldb::pid_t Pid = 1)
+      : T(Pid, -1, Delegate), Arch(Arch) {}
+
+  MOCK_METHOD1(Resume, Status(const ResumeActionList &ResumeActions));
+  MOCK_METHOD0(Halt, Status());
+  MOCK_METHOD0(Detach, Status());
+  MOCK_METHOD1(Signal, Status(int Signo));
+  MOCK_METHOD0(Kill, Status());
+  MOCK_METHOD3(AllocateMemory,
+               Status(size_t Size, uint32_t Permissions, addr_t &Addr));
+  MOCK_METHOD1(DeallocateMemory, Status(addr_t Addr));
+  MOCK_METHOD0(GetSharedLibraryInfoAddress, addr_t());
+  MOCK_METHOD0(UpdateThreads, size_t());
+  MOCK_CONST_METHOD0(GetAuxvData,
+                     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>());
+  MOCK_METHOD2(GetLoadedModuleFileSpec,
+               Status(const char *ModulePath, FileSpec &Spec));
+  MOCK_METHOD2(GetFileLoadAddress,
+               Status(const llvm::StringRef &FileName, addr_t &Addr));
+
+  const ArchSpec &GetArchitecture() const /*override*/ { return Arch; }
+  Status SetBreakpoint(lldb::addr_t Addr, uint32_t Size,
+                       bool Hardware) /*override*/ {
+    if (Hardware)
+      return this->SetHardwareBreakpoint(Addr, Size);
+    else
+      return this->SetSoftwareBreakpoint(Addr, Size);
+  }
+
+  // Redirect base class Read/Write Memory methods to functions whose signatures
+  // are more mock-friendly.
+  Status ReadMemory(addr_t Addr, void *Buf, size_t Size,
+                    size_t &BytesRead) /*override*/ {
+    auto ExpectedMemory = this->ReadMemory(Addr, Size);
+    if (!ExpectedMemory) {
+      BytesRead = 0;
+      return Status(ExpectedMemory.takeError());
+    }
+    BytesRead = ExpectedMemory->size();
+    assert(BytesRead <= Size);
+    std::memcpy(Buf, ExpectedMemory->data(), BytesRead);
+    return Status();
+  }
+
+  Status WriteMemory(addr_t Addr, const void *Buf, size_t Size,
+                     size_t &BytesWritten) /*override*/ {
+    auto ExpectedBytes = this->WriteMemory(
+        Addr, llvm::makeArrayRef(static_cast<const uint8_t *>(Buf), Size));
+    if (!ExpectedBytes) {
+      BytesWritten = 0;
+      return Status(ExpectedBytes.takeError());
+    }
+    BytesWritten = *ExpectedBytes;
+    return Status();
+  }
+
+  MOCK_METHOD2(ReadMemory,
+               llvm::Expected<std::vector<uint8_t>>(addr_t Addr, size_t Size));
+  MOCK_METHOD2(WriteMemory,
+               llvm::Expected<size_t>(addr_t Addr,
+                                      llvm::ArrayRef<uint8_t> Data));
+
+  using T::GetSoftwareBreakpointTrapOpcode;
+  llvm::Expected<std::vector<uint8_t>> ReadMemoryWithoutTrap(addr_t Addr,
+                                                             size_t Size) {
+    std::vector<uint8_t> Data(Size, 0);
+    size_t BytesRead;
+    Status ST =
+        T::ReadMemoryWithoutTrap(Addr, Data.data(), Data.size(), BytesRead);
+    if (ST.Fail())
+      return ST.ToError();
+    Data.resize(BytesRead);
+    return std::move(Data);
+  }
+
+private:
+  ArchSpec Arch;
+};
+
+class FakeMemory {
+public:
+  FakeMemory(llvm::ArrayRef<uint8_t> Data, addr_t start_addr = 0)
+      : Data(Data), m_start_addr(start_addr) {}
+
+  FakeMemory(const void *Data, size_t data_size, addr_t start_addr = 0)
+      : Data((const uint8_t *)Data, ((const uint8_t *)Data) + data_size),
+        m_start_addr(start_addr) {}
+
+  llvm::Expected<std::vector<uint8_t>> Read(addr_t Addr, size_t Size) {
+    Addr -= m_start_addr;
+    if (Addr >= Data.size())
+      return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                     "Address out of range.");
+    Size = std::min(Size, Data.size() - (size_t)Addr);
+    auto Begin = std::next(Data.begin(), Addr);
+    return std::vector<uint8_t>(Begin, std::next(Begin, Size));
+  }
+
+  llvm::Expected<size_t> Write(addr_t Addr, llvm::ArrayRef<uint8_t> Chunk) {
+    Addr -= m_start_addr;
+    if (Addr >= Data.size())
+      return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                     "Address out of range.");
+    size_t Size = std::min(Chunk.size(), Data.size() - (size_t)Addr);
+    std::copy_n(Chunk.begin(), Size, &Data[Addr]);
+    return Size;
+  }
+
+private:
+  std::vector<uint8_t> Data;
+  addr_t m_start_addr;
+};
+} // namespace lldb_private
+
+#endif
\ No newline at end of file