blob: 66877802f028d92837ee14d3871a233729033864 [file] [log] [blame]
// Copyright 2018 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 "garnet/lib/elflib/elflib.h"
#include <limits.h>
#include <stdio.h>
#include <unistd.h>
#if defined(__APPLE__)
#include <mach-o/dyld.h>
#endif
#include <algorithm>
#include <iterator>
#include "gtest/gtest.h"
namespace elflib {
namespace {
constexpr uint64_t kAddrPoison = 0xdeadb33ff00db4b3;
constexpr uint64_t kSymbolPoison = 0xb0bab0ba;
constexpr uint64_t kNoteGnuBuildId = 3;
constexpr uint64_t kMeaninglessNoteType = 42;
std::string GetSelfPath() {
std::string result;
#if defined(__APPLE__)
// Executable path can have relative references ("..") depending on how the
// app was launched.
uint32_t length = 0;
_NSGetExecutablePath(nullptr, &length);
result.resize(length);
_NSGetExecutablePath(&result[0], &length);
result.resize(length - 1); // Length included terminator.
#elif defined(__linux__)
// The realpath() call below will resolve the symbolic link.
result.assign("/proc/self/exe");
#else
#error Write this for your platform.
#endif
char fullpath[PATH_MAX];
return std::string(realpath(result.c_str(), fullpath));
}
inline std::string GetTestFilePath(const std::string& rel_path) {
std::string path = GetSelfPath();
size_t last_slash = path.rfind('/');
if (last_slash == std::string::npos) {
path = "./"; // Just hope the current directory works.
} else {
path.resize(last_slash + 1);
}
return path + rel_path;
}
// The test files will be copied over to this specific location at build time.
const char kRelativeTestDataPath[] = "test_data/elflib/";
class StrippedExampleAccessor : public ElfLib::MemoryAccessor {
public:
StrippedExampleAccessor() {
file_ =
fopen((GetTestFilePath(kRelativeTestDataPath) + "stripped_example.elf")
.c_str(),
"r");
if (!file_) {
abort();
}
}
std::optional<std::vector<uint8_t>> GetMemory(uint64_t offset,
size_t size) override {
std::vector<uint8_t> ret;
ret.resize(size);
fseek(file_, offset, SEEK_SET);
if (fread(ret.data(), size, 1, file_) != 1) {
return std::nullopt;
}
return ret;
}
~StrippedExampleAccessor() {
if (file_) {
fclose(file_);
}
}
private:
FILE* file_ = nullptr;
};
class TestMemoryAccessor : public ElfLib::MemoryAccessor {
public:
TestMemoryAccessor() {
PushData(Elf64_Ehdr{
.e_ident = {EI_MAG0, EI_MAG1, EI_MAG2, EI_MAG3},
.e_version = 1,
.e_shoff = sizeof(Elf64_Ehdr),
.e_ehsize = sizeof(Elf64_Ehdr),
.e_shentsize = sizeof(Elf64_Shdr),
.e_phentsize = sizeof(Elf64_Phdr),
.e_shnum = 4,
.e_phnum = 2,
.e_shstrndx = 0,
});
size_t shstrtab_hdr = PushData(Elf64_Shdr{
.sh_name = 1,
.sh_type = SHT_STRTAB,
.sh_size = 34,
.sh_addr = kAddrPoison,
});
size_t stuff_hdr = PushData(Elf64_Shdr{
.sh_name = 11,
.sh_type = SHT_LOUSER,
.sh_size = 15,
.sh_addr = kAddrPoison,
});
size_t strtab_hdr = PushData(Elf64_Shdr{
.sh_name = 18,
.sh_type = SHT_STRTAB,
.sh_size = 16,
.sh_addr = kAddrPoison,
});
size_t symtab_hdr = PushData(Elf64_Shdr{
.sh_name = 26,
.sh_type = SHT_SYMTAB,
.sh_size = sizeof(Elf64_Sym),
.sh_addr = kAddrPoison,
});
size_t phnote_hdr = PushData(Elf64_Phdr{
.p_type = PT_NOTE,
.p_vaddr = kAddrPoison,
});
DataAt<Elf64_Ehdr>(0)->e_phoff = phnote_hdr;
DataAt<Elf64_Shdr>(shstrtab_hdr)->sh_offset =
PushData("\0.shstrtab\0.stuff\0.strtab\0.symtab\0", 34);
DataAt<Elf64_Shdr>(stuff_hdr)->sh_offset = PushData("This is a test.", 15);
DataAt<Elf64_Shdr>(strtab_hdr)->sh_offset =
PushData("\0zx_frob_handle\0", 16);
DataAt<Elf64_Shdr>(symtab_hdr)->sh_offset = PushData(Elf64_Sym{
.st_name = 1,
.st_shndx = SHN_COMMON,
.st_value = kSymbolPoison,
.st_size = 0,
});
size_t buildid_nhdr = PushData(
Elf64_Nhdr{.n_namesz = 4, .n_descsz = 32, .n_type = kNoteGnuBuildId});
DataAt<Elf64_Phdr>(phnote_hdr)->p_offset = buildid_nhdr;
PushData("GNU\0", 4);
uint8_t desc_data[32] = {
0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7,
0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7,
};
PushData(desc_data, 32);
PushData(Elf64_Nhdr{
.n_namesz = 6,
.n_descsz = 3,
.n_type = kMeaninglessNoteType,
});
PushData("seven\0\0\0", 8);
PushData("foo\0", 4);
DataAt<Elf64_Phdr>(phnote_hdr)->p_filesz = Pos() - buildid_nhdr;
DataAt<Elf64_Phdr>(phnote_hdr)->p_memsz = Pos() - buildid_nhdr;
}
template <typename T>
T* DataAt(size_t offset) {
return reinterpret_cast<T*>(content_.data() + offset);
}
template <typename T>
size_t PushData(T data) {
return PushData(reinterpret_cast<uint8_t*>(&data), sizeof(data));
}
size_t PushData(const char* bytes, size_t size) {
return PushData(reinterpret_cast<const uint8_t*>(bytes), size);
}
size_t PushData(const uint8_t* bytes, size_t size) {
size_t offset = Pos();
std::copy(bytes, bytes + size, std::back_inserter(content_));
return offset;
}
size_t Pos() { return content_.size(); }
std::optional<std::vector<uint8_t>> GetMemory(uint64_t offset, size_t size) {
const auto& start_iter = content_.begin() + offset;
std::vector<uint8_t> out;
out.clear();
out.resize(size);
if (content_.size() < offset + out.size()) {
return std::nullopt;
}
std::copy(start_iter, start_iter + out.size(), out.begin());
return out;
}
std::optional<std::vector<uint8_t>> GetMappedMemory(uint64_t offset,
uint64_t address,
size_t size,
size_t map_size) {
if (address != kAddrPoison) {
return std::nullopt;
}
return GetMemory(offset, size);
}
private:
std::vector<uint8_t> content_;
};
} // namespace
TEST(ElfLib, Create) {
std::unique_ptr<ElfLib> got;
EXPECT_NE(ElfLib::Create(std::make_unique<TestMemoryAccessor>()).get(),
nullptr);
}
TEST(ElfLib, GetSection) {
std::unique_ptr<ElfLib> elf =
ElfLib::Create(std::make_unique<TestMemoryAccessor>());
ASSERT_NE(elf.get(), nullptr);
auto data = elf->GetSectionData(".stuff");
const uint8_t* expected_content =
reinterpret_cast<const uint8_t*>("This is a test.");
auto test = std::vector<uint8_t>(expected_content, expected_content + 15);
ASSERT_NE(data, nullptr);
EXPECT_EQ(test, *data);
}
TEST(ElfLib, GetSymbolValue) {
std::unique_ptr<ElfLib> elf =
ElfLib::Create(std::make_unique<TestMemoryAccessor>());
ASSERT_NE(elf.get(), nullptr);
auto data = elf->GetSymbolValue("zx_frob_handle");
ASSERT_TRUE(data);
EXPECT_EQ(kSymbolPoison, *data);
}
TEST(ElfLib, GetAllSymbols) {
std::unique_ptr<ElfLib> elf =
ElfLib::Create(std::make_unique<TestMemoryAccessor>());
ASSERT_NE(elf.get(), nullptr);
auto syms = elf->GetAllSymbols();
ASSERT_TRUE(syms);
EXPECT_EQ(1U, syms->size());
Elf64_Sym sym = (*syms)["zx_frob_handle"];
EXPECT_EQ(1U, sym.st_name);
EXPECT_EQ(0U, sym.st_size);
EXPECT_EQ(SHN_COMMON, sym.st_shndx);
EXPECT_EQ(kSymbolPoison, sym.st_value);
}
TEST(ElfLib, GetNote) {
std::unique_ptr<ElfLib> elf =
ElfLib::Create(std::make_unique<TestMemoryAccessor>());
ASSERT_NE(elf.get(), nullptr);
auto got = elf->GetNote("GNU", kNoteGnuBuildId);
EXPECT_TRUE(got);
auto data = *got;
EXPECT_EQ(32U, data.size());
for (size_t i = 0; i < 32; i++) {
EXPECT_EQ(i % 8, data[i]);
}
}
TEST(ElfLib, GetIrregularNote) {
std::unique_ptr<ElfLib> elf =
ElfLib::Create(std::make_unique<TestMemoryAccessor>());
ASSERT_NE(elf.get(), nullptr);
auto got = elf->GetNote("seven", kMeaninglessNoteType);
EXPECT_TRUE(got);
auto data = *got;
EXPECT_EQ(3U, data.size());
EXPECT_EQ("foo", std::string(data.data(), data.data() + 3));
}
TEST(ElfLib, GetSymbolsFromStripped) {
std::unique_ptr<ElfLib> elf =
ElfLib::Create(std::make_unique<StrippedExampleAccessor>());
ASSERT_NE(elf.get(), nullptr);
auto syms = elf->GetAllSymbols();
ASSERT_TRUE(syms);
EXPECT_EQ(8U, syms->size());
std::map<std::string, Elf64_Sym>::iterator it;
it = syms->find("");
EXPECT_NE(it, syms->end());
it = syms->find("__bss_start");
EXPECT_NE(it, syms->end());
it = syms->find("__libc_start_main");
EXPECT_NE(it, syms->end());
it = syms->find("__scudo_default_options");
EXPECT_NE(it, syms->end());
it = syms->find("_edata");
EXPECT_NE(it, syms->end());
it = syms->find("_end");
EXPECT_NE(it, syms->end());
it = syms->find("printf");
EXPECT_NE(it, syms->end());
it = syms->find("strlen");
EXPECT_NE(it, syms->end());
}
} // namespace elflib