current commit
260929.11 · deps: update lief to 1.0.0
/* 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";
}


}