/* 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 "logging.hpp"
#include "frozen.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"
#include "LIEF/ELF/hash.hpp"
#include "LIEF/ELF/Segment.hpp"
#include "LIEF/ELF/EnumToString.hpp"
#include "LIEF/ELF/Section.hpp"
#include "ELF/DataHandler/Handler.hpp"
#include "ELF/Structures.hpp"
namespace LIEF::ELF {
static constexpr auto PT_LOPROC = 0x70000000;
static constexpr auto PT_HIPROC = 0x7fffffff;
static constexpr auto PT_LOOS = 0x60000000;
static constexpr auto PT_HIOS = 0x6fffffff;
Segment::TYPE Segment::type_from(uint64_t value, ARCH arch, Header::OS_ABI os) {
using OS_ABI = Header::OS_ABI;
if (PT_LOPROC <= value && value <= PT_HIPROC) {
if (arch == ARCH::NONE) {
LIEF_WARN("Segment type {:#010x} requires a known architecture", value);
return TYPE::UNKNOWN;
}
switch (arch) {
case ARCH::ARM: return TYPE(value | PT_ARM);
case ARCH::AARCH64: return TYPE(value | PT_AARCH64);
case ARCH::MIPS: return TYPE(value | PT_MIPS);
case ARCH::RISCV: return TYPE(value | PT_RISCV);
case ARCH::IA_64: return TYPE(value | PT_IA_64);
default:
{
LIEF_WARN("Unknown segment type {:#010x} for architecture {}", value,
to_string(arch));
return TYPE::UNKNOWN;
}
}
}
// OS-specific type
if (PT_LOOS <= value && value <= PT_HIOS) {
if (os == OS_ABI::HPUX) {
return TYPE(value | PT_HPUX);
}
}
return TYPE(value);
}
Segment::Segment(const Segment& other) :
Object{other},
type_{other.type_},
arch_{other.arch_},
flags_{other.flags_},
file_offset_{other.file_offset_},
virtual_address_{other.virtual_address_},
physical_address_{other.physical_address_},
size_{other.size_},
virtual_size_{other.virtual_size_},
alignment_{other.alignment_},
handler_size_{other.handler_size_},
content_c_{other.content_c_} {}
template<class T>
Segment::Segment(const T& header, ARCH arch, Header::OS_ABI os) :
type_{type_from(header.p_type, arch, os)},
arch_(arch),
flags_{header.p_flags},
file_offset_{header.p_offset},
virtual_address_{header.p_vaddr},
physical_address_{header.p_paddr},
size_{header.p_filesz},
virtual_size_{header.p_memsz},
alignment_{header.p_align},
handler_size_{header.p_filesz} {}
template Segment::Segment(const details::Elf32_Phdr& header, ARCH, Header::OS_ABI);
template Segment::Segment(const details::Elf64_Phdr& header, ARCH, Header::OS_ABI);
void Segment::swap(Segment& other) {
std::swap(type_, other.type_);
std::swap(arch_, other.arch_);
std::swap(flags_, other.flags_);
std::swap(file_offset_, other.file_offset_);
std::swap(virtual_address_, other.virtual_address_);
std::swap(physical_address_, other.physical_address_);
std::swap(size_, other.size_);
std::swap(virtual_size_, other.virtual_size_);
std::swap(alignment_, other.alignment_);
std::swap(handler_size_, other.handler_size_);
std::swap(sections_, other.sections_);
std::swap(datahandler_, other.datahandler_);
std::swap(content_c_, other.content_c_);
}
Segment& Segment::operator=(Segment other) {
swap(other);
return *this;
}
result<Segment> Segment::from_raw(const uint8_t* ptr, size_t size) {
if (size != sizeof(details::Elf32_Phdr) && size != sizeof(details::Elf64_Phdr)) {
LIEF_ERR("Provided data size does not match a valid header size");
return make_error_code(lief_errors::corrupted);
}
if (size == sizeof(details::Elf32_Phdr)) {
return Segment(*reinterpret_cast<const details::Elf32_Phdr*>(ptr));
}
if (size == sizeof(details::Elf64_Phdr)) {
return Segment(*reinterpret_cast<const details::Elf64_Phdr*>(ptr));
}
return make_error_code(lief_errors::not_implemented);
}
span<const uint8_t> Segment::content() const {
if (datahandler_ == nullptr) {
LIEF_DEBUG("Getting content of segment {}@{:#x} from cache", to_string(type()),
virtual_address());
return content_c_;
}
auto res =
datahandler_->get(file_offset(), handler_size(), DataHandler::Node::SEGMENT);
if (!res) {
LIEF_ERR("Node not found, segment content inaccessible");
return {};
}
DataHandler::Node& node = res.value();
// Create a span based on our values
const std::vector<uint8_t>& binary_content = datahandler_->content();
const size_t size = binary_content.size();
if (node.offset() >= size) {
LIEF_ERR("Failed to access segment content {}:{:#x}", to_string(type()),
virtual_address());
return {};
}
const uint8_t* ptr = binary_content.data() + node.offset();
/* node.size() overflow */
if (node.offset() + node.size() < node.offset()) {
return {};
}
if ((node.offset() + node.size()) >= size) {
if ((node.offset() + handler_size()) <= size) {
return {ptr, static_cast<size_t>(handler_size())};
}
LIEF_ERR("Failed to access segment content {}:{:#x}", to_string(type()),
virtual_address());
return {};
}
return {ptr, static_cast<size_t>(node.size())};
}
size_t Segment::get_content_size() const {
if (datahandler_ == nullptr) {
return content_c_.size();
}
auto res =
datahandler_->get(file_offset(), handler_size(), DataHandler::Node::SEGMENT);
if (!res) {
LIEF_ERR("Node not found");
return 0;
}
DataHandler::Node& node = res.value();
return node.size();
}
template<typename T>
T Segment::get_content_value(size_t offset) const {
T ret;
if (datahandler_ == nullptr) {
LIEF_DEBUG("Getting content of segment {}@{:#x} from cache", to_string(type()),
virtual_address());
memcpy(&ret, content_c_.data() + offset, sizeof(T));
} else {
auto res = datahandler_->get(file_offset(), handler_size(),
DataHandler::Node::SEGMENT);
if (!res) {
LIEF_ERR("Node not found for this segment");
memset(&ret, 0, sizeof(T));
return ret;
}
const std::vector<uint8_t>& binary_content = datahandler_->content();
DataHandler::Node& node = res.value();
memcpy(&ret, binary_content.data() + node.offset() + offset, sizeof(T));
}
return ret;
}
template unsigned short
Segment::get_content_value<unsigned short>(size_t offset) const;
template unsigned int
Segment::get_content_value<unsigned int>(size_t offset) const;
template unsigned long
Segment::get_content_value<unsigned long>(size_t offset) const;
template unsigned long long
Segment::get_content_value<unsigned long long>(size_t offset) const;
template<typename T>
void Segment::set_content_value(size_t offset, T value) {
if (datahandler_ == nullptr) {
LIEF_DEBUG("Setting content of segment {}@{:#x}:{:#x} in cache ({:#x} bytes)",
to_string(type()), virtual_address(), offset, sizeof(T));
if (offset + sizeof(T) > content_c_.size()) {
content_c_.resize(offset + sizeof(T));
physical_size(offset + sizeof(T));
}
memcpy(content_c_.data() + offset, &value, sizeof(T));
} else {
auto res = datahandler_->get(file_offset(), handler_size(),
DataHandler::Node::SEGMENT);
if (!res) {
LIEF_ERR("Node not found for this segment, content cannot be updated");
return;
}
DataHandler::Node& node = res.value();
std::vector<uint8_t>& binary_content = datahandler_->content();
if (offset + sizeof(T) > binary_content.size()) {
datahandler_->reserve(node.offset(), offset + sizeof(T));
}
physical_size(node.size());
memcpy(binary_content.data() + node.offset() + offset, &value, sizeof(T));
}
}
template void Segment::set_content_value<unsigned short>(size_t offset,
unsigned short value);
template void Segment::set_content_value<unsigned int>(size_t offset,
unsigned int value);
template void Segment::set_content_value<unsigned long>(size_t offset,
unsigned long value);
template void
Segment::set_content_value<unsigned long long>(size_t offset,
unsigned long long value);
bool Segment::has(const Section& section) const {
auto it_section =
std::find_if(sections_.begin(), sections_.end(),
[§ion](const Section* s) { return *s == section; });
return it_section != sections_.end();
}
bool Segment::has(const std::string& name) const {
auto it_section =
std::find_if(sections_.begin(), sections_.end(),
[&name](const Section* s) { return s->name() == name; });
return it_section != sections_.end();
}
void Segment::add(Segment::FLAGS flag) {
flags(flags() | flag);
}
void Segment::remove(Segment::FLAGS flag) {
flags(flags() & ~flag);
}
void Segment::file_offset(uint64_t file_offset) {
if (datahandler_ != nullptr) {
auto res = datahandler_->get(this->file_offset(), handler_size(),
DataHandler::Node::SEGMENT);
if (res) {
res->get().offset(file_offset);
} else {
LIEF_ERR("Node not found, file offset cannot be updated");
return;
}
}
file_offset_ = file_offset;
}
void Segment::physical_size(uint64_t physical_size) {
if (datahandler_ != nullptr) {
auto node = datahandler_->get(file_offset(), handler_size(),
DataHandler::Node::SEGMENT);
if (node) {
node->get().size(physical_size);
handler_size_ = physical_size;
} else {
LIEF_ERR("Node not found, physical size cannot be updated");
}
}
size_ = physical_size;
}
void Segment::content(std::vector<uint8_t> content) {
if (datahandler_ == nullptr) {
LIEF_DEBUG("Setting content of segment {}@{:#x} in cache ({:#x} bytes)",
to_string(type()), virtual_address(), content.size());
physical_size(content.size());
content_c_ = std::move(content);
return;
}
LIEF_DEBUG(
"Setting content of segment {}@{:#x} in data handler @{:#x} ({:#x} bytes)",
to_string(type()), virtual_address(), file_offset(), content.size()
);
auto res =
datahandler_->get(file_offset(), handler_size(), DataHandler::Node::SEGMENT);
if (!res) {
LIEF_ERR("Node not found for content update");
return;
}
DataHandler::Node& node = res.value();
std::vector<uint8_t>& binary_content = datahandler_->content();
datahandler_->reserve(node.offset(), content.size());
if (node.size() < content.size()) {
LIEF_INFO("Inserted {:#x} bytes in segment {}@{:#x} which is {:#x} wide",
content.size(), to_string(type()), virtual_size(), node.size());
}
auto max_offset = (int64_t)node.offset() + (int64_t)content.size();
if (max_offset < 0 || max_offset > (int64_t)binary_content.size()) {
LIEF_ERR("Write out of range");
return;
}
physical_size(node.size());
std::move(content.begin(), content.end(),
binary_content.begin() + node.offset());
}
void Segment::accept(Visitor& visitor) const {
visitor.visit(*this);
}
std::unique_ptr<SpanStream> Segment::stream() const {
return std::make_unique<SpanStream>(content());
}
span<uint8_t> Segment::writable_content() {
span<const uint8_t> ref = static_cast<const Segment*>(this)->content();
return {const_cast<uint8_t*>(ref.data()), ref.size()};
}
uint64_t Segment::handler_size() const {
if (handler_size_ > 0) {
return handler_size_;
}
return physical_size();
}
std::ostream& operator<<(std::ostream& os, const Segment& segment) {
std::string flags = "---";
if (segment.has(Segment::FLAGS::R)) {
flags[0] = 'r';
}
if (segment.has(Segment::FLAGS::W)) {
flags[1] = 'w';
}
if (segment.has(Segment::FLAGS::X)) {
flags[2] = 'x';
}
std::string segment_ty = to_string(segment.type());
if (segment_ty == "UNKNOWN") {
segment_ty = fmt::format("UNKNOWN[{:#010x}]", (uint32_t)segment.type());
}
os << fmt::format("{} {:#010x}/{:#08x} {:#08x} {:#06x}/{:#06x} {} {}",
segment_ty, segment.virtual_address(), segment.file_offset(),
segment.physical_address(), segment.physical_size(),
segment.virtual_size(), segment.alignment(), flags);
return os;
}
const char* to_string(Segment::TYPE e) {
#define ENTRY(X) std::pair(Segment::TYPE::X, #X)
STRING_MAP enums2str{
ENTRY(PT_NULL_),
ENTRY(LOAD),
ENTRY(DYNAMIC),
ENTRY(INTERP),
ENTRY(NOTE),
ENTRY(SHLIB),
ENTRY(PHDR),
ENTRY(TLS),
ENTRY(GNU_EH_FRAME),
ENTRY(GNU_STACK),
ENTRY(GNU_PROPERTY),
ENTRY(GNU_RELRO),
ENTRY(PAX_FLAGS),
ENTRY(ARM_ARCHEXT),
ENTRY(ARM_EXIDX),
ENTRY(AARCH64_MEMTAG_MTE),
ENTRY(MIPS_REGINFO),
ENTRY(MIPS_RTPROC),
ENTRY(MIPS_OPTIONS),
ENTRY(MIPS_ABIFLAGS),
ENTRY(RISCV_ATTRIBUTES),
ENTRY(IA_64_EXT),
ENTRY(IA_64_UNWIND),
ENTRY(HP_TLS),
ENTRY(HP_CORE_NONE),
ENTRY(HP_CORE_VERSION),
ENTRY(HP_CORE_KERNEL),
ENTRY(HP_CORE_COMM),
ENTRY(HP_CORE_PROC),
ENTRY(HP_CORE_LOADABLE),
ENTRY(HP_CORE_STACK),
ENTRY(HP_CORE_SHM),
ENTRY(HP_CORE_MMF),
ENTRY(HP_PARALLEL),
ENTRY(HP_FASTBIND),
ENTRY(HP_OPT_ANNOT),
ENTRY(HP_HSL_ANNOT),
ENTRY(HP_STACK),
ENTRY(HP_CORE_UTSNAME),
};
#undef ENTRY
if (auto it = enums2str.find(e); it != enums2str.end()) {
return it->second;
}
return "UNKNOWN";
}
const char* to_string(Segment::FLAGS e) {
switch (e) {
case Segment::FLAGS::NONE: return "NONE";
case Segment::FLAGS::R: return "R";
case Segment::FLAGS::W: return "W";
case Segment::FLAGS::X: return "X";
}
return "UNKNOWN";
}
}