//===-- NativeProcessELFTest.cpp ------------------------------------------===// // // 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 { public: using MockProcess::MockProcess; using NativeProcessELF::GetAuxValue; using NativeProcessELF::GetELFImageInfoAddress; }; std::unique_ptr CreateAuxvData( MockProcessELF &process, llvm::ArrayRef> 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 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 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 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); }