/* Copyright 2017 - 2026 R. Thomas
* Copyright 2017 - 2026 Quarkslab
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <iterator>
#include "logging.hpp"
#include "frozen.hpp"
#include "fmt_formatter.hpp"
#include "LIEF/Visitor.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"
#include "LIEF/ELF/Section.hpp"
#include "LIEF/ELF/Segment.hpp"
#include "LIEF/ELF/EnumToString.hpp"
#include "ELF/DataHandler/Handler.hpp"
#include "ELF/Structures.hpp"
FMT_FORMATTER(LIEF::ELF::Section::FLAGS, LIEF::ELF::to_string);
FMT_FORMATTER(LIEF::ELF::Section::TYPE, LIEF::ELF::to_string);
namespace LIEF::ELF {
static constexpr uint64_t SHF_MASKPROC = 0xf0000000;
static constexpr uint64_t SHT_LOPROC = 0x70000000;
static constexpr uint64_t SHT_HIPROC = 0x7fffffff;
static constexpr auto ARRAY_FLAGS = {
Section::FLAGS::NONE,
Section::FLAGS::WRITE,
Section::FLAGS::ALLOC,
Section::FLAGS::EXECINSTR,
Section::FLAGS::MERGE,
Section::FLAGS::STRINGS,
Section::FLAGS::INFO_LINK,
Section::FLAGS::LINK_ORDER,
Section::FLAGS::OS_NONCONFORMING,
Section::FLAGS::GROUP,
Section::FLAGS::TLS,
Section::FLAGS::COMPRESSED,
Section::FLAGS::GNU_RETAIN,
Section::FLAGS::EXCLUDE,
Section::FLAGS::XCORE_SHF_DP_SECTION,
Section::FLAGS::XCORE_SHF_CP_SECTION,
Section::FLAGS::X86_64_LARGE,
Section::FLAGS::HEX_GPREL,
Section::FLAGS::MIPS_NODUPES,
Section::FLAGS::MIPS_NAMES,
Section::FLAGS::MIPS_LOCAL,
Section::FLAGS::MIPS_NOSTRIP,
Section::FLAGS::MIPS_GPREL,
Section::FLAGS::MIPS_MERGE,
Section::FLAGS::MIPS_ADDR,
Section::FLAGS::MIPS_STRING,
Section::FLAGS::ARM_PURECODE,
Section::FLAGS::AARCH64_PURECODE,
};
Section::TYPE Section::type_from(uint32_t value, ARCH arch) {
if (SHT_LOPROC <= value && value <= SHT_HIPROC) {
switch (arch) {
case ARCH::ARM:
return TYPE(value +
(uint64_t(TYPE::_ARM_ID_) << uint8_t(TYPE::_ID_SHIFT_)));
case ARCH::HEXAGON:
return TYPE(value +
(uint64_t(TYPE::_HEX_ID_) << uint8_t(TYPE::_ID_SHIFT_)));
case ARCH::X86_64:
return TYPE(value +
(uint64_t(TYPE::_X86_64_ID_) << uint8_t(TYPE::_ID_SHIFT_)));
case ARCH::MIPS:
return TYPE(value +
(uint64_t(TYPE::_MIPS_ID_) << uint8_t(TYPE::_ID_SHIFT_)));
case ARCH::RISCV:
return TYPE(value +
(uint64_t(TYPE::_RISCV_ID_) << uint8_t(TYPE::_ID_SHIFT_)));
case ARCH::AARCH64:
return TYPE(value +
(uint64_t(TYPE::_AARCH64_ID_) << uint8_t(TYPE::_ID_SHIFT_)));
default:
{
LIEF_ERR("Unrecognized arch-specific section {:#010x} for {}", value,
to_string(arch));
return TYPE::SHT_NULL_;
}
}
}
return TYPE(value);
}
template<class T>
Section::Section(const T& header, ARCH arch) :
arch_{arch},
type_{type_from(header.sh_type, arch)},
flags_{header.sh_flags},
original_size_{header.sh_size},
link_{header.sh_link},
info_{header.sh_info},
address_align_{header.sh_addralign},
entry_size_{header.sh_entsize} {
virtual_address_ = header.sh_addr;
offset_ = header.sh_offset;
size_ = header.sh_size;
}
template Section::Section(const details::Elf32_Shdr& header, ARCH);
template Section::Section(const details::Elf64_Shdr& header, ARCH);
Section::Section(const Section& other) :
LIEF::Section{other},
arch_{other.arch_},
type_{other.type_},
flags_{other.flags_},
original_size_{other.original_size_},
link_{other.link_},
info_{other.info_},
address_align_{other.address_align_},
entry_size_{other.entry_size_},
is_frame_{other.is_frame_},
content_c_{other.content_c_} {}
void Section::swap(Section& other) noexcept {
std::swap(name_, other.name_);
std::swap(virtual_address_, other.virtual_address_);
std::swap(offset_, other.offset_);
std::swap(size_, other.size_);
std::swap(arch_, other.arch_);
std::swap(type_, other.type_);
std::swap(flags_, other.flags_);
std::swap(original_size_, other.original_size_);
std::swap(link_, other.link_);
std::swap(info_, other.info_);
std::swap(address_align_, other.address_align_);
std::swap(entry_size_, other.entry_size_);
std::swap(segments_, other.segments_);
std::swap(is_frame_, other.is_frame_);
std::swap(datahandler_, other.datahandler_);
std::swap(content_c_, other.content_c_);
}
bool Section::has(const Segment& segment) const {
auto it_segment = std::find_if(segments_.begin(), segments_.end(),
[&segment](Segment* s) { return *s == segment; });
return it_segment != segments_.end();
}
void Section::size(uint64_t size) {
if (datahandler_ != nullptr && !is_frame()) {
if (auto node = datahandler_->get(file_offset(), this->size(),
DataHandler::Node::SECTION))
{
node->get().size(size);
} else {
if (type() != TYPE::NOBITS) {
LIEF_ERR("Node not found, cannot resize section '{}'", name());
std::abort();
}
}
}
size_ = size;
}
void Section::offset(uint64_t offset) {
if (datahandler_ != nullptr && !is_frame()) {
if (auto node =
datahandler_->get(file_offset(), size(), DataHandler::Node::SECTION))
{
node->get().offset(offset);
} else {
if (type() != TYPE::NOBITS) {
LIEF_WARN("Node not found, cannot change offset of section '{}'", name());
}
}
}
offset_ = offset;
}
span<const uint8_t> Section::content() const {
if (size() == 0 || is_frame()) {
return {};
}
if (datahandler_ == nullptr) {
return content_c_;
}
if (size() > MAX_SECTION_SIZE) {
return {};
}
auto res = datahandler_->get(offset(), size(), DataHandler::Node::SECTION);
if (!res) {
if (type() != TYPE::NOBITS) {
LIEF_WARN("Section '{}' has no content", name());
}
return {};
}
const std::vector<uint8_t>& binary_content = datahandler_->content();
DataHandler::Node& node = res.value();
auto end_offset = (int64_t)node.offset() + (int64_t)node.size();
if (end_offset <= 0 || end_offset > (int64_t)binary_content.size()) {
return {};
}
const uint8_t* ptr = binary_content.data() + node.offset();
return {ptr, ptr + node.size()};
}
std::vector<Section::FLAGS> Section::flags_list() const {
std::vector<FLAGS> flags;
std::copy_if(ARRAY_FLAGS.begin(), ARRAY_FLAGS.end(), std::back_inserter(flags),
[this](FLAGS f) { return has(f); });
return flags;
}
void Section::content(const std::vector<uint8_t>& data) {
if (is_frame()) {
return;
}
if (!data.empty() && type() == TYPE::NOBITS) {
LIEF_INFO("Inserted {:#x} bytes in SHT_NOBITS section '{}'", data.size(),
name());
}
if (datahandler_ == nullptr) {
LIEF_DEBUG("Setting {:#x} bytes in cache of section '{}'", data.size(),
name());
content_c_ = data;
size(data.size());
return;
}
LIEF_DEBUG("Setting {:#x} bytes in data handler@{:#x} of section '{}'",
data.size(), file_offset(), name());
auto res = datahandler_->get(file_offset(), size(), DataHandler::Node::SECTION);
if (!res) {
LIEF_ERR("Node not found, section content cannot be updated");
return;
}
DataHandler::Node& node = res.value();
std::vector<uint8_t>& binary_content = datahandler_->content();
datahandler_->reserve(node.offset(), data.size());
if (node.size() < data.size()) {
LIEF_INFO("Inserted {:#x} bytes in section '{}' which is {:#x} wide",
data.size(), name(), node.size());
}
auto max_offset = (int64_t)node.offset() + (int64_t)data.size();
if (max_offset < 0 || max_offset > (int64_t)binary_content.size()) {
LIEF_ERR("Write out of range");
return;
}
size(data.size());
std::copy(data.begin(), data.end(), binary_content.begin() + node.offset());
}
void Section::content(std::vector<uint8_t>&& data) {
if (is_frame()) {
return;
}
if (!data.empty() && type() == TYPE::NOBITS) {
LIEF_INFO("Inserted {:#x} bytes in SHT_NOBITS section '{}'", data.size(),
name());
}
if (datahandler_ == nullptr) {
LIEF_DEBUG("Setting {:#x} bytes in cache of section '{}'", data.size(),
name());
size(data.size());
content_c_ = std::move(data);
return;
}
LIEF_DEBUG("Setting {:#x} bytes in data handler@{:#x} of section '{}'",
data.size(), file_offset(), name());
auto res = datahandler_->get(file_offset(), size(), DataHandler::Node::SECTION);
if (!res) {
LIEF_ERR("Node not found, section content cannot be updated");
return;
}
DataHandler::Node& node = res.value();
std::vector<uint8_t>& binary_content = datahandler_->content();
datahandler_->reserve(node.offset(), data.size());
if (node.size() < data.size()) {
LIEF_INFO("Inserted {:#x} bytes in section '{}' which is {:#x} wide",
data.size(), name(), node.size());
}
size(data.size());
auto max_offset = (int64_t)node.offset() + (int64_t)data.size();
if (max_offset < 0 || max_offset > (int64_t)binary_content.size()) {
LIEF_ERR("Write out of range");
return;
}
std::move(data.begin(), data.end(), binary_content.begin() + node.offset());
}
bool Section::has(Section::FLAGS flag) const {
if ((flags_ & SHF_MASKPROC) == 0) {
return (flags_ & (static_cast<uint64_t>(flag) & FLAG_MASK)) != 0;
}
auto raw_flag = static_cast<uint64_t>(flag);
size_t id = raw_flag >> uint8_t(FLAGS::_ID_SHIFT_);
if (id > 0 && arch_ == ARCH::NONE) {
LIEF_WARN("Missing architecture, cannot determine flag presence");
return false;
}
if (id == uint8_t(FLAGS::_XCORE_ID_) && arch_ != ARCH::XCORE) {
return false;
}
if (id == uint8_t(FLAGS::_X86_64_ID_) && arch_ != ARCH::X86_64) {
return false;
}
if (id == uint8_t(FLAGS::_HEX_ID_) && arch_ != ARCH::HEXAGON) {
return false;
}
if (id == uint8_t(FLAGS::_MIPS_ID_) && arch_ != ARCH::MIPS) {
return false;
}
if (id == uint8_t(FLAGS::_ARM_ID_) && arch_ != ARCH::ARM) {
return false;
}
if (id == uint8_t(FLAGS::_AARCH64_ID_) && arch_ != ARCH::AARCH64) {
return false;
}
return (flags_ & (static_cast<uint64_t>(flag) & FLAG_MASK)) != 0;
}
void Section::add(Section::FLAGS flag) {
flags(flags() | (static_cast<uint64_t>(flag) & FLAG_MASK));
}
void Section::remove(Section::FLAGS flag) {
const auto raw_flag = static_cast<uint64_t>(flag) & FLAG_MASK;
flags(flags() & (~raw_flag));
}
Section& Section::clear(uint8_t value) {
if (is_frame()) {
return *this;
}
if (datahandler_ == nullptr) {
std::fill(content_c_.begin(), content_c_.end(), value);
return *this;
}
std::vector<uint8_t>& binary_content = datahandler_->content();
auto res = datahandler_->get(file_offset(), size(), DataHandler::Node::SECTION);
if (!res) {
LIEF_ERR("Node not found, section content cannot be cleared");
return *this;
}
DataHandler::Node& node = res.value();
std::fill_n(binary_content.begin() + node.offset(), size(), value);
return *this;
}
void Section::accept(Visitor& visitor) const {
visitor.visit(*this);
}
span<uint8_t> Section::writable_content() {
if (is_frame()) {
return {};
}
span<const uint8_t> ref = static_cast<const Section*>(this)->content();
return {const_cast<uint8_t*>(ref.data()), ref.size()};
}
std::unique_ptr<SpanStream> Section::stream() const {
return std::make_unique<SpanStream>(content());
}
std::ostream& operator<<(std::ostream& os, const Section& section) {
const auto& flags = section.flags_list();
os << fmt::format("{} ({}) {:#010x}/{:#010x} {:#06x} {} {}", section.name(),
section.type(), section.virtual_address(),
section.file_offset(), section.size(), section.entropy(),
fmt::to_string(flags));
return os;
}
const char* to_string(Section::TYPE e) {
#define ENTRY(X) std::pair(Section::TYPE::X, #X)
STRING_MAP enums2str{
ENTRY(SHT_NULL_),
ENTRY(PROGBITS),
ENTRY(SYMTAB),
ENTRY(STRTAB),
ENTRY(RELA),
ENTRY(HASH),
ENTRY(DYNAMIC),
ENTRY(NOTE),
ENTRY(NOBITS),
ENTRY(REL),
ENTRY(SHLIB),
ENTRY(DYNSYM),
ENTRY(INIT_ARRAY),
ENTRY(FINI_ARRAY),
ENTRY(PREINIT_ARRAY),
ENTRY(GROUP),
ENTRY(SYMTAB_SHNDX),
ENTRY(RELR),
ENTRY(ANDROID_REL),
ENTRY(ANDROID_RELA),
ENTRY(LLVM_ADDRSIG),
ENTRY(ANDROID_RELR),
ENTRY(GNU_ATTRIBUTES),
ENTRY(GNU_HASH),
ENTRY(GNU_VERDEF),
ENTRY(GNU_VERNEED),
ENTRY(GNU_VERSYM),
ENTRY(ARM_EXIDX),
ENTRY(ARM_PREEMPTMAP),
ENTRY(ARM_ATTRIBUTES),
ENTRY(ARM_DEBUGOVERLAY),
ENTRY(ARM_OVERLAYSECTION),
ENTRY(HEX_ORDERED),
ENTRY(X86_64_UNWIND),
ENTRY(MIPS_LIBLIST),
ENTRY(MIPS_MSYM),
ENTRY(MIPS_CONFLICT),
ENTRY(MIPS_GPTAB),
ENTRY(MIPS_UCODE),
ENTRY(MIPS_DEBUG),
ENTRY(MIPS_REGINFO),
ENTRY(MIPS_PACKAGE),
ENTRY(MIPS_PACKSYM),
ENTRY(MIPS_RELD),
ENTRY(MIPS_IFACE),
ENTRY(MIPS_CONTENT),
ENTRY(MIPS_OPTIONS),
ENTRY(MIPS_SHDR),
ENTRY(MIPS_FDESC),
ENTRY(MIPS_EXTSYM),
ENTRY(MIPS_DENSE),
ENTRY(MIPS_PDESC),
ENTRY(MIPS_LOCSYM),
ENTRY(MIPS_AUXSYM),
ENTRY(MIPS_OPTSYM),
ENTRY(MIPS_LOCSTR),
ENTRY(MIPS_LINE),
ENTRY(MIPS_RFDESC),
ENTRY(MIPS_DELTASYM),
ENTRY(MIPS_DELTAINST),
ENTRY(MIPS_DELTACLASS),
ENTRY(MIPS_DWARF),
ENTRY(MIPS_DELTADECL),
ENTRY(MIPS_SYMBOL_LIB),
ENTRY(MIPS_EVENTS),
ENTRY(MIPS_TRANSLATE),
ENTRY(MIPS_PIXIE),
ENTRY(MIPS_XLATE),
ENTRY(MIPS_XLATE_DEBUG),
ENTRY(MIPS_WHIRL),
ENTRY(MIPS_EH_REGION),
ENTRY(MIPS_XLATE_OLD),
ENTRY(MIPS_PDR_EXCEPTION),
ENTRY(MIPS_ABIFLAGS),
ENTRY(MIPS_XHASH),
ENTRY(RISCV_ATTRIBUTES),
ENTRY(AARCH64_ATTRIBUTES),
ENTRY(AARCH64_AUTH_RELR),
ENTRY(AARCH64_MEMTAG_GLOBALS_STATIC),
ENTRY(AARCH64_MEMTAG_GLOBALS_DYNAMIC),
};
#undef ENTRY
if (auto it = enums2str.find(e); it != enums2str.end()) {
return it->second;
}
return "UNKNOWN";
}
const char* to_string(Section::FLAGS e) {
#define ENTRY(X) std::pair(Section::FLAGS::X, #X)
STRING_MAP enums2str{
ENTRY(NONE),
ENTRY(WRITE),
ENTRY(ALLOC),
ENTRY(EXECINSTR),
ENTRY(MERGE),
ENTRY(STRINGS),
ENTRY(INFO_LINK),
ENTRY(LINK_ORDER),
ENTRY(OS_NONCONFORMING),
ENTRY(GROUP),
ENTRY(TLS),
ENTRY(COMPRESSED),
ENTRY(GNU_RETAIN),
ENTRY(EXCLUDE),
ENTRY(XCORE_SHF_DP_SECTION),
ENTRY(XCORE_SHF_CP_SECTION),
ENTRY(X86_64_LARGE),
ENTRY(HEX_GPREL),
ENTRY(MIPS_NODUPES),
ENTRY(MIPS_NAMES),
ENTRY(MIPS_LOCAL),
ENTRY(MIPS_NOSTRIP),
ENTRY(MIPS_GPREL),
ENTRY(MIPS_MERGE),
ENTRY(MIPS_ADDR),
ENTRY(MIPS_STRING),
ENTRY(ARM_PURECODE),
ENTRY(AARCH64_PURECODE),
};
#undef ENTRY
if (auto it = enums2str.find(e); it != enums2str.end()) {
return it->second;
}
return "UNKNOWN";
}
}