aadsm updated this revision to Diff 203476. aadsm added a comment. Herald added a subscriber: emaste.
This creates a NativeProcessELF to deal with ELF specific things. The NativeProcessLinux now extends this one. Added a test case for the GetAuxValue and GetELFImageInfoAddress. I refactored NativeProcessTestUtils to reuse that code. I also addressed other comments on the review. Note about the tests: I only have one test for the GetELFImageInfoAddress that follows the 32bit version and also makes sure we correctly read the load bias. Tbh I'm not sure if this is enough but should be easy to add more cases now. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D62501/new/ https://reviews.llvm.org/D62501 Files: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp lldb/source/Plugins/Process/Linux/NativeProcessLinux.h lldb/source/Plugins/Process/POSIX/CMakeLists.txt lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp lldb/source/Plugins/Process/POSIX/NativeProcessELF.h lldb/unittests/Host/NativeProcessProtocolTest.cpp lldb/unittests/Host/NativeProcessTestUtils.h lldb/unittests/Process/CMakeLists.txt lldb/unittests/Process/POSIX/CMakeLists.txt lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp
Index: lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp =================================================================== --- /dev/null +++ lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp @@ -0,0 +1,133 @@ +//===-- 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 "../../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 "llvm/Testing/Support/Error.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, + std::vector<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); + } + llvm::StringRef stringref((const char *)buffer_sp->GetBytes(), + buffer_sp->GetByteSize()); + return llvm::MemoryBuffer::getMemBufferCopy(stringref, ""); +} +} // namespace + +TEST(NativeProcessELFTest, GetAuxValue) { + NiceMock<MockDelegate> DummyDelegate; + MockProcessELF process(DummyDelegate, ArchSpec("i386-pc-linux")); + + uint32_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)))); + + llvm::Optional<uint64_t> maybe_phdr_addr = + process.GetAuxValue(AuxVector::AUXV_AT_PHDR); + ASSERT_NE(llvm::None, maybe_phdr_addr); + ASSERT_EQ(phdr_addr, *maybe_phdr_addr); +} + +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 consecutive in memory: + // +------------+ + // | PT_PHDR | + // +------------+ + // | PT_DYNAMIC | + // +------------+ + // | DT_DEBUG | + // +------------+ + uint8_t mem_data[2 * sizeof(llvm::ELF::Elf32_Phdr) + + sizeof(llvm::ELF::Elf32_Dyn)]; + DataEncoder mem_encoder((void *)&mem_data[0], sizeof(mem_data), + process.GetByteOrder(), process.GetAddressByteSize()); + uint32_t offset = 0; + + llvm::ELF::Elf32_Phdr phdr_load; + llvm::ELF::Elf32_Phdr phdr_dynamic; + llvm::ELF::Elf32_Dyn dyn_debug; + + // Setup the 2 program header entries + phdr_load.p_type = llvm::ELF::PT_PHDR; + phdr_load.p_vaddr = phdr_addr - load_base; + offset = mem_encoder.PutData(offset, &phdr_load, sizeof(phdr_load)); + + phdr_dynamic.p_type = llvm::ELF::PT_DYNAMIC; + phdr_dynamic.p_vaddr = + (phdr_addr + 2 * sizeof(llvm::ELF::Elf32_Phdr)) - load_base; + phdr_dynamic.p_memsz = sizeof(llvm::ELF::Elf32_Dyn); + offset = mem_encoder.PutData(offset, &phdr_dynamic, sizeof(phdr_dynamic)); + + // Setup the single entry in the .dynamic section + dyn_debug.d_tag = llvm::ELF::DT_DEBUG; + dyn_debug.d_un.d_ptr = info_addr; + mem_encoder.PutData(offset, &dyn_debug, sizeof(dyn_debug)); + + FakeMemory M(mem_data, 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. + DataExtractor mem_extractor( + mem_encoder.GetDataStart(), mem_encoder.GetByteSize(), + mem_encoder.GetByteOrder(), mem_encoder.GetAddressByteSize()); + lldb::offset_t info_addr_offset = elf_info_addr - phdr_addr; + ASSERT_EQ(mem_extractor.GetAddress(&info_addr_offset), info_addr); +} Index: lldb/unittests/Process/POSIX/CMakeLists.txt =================================================================== --- /dev/null +++ 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 + ) Index: lldb/unittests/Process/CMakeLists.txt =================================================================== --- lldb/unittests/Process/CMakeLists.txt +++ 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) Index: lldb/unittests/Host/NativeProcessTestUtils.h =================================================================== --- /dev/null +++ lldb/unittests/Host/NativeProcessTestUtils.h @@ -0,0 +1,146 @@ +//===-- 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) {} + + 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 Index: lldb/unittests/Host/NativeProcessProtocolTest.cpp =================================================================== --- lldb/unittests/Host/NativeProcessProtocolTest.cpp +++ lldb/unittests/Host/NativeProcessProtocolTest.cpp @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +#include "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)); Index: lldb/source/Plugins/Process/POSIX/NativeProcessELF.h =================================================================== --- /dev/null +++ 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 Index: lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp =================================================================== --- /dev/null +++ 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 Index: lldb/source/Plugins/Process/POSIX/CMakeLists.txt =================================================================== --- lldb/source/Plugins/Process/POSIX/CMakeLists.txt +++ 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 Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ 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; Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -38,6 +38,7 @@ #include "lldb/Utility/State.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StringExtractor.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Errno.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Threading.h" @@ -288,7 +289,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 +1390,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 +2078,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); -}
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits