Merge branch 'pr/176'

This commit is contained in:
Romain Thomas 2018-07-23 14:52:36 +02:00
commit 4cb7ba46c7
6 changed files with 392 additions and 34 deletions

View File

@ -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);

View File

@ -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
//!

View File

@ -23,6 +23,8 @@
#include "Object.tcc"
#include <cassert>
namespace LIEF {
namespace ELF {
@ -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) {
@ -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 =
@ -238,28 +248,56 @@ void Builder::build_sections(void) {
}
Section* string_names_section = this->binary_->sections_[header.section_name_table_idx()];
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;
});
// 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()];
}
if(symbol_str_section == string_names_section)
{
Section sec_str_section(".shstrtab", ELF_SECTION_TYPES::SHT_STRTAB);
sec_str_section.content(section_names);
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);
// **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));
//}
// 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() << "'";
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 (it_offset_name == std::end(section_names)) {
throw LIEF::not_found(""); // TODO: msg
}
// Write Section's content
if (section->size() > 0) {
this->ios_.seekp(section->file_offset());
@ -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");
}
const Elf_Off offset_name = static_cast<Elf_Off>(std::distance(std::begin(section_names), it_offset_name));
@ -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()));
@ -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;

View File

@ -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
// ====================
@ -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) {
@ -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);
@ -1366,9 +1389,12 @@ void Parser::parse_section_relocations(uint64_t offset, uint64_t size, Section *
std::begin(this->binary_->relocations_),
std::end(this->binary_->relocations_),
[&reloc] (const Relocation* r) {
return r->address() == reloc->address() and
r->type() == reloc->type() and
r->addend() == reloc->addend();
bool is_same = r->address() == reloc->address() and
r->type() == reloc->type() and
r->addend() == reloc->addend();
if(r->has_symbol())
is_same &= reloc->has_symbol() and reloc->symbol() == r->symbol();
return is_same;
}) == std::end(this->binary_->relocations_)) {
this->binary_->relocations_.push_back(reloc.release());
}

View File

@ -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}

162
tests/elf/test_static.py Normal file
View File

@ -0,0 +1,162 @@
#!/usr/bin/env python
import unittest
import logging
import os
import sys
import subprocess
import tempfile
import shutil
import lief
from lief import Logger
Logger.set_level(lief.LOGGING_LEVEL.INFO)
from subprocess import Popen
from unittest import TestCase
BINADD_C = """\
#include <stdio.h>
#include <stdlib.h>
int add(int a, int b);
int main(int argc, char **argv) {
if (argc != 3) {
printf("Usage: %s <a> <b>\\n", argv[0]);
exit(-1);
}
int res = add(atoi(argv[1]), atoi(argv[2]));
printf("From myLIb, a + b = %d\\n", res);
return 0;
}
"""
ADD_C = """\
int add(int a, int b) {
return a + b;
}
"""
class LibAddSample(object):
COUNT = 0
def __init__(self):
self.logger = logging.getLogger(__name__)
self.tmp_dir = tempfile.mkdtemp(suffix='_lief_sample_{:d}'.format(LibAddSample.COUNT))
self.logger.debug("temp dir: {}".format(self.tmp_dir))
LibAddSample.COUNT += 1
self.binadd_path = os.path.join(self.tmp_dir, "binadd.c")
self.add_c_path = os.path.join(self.tmp_dir, "add.c")
self.binadd_obj = os.path.join(self.tmp_dir, "binadd.o")
self.binadd_bin = os.path.join(self.tmp_dir, "binadd.exe")
self.compiler = '/usr/bin/cc'
if self.compiler is None:
self.logger.error("Unable to find a compiler")
sys.exit(0)
self.logger.debug("Compiler: {}".format(self.compiler))
with open(self.binadd_path, 'w') as f:
f.write(BINADD_C)
with open(self.add_c_path, 'w') as f:
f.write(ADD_C)
self._compile_objadd()
def _compile_objadd(self):
if os.path.isfile(self.binadd_obj):
os.remove(self.binadd_obj)
cmd = [self.compiler, '-c', '-o', self.binadd_obj, self.binadd_path]
self.logger.debug("Compile 'binadd' with: {}".format(" ".join(cmd)))
p = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, _ = p.communicate()
self.logger.debug(stdout)
def compile_object_to_bin(self):
if os.path.isfile(self.binadd_bin):
os.remove(self.binadd_bin)
cmd = [self.compiler, '-o', self.binadd_bin, self.binadd_obj, self.add_c_path]
self.logger.debug("Compile 'binadd' with: {}".format(" ".join(cmd)))
p = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, _ = p.communicate()
self.logger.debug(stdout)
@property
def binadd(self):
return self.binadd_bin
@property
def objadd(self):
return self.binadd_obj
@property
def directory(self):
return self.tmp_dir
def remove(self):
if os.path.isdir(self.directory):
shutil.rmtree(self.directory)
def __del__(self):
self.remove()
class TestStatic(TestCase):
def setUp(self):
self.logger = logging.getLogger(__name__)
@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_write_object(self):
sample = LibAddSample()
tmp_file = os.path.join(sample.directory, "newfile.o")
binadd = lief.parse(sample.objadd)
init_obj = [str(o).strip() for o in binadd.object_relocations]
binadd.write(tmp_file)
binadd = lief.parse(tmp_file)
new_obj = [str(o).strip() for o in binadd.object_relocations]
self.assertEqual(len(init_obj), len(new_obj))
for new, old in zip(new_obj, init_obj):
self.assertEqual(new, old)
# Check it can still be compiled
sample.compile_object_to_bin()
self.assertEqual(subprocess.check_output([sample.binadd_bin, "2", "3"]).decode('ascii', 'ignore'),
'From myLIb, a + b = 5\n')
@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_update_addend_object(self):
sample = LibAddSample()
tmp_file = os.path.join(sample.directory, "newfile.o")
binadd = lief.parse(sample.objadd)
reloc = next(o for o in binadd.object_relocations if o.symbol.name == "add")
reloc.addend = 0xABCD
binadd.write(tmp_file)
binadd = lief.parse(tmp_file)
reloc = next(o for o in binadd.object_relocations if o.symbol.name == "add")
self.assertEqual(reloc.addend, 0xABCD)
if __name__ == '__main__':
root_logger = logging.getLogger()
root_logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
root_logger.addHandler(ch)
unittest.main(verbosity=2)