/// \file DwarfImporter.cpp

//
// This file is distributed under the MIT License. See LICENSE.md for details.
//

#include <csignal>
#include <optional>

#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/EquivalenceClasses.h"
#include "llvm/ADT/Triple.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Progress.h"
#include "llvm/Support/raw_os_ostream.h"
#include "llvm/Support/raw_ostream.h"

#include "revng/ADT/STLExtras.h"
#include "revng/Model/Binary.h"
#include "revng/Model/Importer/Binary/BinaryImporterHelper.h"
#include "revng/Model/Importer/Binary/Options.h"
#include "revng/Model/Importer/DebugInfo/DwarfImporter.h"
#include "revng/Model/Pass/AllPasses.h"
#include "revng/Model/Processing.h"
#include "revng/Support/Assert.h"
#include "revng/Support/Debug.h"
#include "revng/Support/PathList.h"
#include "revng/Support/ProgramRunner.h"

#include "ImportDebugInfoHelper.h"

using namespace llvm;
using namespace llvm::dwarf;

static Logger<> DILogger("dwarf-importer");
static const std::string GlobalDebugDirectory = "/usr/lib/debug/";

template<typename M>
class ScopedSetElement {
private:
  M &Set;
  typename M::value_type ToInsert;

public:
  ScopedSetElement(M &Set, typename M::value_type ToInsert) :
    Set(Set), ToInsert(ToInsert) {}
  ~ScopedSetElement() { Set.erase(ToInsert); }

public:
  bool insert() {
    auto It = Set.find(ToInsert);
    if (It != Set.end()) {
      return false;
    } else {
      Set.insert(It, ToInsert);
      return true;
    }
  }
};

static model::PrimitiveKind::Values dwarfEncodingToModel(uint32_t Encoding) {
  switch (Encoding) {
  case dwarf::DW_ATE_unsigned_char:
  case dwarf::DW_ATE_unsigned:
  case dwarf::DW_ATE_boolean:
    return model::PrimitiveKind::Unsigned;
  case dwarf::DW_ATE_signed_char:
  case dwarf::DW_ATE_signed:
    return model::PrimitiveKind::Signed;
  case dwarf::DW_ATE_float:
    return model::PrimitiveKind::Float;
  default:
    return model::PrimitiveKind::Invalid;
  }
}

static std::optional<uint64_t>
getUnsignedOrSigned(const DWARFFormValue &Value) {
  auto MaybeUnsigned = Value.getAsUnsignedConstant();
  auto MaybeSigned = Value.getAsSignedConstant();
  if (MaybeUnsigned)
    return *MaybeUnsigned;
  else if (MaybeSigned)
    return *MaybeSigned;
  else
    return {};
}

static std::optional<uint64_t> getUnsignedOrSigned(const DWARFDie &Die,
                                                   dwarf::Attribute Attribute) {
  auto Value = Die.find(Attribute);
  if (not Value)
    return {};
  else
    return getUnsignedOrSigned(*Value);
}

static std::optional<uint64_t> getAddress(const DWARFFormValue &Value) {
  auto MaybeResult = Value.getAsAddress();
  if (MaybeResult)
    return *MaybeResult;
  else
    return {};
}

static std::optional<uint64_t> getAddress(const DWARFDie &Die) {
  // TODO: Add support for DW_AT_pc, which is DWARF 5 Standard version of the
  // attribute.
  auto Value = Die.find(DW_AT_low_pc);
  if (not Value) {
    auto Ranges = Die.find(DW_AT_ranges);
    if (not Ranges)
      return {};

    auto Offset = *Ranges->getAsSectionOffset();
    auto MaybeRange = Die.getDwarfUnit()->findRnglistFromOffset(Offset);
    if (auto Error = MaybeRange.takeError()) {
      revng_log(DILogger, "findRnglistFromOffset failed: " << Error);
      consumeError(std::move(Error));
      return {};
    }

    // TODO: This is a vector, so we may want to return LowPC from every range
    // we found.
    return MaybeRange->begin()->LowPC;
  } else {
    return getAddress(*Value);
  }
}

static bool isTrue(const DWARFFormValue &Value) {
  return getUnsignedOrSigned(Value) != 0;
}

template<typename S, typename O, typename... A>
void dumpToStream(S &Stream, const O &Object, A... Args) {
  std::string Buffer;
  {
    llvm::raw_string_ostream WrapperStream(Buffer);
    Object.dump(WrapperStream, Args...);
  }
  Stream << Buffer;
}

static void commentDie(const DWARFDie &Die, const Twine &Reason) {
  if (DILogger.isEnabled()) {
    DILogger << Reason.str();
    dumpToStream(DILogger, Die, 0);
    DILogger << DoLog;
  }
}

static void reportIgnoredDie(const DWARFDie &Die, const Twine &Reason) {
  commentDie(Die, "Ignoring DWARF die: " + Reason);
}

class DwarfToModelConverter : public BinaryImporterHelper {
private:
  DwarfImporter &Importer;
  TupleTree<model::Binary> &Model;
  size_t Index;
  size_t AltIndex;
  size_t TypesWithIdentityCount;
  DWARFContext &Context;
  std::map<size_t, const model::TypeDefinition *> Placeholders;
  std::set<const model::TypeDefinition *> InvalidPrimitives;
  std::set<const DWARFDie *> InProgressDies;

public:
  DwarfToModelConverter(DwarfImporter &Importer,
                        DWARFContext &Context,
                        size_t Index,
                        size_t AltIndex,
                        uint64_t PreferredBaseAddress) :
    BinaryImporterHelper(Importer.getModel()->Architecture(),
                         PreferredBaseAddress),
    Importer(Importer),
    Model(Importer.getModel()),
    Index(Index),
    AltIndex(AltIndex),
    Context(Context) {

    Architecture = Model->Architecture();
    BaseAddress = PreferredBaseAddress;

    // Ensure the architecture is consistent.
    auto Arch = model::Architecture::fromLLVMArchitecture(Context.getArch());
    if (Model->Architecture() == model::Architecture::Invalid)
      Model->Architecture() = Arch;

    // Set default ABI
    if (Model->DefaultABI() == model::ABI::Invalid) {
      if (auto ABI = model::ABI::getDefaultForELF(Model->Architecture())) {
        Model->DefaultABI() = ABI.value();
      } else {
        auto AName = model::Architecture::getName(Model->Architecture()).str();
        revng_abort(("Unsupported architecture for ELF: " + AName).c_str());
      }
    }
  }

private:
  model::ABI::Values getABI(CallingConvention CC = DW_CC_normal) const {
    if (CC != DW_CC_normal)
      return model::ABI::Invalid;

    // NOTE: static functions do not always follow the standard calling
    //       convention which is a problem since `CABIFunctionTypes` we generate
    //       for them do not correspond to the real functions, leading to
    //       problems downstream.
    // TODO: investigate.

    return Model->DefaultABI();
  }

