/* Copyright 2017 - 2026 R. Thomas
* Copyright 2017 - 2026 Quarkslab
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <iterator>
#include <numeric>
#include <sstream>
#include <cctype>
#include "LIEF/DWARF/enums.hpp"
#include "logging.hpp"
#include "LIEF/utils.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"
#include "LIEF/ELF/utils.hpp"
#include "LIEF/ELF/EnumToString.hpp"
#include "LIEF/ELF/Binary.hpp"
#include "LIEF/ELF/DynamicEntry.hpp"
#include "LIEF/ELF/DynamicEntryLibrary.hpp"
#include "LIEF/ELF/DynamicEntryArray.hpp"
#include "LIEF/ELF/DynamicEntryFlags.hpp"
#include "LIEF/ELF/Note.hpp"
#include "LIEF/ELF/Builder.hpp"
#include "LIEF/ELF/Section.hpp"
#include "LIEF/ELF/Segment.hpp"
#include "LIEF/ELF/Relocation.hpp"
#include "LIEF/ELF/Symbol.hpp"
#include "LIEF/ELF/SymbolVersion.hpp"
#include "LIEF/ELF/SymbolVersionDefinition.hpp"
#include "LIEF/ELF/SymbolVersionRequirement.hpp"
#include "LIEF/ELF/SymbolVersionAuxRequirement.hpp"
#include "LIEF/ELF/GnuHash.hpp"
#include "LIEF/ELF/SysvHash.hpp"
#include "LIEF/ELF/hash.hpp"
#include "ELF/DataHandler/Handler.hpp"
#include "ELF/SizingInfo.hpp"
#include "Binary.tcc"
#include "Object.tcc"
#include "internal_utils.hpp"
namespace LIEF::ELF {
inline size_t get_relocation_sizeof(const Binary& bin, const Relocation& R) {
const bool is64 = bin.type() == Header::CLASS::ELF64;
if (R.is_rel() || R.is_rela()) {
return is64 ? (R.is_rela() ? sizeof(details::Elf64_Rela) :
sizeof(details::Elf64_Rel)) :
(R.is_rela() ? sizeof(details::Elf32_Rela) :
sizeof(details::Elf32_Rel));
}
LIEF_WARN("get_relocation_sizeof() only supports REL/RELA encoding");
return size_t(-1);
}
Binary::Binary() :
LIEF::Binary(LIEF::Binary::FORMATS::ELF),
sizing_info_{std::make_unique<sizing_info_t>()} {}
size_t Binary::hash(const std::string& name) {
if (type_ == Header::CLASS::ELF32) {
return hash32(name.c_str());
}
return hash64(name.c_str());
}
LIEF::Binary::sections_t Binary::get_abstract_sections() {
LIEF::Binary::sections_t sections;
sections.reserve(sections_.size());
std::transform(sections_.begin(), sections_.end(), std::back_inserter(sections),
[](const std::unique_ptr<Section>& s) { return s.get(); });
return sections;
}
LIEF::Binary::functions_t Binary::get_abstract_exported_functions() const {
functions_t result;
for (const Symbol& symbol : exported_symbols()) {
if (symbol.type() == Symbol::TYPE::FUNC) {
result.emplace_back(symbol.name(), symbol.value(),
Function::FLAGS::EXPORTED);
}
}
return result;
}
LIEF::Binary::functions_t Binary::get_abstract_imported_functions() const {
functions_t result;
for (const Symbol& symbol : imported_symbols()) {
if (symbol.type() == Symbol::TYPE::FUNC) {
result.emplace_back(symbol.name(), symbol.value(),
Function::FLAGS::IMPORTED);
}
}
return result;
}
std::vector<std::string> Binary::get_abstract_imported_libraries() const {
std::vector<std::string> result;
for (const DynamicEntry& entry : dynamic_entries()) {
if (const auto* lib = entry.cast<DynamicEntryLibrary>()) {
result.push_back(lib->name());
}
}
return result;
}
DynamicEntry& Binary::add(const DynamicEntry& entry) {
std::unique_ptr<DynamicEntry> new_one = entry.clone();
const auto it_new_place =
std::find_if(dynamic_entries_.begin(), dynamic_entries_.end(),
[&new_one](const std::unique_ptr<DynamicEntry>& e) {
return e->tag() == new_one->tag() ||
e->tag() == DynamicEntry::TAG::DT_NULL_;
});
return **dynamic_entries_.insert(it_new_place, std::move(new_one));
}
Note& Binary::add(const Note& note) {
notes_.push_back(note.clone());
return *notes_.back();
}
void Binary::remove(const DynamicEntry& entry) {
const auto it_entry =
std::find_if(dynamic_entries_.begin(), dynamic_entries_.end(),
[&entry](const std::unique_ptr<DynamicEntry>& e) {
return e.get() == &entry;
});
if (it_entry == dynamic_entries_.end()) {
LIEF_WARN("{} not found in dynamic table, cannot remove", to_string(entry));
return;
}
dynamic_entries_.erase(it_entry);
}
void Binary::remove(DynamicEntry::TAG tag) {
dynamic_entries_.erase(
std::remove_if(dynamic_entries_.begin(), dynamic_entries_.end(),
[tag](const std::unique_ptr<DynamicEntry>& E) {
return E->tag() == tag;
}),
dynamic_entries_.end()
);
}
void Binary::remove(const Section& section, bool clear) {
const auto it_section =
std::find_if(sections_.begin(), sections_.end(),
[§ion](const std::unique_ptr<Section>& s) {
return *s == section;
});
if (it_section == sections_.end()) {
LIEF_WARN("Section '{}' not found, cannot remove", section.name());
return;
}
size_t idx = std::distance(sections_.begin(), it_section);
Section* s = it_section->get();
// Remove from segments:
for (std::unique_ptr<Segment>& segment : segments_) {
auto& sections = segment->sections_;
sections.erase(std::remove_if(sections.begin(), sections.end(),
[&s](const Section* sec) { return *sec == *s; }),
sections.end());
}
// Patch Section link
for (std::unique_ptr<Section>& section : sections_) {
if (section->link() == idx) {
section->link(0);
continue;
}
if (section->link() > idx) {
section->link(section->link() - 1);
continue;
}
}
if (clear) {
s->clear(0);
}
datahandler_->remove(s->file_offset(), s->size(), DataHandler::Node::SECTION);
// Patch header
header().numberof_sections(header().numberof_sections() - 1);
if (idx < header().section_name_table_idx()) {
header().section_name_table_idx(header().section_name_table_idx() - 1);
}
sections_.erase(it_section);
}
void Binary::remove(const Note& note) {
const auto it_note =
std::find_if(notes_.begin(), notes_.end(),
[¬e](const std::unique_ptr<Note>& n) { return note == *n; });
if (it_note == notes_.end()) {
LIEF_WARN("Note type {} not found, cannot remove",
to_string(static_cast<Note::TYPE>(note.type())));
return;
}
notes_.erase(it_note);
}
void Binary::remove(Note::TYPE type) {
for (auto it = notes_.begin(); it != notes_.end();) {
std::unique_ptr<Note>& n = *it;
if (n->type() == type) {
n.reset(nullptr);
it = notes_.erase(it);
} else {
++it;
}
}
}
int64_t Binary::symtab_idx(const std::string& name) const {
if (symtab_symbols_.empty()) {
return -1;
}
auto it = std::find_if(symtab_symbols_.begin(), symtab_symbols_.end(),
[&name](const std::unique_ptr<Symbol>& S) {
return S->name() == name;
});
if (it == symtab_symbols_.end()) {
return -1;
}
return std::distance(symtab_symbols_.begin(), it);
}
int64_t Binary::symtab_idx(const Symbol& sym) const {
return symtab_idx(sym.name());
}
int64_t Binary::dynsym_idx(const Symbol& sym) const {
return dynsym_idx(sym.name());
}
int64_t Binary::dynsym_idx(const std::string& name) const {
if (dynamic_symbols_.empty()) {
return -1;
}
auto it = std::find_if(dynamic_symbols_.begin(), dynamic_symbols_.end(),
[&name](const std::unique_ptr<Symbol>& S) {
return S->name() == name;
});
if (it == dynamic_symbols_.end()) {
return -1;
}
return std::distance(dynamic_symbols_.begin(), it);
}
Symbol& Binary::export_symbol(const Symbol& symbol) {
// Check if the symbol is in the dynamic symbol table
const auto it_symbol =
std::find_if(dynamic_symbols_.begin(), dynamic_symbols_.end(),
[&symbol](const std::unique_ptr<Symbol>& s) {
return *s == symbol;
});
Symbol* s = nullptr;
if (it_symbol == dynamic_symbols_.end()) {
// Create a new one
const SymbolVersion& version = SymbolVersion::global();
Symbol& new_sym = add_dynamic_symbol(symbol, &version);
s = &new_sym;
} else {
s = it_symbol->get();
}
const auto it_text = std::find_if(sections_.begin(), sections_.end(),
[](const std::unique_ptr<Section>& s) {
return s->name() == ".text";
});
size_t text_idx = std::distance(sections_.begin(), it_text);
if (s->binding() != Symbol::BINDING::WEAK ||
s->binding() != Symbol::BINDING::GLOBAL)
{
s->binding(Symbol::BINDING::GLOBAL);
}
if (s->type() == Symbol::TYPE::NOTYPE) {
s->type(Symbol::TYPE::COMMON);
}
if (s->shndx() == 0) {
s->shndx(text_idx);
}
s->visibility(Symbol::VISIBILITY::DEFAULT);
return *s;
}
Symbol& Binary::export_symbol(const std::string& symbol_name, uint64_t value) {
Symbol* s = get_dynamic_symbol(symbol_name);
if (s != nullptr) {
if (value > 0) {
s->value(value);
}
return export_symbol(*s);
}
s = get_symtab_symbol(symbol_name);
if (s != nullptr) {
if (value > 0) {
s->value(value);
}
return export_symbol(*s);
}
// Create a new one
Symbol newsym;
newsym.name(symbol_name);
newsym.type(Symbol::TYPE::COMMON);
newsym.binding(Symbol::BINDING::GLOBAL);
newsym.visibility(Symbol::VISIBILITY::DEFAULT);
newsym.value(value);
newsym.size(0x10);
return export_symbol(newsym);
}
Symbol& Binary::add_exported_function(uint64_t address, const std::string& name) {
std::string funcname = name;
if (funcname.empty()) {
std::stringstream ss;
ss << "func_" << std::hex << address;
funcname = ss.str();
}
// First: Check if a symbol with the given 'name' exists in the **dynamic** table
Symbol* s = get_dynamic_symbol(funcname);
if (s != nullptr) {
s->type(Symbol::TYPE::FUNC);
s->binding(Symbol::BINDING::GLOBAL);
s->visibility(Symbol::VISIBILITY::DEFAULT);
s->value(address);
return export_symbol(*s);
}
// Second: Check if a symbol with the given 'name' exists in the **static** table
s = get_symtab_symbol(funcname);
if (s != nullptr) {
s->type(Symbol::TYPE::FUNC);
s->binding(Symbol::BINDING::GLOBAL);
s->visibility(Symbol::VISIBILITY::DEFAULT);
s->value(address);
return export_symbol(*s);
}
// Create a new Symbol
Symbol funcsym;
funcsym.name(funcname);
funcsym.type(Symbol::TYPE::FUNC);
funcsym.binding(Symbol::BINDING::GLOBAL);
funcsym.visibility(Symbol::VISIBILITY::DEFAULT);
funcsym.value(address);
funcsym.size(0x10);
return export_symbol(funcsym);
}
const Symbol* Binary::get_dynamic_symbol(const std::string& name) const {
const auto it_symbol =
std::find_if(dynamic_symbols_.begin(), dynamic_symbols_.end(),
[&name](const std::unique_ptr<Symbol>& s) {
return s->name() == name;
});
if (it_symbol == dynamic_symbols_.end()) {
return nullptr;
}
return it_symbol->get();
}
const Symbol* Binary::get_symtab_symbol(const std::string& name) const {
const auto it_symbol =
std::find_if(symtab_symbols_.begin(), symtab_symbols_.end(),
[&name](const std::unique_ptr<Symbol>& s) {
return s->name() == name;
});
if (it_symbol == symtab_symbols_.end()) {
return nullptr;
}
return it_symbol->get();
}
Binary::string_list_t Binary::strings(size_t min_size) const {
Binary::string_list_t list;
const Section* rodata = get_section(".rodata");
if (rodata == nullptr) {
return {};
}
span<const uint8_t> data = rodata->content();
std::string current;
current.reserve(100);
for (unsigned char c : data) {
// Terminator
if (c == '\0') {
if (current.size() >= min_size) {
list.push_back(current);
current.clear();
continue;
}
current.clear();
continue;
}
// Valid char
if (std::isprint(c) == 0) {
current.clear();
continue;
}
current.push_back(static_cast<char>(c));
}
return list;
}
std::vector<Symbol*> Binary::symtab_dyn_symbols() const {
std::vector<Symbol*> symbols;
symbols.reserve(symtab_symbols_.size() + dynamic_symbols_.size());
for (const std::unique_ptr<Symbol>& s : dynamic_symbols_) {
symbols.push_back(s.get());
}
for (const std::unique_ptr<Symbol>& s : symtab_symbols_) {
symbols.push_back(s.get());
}
return symbols;
}
// Exported
// --------
Binary::it_exported_symbols Binary::exported_symbols() {
return {symtab_dyn_symbols(),
[](const Symbol* symbol) { return symbol->is_exported(); }};
}
Binary::it_const_exported_symbols Binary::exported_symbols() const {
return {symtab_dyn_symbols(),
[](const Symbol* symbol) { return symbol->is_exported(); }};
}
// Imported
// --------
Binary::it_imported_symbols Binary::imported_symbols() {
return {symtab_dyn_symbols(),
[](const Symbol* symbol) { return symbol->is_imported(); }};
}
Binary::it_const_imported_symbols Binary::imported_symbols() const {
return {symtab_dyn_symbols(),
[](const Symbol* symbol) { return symbol->is_imported(); }};
}
void Binary::remove_symbol(const std::string& name) {
remove_symtab_symbol(name);
remove_dynamic_symbol(name);
}
void Binary::remove_symtab_symbol(const std::string& name) {
Symbol* sym = get_symtab_symbol(name);
if (sym == nullptr) {
LIEF_WARN("Symtab symbol '{}' not found, cannot remove", name);
return;
}
remove_symtab_symbol(sym);
}
void Binary::remove_symtab_symbol(Symbol* symbol) {
if (symbol == nullptr) {
return;
}
const auto it_symbol =
std::find_if(symtab_symbols_.begin(), symtab_symbols_.end(),
[symbol](const std::unique_ptr<Symbol>& sym) {
return *symbol == *sym;
});
if (it_symbol == symtab_symbols_.end()) {
LIEF_WARN("Symtab symbol '{}' not found, cannot remove", symbol->name());
return;
}
symtab_symbols_.erase(it_symbol);
}
void Binary::remove_dynamic_symbol(const std::string& name) {
Symbol* sym = get_dynamic_symbol(name);
if (sym == nullptr) {
LIEF_WARN("Dynamic symbol '{}' not found, cannot remove", name);
return;
}
remove_dynamic_symbol(sym);
}
void Binary::remove_dynamic_symbol(Symbol* symbol) {
if (symbol == nullptr) {
return;
}
const auto it_symbol =
std::find_if(dynamic_symbols_.begin(), dynamic_symbols_.end(),
[symbol](const std::unique_ptr<Symbol>& sym) {
return *symbol == *sym;
});
if (it_symbol == dynamic_symbols_.end()) {
LIEF_WARN("Dynamic symbol '{}' not found, cannot remove", symbol->name());
return;
}
// Update relocations
auto it_relocation =
std::find_if(relocations_.begin(), relocations_.end(),
[symbol](const std::unique_ptr<Relocation>& relocation) {
return relocation->purpose() == Relocation::PURPOSE::PLTGOT &&
relocation->has_symbol() &&
relocation->symbol() == symbol;
});
if (it_relocation != relocations_.end()) {
Relocation& R = **it_relocation;
/* That's the tricky part:
*
* If we remove the JUMP_SLOT relocation associated with the symbols,
* it will break the lazy resolution process.
*
* Let's consider the following relocations:
* [0] 0201018 R_X86_64_JUMP_SLO puts
* [1] 0201020 R_X86_64_JUMP_SLO printf
*
* Which are associated with these resolving stubs:
*
* push 0 // Index of puts in the relocations table
* jmp <resolver>
*
* push 1 // Index of printf in the relocation table
* jmp <resolver>
*
* If we remove 'puts' from the relocation table, 'printf' is shifted
* to the index 0 and the index in the resolving stub is corrupted (push 1).
* Thus for the general case, we can't "shrink" the relocation table.
* Instead, unbinding the 'symbol' from the relocation does not break the
* layout while still removing the symbol.
*/
R.symbol(nullptr);
}
const size_t nb_relocs = relocations_.size();
size_t rel_sizeof = 0;
relocations_.erase(
std::remove_if(relocations_.begin(), relocations_.end(),
[symbol, this,
&rel_sizeof](const std::unique_ptr<Relocation>& reloc) {
if (reloc->purpose() != Relocation::PURPOSE::DYNAMIC) {
return false;
}
if (const Symbol* sym = reloc->symbol(); sym == symbol) {
rel_sizeof = get_relocation_sizeof(*this, *reloc);
return true;
}
return false;
}),
relocations_.end()
);
const size_t nb_deleted_relocs = nb_relocs - relocations_.size();
if (nb_deleted_relocs > 0) {
const size_t relocs_size = nb_deleted_relocs * rel_sizeof;
if (auto* DT = get(DynamicEntry::TAG::RELASZ)) {
const uint64_t sizes = DT->value();
if (sizes >= relocs_size) {
DT->value(sizes - relocs_size);
}
} else if (auto* DT = get(DynamicEntry::TAG::RELSZ)) {
const uint64_t sizes = DT->value();
if (sizes >= relocs_size) {
DT->value(sizes - relocs_size);
}
}
}
// Update symbol versions
if (symbol->has_version()) {
const auto it =
std::find_if(symbol_version_table_.begin(), symbol_version_table_.end(),
[symbol](const std::unique_ptr<SymbolVersion>& sv) {
return sv.get() == symbol->symbol_version_;
});
if (it != symbol_version_table_.end()) {
symbol_version_table_.erase(it);
}
}
dynamic_symbols_.erase(it_symbol);
}
// Relocations
// ===========
// Dynamics
// --------
Binary::it_dynamic_relocations Binary::dynamic_relocations() {
return {relocations_, [](const std::unique_ptr<Relocation>& reloc) {
return reloc->purpose() == Relocation::PURPOSE::DYNAMIC;
}};
}
Binary::it_const_dynamic_relocations Binary::dynamic_relocations() const {
return {relocations_, [](const std::unique_ptr<Relocation>& reloc) {
return reloc->purpose() == Relocation::PURPOSE::DYNAMIC;
}};
}
Relocation& Binary::add_dynamic_relocation(const Relocation& relocation) {
auto relocation_ptr = std::make_unique<Relocation>(relocation);
relocation_ptr->purpose(Relocation::PURPOSE::DYNAMIC);
relocation_ptr->architecture_ = header().machine_type();
// Add symbol
const Symbol* associated_sym = relocation.symbol();
if (associated_sym != nullptr) {
Symbol* inner_sym = get_dynamic_symbol(associated_sym->name());
if (inner_sym == nullptr) {
inner_sym = &(add_dynamic_symbol(*associated_sym));
}
const auto it_sym =
std::find_if(dynamic_symbols_.begin(), dynamic_symbols_.end(),
[inner_sym](const std::unique_ptr<Symbol>& s) {
return s->name() == inner_sym->name();
});
const size_t idx = std::distance(dynamic_symbols_.begin(), it_sym);
relocation_ptr->info(idx);
relocation_ptr->symbol(inner_sym);
}
const uint64_t psize = ptr_size();
const uint64_t default_rela =
psize == 8 ? sizeof(details::Elf64_Rela) : sizeof(details::Elf32_Rela);
const uint64_t default_rel =
psize == 8 ? sizeof(details::Elf64_Rel) : sizeof(details::Elf32_Rel);
if (relocation.is_rela()) {
if (DynamicEntry* dt_size = get(DynamicEntry::TAG::RELASZ)) {
dt_size->value(dt_size->value() +
entry_size(DynamicEntry::TAG::RELAENT, default_rela));
} else if (DynamicEntry* dt_size = get(DynamicEntry::TAG::ANDROID_RELASZ)) {
dt_size->value(dt_size->value() + default_rela);
}
} else if (relocation.is_rel()) {
if (DynamicEntry* dt_size = get(DynamicEntry::TAG::RELSZ)) {
dt_size->value(dt_size->value() +
entry_size(DynamicEntry::TAG::RELENT, default_rel));
} else if (DynamicEntry* dt_size = get(DynamicEntry::TAG::ANDROID_RELSZ)) {
dt_size->value(dt_size->value() + default_rela);
}
} else if (relocation.is_relatively_encoded()) {
if (DynamicEntry* dt_size = get(DynamicEntry::TAG::RELRSZ)) {
dt_size->value(dt_size->value() +
entry_size(DynamicEntry::TAG::RELRENT, psize));
} else if (DynamicEntry* dt_size = get(DynamicEntry::TAG::ANDROID_RELRSZ)) {
dt_size->value(dt_size->value() +
entry_size(DynamicEntry::TAG::ANDROID_RELRENT, psize));
}
} else if (relocation.is_android_packed()) {
if (DynamicEntry* dt_size = get(DynamicEntry::TAG::ANDROID_RELASZ)) {
dt_size->value(dt_size->value() + default_rela);
} else if (DynamicEntry* dt_size = get(DynamicEntry::TAG::ANDROID_RELSZ)) {
dt_size->value(dt_size->value() + default_rela);
}
}
return **relocations_.insert(relocations_.end(), std::move(relocation_ptr));
}
Relocation& Binary::add_pltgot_relocation(const Relocation& relocation) {
auto relocation_ptr = std::make_unique<Relocation>(relocation);
relocation_ptr->purpose(Relocation::PURPOSE::PLTGOT);
relocation_ptr->architecture_ = header().machine_type();
// Add symbol
const Symbol* associated_sym = relocation.symbol();
if (associated_sym != nullptr) {
Symbol* inner_sym = get_dynamic_symbol(associated_sym->name());
if (inner_sym == nullptr) {
inner_sym = &(add_dynamic_symbol(*associated_sym));
}
const auto it_sym =
std::find_if(dynamic_symbols_.begin(), dynamic_symbols_.end(),
[inner_sym](const std::unique_ptr<Symbol>& s) {
return s->name() == inner_sym->name();
});
const size_t idx = std::distance(dynamic_symbols_.begin(), it_sym);
relocation_ptr->info(idx);
relocation_ptr->symbol(inner_sym);
}
// Update the Dynamic Section
size_t reloc_size = get_relocation_sizeof(*this, relocation);
DynamicEntry* dt_sz = get(DynamicEntry::TAG::PLTRELSZ);
if (dt_sz != nullptr && has(DynamicEntry::TAG::JMPREL)) {
dt_sz->value(dt_sz->value() + reloc_size);
}
relocations_.push_back(std::move(relocation_ptr));
return *relocations_.back();
}
Relocation* Binary::add_object_relocation(const Relocation& relocation,
const Section& section) {
const auto it_section =
std::find_if(sections_.begin(), sections_.end(),
[§ion](const std::unique_ptr<Section>& sec) {
return §ion == sec.get();
});
if (it_section == sections_.end()) {
LIEF_ERR("Section '{}' not found", section.name());
return nullptr;
}
auto relocation_ptr = std::make_unique<Relocation>(relocation);
relocation_ptr->purpose(Relocation::PURPOSE::OBJECT);
relocation_ptr->architecture_ = header().machine_type();
relocation_ptr->section_ = it_section->get();
relocations_.push_back(std::move(relocation_ptr));
return relocations_.back().get();
}
// plt/got
// -------
Binary::it_pltgot_relocations Binary::pltgot_relocations() {
return {relocations_, [](const std::unique_ptr<Relocation>& reloc) {
return reloc->purpose() == Relocation::PURPOSE::PLTGOT;
}};
}
Binary::it_const_pltgot_relocations Binary::pltgot_relocations() const {
return {relocations_, [](const std::unique_ptr<Relocation>& reloc) {
return reloc->purpose() == Relocation::PURPOSE::PLTGOT;
}};
}
// objects
// -------
Binary::it_object_relocations Binary::object_relocations() {
return {relocations_, [](const std::unique_ptr<Relocation>& reloc) {
return reloc->purpose() == Relocation::PURPOSE::OBJECT;
}};
}
Binary::it_const_object_relocations Binary::object_relocations() const {
return {relocations_, [](const std::unique_ptr<Relocation>& reloc) {
return reloc->purpose() == Relocation::PURPOSE::OBJECT;
}};
}
LIEF::Binary::relocations_t Binary::get_abstract_relocations() {
LIEF::Binary::relocations_t relocations;
relocations.reserve(relocations_.size());
std::transform(relocations_.begin(), relocations_.end(),
std::back_inserter(relocations),
[](const std::unique_ptr<Relocation>& r) { return r.get(); });
return relocations;
}
LIEF::Binary::symbols_t Binary::get_abstract_symbols() {
LIEF::Binary::symbols_t symbols;
symbols.reserve(dynamic_symbols_.size() + symtab_symbols_.size());
std::transform(dynamic_symbols_.begin(), dynamic_symbols_.end(),
std::back_inserter(symbols),
[](std::unique_ptr<Symbol>& s) { return s.get(); });
std::transform(symtab_symbols_.begin(), symtab_symbols_.end(),
std::back_inserter(symbols),
[](std::unique_ptr<Symbol>& s) { return s.get(); });
return symbols;
}
const Section* Binary::get_section(const std::string& name) const {
const auto it_section =
std::find_if(sections_.begin(), sections_.end(),
[&name](const std::unique_ptr<Section>& section) {
return section->name() == name;
});
if (it_section == sections_.end()) {
return nullptr;
}
return it_section->get();
}
Section* Binary::dynamic_section() {
const auto it_dynamic_section =
std::find_if(sections_.begin(), sections_.end(),
[](const std::unique_ptr<Section>& section) {
return section->type() == Section::TYPE::DYNAMIC;
});
if (it_dynamic_section == sections_.end()) {
return nullptr;
}
return it_dynamic_section->get();
}
Section* Binary::hash_section() {
const auto it_hash_section =
std::find_if(sections_.begin(), sections_.end(),
[](const std::unique_ptr<Section>& section) {
return section->type() == Section::TYPE::HASH ||
section->type() == Section::TYPE::GNU_HASH;
});
if (it_hash_section == sections_.end()) {
return nullptr;
}
return it_hash_section->get();
}
Section* Binary::symtab_symbols_section() {
const auto it_symtab_section =
std::find_if(sections_.begin(), sections_.end(),
[](const std::unique_ptr<Section>& section) {
return section->type() == Section::TYPE::SYMTAB;
});
if (it_symtab_section == sections_.end()) {
return nullptr;
}
return it_symtab_section->get();
}
uint64_t Binary::imagebase() const {
auto imagebase = static_cast<uint64_t>(-1);
for (const std::unique_ptr<Segment>& segment : segments_) {
if (segment != nullptr && segment->is_load()) {
imagebase =
std::min(imagebase, segment->virtual_address() - segment->file_offset());
}
}
return imagebase;
}
uint64_t Binary::virtual_size() const {
uint64_t virtual_size = 0;
for (const std::unique_ptr<Segment>& segment : segments_) {
if (segment != nullptr && segment->is_load()) {
virtual_size = std::max(virtual_size, segment->virtual_address() +
segment->virtual_size());
}
}
virtual_size = align(virtual_size, page_size());
return virtual_size - imagebase();
}
std::vector<uint8_t> Binary::raw() {
Builder builder{*this, Builder::config_t{}};
builder.build();
return builder.get_build();
}
result<uint64_t> Binary::get_function_address(const std::string& func_name) const {
if (auto res = get_function_address(func_name, /* demangle */ true)) {
return *res;
}
if (auto res = get_function_address(func_name, /* demangle */ false)) {
return *res;
}
return LIEF::Binary::get_function_address(func_name);
;
}
result<uint64_t> Binary::get_function_address(const std::string& func_name,
bool demangled) const {
const auto it_dynsym =
std::find_if(dynamic_symbols_.begin(), dynamic_symbols_.end(),
[&func_name, demangled](const std::unique_ptr<Symbol>& symbol) {
std::string sname;
if (demangled) {
sname = symbol->demangled_name();
}
if (sname.empty()) {
sname = symbol->name();
}
return sname == func_name &&
symbol->type() == Symbol::TYPE::FUNC;
});
if (it_dynsym != dynamic_symbols_.end()) {
return (*it_dynsym)->value();
}
const auto it_symtab =
std::find_if(symtab_symbols_.begin(), symtab_symbols_.end(),
[&func_name, demangled](const std::unique_ptr<Symbol>& symbol) {
std::string sname;
if (demangled) {
sname = symbol->demangled_name();
}
if (sname.empty()) {
sname = symbol->name();
}
return sname == func_name &&
symbol->type() == Symbol::TYPE::FUNC;
});
if (it_symtab != symtab_symbols_.end()) {
uint64_t value = (*it_symtab)->value();
if (value > 0) {
return value;
}
}
return make_error_code(lief_errors::not_found);
}
Section* Binary::add(const Section& section, bool loaded, SEC_INSERT_POS pos) {
if (section.is_frame()) {
return add_frame_section(section);
}
if (loaded) {
return add_section<true>(section, pos);
}
return add_section<false>(section, pos);
}
Section* Binary::add_frame_section(const Section& sec) {
auto new_section = std::make_unique<Section>(sec);
this->header().numberof_sections(this->header().numberof_sections() + 1);
this->sections_.push_back(std::move(new_section));
return this->sections_.back().get();
}
bool Binary::is_pie() const {
const auto it_segment = std::find_if(segments_.begin(), segments_.end(),
[](const std::unique_ptr<Segment>& entry) {
return entry->is_interpreter();
});
if (header().file_type() != Header::FILE_TYPE::DYN) {
return false;
}
/* If the ELF binary uses an interpreter, then it is position
* independent since the interpreter aims at loading the binary at a random base
* address.
*/
if (it_segment != segments_.end()) {
return true;
}
/* It also exists ELF executables which don't have PT_INTERP but are
* PIE (see: https://github.com/lief-project/LIEF/issues/747). That's
* the case, for instance, when compiling with the -static-pie flag
*
* While header().file_type() == E_TYPE::ET_DYN is a requirement
* for PIC binary (Position independent **CODE**), it does not enable
* to distinguish PI **Executables** from libraries.
*
* Therefore, we add the following checks:
* 1. The binary embeds a PT_DYNAMIC segment
* 2. The dynamic table contains a DT_FLAGS_1 set with PIE
*/
if (has(Segment::TYPE::DYNAMIC)) {
if (const auto* flag =
static_cast<const DynamicEntryFlags*>(get(DynamicEntry::TAG::FLAGS_1)))
{
return flag->has(DynamicEntryFlags::FLAG::PIE);
}
}
return false;
}
bool Binary::has_nx() const {
if (const Segment* gnu_stack = get(Segment::TYPE::GNU_STACK)) {
return !gnu_stack->has(Segment::FLAGS::X);
}
if (header().machine_type() == ARCH::PPC64) {
// The PPC64 ELF ABI has a non-executable stack by default.
return true;
}
return false;
}
Segment* Binary::add(const Segment& segment, uint64_t base) {
switch (header().file_type()) {
case Header::FILE_TYPE::EXEC:
return add_segment<Header::FILE_TYPE::EXEC>(segment, base);
case Header::FILE_TYPE::DYN:
return add_segment<Header::FILE_TYPE::DYN>(segment, base);
default:
{
LIEF_WARN("Adding segment not implemented for {}",
to_string(header().file_type()));
return nullptr;
}
}
}
Segment* Binary::replace(const Segment& new_segment,
const Segment& original_segment, uint64_t base) {
const auto it_original_segment =
std::find_if(segments_.begin(), segments_.end(),
[&original_segment](const std::unique_ptr<Segment>& s) {
return *s == original_segment;
});
if (it_original_segment == segments_.end()) {
LIEF_WARN("Segment not found in binary");
return nullptr;
}
uint64_t new_base = base;
if (new_base == 0) {
const uint64_t alignment =
new_segment.alignment() > 0 ? new_segment.alignment() : page_size();
new_base = align(next_virtual_address(), alignment);
}
span<const uint8_t> content_ref = new_segment.content();
std::vector<uint8_t> content{content_ref.data(), content_ref.end()};
auto new_segment_ptr = std::make_unique<Segment>(new_segment);
new_segment_ptr->datahandler_ = datahandler_.get();
DataHandler::Node new_node{new_segment_ptr->file_offset(),
new_segment_ptr->physical_size(),
DataHandler::Node::SEGMENT};
datahandler_->add(new_node);
new_segment_ptr->handler_size_ = new_segment_ptr->physical_size();
const uint64_t last_offset_sections = last_offset_section();
const uint64_t last_offset_segments = last_offset_segment();
const uint64_t last_offset =
std::max<uint64_t>(last_offset_sections, last_offset_segments);
const auto psize = page_size();
const uint64_t last_offset_aligned = align(last_offset, psize);
new_segment_ptr->file_offset(last_offset_aligned);
if (new_segment_ptr->virtual_address() == 0) {
new_segment_ptr->virtual_address(new_base + last_offset_aligned);
}
new_segment_ptr->physical_address(new_segment_ptr->virtual_address());
uint64_t segmentsize = align(content.size(), psize);
content.resize(segmentsize);
new_segment_ptr->physical_size(segmentsize);
new_segment_ptr->virtual_size(segmentsize);
if (new_segment_ptr->alignment() == 0) {
new_segment_ptr->alignment(psize);
}
auto alloc = datahandler_->make_hole(last_offset_aligned,
new_segment_ptr->physical_size());
if (!alloc) {
LIEF_ERR("Allocation failed");
return nullptr;
}
new_segment_ptr->content(content);
const auto it_segment_phdr =
std::find_if(segments_.begin(), segments_.end(),
[](const std::unique_ptr<Segment>& s) { return s->is_phdr(); });
if (it_segment_phdr != segments_.end()) {
(*it_segment_phdr)->clear();
}
// Remove
std::unique_ptr<Segment> local_original_segment =
std::move(*it_original_segment);
datahandler_->remove(local_original_segment->file_offset(),
local_original_segment->physical_size(),
DataHandler::Node::SEGMENT);
segments_.erase(it_original_segment);
// Patch shdr
Header& header = this->header();
const uint64_t new_section_hdr_offset =
new_segment_ptr->file_offset() + new_segment_ptr->physical_size();
header.section_headers_offset(new_section_hdr_offset);
return segments_.emplace_back(std::move(new_segment_ptr)).get();
}
void Binary::remove(const Segment& segment, bool clear) {
const auto it_segment =
std::find_if(segments_.begin(), segments_.end(),
[&segment](const std::unique_ptr<Segment>& s) {
return s.get() == &segment;
});
if (it_segment == segments_.end()) {
LIEF_ERR("Provided segment not found");
return;
}
std::unique_ptr<Segment> local_segment = std::move(*it_segment);
if (clear) {
local_segment->clear();
}
datahandler_->remove(local_segment->file_offset(),
local_segment->physical_size(), DataHandler::Node::SEGMENT);
if (phdr_reloc_info_.new_offset > 0) {
++phdr_reloc_info_.nb_segments;
}
header().numberof_segments(header().numberof_segments() - 1);
segments_.erase(it_segment);
}
void Binary::remove(Segment::TYPE type, bool clear) {
std::vector<Segment*> to_remove;
for (std::unique_ptr<Segment>& S : segments_) {
if (S->type() == type) {
to_remove.push_back(S.get());
}
}
if (to_remove.empty()) {
return;
}
for (Segment* S : to_remove) {
remove(*S, clear);
}
}
Segment* Binary::extend(const Segment& segment, uint64_t size) {
const Segment::TYPE type = segment.type();
switch (type) {
case Segment::TYPE::PHDR:
case Segment::TYPE::LOAD:
return extend_segment<Segment::TYPE::LOAD>(segment, size);
default:
{
LIEF_WARN("Extending segment '{}' is not supported");
return nullptr;
}
}
}
Section* Binary::extend(const Section& section, uint64_t size) {
const auto it_section =
std::find_if(sections_.begin(), sections_.end(),
[§ion](const std::unique_ptr<Section>& s) {
return *s == section;
});
if (it_section == sections_.end()) {
LIEF_WARN("Section '{}' not found in binary", section.name());
return nullptr;
}
std::unique_ptr<Section>& section_to_extend = *it_section;
uint64_t from_offset = section_to_extend->offset() + section_to_extend->size();
uint64_t from_address =
section_to_extend->virtual_address() + section_to_extend->size();
bool section_loaded = section_to_extend->virtual_address() != 0;
uint64_t shift = size;
auto alloc = datahandler_->make_hole(
section_to_extend->offset() + section_to_extend->size(), size
);
if (!alloc) {
LIEF_ERR("Allocation failed");
return nullptr;
}
shift_sections(from_offset, shift);
shift_segments(from_offset, shift);
// Patch segment size for the segment which contains the new segment
for (std::unique_ptr<Segment>& segment : segments_) {
if ((segment->file_offset() + segment->physical_size()) >= from_offset &&
from_offset >= segment->file_offset())
{
if (section_loaded) {
segment->virtual_size(segment->virtual_size() + shift);
}
segment->physical_size(segment->physical_size() + shift);
}
}
section_to_extend->size(section_to_extend->size() + size);
span<const uint8_t> content_ref = section_to_extend->content();
std::vector<uint8_t> content = {content_ref.begin(), content_ref.end()};
content.resize(section_to_extend->size(), 0);
section_to_extend->content(content);
header().section_headers_offset(header().section_headers_offset() + shift);
if (section_loaded) {
shift_dynamic_entries(from_address, shift);
shift_symbols(from_address, shift);
shift_relocations(from_address, shift);
if (type() == Header::CLASS::ELF32) {
fix_got_entries<details::ELF32>(from_address, shift);
} else {
fix_got_entries<details::ELF64>(from_address, shift);
}
if (header().entrypoint() >= from_address) {
header().entrypoint(header().entrypoint() + shift);
}
}
return section_to_extend.get();
}
// Patch
// =====
void Binary::patch_address(uint64_t address,
const std::vector<uint8_t>& patch_value,
LIEF::Binary::VA_TYPES) {
// Object file does not have segments
if (header().file_type() == Header::FILE_TYPE::REL) {
Section* section = section_from_offset(address);
if (section == nullptr) {
LIEF_ERR("No section found at virtual address {:#x}", address);
return;
}
span<uint8_t> content = section->writable_content();
const uint64_t offset = address - section->file_offset();
if (offset + patch_value.size() > content.size()) {
LIEF_ERR(
"Patch value ({} bytes @{:#x}) exceeds segment bounds (limit: {:#x})",
patch_value.size(), offset, content.size()
);
return;
}
std::copy(patch_value.begin(), patch_value.end(), content.data() + offset);
return;
}
// Find the segment associated with the virtual address
Segment* segment_topatch = segment_from_virtual_address(address);
if (segment_topatch == nullptr) {
LIEF_ERR("No segment found at virtual address {:#x}", address);
return;
}
const uint64_t offset = address - segment_topatch->virtual_address();
span<uint8_t> content_ref = segment_topatch->writable_content();
if (offset + patch_value.size() > content_ref.size()) {
LIEF_ERR("Patch value ({} bytes @{:#x}) exceeds segment bounds (limit: {:#x})",
patch_value.size(), offset, content_ref.size());
return;
}
std::copy(patch_value.begin(), patch_value.end(), content_ref.data() + offset);
}
void Binary::patch_address(uint64_t address, uint64_t patch_value, size_t size,
LIEF::Binary::VA_TYPES) {
if (size > sizeof(patch_value)) {
LIEF_ERR("Patch size ({:#x}) exceeds sizeof(uint64_t)", size);
return;
}
// Object file does not have segments
if (header().file_type() == Header::FILE_TYPE::REL) {
Section* section = section_from_offset(address);
if (section == nullptr) {
LIEF_ERR("No section found at address {:#x}", address);
return;
}
span<uint8_t> content = section->writable_content();
const uint64_t offset = address - section->file_offset();
if (offset > content.size() || (offset + size) > content.size()) {
LIEF_ERR(
"Patch value ({} bytes @{:#x}) exceeds segment bounds (limit: {:#x})",
size, offset, content.size()
);
}
switch (size) {
case sizeof(uint8_t):
{
auto X = static_cast<uint8_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint8_t));
break;
}
case sizeof(uint16_t):
{
auto X = static_cast<uint16_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint16_t));
break;
}
case sizeof(uint32_t):
{
auto X = static_cast<uint32_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint32_t));
break;
}
case sizeof(uint64_t):
{
auto X = static_cast<uint64_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint64_t));
break;
}
default:
{
LIEF_ERR("Provided size ({}) does not match an integer size", size);
return;
}
}
return;
}
Segment* segment_topatch = segment_from_virtual_address(address);
if (segment_topatch == nullptr) {
LIEF_ERR("No segment found at virtual address {:#x}", address);
return;
}
const uint64_t offset = address - segment_topatch->virtual_address();
span<uint8_t> content = segment_topatch->writable_content();
if (offset > content.size() || (offset + size) > content.size()) {
LIEF_ERR("Patch value ({} bytes @{:#x}) exceeds segment bounds (limit: {:#x})",
size, offset, content.size());
}
switch (size) {
case sizeof(uint8_t):
{
const auto X = static_cast<uint8_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint8_t));
break;
}
case sizeof(uint16_t):
{
const auto X = static_cast<uint16_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint16_t));
break;
}
case sizeof(uint32_t):
{
const auto X = static_cast<uint32_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint32_t));
break;
}
case sizeof(uint64_t):
{
const auto X = static_cast<uint64_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint64_t));
break;
}
default:
{
LIEF_ERR("Provided size ({}) does not match an integer size", size);
return;
}
}
}
void Binary::patch_pltgot(const Symbol& symbol, uint64_t address) {
it_pltgot_relocations pltgot_relocations = this->pltgot_relocations();
const auto it_relocation =
std::find_if(pltgot_relocations.begin(), pltgot_relocations.end(),
[&symbol](const Relocation& relocation) {
return relocation.has_symbol() &&
relocation.symbol() == &symbol;
});
if (it_relocation == pltgot_relocations.end()) {
LIEF_ERR("Relocation not found for symbol {}", symbol.name());
return;
}
uint64_t got_address = (*it_relocation).address();
patch_address(got_address, address, sizeof(uint64_t));
}
void Binary::patch_pltgot(const std::string& symbol_name, uint64_t address) {
std::for_each(dynamic_symbols_.begin(), dynamic_symbols_.end(),
[&symbol_name, address, this](const std::unique_ptr<Symbol>& s) {
if (s->name() == symbol_name) {
patch_pltgot(*s, address);
}
});
}
const Segment* Binary::segment_from_virtual_address(uint64_t address) const {
const auto it_segment =
std::find_if(segments_.cbegin(), segments_.cend(),
[address](const std::unique_ptr<Segment>& segment) {
return segment->virtual_address() <= address &&
address < (segment->virtual_address() +
segment->virtual_size());
});
if (it_segment == segments_.cend()) {
return nullptr;
}
return it_segment->get();
}
const Segment* Binary::segment_from_virtual_address(Segment::TYPE type,
uint64_t address) const {
const auto it_segment = std::find_if(
segments_.cbegin(), segments_.cend(),
[address, type](const std::unique_ptr<Segment>& segment) {
return segment->type() == type && segment->virtual_address() <= address &&
address < (segment->virtual_address() + segment->virtual_size());
}
);
if (it_segment == segments_.cend()) {
return nullptr;
}
return it_segment->get();
}
const Segment* Binary::segment_from_offset(uint64_t offset) const {
const auto it_segment =
std::find_if(segments_.cbegin(), segments_.cend(),
[&offset](const std::unique_ptr<Segment>& segment) {
return segment->file_offset() <= offset &&
offset < (segment->file_offset() +
segment->physical_size());
});
if (it_segment == segments_.cend()) {
return nullptr;
}
return it_segment->get();
}
void Binary::remove_section(const std::string& name, bool clear) {
Section* sec = get_section(name);
if (sec == nullptr) {
return;
}
remove(*sec, clear);
}
bool Binary::has_section_with_offset(uint64_t offset) const {
const auto it_section =
std::find_if(sections_.cbegin(), sections_.cend(),
[offset](const std::unique_ptr<Section>& section) {
return section->offset() <= offset &&
offset < (section->offset() + section->size());
});
return it_section != sections_.cend();
}
bool Binary::has_section_with_va(uint64_t va) const {
const auto it_section =
std::find_if(sections_.cbegin(), sections_.cend(),
[va](const std::unique_ptr<Section>& section) {
return section->virtual_address() != 0 &&
section->virtual_address() <= va &&
va < (section->virtual_address() + section->size());
});
return it_section != sections_.cend();
}
void Binary::strip() {
symtab_symbols_.clear();
Section* symtab = get(Section::TYPE::SYMTAB);
if (symtab != nullptr) {
remove(*symtab, /* clear */ true);
}
}
Symbol& Binary::add_symtab_symbol(const Symbol& symbol) {
symtab_symbols_.push_back(std::make_unique<Symbol>(symbol));
return *symtab_symbols_.back();
}
Symbol& Binary::add_dynamic_symbol(const Symbol& symbol,
const SymbolVersion* version) {
auto sym = std::make_unique<Symbol>(symbol);
std::unique_ptr<SymbolVersion> symver;
if (version == nullptr) {
symver = std::make_unique<SymbolVersion>(SymbolVersion::global());
} else {
symver = std::make_unique<SymbolVersion>(*version);
}
sym->symbol_version_ = symver.get();
symbol_version_table_.push_back(std::move(symver));
return **dynamic_symbols_.insert(dynamic_symbols_.end(), std::move(sym));
}
result<uint64_t>
Binary::virtual_address_to_offset(uint64_t virtual_address) const {
const auto it_segment =
std::find_if(segments_.begin(), segments_.end(),
[virtual_address](const std::unique_ptr<Segment>& segment) {
return segment->is_load() &&
segment->virtual_address() <= virtual_address &&
virtual_address < (segment->virtual_address() +
segment->virtual_size());
});
if (it_segment == segments_.end()) {
LIEF_DEBUG("Address: {:#x}", virtual_address);
return make_error_code(lief_errors::conversion_error);
}
uint64_t base_address =
(*it_segment)->virtual_address() - (*it_segment)->file_offset();
uint64_t offset = virtual_address - base_address;
return offset;
}
result<uint64_t> Binary::offset_to_virtual_address(uint64_t offset,
uint64_t slide) const {
const auto it_segment = std::find_if(
segments_.begin(), segments_.end(),
[offset](const std::unique_ptr<Segment>& segment) {
return segment->is_load() && segment->file_offset() <= offset &&
offset < (segment->file_offset() + segment->physical_size());
}
);
if (it_segment == segments_.end()) {
if (slide > 0) {
return slide + offset;
}
return imagebase() + offset;
}
const uint64_t base_address =
(*it_segment)->virtual_address() - (*it_segment)->file_offset();
if (slide > 0) {
return (base_address - imagebase()) + slide + offset;
}
return base_address + offset;
}
bool Binary::has_interpreter() const {
const auto it_segment_interp =
std::find_if(segments_.begin(), segments_.end(),
[](const std::unique_ptr<Segment>& segment) {
return segment->is_interpreter();
});
return it_segment_interp != segments_.end() && !interpreter_.empty();
}
void Binary::write(const std::string& filename, const Builder::config_t& config) {
Builder builder{*this, config};
builder.build();
builder.write(filename);
}
void Binary::write(std::ostream& os, const Builder::config_t& config) {
Builder builder{*this, config};
builder.build();
builder.write(os);
}
const Section* Binary::section_from_offset(uint64_t offset,
bool skip_nobits) const {
const auto it_section =
std::find_if(sections_.cbegin(), sections_.cend(),
[offset, skip_nobits](const std::unique_ptr<Section>& section) {
if (skip_nobits && section->type() == Section::TYPE::NOBITS) {
return false;
}
return section->offset() <= offset &&
offset < (section->offset() + section->size());
});
if (it_section == sections_.cend()) {
return nullptr;
}
return it_section->get();
}
const Section* Binary::section_from_virtual_address(uint64_t address,
bool skip_nobits) const {
const auto it_section = std::find_if(
sections_.cbegin(), sections_.cend(),
[address, skip_nobits](const std::unique_ptr<Section>& section) {
if (skip_nobits && section->type() == Section::TYPE::NOBITS) {
return false;
}
return section->virtual_address() != 0 &&
section->virtual_address() <= address &&
address < (section->virtual_address() + section->size());
}
);
if (it_section == sections_.cend()) {
return nullptr;
}
return it_section->get();
}
span<const uint8_t>
Binary::get_content_from_virtual_address(uint64_t virtual_address,
uint64_t size,
Binary::VA_TYPES) const {
const Segment* segment = segment_from_virtual_address(virtual_address);
if (segment == nullptr) {
return {};
}
span<const uint8_t> content = segment->content();
const uint64_t offset = virtual_address - segment->virtual_address();
uint64_t checked_size = size;
if (offset >= content.size()) {
return {};
}
if ((offset + checked_size) > content.size()) {
checked_size = checked_size - (offset + checked_size - content.size());
}
return {content.data() + offset, static_cast<size_t>(checked_size)};
}
const DynamicEntry* Binary::get(DynamicEntry::TAG tag) const {
const auto it_entry =
std::find_if(dynamic_entries_.begin(), dynamic_entries_.end(),
[tag](const std::unique_ptr<DynamicEntry>& entry) {
return entry->tag() == tag;
});
if (it_entry == dynamic_entries_.end()) {
return nullptr;
}
return it_entry->get();
}
const Segment* Binary::get(Segment::TYPE type) const {
const auto it_segment =
std::find_if(segments_.begin(), segments_.end(),
[type](const std::unique_ptr<Segment>& segment) {
return segment->type() == type;
});
if (it_segment == segments_.end()) {
return nullptr;
}
return it_segment->get();
}
const Note* Binary::get(Note::TYPE type) const {
const auto it_note = std::find_if(notes_.begin(), notes_.end(),
[type](const std::unique_ptr<Note>& note) {
return note->type() == type;
});
if (it_note == notes_.end()) {
return nullptr;
}
return it_note->get();
}
const Section* Binary::get(Section::TYPE type) const {
const auto it_section =
std::find_if(sections_.begin(), sections_.end(),
[type](const std::unique_ptr<Section>& section) {
return section->type() == type;
});
if (it_section == sections_.end()) {
return nullptr;
}
return it_section->get();
}
void Binary::permute_dynamic_symbols(const std::vector<size_t>& permutation) {
std::set<size_t> done;
for (size_t i = 0; i < permutation.size(); ++i) {
if (permutation[i] == i || done.count(permutation[i]) > 0) {
continue;
}
if (dynamic_symbols_[i]->has_version() &&
dynamic_symbols_[permutation[i]]->has_version())
{
std::swap(symbol_version_table_[i], symbol_version_table_[permutation[i]]);
std::swap(dynamic_symbols_[i], dynamic_symbols_[permutation[i]]);
done.insert(permutation[i]);
done.insert(i);
} else if (!dynamic_symbols_[i]->has_version() &&
!dynamic_symbols_[permutation[i]]->has_version())
{
std::swap(dynamic_symbols_[i], dynamic_symbols_[permutation[i]]);
done.insert(permutation[i]);
done.insert(i);
} else {
LIEF_ERR("Failed to apply permutation at index #{:d}", i);
}
}
}
bool Binary::has_notes() const {
const auto it_segment_note =
std::find_if(segments_.begin(), segments_.end(),
[](const std::unique_ptr<Segment>& segment) {
return segment->type() == Segment::TYPE::NOTE;
});
return it_segment_note != segments_.end() && notes().size() > 0;
}
void Binary::accept(LIEF::Visitor& visitor) const {
visitor.visit(*this);
}
void Binary::shift_sections(uint64_t from, uint64_t shift) {
LIEF_DEBUG("[+] Shift Sections");
for (std::unique_ptr<Section>& section : sections_) {
if (section->is_frame()) {
continue;
}
if (section->file_offset() >= from) {
LIEF_DEBUG("[BEFORE] {}", to_string(*section));
section->file_offset(section->file_offset() + shift);
if (section->virtual_address() > 0) {
section->virtual_address(section->virtual_address() + shift);
}
LIEF_DEBUG("[AFTER ] {}", to_string(*section));
}
}
}
void Binary::shift_segments(uint64_t from, uint64_t shift) {
LIEF_DEBUG("Shifting segments by {:#x} from {:#x}", shift, from);
for (std::unique_ptr<Segment>& segment : segments_) {
if (segment->file_offset() >= from) {
LIEF_DEBUG("[BEFORE] {}", to_string(*segment));
segment->file_offset(segment->file_offset() + shift);
segment->virtual_address(segment->virtual_address() + shift);
segment->physical_address(segment->physical_address() + shift);
LIEF_DEBUG("[AFTER ] {}", to_string(*segment));
}
}
}
static void do_dynamic_entry_shift(uint64_t from, uint64_t shift,
DynamicEntry& entry,
Header::CLASS binary_type) {
switch (entry.tag()) {
// Address/Offset types
case DynamicEntry::TAG::PLTGOT:
case DynamicEntry::TAG::HASH:
case DynamicEntry::TAG::STRTAB:
case DynamicEntry::TAG::SYMTAB:
case DynamicEntry::TAG::RELA:
case DynamicEntry::TAG::INIT:
case DynamicEntry::TAG::FINI:
case DynamicEntry::TAG::REL:
case DynamicEntry::TAG::JMPREL:
case DynamicEntry::TAG::SYMTAB_SHNDX:
case DynamicEntry::TAG::RELR:
case DynamicEntry::TAG::GNU_HASH:
case DynamicEntry::TAG::TLSDESC_PLT:
case DynamicEntry::TAG::TLSDESC_GOT:
case DynamicEntry::TAG::VERSYM:
case DynamicEntry::TAG::VERDEF:
case DynamicEntry::TAG::VERNEED:
case DynamicEntry::TAG::ANDROID_REL_OFFSET:
case DynamicEntry::TAG::ANDROID_REL:
case DynamicEntry::TAG::ANDROID_RELA:
case DynamicEntry::TAG::ANDROID_RELR:
case DynamicEntry::TAG::MIPS_MSYM:
case DynamicEntry::TAG::MIPS_CONFLICT:
case DynamicEntry::TAG::MIPS_LIBLIST:
case DynamicEntry::TAG::MIPS_SYMBOL_LIB:
case DynamicEntry::TAG::MIPS_OPTIONS:
case DynamicEntry::TAG::MIPS_INTERFACE:
case DynamicEntry::TAG::MIPS_AUX_DYNAMIC:
case DynamicEntry::TAG::MIPS_PLTGOT:
// Absolute address of the runtime loader map, used for debugging.
case DynamicEntry::TAG::MIPS_RLD_MAP:
{
if (entry.value() >= from) {
entry.value(entry.value() + shift);
}
return;
}
// Array of address/offset types
case DynamicEntry::TAG::INIT_ARRAY:
case DynamicEntry::TAG::FINI_ARRAY:
case DynamicEntry::TAG::PREINIT_ARRAY:
{
DynamicEntryArray::array_t& array = entry.as<DynamicEntryArray>()->array();
for (uint64_t& address : array) {
if (address >= from) {
if ((binary_type == Header::CLASS::ELF32 &&
static_cast<int32_t>(address) > 0) ||
(binary_type == Header::CLASS::ELF64 &&
static_cast<int64_t>(address) > 0))
{
address += shift;
}
}
}
if (entry.value() >= from) {
entry.value(entry.value() + shift);
}
return;
}
// Plain/No value types
case DynamicEntry::TAG::UNKNOWN:
case DynamicEntry::TAG::DT_NULL_:
case DynamicEntry::TAG::NEEDED:
case DynamicEntry::TAG::PLTRELSZ:
case DynamicEntry::TAG::RELASZ:
case DynamicEntry::TAG::RELAENT:
case DynamicEntry::TAG::STRSZ:
case DynamicEntry::TAG::SYMENT:
case DynamicEntry::TAG::SONAME:
case DynamicEntry::TAG::RPATH:
case DynamicEntry::TAG::SYMBOLIC:
case DynamicEntry::TAG::RELSZ:
case DynamicEntry::TAG::RELENT:
case DynamicEntry::TAG::PLTREL:
case DynamicEntry::TAG::DEBUG_TAG:
case DynamicEntry::TAG::TEXTREL:
case DynamicEntry::TAG::BIND_NOW:
case DynamicEntry::TAG::INIT_ARRAYSZ:
case DynamicEntry::TAG::FINI_ARRAYSZ:
case DynamicEntry::TAG::RUNPATH:
case DynamicEntry::TAG::FLAGS:
case DynamicEntry::TAG::PREINIT_ARRAYSZ:
case DynamicEntry::TAG::RELRSZ:
case DynamicEntry::TAG::RELRENT:
case DynamicEntry::TAG::RELACOUNT:
case DynamicEntry::TAG::RELCOUNT:
case DynamicEntry::TAG::FLAGS_1:
case DynamicEntry::TAG::VERDEFNUM:
case DynamicEntry::TAG::VERNEEDNUM:
case DynamicEntry::TAG::AUXILIARY:
case DynamicEntry::TAG::FILTER:
case DynamicEntry::TAG::ANDROID_REL_SIZE:
case DynamicEntry::TAG::ANDROID_RELSZ:
case DynamicEntry::TAG::ANDROID_RELASZ:
case DynamicEntry::TAG::ANDROID_RELRSZ:
case DynamicEntry::TAG::ANDROID_RELRENT:
case DynamicEntry::TAG::ANDROID_RELRCOUNT:
case DynamicEntry::TAG::MIPS_RLD_VERSION:
case DynamicEntry::TAG::MIPS_TIME_STAMP:
case DynamicEntry::TAG::MIPS_ICHECKSUM:
case DynamicEntry::TAG::MIPS_IVERSION:
case DynamicEntry::TAG::MIPS_FLAGS:
case DynamicEntry::TAG::MIPS_BASE_ADDRESS:
case DynamicEntry::TAG::MIPS_LOCAL_GOTNO:
case DynamicEntry::TAG::MIPS_CONFLICTNO:
case DynamicEntry::TAG::MIPS_LIBLISTNO:
case DynamicEntry::TAG::MIPS_SYMTABNO:
case DynamicEntry::TAG::MIPS_UNREFEXTNO:
case DynamicEntry::TAG::MIPS_GOTSYM:
case DynamicEntry::TAG::MIPS_HIPAGENO:
case DynamicEntry::TAG::MIPS_DELTA_CLASS_NO:
case DynamicEntry::TAG::MIPS_DELTA_INSTANCE_NO:
case DynamicEntry::TAG::MIPS_DELTA_RELOC_NO:
case DynamicEntry::TAG::MIPS_DELTA_SYM_NO:
case DynamicEntry::TAG::MIPS_DELTA_CLASSSYM_NO:
case DynamicEntry::TAG::MIPS_CXX_FLAGS:
case DynamicEntry::TAG::MIPS_LOCALPAGE_GOTIDX:
case DynamicEntry::TAG::MIPS_LOCAL_GOTIDX:
case DynamicEntry::TAG::MIPS_HIDDEN_GOTIDX:
case DynamicEntry::TAG::MIPS_PROTECTED_GOTIDX:
case DynamicEntry::TAG::MIPS_INTERFACE_SIZE:
case DynamicEntry::TAG::MIPS_RLD_TEXT_RESOLVE_ADDR:
case DynamicEntry::TAG::MIPS_PERF_SUFFIX:
case DynamicEntry::TAG::MIPS_COMPACT_SIZE:
case DynamicEntry::TAG::MIPS_GP_VALUE:
{
// Explicitly annotate the entry types that don't need shifting. We want to
// warn if we miss something.
return;
}
// DT_MIPS_RLD_MAP_REL holds an offset **relative** to the address of its own
// entry
case DynamicEntry::TAG::MIPS_RLD_MAP_REL:
// Not yet documented types
// If a type is here despite having a description then there is not enough
// clarity about whether we affect it.
// TODO: These should be reduced to zero by moving them to the correct
// category.
case DynamicEntry::TAG::MIPS_DELTA_CLASS: /**< Delta C++ class definition. */
case DynamicEntry::TAG::MIPS_DELTA_INSTANCE: /**< Delta C++ class instances. */
case DynamicEntry::TAG::MIPS_DELTA_RELOC: /**< Delta relocations. */
case DynamicEntry::TAG::MIPS_DELTA_SYM: /**< Delta symbols that Delta
relocations refer to. */
case DynamicEntry::TAG::MIPS_DELTA_CLASSSYM: /**< Delta symbols that hold class
declarations. */
case DynamicEntry::TAG::MIPS_PIXIE_INIT: /**< Pixie information (??? [sic]). */
case DynamicEntry::TAG::MIPS_DYNSTR_ALIGN: /**< Unknown. */
case DynamicEntry::TAG::MIPS_RWPLT:
case DynamicEntry::TAG::MIPS_XHASH:
case DynamicEntry::TAG::AARCH64_BTI_PLT:
case DynamicEntry::TAG::AARCH64_PAC_PLT:
case DynamicEntry::TAG::AARCH64_VARIANT_PCS:
case DynamicEntry::TAG::AARCH64_MEMTAG_MODE:
case DynamicEntry::TAG::AARCH64_MEMTAG_HEAP:
case DynamicEntry::TAG::AARCH64_MEMTAG_STACK:
case DynamicEntry::TAG::AARCH64_MEMTAG_GLOBALS:
case DynamicEntry::TAG::AARCH64_MEMTAG_GLOBALSSZ:
case DynamicEntry::TAG::HEXAGON_SYMSZ:
case DynamicEntry::TAG::HEXAGON_VER:
case DynamicEntry::TAG::HEXAGON_PLT:
case DynamicEntry::TAG::PPC_GOT:
case DynamicEntry::TAG::PPC_OPT:
case DynamicEntry::TAG::PPC64_GLINK:
case DynamicEntry::TAG::PPC64_OPT:
case DynamicEntry::TAG::RISCV_VARIANT_CC:
case DynamicEntry::TAG::X86_64_PLT:
case DynamicEntry::TAG::X86_64_PLTSZ:
case DynamicEntry::TAG::X86_64_PLTENT:
case DynamicEntry::TAG::IA_64_PLT_RESERVE:
case DynamicEntry::TAG::IA_64_VMS_SUBTYPE:
case DynamicEntry::TAG::IA_64_VMS_IMGIOCNT:
case DynamicEntry::TAG::IA_64_VMS_LNKFLAGS:
case DynamicEntry::TAG::IA_64_VMS_VIR_MEM_BLK_SIZ:
case DynamicEntry::TAG::IA_64_VMS_IDENT:
case DynamicEntry::TAG::IA_64_VMS_NEEDED_IDENT:
case DynamicEntry::TAG::IA_64_VMS_IMG_RELA_CNT:
case DynamicEntry::TAG::IA_64_VMS_SEG_RELA_CNT:
case DynamicEntry::TAG::IA_64_VMS_FIXUP_RELA_CNT:
case DynamicEntry::TAG::IA_64_VMS_FIXUP_NEEDED:
case DynamicEntry::TAG::IA_64_VMS_SYMVEC_CNT:
case DynamicEntry::TAG::IA_64_VMS_XLATED:
case DynamicEntry::TAG::IA_64_VMS_STACKSIZE:
case DynamicEntry::TAG::IA_64_VMS_UNWINDSZ:
case DynamicEntry::TAG::IA_64_VMS_UNWIND_CODSEG:
case DynamicEntry::TAG::IA_64_VMS_UNWIND_INFOSEG:
case DynamicEntry::TAG::IA_64_VMS_LINKTIME:
case DynamicEntry::TAG::IA_64_VMS_SEG_NO:
case DynamicEntry::TAG::IA_64_VMS_SYMVEC_OFFSET:
case DynamicEntry::TAG::IA_64_VMS_SYMVEC_SEG:
case DynamicEntry::TAG::IA_64_VMS_UNWIND_OFFSET:
case DynamicEntry::TAG::IA_64_VMS_UNWIND_SEG:
case DynamicEntry::TAG::IA_64_VMS_STRTAB_OFFSET:
case DynamicEntry::TAG::IA_64_VMS_SYSVER_OFFSET:
case DynamicEntry::TAG::IA_64_VMS_IMG_RELA_OFF:
case DynamicEntry::TAG::IA_64_VMS_SEG_RELA_OFF:
case DynamicEntry::TAG::IA_64_VMS_FIXUP_RELA_OFF:
case DynamicEntry::TAG::IA_64_VMS_PLTGOT_OFFSET:
case DynamicEntry::TAG::IA_64_VMS_PLTGOT_SEG:
case DynamicEntry::TAG::IA_64_VMS_FPMODE:
{
LIEF_WARN("Shifting behavior of dynamic entry {} not defined",
to_string(entry.tag()));
return;
}
}
// The lack of a 'default' option in the above cases should also warn at
// build-time if this is reachable with a known enum value.
LIEF_WARN("Shifting behavior of dynamic entry {} not defined",
to_string(entry.tag()));
}
void Binary::shift_dynamic_entries(uint64_t from, uint64_t shift) {
LIEF_DEBUG("Shifting dynamic entries by {:#x} from {:#x}", shift, from);
for (std::unique_ptr<DynamicEntry>& entry : dynamic_entries_) {
LIEF_DEBUG("[BEFORE] {}", to_string(*entry));
do_dynamic_entry_shift(from, shift, *entry, type());
LIEF_DEBUG("[AFTER ] {}", to_string(*entry));
}
}
void Binary::shift_symbols(uint64_t from, uint64_t shift) {
LIEF_DEBUG("Shifting symbols by {:#x} from {:#x}", shift, from);
for (Symbol& symbol : symbols()) {
if (symbol.type() == Symbol::TYPE::TLS) {
continue;
}
if (symbol.value() >= from) {
LIEF_DEBUG("[BEFORE] {}", to_string(symbol));
symbol.value(symbol.value() + shift);
LIEF_DEBUG("[AFTER ] {}", to_string(symbol));
}
}
}
void Binary::shift_relocations(uint64_t from, uint64_t shift) {
const ARCH arch = header().machine_type();
LIEF_DEBUG("Shift relocations for {} by {:#x} from {:#x}", to_string(arch),
shift, from);
switch (arch) {
case ARCH::ARM: patch_relocations<ARCH::ARM>(from, shift); return;
case ARCH::AARCH64: patch_relocations<ARCH::AARCH64>(from, shift); return;
case ARCH::X86_64: patch_relocations<ARCH::X86_64>(from, shift); return;
case ARCH::I386: patch_relocations<ARCH::I386>(from, shift); return;
case ARCH::PPC: patch_relocations<ARCH::PPC>(from, shift); return;
case ARCH::PPC64: patch_relocations<ARCH::PPC64>(from, shift); return;
case ARCH::RISCV: patch_relocations<ARCH::RISCV>(from, shift); return;
case ARCH::SH: patch_relocations<ARCH::SH>(from, shift); return;
case ARCH::S390: patch_relocations<ARCH::S390>(from, shift); return;
default:
{
LIEF_DEBUG("Unsupported relocation architecture: {}", to_string(arch));
}
}
}
uint64_t Binary::last_offset_section() const {
return std::accumulate(
sections_.begin(), sections_.end(), 0llu,
[](uint64_t offset, const std::unique_ptr<Section>& section) {
if (section->is_frame()) {
return offset;
}
if (section->type() == Section::TYPE::NOBITS) {
return offset;
}
return std::max<uint64_t>(section->file_offset() + section->size(),
offset);
}
);
}
uint64_t Binary::last_offset_segment() const {
return std::accumulate(segments_.begin(), segments_.end(), 0llu,
[](uint64_t offset,
const std::unique_ptr<Segment>& segment) {
return std::max<uint64_t>(segment->file_offset() +
segment->physical_size(),
offset);
});
}
uint64_t Binary::next_virtual_address() const {
uint64_t va = std::accumulate(
segments_.begin(), segments_.end(), uint64_t{0u},
[](uint64_t address, const std::unique_ptr<Segment>& segment) {
return std::max<uint64_t>(
segment->virtual_address() + segment->virtual_size(), address
);
}
);
return align(va, page_size());
}
DynamicEntryLibrary& Binary::add_library(const std::string& library_name) {
return *add(DynamicEntryLibrary{library_name}).as<DynamicEntryLibrary>();
}
void Binary::remove_library(const std::string& library_name) {
DynamicEntryLibrary* lib = get_library(library_name);
if (lib == nullptr) {
LIEF_ERR("Library '{}' not found", library_name);
return;
}
remove(*lib);
}
const DynamicEntryLibrary*
Binary::get_library(const std::string& library_name) const {
const auto it_needed =
std::find_if(dynamic_entries_.begin(), dynamic_entries_.end(),
[&library_name](const std::unique_ptr<DynamicEntry>& entry) {
if (const auto* lib = entry->cast<DynamicEntryLibrary>()) {
return lib->name() == library_name;
}
return false;
});
if (it_needed == dynamic_entries_.end()) {
return nullptr;
}
return static_cast<const DynamicEntryLibrary*>(it_needed->get());
}
LIEF::Binary::functions_t Binary::tor_functions(DynamicEntry::TAG tag) const {
LIEF::Binary::functions_t functions;
const DynamicEntry* entry = get(tag);
if (entry == nullptr || !DynamicEntryArray::classof(entry)) {
return {};
}
const DynamicEntryArray::array_t& array =
entry->as<DynamicEntryArray>()->array();
functions.reserve(array.size());
for (uint64_t x : array) {
if (x != 0 && static_cast<uint32_t>(x) != static_cast<uint32_t>(-1) &&
x != static_cast<uint64_t>(-1))
{
functions.emplace_back(x);
}
}
return functions;
}
// Ctor
LIEF::Binary::functions_t Binary::ctor_functions() const {
LIEF::Binary::functions_t functions;
LIEF::Binary::functions_t init = tor_functions(DynamicEntry::TAG::INIT_ARRAY);
std::transform(init.begin(), init.end(), std::back_inserter(functions),
[](Function& f) {
f.add(Function::FLAGS::CONSTRUCTOR);
f.name("__dt_init_array");
return f;
});
LIEF::Binary::functions_t preinit =
tor_functions(DynamicEntry::TAG::PREINIT_ARRAY);
std::transform(preinit.begin(), preinit.end(), std::back_inserter(functions),
[](Function& f) {
f.add(Function::FLAGS::CONSTRUCTOR);
f.name("__dt_preinit_array");
return f;
});
const DynamicEntry* dt_init = get(DynamicEntry::TAG::INIT);
if (dt_init != nullptr) {
functions.emplace_back("__dt_init", dt_init->value(),
Function::FLAGS::CONSTRUCTOR);
}
return functions;
}
LIEF::Binary::functions_t Binary::dtor_functions() const {
LIEF::Binary::functions_t functions;
LIEF::Binary::functions_t fini = tor_functions(DynamicEntry::TAG::FINI_ARRAY);
std::transform(fini.begin(), fini.end(), std::back_inserter(functions),
[](Function& f) {
f.add(Function::FLAGS::DESTRUCTOR);
f.name("__dt_fini_array");
return f;
});
const DynamicEntry* dt_fini = get(DynamicEntry::TAG::FINI);
if (dt_fini != nullptr) {
functions.emplace_back("__dt_fini", dt_fini->value(),
Function::FLAGS::DESTRUCTOR);
}
return functions;
}
const Relocation* Binary::get_relocation(uint64_t address) const {
const auto it = std::find_if(relocations_.begin(), relocations_.end(),
[address](const std::unique_ptr<Relocation>& r) {
return r->address() == address;
});
if (it != relocations_.end()) {
return it->get();
}
return nullptr;
}
const Relocation* Binary::get_relocation(const Symbol& symbol) const {
const auto it = std::find_if(relocations_.begin(), relocations_.end(),
[&symbol](const std::unique_ptr<Relocation>& r) {
return r->has_symbol() && r->symbol() == &symbol;
});
if (it != relocations_.end()) {
return it->get();
}
return nullptr;
}
const Relocation* Binary::get_relocation(const std::string& symbol_name) const {
const LIEF::Symbol* sym = get_symbol(symbol_name);
if (sym == nullptr) {
return nullptr;
}
return get_relocation(*sym->as<Symbol>());
}
LIEF::Binary::functions_t Binary::armexid_functions() const {
LIEF::Binary::functions_t funcs;
static const auto expand_prel31 = [](uint32_t word, uint32_t base) {
uint32_t offset = word & 0x7fffffff;
if ((offset & 0x40000000) != 0u) {
offset |= ~static_cast<uint32_t>(0x7fffffff);
}
return base + offset;
};
const Segment* exidx = get(Segment::TYPE::ARM_EXIDX);
if (exidx != nullptr) {
span<const uint8_t> content = exidx->content();
const size_t nb_functions = content.size() / (2 * sizeof(uint32_t));
funcs.reserve(nb_functions);
const auto* entries = reinterpret_cast<const uint32_t*>(content.data());
for (size_t i = 0; i < 2 * nb_functions; i += 2) {
uint32_t first_word = entries[i];
/*uint32_t second_word = entries[i + 1]; */
if ((first_word & 0x80000000) == 0) {
uint32_t prs_data = expand_prel31(first_word, exidx->virtual_address() +
i * sizeof(uint32_t));
funcs.emplace_back(prs_data);
}
}
}
return funcs;
}
LIEF::Binary::functions_t Binary::eh_frame_functions() const {
LIEF::Binary::functions_t functions;
const Segment* eh_frame_seg = get(Segment::TYPE::GNU_EH_FRAME);
if (eh_frame_seg == nullptr) {
return functions;
}
const uint64_t eh_frame_addr = eh_frame_seg->virtual_address();
const uint64_t eh_frame_rva = eh_frame_addr - imagebase();
uint64_t eh_frame_off = 0;
if (auto res = virtual_address_to_offset(eh_frame_addr)) {
eh_frame_off = *res;
} else {
LIEF_WARN(
"Failed to convert PT_GNU_EH_FRAME virtual address to offset ({:#x})",
eh_frame_addr
);
return functions;
}
const Segment* load_segment =
segment_from_virtual_address(Segment::TYPE::LOAD, eh_frame_addr);
if (load_segment == nullptr) {
LIEF_ERR("LOAD segment for PT_GNU_EH_FRAME not found");
return functions;
}
const bool is64 = (type() == Header::CLASS::ELF64);
eh_frame_off = eh_frame_off - load_segment->file_offset();
SpanStream vs = load_segment->content();
vs.setpos(eh_frame_off);
if (vs.size() < 4 * sizeof(uint8_t)) {
LIEF_WARN("Failed to read EH frame header");
return functions;
}
// Read Eh Frame header
auto version = *vs.read<uint8_t>();
auto eh_frame_ptr_enc = *vs.read<uint8_t>(); // How pointers are encoded
auto fde_count_enc = *vs.read<uint8_t>();
auto table_enc = *vs.read<uint8_t>();
auto res_eh_frame_ptr = vs.read_dwarf_encoded(eh_frame_ptr_enc);
if (!res_eh_frame_ptr) {
LIEF_ERR("Failed to decode eh_frame_ptr_enc");
return functions;
}
auto eh_frame_ptr = *res_eh_frame_ptr;
int64_t fde_count = -1;
if (static_cast<dwarf::EH_ENCODING>(fde_count_enc) != dwarf::EH_ENCODING::OMIT) {
auto res_count = vs.read_dwarf_encoded(fde_count_enc);
if (!res_count) {
return functions;
}
fde_count = *res_count;
}
if (version != 1) {
LIEF_WARN(
"EH frame header version is not 1 ({:d}), structure may be corrupted",
version
);
}
if (fde_count < 0) {
LIEF_WARN("fde_count is corrupted (negative value)");
fde_count = 0;
}
LIEF_DEBUG(" eh_frame_ptr_enc: {:#x}", static_cast<uint32_t>(eh_frame_ptr_enc));
LIEF_DEBUG(" fde_count_enc: {:#x}", static_cast<uint32_t>(fde_count_enc));
LIEF_DEBUG(" table_enc: {:#x}", static_cast<uint32_t>(table_enc));
LIEF_DEBUG(" eh_frame_ptr: {:#x}", static_cast<uint32_t>(eh_frame_ptr));
LIEF_DEBUG(" fde_count: {:#x}", static_cast<uint32_t>(fde_count));
auto table_bias = static_cast<dwarf::EH_ENCODING>(table_enc & 0xF0);
for (size_t i = 0; i < static_cast<size_t>(fde_count); ++i) {
// Read Function address / FDE address within the
// Binary search table
auto res_init_loc = vs.read_dwarf_encoded(table_enc);
if (!res_init_loc) {
LIEF_ERR("Failed to read DWARF initial_location");
return functions;
}
uint32_t initial_location = *res_init_loc;
auto res_address = vs.read_dwarf_encoded(table_enc);
if (!res_address) {
LIEF_ERR("Failed to read DWARF address");
return functions;
}
uint32_t address = *res_address;
uint64_t bias = 0;
switch (table_bias) {
case dwarf::EH_ENCODING::PCREL:
{
bias = (eh_frame_rva + vs.pos());
break;
}
case dwarf::EH_ENCODING::TEXTREL:
{
LIEF_WARN("Unsupported EH_ENCODING::TEXTREL");
break;
}
case dwarf::EH_ENCODING::DATAREL:
{
bias = eh_frame_rva;
break;
}
case dwarf::EH_ENCODING::FUNCREL:
{
LIEF_WARN("Unsupported EH_ENCODING::FUNCREL");
break;
}
case dwarf::EH_ENCODING::ALIGNED:
{
LIEF_WARN("Unsupported EH_ENCODING::ALIGNED");
break;
}
default:
{
LIEF_WARN("Unsupported encoding");
break;
}
}
if (bias == 0) {
break;
}
initial_location += bias;
address += bias;
LIEF_DEBUG("Initial location: {:#x}", initial_location);
LIEF_DEBUG("Address: {:#x}", address);
LIEF_DEBUG("Bias: {:#x}", bias);
const size_t saved_pos = vs.pos();
LIEF_DEBUG("Navigating to eh_frame_off + address - bias: {:#x}",
eh_frame_off + address - bias);
// Go to the FDE structure
vs.setpos(eh_frame_off + address - bias);
{
// Beginning of the FDE structure (to continue)
auto res_fde_length = vs.read<uint32_t>();
if (!res_fde_length) {
LIEF_ERR("Failed to read FDE length");
vs.setpos(saved_pos);
continue;
}
uint64_t fde_length = *res_fde_length;
if (fde_length == static_cast<uint32_t>(-1)) {
if (vs.can_read<uint64_t>()) {
fde_length = *vs.read<uint64_t>();
}
}
auto cie_pointer = vs.read<uint32_t>();
if (!cie_pointer) {
LIEF_ERR("Failed to read CIE pointer");
vs.setpos(saved_pos);
continue;
}
if (*cie_pointer == 0) {
LIEF_DEBUG("CIE pointer is null");
vs.setpos(saved_pos);
continue;
}
const uint32_t cie_offset = vs.pos() - *cie_pointer - sizeof(uint32_t);
LIEF_DEBUG("fde_length@{:#x}: {:#x}", address - bias, fde_length);
LIEF_DEBUG("cie_pointer: {:#x}", *cie_pointer);
LIEF_DEBUG("cie_offset: {:#x}", cie_offset);
// Go to CIE structure
// uint8_t augmentation_data =
// static_cast<uint8_t>(dwarf::EH_ENCODING::OMIT);
const size_t saved_pos = vs.pos();
uint8_t augmentation_data = 0;
vs.setpos(cie_offset);
{
auto res_cie_length = vs.read<uint32_t>();
if (!res_cie_length) {
LIEF_ERR("Failed to read CIE length");
return functions;
}
uint64_t cie_length = *res_cie_length;
if (cie_length == static_cast<uint32_t>(-1)) {
if (vs.can_read<uint64_t>()) {
cie_length = *vs.read<uint64_t>();
}
}
auto cie_id = vs.read<uint32_t>();
if (!cie_id) {
LIEF_ERR("Failed to read CIE ID");
return functions;
}
auto version = vs.read<uint8_t>();
if (!version) {
LIEF_ERR("Failed to read CIE version");
return functions;
}
if (*cie_id != 0) {
LIEF_WARN("Unexpected CIE ID ({:d}), expected 0", *cie_id);
}
if (*version != 1 && *version != 3 && *version != 4) {
LIEF_WARN("Unexpected CIE version ({:d}), expected 1, 3 or 4", *version);
}
LIEF_DEBUG("cie_length: {:#x}", cie_length);
LIEF_DEBUG("ID: {:d}", *cie_id);
LIEF_DEBUG("Version: {:d}", *version);
auto res_cie_augmentation_string = vs.read_string();
if (!res_cie_augmentation_string) {
LIEF_ERR("Failed to read CIE augmentation string");
return functions;
}
std::string cie_augmentation_string =
std::move(*res_cie_augmentation_string);
LIEF_DEBUG("CIE augmentation: {}", cie_augmentation_string);
if (cie_augmentation_string.find("eh") != std::string::npos) {
if (is64) {
/* uint64_t eh_data = */ vs.read<uint64_t>();
} else {
/* uint32_t eh_data = */ vs.read<uint32_t>();
}
}
if (*version >= 4) {
/* uint8_t address_size = */ vs.read<uint8_t>();
/* uint8_t segment_selector_size = */ vs.read<uint8_t>();
}
/* uint64_t code_alignment = */ vs.read_uleb128();
/* int64_t data_alignment = */ vs.read_sleb128();
if (*version == 1) {
/* uint8_t return_address_register = */ vs.read<uint8_t>();
} else {
/* uint64_t return_address_register = */ vs.read_uleb128();
}
if (cie_augmentation_string.find('z') != std::string::npos) {
/* int64_t augmentation_length = */ vs.read_uleb128();
}
LIEF_DEBUG("cie_augmentation_string: {}", cie_augmentation_string);
if (!cie_augmentation_string.empty() && cie_augmentation_string[0] == 'z')
{
if (cie_augmentation_string.find('R') != std::string::npos) {
auto aug_data = vs.read<uint8_t>();
if (!aug_data) {
LIEF_ERR("Failed to read augmentation data");
return functions;
}
augmentation_data = *aug_data;
} else {
LIEF_WARN("Unsupported augmentation string '{}'",
cie_augmentation_string);
}
}
}
LIEF_DEBUG("Augmentation data: {:#x}",
static_cast<uint32_t>(augmentation_data));
// Go back to FDE Structure
vs.setpos(saved_pos);
auto res = vs.read_dwarf_encoded(augmentation_data);
if (!res) {
LIEF_ERR("Failed to read DWARF-encoded function begin");
return functions;
}
int32_t function_begin = eh_frame_rva + vs.pos() + *res;
res = vs.read_dwarf_encoded(augmentation_data);
if (!res) {
LIEF_ERR("Failed to read DWARF-encoded size");
return functions;
}
int32_t size = *res;
// Create the function
Function f{static_cast<uint64_t>(initial_location + imagebase())};
f.size(size);
functions.push_back(std::move(f));
LIEF_DEBUG("PC@{:#x}:{:#x}", function_begin, size);
}
vs.setpos(saved_pos);
}
return functions;
}
LIEF::Binary::functions_t Binary::functions() const {
static const auto func_cmd = [](const Function& lhs, const Function& rhs) {
return lhs.address() < rhs.address();
};
std::set<Function, decltype(func_cmd)> functions_set(func_cmd);
LIEF::Binary::functions_t eh_frame_functions = this->eh_frame_functions();
LIEF::Binary::functions_t armexid_functions = this->armexid_functions();
LIEF::Binary::functions_t ctors = ctor_functions();
LIEF::Binary::functions_t dtors = dtor_functions();
for (const Symbol& s : symbols()) {
if (s.type() == Symbol::TYPE::FUNC && s.value() > 0) {
Function f{s.name(), s.value()};
f.size(s.size());
functions_set.insert(f);
}
}
std::move(ctors.begin(), ctors.end(),
std::inserter(functions_set, functions_set.end()));
std::move(dtors.begin(), dtors.end(),
std::inserter(functions_set, functions_set.end()));
std::move(eh_frame_functions.begin(), eh_frame_functions.end(),
std::inserter(functions_set, functions_set.end()));
std::move(armexid_functions.begin(), armexid_functions.end(),
std::inserter(functions_set, functions_set.end()));
return {functions_set.begin(), functions_set.end()};
}
uint64_t Binary::eof_offset() const {
uint64_t last_offset_sections = 0;
for (const std::unique_ptr<Section>& section : sections_) {
if (section->type() != Section::TYPE::NOBITS && !section->is_frame()) {
last_offset_sections =
std::max<uint64_t>(section->file_offset() + section->size(),
last_offset_sections);
}
}
const uint64_t section_header_size =
type() == Header::CLASS::ELF64 ? sizeof(typename details::ELF64::Elf_Shdr) :
sizeof(typename details::ELF32::Elf_Shdr);
const uint64_t segment_header_size =
type() == Header::CLASS::ELF64 ? sizeof(typename details::ELF64::Elf_Phdr) :
sizeof(typename details::ELF32::Elf_Phdr);
const uint64_t end_sht_table =
header().section_headers_offset() + sections_.size() * section_header_size;
const uint64_t end_phdr_table =
header().program_headers_offset() + segments_.size() * segment_header_size;
last_offset_sections =
std::max<uint64_t>({last_offset_sections, end_sht_table, end_phdr_table});
const uint64_t last_offset_segments = last_offset_segment();
const uint64_t last_offset =
std::max<uint64_t>(last_offset_sections, last_offset_segments);
return last_offset;
}
std::string Binary::shstrtab_name() const {
const Header& hdr = header();
const size_t shstrtab_idx = hdr.section_name_table_idx();
if (shstrtab_idx < sections_.size()) {
return sections_[shstrtab_idx]->name();
}
return ".shstrtab";
}
uint64_t Binary::relocate_phdr_table(PHDR_RELOC type) {
switch (type) {
case PHDR_RELOC::PIE_SHIFT: return relocate_phdr_table_pie();
case PHDR_RELOC::SEGMENT_GAP: return relocate_phdr_table_v1();
case PHDR_RELOC::BSS_END: return relocate_phdr_table_v2();
case PHDR_RELOC::BINARY_END: return relocate_phdr_table_v3();
case PHDR_RELOC::AUTO:
default: return relocate_phdr_table_auto();
}
}
uint64_t Binary::relocate_phdr_table_auto() {
if (phdr_reloc_info_.new_offset > 0) {
// Already relocated
return phdr_reloc_info_.new_offset;
}
const bool has_phdr_s = has(Segment::TYPE::PHDR);
const bool has_interp_s = has(Segment::TYPE::INTERP);
const bool has_soname = has(DynamicEntry::TAG::SONAME);
const bool is_dyn = header_.file_type() == Header::FILE_TYPE::DYN;
const bool is_exec = header_.file_type() == Header::FILE_TYPE::EXEC;
const bool has_ep = entrypoint() > 0;
uint64_t offset = 0;
if (is_dyn && (has_phdr_s || has_interp_s || has_soname)) {
if (offset = relocate_phdr_table_pie(); offset > 0) {
return offset;
}
LIEF_ERR("Failed to relocate PHDR table for this PIE binary");
}
if (is_dyn && !(has_phdr_s || has_interp_s) && !has_ep) {
// See libm-ubuntu24.so
if (offset = relocate_phdr_table_pie(); offset > 0) {
return offset;
}
}
/* This is typically static binaries */
const bool is_valid_for_v3 = (is_dyn || is_exec) && !has_phdr_s && !has_interp_s;
if (is_valid_for_v3) {
LIEF_DEBUG("Try v3 relocator");
offset = relocate_phdr_table_v3();
if (offset > 0) {
return offset;
}
}
LIEF_DEBUG("Try v1 relocator");
if (offset = relocate_phdr_table_v1(); offset == 0) {
LIEF_DEBUG("Try v2 relocator");
if (offset = relocate_phdr_table_v2(); offset == 0) {
LIEF_ERR("Failed to relocate the PHDR table. "
"Please consider opening an issue");
return 0;
}
}
return offset;
}
uint64_t Binary::relocate_phdr_table_pie() {
if (phdr_reloc_info_.new_offset > 0) {
// Already relocated
return phdr_reloc_info_.new_offset;
}
LIEF_DEBUG("Relocating PHDR Table (PIE)");
// --------------------------------------
// Part 1: Make spaces for a new PHDR
// --------------------------------------
const uint64_t phdr_offset = header().program_headers_offset();
uint64_t phdr_size = 0;
if (type() == Header::CLASS::ELF32) {
phdr_size = sizeof(details::ELF32::Elf_Phdr);
}
if (type() == Header::CLASS::ELF64) {
phdr_size = sizeof(details::ELF64::Elf_Phdr);
}
const uint64_t from = phdr_offset + phdr_size * segments_.size();
/*
* We could use a smaller shift value but 0x1000 eases the
* support of corner cases like ADRP on AArch64.
*
* Note: 0x1000 enables to add up to 73 segments which should be enough
* in most of the cases
*
* e.g:
* const ARCH arch = header_.machine_type();
* uint64_t shift = align(phdr_size, 0x10);
* if (arch == ARCH::AARCH64 || arch == ARCH::ARM) {
* shift = 0x1000;
* phdr_reloc_info_.new_offset = phdr_offset;
* phdr_reloc_info_.nb_segments = shift / phdr_size -
* header_.numberof_segments();
* }
*/
static constexpr size_t shift = 0x1000;
phdr_reloc_info_.new_offset = from;
phdr_reloc_info_.nb_segments = shift / phdr_size - header_.numberof_segments();
auto alloc = datahandler_->make_hole(from, shift);
if (!alloc) {
LIEF_ERR("Allocation failed");
return 0;
}
LIEF_DEBUG("Header shift: {:#x}", shift);
header().section_headers_offset(header().section_headers_offset() + shift);
shift_sections(from, shift);
shift_segments(from, shift);
// Patch segment size for the segment which contains the new segment
for (std::unique_ptr<Segment>& segment : segments_) {
if (segment->file_offset() <= from &&
from <= (segment->file_offset() + segment->physical_size()))
{
segment->virtual_size(segment->virtual_size() + shift);
segment->physical_size(segment->physical_size() + shift);
}
}
shift_dynamic_entries(from, shift);
shift_symbols(from, shift);
shift_relocations(from, shift);
if (type() == Header::CLASS::ELF32) {
fix_got_entries<details::ELF32>(from, shift);
} else {
fix_got_entries<details::ELF64>(from, shift);
}
if (header().entrypoint() >= from) {
header().entrypoint(header().entrypoint() + shift);
}
return phdr_offset;
}
/*
* This function relocates the segments table AT THE END of the binary.
* It only works if the binary is not PIE and does not contain a PHDR segment.
* In addition, it requires to expand the bss-like segments so it might
* strongly increase the final size of the binary.
*/
uint64_t Binary::relocate_phdr_table_v3() {
// Reserve space for 10 user's segments
static constexpr size_t USER_SEGMENTS = 10;
uint64_t last_off = 0;
for (const std::unique_ptr<Segment>& segment : segments_) {
if (segment != nullptr && segment->is_load()) {
last_off =
std::max(last_off, segment->physical_size() + segment->file_offset());
}
}
uint64_t last_off_aligned = align(last_off, page_size());
if (phdr_reloc_info_.new_offset > 0) {
return phdr_reloc_info_.new_offset;
}
LIEF_DEBUG("Running v3 (file end) PHDR relocator");
Header& header = this->header();
const uint64_t phdr_size = type() == Header::CLASS::ELF32 ?
sizeof(details::ELF32::Elf_Phdr) :
sizeof(details::ELF64::Elf_Phdr);
LIEF_DEBUG("Moving segment table at the end of the binary ({:#012x})",
last_off_aligned);
phdr_reloc_info_.new_offset = last_off_aligned;
header.program_headers_offset(last_off_aligned);
const size_t new_segtbl_sz =
(header.numberof_segments() + USER_SEGMENTS) * phdr_size;
const uint64_t delta = last_off_aligned - last_off + new_segtbl_sz;
shift_sections(last_off, delta);
uint64_t sections_tbl_off = header.section_headers_offset() + delta;
header.section_headers_offset(sections_tbl_off);
auto alloc = datahandler_->make_hole(phdr_reloc_info_.new_offset, new_segtbl_sz);
if (!alloc) {
LIEF_ERR("Allocation failed");
return 0;
}
// Add a segment that wraps this new PHDR
auto phdr_load_segment = std::make_unique<Segment>();
phdr_load_segment->type(Segment::TYPE::LOAD);
phdr_load_segment->file_offset(phdr_reloc_info_.new_offset);
phdr_load_segment->physical_size(new_segtbl_sz);
phdr_load_segment->virtual_size(new_segtbl_sz);
phdr_load_segment->virtual_address(imagebase() + virtual_size());
phdr_load_segment->physical_address(phdr_load_segment->virtual_address());
phdr_load_segment->alignment(0x1000);
phdr_load_segment->add(Segment::FLAGS::R);
phdr_load_segment->datahandler_ = datahandler_.get();
DataHandler::Node new_node{phdr_reloc_info_.new_offset, new_segtbl_sz,
DataHandler::Node::SEGMENT};
datahandler_->add(new_node);
const auto it_new_place =
std::find_if(segments_.rbegin(), segments_.rend(),
[](const auto& s) { return s->is_load(); });
if (it_new_place == segments_.rend()) {
segments_.push_back(std::move(phdr_load_segment));
} else {
const size_t idx = std::distance(segments_.begin(), it_new_place.base());
segments_.insert(segments_.begin() + idx, std::move(phdr_load_segment));
}
header.numberof_segments(header.numberof_segments() + 1);
phdr_reloc_info_.nb_segments = USER_SEGMENTS - /* For the PHDR LOAD */ 1;
return phdr_reloc_info_.new_offset;
}
/*
* This function relocates the phdr table in the case where:
* 1. The binary is NOT pie
* 2. There is no gap between two adjacent segments (cf. relocate_phdr_table_v1)
*
* It performs the following modifications:
* 1. Expand the .bss section such as virtual size == file size
* 2. Relocate the phdr table right after the (expanded) bss section
* 3. Add a LOAD segment to wrap the new phdr location
*
*/
uint64_t Binary::relocate_phdr_table_v2() {
static constexpr size_t USER_SEGMENTS =
10; // We reserve space for 10 user's segments
if (phdr_reloc_info_.new_offset > 0) {
return phdr_reloc_info_.new_offset;
}
LIEF_DEBUG("Running v2 PHDR relocator");
Header& header = this->header();
const uint64_t phdr_size = type() == Header::CLASS::ELF32 ?
sizeof(details::ELF32::Elf_Phdr) :
sizeof(details::ELF64::Elf_Phdr);
std::vector<Segment*> load_seg;
Segment* bss_segment = nullptr;
size_t bss_cnt = 0;
for (std::unique_ptr<Segment>& segment : segments_) {
if (segment->is_load()) {
load_seg.push_back(segment.get());
if (segment->physical_size() < segment->virtual_size()) {
bss_segment = segment.get();
++bss_cnt;
}
}
}
if (bss_cnt != 1 || bss_segment == nullptr) {
LIEF_ERR("Expected exactly 1 BSS-like segment");
return 0;
}
// Expand the .bss area. This is required since the mapped bss area
// needs to be zero-initialized.
const uint64_t original_psize = bss_segment->physical_size();
/*
* To compute the location of the new segments table,
* we have to deal with some constraints:
* 1. The new location virtual address (VA) must follow:
* VA = image_base + offset (1)
* 2. The .bss area, which does not have a physical
* representation in the file, must be 0. Therefore,
* we can't use this space to put our new segment table.
* Consequently, we need to expand it in the file. (2)
* 3. The req 2. is not enough and need to verify that the virtual address
* is suitable.
*
* Let's consider this layout:
* LOAD_1 0x000000 0x0000000000400000 0x000be4 0x000be4
* LOAD_2 0x000e00 0x0000000000401e00 0x000230 0x000238
* We have to relocate the PHDR at the end of the file.
* Because of Req[2.], we have to expand the last LOAD bss-like segment:
* LOAD_2.file_size = LOAD_2.virtual_size = 0x000230
* So our new segment table **could** be located at:
* LOAD_2.file_offset + LOAD_2.file_size == 0x1030
* Because of Req[1.] it would set the virtual address:
* Imagebase[0x400000] + 0x1030 = 0x401030
* BUT if the size of the new segment table is too large,
* it could override the next virtual address which is associated with LOAD_2.
* LOAD_NEW.VA = 0x401030 < LOAD_2.VA = 0x401e00
* Moreover, it might raise page alignment issues.
*
* Therefore, we **can't take** 0x1030 as offset for the new
* segment table.
* To avoid virtual overlap while still keeping Req[1.],
* we need to use: LOAD_2.virtual_address - Imagebase[0x400000] +
* LOAD_2.virtual_size
* \ /
* ------------ Offset --------------------/
* which should be aligned on a page size to avoid error.
*
* The issue https://github.com/lief-project/LIEF/issues/671
* is a good example of what could go wrong.
*
* |WARNING|
* This modification can increase the binary size drastically
* if it contains a large BSS section.
*
* (1) This is enforced by the Linux loader which uses
* this relationship to compute the module base address
* (2) man elf:
* .bss This section holds uninitialized data that contributes to
* the program's memory image. By definition, the system
* initializes the data with zeros when the program begins to
* run. This section is of type SHT_NOBITS. The attribute
* types are SHF_ALLOC and SHF_WRITE.
*/
const uint64_t new_phdr_offset =
align(bss_segment->virtual_address() - imagebase() +
bss_segment->virtual_size(),
0x1000);
const size_t nb_segments = header.numberof_segments() +
/* custom PT_LOAD */ 1 + USER_SEGMENTS;
const uint64_t new_phdr_size = nb_segments * phdr_size;
phdr_reloc_info_.new_offset = new_phdr_offset;
header.program_headers_offset(new_phdr_offset);
size_t delta_pa = (bss_segment->virtual_size() - bss_segment->physical_size());
phdr_reloc_info_.nb_segments = USER_SEGMENTS;
auto alloc = datahandler_->make_hole(
bss_segment->file_offset() + bss_segment->physical_size(), delta_pa
);
if (!alloc) {
LIEF_ERR("Allocation failed");
return 0;
}
bss_segment->physical_size(bss_segment->virtual_size());
// Create a LOAD segment that wraps the new location of the PT_PHDR.
auto new_segment_ptr = std::make_unique<Segment>();
Segment* nsegment_addr = new_segment_ptr.get();
nsegment_addr->type(Segment::TYPE::LOAD);
nsegment_addr->virtual_size(new_phdr_size);
nsegment_addr->physical_size(new_phdr_size);
nsegment_addr->virtual_address(imagebase() + phdr_reloc_info_.new_offset);
nsegment_addr->physical_address(imagebase() + phdr_reloc_info_.new_offset);
nsegment_addr->flags(Segment::FLAGS::R);
nsegment_addr->alignment(0x1000);
nsegment_addr->file_offset(phdr_reloc_info_.new_offset);
nsegment_addr->datahandler_ = datahandler_.get();
DataHandler::Node new_node{phdr_reloc_info_.new_offset, new_phdr_size,
DataHandler::Node::SEGMENT};
datahandler_->add(new_node);
const auto it_new_segment_place =
std::find_if(segments_.rbegin(), segments_.rend(),
[nsegment_addr](const std::unique_ptr<Segment>& s) {
return s->type() == nsegment_addr->type();
});
if (it_new_segment_place == segments_.rend()) {
segments_.push_back(std::move(new_segment_ptr));
} else {
const size_t idx =
std::distance(segments_.begin(), it_new_segment_place.base());
segments_.insert(segments_.begin() + idx, std::move(new_segment_ptr));
}
this->header().numberof_segments(this->header().numberof_segments() + 1);
const auto it_segment_phdr =
std::find_if(segments_.begin(), segments_.end(),
[](const std::unique_ptr<Segment>& s) { return s->is_phdr(); });
if (it_segment_phdr != segments_.end()) {
const std::unique_ptr<Segment>& phdr_segment = *it_segment_phdr;
phdr_segment->file_offset(nsegment_addr->file_offset());
phdr_segment->virtual_address(nsegment_addr->virtual_address());
phdr_segment->physical_address(nsegment_addr->physical_address());
phdr_segment->clear();
}
// Shift components that come after the bss offset
uint64_t from = bss_segment->file_offset() + original_psize;
uint64_t shift = delta_pa + nb_segments * phdr_size;
this->header().section_headers_offset(this->header().section_headers_offset() +
shift);
// Shift sections
for (const std::unique_ptr<Section>& section : sections_) {
if (section->is_frame()) {
continue;
}
if (section->file_offset() >= from && section->type() != Section::TYPE::NOBITS)
{
LIEF_DEBUG("[BEFORE] {}", to_string(*section));
section->file_offset(section->file_offset() + shift);
if (section->virtual_address() > 0) {
section->virtual_address(section->virtual_address() + shift);
}
LIEF_DEBUG("[AFTER ] {}", to_string(*section));
}
}
return phdr_reloc_info_.new_offset;
}
uint64_t Binary::relocate_phdr_table_v1() {
// The minimum number of segments that need to be available
// to consider this relocation valid
static constexpr auto MIN_POTENTIAL_SIZE = 2;
// Check if we already relocated the segment table in the largest segment's cave
if (phdr_reloc_info_.new_offset > 0) {
return phdr_reloc_info_.new_offset;
}
LIEF_DEBUG("Running v1 (gap) PHDR relocator");
Header& header = this->header();
const uint64_t phdr_size = type() == Header::CLASS::ELF32 ?
sizeof(details::ELF32::Elf_Phdr) :
sizeof(details::ELF64::Elf_Phdr);
const auto it_segment_phdr =
std::find_if(segments_.begin(), segments_.end(),
[](const std::unique_ptr<Segment>& s) { return s->is_phdr(); });
std::vector<Segment*> load_seg;
for (std::unique_ptr<Segment>& segment : segments_) {
if (segment->is_load()) {
load_seg.push_back(segment.get());
}
}
// Take the 2 adjacent segments that have the largest "cave"
Segment* seg_to_extend = nullptr;
Segment* next_to_extend = nullptr;
size_t potential_size = 0;
const size_t nb_loads = load_seg.size();
// This function requires at least 2 segments
if (nb_loads <= 1) {
return 0;
}
for (size_t i = 0; i < (nb_loads - 1); ++i) {
Segment* current = load_seg[i];
// Skip bss-like segments
if (current->virtual_size() != current->physical_size()) {
LIEF_DEBUG("Skipping .bss like segment: {}@{:#x}:{:#x}",
to_string(current->type()), current->virtual_address(),
current->virtual_size());
continue;
}
Segment* adjacent = load_seg[i + 1];
const int64_t gap = adjacent->file_offset() -
(current->file_offset() + current->physical_size());
if (gap <= 0) {
continue;
}
const size_t nb_seg_gap = gap / phdr_size;
LIEF_DEBUG("Gap between {:d} <-> {:d}: {:x} ({:d} segments)", i, i + 1, gap,
nb_seg_gap);
if (nb_seg_gap > potential_size) {
seg_to_extend = current;
next_to_extend = adjacent;
potential_size = nb_seg_gap;
}
}
if (seg_to_extend == nullptr || next_to_extend == nullptr) {
LIEF_DEBUG("No suitable segment found (v1)");
return 0;
}
if (potential_size < (header.numberof_segments() + MIN_POTENTIAL_SIZE)) {
LIEF_DEBUG("Available segment count too small ({} vs {})", potential_size,
header.numberof_segments() + MIN_POTENTIAL_SIZE);
return 0;
}
LIEF_DEBUG("Segment selected for the extension: {}@{:#x}:{:#x}",
to_string(seg_to_extend->type()), seg_to_extend->virtual_address(),
seg_to_extend->virtual_size());
LIEF_DEBUG("Adjacent segment selected for the extension: {}@{:#x}:{:#x}",
to_string(next_to_extend->type()), next_to_extend->virtual_address(),
next_to_extend->virtual_size());
// Extend the segment that wraps the next PHDR table so that it is contiguous
// with the next segment.
int64_t delta = next_to_extend->file_offset() -
(seg_to_extend->file_offset() + seg_to_extend->physical_size());
if (delta <= 0) {
return 0;
}
const size_t nb_segments = delta / phdr_size - header.numberof_segments();
if (nb_segments < header.numberof_segments()) {
LIEF_DEBUG("Binary layout prevents segment table relocation (v1). "
"Need at least {} segments but only {} available.",
header.numberof_segments(), nb_segments);
phdr_reloc_info_.clear();
return 0;
}
// New values
const uint64_t new_phdr_offset =
seg_to_extend->file_offset() + seg_to_extend->physical_size();
const size_t alignment = align(new_phdr_offset, ptr_size()) - new_phdr_offset;
phdr_reloc_info_.new_offset = new_phdr_offset + alignment;
header.program_headers_offset(phdr_reloc_info_.new_offset);
phdr_reloc_info_.nb_segments = nb_segments;
seg_to_extend->physical_size(seg_to_extend->physical_size() + delta);
seg_to_extend->virtual_size(seg_to_extend->virtual_size() + delta);
if (it_segment_phdr != segments_.end()) {
const std::unique_ptr<Segment>& phdr_segment = *it_segment_phdr;
// Update the PHDR segment with our values
const uint64_t base =
seg_to_extend->virtual_address() - seg_to_extend->file_offset();
phdr_segment->file_offset(phdr_reloc_info_.new_offset);
phdr_segment->virtual_address(base + phdr_segment->file_offset());
phdr_segment->physical_address(phdr_segment->virtual_address());
LIEF_DEBUG("{}@{:#x}:{:#x}", to_string(phdr_segment->type()),
phdr_segment->virtual_address(), phdr_segment->virtual_size());
// Clear PHDR segment
phdr_segment->physical_size(delta - alignment);
phdr_segment->virtual_size(delta - alignment);
phdr_segment->clear();
}
return phdr_reloc_info_.new_offset;
}
std::vector<uint64_t>
Binary::get_relocated_dynamic_array(DynamicEntry::TAG tag) const {
const DynamicEntry* entry = get(tag);
if (entry == nullptr || !DynamicEntryArray::classof(entry)) {
return {};
}
const auto& entry_array = static_cast<const DynamicEntryArray&>(*entry);
std::vector<uint64_t> result = entry_array.array();
const uint64_t base_addr = entry_array.value();
const size_t ptr_size =
(type_ == Header::CLASS::ELF64) ? sizeof(uint64_t) : sizeof(uint32_t);
const uint64_t end_addr = base_addr + result.size() * ptr_size;
for (const Relocation& reloc : dynamic_relocations()) {
const uint64_t addr = reloc.address();
bool in_range = base_addr <= addr && addr < end_addr;
if (!in_range) {
continue;
}
const size_t idx = (addr - base_addr) / ptr_size;
if (idx >= result.size()) {
continue;
}
if (reloc.is_relative()) {
result[idx] = reloc.addend();
}
}
return result;
}
bool Binary::is_targeting_android() const {
static constexpr auto ANDROID_DT = {
DynamicEntry::TAG::ANDROID_REL_OFFSET, DynamicEntry::TAG::ANDROID_REL_SIZE,
DynamicEntry::TAG::ANDROID_REL, DynamicEntry::TAG::ANDROID_RELSZ,
DynamicEntry::TAG::ANDROID_RELA, DynamicEntry::TAG::ANDROID_RELASZ,
DynamicEntry::TAG::ANDROID_RELR, DynamicEntry::TAG::ANDROID_RELRSZ,
DynamicEntry::TAG::ANDROID_RELRENT, DynamicEntry::TAG::ANDROID_RELRCOUNT,
};
static constexpr auto ANDROID_NOTES = {
Note::TYPE::ANDROID_IDENT,
Note::TYPE::ANDROID_KUSER,
Note::TYPE::ANDROID_MEMTAG,
};
if (format_ == Binary::FORMATS::OAT) {
return true;
}
if (std::any_of(ANDROID_DT.begin(), ANDROID_DT.end(),
[this](DynamicEntry::TAG t) { return has(t); }))
{
return true;
}
if (std::any_of(ANDROID_NOTES.begin(), ANDROID_NOTES.end(),
[this](Note::TYPE t) { return has(t); }))
{
return true;
}
if (has_section(".note.android.ident")) {
return true;
}
const std::string& interp = interpreter();
if (interp == "/system/bin/linker64" || interp == "/system/bin/linker") {
return true;
}
return false;
}
Section* Binary::add_section(std::unique_ptr<Section> sec) {
Section* sec_ptr = sec.get();
const auto it_new_sec_place =
std::find_if(sections_.begin(), sections_.end(),
[sec_ptr](const std::unique_ptr<Section>& S) {
return S->type() != Section::TYPE::NOBITS &&
S->file_offset() > sec_ptr->file_offset();
});
if (it_new_sec_place == sections_.end()) {
sec_ptr = sections_.emplace_back(std::move(sec)).get();
} else {
size_t idx = std::distance(sections_.begin(), it_new_sec_place);
for (const auto& section : sections_) {
const uint32_t link = section->link();
if (link >= idx) {
section->link(link + 1);
}
}
if (header_.section_name_table_idx() >= idx) {
header_.section_name_table_idx(header_.section_name_table_idx() + 1);
}
sec_ptr = sections_.insert(it_new_sec_place, std::move(sec))->get();
}
return sec_ptr;
}
uint64_t Binary::page_size() const {
if (pagesize_ > 0) {
return pagesize_;
}
return LIEF::Binary::page_size();
}
const SymbolVersionRequirement*
Binary::find_version_requirement(const std::string& libname) const {
auto it = std::find_if(
symbol_version_requirements_.begin(), symbol_version_requirements_.end(),
[&libname](const std::unique_ptr<SymbolVersionRequirement>& symver) {
return symver->name() == libname;
}
);
if (it == symbol_version_requirements_.end()) {
return nullptr;
}
return (*it).get();
}
bool Binary::remove_version_requirement(const std::string& libname) {
auto it = std::find_if(
symbol_version_requirements_.begin(), symbol_version_requirements_.end(),
[&libname](const std::unique_ptr<SymbolVersionRequirement>& symver) {
return symver->name() == libname;
}
);
if (it == symbol_version_requirements_.end()) {
return false;
}
std::set<std::string> versions;
SymbolVersionRequirement* sym_ver_req = it->get();
auto aux = sym_ver_req->auxiliary_symbols();
std::transform(aux.begin(), aux.end(), std::inserter(versions, versions.begin()),
[](const SymbolVersionAuxRequirement& req) {
return req.name();
});
for (Symbol& sym : dynamic_symbols()) {
SymbolVersion* symver = sym.symbol_version();
if (symver == nullptr) {
continue;
}
if (const SymbolVersionAux* vers = symver->symbol_version_auxiliary();
vers != nullptr && versions.count(vers->name()))
{
symver->as_global();
}
}
symbol_version_requirements_.erase(it);
if (DynamicEntry* dt = get(DynamicEntry::TAG::VERNEEDNUM)) {
dt->value(symbol_version_requirements_.size());
}
return true;
}
std::ostream& Binary::print(std::ostream& os) const {
os << "Header {\n" << indent(LIEF::to_string(header()), 2) << "}\n";
{
const auto secs = sections();
if (!secs.empty()) {
os << fmt::format("Sections (#{})\n", secs.size());
for (size_t i = 0; i < secs.size(); ++i) {
os << fmt::format(" Section #{:02} {{\n", i)
<< indent(LIEF::to_string(secs[i]), 4) << " }\n";
}
}
}
{
const auto segs = segments();
if (!segs.empty()) {
os << fmt::format("Segments (#{})\n", segs.size());
for (size_t i = 0; i < segs.size(); ++i) {
os << fmt::format(" Segment #{:02} {{\n", i)
<< indent(LIEF::to_string(segs[i]), 4) << " }\n";
}
}
}
if (auto entries = dynamic_entries(); !entries.empty()) {
os << fmt::format("Dynamic Entries (#{})\n", entries.size());
for (const DynamicEntry& entry : entries) {
os << " " << entry << '\n';
}
}
if (auto syms = dynamic_symbols(); !syms.empty()) {
os << fmt::format("Dynamic Symbols (#{})\n", syms.size());
for (const Symbol& symbol : syms) {
os << " " << symbol << '\n';
}
}
if (auto syms = symtab_symbols(); !syms.empty()) {
os << fmt::format("Symtab Symbols (#{})\n", syms.size());
for (const Symbol& symbol : syms) {
os << " " << symbol << '\n';
}
}
if (auto vers = symbols_version(); !vers.empty()) {
os << fmt::format("Symbol Versions (#{})\n", vers.size());
for (const SymbolVersion& sv : vers) {
os << " " << sv << '\n';
}
}
if (auto defs = symbols_version_definition(); !defs.empty()) {
os << fmt::format("Symbol Version Definitions (#{})\n", defs.size());
for (const SymbolVersionDefinition& svd : defs) {
os << indent(LIEF::to_string(svd), 2);
}
}
if (auto reqs = symbols_version_requirement(); !reqs.empty()) {
os << fmt::format("Symbol Version Requirements (#{})\n", reqs.size());
for (const SymbolVersionRequirement& svr : reqs) {
os << indent(LIEF::to_string(svr), 2);
}
}
if (auto relocs = dynamic_relocations(); !relocs.empty()) {
os << fmt::format("Dynamic Relocations (#{})\n", relocs.size());
for (const Relocation& relocation : relocs) {
os << " " << relocation << '\n';
}
}
if (auto relocs = pltgot_relocations(); !relocs.empty()) {
os << fmt::format(".plt.got Relocations (#{})\n", relocs.size());
for (const Relocation& relocation : relocs) {
os << " " << relocation << '\n';
}
}
if (auto n = this->notes(); !n.empty()) {
os << fmt::format("Notes (#{})\n", n.size());
for (size_t i = 0; i < n.size(); ++i) {
os << fmt::format(" Note #{:02} {{\n", i)
<< indent(LIEF::to_string(n[i]), 4) << " }\n";
}
}
if (const GnuHash* gnu = gnu_hash()) {
os << "GNU Hash Table {\n" << indent(LIEF::to_string(*gnu), 2) << "}\n";
}
if (const SysvHash* sysv = sysv_hash()) {
os << "SYSV Hash Table {\n" << indent(LIEF::to_string(*sysv), 2) << "}\n";
}
return os;
}
Binary::~Binary() = default;
}