blob: 52982059d16a47278afd18c430db692babf4e9fb [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 <string>
#include "garnet/lib/elflib/elflib.h"
namespace elflib {
namespace {
// Load the standard ELF string table format
void LoadStringTable(const std::vector<uint8_t>& content,
std::vector<std::string>* strings) {
const uint8_t* data = content.data();
const uint8_t* end = data + content.size();
while (data != end) {
strings->emplace_back(reinterpret_cast<const char*>(data));
data += strings->back().size() + 1;
}
}
} // namespace
ElfLib::ElfLib(std::unique_ptr<MemoryAccessor>&& memory)
: memory_(std::move(memory)) {}
ElfLib::~ElfLib() = default;
std::unique_ptr<ElfLib> ElfLib::Create(
std::unique_ptr<MemoryAccessor>&& memory) {
std::unique_ptr<ElfLib> out = std::make_unique<ElfLib>(std::move(memory));
std::vector<uint8_t> header_data;
header_data.resize(sizeof(Elf64_Ehdr));
if (!out->memory_->GetMemory(0, &header_data)) {
return std::unique_ptr<ElfLib>();
}
out->header_ = *reinterpret_cast<Elf64_Ehdr*>(header_data.data());
// We don't support non-standard section header sizes.
if (out->header_.e_shentsize != sizeof(Elf64_Shdr)) {
return std::unique_ptr<ElfLib>();
}
return out;
}
const Elf64_Shdr* ElfLib::GetSectionHeader(size_t section) {
if (sections_.empty()) {
std::vector<uint8_t> data;
data.resize(sizeof(Elf64_Shdr) * header_.e_shnum);
if (!memory_->GetMemory(header_.e_shoff, &data)) {
return nullptr;
}
Elf64_Shdr* header_array = reinterpret_cast<Elf64_Shdr*>(data.data());
std::copy(header_array, header_array + header_.e_shnum,
std::back_inserter(sections_));
}
if (section >= sections_.size()) {
return nullptr;
}
return &sections_[section];
}
const std::vector<uint8_t>* ElfLib::GetSectionData(size_t section) {
const auto& iter = section_data_.find(section);
if (iter != section_data_.end()) {
return &iter->second;
}
const Elf64_Shdr* header = GetSectionHeader(section);
if (!header) {
return nullptr;
}
size_t count = header->sh_size;
std::vector<uint8_t> data;
data.resize(count);
if (!memory_->GetMappedMemory(header->sh_offset, header->sh_addr, &data)) {
return nullptr;
}
section_data_[section] = data;
return &section_data_[section];
}
const std::vector<uint8_t>* ElfLib::GetSectionData(const std::string& name) {
if (section_names_.size() == 0) {
const std::vector<uint8_t>* section_name_data =
GetSectionData(header_.e_shstrndx);
if (!section_name_data) {
return nullptr;
}
std::vector<std::string> strings;
LoadStringTable(*section_name_data, &strings);
size_t idx = 0;
// We know sections_ is populated from the GetSectionData above
for (const auto& section : sections_) {
if (section.sh_name < strings.size()) {
section_names_[strings[section.sh_name]] = idx;
}
idx++;
}
}
const auto& iter = section_names_.find(name);
if (iter == section_names_.end()) {
return nullptr;
}
return GetSectionData(iter->second);
}
const std::string* ElfLib::GetString(size_t index) {
if (strings_.empty()) {
const std::vector<uint8_t>* string_data = GetSectionData(".strtab");
if (!string_data) {
return nullptr;
}
LoadStringTable(*string_data, &strings_);
}
if (index >= strings_.size()) {
return nullptr;
}
return &strings_[index];
}
const Elf64_Sym* ElfLib::GetSymbol(const std::string& name) {
if (symbols_.empty()) {
const std::vector<uint8_t>* symbol_data = GetSectionData(".symtab");
if (!symbol_data) {
return nullptr;
}
const Elf64_Sym* start =
reinterpret_cast<const Elf64_Sym*>(symbol_data->data());
const Elf64_Sym* end = start + (symbol_data->size() / sizeof(Elf64_Sym));
std::copy(start, end, std::back_inserter(symbols_));
}
for (const auto& symbol : symbols_) {
const std::string* got_name = GetString(symbol.st_name);
if (got_name != nullptr && *got_name == name) {
return &symbol;
}
}
return nullptr;
}
bool ElfLib::GetSymbolValue(const std::string& name, uint64_t* out) {
const Elf64_Sym* sym = GetSymbol(name);
if (sym) {
*out = sym->st_value;
}
return sym != nullptr;
}
} // namespace elflib