  const model::UpcastableType &record(const DWARFDie &Die,
                                      model::UpcastableType &&Type,
                                      bool IsNotPlaceholder) {
    revng_assert(!Type.isEmpty());
    if (not IsNotPlaceholder) {
      // `model::UpcastableType::empty()` (as in, no definition) represents
      // a primitive placeholder.
      Placeholders[Die.getOffset()] = Type->tryGetAsDefinition();
    }

    return Importer.recordType({ Index, Die.getOffset() }, std::move(Type));
  }

  enum class TypeSearchResult {
    Absent,
    PlaceholderType,
    RegularType
  };

  std::pair<TypeSearchResult, model::UpcastableType>
  findType(const DWARFDie &Die) {
    auto Found = Importer.findType({ Index, Die.getOffset() });
    if (Found.isEmpty())
      return { TypeSearchResult::Absent, model::UpcastableType::empty() };
    else if (Placeholders.contains(Die.getOffset()))
      return { TypeSearchResult::PlaceholderType, std::move(Found) };
    else
      return { TypeSearchResult::RegularType, std::move(Found) };
  }

private:
  static bool isType(dwarf::Tag Tag) {
    switch (Tag) {
    case llvm::dwarf::DW_TAG_base_type:
    case llvm::dwarf::DW_TAG_typedef:
    case llvm::dwarf::DW_TAG_restrict_type:
    case llvm::dwarf::DW_TAG_volatile_type:
    case llvm::dwarf::DW_TAG_structure_type:
    case llvm::dwarf::DW_TAG_union_type:
    case llvm::dwarf::DW_TAG_enumeration_type:
    case llvm::dwarf::DW_TAG_array_type:
    case llvm::dwarf::DW_TAG_const_type:
    case llvm::dwarf::DW_TAG_pointer_type:
    case llvm::dwarf::DW_TAG_subroutine_type:
      return true;
    default:
      return false;
    }
  }

  static bool hasModelIdentity(dwarf::Tag Tag) {
    revng_assert(isType(Tag));
    switch (Tag) {
    case llvm::dwarf::DW_TAG_base_type:
    case llvm::dwarf::DW_TAG_typedef:
    case llvm::dwarf::DW_TAG_restrict_type:
    case llvm::dwarf::DW_TAG_volatile_type:
    case llvm::dwarf::DW_TAG_structure_type:
    case llvm::dwarf::DW_TAG_union_type:
    case llvm::dwarf::DW_TAG_enumeration_type:
    case llvm::dwarf::DW_TAG_subroutine_type:
      return true;
    case llvm::dwarf::DW_TAG_array_type:
    case llvm::dwarf::DW_TAG_const_type:
    case llvm::dwarf::DW_TAG_pointer_type:
      return false;
    default:
      revng_abort();
    }
  }

  void createInvalidPrimitivePlaceholder(const DWARFDie &Die) {
    auto [Definition, Type] = Model->makeTypedefDefinition();
    record(Die, std::move(Type), false);
    InvalidPrimitives.insert(&Definition);
  }

  void createType(const DWARFDie &Die) {
    auto Tag = Die.getTag();
    revng_assert(hasModelIdentity(Tag));

    switch (Tag) {
    case llvm::dwarf::DW_TAG_base_type: {
      uint8_t Size = 0;
      model::PrimitiveKind::Values Kind = model::PrimitiveKind::Invalid;

      auto MaybeByteSize = Die.find(DW_AT_byte_size);
      if (MaybeByteSize)
        Size = *MaybeByteSize->getAsUnsignedConstant();

      auto MaybeEncoding = Die.find(DW_AT_encoding);
      if (MaybeEncoding)
        Kind = dwarfEncodingToModel(*MaybeEncoding->getAsUnsignedConstant());

      if (Kind == model::PrimitiveKind::Invalid) {
        reportIgnoredDie(Die, "Unknown primitive type");
        createInvalidPrimitivePlaceholder(Die);
        return;
      }

      if (Size == 0) {
        reportIgnoredDie(Die, "Invalid size for primitive type");
        createInvalidPrimitivePlaceholder(Die);
        return;
      }

      record(Die, model::PrimitiveType::make(Kind, Size), true);
    } break;

    case llvm::dwarf::DW_TAG_subroutine_type:
      record(Die, Model->makeCABIFunctionDefinition().second, false);
      break;

    case llvm::dwarf::DW_TAG_typedef:
    case llvm::dwarf::DW_TAG_restrict_type:
    case llvm::dwarf::DW_TAG_volatile_type:
      record(Die, std::move(Model->makeTypedefDefinition().second), false);
      break;

    case llvm::dwarf::DW_TAG_structure_type:
      record(Die, std::move(Model->makeStructDefinition().second), false);
      break;

    case llvm::dwarf::DW_TAG_union_type:
      if (auto MaybeByteSize = Die.find(DW_AT_byte_size);
          MaybeByteSize and not Die.hasChildren()) {
        // Handle small empty unions, usually due to transparent unions
        auto Size = *MaybeByteSize->getAsUnsignedConstant();
        record(Die, model::PrimitiveType::makeGeneric(Size), true);
      } else {
        // Handle regular unions
        record(Die, std::move(Model->makeUnionDefinition().second), false);
      }
      break;

    case llvm::dwarf::DW_TAG_enumeration_type:
      record(Die, std::move(Model->makeEnumDefinition().second), false);
      break;

    default:
      reportIgnoredDie(Die, "Unexpected type");
    }
  }

  void handleTypeDeclaration(const DWARFDie &Die) {
    auto Tag = Die.getTag();
    if ((Tag == llvm::dwarf::DW_TAG_structure_type
         or Tag == llvm::dwarf::DW_TAG_union_type
         or Tag == llvm::dwarf::DW_TAG_enumeration_type)) {
      record(Die, model::PrimitiveType::makeVoid(), true);
    } else {
      reportIgnoredDie(Die,
                       "Unexpected declaration for tag "
                         + llvm::dwarf::TagString(Tag));
    }
  }

