/* DWARF 2 debugging format support for GDB.

   Copyright (C) 1994-2026 Free Software Foundation, Inc.

   This file is part of GDB.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#ifndef GDB_DWARF2_DWO_H
#define GDB_DWARF2_DWO_H

#include <atomic>

#include "dwarf2/section.h"
#include "gdb_bfd.h"
#include "gdbsupport/unordered_set.h"
#include "hashtab.h"

struct dwarf2_per_cu;

/* These sections are what may appear in a (real or virtual) DWO file.  */

struct dwo_sections
{
  struct dwarf2_section_info abbrev;
  struct dwarf2_section_info line;
  struct dwarf2_section_info loc;
  struct dwarf2_section_info loclists;
  struct dwarf2_section_info macinfo;
  struct dwarf2_section_info macro;
  struct dwarf2_section_info rnglists;
  struct dwarf2_section_info str;
  struct dwarf2_section_info str_offsets;
  /* In the case of a virtual DWO file, these two are unused.  */
  std::vector<dwarf2_section_info> infos;
  std::vector<dwarf2_section_info> types;
};

/* CUs/TUs in DWP/DWO files.  */

struct dwo_unit
{
  /* Backlink to the containing struct dwo_file.  */
  struct dwo_file *dwo_file = nullptr;

  /* For compile units only, the per-CU structure that represents the skeleton
     that we used to reach this dwo_unit.  It starts as nullptr, and gets set
     in cutu_reader::lookup_dwo_cutu.  */
  std::atomic<dwarf2_per_cu *> per_cu = nullptr;

  /* The "id" that distinguishes this CU/TU.
     .debug_info calls this "dwo_id", .debug_types calls this "signature".
     Since signatures came first, we stick with it for consistency.  */
  ULONGEST signature = 0;

  /* The section this CU/TU lives in, in the DWO file.  */
  dwarf2_section_info *section = nullptr;

  /* This is set if SECTION is owned by this dwo_unit.  */
  dwarf2_section_info_up section_holder;

  /* Same as dwarf2_per_cu::{sect_off,length} but in the DWO section.  */
  sect_offset sect_off {};
  unsigned int length = 0;

  /* For types, offset in the type's DIE of the type defined by this TU.  */
  cu_offset type_offset_in_tu;
};

using dwo_unit_up = std::unique_ptr<dwo_unit>;

/* Hash function for dwo_unit objects, based on the signature.  */

struct dwo_unit_hash
{
  using is_transparent = void;

  std::size_t operator() (ULONGEST signature) const noexcept
  { return signature; }

  std::size_t operator() (const dwo_unit_up &unit) const noexcept
  { return (*this) (unit->signature); }
};

/* Equal function for dwo_unit objects, based on the signature.

   The signature is assumed to be unique within the DWO file.  So while object
   file CU dwo_id's always have the value zero, that's OK, assuming each object
   file DWO file has only one CU, and that's the rule for now.  */

struct dwo_unit_eq
{
  using is_transparent = void;

  bool operator() (ULONGEST sig, const dwo_unit_up  &unit) const noexcept
  { return sig == unit->signature; }

  bool operator() (const dwo_unit_up &a, const dwo_unit_up &b) const noexcept
  { return (*this) (a->signature, b); }
};

/* Set of dwo_unit object, using their signature as identity.  */

using dwo_unit_set = gdb::unordered_set<dwo_unit_up, dwo_unit_hash, dwo_unit_eq>;

/* Data for one DWO file.

   This includes virtual DWO files (a virtual DWO file is a DWO file as it
   appears in a DWP file).  DWP files don't really have DWO files per se -
   comdat folding of types "loses" the DWO file they came from, and from
   a high level view DWP files appear to contain a mass of random types.
   However, to maintain consistency with the non-DWP case we pretend DWP
   files contain virtual DWO files, and we assign each TU with one virtual
   DWO file (generally based on the line and abbrev section offsets -
   a heuristic that seems to work in practice).  */

struct dwo_file
{
  dwo_file () = default;
  DISABLE_COPY_AND_ASSIGN (dwo_file);

  /* Look for a type unit with signature SIGNATURE in this dwo_file.

     Return nullptr if not found.  */
  dwo_unit *find_tu (ULONGEST signature) const
  {
    if (auto it = this->tus.find (signature); it != this->tus.end ())
      return it->get ();

    return nullptr;
  }

  /* The DW_AT_GNU_dwo_name or DW_AT_dwo_name attribute.
     For virtual DWO files the name is constructed from the section offsets
     of abbrev,line,loc,str_offsets so that we combine virtual DWO files
     from related CU+TUs.  */
  std::string dwo_name;

  /* The DW_AT_comp_dir attribute.  */
  const char *comp_dir = nullptr;

  /* The bfd, when the file is open.  Otherwise this is NULL.
     This is unused(NULL) for virtual DWO files where we use dwp_file.dbfd.  */
  gdb_bfd_ref_ptr dbfd;

  /* The sections that make up this DWO file.
     Remember that for virtual DWO files in DWP V2 or DWP V5, these are virtual
     sections (for lack of a better name).  */
  struct dwo_sections sections {};

  /* The CUs in the file.

     Multiple CUs per DWO are supported as an extension to handle LLVM's Link
     Time Optimization output (where multiple source files may be compiled into
     a single object/dwo pair).  */
  dwo_unit_set cus;

  /* Table of TUs in the file.  */
  dwo_unit_set tus;
};

using dwo_file_up = std::unique_ptr<dwo_file>;

/* This is used when looking up entries in a dwo_file_set.  */

struct dwo_file_search
{
  /* Name of the DWO to look for.  */
  const char *dwo_name;

  /* Compilation directory to look for.  */
  const char *comp_dir;
};

/* Hash function for dwo_file objects, using their dwo_name and comp_dir as
   identity.  */

struct dwo_file_hash
{
  using is_transparent = void;

  std::size_t operator() (const dwo_file_search &search) const noexcept
  {
    hashval_t hash = htab_hash_string (search.dwo_name);

    if (search.comp_dir != nullptr)
      hash += htab_hash_string (search.comp_dir);

    return hash;
  }

  std::size_t operator() (const dwo_file_up &file) const noexcept
  {
    return (*this) ({ file->dwo_name.c_str (), file->comp_dir });
  }
};

/* Equal function for dwo_file objects, using their dwo_name and comp_dir as
   identity.  */

struct dwo_file_eq
{
  using is_transparent = void;

  bool operator() (const dwo_file_search &search,
		   const dwo_file_up &dwo_file) const noexcept
  {
    if (search.dwo_name != dwo_file->dwo_name)
      return false;

    if (search.comp_dir == nullptr || dwo_file->comp_dir == nullptr)
      return search.comp_dir == dwo_file->comp_dir;

    return streq (search.comp_dir, dwo_file->comp_dir);
  }

  bool operator() (const dwo_file_up &a, const dwo_file_up &b) const noexcept
  {
    return (*this) ({ a->dwo_name.c_str (), a->comp_dir }, b);
  }
};

/* Set of dwo_file objects, using their dwo_name and comp_dir as identity.  */

using dwo_file_up_set
  = gdb::unordered_set<dwo_file_up, dwo_file_hash, dwo_file_eq>;

#endif /* GDB_DWARF2_DWO_H */
