@ 261107.7
current
/* 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 "logging.hpp"
#include "frozen.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"
#include "LIEF/iostream.hpp"

#include "LIEF/ELF/hash.hpp"
#include "LIEF/ELF/EnumToString.hpp"
#include "LIEF/ELF/NoteDetails/core/CorePrStatus.hpp"
#include "ELF/Structures.hpp"


namespace LIEF::ELF {

template<class ELF_T>
inline CorePrStatus::pr_status_t
    get_status_impl(const Note::description_t& description) {
  using Elf_Prstatus = typename ELF_T::Elf_Prstatus;
  CorePrStatus::pr_status_t status;

  auto stream = SpanStream::from_vector(description);
  if (!stream) {
    return {};
  }

  auto raw_pr_status = stream->read<Elf_Prstatus>();
  if (!raw_pr_status) {
    return {};
  }

  status.info.signo = raw_pr_status->pr_info.si_signo;
  status.info.code = raw_pr_status->pr_info.si_code;
  status.info.err = raw_pr_status->pr_info.si_errno;

  status.cursig = raw_pr_status->pr_cursig;
  status.reserved = raw_pr_status->reserved;
  status.sigpend = raw_pr_status->pr_sigpend;
  status.sighold = raw_pr_status->pr_sighold;

  status.pid = raw_pr_status->pr_pid;
  status.ppid = raw_pr_status->pr_ppid;
  status.pgrp = raw_pr_status->pr_pgrp;
  status.sid = raw_pr_status->pr_sid;

  status.utime.sec = raw_pr_status->pr_utime.tv_sec;
  status.utime.usec = raw_pr_status->pr_utime.tv_usec;

  status.stime.sec = raw_pr_status->pr_stime.tv_sec;
  status.stime.usec = raw_pr_status->pr_stime.tv_usec;

  status.cutime.sec = raw_pr_status->pr_cutime.tv_sec;
  status.cutime.usec = raw_pr_status->pr_cutime.tv_usec;

  status.cstime.sec = raw_pr_status->pr_cstime.tv_sec;
  status.cstime.usec = raw_pr_status->pr_cstime.tv_usec;

  return status;
}

template<class ELF_T>
inline Note::description_t
    write_status_impl(const CorePrStatus::pr_status_t& status) {
  using Elf_Prstatus = typename ELF_T::Elf_Prstatus;
  Elf_Prstatus raw_pr_status;

  raw_pr_status.pr_info.si_signo = status.info.signo;
  raw_pr_status.pr_info.si_code = status.info.code;
  raw_pr_status.pr_info.si_errno = status.info.err;

  raw_pr_status.pr_cursig = status.cursig;
  raw_pr_status.reserved = status.reserved;
  raw_pr_status.pr_sigpend =
      static_cast<decltype(raw_pr_status.pr_sigpend)>(status.sigpend);
  raw_pr_status.pr_sighold =
      static_cast<decltype(raw_pr_status.pr_sighold)>(status.sighold);

  raw_pr_status.pr_pid = status.pid;
  raw_pr_status.pr_ppid = status.ppid;
  raw_pr_status.pr_pgrp = status.pgrp;
  raw_pr_status.pr_sid = status.sid;

  raw_pr_status.pr_utime.tv_sec = status.utime.sec;
  raw_pr_status.pr_utime.tv_usec = status.utime.usec;

  raw_pr_status.pr_cutime.tv_sec = status.cutime.sec;
  raw_pr_status.pr_cutime.tv_usec = status.cutime.usec;

  raw_pr_status.pr_stime.tv_sec = status.stime.sec;
  raw_pr_status.pr_stime.tv_usec = status.stime.usec;

  raw_pr_status.pr_cstime.tv_sec = status.cstime.sec;
  raw_pr_status.pr_cstime.tv_usec = status.cstime.usec;

  vector_iostream ios;
  ios.write<Elf_Prstatus>(raw_pr_status);
  return ios.raw();
}

template<class REG_T>
inline result<uint64_t> get_reg_impl(REG_T reg,
                                     const Note::description_t& description,
                                     Header::CLASS cls, ARCH arch) {
  if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::X86>) {
    if (arch != ARCH::I386) {
      return make_error_code(lief_errors::not_found);
    }
  } else if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::X86_64>) {
    if (arch != ARCH::X86_64) {
      return make_error_code(lief_errors::not_found);
    }
  } else if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::ARM>) {
    if (arch != ARCH::ARM) {
      return make_error_code(lief_errors::not_found);
    }
  } else if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::AARCH64>) {
    if (arch != ARCH::AARCH64) {
      return make_error_code(lief_errors::not_found);
    }
  } else {
    LIEF_WARN("Unsupported architecture");
    return make_error_code(lief_errors::not_found);
  }

  auto pos = static_cast<int32_t>(reg);
  if (pos < 0 || pos >= static_cast<int32_t>(REG_T::_COUNT)) {
    return make_error_code(lief_errors::not_found);
  }

  auto stream = SpanStream::from_vector(description);
  if (!stream) {
    return make_error_code(lief_errors::not_found);
  }

  if (cls == Header::CLASS::ELF32) {
    stream->increment_pos(sizeof(details::ELF32::Elf_Prstatus));
    stream->increment_pos(pos * sizeof(uint32_t));
    auto value = stream->read<uint32_t>();
    if (!value) {
      return make_error_code(lief_errors::corrupted);
    }
    return *value;
  }

  if (cls == Header::CLASS::ELF64) {
    stream->increment_pos(sizeof(details::ELF64::Elf_Prstatus));
    stream->increment_pos(pos * sizeof(uint64_t));
    auto value = stream->read<uint64_t>();
    if (!value) {
      return make_error_code(lief_errors::corrupted);
    }
    return *value;
  }

  return make_error_code(lief_errors::not_found);
}

template<class REG_T>
inline ok_error_t set_reg_impl(REG_T reg, uint64_t value,
                               Note::description_t& description, Header::CLASS cls,
                               ARCH arch) {
  if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::X86>) {
    if (arch != ARCH::I386) {
      return make_error_code(lief_errors::not_found);
    }
  } else if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::X86_64>) {
    if (arch != ARCH::X86_64) {
      return make_error_code(lief_errors::not_found);
    }
  } else if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::ARM>) {
    if (arch != ARCH::ARM) {
      return make_error_code(lief_errors::not_found);
    }
  } else if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::AARCH64>) {
    if (arch != ARCH::AARCH64) {
      return make_error_code(lief_errors::not_found);
    }
  } else {
    LIEF_WARN("Unsupported architecture");
    return make_error_code(lief_errors::not_found);
  }

  auto pos = static_cast<int32_t>(reg);
  if (pos < 0 || pos >= static_cast<int32_t>(REG_T::_COUNT)) {
    return make_error_code(lief_errors::not_found);
  }

  size_t offset = 0;
  vector_iostream os;
  os.write(description);

  if (cls == Header::CLASS::ELF32) {
    offset += sizeof(details::ELF32::Elf_Prstatus) + pos * sizeof(uint32_t);
    os.seekp(offset);
    os.write<uint32_t>(value);
    return ok();
  }

  if (cls == Header::CLASS::ELF64) {
    offset += sizeof(details::ELF64::Elf_Prstatus) + pos * sizeof(uint64_t);
    os.seekp(offset);
    os.write<uint64_t>(value);
    os.move(description);
    return ok();
  }

  return make_error_code(lief_errors::not_found);
}

std::vector<uint64_t> CorePrStatus::register_values() const {
  std::vector<uint64_t> values;
  switch (arch_) {
    case ARCH::X86_64:
    {
      using Reg = Registers::X86_64;
      const auto count = static_cast<size_t>(Reg::_COUNT);
      values.reserve(count);
      for (size_t i = 0; i < count; ++i) {
        if (auto val = get(Reg(i))) {
          values.push_back(*val);
        } else {
          return {};
        }
      }
      return values;
    }
    case ARCH::I386:
    {
      using Reg = Registers::X86;
      const auto count = static_cast<size_t>(Reg::_COUNT);
      values.reserve(count);
      for (size_t i = 0; i < count; ++i) {
        if (auto val = get(Reg(i))) {
          values.push_back(*val);
        } else {
          return {};
        }
      }
      return values;
    }
    case ARCH::ARM:
    {
      using Reg = Registers::ARM;
      const auto count = static_cast<size_t>(Reg::_COUNT);
      values.reserve(count);
      for (size_t i = 0; i < count; ++i) {
        if (auto val = get(Reg(i))) {
          values.push_back(*val);
        } else {
          return {};
        }
      }
      return values;
    }
    case ARCH::AARCH64:
    {
      using Reg = Registers::AARCH64;
      const auto count = static_cast<size_t>(Reg::_COUNT);
      values.reserve(count);
      for (size_t i = 0; i < count; ++i) {
        if (auto val = get(Reg(i))) {
          values.push_back(*val);
        } else {
          return {};
        }
      }
      return values;
    }
    default: return {};
  }
}

CorePrStatus::pr_status_t CorePrStatus::status() const {
  if (class_ == Header::CLASS::ELF32) {
    return get_status_impl<details::ELF32>(description_);
  }
  return get_status_impl<details::ELF64>(description_);
}

void CorePrStatus::status(const pr_status_t& status) {
  Note::description_t description = class_ == Header::CLASS::ELF32 ?
                                        write_status_impl<details::ELF32>(status) :
                                        write_status_impl<details::ELF64>(status);

  if (description.empty()) {
    return;
  }

  if (description_.size() < description.size()) {
    description_.resize(description.size());
  }

  std::move(description.begin(), description.end(), description_.begin());
}

result<uint64_t> CorePrStatus::get(Registers::X86 reg) const {
  return get_reg_impl<Registers::X86>(reg, description_, class_, arch_);
}

result<uint64_t> CorePrStatus::get(Registers::X86_64 reg) const {
  return get_reg_impl<Registers::X86_64>(reg, description_, class_, arch_);
}

result<uint64_t> CorePrStatus::get(Registers::ARM reg) const {
  return get_reg_impl<Registers::ARM>(reg, description_, class_, arch_);
}

result<uint64_t> CorePrStatus::get(Registers::AARCH64 reg) const {
  return get_reg_impl<Registers::AARCH64>(reg, description_, class_, arch_);
}

ok_error_t CorePrStatus::set(Registers::X86 reg, uint64_t value) {
  return set_reg_impl<Registers::X86>(reg, value, description_, class_, arch_);
}

ok_error_t CorePrStatus::set(Registers::X86_64 reg, uint64_t value) {
  return set_reg_impl<Registers::X86_64>(reg, value, description_, class_, arch_);
}

ok_error_t CorePrStatus::set(Registers::ARM reg, uint64_t value) {
  return set_reg_impl<Registers::ARM>(reg, value, description_, class_, arch_);
}

ok_error_t CorePrStatus::set(Registers::AARCH64 reg, uint64_t value) {
  return set_reg_impl<Registers::AARCH64>(reg, value, description_, class_, arch_);
}


result<uint64_t> CorePrStatus::pc() const {
  switch (arch_) {
    case ARCH::AARCH64: return get(Registers::AARCH64::PC);
    case ARCH::ARM: return get(Registers::ARM::R15);
    case ARCH::I386: return get(Registers::X86::EIP);
    case ARCH::X86_64: return get(Registers::X86_64::RIP);
    default: return make_error_code(lief_errors::not_supported);
  }
}

result<uint64_t> CorePrStatus::sp() const {
  switch (arch_) {
    case ARCH::AARCH64: return get(Registers::AARCH64::X31);
    case ARCH::ARM: return get(Registers::ARM::R13);
    case ARCH::I386: return get(Registers::X86::ESP);
    case ARCH::X86_64: return get(Registers::X86_64::RSP);
    default: return make_error_code(lief_errors::not_supported);
  }
}

result<uint64_t> CorePrStatus::return_value() const {
  switch (arch_) {
    case ARCH::AARCH64: return get(Registers::AARCH64::X0);
    case ARCH::ARM: return get(Registers::ARM::R0);
    case ARCH::I386: return get(Registers::X86::EAX);
    case ARCH::X86_64: return get(Registers::X86_64::RAX);
    default: return make_error_code(lief_errors::not_supported);
  }
}

template<class REG>
void dump_impl(std::ostream& os, const std::vector<uint64_t>& reg_vals) {
  for (size_t i = 0; i < reg_vals.size(); ++i) {
    if constexpr (std::is_void_v<REG>) {
      os << fmt::format("   {:#010x}\n", reg_vals[i]);
    } else {
      os << fmt::format("   {}: {:#010x}\n", to_string(REG(i)), reg_vals[i]);
    }
  }
}

void CorePrStatus::dump(std::ostream& os) const {
  Note::dump(os);
  const CorePrStatus::pr_status_t& status = this->status();
  os << '\n'
     << fmt::format("  PID: {:04d} PPID: {:04d} PGRP: {:04d}\n", status.pid,
                    status.ppid, status.pgrp)
     << fmt::format("  SID: {:04d} SIGNO: {:04d} SIGCODE: {:04d}\n", status.sid,
                    status.info.signo, status.info.code)
     << fmt::format("  SIGERR: {:04d} SIGPEND: {:04d} SIGHOLD: {:04d}\n",
                    status.info.err, status.sigpend, status.sighold)
     << fmt::format("  CURRSIG: {:#06x} reserved: {}\n", status.cursig,
                    status.reserved);
  const std::vector<uint64_t>& reg_vals = register_values();
  switch (architecture()) {
    case ARCH::ARM: dump_impl<CorePrStatus::Registers::ARM>(os, reg_vals); break;
    case ARCH::AARCH64:
      dump_impl<CorePrStatus::Registers::AARCH64>(os, reg_vals);
      break;
    case ARCH::I386: dump_impl<CorePrStatus::Registers::X86>(os, reg_vals); break;
    case ARCH::X86_64:
      dump_impl<CorePrStatus::Registers::X86_64>(os, reg_vals);
      break;
    default: dump_impl<void>(os, reg_vals); break;
  }
}

void CorePrStatus::accept(Visitor& visitor) const {
  visitor.visit(*this);
}

const char* to_string(CorePrStatus::Registers::X86 e) {
#define ENTRY(X) std::pair(CorePrStatus::Registers::X86::X, #X)
  STRING_MAP enum_strings{
      ENTRY(EBX), ENTRY(ECX),      ENTRY(EDX), ENTRY(ESI), ENTRY(EDI),
      ENTRY(EBP), ENTRY(EAX),      ENTRY(DS),  ENTRY(ES),  ENTRY(FS),
      ENTRY(GS),  ENTRY(ORIG_EAX), ENTRY(EIP), ENTRY(CS),  ENTRY(EFLAGS),
      ENTRY(ESP), ENTRY(SS),
  };
#undef ENTRY

  if (auto it = enum_strings.find(e); it != enum_strings.end()) {
    return it->second;
  }
  return "UNKNOWN";
}

const char* to_string(CorePrStatus::Registers::X86_64 e) {
#define ENTRY(X) std::pair(CorePrStatus::Registers::X86_64::X, #X)
  STRING_MAP enums2str{
      ENTRY(R15),      ENTRY(R14),     ENTRY(R13),     ENTRY(R12),    ENTRY(RBP),
      ENTRY(RBX),      ENTRY(R11),     ENTRY(R10),     ENTRY(R9),     ENTRY(R8),
      ENTRY(RAX),      ENTRY(RCX),     ENTRY(RDX),     ENTRY(RSI),    ENTRY(RDI),
      ENTRY(ORIG_RAX), ENTRY(RIP),     ENTRY(CS),      ENTRY(EFLAGS), ENTRY(RSP),
      ENTRY(SS),       ENTRY(FS_BASE), ENTRY(GS_BASE), ENTRY(DS),     ENTRY(ES),
  };
#undef ENTRY

  if (auto it = enums2str.find(e); it != enums2str.end()) {
    return it->second;
  }
  return "UNKNOWN";
}

const char* to_string(CorePrStatus::Registers::ARM e) {
#define ENTRY(X) std::pair(CorePrStatus::Registers::ARM::X, #X)
  STRING_MAP enums2str{
      ENTRY(R0),  ENTRY(R1),  ENTRY(R2),  ENTRY(R3),  ENTRY(R4),   ENTRY(R5),
      ENTRY(R6),  ENTRY(R7),  ENTRY(R8),  ENTRY(R9),  ENTRY(R10),  ENTRY(R11),
      ENTRY(R12), ENTRY(R13), ENTRY(R14), ENTRY(R15), ENTRY(CPSR),
  };
#undef ENTRY

  if (auto it = enums2str.find(e); it != enums2str.end()) {
    return it->second;
  }

  return "UNKNOWN";
}

const char* to_string(CorePrStatus::Registers::AARCH64 e) {
#define ENTRY(X) std::pair(CorePrStatus::Registers::AARCH64::X, #X)
  STRING_MAP enum_strings{
      ENTRY(X0),  ENTRY(X1),  ENTRY(X2),  ENTRY(X3),  ENTRY(X4),     ENTRY(X5),
      ENTRY(X6),  ENTRY(X7),  ENTRY(X8),  ENTRY(X9),  ENTRY(X10),    ENTRY(X11),
      ENTRY(X12), ENTRY(X13), ENTRY(X14), ENTRY(X15), ENTRY(X15),    ENTRY(X16),
      ENTRY(X17), ENTRY(X18), ENTRY(X19), ENTRY(X20), ENTRY(X21),    ENTRY(X22),
      ENTRY(X23), ENTRY(X24), ENTRY(X25), ENTRY(X26), ENTRY(X27),    ENTRY(X28),
      ENTRY(X29), ENTRY(X30), ENTRY(X31), ENTRY(PC),  ENTRY(PSTATE),
  };
#undef ENTRY

  if (auto it = enum_strings.find(e); it != enum_strings.end()) {
    return it->second;
  }
  return "UNKNOWN";
}

} // namespace LIEF::ELF