blob: a478a5805a89b734e2f6c2cd6ad94f65989be872 [file]
// Copyright 2026 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 "src/lib/unwinder/loaded_elf_module.h"
#include <elf.h>
#include <lib/fit/result.h>
#include <cstring>
#include <gtest/gtest.h>
#include "src/lib/unwinder/memory.h"
namespace unwinder {
namespace {
template <size_t kNumPhdrs, size_t kNumShdrs = 0>
struct FakeElf64Module {
Elf64_Ehdr ehdr;
Elf64_Phdr phdr[kNumPhdrs];
Elf64_Shdr shdr[kNumShdrs];
// This is just a guess based on the number of sections that we are given. If there isn't enough
// room, |PopulateShStrTab| will fail.
static constexpr size_t kStrtabSize = kNumShdrs * 16;
char strtab[kStrtabSize];
};
template <size_t kNumPhdrs, size_t kNumShdrs = 0>
struct FakeElf32Module {
Elf32_Ehdr ehdr;
Elf32_Phdr phdr[kNumPhdrs];
Elf32_Shdr shdr[kNumShdrs];
// This is just a guess based on the number of sections that we are given. If there isn't enough
// room, |PopulateShStrTab| will fail.
static constexpr size_t kStrtabSize = kNumShdrs * 16;
char strtab[kStrtabSize];
};
// Below are helpers to prevent incorrect usage of PopulateShStrTab (and any future helpers we
// have) since directly writing ELF structs in memory is a bit sketchy, but anything more
// heavyweight than this adds significant complexity to these tests which are otherwise fairly
// simple.
template <typename T, template <std::size_t, std::size_t> class Template>
struct is_specialization : std::false_type {};
template <template <std::size_t, std::size_t> class Template, std::size_t N, std::size_t M>
struct is_specialization<Template<N, M>, Template> : std::true_type {};
template <typename T, template <std::size_t, std::size_t> class Template>
inline constexpr bool is_specialization_v = is_specialization<T, Template>::value;
template <typename T>
concept IsFakeElfModule =
is_specialization_v<T, FakeElf64Module> || is_specialization_v<T, FakeElf32Module>;
// Fills in the section header string table for the given module. Returns an error if the given
// section names do not fit in the allocated strtab of |elf_module|.
fit::result<fit::failed> PopulateShStrTab(IsFakeElfModule auto& elf_module,
std::span<const std::string> section_names) {
// Populate section header string table.
uint32_t table_offset = 0;
size_t section_index = 0;
const size_t strtab_len = sizeof(elf_module.strtab);
for (const auto& name : section_names) {
auto& shdr = elf_module.shdr[section_index];
shdr.sh_name = table_offset;
table_offset += name.size();
if (table_offset >= strtab_len) {
return fit::error(fit::failed());
}
strncpy(elf_module.strtab + shdr.sh_name, name.c_str(), name.size());
section_index++;
}
return fit::ok();
}
TEST(LoadedElfModule, Load64Bit) {
constexpr size_t kNumPhdrs = 3;
using FakeModule = FakeElf64Module<kNumPhdrs>;
FakeModule fake;
memset(&fake, 0, sizeof(fake));
// Set up 64-bit ELF header
memcpy(fake.ehdr.e_ident, ELFMAG, SELFMAG);
fake.ehdr.e_ident[EI_CLASS] = ELFCLASS64;
fake.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
fake.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
fake.ehdr.e_type = ET_DYN;
fake.ehdr.e_machine = EM_X86_64;
fake.ehdr.e_version = EV_CURRENT;
fake.ehdr.e_phoff = offsetof(FakeModule, phdr);
fake.ehdr.e_phnum = kNumPhdrs;
fake.ehdr.e_phentsize = sizeof(Elf64_Phdr);
// Set up a PT_LOAD segment
fake.phdr[0].p_type = PT_LOAD;
fake.phdr[0].p_vaddr = 0x1000;
fake.phdr[0].p_memsz = 0x2000;
fake.phdr[0].p_flags = PF_R | PF_X;
fake.phdr[1].p_type = PT_LOAD;
fake.phdr[1].p_vaddr = 0x3000;
fake.phdr[1].p_memsz = 0x2000;
fake.phdr[1].p_flags = PF_R | PF_X;
fake.phdr[2].p_type = PT_LOAD;
fake.phdr[2].p_vaddr = 0x5000;
fake.phdr[2].p_memsz = 0x2000;
fake.phdr[2].p_flags = PF_R | PF_X;
LocalMemory mem;
uint64_t load_addr = reinterpret_cast<uint64_t>(&fake);
Module module(load_addr, &mem, Module::AddressMode::kProcess);
LoadedElfModule loaded(module);
auto result = loaded.Load();
ASSERT_TRUE(result.is_ok()) << result.error_value().msg();
EXPECT_EQ(loaded.load_address(), load_addr);
EXPECT_EQ(loaded.phdrs().size(), 3u);
EXPECT_EQ(loaded.size(), Module::AddressSize::k64Bit);
// IsValidPC should check (load_addr + p_vaddr) to (load_addr + p_vaddr + p_memsz)
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x1000));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x2fff));
EXPECT_FALSE(loaded.IsValidPC(load_addr + 0x0fff));
// Valid inside of the second phdr.
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x3000));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x3500));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x4000));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x4fff));
// Valid inside of the third phdr.
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x5000));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x5500));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x6000));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x6fff));
// Outside of the range of the last.
EXPECT_FALSE(loaded.IsValidPC(load_addr + 0x7000));
}
TEST(LoadedElfModule, Load64BitOnePhdr) {
constexpr size_t kNumPhdrs = 1;
using FakeModule = FakeElf64Module<kNumPhdrs>;
FakeModule fake;
memset(&fake, 0, sizeof(fake));
// Set up 64-bit ELF header
memcpy(fake.ehdr.e_ident, ELFMAG, SELFMAG);
fake.ehdr.e_ident[EI_CLASS] = ELFCLASS64;
fake.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
fake.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
fake.ehdr.e_type = ET_DYN;
fake.ehdr.e_machine = EM_X86_64;
fake.ehdr.e_version = EV_CURRENT;
fake.ehdr.e_phoff = offsetof(FakeModule, phdr);
fake.ehdr.e_phnum = kNumPhdrs;
fake.ehdr.e_phentsize = sizeof(Elf64_Phdr);
// Set up a PT_LOAD segment
fake.phdr[0].p_type = PT_LOAD;
fake.phdr[0].p_vaddr = 0x1000;
fake.phdr[0].p_memsz = 0x2000;
fake.phdr[0].p_flags = PF_R | PF_X;
LocalMemory mem;
uint64_t load_addr = reinterpret_cast<uint64_t>(&fake);
Module module(load_addr, &mem, Module::AddressMode::kProcess);
LoadedElfModule loaded(module);
auto result = loaded.Load();
ASSERT_TRUE(result.is_ok()) << result.error_value().msg();
EXPECT_EQ(loaded.load_address(), load_addr);
EXPECT_EQ(loaded.size(), Module::AddressSize::k64Bit);
// IsValidPC should check (load_addr + p_vaddr) to (load_addr + p_vaddr + p_memsz)
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x1000));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x2fff));
EXPECT_FALSE(loaded.IsValidPC(load_addr + 0x0fff));
EXPECT_FALSE(loaded.IsValidPC(load_addr + 0x3000));
}
TEST(LoadedElfModule, Load32Bit) {
constexpr size_t kNumPhdrs = 3;
using FakeModule = FakeElf32Module<kNumPhdrs>;
FakeModule fake;
memset(&fake, 0, sizeof(fake));
// Set up 32-bit ELF header
memcpy(fake.ehdr.e_ident, ELFMAG, SELFMAG);
fake.ehdr.e_ident[EI_CLASS] = ELFCLASS32;
fake.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
fake.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
fake.ehdr.e_type = ET_DYN;
fake.ehdr.e_machine = EM_ARM;
fake.ehdr.e_version = EV_CURRENT;
fake.ehdr.e_phoff = offsetof(FakeModule, phdr);
fake.ehdr.e_phnum = kNumPhdrs;
fake.ehdr.e_phentsize = sizeof(Elf32_Phdr);
// Set up a PT_LOAD segment
fake.phdr[0].p_type = PT_LOAD;
fake.phdr[0].p_vaddr = 0x1000;
fake.phdr[0].p_memsz = 0x2000;
fake.phdr[1].p_type = PT_LOAD;
fake.phdr[1].p_vaddr = 0x3000;
fake.phdr[1].p_memsz = 0x2000;
fake.phdr[2].p_type = PT_LOAD;
fake.phdr[2].p_vaddr = 0x5000;
fake.phdr[2].p_memsz = 0x2000;
LocalMemory mem;
uint64_t load_addr = reinterpret_cast<uint64_t>(&fake);
Module module(load_addr, &mem, Module::AddressMode::kProcess);
LoadedElfModule loaded(module);
auto result = loaded.Load();
ASSERT_TRUE(result.is_ok()) << result.error_value().msg();
EXPECT_EQ(loaded.load_address(), load_addr);
EXPECT_EQ(loaded.size(), Module::AddressSize::k32Bit);
EXPECT_EQ(loaded.phdrs().size(), 3u);
// Verify upcasting
EXPECT_EQ(loaded.phdrs()[0].p_type, static_cast<uint32_t>(PT_LOAD));
EXPECT_EQ(loaded.phdrs()[0].p_vaddr, 0x1000u);
EXPECT_EQ(loaded.phdrs()[0].p_memsz, 0x2000u);
EXPECT_EQ(loaded.phdrs()[1].p_type, static_cast<uint32_t>(PT_LOAD));
EXPECT_EQ(loaded.phdrs()[1].p_vaddr, 0x3000u);
EXPECT_EQ(loaded.phdrs()[1].p_memsz, 0x2000u);
EXPECT_EQ(loaded.phdrs()[2].p_type, static_cast<uint32_t>(PT_LOAD));
EXPECT_EQ(loaded.phdrs()[2].p_vaddr, 0x5000u);
EXPECT_EQ(loaded.phdrs()[2].p_memsz, 0x2000u);
// Valid PCs don't start until the beginning of the first loaded segment.
EXPECT_FALSE(loaded.IsValidPC(load_addr + 0x0fff));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x1500));
// Valid inside of the second phdr.
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x3000));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x3500));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x4000));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x4fff));
// Valid inside of the third phdr.
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x5000));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x5500));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x6000));
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x6fff));
// Outside of the range of the last.
EXPECT_FALSE(loaded.IsValidPC(load_addr + 0x7000));
}
TEST(LoadedElfModule, Load32BitOnePhdr) {
constexpr size_t kNumPhdrs = 1;
using FakeModule = FakeElf32Module<kNumPhdrs>;
FakeModule fake;
memset(&fake, 0, sizeof(fake));
// Set up 32-bit ELF header
memcpy(fake.ehdr.e_ident, ELFMAG, SELFMAG);
fake.ehdr.e_ident[EI_CLASS] = ELFCLASS32;
fake.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
fake.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
fake.ehdr.e_type = ET_DYN;
fake.ehdr.e_machine = EM_ARM;
fake.ehdr.e_version = EV_CURRENT;
fake.ehdr.e_phoff = offsetof(FakeModule, phdr);
fake.ehdr.e_phnum = kNumPhdrs;
fake.ehdr.e_phentsize = sizeof(Elf32_Phdr);
// Set up a PT_LOAD segment
fake.phdr[0].p_type = PT_LOAD;
fake.phdr[0].p_vaddr = 0x1000;
fake.phdr[0].p_memsz = 0x2000;
LocalMemory mem;
uint64_t load_addr = reinterpret_cast<uint64_t>(&fake);
Module module(load_addr, &mem, Module::AddressMode::kProcess);
LoadedElfModule loaded(module);
auto result = loaded.Load();
ASSERT_TRUE(result.is_ok()) << result.error_value().msg();
EXPECT_EQ(loaded.size(), Module::AddressSize::k32Bit);
EXPECT_EQ(loaded.phdrs().size(), 1u);
// Verify upcasting
EXPECT_EQ(loaded.phdrs()[0].p_type, static_cast<uint32_t>(PT_LOAD));
EXPECT_EQ(loaded.phdrs()[0].p_vaddr, 0x1000u);
EXPECT_EQ(loaded.phdrs()[0].p_memsz, 0x2000u);
EXPECT_TRUE(loaded.IsValidPC(load_addr + 0x1500));
EXPECT_FALSE(loaded.IsValidPC(load_addr + 0x0fff));
EXPECT_FALSE(loaded.IsValidPC(load_addr + 0x3000));
}
TEST(LoadedElfModule, GetSegmentByType) {
constexpr size_t kNumPhdrs = 1;
using FakeModule = FakeElf64Module<kNumPhdrs>;
FakeModule fake;
memset(&fake, 0, sizeof(fake));
// Set up 64-bit ELF header
memcpy(fake.ehdr.e_ident, ELFMAG, SELFMAG);
fake.ehdr.e_ident[EI_CLASS] = ELFCLASS64;
fake.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
fake.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
fake.ehdr.e_phoff = offsetof(FakeModule, phdr);
fake.ehdr.e_phnum = kNumPhdrs;
fake.ehdr.e_phentsize = sizeof(Elf64_Phdr);
fake.phdr[0].p_type = PT_NOTE;
fake.phdr[0].p_vaddr = 0x1000;
LocalMemory mem;
uint64_t load_addr = reinterpret_cast<uint64_t>(&fake);
Module module(load_addr, &mem, Module::AddressMode::kProcess);
LoadedElfModule loaded(module);
ASSERT_TRUE(loaded.Load().is_ok());
// Success case
auto res = loaded.GetSegmentByType(PT_NOTE);
ASSERT_TRUE(res.is_ok());
EXPECT_EQ(res->p_type, static_cast<uint32_t>(PT_NOTE));
EXPECT_EQ(res->p_vaddr, 0x1000u);
// Failure case
auto res_fail = loaded.GetSegmentByType(PT_DYNAMIC);
EXPECT_TRUE(res_fail.is_error());
}
TEST(LoadedElfModule, GetSegmentByType32) {
constexpr size_t kNumPhdrs = 1;
using FakeModule = FakeElf32Module<kNumPhdrs>;
FakeModule fake;
memset(&fake, 0, sizeof(fake));
// Set up 32-bit ELF header
memcpy(fake.ehdr.e_ident, ELFMAG, SELFMAG);
fake.ehdr.e_ident[EI_CLASS] = ELFCLASS32;
fake.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
fake.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
fake.ehdr.e_phoff = offsetof(FakeModule, phdr);
fake.ehdr.e_phnum = kNumPhdrs;
fake.ehdr.e_phentsize = sizeof(Elf32_Phdr);
fake.phdr[0].p_type = PT_NOTE;
fake.phdr[0].p_vaddr = 0x1000;
LocalMemory mem;
uint64_t load_addr = reinterpret_cast<uint64_t>(&fake);
Module module(load_addr, &mem, Module::AddressMode::kProcess);
LoadedElfModule loaded(module);
ASSERT_TRUE(loaded.Load().is_ok());
auto res = loaded.GetSegmentByType(PT_NOTE);
ASSERT_TRUE(res.is_ok());
EXPECT_EQ(res->p_type, static_cast<uint32_t>(PT_NOTE));
EXPECT_EQ(res->p_vaddr, 0x1000u);
}
TEST(LoadedElfModule, GetSegment) {
constexpr size_t kNumPhdrs = 3;
using FakeModule = FakeElf64Module<kNumPhdrs>;
FakeModule fake;
memset(&fake, 0, sizeof(fake));
// Set up 64-bit ELF header
memcpy(fake.ehdr.e_ident, ELFMAG, SELFMAG);
fake.ehdr.e_ident[EI_CLASS] = ELFCLASS64;
fake.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
fake.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
fake.ehdr.e_phoff = offsetof(FakeModule, phdr);
fake.ehdr.e_phnum = 3;
fake.ehdr.e_phentsize = sizeof(Elf64_Phdr);
fake.phdr[0].p_type = PT_LOAD;
fake.phdr[0].p_vaddr = 0x1000;
fake.phdr[1].p_type = PT_NOTE;
fake.phdr[1].p_vaddr = 0x2000;
fake.phdr[2].p_type = PT_DYNAMIC;
fake.phdr[2].p_vaddr = 0x3000;
LocalMemory mem;
uint64_t load_addr = reinterpret_cast<uint64_t>(&fake);
Module module(load_addr, &mem, Module::AddressMode::kProcess);
LoadedElfModule loaded(module);
ASSERT_TRUE(loaded.Load().is_ok());
auto res = loaded.GetSegmentByType(PT_NOTE);
ASSERT_TRUE(res.is_ok());
EXPECT_EQ(res->p_type, static_cast<uint32_t>(PT_NOTE));
EXPECT_EQ(res->p_vaddr, 0x2000u);
auto res2 = loaded.GetSegmentByType(PT_DYNAMIC);
ASSERT_TRUE(res2.is_ok());
EXPECT_EQ(res2->p_type, static_cast<uint32_t>(PT_DYNAMIC));
EXPECT_EQ(res2->p_vaddr, 0x3000u);
}
TEST(LoadedElfModule, GetSectionByName64) {
constexpr size_t kNumPhdrs = 1;
constexpr size_t kNumShdrs = 2;
using FakeModule = FakeElf64Module<kNumPhdrs, kNumShdrs>;
FakeModule fake;
memset(&fake, 0, sizeof(fake));
memcpy(fake.ehdr.e_ident, ELFMAG, SELFMAG);
fake.ehdr.e_ident[EI_CLASS] = ELFCLASS64;
fake.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
fake.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
fake.ehdr.e_shoff = offsetof(FakeModule, shdr);
fake.ehdr.e_shnum = kNumShdrs;
fake.ehdr.e_shentsize = sizeof(Elf64_Shdr);
fake.ehdr.e_shstrndx = 1;
fake.ehdr.e_phnum = kNumPhdrs;
fake.ehdr.e_phoff = offsetof(FakeModule, phdr);
fake.ehdr.e_phentsize = sizeof(Elf64_Phdr);
// Section 0: .debug_frame
// Section 1: .shstrtab
constexpr std::array<std::string, kNumShdrs> kSectionNames = {".debug_frame", ".shstrtab"};
fake.shdr[0].sh_type = SHT_PROGBITS;
fake.shdr[0].sh_offset = offsetof(FakeModule, shdr);
fake.shdr[0].sh_size = 0;
fake.shdr[1].sh_type = SHT_STRTAB;
fake.shdr[1].sh_offset = offsetof(FakeModule, strtab);
fake.shdr[1].sh_size = sizeof(fake.strtab);
fake.phdr[0].p_type = PT_LOAD;
fake.phdr[0].p_vaddr = 0x1000;
fake.phdr[0].p_memsz = 0x1000;
ASSERT_TRUE(PopulateShStrTab(fake, kSectionNames).is_ok());
LocalMemory mem;
uint64_t load_addr = reinterpret_cast<uint64_t>(&fake);
Module module(load_addr, &mem, Module::AddressMode::kProcess);
LoadedElfModule loaded(module);
// Loading is typically handled when fetching a LoadedElfModule object from the ElfModuleCache,
// but since we don't have that here, we have to load it ourselves.
ASSERT_TRUE(loaded.Load().is_ok());
auto res = loaded.GetSectionByName(".shstrtab");
ASSERT_TRUE(res.is_ok()) << res.error_value().msg();
EXPECT_EQ(res->sh_type, static_cast<uint32_t>(SHT_STRTAB));
res = loaded.GetSectionByName(".debug_frame");
ASSERT_TRUE(res.is_ok()) << res.error_value().msg();
EXPECT_EQ(res->sh_type, static_cast<uint32_t>(SHT_PROGBITS));
res = loaded.GetSectionByName(".nonexistent");
EXPECT_TRUE(res.is_error());
}
TEST(LoadedElfModule, GetSectionByName64NoPhdrs) {
constexpr size_t kNumPhdrs = 0;
constexpr size_t kNumShdrs = 2;
using FakeModule = FakeElf64Module<kNumPhdrs, kNumShdrs>;
FakeModule fake;
memset(&fake, 0, sizeof(fake));
memcpy(fake.ehdr.e_ident, ELFMAG, SELFMAG);
fake.ehdr.e_ident[EI_CLASS] = ELFCLASS64;
fake.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
fake.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
fake.ehdr.e_shoff = offsetof(FakeModule, shdr);
fake.ehdr.e_shnum = kNumShdrs;
fake.ehdr.e_shentsize = sizeof(Elf64_Shdr);
fake.ehdr.e_shstrndx = 1;
fake.ehdr.e_phnum = kNumPhdrs;
// Section 0: .debug_frame
// Section 1: .shstrtab
constexpr std::array<std::string, kNumShdrs> kSectionNames = {".debug_frame", ".shstrtab"};
fake.shdr[0].sh_type = SHT_PROGBITS;
fake.shdr[0].sh_offset = offsetof(FakeModule, shdr);
fake.shdr[0].sh_size = 0;
fake.shdr[1].sh_type = SHT_STRTAB;
fake.shdr[1].sh_offset = offsetof(FakeModule, strtab);
fake.shdr[1].sh_size = sizeof(fake.strtab);
ASSERT_TRUE(PopulateShStrTab(fake, kSectionNames).is_ok());
LocalMemory mem;
uint64_t load_addr = reinterpret_cast<uint64_t>(&fake);
Module module(load_addr, &mem, Module::AddressMode::kProcess);
LoadedElfModule loaded(module);
// Loading is typically handled when fetching a LoadedElfModule object from the ElfModuleCache,
// but since we don't have that here, we have to load it ourselves.
ASSERT_TRUE(loaded.Load().is_ok());
auto res = loaded.GetSectionByName(".shstrtab");
ASSERT_TRUE(res.is_ok()) << res.error_value().msg();
EXPECT_EQ(res->sh_type, static_cast<uint32_t>(SHT_STRTAB));
res = loaded.GetSectionByName(".debug_frame");
ASSERT_TRUE(res.is_ok()) << res.error_value().msg();
EXPECT_EQ(res->sh_type, static_cast<uint32_t>(SHT_PROGBITS));
res = loaded.GetSectionByName(".nonexistent");
EXPECT_TRUE(res.is_error());
}
TEST(LoadedElfModule, GetSectionByName32) {
constexpr size_t kNumPhdrs = 1;
constexpr size_t kNumShdrs = 2;
using FakeModule = FakeElf32Module<kNumPhdrs, kNumShdrs>;
FakeModule fake;
memset(&fake, 0, sizeof(fake));
memcpy(fake.ehdr.e_ident, ELFMAG, SELFMAG);
fake.ehdr.e_ident[EI_CLASS] = ELFCLASS32;
fake.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
fake.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
fake.ehdr.e_shoff = offsetof(FakeModule, shdr);
fake.ehdr.e_shnum = kNumShdrs;
fake.ehdr.e_shentsize = sizeof(Elf32_Shdr);
fake.ehdr.e_shstrndx = 1;
fake.ehdr.e_phnum = kNumPhdrs;
fake.ehdr.e_phoff = offsetof(FakeModule, phdr);
fake.ehdr.e_phentsize = sizeof(Elf32_Phdr);
fake.phdr[0].p_type = PT_LOAD;
fake.phdr[0].p_vaddr = 0x1000;
fake.phdr[0].p_memsz = 0x1000;
// Section 0: .debug_frame
// Section 1: .shstrtab
constexpr std::array<std::string, kNumShdrs> kSectionNames = {".debug_frame", ".shstrtab"};
fake.shdr[0].sh_type = SHT_PROGBITS;
fake.shdr[0].sh_offset = offsetof(FakeModule, shdr);
fake.shdr[0].sh_size = 0;
fake.shdr[1].sh_type = SHT_STRTAB;
fake.shdr[1].sh_offset = offsetof(FakeModule, strtab);
fake.shdr[1].sh_size = sizeof(fake.strtab);
ASSERT_TRUE(PopulateShStrTab(fake, kSectionNames).is_ok());
LocalMemory mem;
uint64_t load_addr = reinterpret_cast<uint64_t>(&fake);
Module module(load_addr, &mem, Module::AddressMode::kProcess);
LoadedElfModule loaded(module);
ASSERT_TRUE(loaded.Load().is_ok());
auto res = loaded.GetSectionByName(".shstrtab");
ASSERT_TRUE(res.is_ok()) << res.error_value().msg();
EXPECT_EQ(res->sh_type, static_cast<uint32_t>(SHT_STRTAB));
res = loaded.GetSectionByName(".debug_frame");
ASSERT_TRUE(res.is_ok()) << res.error_value().msg();
EXPECT_EQ(res->sh_type, static_cast<uint32_t>(SHT_PROGBITS));
res = loaded.GetSectionByName(".nonexistent");
ASSERT_TRUE(res.is_error());
}
TEST(LoadedElfModule, InvalidHeader) {
constexpr size_t kNumPhdrs = 1;
using FakeModule = FakeElf64Module<kNumPhdrs>;
FakeModule fake;
memset(&fake, 0, sizeof(fake));
// Wrong magic
memcpy(fake.ehdr.e_ident, "NOTELF", 6);
LocalMemory mem;
uint64_t load_addr = reinterpret_cast<uint64_t>(&fake);
// Module constructor might fail or ProbeElfModuleClass returns kUnknown
Module module(load_addr, &mem, Module::AddressMode::kProcess);
LoadedElfModule loaded(module);
auto result = loaded.Load();
EXPECT_TRUE(result.is_error());
}
} // namespace
} // namespace unwinder