  void materializeTypesWithIdentity() {
    SmallVector<llvm::DWARFUnit *, 16> CompileUnits;
    for (const auto &CU : Context.compile_units())
      CompileUnits.push_back(CU.get());

    Task T(CompileUnits.size(), "Compile units");
    for (llvm::DWARFUnit *CU : CompileUnits) {
      T.advance("", true);

      SmallVector<llvm::DWARFDebugInfoEntry *, 16> Dies;
      for (llvm::DWARFDebugInfoEntry &Entry : CU->dies())
        Dies.push_back(&Entry);

      for (DWARFDebugInfoEntry *Entry : Dies) {
        DWARFDie Die = { CU, Entry };
        auto Tag = Die.getTag();
        if (isType(Tag) and hasModelIdentity(Tag)) {
          auto MaybeDeclaration = Die.find(DW_AT_declaration);
          if (MaybeDeclaration && isTrue(*MaybeDeclaration)) {
            handleTypeDeclaration(Die);
          } else {
            createType(Die);
          }
        }
      }
    }

    TypesWithIdentityCount = Placeholders.size();
  }

  std::string getName(const DWARFDie &Die) const {
    if (auto MaybeName = Die.find(DW_AT_name)) {
      auto MaybeString = MaybeName->getAsCString();
      if (auto Error = MaybeString.takeError()) {
        revng_log(DILogger, "Can't get DIE name: " << Error);
        consumeError(std::move(Error));
        return {};
      } else {
        return *MaybeString;
      }
    } else if (auto MaybeOrigin = Die.find(DW_AT_abstract_origin)) {
      DWARFDie Origin = Context.getDIEForOffset(*MaybeOrigin->getAsReference());
      return getName(Origin);
    } else {
      return {};
    }
  }

  static bool isNoReturn(DWARFUnit &CU, const DWARFDie &Die) {
    auto Tag = Die.getTag();
    revng_assert(Tag == DW_TAG_subprogram);

    if (Die.find(DW_AT_noreturn))
      return true;

    // Check if the specification of this subprogram defines it.
    auto SpecificationAttribute = Die.find(DW_AT_specification);
    if (SpecificationAttribute) {
      if (SpecificationAttribute->getAsReference()) {
        auto DieOffset = *(SpecificationAttribute->getAsReference());
        DWARFDie SpecificationDie = CU.getDIEForOffset(DieOffset);
        if (SpecificationDie.find(DW_AT_noreturn))
          return true;
      }
    }

    return false;
  }

  RecursiveCoroutine<model::UpcastableType> makeType(const DWARFDie &Die) {
    if (auto Type = Die.find(DW_AT_type)) {
      if (Type->getForm() == llvm::dwarf::DW_FORM_GNU_ref_alt) {
        rc_return Importer.findType({ AltIndex, Type->getRawUValue() }).copy();
      } else {
        DWARFDie InnerDie = Context.getDIEForOffset(*Type->getAsReference());
        rc_return rc_recur resolveType(InnerDie, false);
      }
    } else if (auto MaybeOrigin = Die.find(DW_AT_abstract_origin)) {
      DWARFDie Origin = Context.getDIEForOffset(*MaybeOrigin->getAsReference());
      rc_return rc_recur makeType(Origin);
    } else {
      rc_return model::UpcastableType::empty();
    }
  }

  RecursiveCoroutine<model::UpcastableType>
  makeTypeOrVoid(const DWARFDie &Die) {
    if (model::UpcastableType Result = rc_recur makeType(Die))
      rc_return Result;
    else
      rc_return model::PrimitiveType::makeVoid();
  }

