@ 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 <memory>
#include <algorithm>
#include <utility>

#include "LIEF/utils.hpp"
#include "LIEF/ELF/hash.hpp"
#include "LIEF/ELF/Note.hpp"
#include "LIEF/ELF/NoteDetails/NoteAbi.hpp"
#include "LIEF/ELF/NoteDetails/QNXStack.hpp"
#include "LIEF/ELF/NoteDetails/AndroidIdent.hpp"
#include "LIEF/ELF/NoteDetails/core/CoreAuxv.hpp"
#include "LIEF/ELF/NoteDetails/core/CoreFile.hpp"
#include "LIEF/ELF/NoteDetails/core/CorePrPsInfo.hpp"
#include "LIEF/ELF/NoteDetails/core/CorePrStatus.hpp"
#include "LIEF/ELF/NoteDetails/core/CoreSigInfo.hpp"
#include "LIEF/ELF/NoteDetails/NoteGnuProperty.hpp"
#include "LIEF/iostream.hpp"
#include "LIEF/BinaryStream/BinaryStream.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"

#include "frozen.hpp"
#include "logging.hpp"
#include "internal_utils.hpp"

#define IMPL_READ_AT(T) template result<T> Note::read_at(size_t) const;

#define IMPL_WRITE_AT(T)                                                          \
  template ok_error_t Note::write_at(size_t offset, const T& value);


