/* 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 <memory>
#include <unordered_set>
#include "logging.hpp"
#include "LIEF/BinaryStream/VectorStream.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"
#include "LIEF/ELF/hash.hpp"
#include "LIEF/ELF/Parser.hpp"
#include "LIEF/ELF/DynamicEntryFlags.hpp"
#include "LIEF/ELF/Relocation.hpp"
#include "LIEF/ELF/Segment.hpp"
#include "LIEF/ELF/Section.hpp"
#include "LIEF/ELF/GnuHash.hpp"
#include "LIEF/ELF/DynamicEntryLibrary.hpp"
#include "LIEF/ELF/DynamicEntryArray.hpp"
#include "LIEF/ELF/DynamicSharedObject.hpp"
#include "LIEF/ELF/DynamicEntryAuxiliary.hpp"
#include "LIEF/ELF/DynamicEntryFilter.hpp"
#include "LIEF/ELF/DynamicEntryRunPath.hpp"
#include "LIEF/ELF/DynamicEntryRpath.hpp"
#include "LIEF/ELF/SymbolVersionRequirement.hpp"
#include "LIEF/ELF/SymbolVersionDefinition.hpp"
#include "LIEF/ELF/SymbolVersionAuxRequirement.hpp"
#include "LIEF/ELF/SymbolVersionAux.hpp"
#include "LIEF/ELF/Symbol.hpp"
#include "LIEF/ELF/SymbolVersion.hpp"
#include "LIEF/ELF/Binary.hpp"
#include "ELF/Structures.hpp"
#include "ELF/DataHandler/Handler.hpp"
#include "ELF/SizingInfo.hpp"
#include "Object.tcc"
namespace LIEF::ELF {
template<typename ELF_T>
ok_error_t Parser::parse_binary() {
LIEF_DEBUG("Start parsing");
// Parse header
// ============
auto res = parse_header<ELF_T>();
if (!res) {
LIEF_WARN("ELF Header parsed with errors");
}
// Parse Sections
// ==============
if (binary_->header_.section_headers_offset() > 0) {
parse_sections<ELF_T>();
} else {
if (binary_->header().file_type() != Header::FILE_TYPE::CORE) {
LIEF_WARN("The current binary doesn't have a section header");
}
}
// Parse segments
// ==============
if (binary_->header_.program_headers_offset() > 0) {
parse_segments<ELF_T>();
} else {
if (binary_->header().file_type() != Header::FILE_TYPE::REL) {
LIEF_WARN("Binary doesn't have a program header");
}
}
if (Segment* seg_dyn = binary_->get(Segment::TYPE::DYNAMIC)) {
if (!parse_dyn_table<ELF_T>(*seg_dyn)) {
LIEF_WARN("PT_DYNAMIC parsing failed with error");
}
}
process_dynamic_table<ELF_T>();
if (const Section* sec_symbtab = binary_->get(Section::TYPE::SYMTAB)) {
if (sec_symbtab->link() == 0 ||
sec_symbtab->link() >= binary_->sections_.size())
{
LIEF_WARN("Invalid section link");
} else {
if (config_.parse_symtab_symbols) {
// We should have:
// nb_entries == section->information())
// but lots of compilers do not respect this rule
parse_symtab_symbols<ELF_T>(*sec_symbtab,
*binary_->sections_[sec_symbtab->link()]);
}
}
}
// Parse Symbol hash
// ==================
if (DynamicEntry* dt_hash = binary_->get(DynamicEntry::TAG::HASH)) {
if (auto res = binary_->virtual_address_to_offset(dt_hash->value())) {
parse_symbol_sysv_hash(*res);
} else {
LIEF_WARN("Can't convert DT_HASH.virtual_address into an offset ({:#x})",
dt_hash->value());
}
}
if (DynamicEntry* dt = binary_->get(DynamicEntry::TAG::GNU_HASH)) {
if (Segment* seg = binary_->segment_from_virtual_address(dt->value())) {
const auto dynsymcount = (uint64_t)(binary_->dynamic_symbols_.size());
std::unique_ptr<SpanStream> strm = seg->stream();
const uint64_t addr = dt->value();
if (strm != nullptr) {
strm->set_endian_swap(stream_->should_swap());
assert(addr >= seg->virtual_address());
uint64_t offset = addr - seg->virtual_address();
strm->setpos(offset);
binary_->gnu_hash_ = GnuHash::parse<ELF_T>(*strm, dynsymcount);
if (binary_->gnu_hash_ != nullptr) {
binary_->sizing_info_->gnu_hash = binary_->gnu_hash_->original_size();
}
}
}
}
if (config_.parse_notes) {
// Parse Note segment
// ==================
for (const Segment& segment : binary_->segments()) {
if (segment.type() != Segment::TYPE::NOTE) {
continue;
}
parse_notes(segment.file_offset(), segment.physical_size());
}
// Parse Note Sections
// ===================
for (const Section& section : binary_->sections()) {
if (section.type() != Section::TYPE::NOTE) {
continue;
}
LIEF_DEBUG("Notes from section: {}", section.name());
parse_notes(section.offset(), section.size());
}
}
// Try to parse using sections
// If we don't have any relocations, we parse all relocation sections
// otherwise, only the non-allocated sections to avoid parsing dynamic
// relocations (or plt relocations) twice.
if (config_.parse_relocations) {
bool skip_allocated_sections = !binary_->relocations_.empty();
for (const Section& section : binary_->sections()) {
if (skip_allocated_sections && section.has(Section::FLAGS::ALLOC)) {
continue;
}
if (section.type() == Section::TYPE::REL) {
parse_section_relocations<ELF_T, typename ELF_T::Elf_Rel>(section);
} else if (section.type() == Section::TYPE::RELA) {
parse_section_relocations<ELF_T, typename ELF_T::Elf_Rela>(section);
}
}
}
if (config_.parse_symbol_versions) {
link_symbol_version();
}
if (config_.parse_overlay) {
parse_overlay();
}
return ok();
}
template<class ELF_T>
ok_error_t Parser::parse_dyn_table(Segment& pt_dyn) {
// Parse the dynamic table. To process this table, we can either process
// the content of the PT_DYNAMIC segment or process the content of the PT_LOAD
// segment that wraps the dynamic table. The second approach should be
// preferred since it uses a more accurate representation.
// (c.f. samples `issue_dynamic_table.elf` provided by @lebr0nli)
using Elf_Off = typename ELF_T::Elf_Off;
std::vector<Segment*> segments;
// Find the PT_LOAD segment that wraps the PT_DYNAMIC table.
// As demonstrated in the library: ELF32_x86_library_libshellx.so
// we need to consider overlapping segments and take the "latest" one since
// this is what the loader would do.
for (const std::unique_ptr<Segment>& segment : binary_->segments_) {
if (!segment->is_load()) {
continue;
}
const uint64_t dyn_start = pt_dyn.virtual_address();
const uint64_t dyn_end = dyn_start + pt_dyn.virtual_size();
const uint64_t load_start = segment->virtual_address();
const uint64_t load_end = load_start + segment->virtual_size();
if (!(load_start <= dyn_start && dyn_start < load_end)) {
continue;
}
if (!(load_start < dyn_end && dyn_end <= load_end)) {
continue;
}
segments.push_back(segment.get());
}
binary_->sizing_info_->dynamic = pt_dyn.physical_size();
// Usually #segments is 1 but we might have > 1 for overlapping segments
LIEF_DEBUG("Nb segments: {}", segments.size());
if (segments.empty()) {
// No PT_LOAD segment wrapping up the PT_DYNAMIC table
const Elf_Off offset = pt_dyn.file_offset();
ScopedStream scoped(*stream_, offset);
return parse_dynamic_entries<ELF_T>(*scoped);
}
const Segment& load_seg = *segments.back();
LIEF_DEBUG("Dynamic content wrapped by segment LOAD: [{:#018x}, {:#018x}] "
"[{:#018x}, {:#018x}]",
load_seg.virtual_address(),
load_seg.virtual_address() + load_seg.virtual_size(),
load_seg.file_offset(),
load_seg.file_offset() + load_seg.physical_size());
span<const uint8_t> seg_content = load_seg.content();
if (seg_content.empty()) {
return make_error_code(lief_errors::corrupted);
}
int64_t rel_offset =
(int64_t)pt_dyn.virtual_address() - (int64_t)load_seg.virtual_address();
if (rel_offset < 0 || (uint64_t)rel_offset >= seg_content.size()) {
return make_error_code(lief_errors::corrupted);
}
span<const uint8_t> dynamic_content = seg_content.subspan(rel_offset);
SpanStream stream(dynamic_content);
stream.set_endian_swap(stream_->should_swap());
return parse_dynamic_entries<ELF_T>(stream);
}
template<typename ELF_T>
ok_error_t Parser::process_dynamic_table() {
{
DynamicEntry* dt_symtab = binary_->get(DynamicEntry::TAG::SYMTAB);
DynamicEntry* dt_syment = binary_->get(DynamicEntry::TAG::SYMENT);
if (dt_syment != nullptr &&
dt_syment->value() != sizeof(typename ELF_T::Elf_Sym))
{
LIEF_WARN("DT_SYMTENT is corrupted");
}
if (dt_symtab != nullptr && config_.parse_dyn_symbols) {
const uint64_t virtual_address = dt_symtab->value();
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
parse_dynamic_symbols<ELF_T>(*res);
} else {
LIEF_WARN("Can't convert DT_SYMTAB.virtual_address into an offset ({:#x})",
virtual_address);
}
}
}
{
DynamicEntry* dt_rela = binary_->get(DynamicEntry::TAG::RELA);
DynamicEntry* dt_relasz = binary_->get(DynamicEntry::TAG::RELASZ);
if (dt_rela != nullptr && dt_relasz != nullptr && config_.parse_relocations) {
const uint64_t virtual_address = dt_rela->value();
const uint64_t size = dt_relasz->value();
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
parse_dynamic_relocations<ELF_T, typename ELF_T::Elf_Rela>(*res, size);
binary_->sizing_info_->rela = size;
} else {
LIEF_WARN("Can't convert DT_RELA.virtual_address into an offset ({:#x})",
virtual_address);
}
}
}
{
DynamicEntry* dt_rel = binary_->get(DynamicEntry::TAG::REL);
DynamicEntry* dt_relsz = binary_->get(DynamicEntry::TAG::RELSZ);
if (dt_rel != nullptr && dt_relsz != nullptr && config_.parse_relocations) {
const uint64_t virtual_address = dt_rel->value();
const uint64_t size = dt_relsz->value();
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
parse_dynamic_relocations<ELF_T, typename ELF_T::Elf_Rel>(*res, size);
binary_->sizing_info_->rela = size;
} else {
LIEF_WARN("Can't convert DT_REL.virtual_address into an offset ({:#x})",
virtual_address);
}
}
}
{
DynamicEntry* dt_relr = binary_->get(DynamicEntry::TAG::RELR);
DynamicEntry* dt_relrsz = binary_->get(DynamicEntry::TAG::RELRSZ);
if (dt_relr != nullptr && dt_relrsz != nullptr && config_.parse_relocations) {
const uint64_t virtual_address = dt_relr->value();
const uint64_t size = dt_relrsz->value();
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
parse_relative_relocations<ELF_T>(*res, size);
binary_->sizing_info_->relr = size;
} else {
LIEF_WARN("Can't convert DT_RELR.virtual_address into an offset ({:#x})",
virtual_address);
}
}
}
{
DynamicEntry* dt_relr = binary_->get(DynamicEntry::TAG::ANDROID_RELR);
DynamicEntry* dt_relrsz = binary_->get(DynamicEntry::TAG::ANDROID_RELRSZ);
if (dt_relr != nullptr && dt_relrsz != nullptr && config_.parse_relocations) {
const uint64_t virtual_address = dt_relr->value();
const uint64_t size = dt_relrsz->value();
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
parse_relative_relocations<ELF_T>(*res, size);
binary_->sizing_info_->relr = size;
} else {
LIEF_WARN("Can't convert (Android)DT_RELR.virtual_address into an offset "
"({:#x})",
virtual_address);
}
}
}
{
DynamicEntry* dt_rela = binary_->get(DynamicEntry::TAG::ANDROID_RELA);
DynamicEntry* dt_relasz = binary_->get(DynamicEntry::TAG::ANDROID_RELASZ);
if (dt_rela == nullptr) {
dt_rela = binary_->get(DynamicEntry::TAG::ANDROID_REL);
dt_relasz = binary_->get(DynamicEntry::TAG::ANDROID_RELSZ);
}
if (dt_rela != nullptr && dt_relasz != nullptr && config_.parse_relocations) {
const uint64_t virtual_address = dt_rela->value();
const uint64_t size = dt_relasz->value();
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
parse_packed_relocations<ELF_T>(*res, size);
binary_->sizing_info_->android_rela = size;
} else {
LIEF_WARN("Can't convert DT_ANDROID_REL[A].virtual_address into an offset "
"({:#x})",
virtual_address);
}
}
}
{
DynamicEntry* dt_jmprel = binary_->get(DynamicEntry::TAG::JMPREL);
DynamicEntry* dt_pltrelsz = binary_->get(DynamicEntry::TAG::PLTRELSZ);
if (dt_jmprel != nullptr && dt_pltrelsz != nullptr &&
config_.parse_relocations)
{
const uint64_t virtual_address = dt_jmprel->value();
const uint64_t size = dt_pltrelsz->value();
DynamicEntry* dt_pltrel = binary_->get(DynamicEntry::TAG::PLTREL);
auto type = DynamicEntry::TAG::UNKNOWN;
if (dt_pltrel != nullptr) {
type = DynamicEntry::from_value(dt_pltrel->value(),
binary_->header().machine_type());
} else {
// Try to guess: We assume that on ELF64 -> DT_RELA and on ELF32 -> DT_REL
if constexpr (std::is_same_v<ELF_T, details::ELF64>) {
type = DynamicEntry::TAG::RELA;
} else {
type = DynamicEntry::TAG::REL;
}
}
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
type == DynamicEntry::TAG::RELA ?
parse_pltgot_relocations<ELF_T, typename ELF_T::Elf_Rela>(*res, size) :
parse_pltgot_relocations<ELF_T, typename ELF_T::Elf_Rel>(*res, size);
binary_->sizing_info_->jmprel = size;
} else {
LIEF_WARN("Can't convert DT_JMPREL.virtual_address into an offset ({:#x})",
virtual_address);
}
}
}
if (config_.parse_symbol_versions && config_.parse_dyn_symbols) {
if (DynamicEntry* dt_versym = binary_->get(DynamicEntry::TAG::VERSYM)) {
const uint64_t virtual_address = dt_versym->value();
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
parse_symbol_version(*res);
binary_->sizing_info_->versym =
binary_->dynamic_symbols_.size() * sizeof(uint16_t);
} else {
LIEF_WARN("Can't convert DT_VERSYM.virtual_address into an offset ({:#x})",
virtual_address);
}
}
}
if (config_.parse_symbol_versions) {
DynamicEntry* dt_verneed = binary_->get(DynamicEntry::TAG::VERNEED);
DynamicEntry* dt_verneed_num = binary_->get(DynamicEntry::TAG::VERNEEDNUM);
if (dt_verneed != nullptr && dt_verneed_num != nullptr) {
const uint64_t virtual_address = dt_verneed->value();
const uint32_t nb_entries =
std::min(Parser::NB_MAX_SYMBOLS,
static_cast<uint32_t>(dt_verneed_num->value()));
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
parse_symbol_version_requirement<ELF_T>(*res, nb_entries);
} else {
LIEF_WARN(
"Can't convert DT_VERNEED.virtual_address into an offset ({:#x})",
virtual_address
);
}
}
}
if (config_.parse_symbol_versions) {
DynamicEntry* dt_verdef = binary_->get(DynamicEntry::TAG::VERDEF);
DynamicEntry* dt_verdef_num = binary_->get(DynamicEntry::TAG::VERDEFNUM);
if (dt_verdef != nullptr && dt_verdef_num != nullptr) {
const uint64_t virtual_address = dt_verdef->value();
const uint32_t nb_entries =
std::min<uint32_t>(NB_MAX_SYMBOLS, dt_verdef_num->value());
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
parse_symbol_version_definition<ELF_T>(*res, nb_entries);
} else {
LIEF_WARN("Can't convert DT_VERDEF.virtual_address into an offset ({:#x})",
virtual_address);
}
}
}
return ok();
}
template<typename ELF_T>
ok_error_t Parser::parse_header() {
using Elf_Half = typename ELF_T::Elf_Half;
using Elf_Word = typename ELF_T::Elf_Word;
using Elf_Addr = typename ELF_T::Elf_Addr;
using Elf_Off = typename ELF_T::Elf_Off;
LIEF_DEBUG("[+] Parsing Header");
stream_->setpos(0);
if (auto res = stream_->read<Header::identity_t>()) {
binary_->header_.identity_ = *res;
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_ident");
return make_error_code(lief_errors::read_error);
}
if (auto res = stream_->read<Elf_Half>()) {
binary_->header_.file_type_ = Header::FILE_TYPE(*res);
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_type");
return make_error_code(lief_errors::read_error);
}
if (auto res = stream_->read<Elf_Half>()) {
binary_->header_.machine_type_ = static_cast<ARCH>(*res);
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_machine");
return make_error_code(lief_errors::read_error);
}
if (auto res = stream_->read<Elf_Word>()) {
binary_->header_.object_file_version_ = Header::VERSION(*res);
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_version");
return make_error_code(lief_errors::read_error);
}
if (auto res = stream_->read<Elf_Addr>()) {
binary_->header_.entrypoint_ = *res;
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_entry");
return make_error_code(lief_errors::read_error);
}
if (auto res = stream_->read<Elf_Off>()) {
binary_->header_.program_headers_offset_ = *res;
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_phoff");
return make_error_code(lief_errors::read_error);
}
if (auto res = stream_->read<Elf_Off>()) {
binary_->header_.section_headers_offset_ = *res;
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_shoff");
return make_error_code(lief_errors::read_error);
}
if (auto res = stream_->read<Elf_Word>()) {
binary_->header_.processor_flags_ = *res;
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_flags");
return make_error_code(lief_errors::read_error);
}
if (auto res = stream_->read<Elf_Half>()) {
binary_->header_.header_size_ = *res;
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_ehsize");
return make_error_code(lief_errors::read_error);
}
if (auto res = stream_->read<Elf_Half>()) {
binary_->header_.program_header_size_ = *res;
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_phentsize");
return make_error_code(lief_errors::read_error);
}
if (auto res = stream_->read<Elf_Half>()) {
binary_->header_.numberof_segments_ = *res;
} else {
if (auto res = stream_->read<uint8_t>()) {
binary_->header_.numberof_segments_ = *res;
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_phnum");
return make_error_code(lief_errors::read_error);
}
}
if (auto res = stream_->read<Elf_Half>()) {
binary_->header_.section_header_size_ = *res;
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_shentsize");
return make_error_code(lief_errors::read_error);
}
if (auto res = stream_->read<Elf_Half>()) {
binary_->header_.numberof_sections_ = *res;
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_shnum");
return make_error_code(lief_errors::read_error);
}
if (auto res = stream_->read<Elf_Half>()) {
binary_->header_.section_string_table_idx_ = *res;
} else {
LIEF_ERR("Can't parse Elf_Ehdr.e_shstrndx");
return make_error_code(lief_errors::read_error);
}
return ok();
}
template<typename ELF_T>
result<uint32_t>
Parser::get_numberof_dynamic_symbols(ParserConfig::DYNSYM_COUNT mtd) const {
switch (mtd) {
case ParserConfig::DYNSYM_COUNT::HASH: return nb_dynsym_hash<ELF_T>();
case ParserConfig::DYNSYM_COUNT::SECTION: return nb_dynsym_section<ELF_T>();
case ParserConfig::DYNSYM_COUNT::RELOCATIONS:
return nb_dynsym_relocations<ELF_T>();
case ParserConfig::DYNSYM_COUNT::AUTO:
default:
{
uint32_t nb_reloc = 0;
uint32_t nb_section = 0;
uint32_t nb_hash = 0;
if (auto res = get_numberof_dynamic_symbols<ELF_T>(
ParserConfig::DYNSYM_COUNT::RELOCATIONS
))
{
nb_reloc = *res;
}
if (auto res = get_numberof_dynamic_symbols<ELF_T>(
ParserConfig::DYNSYM_COUNT::SECTION
))
{
nb_section = *res;
}
if (auto res = get_numberof_dynamic_symbols<ELF_T>(
ParserConfig::DYNSYM_COUNT::HASH
))
{
nb_hash = *res;
}
LIEF_DEBUG("#dynsym.reloc: {}", nb_reloc);
LIEF_DEBUG("#dynsym.section: {}", nb_section);
LIEF_DEBUG("#dynsym.hash: {}", nb_hash);
if (nb_hash > 0 && nb_section == nb_hash) {
return nb_hash;
}
uint32_t candidate = nb_reloc;
if (nb_section < Parser::NB_MAX_SYMBOLS && nb_section > nb_reloc &&
(nb_section - nb_reloc) < Parser::DELTA_NB_SYMBOLS)
{
candidate = nb_section;
}
if (nb_hash == 0) {
return candidate;
}
if (nb_hash < Parser::NB_MAX_SYMBOLS && nb_hash > candidate &&
(nb_hash - candidate) < Parser::DELTA_NB_SYMBOLS)
{
candidate = nb_hash;
}
return candidate;
}
}
}
template<typename ELF_T>
result<uint32_t> Parser::nb_dynsym_relocations() const {
using rela_t = typename ELF_T::Elf_Rela;
using rel_t = typename ELF_T::Elf_Rel;
uint32_t nb_symbols = 0;
// Dynamic Relocations
// ===================
// RELA
// ----
DynamicEntry* dt_rela = binary_->get(DynamicEntry::TAG::RELA);
DynamicEntry* dt_relasz = binary_->get(DynamicEntry::TAG::RELASZ);
if (dt_rela != nullptr && dt_relasz != nullptr) {
const uint64_t virtual_address = dt_rela->value();
const uint64_t size = dt_relasz->value();
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
nb_symbols =
std::max(nb_symbols, max_relocation_index<ELF_T, rela_t>(*res, size));
}
}
// REL
// ---
DynamicEntry* dt_rel = binary_->get(DynamicEntry::TAG::REL);
DynamicEntry* dt_relsz = binary_->get(DynamicEntry::TAG::RELSZ);
if (dt_rel != nullptr && dt_relsz != nullptr) {
const uint64_t virtual_address = dt_rel->value();
const uint64_t size = dt_relsz->value();
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
nb_symbols =
std::max(nb_symbols, max_relocation_index<ELF_T, rel_t>(*res, size));
}
}
// Parse PLT/GOT Relocations
// ==========================
DynamicEntry* dt_jmprel = binary_->get(DynamicEntry::TAG::JMPREL);
DynamicEntry* dt_pltrelsz = binary_->get(DynamicEntry::TAG::PLTRELSZ);
if (dt_jmprel != nullptr && dt_pltrelsz != nullptr) {
const uint64_t virtual_address = dt_jmprel->value();
const uint64_t size = dt_pltrelsz->value();
DynamicEntry* dt_pltrel = binary_->get(DynamicEntry::TAG::PLTREL);
auto type = DynamicEntry::TAG::UNKNOWN;
if (dt_pltrel != nullptr) {
type = DynamicEntry::from_value(dt_pltrel->value(),
binary_->header().machine_type());
} else {
// Try to guess: We assume that on ELF64 -> DT_RELA and on ELF32 -> DT_REL
if constexpr (std::is_same_v<ELF_T, details::ELF64>) {
type = DynamicEntry::TAG::RELA;
} else {
type = DynamicEntry::TAG::REL;
}
}
if (auto res = binary_->virtual_address_to_offset(virtual_address)) {
if (type == DynamicEntry::TAG::RELA) {
nb_symbols =
std::max(nb_symbols, max_relocation_index<ELF_T, rela_t>(*res, size));
} else {
nb_symbols =
std::max(nb_symbols, max_relocation_index<ELF_T, rel_t>(*res, size));
}
}
}
return nb_symbols;
}
template<typename ELF_T, typename REL_T>
uint32_t Parser::max_relocation_index(uint64_t relocations_offset,
uint64_t size) const {
static_assert(std::is_same_v<REL_T, typename ELF_T::Elf_Rel> ||
std::is_same_v<REL_T, typename ELF_T::Elf_Rela>,
"REL_T must be Elf_Rel || Elf_Rela");
const uint8_t shift = ELF_T::r_info_shift;
;
const auto nb_entries = static_cast<uint32_t>(size / sizeof(REL_T));
uint32_t idx = 0;
stream_->setpos(relocations_offset);
for (uint32_t i = 0; i < nb_entries; ++i) {
auto reloc_entry = stream_->read<REL_T>();
if (!reloc_entry) {
break;
}
idx = std::max(idx, static_cast<uint32_t>(reloc_entry->r_info >> shift));
}
return idx + 1;
} // max_relocation_index
template<typename ELF_T>
result<uint32_t> Parser::nb_dynsym_section() const {
using Elf_Sym = typename ELF_T::Elf_Sym;
using Elf_Off = typename ELF_T::Elf_Off;
Section* dynsym_sec = binary_->get(Section::TYPE::DYNSYM);
if (dynsym_sec == nullptr) {
return 0;
}
const Elf_Off section_size = dynsym_sec->size();
const auto nb_symbols = static_cast<uint32_t>((section_size / sizeof(Elf_Sym)));
return nb_symbols;
}
template<typename ELF_T>
result<uint32_t> Parser::nb_dynsym_hash() const {
if (binary_->has(DynamicEntry::TAG::HASH)) {
return nb_dynsym_sysv_hash<ELF_T>();
}
if (binary_->has(DynamicEntry::TAG::GNU_HASH)) {
return nb_dynsym_gnu_hash<ELF_T>();
}
return 0;
}
template<typename ELF_T>
result<uint32_t> Parser::nb_dynsym_sysv_hash() const {
using Elf_Off = typename ELF_T::Elf_Off;
const DynamicEntry* dyn_hash = binary_->get(DynamicEntry::TAG::HASH);
if (dyn_hash == nullptr) {
LIEF_ERR("Can't find DT_GNU_HASH");
return make_error_code(lief_errors::not_found);
}
Elf_Off sysv_hash_offset = 0;
if (auto res = binary_->virtual_address_to_offset(dyn_hash->value())) {
sysv_hash_offset = *res;
} else {
return make_error_code(res.error());
}
// From the doc: 'so nchain should equal the number of symbol table entries.'
stream_->setpos(sysv_hash_offset + sizeof(uint32_t));
auto nb_symbols = stream_->read<uint32_t>();
if (nb_symbols) {
return nb_symbols;
}
return 0;
}
template<typename ELF_T>
result<uint32_t> Parser::nb_dynsym_gnu_hash() const {
const DynamicEntry* dyn_hash = binary_->get(DynamicEntry::TAG::GNU_HASH);
if (dyn_hash == nullptr) {
LIEF_ERR("Can't find DT_GNU_HASH");
return make_error_code(lief_errors::not_found);
}
const uint64_t addr = dyn_hash->value();
if (Segment* seg = binary_->segment_from_virtual_address(addr)) {
std::unique_ptr<SpanStream> stream = seg->stream();
if (stream == nullptr) {
return make_error_code(lief_errors::read_error);
}
uint64_t rel_offset = addr - seg->virtual_address();
stream->setpos(rel_offset);
stream->set_endian_swap(stream_->should_swap());
return GnuHash::nb_symbols<ELF_T>(*stream);
}
return make_error_code(lief_errors::not_found);
}
template<typename ELF_T>
ok_error_t Parser::parse_sections() {
using Elf_Shdr = typename ELF_T::Elf_Shdr;
using Elf_Off = typename ELF_T::Elf_Off;
LIEF_DEBUG("Parsing Section");
const Elf_Off shdr_offset = binary_->header_.section_headers_offset();
const auto numberof_sections = binary_->header_.numberof_sections();
const bool is_memory_stream = memory_address_ > 0;
// Sections are usually not mapped so logging errors is not relevant for
// in-memory parsing
logging::Scoped scope(is_memory_stream ? logging::LEVEL::OFF :
logging::get_level());
stream_->setpos(shdr_offset);
std::unordered_map<Section*, size_t> sections_names;
DataHandler::Handler& handler = *binary_->datahandler_;
const ARCH arch = binary_->header().machine_type();
for (size_t i = 0; i < numberof_sections; ++i) {
LIEF_DEBUG(" Elf_Shdr#{:02d}.offset: {:#x} ", i, stream_->pos());
const auto shdr = stream_->read<Elf_Shdr>();
if (!shdr) {
LIEF_ERR(" Can't parse section #{:02d}", i);
break;
}
auto section = std::unique_ptr<Section>(new Section(*shdr, arch));
section->datahandler_ = binary_->datahandler_.get();
const uint64_t section_start = section->file_offset();
bool access_content = true;
if (section_start > stream_->size() ||
section->size() > stream_->size() - section_start)
{
access_content = false;
if (section->type() != Section::TYPE::NOBITS) {
LIEF_WARN("Can't access the content of section #{}", i);
}
}
if (section->size() == 0 && section->file_offset() > 0 && access_content) {
// Even if the size is 0, it is worth creating the node
handler.create(section->file_offset(), 0, DataHandler::Node::SECTION);
}
// Only if it contains data (with bits)
if (section->size() > 0 && access_content) {
int64_t read_size = section->size();
if (static_cast<int32_t>(read_size) < 0) {
LIEF_WARN("Section #{} is {} bytes large. Only the first {} bytes will be "
"taken into account",
i, read_size, Section::MAX_SECTION_SIZE);
read_size = Section::MAX_SECTION_SIZE;
}
if (read_size > Section::MAX_SECTION_SIZE) {
LIEF_WARN("Section #{} is {} bytes large. Only the first {} bytes will be "
"taken into account",
i, read_size, Section::MAX_SECTION_SIZE);
read_size = Section::MAX_SECTION_SIZE;
}
handler.create(section->file_offset(), read_size,
DataHandler::Node::SECTION);
const Elf_Off offset_to_content = section->file_offset();
auto alloc =
binary_->datahandler_->reserve(section->file_offset(), read_size);
if (!alloc) {
LIEF_ERR("Can't allocate memory");
break;
}
/* The DataHandlerStream interface references ELF data that are
* located in the ELF::DataHandler. Therefore, we can skip reading
* the data since they are already present in the data handler.
* This optimization saves memory (which is also performed in
* parse_segments<>(...))
*/
if (stream_->type() != BinaryStream::STREAM_TYPE::ELF_DATA_HANDLER) {
std::vector<uint8_t> sec_content;
if (!stream_->peek_data(sec_content, offset_to_content, read_size)) {
if (section->type() != Section::TYPE::NOBITS) {
LIEF_WARN(" Unable to get content of section #{:d}", i);
}
} else {
section->content(std::move(sec_content));
}
}
}
sections_idx_[i] = section.get();
sections_names[section.get()] = shdr->sh_name;
binary_->sections_.push_back(std::move(section));
}
LIEF_DEBUG(" Parse section names");
// Parse name
if (binary_->header_.section_name_table_idx() >= binary_->sections_.size()) {
LIEF_WARN("The .shstr index is out of range of the section table");
return ok();
}
const size_t section_string_index = binary_->header_.section_name_table_idx();
const std::unique_ptr<Section>& string_section =
binary_->sections_[section_string_index];
for (std::unique_ptr<Section>& section : binary_->sections_) {
const auto it_name_idx = sections_names.find(section.get());
if (it_name_idx == sections_names.end()) {
LIEF_WARN("Missing name_idx for section at offset {:#x}",
section->file_offset());
continue;
}
const size_t name_offset = it_name_idx->second;
auto name =
stream_->peek_string_at(string_section->file_offset() + name_offset);
if (!name) {
LIEF_ERR("Can't read section name for section {:#x}",
section->file_offset());
break;
}
section->name(*name);
}
return ok();
}
template<typename ELF_T>
ok_error_t Parser::parse_segments() {
using Elf_Phdr = typename ELF_T::Elf_Phdr;
using Elf_Off = typename ELF_T::Elf_Off;
LIEF_DEBUG("Parsing segments");
const Header& hdr = binary_->header();
const Elf_Off segment_headers_offset = hdr.program_headers_offset();
const auto nbof_segments =
std::min<uint32_t>(hdr.numberof_segments(), Parser::NB_MAX_SEGMENTS);
stream_->setpos(segment_headers_offset);
const ARCH arch = hdr.machine_type();
const Header::OS_ABI os = hdr.identity_os_abi();
for (size_t i = 0; i < nbof_segments; ++i) {
const auto elf_phdr = stream_->read<Elf_Phdr>();
if (!elf_phdr) {
LIEF_ERR("Can't parse segement #{:d}", i);
break;
}
auto segment = std::unique_ptr<Segment>(new Segment(*elf_phdr, arch, os));
segment->datahandler_ = binary_->datahandler_.get();
if (0 < segment->physical_size() &&
segment->physical_size() < Parser::MAX_SEGMENT_SIZE)
{
uint64_t read_size = segment->physical_size();
if (read_size > Parser::MAX_SEGMENT_SIZE) {
LIEF_WARN("Segment #{} is {} bytes large. Only the first {} bytes will be "
"taken into account",
i, read_size, Parser::MAX_SEGMENT_SIZE);
read_size = Parser::MAX_SEGMENT_SIZE;
}
if (read_size > stream_->size()) {
LIEF_WARN("Segment #{} has a physical size larger than the current stream "
"size ({} > {}). "
"The content will be truncated with the stream size.",
i, read_size, stream_->size());
read_size = stream_->size();
}
segment->datahandler_->create(segment->file_offset(), read_size,
DataHandler::Node::SEGMENT);
segment->handler_size_ = read_size;
const bool corrupted_offset =
segment->file_offset() > stream_->size() ||
(segment->file_offset() + read_size) > stream_->size();
if (!corrupted_offset) {
const Elf_Off offset_to_content = segment->file_offset();
auto alloc =
binary_->datahandler_->reserve(segment->file_offset(), read_size);
if (!alloc) {
LIEF_ERR("Can't allocate memory");
break;
}
/* The DataHandlerStream interface references ELF data that are
* located in the ELF::DataHandler. Therefore, we can skip reading
* the data since they are already present in the data handler.
* This optimization saves memory (which is also performed in
* parse_sections<>(...))
*/
if (stream_->type() != BinaryStream::STREAM_TYPE::ELF_DATA_HANDLER) {
std::vector<uint8_t> seg_content;
if (stream_->peek_data(seg_content, offset_to_content, read_size)) {
segment->content(std::move(seg_content));
} else {
LIEF_ERR("Unable to get the content of segment #{:d}", i);
}
}
if (segment->is_interpreter()) {
auto interpreter = stream_->peek_string_at(offset_to_content, read_size);
if (!interpreter) {
LIEF_ERR("Can't read the interpreter string");
} else {
binary_->interpreter_ = *interpreter;
binary_->sizing_info_->interpreter = read_size;
}
}
}
} else {
segment->handler_size_ = segment->physical_size();
segment->datahandler_->create(segment->file_offset(),
segment->physical_size(),
DataHandler::Node::SEGMENT);
}
for (std::unique_ptr<Section>& section : binary_->sections_) {
if (check_section_in_segment(*section, *segment.get())) {
section->segments_.push_back(segment.get());
segment->sections_.push_back(section.get());
}
}
binary_->segments_.push_back(std::move(segment));
}
return ok();
}
template<typename ELF_T>
ok_error_t Parser::parse_packed_relocations(uint64_t offset, uint64_t size) {
using Elf_Rela = typename ELF_T::Elf_Rela;
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;
LIEF_DEBUG("Parsing Android packed relocations");
if (size < 4) {
LIEF_ERR("Invalid Android packed relocation header");
return make_error_code(lief_errors::read_error);
}
if (offset >= stream_->size()) {
LIEF_ERR("Android packed relocations: offset is out of range");
return make_error_code(lief_errors::read_error);
}
size = std::min<uint64_t>(size, stream_->size() - offset);
const auto* raw = stream_->peek_array<uint8_t>(offset, size);
if (raw == nullptr) {
LIEF_ERR("Can't read the Android packed relocation table");
return make_error_code(lief_errors::read_error);
}
SpanStream table_stream(raw, size);
ScopedStream rel_stream(table_stream);
const auto H0 = rel_stream->read<char>().value_or(0);
const auto H1 = rel_stream->read<char>().value_or(0);
const auto H2 = rel_stream->read<char>().value_or(0);
const auto H3 = rel_stream->read<char>().value_or(0);
LIEF_DEBUG("Header: {} {} {} {}", H0, H1, H2, H3);
// Check for the Magic: APS2
if (H0 != 'A' || H1 != 'P' || H2 != 'S' || H3 != '2') {
LIEF_ERR("Invalid Android packed relocation magic header: "
"{} {} {} {}",
H0, H1, H2, H3);
return make_error_code(lief_errors::read_error);
}
auto res_nb_relocs = rel_stream->read_sleb128();
if (!res_nb_relocs) {
LIEF_ERR("Can't read number of relocations");
return make_error_code(lief_errors::read_error);
}
auto res_rels_offset = rel_stream->read_sleb128();
if (!res_rels_offset) {
LIEF_ERR("Can't read offset");
return make_error_code(lief_errors::read_error);
}
uint64_t nb_relocs = *res_nb_relocs;
uint64_t r_offset = *res_rels_offset;
uint64_t addend = 0;
const ARCH arch = binary_->header().machine_type();
LIEF_DEBUG("Nb relocs: {}", nb_relocs);
if (nb_relocs > NB_MAX_RELOCATIONS) {
LIEF_WARN("Android packed relocations: the number of relocations ({}) "
"exceeds the limit ({}). The table is likely corrupted",
nb_relocs, NB_MAX_RELOCATIONS);
return make_error_code(lief_errors::corrupted);
}
while (nb_relocs > 0) {
auto nb_reloc_group_r = rel_stream->read_sleb128();
if (!nb_reloc_group_r) {
break;
}
uint64_t nb_reloc_group = *nb_reloc_group_r;
LIEF_DEBUG(" Nb relocs in group: {}", nb_reloc_group);
if (nb_reloc_group > nb_relocs) {
break;
}
nb_relocs -= nb_reloc_group;
auto group_flag_r = rel_stream->read_sleb128();
if (!group_flag_r) {
LIEF_ERR("Can't read group flag");
break;
}
uint64_t group_flag = *group_flag_r;
const bool g_by_info = group_flag & GROUPED_BY_INFO_FLAG;
const bool g_by_offset_delta = group_flag & GROUPED_BY_OFFSET_DELTA_FLAG;
const bool g_by_addend = group_flag & GROUPED_BY_ADDEND_FLAG;
const bool g_has_addend = group_flag & GROUP_HAS_ADDEND_FLAG;
uint64_t group_off_delta =
g_by_offset_delta ? rel_stream->read_sleb128().value_or(0) : 0;
uint64_t groupr_info = g_by_info ? rel_stream->read_sleb128().value_or(0) : 0;
if (g_by_addend && g_has_addend) {
addend += rel_stream->read_sleb128().value_or(0);
}
if (!g_has_addend) {
addend = 0;
}
for (size_t i = 0; i < nb_reloc_group; ++i) {
if (!*rel_stream) {
break;
}
r_offset += g_by_offset_delta ? group_off_delta :
rel_stream->read_sleb128().value_or(0);
uint64_t info =
g_by_info ? groupr_info : rel_stream->read_sleb128().value_or(0);
if (g_has_addend && !g_by_addend) {
addend += rel_stream->read_sleb128().value_or(0);
}
Elf_Rela R;
R.r_info = info;
R.r_addend = addend;
R.r_offset = r_offset;
auto reloc = std::unique_ptr<Relocation>(new Relocation(
R, Relocation::PURPOSE::DYNAMIC, Relocation::ENCODING::ANDROID_SLEB, arch
));
bind_symbol(*reloc);
insert_relocation(std::move(reloc));
}
}
return ok();
}
template<typename ELF_T>
ok_error_t Parser::parse_relative_relocations(uint64_t offset, uint64_t size) {
LIEF_DEBUG("Parsing relative relocations");
using Elf_Relr = typename ELF_T::uint;
using Elf_Addr = typename ELF_T::uint;
ScopedStream rel_stream(*stream_, offset);
Elf_Addr base = 0;
const ARCH arch = binary_->header().machine_type();
Relocation::TYPE type = Relocation::TYPE::UNKNOWN;
switch (arch) {
case ARCH::AARCH64: type = Relocation::TYPE::AARCH64_RELATIVE; break;
case ARCH::X86_64: type = Relocation::TYPE::X86_64_RELATIVE; break;
case ARCH::ARM: type = Relocation::TYPE::ARM_RELATIVE; break;
case ARCH::HEXAGON: type = Relocation::TYPE::HEX_RELATIVE; break;
case ARCH::PPC64: type = Relocation::TYPE::PPC64_RELATIVE; break;
case ARCH::PPC: type = Relocation::TYPE::PPC_RELATIVE; break;
case ARCH::I386:
case ARCH::IAMCU: type = Relocation::TYPE::X86_RELATIVE; break;
default: break;
}
size_t nb_relocs = 0;
while (rel_stream->pos() < (offset + size)) {
if (nb_relocs >= NB_MAX_RELOCATIONS) {
LIEF_WARN("RELR relocations: the number of relocations exceeds the limit "
"({}). The table is likely corrupted",
NB_MAX_RELOCATIONS);
break;
}
auto opt_relr = rel_stream->read<Elf_Relr>();
if (!opt_relr) {
break;
}
Elf_Relr rel = *opt_relr;
if ((rel & 1) == 0) {
Elf_Addr r_offset = rel;
auto reloc =
std::make_unique<Relocation>(r_offset, type, Relocation::ENCODING::RELR);
reloc->purpose(Relocation::PURPOSE::DYNAMIC);
insert_relocation(std::move(reloc));
++nb_relocs;
base = rel + sizeof(Elf_Addr);
} else {
for (Elf_Addr offset = base; (rel >>= 1) != 0; offset += sizeof(Elf_Addr)) {
if ((rel & 1) != 0) {
Elf_Addr r_offset = offset;
auto reloc = std::make_unique<Relocation>(r_offset, type,
Relocation::ENCODING::RELR);
reloc->purpose(Relocation::PURPOSE::DYNAMIC);
insert_relocation(std::move(reloc));
++nb_relocs;
}
}
base += (8 * sizeof(Elf_Relr) - 1) * sizeof(Elf_Addr);
}
}
return ok();
}
template<typename ELF_T, typename REL_T>
ok_error_t Parser::parse_dynamic_relocations(uint64_t relocations_offset,
uint64_t size) {
static_assert(std::is_same_v<REL_T, typename ELF_T::Elf_Rel> ||
std::is_same_v<REL_T, typename ELF_T::Elf_Rela>,
"REL_T must be Elf_Rel || Elf_Rela");
LIEF_DEBUG("Parsing dynamic relocations");
// Already parsed
if (binary_->dynamic_relocations().size() > 0) {
return ok();
}
auto nb_entries = static_cast<uint32_t>(size / sizeof(REL_T));
nb_entries = std::min<uint32_t>(nb_entries, Parser::NB_MAX_RELOCATIONS);
binary_->relocations_.reserve(nb_entries);
stream_->setpos(relocations_offset);
const ARCH arch = binary_->header().machine_type();
const Relocation::ENCODING enc = std::is_same_v<REL_T, typename ELF_T::Elf_Rel> ?
Relocation::ENCODING::REL :
Relocation::ENCODING::RELA;
for (uint32_t i = 0; i < nb_entries; ++i) {
const auto raw_reloc = stream_->read<REL_T>();
if (!raw_reloc) {
break;
}
auto reloc = std::unique_ptr<Relocation>(new Relocation(
std::move(*raw_reloc), Relocation::PURPOSE::DYNAMIC, enc, arch
));
bind_symbol(*reloc);
insert_relocation(std::move(reloc));
}
return ok();
} // build_dynamic_reclocations
template<typename ELF_T>
ok_error_t Parser::parse_symtab_symbols(const Section& symtab_section,
const Section& string_section) {
static constexpr size_t MAX_RESERVED_SYMBOLS = 10000;
using Elf_Sym = typename ELF_T::Elf_Sym;
LIEF_DEBUG("Parsing symtab symbols");
LIEF::SpanStream stream(symtab_section.content());
stream.set_endian_swap(stream_->should_swap());
const auto nb_symbols = static_cast<uint32_t>((symtab_section.size() /
sizeof(typename ELF_T::Elf_Sym)));
const size_t nb_reserved = std::min<size_t>(nb_symbols, MAX_RESERVED_SYMBOLS);
binary_->symtab_symbols_.reserve(nb_reserved);
const ARCH arch = binary_->header().machine_type();
size_t sym_idx = 0;
while (stream) {
const auto raw_sym = stream.read<Elf_Sym>();
if (!raw_sym) {
return make_error_code(raw_sym.error());
}
auto symbol = std::unique_ptr<Symbol>(new Symbol(std::move(*raw_sym), arch));
const auto name_offset = string_section.file_offset() + raw_sym->st_name;
if (auto symbol_name = stream_->peek_string_at(name_offset)) {
symbol->name(std::move(*symbol_name));
} else {
LIEF_ERR("Can't read the symbol's name for symbol #{}", sym_idx);
}
link_symbol_section(*symbol);
binary_->symtab_symbols_.push_back(std::move(symbol));
++sym_idx;
}
return ok();
}
template<typename ELF_T>
ok_error_t Parser::parse_dynamic_symbols(uint64_t offset) {
using Elf_Sym = typename ELF_T::Elf_Sym;
using Elf_Off = typename ELF_T::Elf_Off;
static constexpr size_t MAX_RESERVED_SYMBOLS = 10000;
LIEF_DEBUG("Parsing dynamic symbols");
auto res = get_numberof_dynamic_symbols<ELF_T>(config_.count_mtd);
if (!res) {
LIEF_ERR("Fail to get the number of dynamic symbols with the current counting "
"method");
return make_error_code(lief_errors::parsing_error);
}
const uint32_t nb_symbols = res.value();
const Elf_Off dynamic_symbols_offset = offset;
const Elf_Off string_offset = get_dynamic_string_table();
LIEF_DEBUG(" - Number of symbols counted: {:d}", nb_symbols);
LIEF_DEBUG(" - Table Offset: {:#x}", dynamic_symbols_offset);
LIEF_DEBUG(" - String Table Offset: {:#x}", string_offset);
if (string_offset == 0) {
LIEF_WARN("Unable to find the .dynstr section");
return make_error_code(lief_errors::parsing_error);
}
size_t nb_reserved = std::min<size_t>(nb_symbols, MAX_RESERVED_SYMBOLS);
binary_->dynamic_symbols_.reserve(nb_reserved);
stream_->setpos(dynamic_symbols_offset);
for (size_t i = 0; i < nb_symbols; ++i) {
const auto symbol_header = stream_->read<Elf_Sym>();
if (!symbol_header) {
LIEF_DEBUG("Break on symbol #{:d}", i);
break;
}
auto symbol =
std::unique_ptr<Symbol>(new Symbol(std::move(*symbol_header),
binary_->header().machine_type()));
if (symbol_header->st_name > 0) {
auto name = stream_->peek_string_at(string_offset + symbol_header->st_name);
if (!name) {
break;
}
if (name->empty() && i > 0) {
LIEF_DEBUG("Symbol name #{:d} is empty", i);
}
symbol->name(std::move(*name));
}
link_symbol_section(*symbol);
binary_->dynamic_symbols_.push_back(std::move(symbol));
}
binary_->sizing_info_->dynsym =
binary_->dynamic_symbols_.size() * sizeof(Elf_Sym);
if (const auto* dt_strsz = binary_->get(DynamicEntry::TAG::STRSZ)) {
binary_->sizing_info_->dynstr = dt_strsz->value();
}
return ok();
}
template<typename ELF_T>
ok_error_t Parser::parse_dynamic_entries(BinaryStream& stream) {
using Elf_Dyn = typename ELF_T::Elf_Dyn;
using uint__ = typename ELF_T::uint;
using Elf_Addr = typename ELF_T::Elf_Addr;
using Elf_Off = typename ELF_T::Elf_Off;
LIEF_DEBUG("Parsing dynamic entries");
uint32_t max_nb_entries = stream.size() / sizeof(Elf_Dyn);
max_nb_entries =
std::min<uint32_t>(max_nb_entries, Parser::NB_MAX_DYNAMIC_ENTRIES);
Elf_Off dynamic_string_offset = get_dynamic_string_table(&stream);
bool end_of_dynamic = false;
uint32_t count = 0;
while (stream && count < max_nb_entries) {
const auto res_entry = stream.read<Elf_Dyn>();
if (!res_entry) {
break;
}
++count;
const auto entry = *res_entry;
std::unique_ptr<DynamicEntry> dynamic_entry;
const ARCH arch = binary_->header().machine_type();
switch (DynamicEntry::from_value(entry.d_tag, arch)) {
case DynamicEntry::TAG::NEEDED:
{
dynamic_entry = std::make_unique<DynamicEntryLibrary>(entry, arch);
auto library_name = stream_->peek_string_at(dynamic_string_offset +
dynamic_entry->value());
if (!library_name) {
LIEF_ERR("Can't read library name for DT_NEEDED entry");
break;
}
dynamic_entry->as<DynamicEntryLibrary>()->name(std::move(*library_name));
break;
}
case DynamicEntry::TAG::SONAME:
{
dynamic_entry = std::make_unique<DynamicSharedObject>(entry, arch);
auto sharename = stream_->peek_string_at(dynamic_string_offset +
dynamic_entry->value());
if (!sharename) {
LIEF_ERR("Can't read library name for DT_SONAME entry");
break;
}
dynamic_entry->as<DynamicSharedObject>()->name(std::move(*sharename));
break;
}
case DynamicEntry::TAG::AUXILIARY:
{
dynamic_entry = std::make_unique<DynamicEntryAuxiliary>(entry, arch);
auto name = stream_->peek_string_at(dynamic_string_offset +
dynamic_entry->value());
if (!name) {
LIEF_ERR("Can't read library name for DT_AUXILIARY entry");
break;
}
dynamic_entry->as<DynamicEntryAuxiliary>()->name(std::move(*name));
break;
}
case DynamicEntry::TAG::FILTER:
{
dynamic_entry = std::make_unique<DynamicEntryFilter>(entry, arch);
auto name = stream_->peek_string_at(dynamic_string_offset +
dynamic_entry->value());
if (!name) {
LIEF_ERR("Can't read library name for DT_FILTER entry");
break;
}
dynamic_entry->as<DynamicEntryFilter>()->name(std::move(*name));
break;
}
case DynamicEntry::TAG::RPATH:
{
dynamic_entry = std::make_unique<DynamicEntryRpath>(entry, arch);
auto name = stream_->peek_string_at(dynamic_string_offset +
dynamic_entry->value());
if (!name) {
LIEF_ERR("Can't read rpath string value for DT_RPATH");
break;
}
dynamic_entry->as<DynamicEntryRpath>()->rpath(std::move(*name));
break;
}
case DynamicEntry::TAG::RUNPATH:
{
dynamic_entry = std::make_unique<DynamicEntryRunPath>(entry, arch);
auto name = stream_->peek_string_at(dynamic_string_offset +
dynamic_entry->value());
if (!name) {
LIEF_ERR("Can't read runpath string value for DT_RUNPATH");
break;
}
dynamic_entry->as<DynamicEntryRunPath>()->runpath(std::move(*name));
break;
}
case DynamicEntry::TAG::FLAGS_1:
case DynamicEntry::TAG::FLAGS:
{
dynamic_entry = std::make_unique<DynamicEntryFlags>(entry, arch);
break;
}
case DynamicEntry::TAG::SYMTAB:
case DynamicEntry::TAG::SYMENT:
case DynamicEntry::TAG::RELA:
case DynamicEntry::TAG::RELASZ:
case DynamicEntry::TAG::REL:
case DynamicEntry::TAG::RELSZ:
case DynamicEntry::TAG::JMPREL:
case DynamicEntry::TAG::PLTRELSZ:
case DynamicEntry::TAG::PLTREL:
case DynamicEntry::TAG::VERSYM:
case DynamicEntry::TAG::VERNEED:
case DynamicEntry::TAG::VERNEEDNUM:
case DynamicEntry::TAG::VERDEF:
case DynamicEntry::TAG::VERDEFNUM:
{
dynamic_entry = std::make_unique<DynamicEntry>(entry, arch);
break;
}
case DynamicEntry::TAG::FINI_ARRAY:
case DynamicEntry::TAG::INIT_ARRAY:
case DynamicEntry::TAG::PREINIT_ARRAY:
{
dynamic_entry = std::make_unique<DynamicEntryArray>(entry, arch);
break;
}
case DynamicEntry::TAG::DT_NULL_:
{
dynamic_entry = std::make_unique<DynamicEntry>(entry, arch);
end_of_dynamic = true;
break;
}
default:
{
dynamic_entry = std::make_unique<DynamicEntry>(entry, arch);
}
}
if (dynamic_entry != nullptr) {
binary_->dynamic_entries_.push_back(std::move(dynamic_entry));
} else {
LIEF_WARN("Null dynamic_entry");
}
if (end_of_dynamic) {
break;
}
}
// Check for INIT array
// ====================
if (DynamicEntry* dt_init_array = binary_->get(DynamicEntry::TAG::INIT_ARRAY)) {
if (DynamicEntry* dt_init_arraysz =
binary_->get(DynamicEntry::TAG::INIT_ARRAYSZ))
{
binary_->sizing_info_->init_array = dt_init_arraysz->value();
std::vector<uint64_t>& array =
dt_init_array->as<DynamicEntryArray>()->array();
const auto nb_functions =
static_cast<uint32_t>(dt_init_arraysz->value() / sizeof(uint__));
if (auto offset = binary_->virtual_address_to_offset(dt_init_array->value()))
{
stream_->setpos(*offset);
for (size_t i = 0; i < nb_functions; ++i) {
if (auto val = stream_->read<Elf_Addr>()) {
array.push_back(*val);
} else {
break;
}
}
}
} else {
LIEF_WARN("The binary is not consistent. Found DT_INIT_ARRAY but missing "
"DT_INIT_ARRAYSZ");
}
}
// Check for FINI array
// ====================
if (DynamicEntry* dt_fini_array = binary_->get(DynamicEntry::TAG::FINI_ARRAY)) {
if (DynamicEntry* dt_fini_arraysz =
binary_->get(DynamicEntry::TAG::FINI_ARRAYSZ))
{
binary_->sizing_info_->fini_array = dt_fini_arraysz->value();
std::vector<uint64_t>& array =
dt_fini_array->as<DynamicEntryArray>()->array();
const auto nb_functions =
static_cast<uint32_t>(dt_fini_arraysz->value() / sizeof(uint__));
if (auto offset = binary_->virtual_address_to_offset(dt_fini_array->value()))
{
stream_->setpos(*offset);
for (size_t i = 0; i < nb_functions; ++i) {
if (auto val = stream_->read<Elf_Addr>()) {
array.push_back(*val);
} else {
break;
}
}
}
} else {
LIEF_WARN("The binary is not consistent. Found DT_FINI_ARRAY but missing "
"DT_FINI_ARRAYSZ");
}
}
// Check for PREINIT array
// =======================
if (DynamicEntry* dt_preini_array =
binary_->get(DynamicEntry::TAG::PREINIT_ARRAY))
{
if (DynamicEntry* dt_preinit_arraysz =
binary_->get(DynamicEntry::TAG::PREINIT_ARRAYSZ))
{
binary_->sizing_info_->preinit_array = dt_preinit_arraysz->value();
std::vector<uint64_t>& array =
dt_preini_array->as<DynamicEntryArray>()->array();
const auto nb_functions =
static_cast<uint32_t>(dt_preinit_arraysz->value() / sizeof(uint__));
if (auto offset =
binary_->virtual_address_to_offset(dt_preini_array->value()))
{
stream_->setpos(static_cast<Elf_Off>(*offset));
for (size_t i = 0; i < nb_functions; ++i) {
if (auto val = stream_->read<Elf_Addr>()) {
array.push_back(*val);
} else {
break;
}
}
}
} else {
LIEF_WARN("The binary is not consistent. Found DT_PREINIT_ARRAY but missing "
"DT_PREINIT_ARRAYSZ");
}
}
return ok();
}
template<typename ELF_T, typename REL_T>
ok_error_t Parser::parse_pltgot_relocations(uint64_t offset, uint64_t size) {
static_assert(std::is_same_v<REL_T, typename ELF_T::Elf_Rel> ||
std::is_same_v<REL_T, typename ELF_T::Elf_Rela>,
"REL_T must be Elf_Rel or Elf_Rela");
using Elf_Off = typename ELF_T::Elf_Off;
// Already Parsed
if (binary_->pltgot_relocations().size() > 0) {
return ok();
}
const Elf_Off offset_relocations = offset;
auto nb_entries = static_cast<uint32_t>(size / sizeof(REL_T));
nb_entries = std::min<uint32_t>(nb_entries, Parser::NB_MAX_RELOCATIONS);
const ARCH arch = binary_->header_.machine_type();
const Relocation::ENCODING enc = std::is_same_v<REL_T, typename ELF_T::Elf_Rel> ?
Relocation::ENCODING::REL :
Relocation::ENCODING::RELA;
stream_->setpos(offset_relocations);
for (uint32_t i = 0; i < nb_entries; ++i) {
const auto rel_hdr = stream_->read<REL_T>();
if (!rel_hdr) {
break;
}
auto reloc = std::unique_ptr<Relocation>(
new Relocation(std::move(*rel_hdr), Relocation::PURPOSE::PLTGOT, enc, arch)
);
bind_symbol(*reloc);
insert_relocation(std::move(reloc));
}
return ok();
}
struct RelocationSetEq {
bool operator()(const Relocation* lhs, const Relocation* rhs) const {
bool check = lhs->address() == rhs->address() && lhs->type() == rhs->type() &&
lhs->addend() == rhs->addend() && lhs->info() == rhs->info() &&
lhs->has_symbol() == rhs->has_symbol();
if (!check) {
return false;
}
if (lhs->has_symbol()) { // The fact that rhs->has_symbol is checked previously
return lhs->symbol()->name() == rhs->symbol()->name();
}
return check;
}
};
struct RelocationSetHash {
size_t operator()(const Relocation* reloc) const {
Hash hasher;
hasher.process(reloc->address())
.process(reloc->type())
.process(reloc->info())
.process(reloc->addend());
const Symbol* sym = reloc->symbol();
if (sym != nullptr) {
hasher.process(sym->name());
}
return hasher.value();
}
};
template<typename ELF_T, typename REL_T>
ok_error_t Parser::parse_section_relocations(const Section& section) {
using Elf_Rel = typename ELF_T::Elf_Rel;
using Elf_Rela = typename ELF_T::Elf_Rela;
static_assert(std::is_same_v<REL_T, Elf_Rel> || std::is_same_v<REL_T, Elf_Rela>,
"REL_T must be Elf_Rel || Elf_Rela");
// A relocation section can reference two other sections: a symbol table,
// identified by the sh_link section header entry, and a section to modify,
// identified by the sh_info
// See Figure 4-12 in
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html#sh_link
Section* applies_to = nullptr;
const size_t sh_info = section.information();
if (sh_info > 0 && sh_info < binary_->sections_.size()) {
applies_to = binary_->sections_[sh_info].get();
}
Section* symbol_table = nullptr;
if (section.link() > 0 && section.link() < binary_->sections_.size()) {
const size_t sh_link = section.link();
symbol_table = binary_->sections_[sh_link].get();
}
constexpr uint8_t shift = ELF_T::r_info_shift;
const ARCH arch = binary_->header_.machine_type();
constexpr Relocation::ENCODING enc =
std::is_same_v<REL_T, typename ELF_T::Elf_Rel> ? Relocation::ENCODING::REL :
Relocation::ENCODING::RELA;
auto nb_entries = static_cast<uint32_t>(section.size() / sizeof(REL_T));
nb_entries = std::min<uint32_t>(nb_entries, Parser::NB_MAX_RELOCATIONS);
std::unordered_set<Relocation*, RelocationSetHash, RelocationSetEq> reloc_hash;
SpanStream reloc_stream(section.content());
reloc_stream.set_endian_swap(stream_->should_swap());
const bool is_object_file =
binary_->header().file_type() == Header::FILE_TYPE::REL &&
binary_->segments_.empty();
size_t count = 0;
while (reloc_stream) {
auto rel_hdr = reloc_stream.read<REL_T>();
if (!rel_hdr) {
LIEF_WARN("Can't parse relocation at offset: {:#06x} in {}",
reloc_stream.pos(), section.name());
break;
}
auto reloc = std::unique_ptr<Relocation>(
new Relocation(*rel_hdr, Relocation::PURPOSE::NONE, enc, arch)
);
reloc->section_ = applies_to;
reloc->symbol_table_ = symbol_table;
if (is_object_file) {
reloc->purpose(Relocation::PURPOSE::OBJECT);
}
const auto idx = static_cast<uint32_t>(rel_hdr->r_info >> shift);
const bool is_from_dynsym =
idx > 0 && idx < binary_->dynamic_symbols_.size() &&
(symbol_table == nullptr || symbol_table->type() == Section::TYPE::DYNSYM);
const bool is_from_symtab =
idx < binary_->symtab_symbols_.size() &&
(symbol_table == nullptr || symbol_table->type() == Section::TYPE::SYMTAB);
if (is_from_dynsym) {
reloc->symbol_ = binary_->dynamic_symbols_[idx].get();
} else if (is_from_symtab) {
reloc->symbol_ = binary_->symtab_symbols_[idx].get();
}
if (reloc_hash.insert(reloc.get()).second) {
++count;
insert_relocation(std::move(reloc));
}
}
LIEF_DEBUG("#{} relocations found in {}", count, section.name());
return ok();
}
template<typename ELF_T>
ok_error_t Parser::parse_symbol_version_requirement(uint64_t offset,
uint32_t nb_entries) {
using Elf_Verneed = typename ELF_T::Elf_Verneed;
using Elf_Vernaux = typename ELF_T::Elf_Vernaux;
LIEF_DEBUG("Parsing symbol version requirements");
const uint64_t svr_offset = offset;
LIEF_DEBUG("svr_offset: {:#x}", svr_offset);
const uint64_t string_offset = get_dynamic_string_table();
uint64_t next_symbol_offset = 0;
for (size_t sym_idx = 0; sym_idx < nb_entries; ++sym_idx) {
const auto header =
stream_->peek<Elf_Verneed>(svr_offset + next_symbol_offset);
if (!header) {
break;
}
auto symbol_version_requirement =
std::make_unique<SymbolVersionRequirement>(*header);
if (string_offset != 0) {
auto name = stream_->peek_string_at(string_offset + header->vn_file);
if (name) {
symbol_version_requirement->name(std::move(*name));
}
}
const uint32_t nb_symbol_aux = header->vn_cnt;
if (nb_symbol_aux > 0 && header->vn_aux > 0) {
uint64_t next_aux_offset = 0;
for (size_t j = 0; j < nb_symbol_aux; ++j) {
const uint64_t aux_hdr_off =
svr_offset + next_symbol_offset + header->vn_aux + next_aux_offset;
const auto aux_header = stream_->peek<Elf_Vernaux>(aux_hdr_off);
if (!aux_header) {
break;
}
auto svar = std::make_unique<SymbolVersionAuxRequirement>(*aux_header);
if (string_offset != 0) {
auto name =
stream_->peek_string_at(string_offset + aux_header->vna_name);
if (name) {
svar->name(std::move(*name));
}
}
symbol_version_requirement->aux_requirements_.push_back(std::move(svar));
if (aux_header->vna_next == 0) {
break;
}
const uint64_t prev_aux_offset = next_aux_offset;
next_aux_offset += aux_header->vna_next;
if (next_aux_offset <= prev_aux_offset) {
break;
}
}
binary_->symbol_version_requirements_.push_back(
std::move(symbol_version_requirement)
);
}
if (header->vn_next == 0) {
break;
}
const uint64_t prev_symbol_offset = next_symbol_offset;
next_symbol_offset += header->vn_next;
if (next_symbol_offset <= prev_symbol_offset) {
break;
}
}
// Associate Symbol Version with auxiliary symbol
// Symbol version requirement is used to map
// SymbolVersion::SymbolVersionAux <------> SymbolVersionAuxRequirement
//
// We mask the 15th (7FFF) bit because it indicates if this symbol is a hidden
// one or not, but we don't care
for (const std::unique_ptr<SymbolVersionRequirement>& svr :
binary_->symbol_version_requirements_)
{
binary_->sizing_info_->verneed += sizeof(Elf_Verneed);
for (std::unique_ptr<SymbolVersionAuxRequirement>& svar :
svr->aux_requirements_)
{
binary_->sizing_info_->verneed += sizeof(Elf_Vernaux);
for (const std::unique_ptr<SymbolVersion>& sv :
binary_->symbol_version_table_)
{
if ((sv->value() & 0x7FFF) == svar->other()) {
sv->symbol_aux_ = svar.get();
}
}
}
}
return ok();
}
template<typename ELF_T>
ok_error_t Parser::parse_symbol_version_definition(uint64_t offset,
uint32_t nb_entries) {
using Elf_Verdef = typename ELF_T::Elf_Verdef;
using Elf_Verdaux = typename ELF_T::Elf_Verdaux;
const uint64_t string_offset = get_dynamic_string_table();
ScopedStream verdef_stream(*stream_, offset);
uint64_t def_size = 0;
for (size_t i = 0; i < nb_entries; ++i) {
const auto svd_header = verdef_stream->peek<Elf_Verdef>();
def_size =
std::max(def_size, verdef_stream->pos() - offset + sizeof(Elf_Verdef));
if (!svd_header) {
break;
}
auto symbol_version_definition =
std::make_unique<SymbolVersionDefinition>(*svd_header);
uint32_t nb_aux_symbols = svd_header->vd_cnt;
{
ScopedStream aux_stream(*stream_, verdef_stream->pos() + svd_header->vd_aux);
for (size_t j = 0; j < nb_aux_symbols; ++j) {
const auto svda_header = aux_stream->peek<Elf_Verdaux>();
def_size =
std::max(def_size, aux_stream->pos() - offset + sizeof(Elf_Verdaux));
if (!svda_header) {
break;
}
if (string_offset != 0) {
auto name =
stream_->peek_string_at(string_offset + svda_header->vda_name);
if (name) {
symbol_version_definition->symbol_version_aux_.emplace_back(
new SymbolVersionAux{std::move(*name)}
);
}
}
// Additional check
if (svda_header->vda_next == 0) {
break;
}
aux_stream->increment_pos(svda_header->vda_next);
}
}
binary_->symbol_version_definition_.push_back(
std::move(symbol_version_definition)
);
// Additional check
if (svd_header->vd_next == 0) {
break;
}
verdef_stream->increment_pos(svd_header->vd_next);
}
binary_->sizing_info_->verdef = def_size;
// Associate Symbol Version with auxiliary symbol
// We mask the 15th bit because it indicates if this symbol is a hidden one or
// not, but we don't care
for (std::unique_ptr<SymbolVersionDefinition>& svd :
binary_->symbol_version_definition_)
{
for (std::unique_ptr<SymbolVersionAux>& sva : svd->symbol_version_aux_) {
for (std::unique_ptr<SymbolVersion>& sv : binary_->symbol_version_table_) {
if (svd->ndx() > 1 && (sv->value() & 0x7FFF) == svd->ndx() &&
!sv->symbol_aux_)
{
sv->symbol_aux_ = sva.get();
}
}
}
}
return ok();
}
}