  RecursiveCoroutine<void>
  resolveTypeWithIdentity(const DWARFDie &Die, model::UpcastableType &Type) {
    using namespace model;

    auto Offset = Die.getOffset();
    auto Tag = Die.getTag();

    revng_assert(Placeholders.contains(Offset));

    std::string Name = getName(Die);

    model::TypeDefinition &Definition = *Type->tryGetAsDefinition();
    if (InvalidPrimitives.contains(&Definition))
      rc_return;

    switch (Tag) {
    case llvm::dwarf::DW_TAG_subroutine_type: {
      auto &FunctionType = cast<model::CABIFunctionDefinition>(Definition);
      FunctionType.OriginalName() = Name;
      FunctionType.ABI() = getABI();

      if (FunctionType.ABI() == model::ABI::Invalid) {
        reportIgnoredDie(Die, "Unknown calling convention");
        rc_return;
      }

      FunctionType.ReturnType() = rc_recur makeType(Die);

      uint64_t Index = 0;
      for (const DWARFDie &ChildDie : Die.children()) {
        if (ChildDie.getTag() == DW_TAG_formal_parameter) {

          model::UpcastableType ArgumentType = rc_recur makeType(ChildDie);
          if (ArgumentType.isEmpty()) {
            reportIgnoredDie(Die,
                             "The type of argument " + Twine(++Index)
                               + " cannot be resolved");
            rc_return;
          }

          // Note: at this stage we don't check the size. If an argument is
          // unsized, the function will be purged later on.
          FunctionType.addArgument(std::move(ArgumentType));
        }
      }

    } break;

    case llvm::dwarf::DW_TAG_typedef:
    case llvm::dwarf::DW_TAG_restrict_type:
    case llvm::dwarf::DW_TAG_volatile_type: {
      auto &Typedef = cast<model::TypedefDefinition>(Definition);
      Typedef.OriginalName() = Name;
      Typedef.UnderlyingType() = rc_recur makeTypeOrVoid(Die);

    } break;

    case llvm::dwarf::DW_TAG_structure_type: {
      auto MaybeSize = Die.find(DW_AT_byte_size);

      if (not MaybeSize) {
        reportIgnoredDie(Die, "Struct has no size");
        rc_return;
      }

      auto &Struct = cast<model::StructDefinition>(Definition);
      Struct.OriginalName() = Name;
      Struct.Size() = *MaybeSize->getAsUnsignedConstant();

      uint64_t Index = 0;
      for (const DWARFDie &ChildDie : Die.children()) {
        if (ChildDie.getTag() == DW_TAG_member) {

          // Collect offset
          auto MaybeOffset = ChildDie.find(DW_AT_data_member_location);

          if (not MaybeOffset) {
            reportIgnoredDie(ChildDie, "Struct field has no offset");
            continue;
          }

          auto Offset = *MaybeOffset->getAsUnsignedConstant();

          if (ChildDie.find(DW_AT_bit_size)) {
            reportIgnoredDie(ChildDie, "Ignoring bitfield in struct");
            continue;
          }

          model::UpcastableType MemberType = rc_recur makeType(ChildDie);
          if (MemberType.isEmpty()) {
            reportIgnoredDie(Die,
                             "The type of member " + Twine(Index + 1)
                               + " cannot be resolved");
            rc_return;
          }

          // Create new field
          auto &Field = Struct.Fields()[Offset];
          Field.OriginalName() = getName(ChildDie);
          Field.Type() = std::move(MemberType);

          ++Index;
        }
      }

      if (Index == 0) {
        reportIgnoredDie(Die, "Struct has no fields");
        rc_return;
      }

    } break;

    case llvm::dwarf::DW_TAG_union_type: {
      auto &Union = cast<model::UnionDefinition>(Definition);
      Union.OriginalName() = Name;

      for (const DWARFDie &ChildDie : Die.children()) {
        if (ChildDie.getTag() == DW_TAG_member) {
          model::UpcastableType MemberType = rc_recur makeType(ChildDie);
          if (MemberType.isEmpty()) {
            reportIgnoredDie(Die,
                             "The type of one of the fields cannot be "
                             "resolved");
            rc_return;
          }

          // Create new field
          auto &Field = Union.addField(std::move(MemberType));
          Field.OriginalName() = getName(ChildDie);
        }
      }

      if (Union.Fields().empty()) {
        reportIgnoredDie(Die, "Union has no fields");
        rc_return;
      }

    } break;

    case llvm::dwarf::DW_TAG_enumeration_type: {
      auto &Enum = llvm::cast<model::EnumDefinition>(Definition);
      Enum.OriginalName() = Name;

      const model::UpcastableType UnderlyingType = rc_recur makeType(Die);
      if (UnderlyingType.isEmpty()) {
        reportIgnoredDie(Die, "The enum underlying type cannot be resolved");
        rc_return;
      }

      revng_assert(UnderlyingType->isPrimitive());
      Enum.UnderlyingType() = std::move(UnderlyingType);

      uint64_t Index = 0;
      for (const DWARFDie &ChildDie : Die.children()) {
        if (ChildDie.getTag() == DW_TAG_enumerator) {
          // Collect value
          auto MaybeValue = getUnsignedOrSigned(ChildDie, DW_AT_const_value);
          if (not MaybeValue) {
            reportIgnoredDie(ChildDie,
                             "Ignoring enum entry " + Twine(Index + 1)
                               + " without a value");
            rc_return;
          }

          uint64_t Value = *MaybeValue;

          // Create new entry
          std::string EntryName = getName(ChildDie);

          // If it's the first time, set OriginalName
          auto *It = Enum.Entries().tryGet(Value);
          if (It == nullptr) {
            auto &Entry = Enum.Entries()[Value];
            Entry.OriginalName() = EntryName;
          } else {
            // Ignore aliases
          }

          ++Index;
        }
      }

    } break;

    default:
      reportIgnoredDie(Die, "Unknown type");
      rc_return;
    }

    Placeholders.erase(Offset);

    rc_return;
  }

  RecursiveCoroutine<model::UpcastableType>
  resolveType(const DWARFDie &Die, bool ResolveIfHasIdentity) {
    // Ensure there are no loops in the dies we're exploring
    using ScopedSetElement = ScopedSetElement<decltype(InProgressDies)>;
    ScopedSetElement InProgressDie(InProgressDies, &Die);
    if (not InProgressDie.insert()) {
      reportIgnoredDie(Die, "Recursive die");
      rc_return model::UpcastableType::empty();
    }

    auto Tag = Die.getTag();
    auto [SearchResult, Type] = findType(Die);

    switch (SearchResult) {
    case TypeSearchResult::Absent: {
      // At this stage, all the type definitions (as in, types with an identity
      // in the model) should have been materialized.
      // Therefore, here we only deal with DWARF types the model represents as
      // by nesting.

      /// \note There could be some TAGs we do not handle/recognize as types.
      if (isType(Tag))
        revng_assert(not hasModelIdentity(Tag));

      bool HasType = Die.find(DW_AT_type).has_value();
      model::UpcastableType Result = rc_recur makeTypeOrVoid(Die);

      switch (Tag) {

      case llvm::dwarf::DW_TAG_const_type: {
        revng_assert(Result->IsConst() == false);
        Result->IsConst() = true;
      } break;

      case llvm::dwarf::DW_TAG_array_type: {

        if (not HasType) {
          reportIgnoredDie(Die, "Array does not specify element type");
          rc_return model::UpcastableType::empty();
        }

        for (const DWARFDie &ChildDie : Die.children()) {
          if (ChildDie.getTag() == llvm::dwarf::DW_TAG_subrange_type) {
            auto MaybeUpperBound = getUnsignedOrSigned(ChildDie,
                                                       DW_AT_upper_bound);
            auto MaybeCount = getUnsignedOrSigned(ChildDie, DW_AT_count);

            if (MaybeUpperBound and MaybeCount
                and *MaybeUpperBound != *MaybeCount + 1) {
              reportIgnoredDie(Die, "DW_AT_upper_bound != DW_AT_count + 1");
              rc_return model::UpcastableType::empty();
            }

            if (MaybeUpperBound) {
              Result = model::ArrayType::make(std::move(Result),
                                              *MaybeUpperBound + 1);
            } else if (MaybeCount) {
              Result = model::ArrayType::make(std::move(Result), *MaybeCount);
            } else {
              reportIgnoredDie(Die,
                               "Array upper bound/elements count missing or "
                               "invalid");
              rc_return model::UpcastableType::empty();
            }
          }
        }
      } break;

      case llvm::dwarf::DW_TAG_pointer_type: {
        auto MaybeByteSize = Die.find(DW_AT_byte_size);
        if (not MaybeByteSize) {
          // TODO: force architecture pointer size
          reportIgnoredDie(Die, "Pointer has no size");
          rc_return model::UpcastableType::empty();
        }

        uint64_t PointerSize = *MaybeByteSize->getAsUnsignedConstant();
        Result = model::PointerType::make(std::move(Result), PointerSize);
      } break;

      default:
        reportIgnoredDie(Die, "Unknown type");
        rc_return model::UpcastableType::empty();
      }

      rc_return record(Die, std::move(Result), true).copy();
    }
    case TypeSearchResult::PlaceholderType: {
      if (Type.isEmpty()) {
        reportIgnoredDie(Die, "Couldn't materialize type");
        rc_return model::UpcastableType::empty();
      }

      revng_assert(Placeholders.contains(Die.getOffset()));
      // This die is already present in the map. Either it has already been
      // fully imported, or it's a type with an identity on the model.
      // In the latter case, proceed only if explicitly told to do so.
      if (ResolveIfHasIdentity)
        rc_recur resolveTypeWithIdentity(Die, Type);

      rc_return std::move(Type);
    }

    case TypeSearchResult::RegularType:
      if (Type.isEmpty()) {
        reportIgnoredDie(Die, "Couldn't materialize type");
        rc_return model::UpcastableType::empty();
      }

      rc_return std::move(Type);

    default:
      revng_abort();
    }
  }

