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:
155
lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp
Normal file
155
lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp
Normal file
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user