namespace LIEF::ELF {

static constexpr auto NT_CORE_NAME = "CORE";
static constexpr auto NT_GNU_NAME = "GNU";
static constexpr auto NT_ANDROID_NAME = "Android";
static constexpr auto NT_LINUX_NAME = "LINUX";
static constexpr auto NT_GO_NAME = "Go";
static constexpr auto NT_STAPSDT_NAME = "stapsdt";
static constexpr auto NT_CRASHPAD_NAME = "Crashpad";
static constexpr auto NT_QNX = "QNX";

CONST_MAP_ALT GNU_TYPES{
    std::pair(1, Note::TYPE::GNU_ABI_TAG),
    std::pair(2, Note::TYPE::GNU_HWCAP),
    std::pair(3, Note::TYPE::GNU_BUILD_ID),
    std::pair(4, Note::TYPE::GNU_GOLD_VERSION),
    std::pair(5, Note::TYPE::GNU_PROPERTY_TYPE_0),
};

CONST_MAP_ALT GENERIC_TYPES{
    std::pair(0x100, Note::TYPE::GNU_BUILD_ATTRIBUTE_OPEN),
    std::pair(0x101, Note::TYPE::GNU_BUILD_ATTRIBUTE_FUNC),
};

/* Core note types. */
CONST_MAP_ALT CORE_TYPES{
    std::pair(1, Note::TYPE::CORE_PRSTATUS),
    std::pair(2, Note::TYPE::CORE_FPREGSET),
    std::pair(3, Note::TYPE::CORE_PRPSINFO),
    std::pair(4, Note::TYPE::CORE_TASKSTRUCT),
    std::pair(6, Note::TYPE::CORE_AUXV),
    std::pair(10, Note::TYPE::CORE_PSTATUS),
    std::pair(12, Note::TYPE::CORE_FPREGS),
    std::pair(13, Note::TYPE::CORE_PSINFO),
    std::pair(16, Note::TYPE::CORE_LWPSTATUS),
    std::pair(17, Note::TYPE::CORE_LWPSINFO),
    std::pair(18, Note::TYPE::CORE_WIN32PSTATUS),
    std::pair(0x53494749, Note::TYPE::CORE_SIGINFO),
    std::pair(0x46e62b7f, Note::TYPE::CORE_PRXFPREG),
    std::pair(0x46494c45, Note::TYPE::CORE_FILE),
};

CONST_MAP_ALT CORE_ARM_YPES{
    std::pair(0x400, Note::TYPE::CORE_ARM_VFP),
    std::pair(0x401, Note::TYPE::CORE_ARM_TLS),
    std::pair(0x402, Note::TYPE::CORE_ARM_HW_BREAK),
    std::pair(0x403, Note::TYPE::CORE_ARM_HW_WATCH),
    std::pair(0x404, Note::TYPE::CORE_ARM_SYSTEM_CALL),
    std::pair(0x405, Note::TYPE::CORE_ARM_SVE),
    std::pair(0x406, Note::TYPE::CORE_ARM_PAC_MASK),
    std::pair(0x407, Note::TYPE::CORE_ARM_PACA_KEYS),
    std::pair(0x408, Note::TYPE::CORE_ARM_PACG_KEYS),
    std::pair(0x409, Note::TYPE::CORE_TAGGED_ADDR_CTRL),
    std::pair(0x40a, Note::TYPE::CORE_PAC_ENABLED_KEYS),
};

CONST_MAP_ALT CORE_X86_TYPES{
    std::pair(0x200, Note::TYPE::CORE_X86_TLS),
    std::pair(0x201, Note::TYPE::CORE_X86_IOPERM),
    std::pair(0x202, Note::TYPE::CORE_X86_XSTATE),
    std::pair(0x203, Note::TYPE::CORE_X86_CET),
};


/* Android notes */
CONST_MAP_ALT ANDROID_TYPES{
    std::pair(1, Note::TYPE::ANDROID_IDENT),
    std::pair(3, Note::TYPE::ANDROID_KUSER),
    std::pair(4, Note::TYPE::ANDROID_MEMTAG),
};

/* Go types. */
CONST_MAP_ALT GO_TYPES{
    std::pair(4, Note::TYPE::GO_BUILDID),
};

/* Stapsdt types. */
CONST_MAP_ALT STAPSDT_TYPES{
    std::pair(3, Note::TYPE::STAPSDT),
};

/* Crashpad types. */
CONST_MAP_ALT CRASHPAD_TYPES{
    std::pair(0x4f464e49, Note::TYPE::CRASHPAD),
};

/* Crashpad types. */
CONST_MAP_ALT QNX_TYPES{
    std::pair(3, Note::TYPE::QNX_STACK),
};

static inline std::string strip_zero(const std::string& name) {
  return name.c_str();
}

result<const char*> Note::type_to_section(TYPE type) {
  CONST_MAP_ALT TYPE2SECTION{
      std::pair(TYPE::GNU_ABI_TAG, ".note.ABI-tag"),
      std::pair(TYPE::GNU_HWCAP, ".note.gnu.hwcap"),
      std::pair(TYPE::GNU_BUILD_ID, ".note.gnu.build-id"),
      std::pair(TYPE::GNU_GOLD_VERSION, ".note.gnu.gold-version"),
      std::pair(TYPE::GNU_PROPERTY_TYPE_0, ".note.gnu.property"),
      std::pair(TYPE::GNU_BUILD_ATTRIBUTE_OPEN, ".gnu.build.attributes"),
      std::pair(TYPE::GNU_BUILD_ATTRIBUTE_FUNC, ".gnu.build.attributes"),

      std::pair(TYPE::STAPSDT, ".note.stapsdt"),
      std::pair(TYPE::CRASHPAD, ".note.crashpad.info"),
      std::pair(TYPE::ANDROID_IDENT, ".note.android.ident"),
      std::pair(TYPE::GO_BUILDID, ".note.go.buildid"),
      std::pair(TYPE::QNX_STACK, ".note"),
  };

  if (auto it = TYPE2SECTION.find(type); it != TYPE2SECTION.end()) {
    return it->second;
  }
  return make_error_code(lief_errors::not_found);
}

inline result<uint32_t> raw_type(const std::string& name, Note::TYPE type) {
  std::string norm_name = strip_zero(name);

  if (norm_name == NT_CORE_NAME) {
    for (const auto& [nt, ntype] : CORE_TYPES) {
      if (ntype == type) {
        return nt;
      }
    }
    return make_error_code(lief_errors::not_found);
  }

  if (norm_name == NT_LINUX_NAME) {
    for (const auto& [nt, ntype] : CORE_ARM_YPES) {
      if (ntype == type) {
        return nt;
      }
    }
    for (const auto& [nt, ntype] : CORE_X86_TYPES) {
      if (ntype == type) {
        return nt;
      }
    }
    return make_error_code(lief_errors::not_found);
  }

  if (norm_name == NT_ANDROID_NAME) {
    for (const auto& [nt, ntype] : ANDROID_TYPES) {
      if (ntype == type) {
        return nt;
      }
    }
    return make_error_code(lief_errors::not_found);
  }

  if (norm_name == NT_GNU_NAME) {
    for (const auto& [nt, ntype] : GNU_TYPES) {
      if (ntype == type) {
        return nt;
      }
    }

    return make_error_code(lief_errors::not_found);
  }

  if (norm_name == NT_GO_NAME) {
    for (const auto& [nt, ntype] : GO_TYPES) {
      if (ntype == type) {
        return nt;
      }
    }

    return make_error_code(lief_errors::not_found);
  }

  if (norm_name == NT_STAPSDT_NAME) {
    for (const auto& [nt, ntype] : STAPSDT_TYPES) {
      if (ntype == type) {
        return nt;
      }
    }
    return make_error_code(lief_errors::not_found);
  }

  if (norm_name == NT_CRASHPAD_NAME) {
    for (const auto& [nt, ntype] : CRASHPAD_TYPES) {
      if (ntype == type) {
        return nt;
      }
    }

    return make_error_code(lief_errors::not_found);
  }


  if (norm_name == NT_QNX) {
    for (const auto& [nt, ntype] : QNX_TYPES) {
      if (ntype == type) {
        return nt;
      }
    }

    return make_error_code(lief_errors::not_found);
  }

  for (const auto& [nt, ntype] : GENERIC_TYPES) {
    if (ntype == type) {
      return nt;
    }
  }

  return make_error_code(lief_errors::not_found);
}


result<const char*> Note::type_owner(Note::TYPE type) {
  CONST_MAP_ALT TYPE2OWNER{
      std::pair(TYPE::GNU_ABI_TAG, NT_GNU_NAME),
      std::pair(TYPE::GNU_HWCAP, NT_GNU_NAME),
      std::pair(TYPE::GNU_BUILD_ID, NT_GNU_NAME),
      std::pair(TYPE::GNU_GOLD_VERSION, NT_GNU_NAME),
      std::pair(TYPE::GNU_PROPERTY_TYPE_0, NT_GNU_NAME),
      std::pair(TYPE::CORE_PRSTATUS, NT_CORE_NAME),
      std::pair(TYPE::CORE_FPREGSET, NT_CORE_NAME),
      std::pair(TYPE::CORE_PRPSINFO, NT_CORE_NAME),
      std::pair(TYPE::CORE_TASKSTRUCT, NT_CORE_NAME),
      std::pair(TYPE::CORE_AUXV, NT_CORE_NAME),
      std::pair(TYPE::CORE_PSTATUS, NT_CORE_NAME),
      std::pair(TYPE::CORE_FPREGS, NT_CORE_NAME),
      std::pair(TYPE::CORE_PSINFO, NT_CORE_NAME),
      std::pair(TYPE::CORE_LWPSTATUS, NT_CORE_NAME),
      std::pair(TYPE::CORE_LWPSINFO, NT_CORE_NAME),
      std::pair(TYPE::CORE_WIN32PSTATUS, NT_CORE_NAME),
      std::pair(TYPE::CORE_FILE, NT_CORE_NAME),
      std::pair(TYPE::CORE_PRXFPREG, NT_CORE_NAME),
      std::pair(TYPE::CORE_SIGINFO, NT_CORE_NAME),
      std::pair(TYPE::CORE_ARM_VFP, NT_LINUX_NAME),
      std::pair(TYPE::CORE_ARM_TLS, NT_LINUX_NAME),
      std::pair(TYPE::CORE_ARM_HW_BREAK, NT_LINUX_NAME),
      std::pair(TYPE::CORE_ARM_HW_WATCH, NT_LINUX_NAME),
      std::pair(TYPE::CORE_ARM_SYSTEM_CALL, NT_LINUX_NAME),
      std::pair(TYPE::CORE_ARM_SVE, NT_LINUX_NAME),
      std::pair(TYPE::CORE_ARM_PAC_MASK, NT_LINUX_NAME),
      std::pair(TYPE::CORE_ARM_PACA_KEYS, NT_LINUX_NAME),
      std::pair(TYPE::CORE_ARM_PACG_KEYS, NT_LINUX_NAME),
      std::pair(TYPE::CORE_TAGGED_ADDR_CTRL, NT_LINUX_NAME),
      std::pair(TYPE::CORE_PAC_ENABLED_KEYS, NT_LINUX_NAME),
      std::pair(TYPE::CORE_X86_TLS, NT_LINUX_NAME),
      std::pair(TYPE::CORE_X86_IOPERM, NT_LINUX_NAME),
      std::pair(TYPE::CORE_X86_XSTATE, NT_LINUX_NAME),
      std::pair(TYPE::CORE_X86_CET, NT_LINUX_NAME),
      std::pair(TYPE::ANDROID_MEMTAG, NT_ANDROID_NAME),
      std::pair(TYPE::ANDROID_KUSER, NT_ANDROID_NAME),
      std::pair(TYPE::ANDROID_IDENT, NT_ANDROID_NAME),
      std::pair(TYPE::GO_BUILDID, NT_GO_NAME),
      std::pair(TYPE::STAPSDT, NT_STAPSDT_NAME),
      std::pair(TYPE::CRASHPAD, NT_CRASHPAD_NAME),
      std::pair(TYPE::QNX_STACK, NT_QNX),
  };

  if (auto it = TYPE2OWNER.find(type); it != TYPE2OWNER.end()) {
    return it->second;
  }
  return make_error_code(lief_errors::not_found);
}


result<Note::TYPE> Note::convert_type(Header::FILE_TYPE ftype, uint32_t type,
                                      const std::string& name) {
  std::string norm_name = strip_zero(name);

  if (ftype == Header::FILE_TYPE::CORE) {
    if (norm_name == NT_CORE_NAME) {
      if (auto it = CORE_TYPES.find(type); it != CORE_TYPES.end()) {
        return it->second;
      }
      return make_error_code(lief_errors::not_found);
    }

    if (norm_name == NT_LINUX_NAME) {
      if (auto it = CORE_ARM_YPES.find(type); it != CORE_ARM_YPES.end()) {
        return it->second;
      }
      if (auto it = CORE_X86_TYPES.find(type); it != CORE_X86_TYPES.end()) {
        return it->second;
      }
      return make_error_code(lief_errors::not_found);
    }
    return make_error_code(lief_errors::not_found);
  }

  if (norm_name == NT_ANDROID_NAME) {
    if (auto it = ANDROID_TYPES.find(type); it != ANDROID_TYPES.end()) {
      return it->second;
    }
    return make_error_code(lief_errors::not_found);
  }

  if (norm_name == NT_GNU_NAME) {
    if (auto it = GNU_TYPES.find(type); it != GNU_TYPES.end()) {
      return it->second;
    }
    return make_error_code(lief_errors::not_found);
  }

  if (norm_name == NT_GO_NAME) {
    if (auto it = GO_TYPES.find(type); it != GO_TYPES.end()) {
      return it->second;
    }
    return make_error_code(lief_errors::not_found);
  }

  if (norm_name == NT_STAPSDT_NAME) {
    if (auto it = STAPSDT_TYPES.find(type); it != STAPSDT_TYPES.end()) {
      return it->second;
    }
    return make_error_code(lief_errors::not_found);
  }

  if (norm_name == NT_CRASHPAD_NAME) {
    if (auto it = CRASHPAD_TYPES.find(type); it != CRASHPAD_TYPES.end()) {
      return it->second;
    }
    return make_error_code(lief_errors::not_found);
  }

  if (norm_name == NT_QNX) {
    if (auto it = QNX_TYPES.find(type); it != QNX_TYPES.end()) {
      return it->second;
    }
    return make_error_code(lief_errors::not_found);
  }

  // Generic types
  if (auto it = GENERIC_TYPES.find(type); it != GENERIC_TYPES.end()) {
    return it->second;
  }

  return make_error_code(lief_errors::not_found);
}

std::unique_ptr<Note> Note::create(BinaryStream& stream, std::string section_name,
                                   Header::FILE_TYPE ftype, ARCH arch,
                                   Header::CLASS cls) {
  static constexpr uint32_t MAX_NOTE_DESCRIPTION = 1_MB;
  const size_t pos = stream.pos();
  auto res_namesz = stream.read<uint32_t>();
  if (!res_namesz) {
    return nullptr;
  }

  const auto namesz = *res_namesz;
  LIEF_DEBUG("[{:#08x}] Name size: {:#x}", pos, namesz);

  auto res_descz = stream.read<uint32_t>();
  if (!res_descz) {
    return nullptr;
  }

  uint32_t descsz = std::min(*res_descz, MAX_NOTE_DESCRIPTION);
  LIEF_DEBUG("Description size: {:#x}", descsz);

  auto res_type = stream.read<uint32_t>();
  if (!res_type) {
    return nullptr;
  }

  uint32_t type = *res_type;
  LIEF_DEBUG("Type: {:#x}", type);

  if (namesz == 0) { // System reserves
    return nullptr;
  }

  if (namesz > stream.size() - stream.pos()) {
    LIEF_ERR("Note name size ({:#x}) is larger than the remaining stream", namesz);
    return nullptr;
  }

  std::vector<uint8_t> name_buffer(namesz, 0);
  if (!stream.read_data(name_buffer, namesz)) {
    LIEF_ERR("Failed to read note name");
    return nullptr;
  }

  std::string name(reinterpret_cast<const char*>(name_buffer.data()),
                   name_buffer.size());
  LIEF_DEBUG("Name: {}", name);

  stream.align(sizeof(uint32_t));

  std::vector<uint32_t> description;
  if (descsz > 0) {
    const size_t nb_chunks = (descsz - 1) / sizeof(uint32_t) + 1;
    description.reserve(nb_chunks);
    for (size_t i = 0; i < nb_chunks; ++i) {
      if (const auto chunk = stream.read<uint32_t>()) {
        description.push_back(*chunk);
      } else {
        break;
      }
    }
    stream.align(sizeof(uint32_t));
  }
  std::vector<uint8_t> desc_bytes;
  if (!description.empty()) {
    const auto* start_ptr = reinterpret_cast<const uint8_t*>(description.data());
    desc_bytes = {start_ptr, start_ptr + description.size() * sizeof(uint32_t)};
  }

  return create(name, type, std::move(desc_bytes), std::move(section_name), ftype,
                arch, cls);
}

std::unique_ptr<Note> Note::create(const std::string& name, Note::TYPE ntype,
                                   description_t description,
                                   std::string section_name, ARCH arch,
                                   Header::CLASS cls) {
  std::string owner;
  auto res_owner = type_owner(ntype);
  if (res_owner) {
    if (!name.empty() && strip_zero(name) != *res_owner) {
      LIEF_WARN("Note owner for '{}' should be '{}' but '{}' was provided",
                to_string(ntype), *res_owner, name);
    }
    owner = *res_owner;
  } else {
    if (!name.empty()) {
      owner = name;
    } else {
      LIEF_ERR("Failed to resolve note owner name");
      return nullptr;
    }
  }

  auto int_type = raw_type(owner, ntype);
  if (!int_type) {
    LIEF_ERR("Failed to determine raw type for note '{}' with owner '{}'",
             to_string(ntype), owner);
    return nullptr;
  }
  std::string norm_name = owner;

  // The names of these types have a special meaning
  if (ntype != Note::TYPE::GNU_BUILD_ATTRIBUTE_FUNC &&
      ntype != Note::TYPE::GNU_BUILD_ATTRIBUTE_OPEN)
  {
    norm_name = strip_zero(owner);
  }
  switch (ntype) {
    case Note::TYPE::CORE_PRSTATUS:
    {
      if (cls != Header::CLASS::ELF32 && cls != Header::CLASS::ELF64) {
        LIEF_WARN("CORE_PRSTATUS requires a valid ELF class");
        return nullptr;
      }

      if (arch == ARCH::NONE) {
        LIEF_WARN("CORE_PRSTATUS requires a valid architecture");
        return nullptr;
      }
      return std::make_unique<CorePrStatus>(arch, cls, std::move(norm_name),
                                            *int_type, std::move(description));
    }
    case Note::TYPE::CORE_PRPSINFO:
    {
      if (cls != Header::CLASS::ELF32 && cls != Header::CLASS::ELF64) {
        LIEF_WARN("CORE_PRPSINFO requires a valid ELF class");
        return nullptr;
      }

      if (arch == ARCH::NONE) {
        LIEF_WARN("CORE_PRPSINFO requires a valid architecture");
        return nullptr;
      }
      return std::make_unique<CorePrPsInfo>(arch, cls, std::move(norm_name),
                                            *int_type, std::move(description));
    }
    case Note::TYPE::CORE_FILE:
    {
      if (cls != Header::CLASS::ELF32 && cls != Header::CLASS::ELF64) {
        LIEF_WARN("CORE_FILE requires a valid ELF class");
        return nullptr;
      }

      if (arch == ARCH::NONE) {
        LIEF_WARN("CORE_FILE requires a valid architecture");
        return nullptr;
      }
      return std::make_unique<CoreFile>(arch, cls, std::move(norm_name), *int_type,
                                        std::move(description));
    }
    case Note::TYPE::CORE_AUXV:
    {
      if (cls != Header::CLASS::ELF32 && cls != Header::CLASS::ELF64) {
        LIEF_WARN("CORE_AUXV requires a valid ELF class");
        return nullptr;
      }

      if (arch == ARCH::NONE) {
        LIEF_WARN("CORE_AUXV requires a valid architecture");
        return nullptr;
      }
      return std::make_unique<CoreAuxv>(arch, cls, std::move(norm_name), *int_type,
                                        std::move(description));
    }
    case Note::TYPE::CORE_SIGINFO:
    {
      return std::unique_ptr<CoreSigInfo>(
          new CoreSigInfo(std::move(norm_name), ntype, *int_type,
                          std::move(description), std::move(section_name))
      );
    }
    case Note::TYPE::GNU_PROPERTY_TYPE_0:
    {
      if (cls != Header::CLASS::ELF32 && cls != Header::CLASS::ELF64) {
        LIEF_WARN("GNU_PROPERTY_TYPE_0 requires a valid ELF class");
        return nullptr;
      }

      if (arch == ARCH::NONE) {
        LIEF_WARN("GNU_PROPERTY_TYPE_0 requires a valid architecture");
        return nullptr;
      }
      return std::make_unique<NoteGnuProperty>(arch, cls, std::move(norm_name),
                                               *int_type, std::move(description),
                                               std::move(section_name));
    }
    case Note::TYPE::ANDROID_IDENT:
      return std::unique_ptr<AndroidIdent>(
          new AndroidIdent(std::move(norm_name), ntype, *int_type,
                           std::move(description), std::move(section_name))
      );
    case Note::TYPE::QNX_STACK:
      return std::unique_ptr<QNXStack>(
          new QNXStack(std::move(norm_name), ntype, *int_type,
                       std::move(description), std::move(section_name))
      );
    case Note::TYPE::GNU_ABI_TAG:
      return std::unique_ptr<NoteAbi>(
          new NoteAbi(std::move(norm_name), ntype, *int_type,
                      std::move(description), std::move(section_name))
      );

    default:
      return std::unique_ptr<Note>(new Note(std::move(norm_name), ntype, *int_type,
                                            std::move(description),
                                            std::move(section_name)));
  }
  return nullptr;
}


std::unique_ptr<Note> Note::create(const std::string& name, uint32_t type,
                                   description_t description,
                                   std::string section_name,
                                   Header::FILE_TYPE ftype, ARCH arch,
                                   Header::CLASS cls) {
  auto conv = Note::convert_type(ftype, type, name);
  if (!conv) {
    LIEF_DEBUG("Unsupported note type {:#x} for owner '{}'", type, name);
    return std::unique_ptr<Note>(new Note(name, Note::TYPE::UNKNOWN, type,
                                          std::move(description),
                                          std::move(section_name)));
  }
  return create(name, *conv, std::move(description), std::move(section_name), arch,
                cls);
}


uint64_t Note::size() const {
  uint64_t size = 0;
  size += 3 * sizeof(uint32_t);
  size += name().size() + 1;
  size = align(size, sizeof(uint32_t));
  size += description().size();
  size = align(size, sizeof(uint32_t));
  return size;
}

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

void Note::dump(std::ostream& os) const {
  std::string note_name = printable_string(name());
  os << fmt::format("{}({:#06x}) '{}' [{}]", to_string(type()), original_type(),
                    note_name, to_hex(description(), 10));
}


const char* to_string(Note::TYPE type) {
#define ENTRY(X) std::pair(Note::TYPE::X, #X)
  STRING_MAP enums2str{
      ENTRY(UNKNOWN),
      ENTRY(GNU_ABI_TAG),
      ENTRY(GNU_HWCAP),
      ENTRY(GNU_ABI_TAG),
      ENTRY(GNU_HWCAP),
      ENTRY(GNU_BUILD_ID),
      ENTRY(GNU_GOLD_VERSION),
      ENTRY(GNU_PROPERTY_TYPE_0),
      ENTRY(GNU_BUILD_ATTRIBUTE_OPEN),
      ENTRY(GNU_BUILD_ATTRIBUTE_FUNC),
      ENTRY(CRASHPAD),
      ENTRY(CORE_PRSTATUS),
      ENTRY(CORE_FPREGSET),
      ENTRY(CORE_PRPSINFO),
      ENTRY(CORE_TASKSTRUCT),
      ENTRY(CORE_AUXV),
      ENTRY(CORE_PSTATUS),
      ENTRY(CORE_FPREGS),
      ENTRY(CORE_PSINFO),
      ENTRY(CORE_LWPSTATUS),
      ENTRY(CORE_LWPSINFO),
      ENTRY(CORE_WIN32PSTATUS),
      ENTRY(CORE_FILE),
      ENTRY(CORE_PRXFPREG),
      ENTRY(CORE_SIGINFO),

      ENTRY(CORE_ARM_VFP),
      ENTRY(CORE_ARM_TLS),
      ENTRY(CORE_ARM_HW_BREAK),
      ENTRY(CORE_ARM_HW_WATCH),
      ENTRY(CORE_ARM_SYSTEM_CALL),
      ENTRY(CORE_ARM_SVE),
      ENTRY(CORE_ARM_PAC_MASK),
      ENTRY(CORE_ARM_PACA_KEYS),
      ENTRY(CORE_ARM_PACG_KEYS),
      ENTRY(CORE_TAGGED_ADDR_CTRL),
      ENTRY(CORE_PAC_ENABLED_KEYS),
      ENTRY(CORE_X86_TLS),
      ENTRY(CORE_X86_IOPERM),
      ENTRY(CORE_X86_XSTATE),
      ENTRY(CORE_X86_CET),

      ENTRY(ANDROID_MEMTAG),
      ENTRY(ANDROID_KUSER),
      ENTRY(ANDROID_IDENT),
      ENTRY(STAPSDT),
      ENTRY(GO_BUILDID),
      ENTRY(QNX_STACK),
  };
#undef ENTRY

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

  return "UNKNOWN";
}


template<class T>
result<T> Note::read_at(size_t offset) const {
  auto stream = SpanStream::from_vector(description_);
  if (!stream) {
    return make_error_code(get_error(stream));
  }
  return stream->peek<T>(offset);
}


result<std::string> Note::read_string_at(size_t offset, size_t max_size) const {
  auto stream = SpanStream::from_vector(description_);
  if (!stream) {
    return make_error_code(get_error(stream));
  }
  if (max_size > 0) {
    return stream->peek_string_at(offset, max_size);
  }

  return stream->peek_string_at(offset);
}

template<class T>
ok_error_t Note::write_at(size_t offset, const T& value) {
  Note::description_t original = description_;
  vector_iostream ios;
  ios.write(original);
  ios.seekp(offset);
  ios.write<T>(value);
  ios.move(description_);
  return ok();
}

ok_error_t Note::write_string_at(size_t offset, const std::string& value) {
  Note::description_t original = description_;
  const auto* data = reinterpret_cast<const uint8_t*>(value.data());

  vector_iostream ios;
  ios.write(original);
  ios.seekp(offset);
  ios.write(data, value.size());
  ios.move(description_);
  return ok();
}

IMPL_READ_AT(bool)
IMPL_READ_AT(uint8_t)
IMPL_READ_AT(int8_t)
IMPL_READ_AT(uint16_t)
IMPL_READ_AT(int16_t)
IMPL_READ_AT(uint32_t)
IMPL_READ_AT(int32_t)
IMPL_READ_AT(uint64_t)
IMPL_READ_AT(int64_t)

IMPL_WRITE_AT(bool)
IMPL_WRITE_AT(uint8_t)
IMPL_WRITE_AT(int8_t)
IMPL_WRITE_AT(uint16_t)
IMPL_WRITE_AT(int16_t)
IMPL_WRITE_AT(uint32_t)
IMPL_WRITE_AT(int32_t)
IMPL_WRITE_AT(uint64_t)
IMPL_WRITE_AT(int64_t)

} // namespace LIEF::ELF