Files
LIEF/src/ELF/ExeLayout.hpp
T

1827 lines
58 KiB
C++

/* Copyright 2021 - 2026 R. Thomas
*
* 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.
*/
#ifndef LIEF_ELF_EXE_LAYOUT_H
#define LIEF_ELF_EXE_LAYOUT_H
#include "LIEF/ELF/Binary.hpp"
#include "LIEF/ELF/Builder.hpp"
#include "LIEF/ELF/DynamicEntryArray.hpp"
#include "LIEF/ELF/DynamicEntryAuxiliary.hpp"
#include "LIEF/ELF/DynamicEntryFilter.hpp"
#include "LIEF/ELF/DynamicEntryLibrary.hpp"
#include "LIEF/ELF/DynamicEntryRpath.hpp"
#include "LIEF/ELF/DynamicEntryRunPath.hpp"
#include "LIEF/ELF/DynamicSharedObject.hpp"
#include "LIEF/ELF/GnuHash.hpp"
#include "LIEF/ELF/Note.hpp"
#include "LIEF/ELF/Relocation.hpp"
#include "LIEF/ELF/Section.hpp"
#include "LIEF/ELF/Segment.hpp"
#include "LIEF/ELF/Symbol.hpp"
#include "LIEF/ELF/SymbolVersionAux.hpp"
#include "LIEF/ELF/SymbolVersionAuxRequirement.hpp"
#include "LIEF/ELF/SymbolVersionDefinition.hpp"
#include "LIEF/ELF/SymbolVersionRequirement.hpp"
#include "LIEF/ELF/SysvHash.hpp"
#include "LIEF/ELF/utils.hpp"
#include "LIEF/errors.hpp"
#include "LIEF/iostream.hpp"
#include "LIEF/visibility.h"
#include <algorithm>
#include <iterator>
#include "ELF/Structures.hpp"
#include "Layout.hpp"
#include "internal_utils.hpp"
#include "logging.hpp"
namespace LIEF::ELF {
inline Relocation::TYPE relative_from_arch(ARCH arch) {
using TYPE = Relocation::TYPE;
switch (arch) {
case ARCH::AARCH64: return TYPE::AARCH64_RELATIVE;
case ARCH::ARM: return TYPE::ARM_RELATIVE;
case ARCH::X86_64: return TYPE::X86_64_RELATIVE;
case ARCH::I386: return TYPE::X86_RELATIVE;
case ARCH::PPC: return TYPE::PPC_RELATIVE;
case ARCH::PPC64: return TYPE::PPC64_RELATIVE;
case ARCH::HEXAGON: return TYPE::HEX_RELATIVE;
default: return TYPE::UNKNOWN;
}
return TYPE::UNKNOWN;
}
/// Compute the size and the offset of the elements
/// needed to rebuild the ELF file.
class LIEF_LOCAL ExeLayout : public Layout {
public:
struct sym_verdef_info_t {
using list_names_t = std::vector<std::string>;
std::set<list_names_t> names_list;
std::unordered_map<const SymbolVersionDefinition*, const list_names_t*>
def_to_names;
std::unordered_map<const list_names_t*, size_t> names_offset;
};
using Layout::Layout;
ExeLayout(const ExeLayout&) = delete;
ExeLayout& operator=(const ExeLayout&) = delete;
ExeLayout(ExeLayout&&) = default;
ExeLayout& operator=(ExeLayout&&) = default;
template<class ELF_T>
size_t dynamic_size() {
// The size of the .dynamic / PT_DYNAMIC area
// is the number of elements times the size of each element (Elf64_Dyn or
// Elf32_Dyn)
using Elf_Dyn = typename ELF_T::Elf_Dyn;
return binary_->dynamic_entries_.size() * sizeof(Elf_Dyn);
}
template<class ELF_T>
size_t dynstr_size() {
// The .dynstr section contains:
// - library names (DT_NEEDED / DT_SONAME / DT_RPATH / DT_RUNPATH)
// - The symbol names from the .dynsym section
// - Names associated with:
// * Symbol definition
// * Symbol version requirement
// * Symbol version definition
if (!raw_dynstr_.empty()) {
return raw_dynstr_.size();
}
// Start with dynamic entries: NEEDED / SONAME etc.
vector_iostream raw_dynstr(should_swap());
raw_dynstr.write<uint8_t>(0);
std::vector<std::string_view> opt_list;
std::transform(binary_->dynamic_symbols_.begin(),
binary_->dynamic_symbols_.end(), std::back_inserter(opt_list),
[](const std::unique_ptr<Symbol>& sym) {
return static_cast<const Symbol&>(*sym).name();
});
for (std::unique_ptr<DynamicEntry>& entry : binary_->dynamic_entries_) {
switch (entry->tag()) {
case DynamicEntry::TAG::NEEDED:
{
opt_list.push_back(entry->as<DynamicEntryLibrary>()->name());
break;
}
case DynamicEntry::TAG::SONAME:
{
opt_list.push_back(entry->as<DynamicSharedObject>()->name());
break;
}
case DynamicEntry::TAG::AUXILIARY:
{
opt_list.push_back(entry->as<DynamicEntryAuxiliary>()->name());
break;
}
case DynamicEntry::TAG::FILTER:
{
opt_list.push_back(entry->as<DynamicEntryFilter>()->name());
break;
}
case DynamicEntry::TAG::RPATH:
{
opt_list.push_back(entry->as<DynamicEntryRpath>()->rpath());
break;
}
case DynamicEntry::TAG::RUNPATH:
{
opt_list.push_back(entry->as<DynamicEntryRunPath>()->runpath());
break;
}
default:
{
}
}
}
// Symbol definition
for (const SymbolVersionDefinition& svd :
binary_->symbols_version_definition())
{
sym_verdef_info_t::list_names_t aux_names;
auto saux = svd.symbols_aux();
aux_names.reserve(saux.size());
for (const SymbolVersionAux& sva : saux) {
aux_names.emplace_back(sva.name());
opt_list.push_back(sva.name());
}
auto res = verdef_info_.names_list.insert(std::move(aux_names));
verdef_info_.def_to_names[&svd] = &*res.first;
}
// Symbol version requirement
for (const SymbolVersionRequirement& svr :
binary_->symbols_version_requirement())
{
opt_list.push_back(svr.name());
for (const SymbolVersionAuxRequirement& svar : svr.auxiliary_symbols()) {
opt_list.push_back(svar.name());
}
}
size_t offset_counter = raw_dynstr.tellp();
std::vector<std::string> string_table_optimized = optimize(
opt_list, [](const std::string_view& name) { return name; },
offset_counter, &offset_name_map_
);
for (std::string_view name : string_table_optimized) {
raw_dynstr.write(name);
}
raw_dynstr.move(raw_dynstr_);
return raw_dynstr_.size();
}
template<class ELF_T>
size_t dynsym_size() {
using Elf_Sym = typename ELF_T::Elf_Sym;
return binary_->dynamic_symbols_.size() * sizeof(Elf_Sym);
}
template<class ELF_T>
size_t static_sym_size() {
using Elf_Sym = typename ELF_T::Elf_Sym;
return binary_->symtab_symbols_.size() * sizeof(Elf_Sym);
}
template<class ELF_T>
size_t dynamic_arraysize(DynamicEntry::TAG tag) {
using uint = typename ELF_T::uint;
DynamicEntry* entry = binary_->get(tag);
if (entry == nullptr || !DynamicEntryArray::classof(entry)) {
return 0;
}
return entry->as<const DynamicEntryArray&>()->size() * sizeof(uint);
}
template<class ELF_T>
size_t note_size() {
if (!raw_notes_.empty()) {
return raw_notes_.size();
}
vector_iostream raw_notes(should_swap());
for (const Note& note : binary_->notes()) {
size_t pos = raw_notes.tellp();
// First we have to write the length of the Note's name
const auto namesz = static_cast<uint32_t>(note.name().size() + 1);
raw_notes.write<uint32_t>(namesz);
// Then the length of the Note's description
const auto descsz = static_cast<uint32_t>(note.description().size());
raw_notes.write<uint32_t>(descsz);
// Then the note's type
const uint32_t type = note.original_type();
raw_notes.write<uint32_t>(type);
// Then we write the note's name
raw_notes.write(note.name());
// Alignment
raw_notes.align(sizeof(uint32_t), 0);
// description content (manipulated in 4 byte/uint32_t chunks)
span<const uint8_t> description = note.description();
const auto* desc_ptr = reinterpret_cast<const uint32_t*>(description.data());
size_t i = 0;
for (; i < description.size() / sizeof(uint32_t); i++) {
raw_notes.write<uint32_t>(desc_ptr[i]);
}
if (description.size() % sizeof(uint32_t) != 0) {
uint32_t padded = 0;
auto* ptr = reinterpret_cast<uint8_t*>(&padded);
memcpy(ptr, desc_ptr + i, description.size() % sizeof(uint32_t));
raw_notes.write<uint32_t>(padded);
}
notes_off_map_.emplace(&note, pos);
}
raw_notes.move(raw_notes_);
return raw_notes_.size();
}
template<class ELF_T>
size_t symbol_sysv_hash_size() {
const SysvHash* sysv_hash = binary_->sysv_hash();
if (sysv_hash == nullptr) {
return 0;
}
nchain_ = sysv_hash->nchain();
if (nchain_ != binary_->dynamic_symbols_.size()) {
LIEF_DEBUG("nchain of .hash section changed from {:d} to {:d}", nchain_,
binary_->dynamic_symbols_.size());
nchain_ = binary_->dynamic_symbols_.size();
}
return (sysv_hash->nbucket() + nchain_ + /* header */ 2) * sizeof(uint32_t);
}
template<class ELF_T>
size_t section_table_size() {
using Elf_Shdr = typename ELF_T::Elf_Shdr;
return binary_->sections_.size() * sizeof(Elf_Shdr);
}
template<class ELF_T>
size_t symbol_gnu_hash_size() {
// Mainly inspired by
// * https://github.com/llvm-mirror/lld/blob/master/ELF/SyntheticSections.cpp
//
// Checking is performed here:
// * https://github.com/lattera/glibc/blob/a2f34833b1042d5d8eeb263b4cf4caaea138c4ad/elf/dl-lookup.c#L228
//
// See also:
// * p.9, https://www.akkadia.org/drepper/dsohowto.pdf
using uint = typename ELF_T::uint;
if (!raw_gnu_hash_.empty()) {
return raw_gnu_hash_.size();
}
uint32_t first_exported_symbol_index = 0;
if (new_symndx_ >= 0) {
first_exported_symbol_index = new_symndx_;
} else {
LIEF_WARN("First exported symbol index not set");
}
const GnuHash* gnu_hash = binary_->gnu_hash();
if (gnu_hash == nullptr) {
return 0;
}
const uint32_t nb_buckets = gnu_hash->nb_buckets();
const uint32_t symndx = first_exported_symbol_index;
const uint32_t maskwords = gnu_hash->maskwords();
const uint32_t shift2 = gnu_hash->shift2();
const std::vector<uint64_t>& filters = gnu_hash->bloom_filters();
if (!filters.empty() && filters[0] == 0) {
LIEF_DEBUG("Bloom filter is null");
}
if (shift2 == 0) {
LIEF_DEBUG("Shift2 is null");
}
LIEF_DEBUG("Number of buckets : {:#x}", nb_buckets);
LIEF_DEBUG("First symbol idx : {:#x}", symndx);
LIEF_DEBUG("Number of bloom filters : {:#x}", maskwords);
LIEF_DEBUG("Shift : {:#x}", shift2);
// MANDATORY !
std::stable_sort(binary_->dynamic_symbols_.begin() + symndx,
binary_->dynamic_symbols_.end(),
[&nb_buckets](const std::unique_ptr<Symbol>& lhs,
const std::unique_ptr<Symbol>& rhs) {
return (dl_new_hash(lhs->name().c_str()) % nb_buckets) <
(dl_new_hash(rhs->name().c_str()) % nb_buckets);
});
Binary::it_dynamic_symbols dynamic_symbols = binary_->dynamic_symbols();
vector_iostream raw_gnuhash(should_swap());
raw_gnuhash.reserve(4 * sizeof(uint32_t) + // header
maskwords * sizeof(uint) + // bloom filters
nb_buckets * sizeof(uint32_t) + // buckets
(dynamic_symbols.size() - symndx) *
sizeof(uint32_t)); // hash values
// Write header
// =================================
raw_gnuhash.write<uint32_t>(nb_buckets)
.write<uint32_t>(symndx)
.write<uint32_t>(maskwords)
.write<uint32_t>(shift2);
// Compute Bloom filters
// =================================
std::vector<uint> bloom_filters(maskwords, 0);
size_t C = sizeof(uint) * 8; // 32 for ELF, 64 for ELF64
for (size_t i = symndx; i < dynamic_symbols.size(); ++i) {
const uint32_t hash = dl_new_hash(dynamic_symbols[i].name().c_str());
const size_t pos = (hash / C) & (gnu_hash->maskwords() - 1);
uint V = (static_cast<uint>(1) << (hash % C)) |
(static_cast<uint>(1) << ((hash >> gnu_hash->shift2()) % C));
bloom_filters[pos] |= V;
}
for (size_t idx = 0; idx < bloom_filters.size(); ++idx) {
LIEF_DEBUG("Bloom filter [{:d}]: {:#x}", idx, bloom_filters[idx]);
}
raw_gnuhash.write(bloom_filters);
// Write buckets and hash
// =================================
int previous_bucket = -1;
size_t hash_value_idx = 0;
std::vector<uint32_t> buckets(nb_buckets, 0);
std::vector<uint32_t> hash_values(dynamic_symbols.size() - symndx, 0);
for (size_t i = symndx; i < dynamic_symbols.size(); ++i) {
LIEF_DEBUG("Processing symbol {}", dynamic_symbols[i].name());
const uint32_t hash = dl_new_hash(dynamic_symbols[i].name().c_str());
int bucket = hash % nb_buckets;
if (bucket < previous_bucket) {
LIEF_ERR("Previous bucket is greater than the current one ({} < {})",
bucket, previous_bucket);
return 0;
}
if (bucket != previous_bucket) {
buckets[bucket] = i;
previous_bucket = bucket;
if (hash_value_idx > 0) {
hash_values[hash_value_idx - 1] |= 1;
}
}
hash_values[hash_value_idx] = hash & ~1;
++hash_value_idx;
}
if (hash_value_idx > 0) {
hash_values[hash_value_idx - 1] |= 1;
}
raw_gnuhash.write(buckets).write(hash_values);
raw_gnuhash.move(raw_gnu_hash_);
return raw_gnu_hash_.size();
}
template<class ELF_T>
size_t dynamic_relocations_size() {
using Elf_Rela = typename ELF_T::Elf_Rela;
using Elf_Rel = typename ELF_T::Elf_Rel;
const Binary::it_dynamic_relocations& dyn_relocs =
binary_->dynamic_relocations();
const size_t nb_rel_a = std::count_if(dyn_relocs.begin(), dyn_relocs.end(),
[](const Relocation& R) {
return R.is_rel() || R.is_rela();
});
const size_t computed_size = binary_->has(DynamicEntry::TAG::RELA) ?
nb_rel_a * sizeof(Elf_Rela) :
nb_rel_a * sizeof(Elf_Rel);
return computed_size;
}
template<class ELF_T>
size_t pltgot_relocations_size() {
using Elf_Rela = typename ELF_T::Elf_Rela;
using Elf_Rel = typename ELF_T::Elf_Rel;
const Binary::it_pltgot_relocations& pltgot_relocs =
binary_->pltgot_relocations();
const DynamicEntry* dt_rela = binary_->get(DynamicEntry::TAG::PLTREL);
const ARCH arch = binary_->header().machine_type();
const bool is_rela =
dt_rela != nullptr && DynamicEntry::from_value(dt_rela->value(), arch) ==
DynamicEntry::TAG::RELA;
if (is_rela) {
return pltgot_relocs.size() * sizeof(Elf_Rela);
}
return pltgot_relocs.size() * sizeof(Elf_Rel);
}
template<class ELF_T>
size_t symbol_version() {
return binary_->symbol_version_table_.size() * sizeof(uint16_t);
}
template<class ELF_T>
size_t symbol_vdef_size() {
using Elf_Verdef = typename ELF_T::Elf_Verdef;
using Elf_Verdaux = typename ELF_T::Elf_Verdaux;
CHECK_FATAL(!binary_->symbols_version_definition().empty() &&
verdef_info_.def_to_names.empty(),
"Inconsistent state");
size_t computed_size =
binary_->symbols_version_definition().size() * sizeof(Elf_Verdef);
for (const sym_verdef_info_t::list_names_t& names : verdef_info_.names_list) {
computed_size += sizeof(Elf_Verdaux) * names.size();
}
return computed_size;
}
template<class ELF_T>
size_t symbol_vreq_size() {
using Elf_Verneed = typename ELF_T::Elf_Verneed;
using Elf_Vernaux = typename ELF_T::Elf_Vernaux;
size_t computed_size = 0;
for (const SymbolVersionRequirement& svr :
binary_->symbols_version_requirement())
{
computed_size += sizeof(Elf_Verneed) +
svr.auxiliary_symbols().size() * sizeof(Elf_Vernaux);
}
return computed_size;
}
template<class ELF_T>
size_t interpreter_size() {
// Access private field directly as
// we want to avoid has_interpreter() check
return binary_->interpreter_.size() + 1;
}
template<class ELF_T>
size_t android_relocations_size(bool force = false) {
static constexpr uint64_t GROUPED_BY_INFO_FLAG = 1 << 0;
static constexpr uint64_t GROUPED_BY_OFFSET_DELTA_FLAG = 1 << 1;
/* static constexpr uint64_t GROUPED_BY_ADDEND_FLAG = 1 << 2; */
static constexpr uint64_t GROUP_HAS_ADDEND_FLAG = 1 << 3;
using Elf_Xword = typename ELF_T::Elf_Xword;
// This code reproduces what the lld linker is doing for generating the
// packed relocations. See lld/ELF/SyntheticSections.cpp -
// AndroidPackedRelocationSection:updateAllocSize
constexpr size_t wordsize = sizeof(typename ELF_T::Elf_Addr);
const bool is_rela = binary_->has(DynamicEntry::TAG::ANDROID_RELA);
const Relocation::TYPE relative_reloc =
relative_from_arch(binary_->header().machine_type());
const uint64_t raw_relative_reloc = Relocation::to_value(relative_reloc);
const Header::CLASS elf_class = std::is_same_v<ELF_T, details::ELF32> ?
Header::CLASS::ELF32 :
Header::CLASS::ELF64;
if (force) {
raw_android_rela_.clear();
}
if (!raw_android_rela_.empty()) {
return raw_android_rela_.size();
}
std::vector<const Relocation*> android_relocs;
std::vector<const Relocation*> relative_rels;
std::vector<const Relocation*> non_relative_rels;
android_relocs.reserve(20);
for (const Relocation& R : binary_->relocations()) {
if (!R.is_android_packed()) {
continue;
}
android_relocs.push_back(&R);
R.is_relative() ? relative_rels.push_back(&R) :
non_relative_rels.push_back(&R);
}
std::sort(relative_rels.begin(), relative_rels.end(),
[](const Relocation* lhs, const Relocation* rhs) {
return lhs->address() < rhs->address();
});
std::vector<const Relocation*> ungrouped_relative;
std::vector<std::vector<const Relocation*>> relative_groups;
for (auto i = relative_rels.begin(), e = relative_rels.end(); i != e;) {
std::vector<const Relocation*> group;
do {
group.push_back(*i++);
} while (i != e && (*(i - 1))->address() + wordsize == (*i)->address());
if (group.size() < 8) {
ungrouped_relative.insert(ungrouped_relative.end(), group.begin(),
group.end());
} else {
relative_groups.emplace_back(std::move(group));
}
}
std::sort(non_relative_rels.begin(), non_relative_rels.end(),
[elf_class](const Relocation* lhs, const Relocation* rhs) {
if (lhs->r_info(elf_class) != rhs->r_info(elf_class)) {
return lhs->r_info(elf_class) < rhs->r_info(elf_class);
}
if (lhs->addend() != rhs->addend()) {
return lhs->addend() < rhs->addend();
}
return lhs->address() < rhs->address();
});
std::vector<const Relocation*> ungrouped_non_relative;
std::vector<std::vector<const Relocation*>> non_relative_group;
for (auto i = non_relative_rels.begin(), e = non_relative_rels.end(); i != e;)
{
auto j = i + 1;
while (j != e && (*i)->r_info(elf_class) == (*j)->r_info(elf_class) &&
(!is_rela || (*i)->addend() == (*j)->addend()))
{
++j;
}
if ((j - i) < 3 || (is_rela && (*i)->addend() != 0)) {
ungrouped_non_relative.insert(ungrouped_non_relative.end(), i, j);
} else {
non_relative_group.emplace_back(i, j);
}
i = j;
}
std::sort(ungrouped_non_relative.begin(), ungrouped_non_relative.end(),
[](const Relocation* lhs, const Relocation* rhs) {
return lhs->address() < rhs->address();
});
const unsigned has_addend_with_rela = is_rela ? GROUP_HAS_ADDEND_FLAG : 0;
uint64_t offset = 0;
uint64_t addend = 0;
vector_iostream ios(should_swap());
ios.write('A').write('P').write('S').write('2');
ios.write_sleb128(android_relocs.size());
ios.write_sleb128(0);
for (const std::vector<const Relocation*>& g : relative_groups) {
ios.write_sleb128(1);
ios.write_sleb128(GROUPED_BY_OFFSET_DELTA_FLAG | GROUPED_BY_INFO_FLAG |
has_addend_with_rela);
ios.write_sleb128(g[0]->address() - offset);
ios.write_sleb128(raw_relative_reloc);
if (is_rela) {
ios.write_sleb128(g[0]->addend() - addend);
addend = g[0]->addend();
}
ios.write_sleb128(g.size() - 1);
ios.write_sleb128(GROUPED_BY_OFFSET_DELTA_FLAG | GROUPED_BY_INFO_FLAG |
has_addend_with_rela);
ios.write_sleb128(wordsize);
ios.write_sleb128(raw_relative_reloc);
if (is_rela) {
auto it = g.begin();
++it;
for (; it != g.end(); ++it) {
ios.write_sleb128((*it)->addend() - addend);
addend = (*it)->addend();
}
}
offset = g.back()->address();
}
if (!ungrouped_relative.empty()) {
ios.write_sleb128(ungrouped_relative.size());
ios.write_sleb128(GROUPED_BY_INFO_FLAG | has_addend_with_rela);
ios.write_sleb128(raw_relative_reloc);
for (const Relocation* R : ungrouped_relative) {
ios.write_sleb128(R->address() - offset);
offset = R->address();
if (is_rela) {
ios.write_sleb128(R->addend() - addend);
addend = R->addend();
}
}
}
for (const std::vector<const Relocation*>& g : non_relative_group) {
ios.write_sleb128(g.size());
ios.write_sleb128(GROUPED_BY_INFO_FLAG);
ios.write_sleb128(static_cast<Elf_Xword>(g[0]->r_info(elf_class)));
for (const Relocation* R : g) {
ios.write_sleb128(R->address() - offset);
offset = R->address();
}
addend = 0;
}
if (!ungrouped_non_relative.empty()) {
ios.write_sleb128(ungrouped_non_relative.size());
ios.write_sleb128(has_addend_with_rela);
for (const Relocation* R : ungrouped_non_relative) {
ios.write_sleb128(R->address() - offset);
offset = R->address();
ios.write_sleb128(static_cast<Elf_Xword>(R->r_info(elf_class)));
if (is_rela) {
ios.write_sleb128(R->addend() - addend);
addend = R->addend();
}
}
}
ios.move(raw_android_rela_);
return raw_android_rela_.size();
}
template<class ELF_T>
size_t relative_relocations_size(bool force = false) {
// This code is inspired by LLVM-lld:
// lld/ELF/SyntheticSections.cpp - RelrSection<ELFT>::updateAllocSize
// https://github.com/llvm/llvm-project/blob/754a8add57098ef71e4a51a9caa0cc175e94377d/lld/ELF/SyntheticSections.cpp#L1997-L2078
using Elf_Addr = typename ELF_T::Elf_Addr;
if (force) {
raw_relr_.clear();
}
if (!raw_relr_.empty()) {
return raw_relr_.size();
}
std::vector<const Relocation*> relr_relocs;
relr_relocs.reserve(20);
for (const Relocation& R : binary_->relocations()) {
if (R.is_relatively_encoded()) {
relr_relocs.push_back(&R);
}
}
std::unique_ptr<uint64_t[]> offsets(new uint64_t[relr_relocs.size()]);
for (size_t i = 0; i < relr_relocs.size(); ++i) {
offsets[i] = relr_relocs[i]->address();
}
std::sort(offsets.get(), offsets.get() + relr_relocs.size());
const size_t wordsize = sizeof(Elf_Addr);
const size_t nbits = wordsize * 8 - 1;
vector_iostream raw_relr(should_swap());
for (size_t i = 0, e = relr_relocs.size(); i != e;) {
raw_relr.write<Elf_Addr>(offsets[i]);
uint64_t base = offsets[i] + wordsize;
++i;
for (;;) {
uint64_t bitmap = 0;
for (; i != e; ++i) {
uint64_t d = offsets[i] - base;
if (d >= (nbits * wordsize) || (d % wordsize) != 0) {
break;
}
bitmap |= uint64_t(1) << (d / wordsize);
}
if (!bitmap) {
break;
}
raw_relr.write<Elf_Addr>((bitmap << 1) | 1);
base += nbits * wordsize;
}
}
raw_relr.move(raw_relr_);
return raw_relr_.size();
}
void relocate_dynamic(uint64_t size) {
dynamic_size_ = size;
}
void relocate_dynstr(bool val) {
relocate_dynstr_ = val;
}
void relocate_relr(bool val) {
relocate_relr_ = val;
}
void relocate_android_rela(bool val) {
relocate_android_rela_ = val;
}
void relocate_shstr(bool val) {
relocate_shstrtab_ = val;
}
void relocate_strtab(bool val) {
relocate_strtab_ = val;
}
void relocate_gnu_hash(bool val) {
relocate_gnu_hash_ = val;
}
void relocate_sysv_hash(uint64_t size) {
sysv_size_ = size;
}
void relocate_dynsym(uint64_t size) {
dynsym_size_ = size;
}
void relocate_symver(uint64_t size) {
sver_size_ = size;
}
void relocate_symverd(uint64_t size) {
sverd_size_ = size;
}
void relocate_symverr(uint64_t size) {
sverr_size_ = size;
}
void relocate_preinit_array(uint64_t size) {
preinit_size_ = size;
}
void relocate_init_array(uint64_t size) {
init_size_ = size;
}
void relocate_fini_array(uint64_t size) {
fini_size_ = size;
}
void relocate_dyn_reloc(uint64_t size) {
dynamic_reloc_size_ = size;
}
void relocate_plt_reloc(uint64_t size) {
pltgot_reloc_size_ = size;
}
void relocate_interpreter(uint64_t size) {
interp_size_ = size;
}
void relocate_notes(bool value) {
relocate_notes_ = value;
}
void relocate_symtab(size_t size) {
symtab_size_ = size;
}
const std::vector<uint8_t>& raw_dynstr() const {
return raw_dynstr_;
}
const std::vector<uint8_t>& raw_shstr() const override {
return raw_shstrtab_;
}
const std::vector<uint8_t>& raw_gnuhash() const {
return raw_gnu_hash_;
}
const std::vector<uint8_t>& raw_notes() const {
return raw_notes_;
}
sym_verdef_info_t& verdef_info() {
return verdef_info_;
}
const sym_verdef_info_t& verdef_info() const {
return verdef_info_;
}
const std::vector<uint8_t>& raw_relr() const {
return raw_relr_;
}
const std::vector<uint8_t>& raw_android_rela() const {
return raw_android_rela_;
}
result<bool> relocate() {
const bool needs_interp =
interp_size_ > 0 && !binary_->has(Segment::TYPE::INTERP);
uint64_t read_segment = interp_size_ + sysv_size_ + dynsym_size_ + sver_size_ +
sverd_size_ + sverr_size_ + dynamic_reloc_size_ +
pltgot_reloc_size_;
if (relocate_relr_) {
read_segment += raw_relr_.size();
}
if (relocate_android_rela_) {
read_segment += raw_android_rela_.size();
}
if (relocate_notes_) {
read_segment += raw_notes_.size();
}
if (relocate_dynstr_) {
read_segment += raw_dynstr_.size();
}
if (relocate_gnu_hash_) {
read_segment += raw_gnu_hash_.size();
}
const uint64_t read_write_segment =
init_size_ + preinit_size_ + fini_size_ + dynamic_size_;
const size_t required_segments = static_cast<size_t>(needs_interp) +
static_cast<size_t>(read_segment > 0) +
static_cast<size_t>(read_write_segment > 0);
if (required_segments > 0 &&
binary_->relocate_phdr_table_auto(required_segments) == 0)
{
LIEF_ERR("Unable to reserve {} program headers before relocating the "
"executable layout",
required_segments);
return make_error_code(lief_errors::build_error);
}
/* PT_INTERP segment (optional)
*
*/
if (needs_interp) {
Segment interp_segment;
interp_segment.alignment(0x8);
interp_segment.type(Segment::TYPE::INTERP);
interp_segment.add(Segment::FLAGS::R);
interp_segment.content(std::vector<uint8_t>(interp_size_));
if (Segment* interp = binary_->add(interp_segment)) {
LIEF_DEBUG("Interp Segment: {:#x}:{:#x}", interp->virtual_address(),
interp->virtual_size());
} else {
LIEF_ERR("Failed to add PT_INTERP segment");
}
}
/* Segment 1.
* .interp
* .note.*
* .gnu.hash
* .hash
* .dynsym
* .dynstr
* .gnu.version
* .gnu.version_d
* .gnu.version_r
* .rela.dyn
* .rela.plt
* .relr.dyn
* Perm: READ ONLY
*/
Segment* new_rsegment = nullptr;
if (read_segment > 0) {
Segment rsegment;
rsegment.type(Segment::TYPE::LOAD);
rsegment.add(Segment::FLAGS::R);
rsegment.content(std::vector<uint8_t>(read_segment));
new_rsegment = binary_->add(rsegment);
if (new_rsegment == nullptr) {
LIEF_ERR("Failed to add R-segment");
return make_error_code(lief_errors::build_error);
}
LIEF_DEBUG("R-Segment: {:#06x}:{:#06x}", new_rsegment->virtual_address(),
new_rsegment->virtual_size());
}
/* Segment 2
*
* .init_array
* .fini_array
* .preinit_array
* .prefini_array
* .dynamic
* .got
* .got.plt
* Perm: READ | WRITE
*/
Segment* new_rwsegment = nullptr;
Segment rwsegment;
if (read_write_segment > 0) {
rwsegment.type(Segment::TYPE::LOAD);
rwsegment.add(Segment::FLAGS::R | Segment::FLAGS::W);
rwsegment.content(std::vector<uint8_t>(read_write_segment));
new_rwsegment = binary_->add(rwsegment);
if (new_rwsegment != nullptr) {
LIEF_DEBUG("RW-Segment: {:#x}:{:#x}", new_rwsegment->virtual_address(),
new_rwsegment->virtual_size());
} else {
LIEF_ERR("Failed to add RW-segment");
return make_error_code(lief_errors::build_error);
}
}
if (relocate_shstrtab_) {
LIEF_DEBUG("Relocating .shstrtab");
// Remove the current .shstrtab section
Header& hdr = binary_->header();
if (hdr.section_name_table_idx() >= binary_->sections_.size()) {
LIEF_ERR("Section names table index out of range");
return make_error_code(lief_errors::file_format_error);
}
std::unique_ptr<Section>& string_names_section =
binary_->sections_[hdr.section_name_table_idx()];
std::string_view sec_name = binary_->shstrtab_name();
binary_->remove(*string_names_section, /* clear */ true);
Section sec_str_section(std::string(sec_name), Section::TYPE::STRTAB);
sec_str_section.content(std::vector<uint8_t>(raw_shstrtab_.size()));
Section* sec = binary_->add(sec_str_section, /*loaded=*/false,
/*pos=*/Binary::SEC_INSERT_POS::POST_SECTION);
if (sec != nullptr) {
if (auto idx = binary_->get_section_idx(*sec)) {
hdr.section_name_table_idx(*idx);
}
}
}
for (std::unique_ptr<Relocation>& reloc : binary_->relocations_) {
relocations_addresses_[reloc->address()] = reloc.get();
}
[[maybe_unused]] uint64_t va_r_base =
new_rsegment != nullptr ? new_rsegment->virtual_address() : 0;
[[maybe_unused]] uint64_t va_rw_base =
new_rwsegment != nullptr ? new_rwsegment->virtual_address() : 0;
if (interp_size_ > 0) {
Segment* pt_interp = binary_->get(Segment::TYPE::INTERP);
if (pt_interp == nullptr) {
LIEF_ERR("PT_INTERP segment not found");
return make_error_code(lief_errors::file_format_error);
}
Section* section = nullptr;
Segment::it_sections sections = pt_interp->sections();
if (!sections.empty()) {
section = &sections[0];
}
pt_interp->virtual_address(va_r_base);
pt_interp->virtual_size(interp_size_);
pt_interp->physical_address(va_r_base);
pt_interp->physical_size(interp_size_);
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
pt_interp->file_offset(offset_r_base);
if (section != nullptr) {
section->virtual_address(va_r_base);
section->size(interp_size_);
section->offset(offset_r_base);
section->original_size_ = interp_size_;
}
va_r_base += interp_size_;
}
if (relocate_notes_) {
Segment* note_segment = binary_->get(Segment::TYPE::NOTE);
if (note_segment == nullptr) {
LIEF_ERR("PT_NOTE segment not found");
return make_error_code(lief_errors::file_format_error);
}
note_segment->virtual_address(va_r_base);
note_segment->virtual_size(raw_notes_.size());
note_segment->physical_address(va_r_base);
note_segment->physical_size(raw_notes_.size());
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
note_segment->file_offset(offset_r_base);
va_r_base += raw_notes_.size();
}
if (dynamic_size_ > 0) {
// Update .dynamic / PT_DYNAMIC
// Update relocations associated with .init_array etc.
Segment* dynamic_segment = binary_->get(Segment::TYPE::DYNAMIC);
if (dynamic_segment == nullptr) {
LIEF_ERR("Dynamic section/segment not found");
return make_error_code(lief_errors::file_format_error);
}
uint64_t offset_rw_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_rw_base)) {
offset_rw_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
dynamic_segment->virtual_address(va_rw_base);
dynamic_segment->virtual_size(dynamic_size_);
dynamic_segment->physical_address(va_rw_base);
dynamic_segment->file_offset(offset_rw_base);
dynamic_segment->physical_size(dynamic_size_);
if (Section* section = binary_->dynamic_section()) {
section->virtual_address(va_rw_base);
section->size(dynamic_size_);
section->offset(offset_rw_base);
section->original_size_ = dynamic_size_;
}
va_rw_base += dynamic_size_;
}
if (dynsym_size_ > 0) {
// Update .dynsym / DT_SYMTAB
DynamicEntry* dt_symtab = binary_->get(DynamicEntry::TAG::SYMTAB);
if (dt_symtab == nullptr) {
LIEF_ERR("DT_SYMTAB not found");
return make_error_code(lief_errors::file_format_error);
}
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section =
binary_->section_from_virtual_address(dt_symtab->value()))
{
section->virtual_address(va_r_base);
section->size(dynsym_size_);
section->offset(offset_r_base);
section->original_size_ = dynsym_size_;
}
dt_symtab->value(va_r_base);
va_r_base += dynsym_size_;
}
if (relocate_dynstr_) {
// Update .dynstr section, DT_SYMTAB, DT_STRSZ
DynamicEntry* dt_strtab = binary_->get(DynamicEntry::TAG::STRTAB);
DynamicEntry* dt_strsize = binary_->get(DynamicEntry::TAG::STRSZ);
if (dt_strtab == nullptr || dt_strsize == nullptr) {
LIEF_ERR("DT_STRTAB/DT_STRSZ not found");
return make_error_code(lief_errors::file_format_error);
}
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section =
binary_->section_from_virtual_address(dt_strtab->value()))
{
section->virtual_address(va_r_base);
section->size(raw_dynstr_.size());
section->offset(offset_r_base);
section->original_size_ = raw_dynstr_.size();
}
dt_strtab->value(va_r_base);
dt_strsize->value(raw_dynstr_.size());
va_r_base += raw_dynstr_.size();
}
if (sver_size_ > 0) {
DynamicEntry* dt_versym = binary_->get(DynamicEntry::TAG::VERSYM);
if (dt_versym == nullptr) {
LIEF_ERR("DT_VERSYM not found");
return make_error_code(lief_errors::file_format_error);
}
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section =
binary_->section_from_virtual_address(dt_versym->value()))
{
section->virtual_address(va_r_base);
section->size(sver_size_);
section->offset(offset_r_base);
section->original_size_ = sver_size_;
}
dt_versym->value(va_r_base);
va_r_base += sver_size_;
}
if (sverd_size_ > 0) {
DynamicEntry* dt_verdef = binary_->get(DynamicEntry::TAG::VERDEF);
if (dt_verdef == nullptr) {
LIEF_ERR("DT_VERDEF not found");
return make_error_code(lief_errors::file_format_error);
}
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section =
binary_->section_from_virtual_address(dt_verdef->value()))
{
section->virtual_address(va_r_base);
section->size(sverd_size_);
section->offset(offset_r_base);
section->original_size_ = sverd_size_;
}
dt_verdef->value(va_r_base);
va_r_base += sverd_size_;
}
if (sverr_size_ > 0) {
DynamicEntry* dt_verreq = binary_->get(DynamicEntry::TAG::VERNEED);
if (dt_verreq == nullptr) {
LIEF_ERR("DT_VERNEED not found");
return make_error_code(lief_errors::file_format_error);
}
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section =
binary_->section_from_virtual_address(dt_verreq->value()))
{
section->virtual_address(va_r_base);
section->size(sverr_size_);
section->offset(offset_r_base);
section->original_size_ = sverr_size_;
}
dt_verreq->value(va_r_base);
va_r_base += sverr_size_;
}
if (dynamic_reloc_size_ > 0) {
// Update:
// - DT_REL / DT_RELA
// - DT_RELSZ / DT_RELASZ
// - .dyn.rel
DynamicEntry* dt_rela = binary_->get(DynamicEntry::TAG::RELA);
const bool is_rela = dt_rela != nullptr;
DynamicEntry* dt_reloc =
is_rela ? dt_rela : binary_->get(DynamicEntry::TAG::REL);
DynamicEntry* dt_relocsz = is_rela ?
binary_->get(DynamicEntry::TAG::RELASZ) :
binary_->get(DynamicEntry::TAG::RELSZ);
if (dt_reloc == nullptr || dt_relocsz == nullptr) {
LIEF_ERR("DT_REL(A)/DT_REL(A)SZ not found");
return make_error_code(lief_errors::file_format_error);
}
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section =
binary_->section_from_virtual_address(dt_reloc->value()))
{
section->virtual_address(va_r_base);
section->size(dynamic_reloc_size_);
section->offset(offset_r_base);
section->original_size_ = dynamic_reloc_size_;
}
dt_reloc->value(va_r_base);
dt_relocsz->value(dynamic_reloc_size_);
va_r_base += dynamic_reloc_size_;
}
if (pltgot_reloc_size_ > 0) {
// Update:
// - DT_JMPREL / DT_PLTRELSZ
// - .plt.rel
DynamicEntry* dt_reloc = binary_->get(DynamicEntry::TAG::JMPREL);
DynamicEntry* dt_relocsz = binary_->get(DynamicEntry::TAG::PLTRELSZ);
if (dt_reloc == nullptr || dt_relocsz == nullptr) {
LIEF_ERR("DT_JMPREL/DT_PLTRELSZ not found");
return make_error_code(lief_errors::file_format_error);
}
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section =
binary_->section_from_virtual_address(dt_reloc->value()))
{
section->virtual_address(va_r_base);
section->size(pltgot_reloc_size_);
section->offset(offset_r_base);
section->original_size_ = pltgot_reloc_size_;
}
dt_reloc->value(va_r_base);
dt_relocsz->value(pltgot_reloc_size_);
va_r_base += pltgot_reloc_size_;
}
if (relocate_relr_) {
DynamicEntry* dt_relr = binary_->get(DynamicEntry::TAG::RELR);
if (dt_relr == nullptr) {
dt_relr = binary_->get(DynamicEntry::TAG::ANDROID_RELR);
}
if (dt_relr == nullptr) {
LIEF_ERR("DT_RELR/DT_ANDROID_RELR not found");
return make_error_code(lief_errors::file_format_error);
}
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section =
binary_->section_from_virtual_address(dt_relr->value()))
{
section->virtual_address(va_r_base);
section->size(raw_relr_.size());
section->offset(offset_r_base);
section->original_size_ = raw_relr_.size();
}
dt_relr->value(va_r_base);
va_r_base += raw_relr_.size();
}
if (relocate_android_rela_) {
DynamicEntry* dt_rel = binary_->get(DynamicEntry::TAG::ANDROID_RELA);
if (dt_rel == nullptr) {
dt_rel = binary_->get(DynamicEntry::TAG::ANDROID_REL);
}
if (dt_rel == nullptr) {
LIEF_ERR("DT_ANDROID_REL[A] not found");
return make_error_code(lief_errors::file_format_error);
}
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section =
binary_->section_from_virtual_address(dt_rel->value()))
{
section->virtual_address(va_r_base);
section->size(raw_android_rela_.size());
section->offset(offset_r_base);
section->original_size_ = raw_android_rela_.size();
}
dt_rel->value(va_r_base);
va_r_base += raw_android_rela_.size();
}
if (relocate_gnu_hash_) {
// Update .gnu.hash section / DT_GNU_HASH
DynamicEntry* dt_gnu_hash = binary_->get(DynamicEntry::TAG::GNU_HASH);
if (dt_gnu_hash == nullptr) {
LIEF_ERR("DT_GNU_HASH not found");
return make_error_code(lief_errors::file_format_error);
}
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section =
binary_->section_from_virtual_address(dt_gnu_hash->value()))
{
section->virtual_address(va_r_base);
section->size(raw_gnu_hash_.size());
section->offset(offset_r_base);
section->original_size_ = raw_gnu_hash_.size();
}
dt_gnu_hash->value(va_r_base);
va_r_base += raw_gnu_hash_.size();
}
if (sysv_size_ > 0) {
// Update .hash section / DT_HASH
DynamicEntry* dt_hash = binary_->get(DynamicEntry::TAG::HASH);
if (dt_hash == nullptr) {
LIEF_ERR("DT_HASH not found");
return make_error_code(lief_errors::file_format_error);
}
uint64_t offset_r_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_r_base)) {
offset_r_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section =
binary_->section_from_virtual_address(dt_hash->value()))
{
section->virtual_address(va_r_base);
section->size(sysv_size_);
section->offset(offset_r_base);
section->original_size_ = sysv_size_;
}
dt_hash->value(va_r_base);
va_r_base += sysv_size_;
}
// RW-Segment
// ====================================
if (init_size_ > 0) { // .init_array
DynamicEntry* raw_dt_init = binary_->get(DynamicEntry::TAG::INIT_ARRAY);
if (raw_dt_init == nullptr || !DynamicEntryArray::classof(raw_dt_init)) {
LIEF_ERR("DT_INIT_ARRAY not found");
return make_error_code(lief_errors::file_format_error);
}
auto* dt_init_array = raw_dt_init->as<DynamicEntryArray>();
DynamicEntry* dt_init_arraysz =
binary_->get(DynamicEntry::TAG::INIT_ARRAYSZ);
if (dt_init_arraysz == nullptr) {
LIEF_ERR("DT_INIT_ARRAYSZ not found");
return make_error_code(lief_errors::file_format_error);
}
// Update relocation range
if (binary_->header().file_type() == Header::FILE_TYPE::DYN) {
LIEF_WARN("Relocating .init_array may not work on Linux");
const std::vector<uint64_t>& array = dt_init_array->array();
const size_t sizeof_p = binary_->type() == Header::CLASS::ELF32 ?
sizeof(uint32_t) :
sizeof(uint64_t);
// Since the values of the .init_array have moved elsewhere,
// we need to change the relocation associated with the former .init_array
const uint64_t array_base_address = dt_init_array->value();
for (size_t i = 0; i < array.size(); ++i) {
auto it_reloc =
relocations_addresses_.find(array_base_address + i * sizeof_p);
if (it_reloc == relocations_addresses_.end()) {
LIEF_ERR("Missing relocation for .init_array[{:d}]: {:#x}", i,
array[i]);
continue;
}
Relocation* reloc = it_reloc->second;
reloc->address(va_rw_base + i * sizeof_p);
}
}
uint64_t offset_rw_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_rw_base)) {
offset_rw_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section = binary_->get(Section::TYPE::INIT_ARRAY)) {
section->virtual_address(va_rw_base);
section->size(init_size_);
section->offset(offset_rw_base);
section->original_size_ = init_size_;
}
dt_init_array->value(va_rw_base);
dt_init_arraysz->value(init_size_);
va_rw_base += init_size_;
}
if (preinit_size_ > 0) { // .preinit_array
DynamicEntry* raw_dt_preinit =
binary_->get(DynamicEntry::TAG::PREINIT_ARRAY);
if (raw_dt_preinit == nullptr || !DynamicEntryArray::classof(raw_dt_preinit))
{
LIEF_ERR("DT_PREINIT_ARRAY not found");
return make_error_code(lief_errors::file_format_error);
}
auto* dt_preinit_array = raw_dt_preinit->as<DynamicEntryArray>();
DynamicEntry* dt_preinit_arraysz =
binary_->get(DynamicEntry::TAG::PREINIT_ARRAYSZ);
if (dt_preinit_array == nullptr) {
LIEF_ERR("DT_PREINIT_ARRAYSZ not found");
return make_error_code(lief_errors::file_format_error);
}
if (binary_->header().file_type() == Header::FILE_TYPE::DYN) {
const std::vector<uint64_t>& array = dt_preinit_array->array();
const size_t sizeof_p = binary_->type() == Header::CLASS::ELF32 ?
sizeof(uint32_t) :
sizeof(uint64_t);
LIEF_WARN("Relocating .preinit_array may not work on Linux");
const uint64_t array_base_address = dt_preinit_array->value();
for (size_t i = 0; i < array.size(); ++i) {
auto it_reloc =
relocations_addresses_.find(array_base_address + i * sizeof_p);
if (it_reloc == relocations_addresses_.end()) {
LIEF_ERR("Missing relocation for .preinit_array[{:d}]: {:#x}", i,
array[i]);
continue;
}
Relocation* reloc = it_reloc->second;
reloc->address(va_rw_base + i * sizeof_p);
}
}
uint64_t offset_rw_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_rw_base)) {
offset_rw_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section = binary_->get(Section::TYPE::PREINIT_ARRAY)) {
section->virtual_address(va_rw_base);
section->size(preinit_size_);
section->offset(offset_rw_base);
section->original_size_ = preinit_size_;
}
dt_preinit_array->value(va_rw_base);
dt_preinit_arraysz->value(preinit_size_);
va_rw_base += preinit_size_;
}
if (fini_size_ > 0) { // .fini_array
DynamicEntry* raw_dt_fini = binary_->get(DynamicEntry::TAG::FINI_ARRAY);
if (raw_dt_fini == nullptr || !DynamicEntryArray::classof(raw_dt_fini)) {
LIEF_ERR("DT_FINI_ARRAY not found");
return make_error_code(lief_errors::file_format_error);
}
auto* dt_fini_array = raw_dt_fini->as<DynamicEntryArray>();
DynamicEntry* dt_fini_arraysz =
binary_->get(DynamicEntry::TAG::FINI_ARRAYSZ);
if (dt_fini_arraysz == nullptr) {
LIEF_ERR("DT_FINI_ARRAYSZ not found");
return make_error_code(lief_errors::file_format_error);
}
if (binary_->header().file_type() == Header::FILE_TYPE::DYN) {
const std::vector<uint64_t>& array = dt_fini_array->array();
const size_t sizeof_p = binary_->type() == Header::CLASS::ELF32 ?
sizeof(uint32_t) :
sizeof(uint64_t);
LIEF_WARN("Relocating .fini_array may not work on Linux");
const uint64_t array_base_address = dt_fini_array->value();
for (size_t i = 0; i < array.size(); ++i) {
auto it_reloc =
relocations_addresses_.find(array_base_address + i * sizeof_p);
if (it_reloc == relocations_addresses_.end()) {
LIEF_ERR("Missing relocation for .fini_array[{:d}]: {:#x}", i,
array[i]);
continue;
}
Relocation* reloc = it_reloc->second;
reloc->address(va_rw_base + i * sizeof_p);
}
}
uint64_t offset_rw_base = 0;
if (auto res = binary_->virtual_address_to_offset(va_rw_base)) {
offset_rw_base = *res;
} else {
return make_error_code(lief_errors::build_error);
}
if (Section* section = binary_->get(Section::TYPE::FINI_ARRAY)) {
section->virtual_address(va_rw_base);
section->size(fini_size_);
section->offset(offset_rw_base);
section->original_size_ = fini_size_;
}
dt_fini_array->value(va_rw_base);
dt_fini_arraysz->value(fini_size_);
va_rw_base += fini_size_;
}
// Check if we need to relocate the .strtab that contains
// symbol's names associated with debug symbol (not mandatory)
size_t strtab_idx = 0;
if (relocate_strtab_) {
LIEF_DEBUG("Relocating .strtab");
if (is_strtab_shared_shstrtab()) {
LIEF_ERR(
"Strtab inconsistency"
); // The strtab should be located in the .shstrtab section
return make_error_code(lief_errors::file_format_error);
}
if (strtab_section_ != nullptr) {
LIEF_DEBUG("Removing the old section: {} {:#x} (size: {:#x})",
strtab_section_->name(), strtab_section_->file_offset(),
strtab_section_->size());
binary_->remove(*strtab_section_, /* clear */ true);
}
Section strtab{".strtab", Section::TYPE::STRTAB};
strtab.content(raw_strtab_);
strtab.alignment(1);
Section* new_strtab =
binary_->add(strtab, /*loaded=*/false,
/*pos=*/Binary::SEC_INSERT_POS::POST_SECTION);
strtab_idx = binary_->sections().size() - 1;
if (new_strtab == nullptr) {
LIEF_ERR("Failed to add .strtab section");
return make_error_code(lief_errors::build_error);
}
LIEF_DEBUG("New .strtab section: #{:d} {} {:#x} (size: {:x})", strtab_idx,
new_strtab->name(), new_strtab->file_offset(),
new_strtab->size());
Section* sec_symtab = binary_->get(Section::TYPE::SYMTAB);
if (sec_symtab != nullptr) {
LIEF_DEBUG("Linking section {} with new .strtab (idx: #{:d})",
sec_symtab->name(), strtab_idx);
sec_symtab->link(strtab_idx);
}
set_strtab_section(*new_strtab);
}
if (strtab_section_ != nullptr) {
strtab_section_->content(raw_strtab_);
}
LIEF_DEBUG("strtab_idx: {:d}", strtab_idx);
// Sections that are not associated with segments (mostly debug information)
// currently we only handle the symtab symbol table: .symtab
if (symtab_size_ > 0) {
LIEF_DEBUG("Relocating .symtab");
const auto sections = binary_->sections();
auto it_sec_symtab =
std::find_if(sections.begin(), sections.end(), [](const Section& sec) {
return sec.type() == Section::TYPE::SYMTAB;
});
if (it_sec_symtab != sections.end()) {
const size_t pos = std::distance(sections.begin(), it_sec_symtab);
if (strtab_idx == 0) {
strtab_idx = it_sec_symtab->link();
}
LIEF_DEBUG("Removing the old section: {} {:#x} (size: {:#x})",
it_sec_symtab->name(), it_sec_symtab->file_offset(),
it_sec_symtab->size());
binary_->remove(*it_sec_symtab, /* clear */ true);
if (pos < strtab_idx) {
--strtab_idx;
}
}
Section symtab{".symtab", Section::TYPE::SYMTAB};
symtab.content(std::vector<uint8_t>(symtab_size_));
const size_t sizeof_sym = binary_->type() == Header::CLASS::ELF32 ?
sizeof(details::Elf32_Sym) :
sizeof(details::Elf64_Sym);
symtab.entry_size(sizeof_sym);
symtab.alignment(8);
symtab.link(strtab_idx);
Section* new_symtab =
binary_->add(symtab, /*loaded=*/false,
/*pos=*/Binary::SEC_INSERT_POS::POST_SECTION);
if (new_symtab == nullptr) {
LIEF_ERR("Failed to add .symtab section");
return make_error_code(lief_errors::build_error);
}
LIEF_DEBUG("New .symtab section: {} {:#x} (size: {:x})", new_symtab->name(),
new_symtab->file_offset(), new_symtab->size());
}
// Process note sections
if (const Segment* segment_note = binary_->get(Segment::TYPE::NOTE)) {
for (const Note& note : binary_->notes()) {
auto section_res = Note::note_to_section(note);
const auto& it_offset = notes_off_map_.find(&note);
if (!section_res) {
if (binary_->header().file_type() != Header::FILE_TYPE::CORE) {
LIEF_ERR("Unsupported note type {} ('{}') for section",
to_string(note.type()), note.name());
}
continue;
}
std::string sec_name = *section_res;
// If the binary has the note type but does not have
// the section (likely because the user added the note manually)
// then, create the section
if (const Section* nsec = binary_->get_section(*section_res);
nsec == nullptr)
{
if (it_offset == notes_off_map_.end()) {
LIEF_ERR("Raw data not found for note '{}'", to_string(note.type()));
continue;
}
const size_t note_offset = it_offset->second;
Section section{sec_name, Section::TYPE::NOTE};
section += Section::FLAGS::ALLOC;
Section* section_added =
binary_->add(section, /*loaded=*/false,
/*pos=*/Binary::SEC_INSERT_POS::POST_SECTION);
if (section_added == nullptr) {
LIEF_ERR("Failed to add SHT_NOTE section");
return make_error_code(lief_errors::build_error);
}
section_added->offset(segment_note->file_offset() + note_offset);
section_added->size(note.size());
section.virtual_address(segment_note->virtual_address() + note_offset);
section_added->alignment(4);
}
}
}
return true;
}
const std::unordered_map<std::string_view, size_t>& dynstr_map() const {
return offset_name_map_;
}
const std::unordered_map<const Note*, size_t>& note_off_map() const {
return notes_off_map_;
}
uint32_t sysv_nchain() const {
return nchain_;
}
~ExeLayout() override = default;
ExeLayout() = delete;
private:
std::unordered_map<std::string_view, size_t> offset_name_map_;
std::unordered_map<const Note*, size_t> notes_off_map_;
sym_verdef_info_t verdef_info_;
std::vector<uint8_t> raw_notes_;
bool relocate_notes_ = false;
std::vector<uint8_t> raw_dynstr_;
bool relocate_dynstr_ = false;
bool relocate_shstrtab_ = false;
bool relocate_strtab_ = false;
std::vector<uint8_t> raw_gnu_hash_;
bool relocate_gnu_hash_ = false;
std::vector<uint8_t> raw_relr_;
bool relocate_relr_ = false;
std::vector<uint8_t> raw_android_rela_;
bool relocate_android_rela_ = false;
uint64_t sysv_size_ = 0;
uint64_t dynamic_size_ = 0;
uint64_t dynsym_size_ = 0;
uint64_t pltgot_reloc_size_ = 0;
uint64_t dynamic_reloc_size_ = 0;
uint64_t sver_size_ = 0;
uint64_t sverd_size_ = 0;
uint64_t sverr_size_ = 0;
uint64_t preinit_size_ = 0;
uint64_t init_size_ = 0;
uint64_t fini_size_ = 0;
uint64_t interp_size_ = 0;
uint32_t nchain_ = 0;
uint64_t symtab_size_ = 0;
// uint64_t pltgot_reloc_size_ = 0;
std::unordered_map<uint64_t, Relocation*> relocations_addresses_;
};
}
#endif