  void resolveAllTypes() {
    for (const auto &CU : Context.compile_units()) {
      for (const auto &Entry : CU->dies()) {
        DWARFDie Die = { CU.get(), &Entry };
        if (not isType(Die.getTag()))
          continue;
        resolveType(Die, true);
      }
    }
  }

  model::UpcastableType getSubprogramPrototype(const DWARFDie &Die) {
    // Create function type
    auto NewType = model::makeTypeDefinition<model::CABIFunctionDefinition>();
    auto &FunctionType = cast<model::CABIFunctionDefinition>(*NewType.get());

    // Detect ABI
    CallingConvention CC = DW_CC_normal;
    auto MaybeCC = getUnsignedOrSigned(Die, DW_AT_calling_convention);
    if (MaybeCC)
      CC = static_cast<CallingConvention>(*MaybeCC);
    FunctionType.ABI() = getABI(CC);

    if (FunctionType.ABI() == model::ABI::Invalid) {
      reportIgnoredDie(Die, "Unknown calling convention");
      return model::UpcastableType::empty();
    }

    // Arguments
    uint64_t Index = 0;
    for (const DWARFDie &ChildDie : Die.children()) {
      if (ChildDie.getTag() == DW_TAG_formal_parameter) {
        model::UpcastableType ArgumentType = makeType(ChildDie);
        if (ArgumentType.isEmpty()) {
          reportIgnoredDie(Die,
                           "The type of argument " + Twine(++Index)
                             + " cannot be resolved");
          return model::UpcastableType::empty();
        }

        // Note: at this stage we don't check the size. If an argument is
        // unsized, the function will be purged later on.
        model::Argument &A = FunctionType.addArgument(std::move(ArgumentType));
        A.OriginalName() = getName(ChildDie);
      }
    }

    // Return type
    FunctionType.ReturnType() = makeType(Die);
    return Model->recordNewType(std::move(NewType)).second;
  }

  void createFunctions() {
    revng_log(DILogger, "Creating functions");
    for (const auto &CU : Context.compile_units()) {
      for (const auto &Entry : CU->dies()) {
        DWARFDie Die = { CU.get(), &Entry };

        if (Die.getTag() != DW_TAG_subprogram)
          continue;

        auto &Functions = Model->ImportedDynamicFunctions();
        model::UpcastableType Prototype = getSubprogramPrototype(Die);
        std::string SymbolName = getName(Die);

        MetaAddress LowPC;
        if (auto MaybeLowPC = getAddress(Die)) {
          // TODO: do a proper check to see if it's in a valid segment
          if (*MaybeLowPC != 0)
            LowPC = relocate(fromPC(*MaybeLowPC));
        }

        if (LowPC.isValid()) {
          revng_log(DILogger,
                    "Found a subprogram with LowPC "
                      << LowPC.toString() << " and name \"" << SymbolName
                      << "\"");

          // Get/create the local function
          auto &Function = Model->Functions()[LowPC];

          if (Prototype.isEmpty())
            revng_log(DILogger, "Can't get the prototype");
          else if (not Function.prototype())
            Function.Prototype() = std::move(Prototype);

          if (SymbolName.size() != 0) {
            Function.ExportedNames().insert(SymbolName);
            if (Function.OriginalName().size() == 0)
              Function.OriginalName() = SymbolName;
          }

          if (isNoReturn(*CU.get(), Die))
            Function.Attributes().insert(model::FunctionAttribute::NoReturn);
        } else if (not SymbolName.empty() and Functions.contains(SymbolName)) {
          // It's a dynamic function
          if (Prototype.isEmpty()) {
            reportIgnoredDie(Die, "Couldn't build subprogram prototype");
            continue;
          }

          // Get/create dynamic function
          auto &DynamicFunction = Model->ImportedDynamicFunctions()[SymbolName];

          // If a function already has a valid prototype, don't override it
          if (DynamicFunction.prototype() != nullptr)
            continue;

          const model::TypeDefinition &Def = *Prototype->tryGetAsDefinition();
          revng_assert(isa<model::CABIFunctionDefinition>(Def));
          DynamicFunction.Prototype() = std::move(Prototype);

          if (isNoReturn(*CU.get(), Die)) {
            using namespace model;
            DynamicFunction.Attributes().insert(FunctionAttribute::NoReturn);
          }
        } else {
          reportIgnoredDie(Die, "Ignoring subprogram");
        }
      }
    }
  }

  void purgeUnresolvedPlaceholders() {
    std::set<const model::TypeDefinition *> ToDrop;
    for (const auto [_, Type] : Placeholders)
      ToDrop.insert(Type);

    unsigned DroppedTypes = dropTypesDependingOnDefinitions(Model, ToDrop);
    revng_log(DILogger,
              "Purging " << DroppedTypes << " types (out of "
                         << TypesWithIdentityCount << ") due to "
                         << Placeholders.size() << " unresolved types");

    Placeholders.clear();
  }

public:
  void run() {
    Task T(9, "Importing DWARF");
    T.advance("Materialize types with an identity", true);
    materializeTypesWithIdentity();
    T.advance("Resolve types", true);
    resolveAllTypes();
    T.advance("Create model functions", true);
    createFunctions();
    T.advance("Remove types that depend on unresolved placeholders", true);
    purgeUnresolvedPlaceholders();
    T.advance("Remove types that couldn't be imported fully", true);
    purgeInvalidTypes(Model);
    T.advance("Deduplicate equivalent types", true);
    deduplicateEquivalentTypes(Model);
    T.advance("Promote OriginalName", true);
    promoteOriginalName(Model);
    T.advance("Purge unnamed unreachable types", true);
    purgeUnnamedAndUnreachableTypes(Model);
    T.advance("Verify the model", true);
    Model->verify(true);
  }
};

