/* 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 "frozen.hpp"
#include "LIEF/ELF/hash.hpp"
#include "LIEF/ELF/Header.hpp"
#include "LIEF/ELF/EnumToString.hpp"
#include "ELF/Structures.hpp"
#include "logging.hpp"
namespace LIEF::ELF {
static constexpr uint64_t ARM_EABIMASK = 0xFF000000U;
static constexpr uint64_t MIPS_ABI_MASK = 0x0000f000U;
static constexpr uint64_t MIPS_MACH_MASK = 0x00ff0000U;
static constexpr uint64_t MIPS_ARCH_ASE_MASK = 0x0f000000U;
static constexpr uint64_t MIPS_ARCH_MASK = 0xf0000000U;
static constexpr uint64_t RISCV_FLOAT_ABI_MASK = 0x0006; // EF_RISCV_FLOAT_ABI
static constexpr auto PFLAGS_LIST = {
PROCESSOR_FLAGS::ARM_EABI_UNKNOWN,
PROCESSOR_FLAGS::ARM_SOFT_FLOAT,
PROCESSOR_FLAGS::ARM_VFP_FLOAT,
PROCESSOR_FLAGS::ARM_EABI_VER1,
PROCESSOR_FLAGS::ARM_EABI_VER2,
PROCESSOR_FLAGS::ARM_EABI_VER3,
PROCESSOR_FLAGS::ARM_EABI_VER4,
PROCESSOR_FLAGS::ARM_EABI_VER5,
PROCESSOR_FLAGS::HEXAGON_MACH_V2,
PROCESSOR_FLAGS::HEXAGON_MACH_V3,
PROCESSOR_FLAGS::HEXAGON_MACH_V4,
PROCESSOR_FLAGS::HEXAGON_MACH_V5,
PROCESSOR_FLAGS::HEXAGON_ISA_V2,
PROCESSOR_FLAGS::HEXAGON_ISA_V3,
PROCESSOR_FLAGS::HEXAGON_ISA_V4,
PROCESSOR_FLAGS::HEXAGON_ISA_V5,
PROCESSOR_FLAGS::LOONGARCH_ABI_SOFT_FLOAT,
PROCESSOR_FLAGS::LOONGARCH_ABI_SINGLE_FLOAT,
PROCESSOR_FLAGS::LOONGARCH_ABI_DOUBLE_FLOAT,
PROCESSOR_FLAGS::MIPS_NOREORDER,
PROCESSOR_FLAGS::MIPS_PIC,
PROCESSOR_FLAGS::MIPS_CPIC,
PROCESSOR_FLAGS::MIPS_ABI2,
PROCESSOR_FLAGS::MIPS_32BITMODE,
PROCESSOR_FLAGS::MIPS_FP64,
PROCESSOR_FLAGS::MIPS_NAN2008,
PROCESSOR_FLAGS::MIPS_ABI_O32,
PROCESSOR_FLAGS::MIPS_ABI_O64,
PROCESSOR_FLAGS::MIPS_ABI_EABI32,
PROCESSOR_FLAGS::MIPS_ABI_EABI64,
PROCESSOR_FLAGS::MIPS_MACH_3900,
PROCESSOR_FLAGS::MIPS_MACH_4010,
PROCESSOR_FLAGS::MIPS_MACH_4100,
PROCESSOR_FLAGS::MIPS_MACH_4650,
PROCESSOR_FLAGS::MIPS_MACH_4120,
PROCESSOR_FLAGS::MIPS_MACH_4111,
PROCESSOR_FLAGS::MIPS_MACH_SB1,
PROCESSOR_FLAGS::MIPS_MACH_OCTEON,
PROCESSOR_FLAGS::MIPS_MACH_XLR,
PROCESSOR_FLAGS::MIPS_MACH_OCTEON2,
PROCESSOR_FLAGS::MIPS_MACH_OCTEON3,
PROCESSOR_FLAGS::MIPS_MACH_5400,
PROCESSOR_FLAGS::MIPS_MACH_5900,
PROCESSOR_FLAGS::MIPS_MACH_5500,
PROCESSOR_FLAGS::MIPS_MACH_9000,
PROCESSOR_FLAGS::MIPS_MACH_LS2E,
PROCESSOR_FLAGS::MIPS_MACH_LS2F,
PROCESSOR_FLAGS::MIPS_MACH_LS3A,
PROCESSOR_FLAGS::MIPS_MICROMIPS,
PROCESSOR_FLAGS::MIPS_ARCH_ASE_M16,
PROCESSOR_FLAGS::MIPS_ARCH_ASE_MDMX,
PROCESSOR_FLAGS::MIPS_ARCH_1,
PROCESSOR_FLAGS::MIPS_ARCH_2,
PROCESSOR_FLAGS::MIPS_ARCH_3,
PROCESSOR_FLAGS::MIPS_ARCH_4,
PROCESSOR_FLAGS::MIPS_ARCH_5,
PROCESSOR_FLAGS::MIPS_ARCH_32,
PROCESSOR_FLAGS::MIPS_ARCH_64,
PROCESSOR_FLAGS::MIPS_ARCH_32R2,
PROCESSOR_FLAGS::MIPS_ARCH_64R2,
PROCESSOR_FLAGS::MIPS_ARCH_32R6,
PROCESSOR_FLAGS::MIPS_ARCH_64R6,
PROCESSOR_FLAGS::RISCV_RVC,
PROCESSOR_FLAGS::RISCV_FLOAT_ABI_SOFT,
PROCESSOR_FLAGS::RISCV_FLOAT_ABI_SINGLE,
PROCESSOR_FLAGS::RISCV_FLOAT_ABI_DOUBLE,
PROCESSOR_FLAGS::RISCV_FLOAT_ABI_QUAD,
PROCESSOR_FLAGS::RISCV_FLOAT_ABI_RVE,
PROCESSOR_FLAGS::RISCV_FLOAT_ABI_TSO,
};
template<class T>
Header::Header(const T& header) :
identity_{},
file_type_(FILE_TYPE(header.e_type)),
machine_type_(ARCH(header.e_machine)),
object_file_version_(VERSION(header.e_version)),
entrypoint_(header.e_entry),
program_headers_offset_(header.e_phoff),
section_headers_offset_(header.e_shoff),
processor_flags_(header.e_flags),
header_size_(header.e_ehsize),
program_header_size_(header.e_phentsize),
numberof_segments_(header.e_phnum),
section_header_size_(header.e_shentsize),
numberof_sections_(header.e_shnum),
section_string_table_idx_(header.e_shstrndx) {
std::copy(std::begin(header.e_ident), std::end(header.e_ident),
std::begin(identity_));
}
template Header::Header(const details::Elf32_Ehdr& header);
template Header::Header(const details::Elf64_Ehdr& header);
void Header::identity(const std::string& identity) {
std::copy(identity.begin(), identity.end(), identity_.begin());
}
void Header::identity(const Header::identity_t& identity) {
std::copy(identity.begin(), identity.end(), identity_.begin());
}
void Header::accept(LIEF::Visitor& visitor) const {
visitor.visit(*this);
}
bool Header::has(PROCESSOR_FLAGS flag) const {
const auto ID = uint64_t(flag) >> PFLAGS_BIT;
const ARCH arch = machine_type();
uint32_t raw_flag = uint64_t(flag) & PFLAGS_MASK;
if (ID == PF_ARM_ID) {
if (arch != ARCH::ARM) {
return false;
}
switch (flag) {
case PROCESSOR_FLAGS::ARM_EABI_VER1:
case PROCESSOR_FLAGS::ARM_EABI_VER2:
case PROCESSOR_FLAGS::ARM_EABI_VER3:
case PROCESSOR_FLAGS::ARM_EABI_VER4:
case PROCESSOR_FLAGS::ARM_EABI_VER5:
return (processor_flag() & ARM_EABIMASK) == raw_flag;
default: return (processor_flag() & raw_flag) != 0;
}
return false;
}
if (ID == PF_RISCV_ID) {
if (arch != ARCH::RISCV) {
return false;
}
switch (flag) {
case PROCESSOR_FLAGS::RISCV_FLOAT_ABI_SOFT:
case PROCESSOR_FLAGS::RISCV_FLOAT_ABI_SINGLE:
case PROCESSOR_FLAGS::RISCV_FLOAT_ABI_DOUBLE:
case PROCESSOR_FLAGS::RISCV_FLOAT_ABI_QUAD:
case PROCESSOR_FLAGS::ARM_EABI_VER5:
return (processor_flag() & RISCV_FLOAT_ABI_MASK) == raw_flag;
default: return (processor_flag() & raw_flag) != 0;
}
}
if (ID == PF_HEX_ID) {
if (arch != ARCH::HEXAGON) {
return false;
}
return (processor_flag() & raw_flag) != 0;
}
if (ID == PF_LOONGARCH_ID) {
if (arch != ARCH::LOONGARCH) {
return false;
}
return (processor_flag() & raw_flag) != 0;
}
if (ID == PF_MIPS_ID) {
if (arch != ARCH::MIPS && arch != ARCH::MIPS_RS3_LE && arch != ARCH::MIPS_X) {
return false;
}
switch (flag) {
case PROCESSOR_FLAGS::MIPS_NOREORDER:
case PROCESSOR_FLAGS::MIPS_PIC:
case PROCESSOR_FLAGS::MIPS_CPIC:
case PROCESSOR_FLAGS::MIPS_ABI2:
case PROCESSOR_FLAGS::MIPS_32BITMODE:
case PROCESSOR_FLAGS::MIPS_FP64:
case PROCESSOR_FLAGS::MIPS_NAN2008:
return (processor_flag() & raw_flag) != 0;
case PROCESSOR_FLAGS::MIPS_ABI_O32:
case PROCESSOR_FLAGS::MIPS_ABI_O64:
case PROCESSOR_FLAGS::MIPS_ABI_EABI32:
case PROCESSOR_FLAGS::MIPS_ABI_EABI64:
return (processor_flag() & MIPS_ABI_MASK) == (raw_flag & MIPS_ABI_MASK);
case PROCESSOR_FLAGS::MIPS_MACH_3900:
case PROCESSOR_FLAGS::MIPS_MACH_4010:
case PROCESSOR_FLAGS::MIPS_MACH_4100:
case PROCESSOR_FLAGS::MIPS_MACH_4650:
case PROCESSOR_FLAGS::MIPS_MACH_4120:
case PROCESSOR_FLAGS::MIPS_MACH_4111:
case PROCESSOR_FLAGS::MIPS_MACH_SB1:
case PROCESSOR_FLAGS::MIPS_MACH_OCTEON:
case PROCESSOR_FLAGS::MIPS_MACH_XLR:
case PROCESSOR_FLAGS::MIPS_MACH_OCTEON2:
case PROCESSOR_FLAGS::MIPS_MACH_OCTEON3:
case PROCESSOR_FLAGS::MIPS_MACH_5400:
case PROCESSOR_FLAGS::MIPS_MACH_5900:
case PROCESSOR_FLAGS::MIPS_MACH_5500:
case PROCESSOR_FLAGS::MIPS_MACH_9000:
case PROCESSOR_FLAGS::MIPS_MACH_LS2E:
case PROCESSOR_FLAGS::MIPS_MACH_LS2F:
case PROCESSOR_FLAGS::MIPS_MACH_LS3A:
return (processor_flag() & MIPS_MACH_MASK) == (raw_flag & MIPS_MACH_MASK);
case PROCESSOR_FLAGS::MIPS_MICROMIPS:
case PROCESSOR_FLAGS::MIPS_ARCH_ASE_M16:
case PROCESSOR_FLAGS::MIPS_ARCH_ASE_MDMX:
return (processor_flag() & MIPS_ARCH_ASE_MASK) ==
(raw_flag & MIPS_ARCH_ASE_MASK);
case PROCESSOR_FLAGS::MIPS_ARCH_1:
case PROCESSOR_FLAGS::MIPS_ARCH_2:
case PROCESSOR_FLAGS::MIPS_ARCH_3:
case PROCESSOR_FLAGS::MIPS_ARCH_4:
case PROCESSOR_FLAGS::MIPS_ARCH_5:
case PROCESSOR_FLAGS::MIPS_ARCH_32:
case PROCESSOR_FLAGS::MIPS_ARCH_64:
case PROCESSOR_FLAGS::MIPS_ARCH_32R2:
case PROCESSOR_FLAGS::MIPS_ARCH_64R2:
case PROCESSOR_FLAGS::MIPS_ARCH_32R6:
case PROCESSOR_FLAGS::MIPS_ARCH_64R6:
return (processor_flag() & MIPS_ARCH_MASK) == raw_flag;
default: return (processor_flag() & raw_flag) != 0;
}
}
if (ID != 0) {
LIEF_WARN("Unsupported architecture {} for this flag", to_string(arch));
return false;
}
return false;
}
std::vector<PROCESSOR_FLAGS> Header::flags_list() const {
std::vector<PROCESSOR_FLAGS> flags;
std::copy_if(PFLAGS_LIST.begin(), PFLAGS_LIST.end(), std::back_inserter(flags),
[this](PROCESSOR_FLAGS f) { return this->has(f); });
return flags;
}
std::ostream& operator<<(std::ostream& os, const Header& hdr) {
static constexpr auto PADDING = 35;
const std::vector<PROCESSOR_FLAGS> flags = hdr.flags_list();
std::vector<std::string> flags_str;
flags_str.reserve(flags.size());
std::transform(flags.begin(), flags.end(), std::back_inserter(flags_str),
[](PROCESSOR_FLAGS f) { return to_string(f); });
os << "ELF Header:\n"
<< fmt::format("{:<{}} {:02x}\n", "Magic:", PADDING,
fmt::join(hdr.identity(), " "))
<< fmt::format("{:<{}} {}\n", "Class:", PADDING,
to_string(hdr.identity_class()))
<< fmt::format("{:<{}} {}\n", "Data:", PADDING,
to_string(hdr.identity_data()))
<< fmt::format("{:<{}} {}\n", "Version:", PADDING,
to_string(hdr.identity_version()))
<< fmt::format("{:<{}} {}\n", "OS/ABI:", PADDING,
to_string(hdr.identity_os_abi()))
<< fmt::format("{:<{}} {}\n", "ABI Version:", PADDING,
hdr.identity_abi_version())
<< fmt::format("{:<{}} {}\n", "Type:", PADDING, to_string(hdr.file_type()))
<< fmt::format("{:<{}} {}\n", "Machine:", PADDING,
to_string(hdr.machine_type()))
<< fmt::format("{:<{}} {}\n", "Version:", PADDING,
to_string(hdr.object_file_version()))
<< fmt::format("{:<{}} {:#010x}\n", "Entry point address:", PADDING,
hdr.entrypoint())
<< fmt::format("{:<{}} {} (bytes into file)\n",
"Start of program headers:", PADDING,
hdr.program_headers_offset())
<< fmt::format("{:<{}} {} (bytes into file)\n",
"Start of section headers:", PADDING,
hdr.section_headers_offset())
<< fmt::format("{:<{}} {:#x} {}\n", "Flags:", PADDING, hdr.processor_flag(),
fmt::to_string(fmt::join(flags_str, ", ")))
<< fmt::format("{:<{}} {} (bytes)\n", "Size of this header:", PADDING,
hdr.header_size())
<< fmt::format("{:<{}} {} (bytes)\n", "Size of program headers:", PADDING,
hdr.program_header_size())
<< fmt::format("{:<{}} {}\n", "Number of program headers:", PADDING,
hdr.numberof_segments())
<< fmt::format("{:<{}} {} (bytes)\n", "Size of section headers:", PADDING,
hdr.section_header_size())
<< fmt::format("{:<{}} {}\n", "Number of section headers:", PADDING,
hdr.numberof_sections())
<< fmt::format("{:<{}} {}\n", "Section header string table index:", PADDING,
hdr.section_name_table_idx());
return os;
}
const char* to_string(Header::FILE_TYPE e) {
switch (e) {
case Header::FILE_TYPE::NONE: return "NONE";
case Header::FILE_TYPE::REL: return "REL";
case Header::FILE_TYPE::EXEC: return "EXEC";
case Header::FILE_TYPE::DYN: return "DYN";
case Header::FILE_TYPE::CORE: return "CORE";
}
return "UNKNOWN";
}
const char* to_string(Header::VERSION e) {
switch (e) {
case Header::VERSION::NONE: return "NONE";
case Header::VERSION::CURRENT: return "CURRENT";
}
return "UNKNOWN";
}
const char* to_string(Header::CLASS e) {
switch (e) {
case Header::CLASS::NONE: return "NONE";
case Header::CLASS::ELF32: return "ELF32";
case Header::CLASS::ELF64: return "ELF64";
}
return "UNKNOWN";
}
const char* to_string(Header::ELF_DATA e) {
switch (e) {
case Header::ELF_DATA::NONE: return "NONE";
case Header::ELF_DATA::LSB: return "LSB";
case Header::ELF_DATA::MSB: return "MSB";
}
return "UNKNOWN";
}
const char* to_string(Header::OS_ABI e) {
#define ENTRY(X) std::pair(Header::OS_ABI::X, #X)
STRING_MAP enums2str{
ENTRY(SYSTEMV), ENTRY(HPUX), ENTRY(NETBSD), ENTRY(GNU),
ENTRY(LINUX), ENTRY(HURD), ENTRY(SOLARIS), ENTRY(AIX),
ENTRY(IRIX), ENTRY(FREEBSD), ENTRY(TRU64), ENTRY(MODESTO),
ENTRY(OPENBSD), ENTRY(OPENVMS), ENTRY(NSK), ENTRY(AROS),
ENTRY(FENIXOS), ENTRY(CLOUDABI), ENTRY(C6000_ELFABI), ENTRY(AMDGPU_HSA),
ENTRY(C6000_LINUX), ENTRY(ARM), ENTRY(STANDALONE),
};
#undef ENTRY
if (auto it = enums2str.find(e); it != enums2str.end()) {
return it->second;
}
return "UNKNOWN";
}
}