blob: 6799ed15305ef133bfdea21c0feb27740aca2d65 [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));
auto header_data = out->memory_->GetMemory(0, sizeof(Elf64_Ehdr));
if (!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. Stripped binaries that
// don't have sections sometimes zero out the shentsize, so we can ignore it
// if we have no sections.
if (out->header_.e_shnum > 0 &&
out->header_.e_shentsize != sizeof(Elf64_Shdr)) {
return std::unique_ptr<ElfLib>();
}
// We don't support non-standard program header sizes.
if (out->header_.e_phentsize != sizeof(Elf64_Phdr)) {
return std::unique_ptr<ElfLib>();
}
return out;
}
const Elf64_Shdr* ElfLib::GetSectionHeader(size_t section) {
if (sections_.empty()) {
auto data = memory_->GetMemory(header_.e_shoff,
sizeof(Elf64_Shdr) * header_.e_shnum);
if (!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];
}
bool ElfLib::LoadProgramHeaders() {
if (!segments_.empty()) {
return true;
}
auto data =
memory_->GetMemory(header_.e_phoff, sizeof(Elf64_Phdr) * header_.e_phnum);
if (!data) {
return false;
}
Elf64_Phdr* header_array = reinterpret_cast<Elf64_Phdr*>(data->data());
std::copy(header_array, header_array + header_.e_phnum,
std::back_inserter(segments_));
return true;
}
const std::vector<uint8_t>* ElfLib::GetSegmentData(size_t segment) {
const auto& iter = segment_data_.find(segment);
if (iter != segment_data_.end()) {
return &iter->second;
}
LoadProgramHeaders();
if (segment > segments_.size()) {
return nullptr;
}
const Elf64_Phdr* header = &segments_[segment];
auto data = memory_->GetMappedMemory(header->p_offset, header->p_vaddr,
header->p_filesz, header->p_memsz);
if (!data) {
return nullptr;
}
segment_data_[segment] = *data;
return &segment_data_[segment];
}
const std::optional<std::vector<uint8_t>> ElfLib::GetNote(
const std::string& name, uint64_t type) {
LoadProgramHeaders();
for (size_t idx = 0; idx < segments_.size(); idx++) {
if (segments_[idx].p_type != PT_NOTE) {
continue;
}
auto data = GetSegmentData(idx);
const Elf64_Nhdr* header;
size_t namesz_padded;
size_t descsz_padded;
for (const uint8_t* pos = data->data(); pos < data->data() + data->size();
pos += sizeof(Elf64_Nhdr) + namesz_padded + descsz_padded) {
header = reinterpret_cast<const Elf64_Nhdr*>(pos);
namesz_padded = (header->n_namesz + 3) & ~3UL;
descsz_padded = (header->n_descsz + 3) & ~3UL;
if (header->n_type != type) {
continue;
}
auto name_data = pos + sizeof(Elf64_Nhdr);
std::string entry_name(reinterpret_cast<const char*>(name_data),
header->n_namesz - 1);
if (entry_name == name) {
auto desc_data = name_data + namesz_padded;
return std::vector(desc_data, desc_data + header->n_descsz);
}
}
}
return std::nullopt;
}
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;
}
auto data = memory_->GetMappedMemory(header->sh_offset, header->sh_addr,
header->sh_size, header->sh_size);
if (!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];
}
bool ElfLib::LoadSymbols() {
if (symbols_.empty()) {
const std::vector<uint8_t>* symbol_data = GetSectionData(".symtab");
if (!symbol_data) {
return false;
}
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_));
}
return true;
}
const Elf64_Sym* ElfLib::GetSymbol(const std::string& name) {
if (!LoadSymbols()) {
return nullptr;
}
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;
}
std::optional<std::map<std::string, Elf64_Sym>> ElfLib::GetAllSymbols() {
if (!LoadSymbols()) {
return std::nullopt;
}
std::map<std::string, Elf64_Sym> out;
for (const auto& symbol : symbols_) {
const std::string* got_name = GetString(symbol.st_name);
if (got_name != nullptr) {
out[*got_name] = symbol;
}
}
return out;
}
std::optional<uint64_t> ElfLib::GetSymbolValue(const std::string& name) {
const Elf64_Sym* sym = GetSymbol(name);
if (sym) {
return sym->st_value;
}
return std::nullopt;
}
} // namespace elflib