template<typename T>
ArrayRef<uint8_t> getSectionsContents(StringRef Name, T &ELF) {
  auto MaybeSections = ELF.sections();
  if (auto Error = MaybeSections.takeError()) {
    revng_log(DILogger, "Failed to get sections: " << Error);
    consumeError(std::move(Error));
    return {};
  }

  for (const auto &Section : *MaybeSections) {
    auto MaybeName = expectedToOptional(ELF.getSectionName(Section));
    if (MaybeName and *MaybeName == Name) {
      auto MaybeContents = expectedToOptional(ELF.getSectionContents(Section));
      if (MaybeContents)
        return *MaybeContents;
    }
  }

  return {};
}

static std::string getBuildID(const object::Binary *B) {
  using namespace llvm::object;

  auto Handler = [&](auto *ELFObject) -> std::string {
    const auto &ELF = ELFObject->getELFFile();
    ArrayRef<uint8_t> Contents = getSectionsContents(".note.gnu.build-id", ELF);
    if (Contents.size() == 0)
      return {};

    std::string StringForBytes;
    raw_string_ostream OutputStream(StringForBytes);
    for (uint8_t Byte : Contents)
      OutputStream << format_hex_no_prefix(Byte, 2);

    // Build ID uses SHA1, so it is 20 bytes long.
    constexpr unsigned SHA1Size = 40;
    return OutputStream.str().substr(OutputStream.str().size() - SHA1Size);
  };

  std::string BuildID;
  if (auto *ELF = dyn_cast<ELF32BEObjectFile>(B)) {
    BuildID = Handler(ELF);
  } else if (auto *ELF = dyn_cast<ELF64BEObjectFile>(B)) {
    BuildID = Handler(ELF);
  } else if (auto *ELF = dyn_cast<ELF32LEObjectFile>(B)) {
    BuildID = Handler(ELF);
  } else if (auto *ELF = dyn_cast<ELF64LEObjectFile>(B)) {
    BuildID = Handler(ELF);
  } else {
    revng_abort();
  }

  return BuildID;
}

static StringRef getDebugFileName(const object::Binary *B) {
  using namespace llvm::object;

  // TODO: Handle Split DWARF/DW_AT_GNU_dwo_name. Part of DWARF 5.

  auto Handler = [&](auto *ELFObject) -> StringRef {
    const auto &ELF = ELFObject->getELFFile();
    ArrayRef<uint8_t> Contents = getSectionsContents(".gnu_debuglink", ELF);
    if (Contents.size() == 0) {
      // If there is no ".gnu_debuglink", try ".gnu_debugaltlink".
      Contents = getSectionsContents(".gnu_debugaltlink", ELF);
    }

    if (Contents.size() == 0) {
      // TODO: Handle .debug_sup, which is DWARF 5 implementation of GNU
      // extension .gnu_debuglink sections.
      return {};
    }

    // TODO: improve accuracy
    // Extract path name and ignore everything after \0
    return StringRef(reinterpret_cast<const char *>(Contents.data()));
  };

  StringRef AltDebugLinkPath;
  if (auto *ELF = dyn_cast<ELF32BEObjectFile>(B)) {
    AltDebugLinkPath = Handler(ELF);
  } else if (auto *ELF = dyn_cast<ELF64BEObjectFile>(B)) {
    AltDebugLinkPath = Handler(ELF);
  } else if (auto *ELF = dyn_cast<ELF32LEObjectFile>(B)) {
    AltDebugLinkPath = Handler(ELF);
  } else if (auto *ELF = dyn_cast<ELF64LEObjectFile>(B)) {
    AltDebugLinkPath = Handler(ELF);
  } else {
    revng_abort();
  }

  return llvm::sys::path::filename(AltDebugLinkPath);
}

static bool fileExists(const Twine &Path) {
  bool Result = sys::fs::exists(Path);

  if (Result) {
    revng_log(DILogger, "Found: " << Path.str());
  } else {
    revng_log(DILogger, "The following path does not exist: " << Path.str());
  }

  return Result;
}

static std::optional<std::string>
findDebugInfoFileByName(StringRef FileName,
                        StringRef DebugFileName,
                        llvm::object::ObjectFile *ELF) {
  // Let's find it in canonical places, where debug info was fetched.
  //  1) Look for a .gnu_debuglink/.gnu_debugaltlink/.debug_sup section.
  //  The .debug file should be in canonical places.
  //  E.g., if the executable is `/usr/bin/ls`, we look for:
  //     - /usr/bin/ls.debug (current dir of exe)
  //     - /usr/bin/.debug/ls.debug
  //     - /usr/lib/debug/usr/bin/ls.debug
  llvm::SmallString<128> ResultPath;
  if (llvm::sys::path::has_parent_path(FileName)) {
    llvm::sys::path::append(ResultPath,
                            llvm::sys::path::parent_path(FileName),
                            DebugFileName);
  } else {
    llvm::sys::path::append(ResultPath, DebugFileName);
  }

  if (fileExists(ResultPath.str())) {
    return std::string(ResultPath.str());
  } else {
    // Try in .debug/ directory.
    ResultPath.clear();
    llvm::sys::path::append(ResultPath,
                            llvm::sys::path::parent_path(FileName),
                            ".debug/",
                            DebugFileName);

    if (fileExists(ResultPath.str())) {
      return std::string(ResultPath.str());
    } else {
      // Try `/usr/lib/debug/usr/bin/ls.debug`-like path.
      ResultPath.clear();
      if (sys::path::is_absolute(FileName)) {
        llvm::sys::path::append(ResultPath,
                                GlobalDebugDirectory,
                                llvm::sys::path::parent_path(FileName),
                                DebugFileName);
      } else {
        // Relative path.
        llvm::SmallString<64> CurrentDirectory;
        auto ErrorCode = llvm::sys::fs::current_path(CurrentDirectory);
        if (!ErrorCode) {
          llvm::sys::path::append(ResultPath,
                                  GlobalDebugDirectory,
                                  CurrentDirectory,
                                  llvm::sys::path::parent_path(FileName),
                                  DebugFileName);
        } else {
          revng_log(DILogger, "Can't get current working path.");
        }
      }

      if (fileExists(ResultPath.str())) {
        return std::string(ResultPath.str());
      } else {
        // Try If build-id is `abcdef1234`, we look for:
        // - /usr/lib/debug/.build-id/ab/cdef1234.debug
        ResultPath.clear();
        auto BuildID = getBuildID(ELF);
        if (BuildID.size()) {
          // First two chars of build-id forms the debug info file directory.
          auto DebugDir = BuildID.substr(0, 2);
          // The rest of build-id forms the debug info file name.
          auto DebugFile = BuildID.substr(BuildID.size() - 38);
          auto DebugFileWithExtension = DebugFile.append(".debug");
          llvm::sys::path::append(ResultPath,
                                  GlobalDebugDirectory,
                                  ".build-id/",
                                  DebugDir,
                                  DebugFileWithExtension);

          if (fileExists(ResultPath.str())) {
            return std::string(ResultPath.str());
          }

          // Try in CacheDirectory at the end.
          ResultPath.clear();
          std::string CacheDir = getCacheDirectory();
          ResultPath = joinPath(CacheDir,
                                "debug-symbols",
                                "elf",
                                BuildID,
                                "debug");

          if (fileExists(ResultPath.str())) {
            return ResultPath.str().str();
          }
          revng_log(DILogger, "Can't find " << DebugFileName);
        } else {
          revng_log(DILogger, "Can't parse build-id.");
        }
      }
    }
  }

  // We have not found the debug info file on the device.
  return std::nullopt;
}

