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 "spdlog/fmt/fmt.h"

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

#include "ELF/NoteDetails/properties/common.hpp"

#include "LIEF/Visitor.hpp"
#include "LIEF/ELF/NoteDetails/NoteGnuProperty.hpp"
#include "LIEF/ELF/NoteDetails/properties/Generic.hpp"
#include "LIEF/ELF/NoteDetails/properties/AArch64Feature.hpp"
#include "LIEF/ELF/NoteDetails/properties/AArch64PAuth.hpp"
#include "LIEF/ELF/NoteDetails/properties/StackSize.hpp"
#include "LIEF/ELF/NoteDetails/properties/X86Feature.hpp"
#include "LIEF/ELF/NoteDetails/properties/X86ISA.hpp"
#include "LIEF/ELF/NoteDetails/properties/Needed.hpp"
#include "LIEF/ELF/NoteDetails/properties/NoteNoCopyOnProtected.hpp"

#include "LIEF/BinaryStream/SpanStream.hpp"


namespace LIEF::ELF {

inline std::unique_ptr<NoteGnuProperty::Property>
    parse_property(ARCH arch, SpanStream& stream) {

  auto res_type = stream.read<uint32_t>();
  if (!res_type) {
    LIEF_DEBUG("Failed to read type at offset {:#06x}", stream.pos());
    return nullptr;
  }

  auto data_size = stream.read<uint32_t>();
  if (!data_size) {
    LIEF_DEBUG("Failed to read data size at offset {:#06x}", stream.pos());
    return nullptr;
  }

  auto res_content = stream.slice(stream.pos(), *data_size);
  stream.increment_pos(*data_size);
  if (!res_content) {
    return nullptr;
  }

  SpanStream content = std::move(*res_content);
  const uint32_t type = *res_type;
  if (GNU_PROPERTY_LOPROC <= type && type <= GNU_PROPERTY_HIPROC) {
    if (arch == ARCH::X86_64 || arch == ARCH::IAMCU || arch == ARCH::I386) {
      switch (type) {
        case GNU_PROPERTY_X86_ISA_1_USED:
        case GNU_PROPERTY_X86_ISA_1_NEEDED:
        case GNU_PROPERTY_X86_COMPAT_ISA_1_USED:
        case GNU_PROPERTY_X86_COMPAT_ISA_1_NEEDED:
        case GNU_PROPERTY_X86_COMPAT_2_ISA_1_USED:
        case GNU_PROPERTY_X86_COMPAT_2_ISA_1_NEEDED:
          return X86ISA::create(type, content);
        case GNU_PROPERTY_X86_FEATURE_1_AND:
        case GNU_PROPERTY_X86_FEATURE_2_USED:
        case GNU_PROPERTY_X86_FEATURE_2_NEEDED:
          return X86Features::create(type, content);
        default: return Generic::create(type);
      }
    }

    if (arch == ARCH::AARCH64) {
      if (type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) {
        return AArch64Feature::create(content);
      }

      if (type == GNU_PROPERTY_AARCH64_FEATURE_PAUTH) {
        return AArch64PAuth::create(content);
      }
      return Generic::create(type);
    }
  }

  switch (type) {
    case GNU_PROPERTY_STACK_SIZE:
    {
      const size_t csize = content.size();
      if (csize == sizeof(uint64_t)) {
        return StackSize::create(content.read<uint64_t>().value_or(0));
      }
      if (csize == sizeof(uint32_t)) {
        return StackSize::create(content.read<uint32_t>().value_or(0));
      }
      if (csize == sizeof(uint16_t)) {
        return StackSize::create(content.read<uint16_t>().value_or(0));
      }
      if (csize == sizeof(uint8_t)) {
        return StackSize::create(content.read<uint8_t>().value_or(0));
      }
      LIEF_ERR("Invalid stack size: {}", csize);
      return StackSize::create(0);
    }
    case GNU_PROPERTY_NO_COPY_ON_PROTECTED: return NoteNoCopyOnProtected::create();
    default:
    {
      if ((GNU_PROPERTY_UINT32_AND_LO <= type &&
           type <= GNU_PROPERTY_UINT32_AND_HI) ||
          (GNU_PROPERTY_UINT32_OR_LO <= type && type <= GNU_PROPERTY_UINT32_OR_HI))
      {
        // NOLINTNEXTLINE(bugprone-unhandled-code-paths)
        switch (type) {
          case GNU_PROPERTY_1_NEEDED: return Needed::create(content);
          default: return Generic::create(type);
        }
      }
    }
  }
  return Generic::create(type);
}

NoteGnuProperty::properties_t NoteGnuProperty::properties() const {
  auto stream = SpanStream::from_vector(description_);
  if (!stream) {
    LIEF_WARN("Failed to create stream");
    return {};
  }
  const bool is64 = arch_ == ARCH::IA_64 || arch_ == ARCH::AARCH64 ||
                    arch_ == ARCH::LOONGARCH || arch_ == ARCH::X86_64;

  properties_t props;

  while (stream) {
    if (auto prop = parse_property(arch_, *stream)) {
      props.push_back(std::move(prop));
      stream->align(is64 ? 8 : 4);
    } else {
      break;
    }
  }
  return props;
}


std::unique_ptr<NoteGnuProperty::Property>
    NoteGnuProperty::find(Property::TYPE type) const {
  properties_t props = properties();
  auto it = std::find_if(props.begin(), props.end(),
                         [type](const std::unique_ptr<Property>& prop) {
                           return prop->type() == type;
                         });

  if (it != props.end()) {
    return std::move(*it);
  }
  return nullptr;
}

void NoteGnuProperty::dump(std::ostream& os) const {
  Note::dump(os);
  os << '\n';
  const NoteGnuProperty::properties_t& props = properties();
  for (const std::unique_ptr<NoteGnuProperty::Property>& prop : props) {
    os << "  " << *prop << '\n';
  }
}

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

const char* to_string(NoteGnuProperty::Property::TYPE type) {
#define ENTRY(X) std::pair(NoteGnuProperty::Property::TYPE::X, #X)
  STRING_MAP enums2str{
      ENTRY(UNKNOWN),       ENTRY(GENERIC),     ENTRY(AARCH64_FEATURES),
      ENTRY(AARCH64_PAUTH), ENTRY(STACK_SIZE),  ENTRY(NO_COPY_ON_PROTECTED),
      ENTRY(X86_ISA),       ENTRY(X86_FEATURE), ENTRY(NEEDED),
  };
#undef ENTRY

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

  return "UNKNOWN";
}

void NoteGnuProperty::Property::dump(std::ostream& os) const {
  os << to_string(this->type());
}

} // namespace LIEF::ELF