blob: 9f35c9e497833444701ae784f7fce30fff96f900 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/elfldltl/container.h>
#include <lib/elfldltl/diagnostics.h>
#include <lib/elfldltl/dynamic.h>
#include <lib/elfldltl/machine.h>
#include <lib/elfldltl/memory.h>
#include <lib/elfldltl/testing/diagnostics.h>
#include <lib/elfldltl/testing/typed-test.h>
#include <vector>
#include "symbol-tests.h"
namespace {
using elfldltl::testing::ExpectedSingleError;
using elfldltl::testing::ExpectOkDiagnostics;
FORMAT_TYPED_TEST_SUITE(ElfldltlDynamicTests);
TYPED_TEST(ElfldltlDynamicTests, Empty) {
using Elf = typename TestFixture::Elf;
ExpectOkDiagnostics diag;
elfldltl::DirectMemory memory({}, 0);
// Nothing but the terminator.
constexpr typename Elf::Dyn dyn[] = {
{.tag = elfldltl::ElfDynTag::kNull},
};
// No matchers and nothing to match.
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, memory, cpp20::span(dyn)));
}
TYPED_TEST(ElfldltlDynamicTests, MissingTerminator) {
using Elf = typename TestFixture::Elf;
ExpectedSingleError diag{"missing DT_NULL terminator in PT_DYNAMIC"};
elfldltl::DirectMemory memory({}, 0);
// Empty span has no terminator.
cpp20::span<const typename Elf::Dyn> dyn;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, memory, dyn));
}
TYPED_TEST(ElfldltlDynamicTests, RejectTextrel) {
using Elf = typename TestFixture::Elf;
elfldltl::DirectMemory memory({}, 0);
{
// PT_DYNAMIC without DT_TEXTREL.
constexpr typename Elf::Dyn dyn_notextrel[] = {
{.tag = elfldltl::ElfDynTag::kNull},
};
ExpectOkDiagnostics diag;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, memory, cpp20::span(dyn_notextrel),
elfldltl::DynamicTextrelRejectObserver{}));
}
{
// PT_DYNAMIC with DT_TEXTREL.
constexpr typename Elf::Dyn dyn_textrel[] = {
{.tag = elfldltl::ElfDynTag::kTextRel},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::testing::ExpectedSingleError expected{
elfldltl::DynamicTextrelRejectObserver::kMessage,
};
EXPECT_TRUE(elfldltl::DecodeDynamic(expected, memory, cpp20::span(dyn_textrel),
elfldltl::DynamicTextrelRejectObserver{}));
}
{
// PT_DYNAMIC with DF_TEXTREL.
constexpr typename Elf::Dyn dyn_flags_textrel[] = {
{
.tag = elfldltl::ElfDynTag::kFlags,
.val = elfldltl::ElfDynFlags::kTextRel | elfldltl::ElfDynFlags::kBindNow,
},
{.tag = elfldltl::ElfDynTag::kNull},
};
auto expected = elfldltl::testing::ExpectedSingleError{
elfldltl::DynamicTextrelRejectObserver::kMessage,
};
EXPECT_TRUE(elfldltl::DecodeDynamic(expected, memory, cpp20::span(dyn_flags_textrel),
elfldltl::DynamicTextrelRejectObserver{}));
}
}
TYPED_TEST(ElfldltlDynamicTests, RelocationInfoObserverEmpty) {
using Elf = typename TestFixture::Elf;
using Dyn = typename Elf::Dyn;
ExpectOkDiagnostics diag;
elfldltl::DirectMemory empty_memory({}, 0);
// PT_DYNAMIC with no reloc info.
constexpr Dyn dyn_noreloc[] = {
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::RelocationInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, empty_memory, cpp20::span(dyn_noreloc),
elfldltl::DynamicRelocationInfoObserver(info)));
EXPECT_TRUE(info.rel_relative().empty());
EXPECT_TRUE(info.rel_symbolic().empty());
EXPECT_TRUE(info.rela_relative().empty());
EXPECT_TRUE(info.rela_symbolic().empty());
EXPECT_TRUE(info.relr().empty());
std::visit([](const auto& table) { EXPECT_TRUE(table.empty()); }, info.jmprel());
}
// This synthesizes a memory image of relocation test data with known
// offsets and addresses that can be referenced in dynamic section entries in
// the specific test data. The same image contents are used for several tests
// below with different dynamic section data. Because the Memory API admits
// mutation of the image, the same image buffer shouldn't be reused for
// multiple tests just in case a test mutates the buffer (though they are meant
// not to). So this helper object is created in each test case to reconstruct
// the same data afresh.
template <typename Elf>
class RelocInfoTestImage {
public:
using size_type = typename Elf::size_type;
using Addr = typename Elf::Addr;
using Dyn = typename Elf::Dyn;
using Rel = typename Elf::Rel;
using Rela = typename Elf::Rela;
using Sym = typename Elf::Sym;
static size_type size_bytes() { return sizeof(image_); }
static size_type image_addr() { return kImageAddr; }
static size_type rel_size_bytes() { return sizeof(image_.rel); }
static size_type relent_size_bytes() { return sizeof(image_.rel[0]); }
static size_type rela_size_bytes() { return sizeof(image_.rela); }
static size_type relaent_size_bytes() { return sizeof(image_.rela[0]); }
static size_type relr_size_bytes() { return sizeof(image_.relr); }
static size_type relrent_size_bytes() { return sizeof(image_.relr[0]); }
size_type rel_addr() const { return ImageAddr(image_.rel); }
size_type rela_addr() const { return ImageAddr(image_.rela); }
size_type relr_addr() const { return ImageAddr(image_.relr); }
elfldltl::DirectMemory memory() { return elfldltl::DirectMemory(image_bytes(), kImageAddr); }
private:
// Build up some good relocation data in a memory image.
static constexpr size_type kImageAddr = 0x123400;
static constexpr auto kTestMachine = elfldltl::ElfMachine::kNone;
using TestType = elfldltl::RelocationTraits<kTestMachine>::Type;
static constexpr uint32_t kRelativeType = static_cast<uint32_t>(TestType::kRelative);
static constexpr uint32_t kAbsoluteType = static_cast<uint32_t>(TestType::kAbsolute);
template <typename T>
size_type ImageAddr(const T& data) const {
return static_cast<size_type>(reinterpret_cast<const std::byte*>(&data) -
reinterpret_cast<const std::byte*>(&image_)) +
kImageAddr;
}
struct ImageData {
Rel rel[3] = {
{8, kRelativeType},
{24, kRelativeType},
{4096, kAbsoluteType},
};
Rela rela[3] = {
{8, kRelativeType, 0x11111111},
{24, kRelativeType, 0x33333333},
{4096, kAbsoluteType, 0x1234},
};
Addr relr[3] = {
32,
0x55555555,
0xaaaaaaaa | 1,
};
} image_;
cpp20::span<std::byte> image_bytes() { return cpp20::as_writable_bytes(cpp20::span(&image_, 1)); }
};
TYPED_TEST(ElfldltlDynamicTests, RelocationInfoObserverFullValid) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
ExpectOkDiagnostics diag;
RelocInfoTestImage<Elf> test_image;
// PT_DYNAMIC with full valid reloc info.
const Dyn dyn_goodreloc[] = {
{
.tag = elfldltl::ElfDynTag::kRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{.tag = elfldltl::ElfDynTag::kRelSz, .val = test_image.rel_size_bytes()},
{
.tag = elfldltl::ElfDynTag::kRelEnt,
.val = test_image.relent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kRela,
.val = static_cast<size_type>(test_image.rela_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelaSz,
.val = test_image.rela_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelaEnt,
.val = test_image.relaent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelaCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kJmpRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{
.tag = elfldltl::ElfDynTag::kPltRelSz,
.val = test_image.rel_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kPltRel,
.val = static_cast<size_type>(elfldltl::ElfDynTag::kRel),
},
{
.tag = elfldltl::ElfDynTag::kRelr,
.val = static_cast<size_type>(test_image.relr_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelrSz,
.val = test_image.relr_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelrEnt,
.val = test_image.relrent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::RelocationInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, test_image.memory(), cpp20::span(dyn_goodreloc),
elfldltl::DynamicRelocationInfoObserver(info)));
EXPECT_EQ(2u, info.rel_relative().size());
EXPECT_EQ(1u, info.rel_symbolic().size());
EXPECT_EQ(2u, info.rela_relative().size());
EXPECT_EQ(1u, info.rela_symbolic().size());
EXPECT_EQ(3u, info.relr().size());
std::visit([](const auto& table) { EXPECT_EQ(3u, table.size()); }, info.jmprel());
}
// We'll reuse that same image for the various error case tests.
// These cases only differ in their PT_DYNAMIC contents.
TYPED_TEST(ElfldltlDynamicTests, RelocationInfoObserverBadRelent) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
ExpectedSingleError diag{"incorrect DT_RELENT value"};
RelocInfoTestImage<Elf> test_image;
const Dyn dyn_bad_relent[] = {
{
.tag = elfldltl::ElfDynTag::kRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{.tag = elfldltl::ElfDynTag::kRelSz, .val = test_image.rel_size_bytes()},
{.tag = elfldltl::ElfDynTag::kRelEnt, .val = 17}, // Wrong size.
{.tag = elfldltl::ElfDynTag::kRelCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kRela,
.val = static_cast<size_type>(test_image.rela_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelaSz,
.val = test_image.rela_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelaEnt,
.val = test_image.relaent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelaCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kJmpRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{
.tag = elfldltl::ElfDynTag::kPltRelSz,
.val = test_image.rel_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kPltRel,
.val = static_cast<size_type>(elfldltl::ElfDynTag::kRel),
},
{
.tag = elfldltl::ElfDynTag::kRelr,
.val = static_cast<size_type>(test_image.relr_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelrSz,
.val = test_image.relr_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelrEnt,
.val = test_image.relrent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::RelocationInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, test_image.memory(), cpp20::span(dyn_bad_relent),
elfldltl::DynamicRelocationInfoObserver(info)));
// With keep-going, the data is delivered anyway.
EXPECT_EQ(2u, info.rel_relative().size());
EXPECT_EQ(1u, info.rel_symbolic().size());
EXPECT_EQ(2u, info.rela_relative().size());
EXPECT_EQ(1u, info.rela_symbolic().size());
EXPECT_EQ(3u, info.relr().size());
std::visit([](const auto& table) { EXPECT_EQ(3u, table.size()); }, info.jmprel());
}
TYPED_TEST(ElfldltlDynamicTests, RelocationInfoObserverBadRelaent) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
ExpectedSingleError diag{"incorrect DT_RELAENT value"};
RelocInfoTestImage<Elf> test_image;
const Dyn dyn_bad_relaent[] = {
{
.tag = elfldltl::ElfDynTag::kRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{.tag = elfldltl::ElfDynTag::kRelSz, .val = test_image.rel_size_bytes()},
{
.tag = elfldltl::ElfDynTag::kRelEnt,
.val = test_image.relent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kRela,
.val = static_cast<size_type>(test_image.rela_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelaSz,
.val = test_image.rela_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelaEnt, .val = 17}, // Wrong size.
{.tag = elfldltl::ElfDynTag::kRelaCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kJmpRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{
.tag = elfldltl::ElfDynTag::kPltRelSz,
.val = test_image.rel_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kPltRel,
.val = static_cast<size_type>(elfldltl::ElfDynTag::kRel),
},
{
.tag = elfldltl::ElfDynTag::kRelr,
.val = static_cast<size_type>(test_image.relr_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelrSz,
.val = test_image.relr_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelrEnt,
.val = test_image.relrent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::RelocationInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, test_image.memory(), cpp20::span(dyn_bad_relaent),
elfldltl::DynamicRelocationInfoObserver(info)));
// With keep-going, the data is delivered anyway.
EXPECT_EQ(2u, info.rel_relative().size());
EXPECT_EQ(1u, info.rel_symbolic().size());
EXPECT_EQ(2u, info.rela_relative().size());
EXPECT_EQ(1u, info.rela_symbolic().size());
EXPECT_EQ(3u, info.relr().size());
std::visit([](const auto& table) { EXPECT_EQ(3u, table.size()); }, info.jmprel());
}
TYPED_TEST(ElfldltlDynamicTests, RelocationInfoObserverBadRelrent) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
ExpectedSingleError diag{"incorrect DT_RELRENT value"};
RelocInfoTestImage<Elf> test_image;
const Dyn dyn_bad_relrent[] = {
{
.tag = elfldltl::ElfDynTag::kRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{.tag = elfldltl::ElfDynTag::kRelSz, .val = test_image.rel_size_bytes()},
{
.tag = elfldltl::ElfDynTag::kRelEnt,
.val = test_image.relent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kRela,
.val = static_cast<size_type>(test_image.rela_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelaSz,
.val = test_image.rela_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelaEnt,
.val = test_image.relaent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelaCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kJmpRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{
.tag = elfldltl::ElfDynTag::kPltRelSz,
.val = test_image.rel_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kPltRel,
.val = static_cast<size_type>(elfldltl::ElfDynTag::kRel),
},
{
.tag = elfldltl::ElfDynTag::kRelr,
.val = static_cast<size_type>(test_image.relr_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelrSz,
.val = test_image.relr_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelrEnt, .val = 3}, // Wrong size.
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::RelocationInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, test_image.memory(), cpp20::span(dyn_bad_relrent),
elfldltl::DynamicRelocationInfoObserver(info)));
// With keep-going, the data is delivered anyway.
EXPECT_EQ(2u, info.rel_relative().size());
EXPECT_EQ(1u, info.rel_symbolic().size());
EXPECT_EQ(2u, info.rela_relative().size());
EXPECT_EQ(1u, info.rela_symbolic().size());
EXPECT_EQ(3u, info.relr().size());
std::visit([](const auto& table) { EXPECT_EQ(3u, table.size()); }, info.jmprel());
}
TYPED_TEST(ElfldltlDynamicTests, RelocationInfoObserverMissingPltrel) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
ExpectedSingleError diag{"invalid DT_PLTREL entry"};
RelocInfoTestImage<Elf> test_image;
const Dyn dyn_missing_pltrel[] = {
{
.tag = elfldltl::ElfDynTag::kRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelSz,
.val = test_image.rel_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelEnt,
.val = test_image.relent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kRela,
.val = static_cast<size_type>(test_image.rela_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelaSz,
.val = test_image.rela_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelaEnt,
.val = test_image.relaent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelaCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kJmpRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{
.tag = elfldltl::ElfDynTag::kPltRelSz,
.val = test_image.rel_size_bytes(),
},
// Missing DT_PLTREL.
{
.tag = elfldltl::ElfDynTag::kRelr,
.val = static_cast<size_type>(test_image.relr_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelrSz,
.val = test_image.relr_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelrEnt,
.val = test_image.relrent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::RelocationInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, test_image.memory(), cpp20::span(dyn_missing_pltrel),
elfldltl::DynamicRelocationInfoObserver(info)));
// DT_JMPREL was ignored but the rest is normal.
EXPECT_EQ(2u, info.rel_relative().size());
EXPECT_EQ(1u, info.rel_symbolic().size());
EXPECT_EQ(2u, info.rela_relative().size());
EXPECT_EQ(1u, info.rela_symbolic().size());
EXPECT_EQ(3u, info.relr().size());
std::visit([](const auto& table) { EXPECT_EQ(0u, table.size()); }, info.jmprel());
}
TYPED_TEST(ElfldltlDynamicTests, RelocationInfoObserverBadPltrel) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
ExpectedSingleError diag{"missing DT_PLTREL entry"};
RelocInfoTestImage<Elf> test_image;
const Dyn dyn_bad_pltrel[] = {
{
.tag = elfldltl::ElfDynTag::kRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{.tag = elfldltl::ElfDynTag::kRelSz, .val = test_image.rel_size_bytes()},
{
.tag = elfldltl::ElfDynTag::kRelEnt,
.val = test_image.relent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kRela,
.val = static_cast<size_type>(test_image.rela_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelaSz,
.val = test_image.rela_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelaEnt,
.val = test_image.relaent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelaCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kJmpRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{
.tag = elfldltl::ElfDynTag::kPltRelSz,
.val = test_image.rel_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kPltRel, .val = 0}, // Invalid value.
{
.tag = elfldltl::ElfDynTag::kRelr,
.val = static_cast<size_type>(test_image.relr_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelrSz,
.val = test_image.relr_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelrEnt,
.val = test_image.relrent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::RelocationInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, test_image.memory(), cpp20::span(dyn_bad_pltrel),
elfldltl::DynamicRelocationInfoObserver(info)));
// DT_JMPREL was ignored but the rest is normal.
EXPECT_EQ(2u, info.rel_relative().size());
EXPECT_EQ(1u, info.rel_symbolic().size());
EXPECT_EQ(2u, info.rela_relative().size());
EXPECT_EQ(1u, info.rela_symbolic().size());
EXPECT_EQ(3u, info.relr().size());
std::visit([](const auto& table) { EXPECT_EQ(0u, table.size()); }, info.jmprel());
}
// The bad address, size, and alignment cases are all the same template code
// paths for each table so we only test DT_REL to stand in for the rest.
TYPED_TEST(ElfldltlDynamicTests, RelocationInfoObserverBadRelAddr) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
ExpectedSingleError diag{"DT_REL has misaligned address"};
RelocInfoTestImage<Elf> test_image;
const Dyn dyn_bad_rel_addr[] = {
{
.tag = elfldltl::ElfDynTag::kRel,
// This is an invalid address, before the image starts.
.val = test_image.image_addr() - 1,
},
{.tag = elfldltl::ElfDynTag::kRelSz, .val = test_image.rel_size_bytes()},
{
.tag = elfldltl::ElfDynTag::kRelEnt,
.val = test_image.relent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kRela,
.val = static_cast<size_type>(test_image.rela_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelaSz,
.val = test_image.rela_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelaEnt,
.val = test_image.relaent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelaCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kJmpRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{
.tag = elfldltl::ElfDynTag::kPltRelSz,
.val = test_image.rel_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kPltRel,
.val = static_cast<size_type>(elfldltl::ElfDynTag::kRel),
},
{
.tag = elfldltl::ElfDynTag::kRelr,
.val = static_cast<size_type>(test_image.relr_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelrSz,
.val = test_image.relr_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelrEnt,
.val = test_image.relrent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::RelocationInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, test_image.memory(), cpp20::span(dyn_bad_rel_addr),
elfldltl::DynamicRelocationInfoObserver(info)));
// DT_REL was ignored but the rest is normal.
EXPECT_EQ(0u, info.rel_relative().size());
EXPECT_EQ(0u, info.rel_symbolic().size());
EXPECT_EQ(2u, info.rela_relative().size());
EXPECT_EQ(1u, info.rela_symbolic().size());
EXPECT_EQ(3u, info.relr().size());
std::visit([](const auto& table) { EXPECT_EQ(3u, table.size()); }, info.jmprel());
}
TYPED_TEST(ElfldltlDynamicTests, RelocationInfoObserverBadRelSz) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
ExpectedSingleError diag{"DT_RELSZ not a multiple of DT_REL entry size"};
RelocInfoTestImage<Elf> test_image;
const Dyn dyn_bad_relsz[] = {
{.tag = elfldltl::ElfDynTag::kRel, .val = test_image.rel_addr()},
{
.tag = elfldltl::ElfDynTag::kRelSz,
// This is an invalid size, bigger than the whole image.
.val = test_image.size_bytes() + 1,
},
{
.tag = elfldltl::ElfDynTag::kRelEnt,
.val = test_image.relent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kRela,
.val = static_cast<size_type>(test_image.rela_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelaSz,
.val = test_image.rela_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelaEnt,
.val = test_image.relaent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelaCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kJmpRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{
.tag = elfldltl::ElfDynTag::kPltRelSz,
.val = test_image.rel_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kPltRel,
.val = static_cast<size_type>(elfldltl::ElfDynTag::kRel),
},
{
.tag = elfldltl::ElfDynTag::kRelr,
.val = static_cast<size_type>(test_image.relr_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelrSz,
.val = test_image.relr_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelrEnt,
.val = test_image.relrent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::RelocationInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, test_image.memory(), cpp20::span(dyn_bad_relsz),
elfldltl::DynamicRelocationInfoObserver(info)));
// DT_REL was ignored but the rest is normal.
EXPECT_EQ(0u, info.rel_relative().size());
EXPECT_EQ(0u, info.rel_symbolic().size());
EXPECT_EQ(2u, info.rela_relative().size());
EXPECT_EQ(1u, info.rela_symbolic().size());
EXPECT_EQ(3u, info.relr().size());
std::visit([](const auto& table) { EXPECT_EQ(3u, table.size()); }, info.jmprel());
}
TYPED_TEST(ElfldltlDynamicTests, RelocationInfoObserverBadRelSzAlign) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
ExpectedSingleError diag{"DT_RELSZ not a multiple of DT_REL entry size"};
RelocInfoTestImage<Elf> test_image;
const Dyn dyn_bad_relsz_align[] = {
{.tag = elfldltl::ElfDynTag::kRel, .val = test_image.rel_addr()},
{
.tag = elfldltl::ElfDynTag::kRelSz,
// This size is not a multiple of the entry size.
.val = test_image.rel_size_bytes() - 3,
},
{
.tag = elfldltl::ElfDynTag::kRelEnt,
.val = test_image.relent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kRela,
.val = static_cast<size_type>(test_image.rela_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelaSz,
.val = test_image.rela_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelaEnt,
.val = test_image.relaent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kRelaCount, .val = 2},
{
.tag = elfldltl::ElfDynTag::kJmpRel,
.val = static_cast<size_type>(test_image.rel_addr()),
},
{
.tag = elfldltl::ElfDynTag::kPltRelSz,
.val = test_image.rel_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kPltRel,
.val = static_cast<size_type>(elfldltl::ElfDynTag::kRel),
},
{
.tag = elfldltl::ElfDynTag::kRelr,
.val = static_cast<size_type>(test_image.relr_addr()),
},
{
.tag = elfldltl::ElfDynTag::kRelrSz,
.val = test_image.relr_size_bytes(),
},
{
.tag = elfldltl::ElfDynTag::kRelrEnt,
.val = test_image.relrent_size_bytes(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::RelocationInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, test_image.memory(), cpp20::span(dyn_bad_relsz_align),
elfldltl::DynamicRelocationInfoObserver(info)));
// DT_REL was ignored but the rest is normal.
EXPECT_EQ(0u, info.rel_relative().size());
EXPECT_EQ(0u, info.rel_symbolic().size());
EXPECT_EQ(2u, info.rela_relative().size());
EXPECT_EQ(1u, info.rela_symbolic().size());
EXPECT_EQ(3u, info.relr().size());
std::visit([](const auto& table) { EXPECT_EQ(3u, table.size()); }, info.jmprel());
}
// This synthesizes a memory image of symbol-related test data with known
// offsets and addresses that can be referenced in dynamic section entries in
// the specific test data. The same image contents are used for several tests
// below with different dynamic section data. Because the Memory API admits
// mutation of the image, the same image buffer shouldn't be reused for
// multiple tests just in case a test mutates the buffer (though they are meant
// not to). So this helper object is created in each test case to reconstruct
// the same data afresh.
template <typename Elf>
class SymbolInfoTestImage {
public:
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
using Sym = typename Elf::Sym;
SymbolInfoTestImage() {
// Build up some good symbol data in a memory image.
soname_offset_ = test_syms_.AddString("libfoo.so");
auto symtab_bytes = cpp20::as_bytes(test_syms_.symtab());
cpp20::span<const std::byte> strtab_bytes{
reinterpret_cast<const std::byte*>(test_syms_.strtab().data()),
test_syms_.strtab().size(),
};
image_ = std::vector<std::byte>(symtab_bytes.begin(), symtab_bytes.end());
auto next_addr = [this]() -> size_type {
size_t align_pad = sizeof(size_type) - (image_.size() % sizeof(size_type));
image_.insert(image_.end(), align_pad, std::byte{});
return kSymtabAddr + static_cast<size_type>(image_.size());
};
strtab_addr_ = next_addr();
image_.insert(image_.end(), strtab_bytes.begin(), strtab_bytes.end());
gnu_hash_addr_ = next_addr();
auto gnu_hash_data = cpp20::span(kTestGnuHash<typename Elf::Addr>);
auto gnu_hash_bytes = cpp20::as_bytes(gnu_hash_data);
image_.insert(image_.end(), gnu_hash_bytes.begin(), gnu_hash_bytes.end());
hash_addr_ = next_addr();
auto hash_data = cpp20::span(kTestCompatHash<typename Elf::Word>);
auto hash_bytes = cpp20::as_bytes(hash_data);
image_.insert(image_.end(), hash_bytes.begin(), hash_bytes.end());
}
size_type soname_offset() const { return soname_offset_; }
size_type strtab_addr() const { return strtab_addr_; }
size_t strtab_size_bytes() const { return test_syms_.strtab().size(); }
size_type symtab_addr() { return kSymtabAddr; }
size_type hash_addr() const { return hash_addr_; }
size_type gnu_hash_addr() const { return gnu_hash_addr_; }
const TestSymtab<Elf>& test_syms() const { return test_syms_; }
size_t size_bytes() const { return image_.size(); }
elfldltl::DirectMemory memory() { return elfldltl::DirectMemory(image_, kSymtabAddr); }
private:
static constexpr size_type kSymtabAddr = 0x1000;
std::vector<std::byte> image_;
TestSymtab<Elf> test_syms_ = kTestSymbols<Elf>;
size_type soname_offset_ = 0;
size_type strtab_addr_ = 0;
size_type hash_addr_ = 0;
size_type gnu_hash_addr_ = 0;
};
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverEmpty) {
using Elf = typename TestFixture::Elf;
using Dyn = typename Elf::Dyn;
ExpectOkDiagnostics diag;
elfldltl::DirectMemory empty_memory({}, 0);
// PT_DYNAMIC with no symbol info.
constexpr Dyn dyn_nosyms[] = {
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, empty_memory, cpp20::span(dyn_nosyms),
elfldltl::DynamicSymbolInfoObserver(info)));
EXPECT_EQ(info.strtab().size(), 1u);
EXPECT_TRUE(info.symtab().empty());
EXPECT_TRUE(info.soname().empty());
EXPECT_FALSE(info.compat_hash());
EXPECT_FALSE(info.gnu_hash());
}
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverFullValid) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
using Sym = typename Elf::Sym;
ExpectOkDiagnostics diag;
SymbolInfoTestImage<Elf> test_image;
constexpr uint32_t kDynFlags =
elfldltl::ElfDynFlags::kBindNow | elfldltl::ElfDynFlags::kStaticTls;
constexpr uint32_t kDynFlags1 = 0x3;
// PT_DYNAMIC with full valid symbol info.
const Dyn dyn_goodsyms[] = {
{.tag = elfldltl::ElfDynTag::kSoname, .val = test_image.soname_offset()},
{.tag = elfldltl::ElfDynTag::kSymTab, .val = test_image.symtab_addr()},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = sizeof(Sym)},
{.tag = elfldltl::ElfDynTag::kStrTab, .val = test_image.strtab_addr()},
{
.tag = elfldltl::ElfDynTag::kStrSz,
.val = static_cast<size_type>(test_image.strtab_size_bytes()),
},
{.tag = elfldltl::ElfDynTag::kHash, .val = test_image.hash_addr()},
{
.tag = elfldltl::ElfDynTag::kGnuHash,
.val = test_image.gnu_hash_addr(),
},
{.tag = elfldltl::ElfDynTag::kFlags, .val = kDynFlags},
{.tag = elfldltl::ElfDynTag::kFlags1, .val = kDynFlags1},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, test_image.memory(), cpp20::span(dyn_goodsyms),
elfldltl::DynamicSymbolInfoObserver(info)));
EXPECT_EQ(info.strtab().size(), test_image.test_syms().strtab().size());
EXPECT_EQ(info.strtab(), test_image.test_syms().strtab());
EXPECT_EQ(info.safe_symtab().size(), test_image.test_syms().symtab().size());
EXPECT_EQ(info.soname(), "libfoo.so");
EXPECT_TRUE(info.compat_hash());
EXPECT_TRUE(info.gnu_hash());
EXPECT_EQ(info.flags(), kDynFlags);
EXPECT_EQ(info.flags1(), kDynFlags1);
}
// We'll reuse that same image for the various error case tests.
// These cases only differ in their PT_DYNAMIC contents.
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverBadSonameOffset) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
using Sym = typename Elf::Sym;
ExpectedSingleError diag{"DT_SONAME does not fit in DT_STRTAB"};
SymbolInfoTestImage<Elf> test_image;
elfldltl::DirectMemory image_memory = test_image.memory();
const Dyn dyn_bad_soname_offset[] = {
{
.tag = elfldltl::ElfDynTag::kSoname,
// This is an invalid string table offset.
.val = static_cast<size_type>(test_image.test_syms().strtab().size()),
},
{.tag = elfldltl::ElfDynTag::kSymTab, .val = test_image.symtab_addr()},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = sizeof(Sym)},
{.tag = elfldltl::ElfDynTag::kStrTab, .val = test_image.strtab_addr()},
{
.tag = elfldltl::ElfDynTag::kStrSz,
.val = static_cast<size_type>(test_image.strtab_size_bytes()),
},
{.tag = elfldltl::ElfDynTag::kHash, .val = test_image.hash_addr()},
{.tag = elfldltl::ElfDynTag::kGnuHash, .val = test_image.gnu_hash_addr()},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, image_memory, cpp20::span(dyn_bad_soname_offset),
elfldltl::DynamicSymbolInfoObserver(info)));
}
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverBadSyment) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
ExpectedSingleError diag{"incorrect DT_SYMENT value ", 17};
SymbolInfoTestImage<Elf> test_image;
elfldltl::DirectMemory image_memory = test_image.memory();
const Dyn dyn_bad_syment[] = {
{.tag = elfldltl::ElfDynTag::kSoname, .val = test_image.soname_offset()},
{.tag = elfldltl::ElfDynTag::kSymTab, .val = test_image.symtab_addr()},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = 17}, // Wrong size.
{.tag = elfldltl::ElfDynTag::kStrTab, .val = test_image.strtab_addr()},
{
.tag = elfldltl::ElfDynTag::kStrSz,
.val = static_cast<size_type>(test_image.strtab_size_bytes()),
},
{.tag = elfldltl::ElfDynTag::kHash, .val = test_image.hash_addr()},
{
.tag = elfldltl::ElfDynTag::kGnuHash,
.val = test_image.gnu_hash_addr(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, image_memory, cpp20::span(dyn_bad_syment),
elfldltl::DynamicSymbolInfoObserver(info)));
}
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverMissingStrsz) {
using Elf = typename TestFixture::Elf;
using Dyn = typename Elf::Dyn;
using Sym = typename Elf::Sym;
ExpectedSingleError diag{"DT_STRTAB without DT_STRSZ"};
SymbolInfoTestImage<Elf> test_image;
elfldltl::DirectMemory image_memory = test_image.memory();
const Dyn dyn_missing_strsz[] = {
{.tag = elfldltl::ElfDynTag::kSymTab, .val = test_image.symtab_addr()},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = sizeof(Sym)},
{.tag = elfldltl::ElfDynTag::kStrTab, .val = test_image.strtab_addr()},
// DT_STRSZ omitted with DT_STRTAB present.
{.tag = elfldltl::ElfDynTag::kHash, .val = test_image.hash_addr()},
{
.tag = elfldltl::ElfDynTag::kGnuHash,
.val = test_image.gnu_hash_addr(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, image_memory, cpp20::span(dyn_missing_strsz),
elfldltl::DynamicSymbolInfoObserver(info)));
}
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverMissingStrtab) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
using Sym = typename Elf::Sym;
ExpectedSingleError diag{"DT_STRSZ without DT_STRTAB"};
SymbolInfoTestImage<Elf> test_image;
elfldltl::DirectMemory image_memory = test_image.memory();
const Dyn dyn_missing_strtab[] = {
{.tag = elfldltl::ElfDynTag::kSymTab, .val = test_image.symtab_addr()},
// DT_STRTAB omitted with DT_STRSZ present.
{
.tag = elfldltl::ElfDynTag::kStrSz,
.val = static_cast<size_type>(test_image.strtab_size_bytes()),
},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = sizeof(Sym)},
{.tag = elfldltl::ElfDynTag::kHash, .val = test_image.hash_addr()},
{
.tag = elfldltl::ElfDynTag::kGnuHash,
.val = test_image.gnu_hash_addr(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, image_memory, cpp20::span(dyn_missing_strtab),
elfldltl::DynamicSymbolInfoObserver(info)));
}
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverBadStrtabAddr) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
using Sym = typename Elf::Sym;
ExpectedSingleError diag{
"invalid address in DT_STRTAB or invalid size in DT_STRSZ",
};
SymbolInfoTestImage<Elf> test_image;
elfldltl::DirectMemory image_memory = test_image.memory();
const Dyn dyn_bad_strtab_addr[] = {
{.tag = elfldltl::ElfDynTag::kSymTab, .val = test_image.symtab_addr()},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = sizeof(Sym)},
// This is an invalid address, before the image start.
{
.tag = elfldltl::ElfDynTag::kStrTab,
.val = test_image.symtab_addr() - 1,
},
{
.tag = elfldltl::ElfDynTag::kStrSz,
.val = static_cast<size_type>(test_image.strtab_size_bytes()),
},
{.tag = elfldltl::ElfDynTag::kHash, .val = test_image.hash_addr()},
{
.tag = elfldltl::ElfDynTag::kGnuHash,
.val = test_image.gnu_hash_addr(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, image_memory, cpp20::span(dyn_bad_strtab_addr),
elfldltl::DynamicSymbolInfoObserver(info)));
}
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverBadSymtabAddr) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
using Sym = typename Elf::Sym;
ExpectOkDiagnostics diag;
SymbolInfoTestImage<Elf> test_image;
elfldltl::DirectMemory image_memory = test_image.memory();
// Since the symtab has no known bounds, bad addresses are only diagnosed via
// the memory object and cause hard failure, not via the diag object where
// keep_going causes success return.
const Dyn dyn_bad_symtab_addr[] = {
{.tag = elfldltl::ElfDynTag::kSoname, .val = test_image.soname_offset()},
{
.tag = elfldltl::ElfDynTag::kSymTab,
// This is an invalid address, past the image end.
.val = static_cast<size_type>(test_image.symtab_addr() + test_image.size_bytes()),
},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = sizeof(Sym)},
{.tag = elfldltl::ElfDynTag::kStrTab, .val = test_image.strtab_addr()},
{
.tag = elfldltl::ElfDynTag::kStrSz,
.val = static_cast<size_type>(test_image.strtab_size_bytes()),
},
{.tag = elfldltl::ElfDynTag::kHash, .val = test_image.hash_addr()},
{
.tag = elfldltl::ElfDynTag::kGnuHash,
.val = test_image.gnu_hash_addr(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_FALSE(elfldltl::DecodeDynamic(diag, image_memory, cpp20::span(dyn_bad_symtab_addr),
elfldltl::DynamicSymbolInfoObserver(info)));
}
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverBadSymtabAlign) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
using Sym = typename Elf::Sym;
ExpectedSingleError diag{"DT_SYMTAB has misaligned address"};
SymbolInfoTestImage<Elf> test_image;
elfldltl::DirectMemory image_memory = test_image.memory();
// A misaligned symtab becomes a hard failure after diagnosis because it's
// treated like a memory failure in addition to the diagnosed error.
const Dyn dyn_bad_symtab_align[] = {
{.tag = elfldltl::ElfDynTag::kSoname, .val = test_image.soname_offset()},
{
.tag = elfldltl::ElfDynTag::kSymTab,
// This is misaligned vs alignof(Sym).
.val = test_image.symtab_addr() + 2,
},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = sizeof(Sym)},
{.tag = elfldltl::ElfDynTag::kStrTab, .val = test_image.strtab_addr()},
{
.tag = elfldltl::ElfDynTag::kStrSz,
.val = static_cast<size_type>(test_image.strtab_size_bytes()),
},
{.tag = elfldltl::ElfDynTag::kHash, .val = test_image.hash_addr()},
{
.tag = elfldltl::ElfDynTag::kGnuHash,
.val = test_image.gnu_hash_addr(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_FALSE(elfldltl::DecodeDynamic(diag, image_memory, cpp20::span(dyn_bad_symtab_align),
elfldltl::DynamicSymbolInfoObserver(info)));
}
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverBadHashAddr) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
using Sym = typename Elf::Sym;
ExpectOkDiagnostics diag;
SymbolInfoTestImage<Elf> test_image;
elfldltl::DirectMemory image_memory = test_image.memory();
// Since DT_HASH has no known bounds, bad addresses are only diagnosed via
// the memory object and cause hard failure, not via the diag object where
// keep_going causes success return.
const Dyn dyn_bad_hash_addr[] = {
{.tag = elfldltl::ElfDynTag::kSoname, .val = test_image.soname_offset()},
{.tag = elfldltl::ElfDynTag::kSymTab, .val = test_image.symtab_addr()},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = sizeof(Sym)},
{.tag = elfldltl::ElfDynTag::kStrTab, .val = test_image.strtab_addr()},
{
.tag = elfldltl::ElfDynTag::kStrSz,
.val = static_cast<size_type>(test_image.strtab_size_bytes()),
},
{
.tag = elfldltl::ElfDynTag::kHash,
// This is an invalid address, past the image end.
.val = static_cast<size_type>(test_image.symtab_addr() + test_image.size_bytes()),
},
{
.tag = elfldltl::ElfDynTag::kGnuHash,
.val = test_image.gnu_hash_addr(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_FALSE(elfldltl::DecodeDynamic(diag, image_memory, cpp20::span(dyn_bad_hash_addr),
elfldltl::DynamicSymbolInfoObserver(info)));
}
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverBadHashAlign) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
using Sym = typename Elf::Sym;
ExpectedSingleError diag{"DT_HASH has misaligned address"};
SymbolInfoTestImage<Elf> test_image;
elfldltl::DirectMemory image_memory = test_image.memory();
const Dyn dyn_bad_hash_align[] = {
{.tag = elfldltl::ElfDynTag::kSoname, .val = test_image.soname_offset()},
{.tag = elfldltl::ElfDynTag::kSymTab, .val = test_image.symtab_addr()},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = sizeof(Sym)},
{.tag = elfldltl::ElfDynTag::kStrTab, .val = test_image.strtab_addr()},
{
.tag = elfldltl::ElfDynTag::kStrSz,
.val = static_cast<size_type>(test_image.strtab_size_bytes()),
},
{
.tag = elfldltl::ElfDynTag::kHash,
// This is misaligned vs alignof(Word).
.val = test_image.hash_addr() + 2,
},
{
.tag = elfldltl::ElfDynTag::kGnuHash,
.val = test_image.gnu_hash_addr(),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, image_memory, cpp20::span(dyn_bad_hash_align),
elfldltl::DynamicSymbolInfoObserver(info)));
}
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverBadGnuHashAddr) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
using Sym = typename Elf::Sym;
ExpectOkDiagnostics diag;
SymbolInfoTestImage<Elf> test_image;
elfldltl::DirectMemory image_memory = test_image.memory();
// Since DT_GNU_HASH has no known bounds, bad addresses are only diagnosed
// via the memory object and cause hard failure, not via the diag object
// where keep_going causes success return.
const Dyn dyn_bad_gnu_hash_addr[] = {
{.tag = elfldltl::ElfDynTag::kSoname, .val = test_image.soname_offset()},
{.tag = elfldltl::ElfDynTag::kSymTab, .val = test_image.symtab_addr()},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = sizeof(Sym)},
{.tag = elfldltl::ElfDynTag::kStrTab, .val = test_image.strtab_addr()},
{
.tag = elfldltl::ElfDynTag::kStrSz,
.val = static_cast<size_type>(test_image.strtab_size_bytes()),
},
{.tag = elfldltl::ElfDynTag::kHash, .val = test_image.hash_addr()},
{
.tag = elfldltl::ElfDynTag::kGnuHash,
// This is an invalid address, past the image end.
.val = static_cast<size_type>(test_image.symtab_addr() + test_image.size_bytes()),
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_FALSE(elfldltl::DecodeDynamic(diag, image_memory, cpp20::span(dyn_bad_gnu_hash_addr),
elfldltl::DynamicSymbolInfoObserver(info)));
}
TYPED_TEST(ElfldltlDynamicTests, SymbolInfoObserverBadGnuHashAlign) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
using Dyn = typename Elf::Dyn;
using Sym = typename Elf::Sym;
ExpectedSingleError diag{"DT_GNU_HASH has misaligned address"};
SymbolInfoTestImage<Elf> test_image;
elfldltl::DirectMemory image_memory = test_image.memory();
const Dyn dyn_bad_gnu_hash_align[] = {
{.tag = elfldltl::ElfDynTag::kSoname, .val = test_image.soname_offset()},
{.tag = elfldltl::ElfDynTag::kSymTab, .val = test_image.symtab_addr()},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = sizeof(Sym)},
{.tag = elfldltl::ElfDynTag::kStrTab, .val = test_image.strtab_addr()},
{
.tag = elfldltl::ElfDynTag::kStrSz,
.val = static_cast<size_type>(test_image.strtab_size_bytes()),
},
{.tag = elfldltl::ElfDynTag::kHash, .val = test_image.hash_addr()},
{
.tag = elfldltl::ElfDynTag::kGnuHash,
// This is misaligned vs alignof(size_type).
.val = test_image.hash_addr() + sizeof(size_type) - 1,
},
{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::SymbolInfo<Elf> info;
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, image_memory, cpp20::span(dyn_bad_gnu_hash_align),
elfldltl::DynamicSymbolInfoObserver(info)));
}
template <class Elf, class AbiTraits = elfldltl::LocalAbiTraits>
struct NotCalledSymbolInfo {
std::string_view string(typename Elf::size_type) const {
ADD_FAILURE();
return {};
}
};
TYPED_TEST(ElfldltlDynamicTests, ObserveNeededEmpty) {
using Elf = typename TestFixture::Elf;
auto diag = ExpectOkDiagnostics();
elfldltl::DirectMemory memory({}, 0);
NotCalledSymbolInfo<Elf> si;
constexpr typename Elf::Dyn dyn[] = {
{.tag = elfldltl::ElfDynTag::kNull},
};
EXPECT_TRUE(
elfldltl::DecodeDynamic(diag, memory, cpp20::span(dyn),
elfldltl::DynamicNeededObserver(si, [](std::string_view needed) {
ADD_FAILURE() << "Unexpected needed entry:", needed.data();
return false;
})));
}
TYPED_TEST(ElfldltlDynamicTests, ObserveNeeded) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
auto diag = ExpectOkDiagnostics();
elfldltl::DirectMemory memory({}, 0);
elfldltl::SymbolInfo<Elf> si;
constexpr std::string_view kNeededStrings[] = {"zero.so", "one.so", "two.so", "3.so"};
TestSymtab<Elf> symtab;
const typename Elf::Dyn dyn[] = {
{.tag = elfldltl::ElfDynTag::kNeeded, .val = symtab.AddString(kNeededStrings[0])},
{.tag = elfldltl::ElfDynTag::kNeeded, .val = symtab.AddString(kNeededStrings[1])},
{.tag = elfldltl::ElfDynTag::kNeeded, .val = symtab.AddString(kNeededStrings[2])},
{.tag = elfldltl::ElfDynTag::kNeeded, .val = symtab.AddString(kNeededStrings[3])},
{.tag = elfldltl::ElfDynTag::kNull},
};
symtab.SetInfo(si);
size_type current_index = 0;
auto expect_next = [&](std::string_view needed) {
EXPECT_EQ(kNeededStrings[current_index++], needed);
return true;
};
EXPECT_TRUE(elfldltl::DecodeDynamic(diag, memory, cpp20::span(dyn),
elfldltl::DynamicNeededObserver(si, expect_next)));
}
TYPED_TEST(ElfldltlDynamicTests, ObserveValueCollection) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
auto diag = ExpectOkDiagnostics();
elfldltl::DirectMemory memory({}, 0);
TestSymtab<Elf> symtab;
auto val0 = symtab.AddString("zero.so");
auto val1 = symtab.AddString("one.so");
auto val2 = symtab.AddString("two.so");
auto val3 = symtab.AddString("three.so");
const typename Elf::Dyn dyn[] = {
{.tag = elfldltl::ElfDynTag::kNeeded, .val = val0},
{.tag = elfldltl::ElfDynTag::kNeeded, .val = val1},
{.tag = elfldltl::ElfDynTag::kNeeded, .val = val2},
{.tag = elfldltl::ElfDynTag::kNeeded, .val = val3},
// These tags should not be matched or collected by the observer.
{.tag = elfldltl::ElfDynTag::kSoname, .val = 0x1},
{.tag = elfldltl::ElfDynTag::kSymTab, .val = 0x2},
{.tag = elfldltl::ElfDynTag::kSymEnt, .val = 0x3},
{.tag = elfldltl::ElfDynTag::kNull},
};
static const constexpr std::string_view kCollectionError = "Failed to push value to collection.";
elfldltl::StdContainer<std::vector>::Container<size_type> values;
EXPECT_TRUE(elfldltl::DecodeDynamic(
diag, memory, cpp20::span(dyn),
elfldltl::DynamicValueCollectionObserver<
Elf, elfldltl::ElfDynTag::kNeeded,
elfldltl::StdContainer<std::vector>::Container<size_type>, kCollectionError>(values)));
EXPECT_EQ(values.size(), 4u);
EXPECT_EQ(values[0], val0);
EXPECT_EQ(values[1], val1);
EXPECT_EQ(values[2], val2);
EXPECT_EQ(values[3], val3);
}
} // namespace