void DwarfImporter::import(StringRef FileName, const ImporterOptions &Options) {
  Task T(3,
         "Importing DWARF information for "
           + llvm::sys::path::filename(FileName));

  T.advance("Fetching debug info", true);

  using namespace llvm::object;
  ErrorOr<std::unique_ptr<MemoryBuffer>>
    MaybeBuffer = MemoryBuffer::getFileOrSTDIN(FileName);

  if (not MaybeBuffer) {
    revng_log(DILogger,
              "Couldn't open file " << FileName << " "
                                    << MaybeBuffer.getError().message());
    return;
  }

  std::unique_ptr<MemoryBuffer> Buffer = std::move(MaybeBuffer.get());
  Expected<std::unique_ptr<Binary>> MaybeBinary = object::createBinary(*Buffer);

  if (auto Error = MaybeBinary.takeError()) {
    revng_log(DILogger, "Couldn't load binary: " << Error);
    consumeError(std::move(Error));
    return;
  }

  // Find Debugging Information.
  // If the file has debug info sections within itself, no need for finding
  // it on the device.
  // TODO: When we add support for Split DWARF, this will need additional
  // improvement.
  auto HasDebugInfo = [](ObjectFile *Object) {
    for (const SectionRef &Section : Object->sections()) {
      StringRef SectionName;
      if (Expected<StringRef> NameOrErr = Section.getName()) {
        SectionName = *NameOrErr;
      } else {
        llvm::consumeError(NameOrErr.takeError());
        continue;
      }

      // TODO: When adding support for Split dwarf, there will be
      // .debug_info.dwo section, so we need to handle it.
      if (SectionName == ".debug_info")
        return true;
    }
    return false;
  };

  auto PerformImport = [this, &T, &Options](StringRef FilePath,
                                            StringRef TheDebugFile) {
    auto ExpectedBinary = object::createBinary(FilePath);
    if (!ExpectedBinary) {
      revng_log(DILogger, "Can't create binary for " << FilePath);
      llvm::consumeError(ExpectedBinary.takeError());
    } else {
      revng_log(DILogger, "Importing " << TheDebugFile.str());
      T.advance("Parsing detached debug info file "
                  + llvm::sys::path::filename(TheDebugFile),
                true);
      import(*ExpectedBinary->getBinary(), TheDebugFile, Options.BaseAddress);
    }
  };

  if (auto *ELF = dyn_cast<ObjectFile>(MaybeBinary->get())) {
    if (Options.DebugInfo != DebugInfoLevel::No && !HasDebugInfo(ELF)) {
      // There are no .debug_* sections in the file itself, let's try to find it
      // on the device, otherwise find it on web by using the `fetch-debuginfo`
      // tool.
      auto DebugFile = getDebugFileName(MaybeBinary->get());
      if (!DebugFile.size()) {
        revng_log(DILogger, "Can't find file name of the debug file.");
        return;
      }
      auto DebugFilePath = findDebugInfoFileByName(FileName, DebugFile, ELF);
      if (!DebugFilePath) {
        if (!::Runner.isProgramAvailable("revng")) {
          revng_log(DILogger,
                    "Can't find `revng` binary to run `fetch-debuginfo`.");
          return;
        }

        int ExitCode = runFetchDebugInfo(FileName);
        if (ExitCode != 0) {
          revng_log(DILogger,
                    "Failed to find debug info with `revng model "
                    "fetch-debuginfo`.");
        } else {
          DebugFilePath = findDebugInfoFileByName(FileName, DebugFile, ELF);
          if (DebugFilePath)
            PerformImport(*DebugFilePath, DebugFile);
        }
      } else {
        PerformImport(*DebugFilePath, DebugFile);
      }
    }
  }

  T.advance("Parsing debug info in the binary itself", true);
  import(*MaybeBinary->get(), FileName, Options.BaseAddress);
}

auto zipPairs(auto &&R) {
  auto BeginIt = R.begin();
  auto EndIt = R.end();

  if (BeginIt == EndIt)
    return zip(make_range(EndIt, EndIt), make_range(EndIt, EndIt));

  auto First = BeginIt;
  auto Second = ++BeginIt;

  if (Second == EndIt)
    return zip(make_range(EndIt, EndIt), make_range(EndIt, EndIt));

  auto End = EndIt;
  auto Last = --EndIt;

  return zip(make_range(First, Last), make_range(Second, End));
}

