Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add support for static relocation writing.
  • Loading branch information
pbrunet authored and romainthomas committed Jul 23, 2018
1 parent 2dde1af commit d1b98d6
Show file tree
Hide file tree
Showing 6 changed files with 385 additions and 30 deletions.
3 changes: 3 additions & 0 deletions include/LIEF/ELF/Builder.hpp
Expand Up @@ -75,6 +75,9 @@ class LIEF_API Builder {
template<typename ELF_T>
void build_pltgot_relocations(void);

template<typename ELF_T>
void build_section_relocations(void);

template<typename ELF_T>
void build_hash_table(void);

Expand Down
2 changes: 1 addition & 1 deletion include/LIEF/ELF/Parser.hpp
Expand Up @@ -189,7 +189,7 @@ class LIEF_API Parser : public LIEF::Parser {
//! use parse relocations by using LIEF::ELF::Segment. This method parse relocations
//! that are not reachable through segments (For example Object file).
template<typename ELF_T, typename REL_T>
void parse_section_relocations(uint64_t offset, uint64_t size, Section *applies_to = nullptr);
void parse_section_relocations(Section const& section);

//! @brief Parse SymbolVersionRequirement
//!
Expand Down
198 changes: 181 additions & 17 deletions src/ELF/Builder.tcc
Expand Up @@ -23,6 +23,8 @@

#include "Object.tcc"

#include <cassert>

namespace LIEF {
namespace ELF {

Expand Down Expand Up @@ -117,6 +119,13 @@ void Builder::build(void) {
}
}

if (this->binary_->object_relocations().size() > 0) {
try {
this->build_section_relocations<ELF_T>();
} catch (const LIEF::exception& e) {
LOG(ERROR) << e.what();
}
}

// Build sections
if (this->binary_->sections_.size() > 0) {
Expand Down Expand Up @@ -223,7 +232,8 @@ void Builder::build_sections(void) {
using Elf_Shdr = typename ELF_T::Elf_Shdr;
VLOG(VDEBUG) << "[+] Build sections";

const Header& header = this->binary_->header();
// FIXME: Keep it global const and local non const
Header& header = this->binary_->header();
const Elf_Off section_headers_offset = header.section_headers_offset();

std::vector<std::string> stringTableOpti =
Expand All @@ -238,27 +248,55 @@ void Builder::build_sections(void) {
}

Section* string_names_section = this->binary_->sections_[header.section_name_table_idx()];
string_names_section->content(section_names);

// **Should** be safe since .shstr is located at the end of the binary
//if (string_names_section->size() < section_names.size()) {
// string_names_section = &(this->binary_->extend_section(*string_names_section, section_names.size() - string_names_section->size() + 1));
//}
auto&& it_symtab_section = std::find_if(
std::begin(this->binary_->sections_),
std::end(this->binary_->sections_),
[] (const Section* section)
{
return section != nullptr and section->type() == ELF_SECTION_TYPES::SHT_SYMTAB;
});

for (size_t i = 0; i < this->binary_->sections_.size(); i++) {
const Section* section = this->binary_->sections_[i];
VLOG(VDEBUG) << "Writing back '" << section->name() << "'";
// If there is already a symtab section with a str_section that is the same
// as the str_section of sections, create a new one for str_section of sections
if (it_symtab_section != std::end(this->binary_->sections_)) {
Section& symbol_section = **it_symtab_section;
Section* symbol_str_section = nullptr;
if (symbol_section.link() != 0 or
symbol_section.link() < this->binary_->sections_.size()) {
symbol_str_section = this->binary_->sections_[symbol_section.link()];
}

auto&& it_offset_name = std::search(
std::begin(section_names),
std::end(section_names),
section->name().c_str(),
section->name().c_str() + section->name().size() + 1);
if(symbol_str_section == string_names_section)
{
Section sec_str_section(".shstrtab", ELF_SECTION_TYPES::SHT_STRTAB);
sec_str_section.content(section_names);

if (it_offset_name == std::end(section_names)) {
throw LIEF::not_found(""); // TODO: msg
auto& new_str_section = this->binary_->add(sec_str_section, false);

auto it = std::find_if(std::begin(this->binary_->sections_),
std::end(this->binary_->sections_),
[&new_str_section](Section* S) {
return S == &new_str_section;
});
assert(it != std::end(this->binary_->sections_));

// FIXME: We should remove the old section
header.section_name_table_idx(std::distance(std::begin(this->binary_->sections_), it));

return this->build<ELF_T>();
}
}

// FIXME: Handle if we add sections names and we shoudl increase section size
string_names_section->content(section_names);

// First write every section and then the header because if we do all of it
// in a row, we will write the old header section after some new header so they
// will be remove
for (size_t i = 0; i < this->binary_->sections_.size(); i++) {
const Section* section = this->binary_->sections_[i];
VLOG(VDEBUG) << "Writing back '" << section->name() << "'";

// Write Section's content
if (section->size() > 0) {
Expand All @@ -278,7 +316,7 @@ void Builder::build_sections(void) {
section->name().c_str() + section->name().size() + 1);

if (it_offset_name == std::end(section_names)) {
throw LIEF::not_found(""); // TODO: msg
throw LIEF::not_found("Section name not found"); // TODO: msg
}

const Elf_Off offset_name = static_cast<Elf_Off>(std::distance(std::begin(section_names), it_offset_name));
Expand Down Expand Up @@ -438,6 +476,7 @@ void Builder::build_static_symbols(void) {
content.write_conv<Elf_Sym>(sym_hdr);
}

// FIXME: Handle increase of size in symbol_str_section
symbol_str_section.content(std::move(string_table));
symbol_section.content(std::move(content.raw()));

Expand Down Expand Up @@ -1098,6 +1137,131 @@ void Builder::build_dynamic_symbols(void) {

}

template<typename ELF_T>
void Builder::build_section_relocations(void) {
using Elf_Addr = typename ELF_T::Elf_Addr;
using Elf_Xword = typename ELF_T::Elf_Xword;
using Elf_Sxword = typename ELF_T::Elf_Sxword;

using Elf_Rela = typename ELF_T::Elf_Rela;
using Elf_Rel = typename ELF_T::Elf_Rel;
VLOG(VDEBUG) << "[+] Building object relocations";

it_object_relocations object_relocations = this->binary_->object_relocations();

bool isRela = object_relocations[0].is_rela();
if (not std::all_of(
std::begin(object_relocations),
std::end(object_relocations),
[isRela] (const Relocation& relocation) {
return relocation.is_rela() == isRela;
})) {
throw LIEF::type_error("Object relocations are not of the same type");
}

it_sections sections = this->binary_->sections();

std::vector<Section*> rel_section;
for(Section& S: sections)
if(S.type() == ((isRela)?ELF_SECTION_TYPES::SHT_RELA:ELF_SECTION_TYPES::SHT_REL))
rel_section.push_back(&S);


// FIXME: Warn if not rel section found?

for(Section* section: rel_section) {

if (section->information() == 0 or section->information() >= sections.size())
throw LIEF::not_found("Unable to find associated section for SHT_REL{A} section");

const size_t sh_info = section->information();

Section& AssociatedSection = sections[sh_info];

std::vector<uint8_t> content;
for (const Relocation& relocation : this->binary_->object_relocations()) {

// Only write relocation in the matching section
// (relocation for .text in .rela.text)
// FIXME: static relocation on a new section will be ignored (SILENTLY!!)
if(relocation.section_ != &AssociatedSection)
continue;

uint32_t idx = 0;
if (relocation.has_symbol()) {
const Symbol& symbol = relocation.symbol();
auto it_name = std::find_if(
std::begin(this->binary_->dynamic_symbols_),
std::end(this->binary_->dynamic_symbols_),
[&symbol] (const Symbol* s) {
return s == &symbol;
});

if (it_name == std::end(this->binary_->dynamic_symbols_)) {
// FIXME: Do we have a way to walk both?
auto it_name = std::find_if(
std::begin(this->binary_->static_symbols_),
std::end(this->binary_->static_symbols_),
[&symbol] (const Symbol* s) {
return s == &symbol;
});

if (it_name == std::end(this->binary_->static_symbols_)) {
throw not_found("Unable to find the symbol associated with the relocation");
}
idx = static_cast<uint32_t>(std::distance(std::begin(this->binary_->static_symbols_), it_name));
} else
idx = static_cast<uint32_t>(std::distance(std::begin(this->binary_->dynamic_symbols_), it_name));
}


Elf_Xword info = 0;
if (std::is_same<ELF_T, ELF32>::value) {
info = (static_cast<Elf_Xword>(idx) << 8) | relocation.type();
} else {
info = (static_cast<Elf_Xword>(idx) << 32) | (relocation.type() & 0xffffffffL);
}

if (isRela) {
Elf_Rela relahdr;
relahdr.r_offset = static_cast<Elf_Addr>(relocation.address());
relahdr.r_info = static_cast<Elf_Xword>(info);
relahdr.r_addend = static_cast<Elf_Sxword>(relocation.addend());

content.insert(
std::end(content),
reinterpret_cast<uint8_t*>(&relahdr),
reinterpret_cast<uint8_t*>(&relahdr) + sizeof(Elf_Rela));

} else {
Elf_Rel relhdr;
relhdr.r_offset = static_cast<Elf_Addr>(relocation.address());
relhdr.r_info = static_cast<Elf_Xword>(info);

content.insert(
std::end(content),
reinterpret_cast<uint8_t*>(&relhdr),
reinterpret_cast<uint8_t*>(&relhdr) + sizeof(Elf_Rel));
}

}

VLOG(VDEBUG) << "Section associated with object relocations: " << section->name();
VLOG(VDEBUG) << "Is Rela: " << std::boolalpha << isRela;
// Relocation the '.rela.xxxx' section
if (content.size() > section->original_size()) {
Section rela_section(section->name(), (isRela)?ELF_SECTION_TYPES::SHT_RELA:ELF_SECTION_TYPES::SHT_REL);
rela_section.content(content);
this->binary_->add(rela_section, false);
this->binary_->remove(*section, true);

return this->build<ELF_T>();

}
section->content(std::move(content));
}
}

template<typename ELF_T>
void Builder::build_dynamic_relocations(void) {
using Elf_Addr = typename ELF_T::Elf_Addr;
Expand Down
47 changes: 35 additions & 12 deletions src/ELF/Parser.tcc
Expand Up @@ -339,8 +339,21 @@ void Parser::parse_binary(void) {
nb_entries,
this->binary_->sections_[section->link()]);
}

it_symtab_section = std::find_if(
it_symtab_section + 1,
std::end(this->binary_->sections_),
[] (const Section* section)
{
return section != nullptr and section->type() == ELF_SECTION_TYPES::SHT_SYMTAB;
});

if (it_symtab_section != std::end(this->binary_->sections_)) {
LOG(WARNING) << "Support for multiple SHT_SYMTAB section is not implemented\n";
}
}


// Parse Symbols's hash
// ====================

Expand Down Expand Up @@ -418,21 +431,14 @@ void Parser::parse_binary(void) {
// Try to parse using sections
if (this->binary_->relocations_.size() == 0) {
for (const Section& section : this->binary_->sections()) {
Section* section_associated = nullptr;
if (section.information() > 0 and section.information() < this->binary_->sections_.size()) {
const size_t sh_info = section.information();
section_associated = this->binary_->sections_[sh_info];
}

try {
if (section.type() == ELF_SECTION_TYPES::SHT_REL) {

this->parse_section_relocations<ELF_T, typename ELF_T::Elf_Rel>(
section.file_offset(), section.size(), section_associated);
this->parse_section_relocations<ELF_T, typename ELF_T::Elf_Rel>(section);
}
else if (section.type() == ELF_SECTION_TYPES::SHT_RELA) {
this->parse_section_relocations<ELF_T, typename ELF_T::Elf_Rela>(
section.file_offset(), section.size(), section_associated);
this->parse_section_relocations<ELF_T, typename ELF_T::Elf_Rela>(section);
}

} catch (const exception& e) {
Expand Down Expand Up @@ -1326,17 +1332,34 @@ void Parser::parse_pltgot_relocations(uint64_t offset, uint64_t size) {
}

template<typename ELF_T, typename REL_T>
void Parser::parse_section_relocations(uint64_t offset, uint64_t size, Section *applies_to) {
void Parser::parse_section_relocations(Section const& section) {
using Elf_Rel = typename ELF_T::Elf_Rel;
using Elf_Rela = typename ELF_T::Elf_Rela;

static_assert(std::is_same<REL_T, Elf_Rel>::value or
std::is_same<REL_T, Elf_Rela>::value, "REL_T must be Elf_Rel or Elf_Rela");

const uint64_t offset_relocations = offset;
// A relocation section can reference two other sections: a symbol table,
// identified by the sh_info section header entry, and a section to modify,
// identified by the sh_link
// BUT: in practice sh_info and sh_link are inverted
Section* applies_to = nullptr;
if (section.information() > 0 and section.information() < this->binary_->sections_.size()) {
const size_t sh_info = section.information();
applies_to = this->binary_->sections_[sh_info];
}

// FIXME: Use it
// Section* section_associated = nullptr;
// if (section.link() > 0 and section.link() < this->binary_->sections_.size()) {
// const size_t sh_link = section.link();
// section_associated = this->binary_->sections_[sh_link];
// }

const uint64_t offset_relocations = section.file_offset();
const uint8_t shift = std::is_same<ELF_T, ELF32>::value ? 8 : 32;

uint32_t nb_entries = static_cast<uint32_t>(size / sizeof(REL_T));
uint32_t nb_entries = static_cast<uint32_t>(section.size() / sizeof(REL_T));
nb_entries = std::min<uint32_t>(nb_entries, Parser::NB_MAX_RELOCATIONS);

this->stream_->setpos(offset_relocations);
Expand Down
3 changes: 3 additions & 0 deletions tests/elf/CMakeLists.txt
Expand Up @@ -205,6 +205,9 @@ if (PYTHON_TESTS_ENABLED)
${PYTHON_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}/test_dynamic.py")

ADD_PYTHON_TEST(ELF_PYTHON_test_static
${PYTHON_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}/test_static.py")

ADD_PYTHON_TEST(ELF_PYTHON_hash_test
${PYTHON_EXECUTABLE}
Expand Down

0 comments on commit d1b98d6

Please sign in to comment.