blob: 4f5b68bae79887242740015dbe8a1d9a73195346 [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/diagnostics.h>
#include <lib/elfldltl/dynamic.h>
#include <lib/elfldltl/init-fini.h>
#include <lib/elfldltl/memory.h>
#include <lib/elfldltl/testing/typed-test.h>
#include <array>
#include <string>
#include <vector>
#include <gtest/gtest.h>
namespace {
using NativeInfo = elfldltl::InitFiniInfo<elfldltl::Elf<>>;
template <class Elf>
constexpr typename Elf::size_type kImageAddr = 0x1234000;
template <class Elf>
constexpr typename Elf::Addr kImageData[] = {1, 2, 3, 4};
template <class Elf>
constexpr cpp20::span kImage(kImageData<Elf>);
template <class Elf>
const auto kImageBytes = cpp20::as_bytes(kImage<Elf>);
template <typename Dyn, size_t N>
constexpr cpp20::span<const Dyn> DynSpan(const std::array<Dyn, N>& dyn) {
return {dyn};
}
constexpr elfldltl::DiagnosticsFlags kDiagFlags = {.multiple_errors = true};
FORMAT_TYPED_TEST_SUITE(ElfldltlInitFiniTests);
TYPED_TEST(ElfldltlInitFiniTests, Empty) {
using Elf = typename TestFixture::Elf;
using Dyn = typename Elf::Dyn;
std::vector<std::string> errors;
auto diag = elfldltl::CollectStringsDiagnostics(errors, kDiagFlags);
elfldltl::DirectMemory memory{
{
const_cast<std::byte*>(kImageBytes<Elf>.data()),
kImageBytes<Elf>.size(),
},
kImageAddr<Elf>,
};
constexpr std::array dyn{
Dyn{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::InitFiniInfo<Elf> info;
EXPECT_TRUE(
elfldltl::DecodeDynamic(diag, memory, DynSpan(dyn), elfldltl::DynamicInitObserver(info)));
EXPECT_EQ(0u, diag.errors());
EXPECT_EQ(0u, diag.warnings());
EXPECT_EQ(0u, errors.size());
EXPECT_EQ(0u, info.size());
info.VisitInit([](auto&&... args) { FAIL() << "should not be called"; }, 0);
info.VisitFini([](auto&&... args) { FAIL() << "should not be called"; }, 0);
}
TYPED_TEST(ElfldltlInitFiniTests, ArrayOnly) {
using Elf = typename TestFixture::Elf;
using Dyn = typename Elf::Dyn;
std::vector<std::string> errors;
auto diag = elfldltl::CollectStringsDiagnostics(errors, kDiagFlags);
elfldltl::DirectMemory memory{
{
const_cast<std::byte*>(kImageBytes<Elf>.data()),
kImageBytes<Elf>.size(),
},
kImageAddr<Elf>,
};
constexpr std::array dyn{
Dyn{.tag = elfldltl::ElfDynTag::kInitArray, .val = kImageAddr<Elf>},
Dyn{.tag = elfldltl::ElfDynTag::kInitArraySz, .val = kImageBytes<Elf>.size()},
Dyn{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::InitFiniInfo<Elf> info;
EXPECT_TRUE(
elfldltl::DecodeDynamic(diag, memory, DynSpan(dyn), elfldltl::DynamicInitObserver(info)));
EXPECT_EQ(0u, diag.errors());
EXPECT_EQ(0u, diag.warnings());
EXPECT_EQ(0u, errors.size());
EXPECT_EQ(4u, info.size());
}
TYPED_TEST(ElfldltlInitFiniTests, LegacyOnly) {
using Elf = typename TestFixture::Elf;
using Dyn = typename Elf::Dyn;
std::vector<std::string> errors;
auto diag = elfldltl::CollectStringsDiagnostics(errors, kDiagFlags);
elfldltl::DirectMemory memory{
{
const_cast<std::byte*>(kImageBytes<Elf>.data()),
kImageBytes<Elf>.size(),
},
kImageAddr<Elf>,
};
constexpr std::array dyn{
Dyn{.tag = elfldltl::ElfDynTag::kInit, .val = 0x5678},
Dyn{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::InitFiniInfo<Elf> info;
EXPECT_TRUE(
elfldltl::DecodeDynamic(diag, memory, DynSpan(dyn), elfldltl::DynamicInitObserver(info)));
EXPECT_EQ(0u, diag.errors());
EXPECT_EQ(0u, diag.warnings());
EXPECT_EQ(0u, errors.size());
EXPECT_EQ(1u, info.size());
EXPECT_EQ(0x5678u, info.legacy());
}
TYPED_TEST(ElfldltlInitFiniTests, ArrayWithLegacy) {
using Elf = typename TestFixture::Elf;
using Dyn = typename Elf::Dyn;
std::vector<std::string> errors;
auto diag = elfldltl::CollectStringsDiagnostics(errors, kDiagFlags);
elfldltl::DirectMemory memory{
{
const_cast<std::byte*>(kImageBytes<Elf>.data()),
kImageBytes<Elf>.size(),
},
kImageAddr<Elf>,
};
constexpr std::array dyn{
Dyn{.tag = elfldltl::ElfDynTag::kInit, .val = 0x5678},
Dyn{.tag = elfldltl::ElfDynTag::kInitArray, .val = kImageAddr<Elf>},
Dyn{.tag = elfldltl::ElfDynTag::kInitArraySz, .val = kImageBytes<Elf>.size()},
Dyn{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::InitFiniInfo<Elf> info;
EXPECT_TRUE(
elfldltl::DecodeDynamic(diag, memory, DynSpan(dyn), elfldltl::DynamicInitObserver(info)));
EXPECT_EQ(0u, diag.errors());
EXPECT_EQ(0u, diag.warnings());
EXPECT_EQ(0u, errors.size());
EXPECT_EQ(5u, info.size());
}
TYPED_TEST(ElfldltlInitFiniTests, MissingArray) {
using Elf = typename TestFixture::Elf;
using Dyn = typename Elf::Dyn;
std::vector<std::string> errors;
auto diag = elfldltl::CollectStringsDiagnostics(errors, kDiagFlags);
elfldltl::DirectMemory memory{
{
const_cast<std::byte*>(kImageBytes<Elf>.data()),
kImageBytes<Elf>.size(),
},
kImageAddr<Elf>,
};
constexpr std::array dyn{
// DT_INIT_ARRAY missing with DT_INIT_ARRAYSZ present.
Dyn{.tag = elfldltl::ElfDynTag::kInitArraySz, .val = kImageBytes<Elf>.size()},
Dyn{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::InitFiniInfo<Elf> info;
EXPECT_TRUE(
elfldltl::DecodeDynamic(diag, memory, DynSpan(dyn), elfldltl::DynamicInitObserver(info)));
EXPECT_EQ(1u, diag.errors());
EXPECT_EQ(0u, diag.warnings());
EXPECT_EQ(1u, errors.size());
EXPECT_EQ(0u, info.size());
}
TYPED_TEST(ElfldltlInitFiniTests, MissingSize) {
using Elf = typename TestFixture::Elf;
using Dyn = typename Elf::Dyn;
std::vector<std::string> errors;
auto diag = elfldltl::CollectStringsDiagnostics(errors, kDiagFlags);
elfldltl::DirectMemory memory{
{
const_cast<std::byte*>(kImageBytes<Elf>.data()),
kImageBytes<Elf>.size(),
},
kImageAddr<Elf>,
};
constexpr std::array dyn{
Dyn{.tag = elfldltl::ElfDynTag::kInitArray, .val = kImageAddr<Elf>},
// DT_INIT_ARRAYSZ missing with DT_INIT_ARRAY present.
Dyn{.tag = elfldltl::ElfDynTag::kNull},
};
elfldltl::InitFiniInfo<Elf> info;
EXPECT_TRUE(
elfldltl::DecodeDynamic(diag, memory, DynSpan(dyn), elfldltl::DynamicInitObserver(info)));
EXPECT_EQ(1u, diag.errors());
EXPECT_EQ(0u, diag.warnings());
EXPECT_EQ(1u, errors.size());
EXPECT_EQ(0u, info.size());
}
TYPED_TEST(ElfldltlInitFiniTests, VisitInitTests) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
constexpr typename Elf::Addr array[] = {2, 3, 4, 5};
elfldltl::InitFiniInfo<Elf> info;
info.set_array(cpp20::span(array));
info.set_legacy(1);
ASSERT_EQ(5u, info.size());
info.VisitInit(
[i = size_type{1}](size_type addr, bool relocated) mutable {
EXPECT_EQ(i, addr);
EXPECT_EQ(relocated, addr != 1);
++i;
},
true);
info.VisitInit(
[i = size_type{1}](size_type addr, bool relocated) mutable {
EXPECT_EQ(i, addr);
EXPECT_FALSE(relocated);
++i;
},
false);
}
TYPED_TEST(ElfldltlInitFiniTests, VisitFiniTests) {
using Elf = typename TestFixture::Elf;
using size_type = typename Elf::size_type;
constexpr typename Elf::Addr array[] = {2, 3, 4, 5};
elfldltl::InitFiniInfo<Elf> info;
info.set_array(cpp20::span(array));
info.set_legacy(1);
ASSERT_EQ(5u, info.size());
info.VisitFini(
[i = size_type{5}](size_type addr, bool relocated) mutable {
EXPECT_EQ(i, addr);
EXPECT_EQ(relocated, addr != 1);
--i;
},
true);
info.VisitFini(
[i = size_type{5}](size_type addr, bool relocated) mutable {
EXPECT_EQ(i, addr);
EXPECT_FALSE(relocated);
--i;
},
false);
}
TYPED_TEST(ElfldltlInitFiniTests, Remote) {
using Elf = typename TestFixture::Elf;
using RemoteInitFiniInfo = elfldltl::InitFiniInfo<Elf, elfldltl::RemoteAbiTraits>;
RemoteInitFiniInfo info;
info = RemoteInitFiniInfo(info);
}
// The tests for CallInit and CallFini must use global state since
// the callees are simple function pointers taking no arguments.
std::vector<int> gCalls;
template <int I>
void AppendCall() {
gCalls.push_back(I);
}
const std::array<elfldltl::Elf<>::Addr, 3> gThreeCalls = {
reinterpret_cast<uintptr_t>(&AppendCall<1>),
reinterpret_cast<uintptr_t>(&AppendCall<2>),
reinterpret_cast<uintptr_t>(&AppendCall<3>),
};
TEST(ElfldltlInitFiniTests, CallInitNoLegacy) {
NativeInfo info;
info.set_array(gThreeCalls);
gCalls.clear();
info.CallInit(0);
ASSERT_EQ(gCalls.size(), 3u);
EXPECT_EQ(gCalls[0], 1);
EXPECT_EQ(gCalls[1], 2);
EXPECT_EQ(gCalls[2], 3);
}
TEST(ElfldltlInitFiniTests, CallInitWithLegacy) {
NativeInfo info;
info.set_array(gThreeCalls);
constexpr auto kRelocationAdjustment = kImageAddr<elfldltl::Elf<>>;
info.set_legacy(reinterpret_cast<uintptr_t>(&AppendCall<0>) - kRelocationAdjustment);
gCalls.clear();
info.CallInit(kRelocationAdjustment);
ASSERT_EQ(gCalls.size(), 4u);
EXPECT_EQ(gCalls[0], 0);
EXPECT_EQ(gCalls[1], 1);
EXPECT_EQ(gCalls[2], 2);
EXPECT_EQ(gCalls[3], 3);
}
TEST(ElfldltlInitFiniTests, CallFiniNoLegacy) {
NativeInfo info;
info.set_array(gThreeCalls);
gCalls.clear();
info.CallFini(0);
ASSERT_EQ(gCalls.size(), 3u);
EXPECT_EQ(gCalls[0], 3);
EXPECT_EQ(gCalls[1], 2);
EXPECT_EQ(gCalls[2], 1);
}
TEST(ElfldltlInitFiniTests, CallFiniWithLegacy) {
NativeInfo info;
info.set_array(gThreeCalls);
constexpr auto kRelocationAdjustment = kImageAddr<elfldltl::Elf<>>;
info.set_legacy(reinterpret_cast<uintptr_t>(&AppendCall<0>) - kRelocationAdjustment);
gCalls.clear();
info.CallFini(kRelocationAdjustment);
ASSERT_EQ(gCalls.size(), 4u);
EXPECT_EQ(gCalls[0], 3);
EXPECT_EQ(gCalls[1], 2);
EXPECT_EQ(gCalls[2], 1);
EXPECT_EQ(gCalls[3], 0);
}
} // namespace