Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Enable to add relocation associated with symbol
Resolve #184
  • Loading branch information
romainthomas committed Jun 19, 2018
1 parent cd9cb41 commit a9f3cb8
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 69 deletions.
5 changes: 3 additions & 2 deletions api/python/ELF/objects/pyRelocation.cpp
Expand Up @@ -68,10 +68,11 @@ void create<Relocation>(py::module& m) {
&Relocation::has_symbol,
"``True`` if a " RST_CLASS_REF(lief.ELF.Symbol) " is associated with the relocations")

.def_property_readonly("symbol",
.def_property("symbol",
static_cast<Symbol& (Relocation::*)(void)>(&Relocation::symbol),
static_cast<void (Relocation::*)(Symbol*)>(&Relocation::symbol),
"" RST_CLASS_REF(lief.ELF.Symbol) " associated with the relocation",
py::return_value_policy::reference_internal)
py::return_value_policy::reference)

.def_property_readonly("has_section",
&Relocation::has_section,
Expand Down
126 changes: 63 additions & 63 deletions include/LIEF/ELF/Relocation.hpp
Expand Up @@ -42,71 +42,71 @@ class LIEF_API Relocation : public LIEF::Relocation {
friend class Builder;

public:
Relocation(const Elf32_Rel* header);
Relocation(const Elf32_Rela* header);
Relocation(const Elf64_Rel* header);
Relocation(const Elf64_Rela* header);
Relocation(uint64_t address, uint32_t type = 0, int64_t addend = 0, bool isRela = false);

template<class T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
Relocation(uint64_t address, T type, int64_t addend = 0, bool isRela = false) :
Relocation{address, static_cast<uint32_t>(type), addend, isRela}
{}

Relocation(void);
virtual ~Relocation(void);

Relocation& operator=(Relocation other);
Relocation(const Relocation& other);
void swap(Relocation& other);

//uint64_t address(void) const;
int64_t addend(void) const;
uint32_t type(void) const;
bool is_rela(void) const;
bool is_rel(void) const;
uint32_t info(void) const;
ARCH architecture(void) const;
RELOCATION_PURPOSES purpose(void) const;

//! @brief Return the **bit** size of the value to patch
//!
//! Return -1 if it fails
virtual size_t size(void) const override;

bool has_symbol(void) const;
Symbol& symbol(void);
const Symbol& symbol(void) const;

//! True if the relocation has a section associated
bool has_section(void) const;

//! Section associated with this relocation
Section& section(void);
const Section& section(void) const;

//void address(uint64_t address);
void addend(int64_t addend);
void type(uint32_t type);
void purpose(RELOCATION_PURPOSES purpose);
void info(uint32_t v);

virtual void accept(Visitor& visitor) const override;

bool operator==(const Relocation& rhs) const;
bool operator!=(const Relocation& rhs) const;

LIEF_API friend std::ostream& operator<<(std::ostream& os, const Relocation& entry);
Relocation(const Elf32_Rel* header);
Relocation(const Elf32_Rela* header);
Relocation(const Elf64_Rel* header);
Relocation(const Elf64_Rela* header);
Relocation(uint64_t address, uint32_t type = 0, int64_t addend = 0, bool isRela = false);

template<class T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
Relocation(uint64_t address, T type, int64_t addend = 0, bool isRela = false) :
Relocation{address, static_cast<uint32_t>(type), addend, isRela}
{}

Relocation(void);
virtual ~Relocation(void);

Relocation& operator=(Relocation other);
Relocation(const Relocation& other);
void swap(Relocation& other);

//uint64_t address(void) const;
int64_t addend(void) const;
uint32_t type(void) const;
bool is_rela(void) const;
bool is_rel(void) const;
uint32_t info(void) const;
ARCH architecture(void) const;
RELOCATION_PURPOSES purpose(void) const;

//! @brief Return the **bit** size of the value to patch
//!
//! Return -1 if it fails
virtual size_t size(void) const override;

bool has_symbol(void) const;
Symbol& symbol(void);
const Symbol& symbol(void) const;

//! True if the relocation has a section associated
bool has_section(void) const;

//! Section associated with this relocation
Section& section(void);
const Section& section(void) const;

void addend(int64_t addend);
void type(uint32_t type);
void purpose(RELOCATION_PURPOSES purpose);
void info(uint32_t v);
void symbol(Symbol* symbol);

virtual void accept(Visitor& visitor) const override;

bool operator==(const Relocation& rhs) const;
bool operator!=(const Relocation& rhs) const;

LIEF_API friend std::ostream& operator<<(std::ostream& os, const Relocation& entry);

private:
uint32_t type_;
int64_t addend_;
bool isRela_;
Symbol* symbol_{nullptr};
ARCH architecture_;
RELOCATION_PURPOSES purpose_;
Section* section_{nullptr};
uint32_t info_;
uint32_t type_;
int64_t addend_;
bool isRela_;
Symbol* symbol_{nullptr};
ARCH architecture_;
RELOCATION_PURPOSES purpose_;
Section* section_{nullptr};
uint32_t info_;
};


Expand Down
75 changes: 71 additions & 4 deletions src/ELF/Binary.cpp
Expand Up @@ -764,15 +764,36 @@ Relocation& Binary::add_dynamic_relocation(const Relocation& relocation) {
relocation_ptr->architecture_ = this->header().machine_type();
this->relocations_.push_back(relocation_ptr);

// Add symbol
if (relocation.has_symbol()) {
const Symbol& associated_sym = relocation.symbol();
Symbol* inner_sym = nullptr;
if (not this->has_dynamic_symbol(associated_sym.name())) {
inner_sym = &(this->add_dynamic_symbol(associated_sym));
} else {
inner_sym = &(this->get_dynamic_symbol(associated_sym.name()));
}

auto&& it_sym = std::find_if(
std::begin(this->dynamic_symbols_),
std::end(this->dynamic_symbols_),
[&inner_sym] (const Symbol* s) {
return s->name() == inner_sym->name();
});
const size_t idx = std::distance(std::begin(this->dynamic_symbols_), it_sym);
relocation_ptr->info(idx);
relocation_ptr->symbol(inner_sym);
}

// Update the Dynamic Section (Thanks to @yd0b0N)
bool is_rela = relocation.is_rela();
DYNAMIC_TAGS tag_sz = is_rela ? DYNAMIC_TAGS::DT_RELASZ : DYNAMIC_TAGS::DT_RELSZ;
DYNAMIC_TAGS tag_ent = is_rela ? DYNAMIC_TAGS::DT_RELAENT : DYNAMIC_TAGS::DT_RELENT;

if (this->has(tag_sz) and this->has(tag_ent)) {
DynamicEntry &dt_sz = this->get(tag_sz);
DynamicEntry &dt_ent = this->get(tag_ent);
dt_sz.value(dt_sz.value() + dt_ent.value());
DynamicEntry &dt_sz = this->get(tag_sz);
DynamicEntry &dt_ent = this->get(tag_ent);
dt_sz.value(dt_sz.value() + dt_ent.value());
}

return *relocation_ptr;
Expand All @@ -783,6 +804,52 @@ Relocation& Binary::add_pltgot_relocation(const Relocation& relocation) {
Relocation* relocation_ptr = new Relocation{relocation};
relocation_ptr->purpose(RELOCATION_PURPOSES::RELOC_PURPOSE_PLTGOT);
relocation_ptr->architecture_ = this->header().machine_type();

// Add symbol
if (relocation.has_symbol()) {
const Symbol& associated_sym = relocation.symbol();
Symbol* inner_sym = nullptr;
if (not this->has_dynamic_symbol(associated_sym.name())) {
inner_sym = &(this->add_dynamic_symbol(associated_sym));
} else {
inner_sym = &(this->get_dynamic_symbol(associated_sym.name()));
}

auto&& it_sym = std::find_if(
std::begin(this->dynamic_symbols_),
std::end(this->dynamic_symbols_),
[&inner_sym] (const Symbol* s) {
return s->name() == inner_sym->name();
});
const size_t idx = std::distance(std::begin(this->dynamic_symbols_), it_sym);
relocation_ptr->info(idx);
relocation_ptr->symbol(inner_sym);
}

// Update the Dynamic Section
const bool is_rela = relocation.is_rela();
const bool is64 = (this->type() == ELF_CLASS::ELFCLASS64);

size_t reloc_size = 0;
if (is_rela) {
if (is64) {
reloc_size = sizeof(Elf64_Rela);
} else {
reloc_size = sizeof(Elf32_Rela);
}
} else {
if (is64) {
reloc_size = sizeof(Elf64_Rel);
} else {
reloc_size = sizeof(Elf32_Rel);
}
}

if (this->has(DYNAMIC_TAGS::DT_PLTRELSZ) and this->has(DYNAMIC_TAGS::DT_JMPREL)) {
DynamicEntry &dt_sz = this->get(DYNAMIC_TAGS::DT_PLTRELSZ);
dt_sz.value(dt_sz.value() + reloc_size);
}

this->relocations_.push_back(relocation_ptr);
return *relocation_ptr;
}
Expand Down Expand Up @@ -1926,7 +1993,7 @@ void Binary::shift_relocations(uint64_t from, uint64_t shift) {
break;
}
*/

default:
{
LOG(WARNING) << "Relocations for architecture " << to_string(arch) << " is not supported!";
Expand Down
4 changes: 4 additions & 0 deletions src/ELF/Relocation.cpp
Expand Up @@ -290,6 +290,10 @@ void Relocation::info(uint32_t v) {
this->info_ = v;
}

void Relocation::symbol(Symbol* sym) {
this->symbol_ = sym;
}


void Relocation::purpose(RELOCATION_PURPOSES purpose) {
this->purpose_ = purpose;
Expand Down
4 changes: 4 additions & 0 deletions tests/elf/CMakeLists.txt
Expand Up @@ -224,6 +224,10 @@ if (PYTHON_TESTS_ENABLED)
${PYTHON_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}/test_bin2lib.py")

ADD_PYTHON_TEST(ELF_PYTHON_modify_relocations
${PYTHON_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}/modify_relocations.py")

# Examples
# --------
ADD_PYTHON_TEST(EXAMPLE_PYTHON_elf_reader_ls
Expand Down
122 changes: 122 additions & 0 deletions tests/elf/modify_relocations.py
@@ -0,0 +1,122 @@
#!/usr/bin/env python
import unittest
import logging
import os
import sys
import stat
import re
import subprocess
import tempfile
import shutil
from subprocess import Popen

import lief
from lief.ELF import Section

from unittest import TestCase
from utils import get_sample

class TestRelocations(TestCase):
def setUp(self):
self.logger = logging.getLogger(__name__)
self.tmp_dir = tempfile.mkdtemp(suffix='_lief_test_relocations')
self.logger.debug("temp dir: {}".format(self.tmp_dir))


@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_simple(self):
sample_path = get_sample('ELF/ELF64_x86-64_binary_ls.bin')
output = os.path.join(self.tmp_dir, "ls.relocation")

ls = lief.parse(sample_path)

relocation = lief.ELF.Relocation(0x61D370, type=lief.ELF.RELOCATION_X86_64.JUMP_SLOT, is_rela=True)

symbol = lief.ELF.Symbol()
symbol.name = "printf123"

relocation.symbol = symbol

ls.add_pltgot_relocation(relocation)

ls.write(output)

st = os.stat(output)
os.chmod(output, st.st_mode | stat.S_IEXEC)

p = Popen([output, "--version"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, _ = p.communicate()
self.logger.debug(stdout.decode("utf8"))
self.assertIsNotNone(re.search(r'ls \(GNU coreutils\) ', stdout.decode("utf8")))

@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_all(self):
sample_path = get_sample('ELF/ELF64_x86-64_binary_all.bin')
output = os.path.join(self.tmp_dir, "all.relocation")

target = lief.parse(sample_path)

relocation = lief.ELF.Relocation(0x201028, type=lief.ELF.RELOCATION_X86_64.JUMP_SLOT, is_rela=True)

symbol = lief.ELF.Symbol()
symbol.name = "printf123"

relocation.symbol = symbol
target.add_pltgot_relocation(relocation)

target.write(output)

st = os.stat(output)
os.chmod(output, st.st_mode | stat.S_IEXEC)

p = Popen([output], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, _ = p.communicate()
self.logger.debug(stdout.decode("utf8"))
self.assertIsNotNone(re.search(r'Hello World: 1', stdout.decode("utf8")))


@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_all32(self):
sample_path = get_sample('ELF/ELF32_x86_binary_all.bin')
output = os.path.join(self.tmp_dir, "all32.relocation")
output = "/tmp/foo"

target = lief.parse(sample_path)

relocation = lief.ELF.Relocation(0x2018, type=lief.ELF.RELOCATION_i386.JUMP_SLOT, is_rela=False)

symbol = lief.ELF.Symbol()
symbol.name = "printf123"

relocation.symbol = symbol
target.add_pltgot_relocation(relocation)

target.write(output)

st = os.stat(output)
os.chmod(output, st.st_mode | stat.S_IEXEC)

p = Popen([output], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, _ = p.communicate()
self.logger.debug(stdout.decode("utf8"))
self.assertIsNotNone(re.search(r'Hello World: 1', stdout.decode("utf8")))



def tearDown(self):
# Delete it
if os.path.isdir(self.tmp_dir):
#shutil.rmtree(self.tmp_dir)
pass

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)

0 comments on commit a9f3cb8

Please sign in to comment.