blob: 508cda54f1bbd5e73779ec971158163177a72951 [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.
#ifndef GARNET_LIB_ELFLIB_ELFLIB_H_
#define GARNET_LIB_ELFLIB_ELFLIB_H_
#include <fbl/macros.h>
#include <stdio.h>
#include <map>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
#include "garnet/third_party/llvm/include/llvm/BinaryFormat/ELF.h"
namespace elflib {
using namespace llvm::ELF;
class ElfLib {
public:
class MemoryAccessor;
// Essentially just a pointer with a bound.
struct MemoryRegion {
const uint8_t* ptr;
size_t size;
};
// How do we expect the ELF structures to be mapped? Are they packed in a
// file or mapped as they would be in a running process?
enum class AddressMode { kFile, kProcess };
// Whether we should take ownership of the FILE handle given to our Create
// method.
enum class Ownership { kTakeOwnership, kDontTakeOwnership };
// Do not use. See Create.
explicit ElfLib(std::unique_ptr<MemoryAccessor>&& memory,
AddressMode address_mode);
virtual ~ElfLib();
// Attach a second ElfLib to this one which contains debug info. This second
// object will be treated as authoritative on section headers.
//
// Returns true on success. Returns false if either this or the given debug
// data already have debug data associated, or if this has sections
// associated already.
bool SetDebugData(std::unique_ptr<ElfLib> debug);
// Get the contents of a section by its name. Return nullptr if there is no
// section by that name.
MemoryRegion GetSectionData(const std::string& name);
// Get a list of all segement headers.
const std::vector<Elf64_Phdr>& GetSegmentHeaders();
// Get the contents of a segment by its index. Return nullptr if the index is
// invalid.
MemoryRegion GetSegmentData(size_t segment);
// Get a note from the notes section.
std::optional<std::vector<uint8_t>> GetNote(const std::string& name,
uint64_t type);
// Get the NT_GNU_BUILD_ID note as a hex string. Return empty string if we
// don't have that note.
std::string GetGNUBuildID();
// Get a symbol from the symbol table. Return nullptr if there is no such
// symbol. Pointer should live as long as the memory accessor.
const Elf64_Sym* GetSymbol(const std::string& name);
// Get a map of all symbols and their string names. Returns nullopt if the
// symbols could not be loaded.
std::optional<std::map<std::string, Elf64_Sym>> GetAllSymbols();
// Get a symbol from the symbol table. Return nullptr if there is no such
// symbol. Pointer should live as long as the memory accessor.
const Elf64_Sym* GetDynamicSymbol(const std::string& name);
// Get a map of all symbols and their string names. Returns nullopt if the
// symbols could not be loaded.
std::optional<std::map<std::string, Elf64_Sym>> GetAllDynamicSymbols();
// Create a new ElfLib object for reading a file. If take_ownership is set to
// true, the given handle will be closed when the ElfLib object is destroyed.
static std::unique_ptr<ElfLib> Create(FILE* fp, Ownership owned);
// Create a new ElfLib object for reading a file. ElfLib will attempt to open
// the file and retain a handle to it until the object is destroyed.
static std::unique_ptr<ElfLib> Create(const std::string& path);
// Create a new ElfLib object for accessing an ELF file mapped into memory.
// This is expected to be a file, not an address space, and will be addressed
// accordingly.
static std::unique_ptr<ElfLib> Create(const uint8_t* mem, size_t size);
// Create an ElfLib object for reading ELF structures via a read callback.
// The offsets will assume either an ELF file or an ELF mapped address space
// depending on the value of the address_mode argument.
static std::unique_ptr<ElfLib> Create(
std::function<bool(uint64_t, std::vector<uint8_t>*)> fetch,
AddressMode address_mode = AddressMode::kProcess);
// Returns a map from symbol names to the locations of their PLT entries.
// Returns an empty map if the data is inaccessible.
//
// Getting this information is architecture-specific and involves reading and
// decoding the actual jump table instructions in the .plt section. Once
// we've done that decoding we can quickly get relocation indices and then
// symbol table mappings.
std::map<std::string, uint64_t> GetPLTOffsets();
// ElfLib may notice inconsistencies as it parses the ELF file or address
// space, but may be able to continue. In such cases it will log a warning
// message internally. This method will retrieve those messages and clear
// them from the internal list.
std::vector<std::string> GetAndClearWarnings() {
auto ret = std::move(warnings_);
warnings_.clear();
return ret;
}
private:
// Add a warning to this instance. See GetAndClearWarnings.
void Warn(const std::string&& m) { warnings_.push_back(m); }
// Location of a section specified by data gleaned from the dynamic segment.
struct DynamicSection {
std::optional<uint64_t> offset;
std::optional<size_t> size;
bool IsValid() { return offset && size; }
};
// Create a new ElfLib object.
static std::unique_ptr<ElfLib> Create(
std::unique_ptr<MemoryAccessor>&& memory, AddressMode address_mode);
// x64-specific implementation of GetPLTOffsets
std::map<std::string, uint64_t> GetPLTOffsetsX64();
// Get the header for a section by its index. Return nullptr if the index is
// invalid.
const Elf64_Shdr* GetSectionHeader(size_t section);
// Load the program header table into the cache in segments_. Return true
// unless a read error occurred.
bool LoadProgramHeaders();
// Load the section name-to-index mappings and cache them in section_names_.
bool LoadSectionNames();
// Get the contents of a section by its index. Return nullptr if the index is
// invalid.
MemoryRegion GetSectionData(size_t section);
const AddressMode address_mode_;
// Get the contents of the symbol table. Return nullptr if it is not present
// or we do not have the means to locate it. Size is number of structs, not
// number of bytes.
std::pair<const Elf64_Sym*, size_t> GetSymtab();
// Get the contents of the dynamic symbol table. Return nullptr if it is not
// present or we do not have the means to locate it. Size is number of
// structs, not number of bytes.
std::pair<const Elf64_Sym*, size_t> GetDynamicSymtab();
// Get a string from the .strtab section. Return nullptr if the offset is
// invalid.
std::optional<std::string> GetString(size_t offset);
// Get a string from the .dynstr section. Return nullptr if the offset is
// invalid.
std::optional<std::string> GetDynamicString(size_t offset);
// Load symbols from the dynamic segment of the target. We only do this when
// the section data isn't available and we can't use the regular .symtab
// information. Returns true unless an error occurred.
bool LoadDynamicSymbols();
bool did_load_dynamic_symbols_ = false;
std::unique_ptr<MemoryAccessor> memory_;
Elf64_Ehdr header_;
std::optional<bool> dynamic_plt_use_rela_;
DynamicSection dynsym_;
DynamicSection dynstr_;
std::vector<Elf64_Shdr> sections_;
std::vector<Elf64_Phdr> segments_;
std::map<std::string, size_t> section_names_;
std::unique_ptr<ElfLib> debug_;
std::vector<std::string> warnings_;
DISALLOW_COPY_ASSIGN_AND_MOVE(ElfLib);
};
} // namespace elflib
#endif // GARNET_LIB_ELFLIB_ELFLIB_H_