/// This function considers all symbols with name of type STT_FUNC and clusters
/// them by address/type
static EquivalenceClasses<StringRef>
computeEquivalentSymbols(const llvm::object::ObjectFile &ELF) {
  using namespace llvm::object;

  EquivalenceClasses<StringRef> Result;

  struct SymbolDescriptor {
    uint64_t Address = 0;
    // TODO: one day we will want to consider STT_OBJECT too
    SymbolRef::Type Type = SymbolRef::ST_Unknown;
    /// \note we ignore this field for comparison purposes
    StringRef Name;

    auto key() const { return std::tie(Address, Type); }

    bool operator<(const SymbolDescriptor &Other) const {
      return key() < Other.key();
    }

    bool operator==(const SymbolDescriptor &Other) const {
      return key() == Other.key();
    }
  };

  std::vector<SymbolDescriptor> Symbols;
  for (const object::SymbolRef &Symbol : ELF.symbols()) {
    SymbolDescriptor NewSymbol;

    auto MaybeType = Symbol.getType();
    auto MaybeAddress = Symbol.getAddress();
    auto MaybeName = Symbol.getName();
    auto MaybeFlags = Symbol.getFlags();

    if (auto Error = MaybeType.takeError()) {
      consumeError(std::move(Error));
      continue;
    } else if (auto Error = MaybeAddress.takeError()) {
      consumeError(std::move(Error));
      continue;
    } else if (auto Error = MaybeName.takeError()) {
      consumeError(std::move(Error));
      continue;
    } else if (auto Error = MaybeFlags.takeError()) {
      consumeError(std::move(Error));
      continue;
    }

    // Ignore unnamed and nullptr symbols
    if (MaybeName->size() == 0 or *MaybeAddress == 0)
      continue;

    // Consider only STT_FUNC symbols
    if (*MaybeType != SymbolRef::ST_Function)
      continue;

    // Consider only global symbols
    if (!((*MaybeFlags) & SymbolRef::SF_Global))
      continue;

    Symbols.push_back({ *MaybeAddress, *MaybeType, *MaybeName });
  }

  llvm::sort(Symbols);

  for (const auto &[Previous, Current] : zipPairs(Symbols))
    if (Previous == Current)
      Result.unionSets(Previous.Name, Current.Name);

  return Result;
}

// TODO: it would be beneficial to do this even at other levels
inline void detectAliases(const llvm::object::ObjectFile &ELF,
                          TupleTree<model::Binary> &Model) {
  EquivalenceClasses<StringRef> Aliases = computeEquivalentSymbols(ELF);
  auto &ImportedDynamicFunctions = Model->ImportedDynamicFunctions();
  auto &Functions = Model->Functions();

  std::unordered_map<std::string, model::Function *> FunctionsByName;
  // Map functions by names, so we have faster lookup below.
  for (auto &Function : Functions) {
    if (Function.OriginalName().size()) {
      FunctionsByName[Function.OriginalName()] = &Function;
    }
  }

  for (auto AliasesIt = Aliases.begin(), E = Aliases.end(); AliasesIt != E;
       ++AliasesIt) {
    llvm::SmallVector<std::string, 4> CurrentAliases;
    if (AliasesIt->isLeader()) {
      SmallVector<std::string, 4> UnprototypedFunctionsNames;
      model::UpcastableType Prototype;
      for (auto AliasSetIt = Aliases.member_begin(AliasesIt);
           AliasSetIt != Aliases.member_end();
           ++AliasSetIt) {

        std::string Name = AliasSetIt->str();
        if (Name.size() == 0)
          continue;

        CurrentAliases.push_back(Name);

        // Create DynamicFunction, if it doesn't exist already
        auto It = ImportedDynamicFunctions.tryGet(Name);
        bool Found = It != nullptr;

        // If DynamicFunction doesn't have a prototype, register it for copying
        // it from the leader.
        // Otherwise, record the type as the leader.
        if (Found and not It->Prototype().isEmpty()) {
          Prototype = It->Prototype().copy();
        } else {
          UnprototypedFunctionsNames.push_back(Name);
        }
      }

      // Check if we should add an ExportedName for local Functions.
      llvm::SmallVector<std::string, 4> PotentialExportedNamesToBeAdded;
      bool IsLocalFunction = false;
      model::Function *TheFunction = nullptr;
      for (auto &Name : CurrentAliases) {
        auto It = FunctionsByName.find(Name);
        PotentialExportedNamesToBeAdded.push_back(Name);
        if (It != FunctionsByName.end()) {
          // We found a local function.
          // TODO: In some situations OriginalName is not in the ExportedNames?
          // For example in the case of importing `__libc_calloc` from
          // libc.so.6.
          TheFunction = It->second;
        }
      }
      // It is a local function. Populate the ExportedNames.
      if (TheFunction) {
        for (auto &Name : PotentialExportedNamesToBeAdded)
          TheFunction->ExportedNames().insert(Name);
        continue;
      }

      // Consider it as a Dynamic function.
      if (not Prototype.isEmpty()) {
        for (const std::string &Name : UnprototypedFunctionsNames) {
          auto It = ImportedDynamicFunctions.find(Name);
          if (It == ImportedDynamicFunctions.end())
            It = ImportedDynamicFunctions.insert({ Name }).first;

          It->Prototype() = std::move(Prototype);
        }
      }
    }
  }
}

void DwarfImporter::import(const llvm::object::Binary &TheBinary,
                           StringRef FileName,
                           uint64_t PreferredBaseAddress) {
  using namespace llvm::object;

  if (auto *ELF = dyn_cast<ELFObjectFileBase>(&TheBinary)) {

    {
      using namespace model::Architecture;
      if (Model->Architecture() == Invalid)
        Model->Architecture() = fromLLVMArchitecture(ELF->getArch());
    }

    if (ELF->getEType() != ELF::ET_DYN)
      PreferredBaseAddress = 0;

    // Check if we already loaded the alt debug info file
    size_t AltIndex = -1;
    // Check if we already loaded the alt debug info file.
    StringRef SeparateDebugFileName = getDebugFileName(ELF);
    if (SeparateDebugFileName.size() > 0) {
      auto Begin = LoadedFiles.begin();
      auto End = LoadedFiles.end();
      auto It = std::find(Begin, End, SeparateDebugFileName);
      if (It != End)
        AltIndex = It - Begin;
    }
    auto TheDWARFContext = DWARFContext::create(*ELF);
    DwarfToModelConverter Converter(*this,
                                    *TheDWARFContext,
                                    LoadedFiles.size(),
                                    AltIndex,
                                    PreferredBaseAddress);
    Converter.run();

    detectAliases(*ELF, Model);
  }

  LoadedFiles.push_back(sys::path::filename(FileName).str());
}
