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
This commit is contained in:
parent
2de984cd30
commit
f4335b8e3c
|
@ -288,7 +288,7 @@ NativeProcessLinux::NativeProcessLinux(::pid_t pid, int terminal_fd,
|
|||
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 @@ Status NativeProcessLinux::DeallocateMemory(lldb::addr_t addr) {
|
|||
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 @@ Status NativeProcessLinux::StopProcessorTracingOnThread(lldb::user_id_t traceid,
|
|||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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 @@ namespace process_linux {
|
|||
/// 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 @@ public:
|
|||
|
||||
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 @@ public:
|
|||
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 @@ protected:
|
|||
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;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
add_lldb_library(lldbPluginProcessPOSIX PLUGIN
|
||||
CrashReason.cpp
|
||||
NativeProcessELF.cpp
|
||||
ProcessMessage.cpp
|
||||
ProcessPOSIXLog.cpp
|
||||
|
||||
LINK_LIBS
|
||||
lldbPluginProcessUtility
|
||||
lldbUtility
|
||||
LINK_COMPONENTS
|
||||
Support
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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_private;
|
|||
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, SetBreakpoint) {
|
|||
|
||||
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, SetBreakpointFailRead) {
|
|||
|
||||
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, SetBreakpointFailWrite) {
|
|||
|
||||
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, SetBreakpointFailVerify) {
|
|||
|
||||
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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
include_directories(${LLDB_SOURCE_DIR}/source/Plugins/Process/POSIX)
|
||||
|
||||
add_lldb_unittest(ProcessPOSIXTest
|
||||
NativeProcessELFTest.cpp
|
||||
|
||||
LINK_LIBS
|
||||
lldbPluginProcessPOSIX
|
||||
)
|
|
@ -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);
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue