/* Copyright (C) 2021-2023 Free Software Foundation, Inc.
   Contributed by Oracle.

   This file is part of GNU Binutils.

   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, 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, write to the Free Software
   Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

#ifndef _DWARFLIB_H_
#define _DWARFLIB_H_

#include "dwarf2.h"

class ElfReloc;
class Dwr_type;
class SourceFile;

template <class ITEM> class Vector;
template <class ITEM> class DbeArray;
template <typename Key_t, typename Value_t> class DefaultMap;

typedef uint64_t ULEB128;
typedef int64_t SLEB128;
typedef unsigned short Dwarf_Half;
typedef unsigned char Dwarf_Small;
typedef uint64_t Dwarf_Off;
typedef uint64_t Dwarf_Addr;
typedef uint64_t Dwarf_Unsigned;
typedef int64_t Dwarf_Die;
typedef int32_t Dwarf_Debug;
typedef int32_t Dwarf_Attribute;


class DwrSec
{
public:
  DwrSec (unsigned char *_data, uint64_t _size, bool _need_swap_endian, bool _addr32);
  DwrSec (DwrSec *secp, uint64_t _offset);
  ~DwrSec ();
  unsigned char Get_8 ();
  unsigned short Get_16 ();
  uint32_t Get_32 ();
  uint64_t Get_64 ();
  uint64_t GetRef ();
  uint64_t GetADDR ();
  uint64_t GetADDR_32 ();
  uint64_t GetADDR_64 ();
  uint64_t GetLong ();
  uint64_t ReadLength ();
  SLEB128 GetSLEB128 ();
  ULEB128 GetULEB128 ();
  char *GetString (uint64_t *lenp);
  char *GetData (uint64_t len);
  void dump (char *msg);

  inline uint32_t
  GetULEB128_32 ()
  {
    return (uint32_t) GetULEB128 ();
  }

  bool
  inRange (uint64_t left, uint64_t right)
  {
    return (offset >= left) && (offset < right);
  };

  ElfReloc *reloc;
  uint64_t sizeSec;
  uint64_t size;
  uint64_t offset;
  bool fmt64;
  bool addr32;
  bool need_swap_endian;

private:
  bool isCopy;
  unsigned char *data;
  bool bounds_violation (uint64_t sz);
};

class DwrFileName
{
public:
  DwrFileName (char *_fname);
  ~DwrFileName ();
  uint64_t timestamp;
  uint64_t file_size;
  int dir_index;
  char *fname;
  char *path;
  bool isUsed;
};

class DwrLine
{
public:
  DwrLine ();
  ~DwrLine ();
  uint64_t address;
  uint32_t file;
  uint32_t line;
  uint32_t column;
};

class DwrInlinedSubr
{
public:
  DwrInlinedSubr (int64_t _abstract_origin, uint64_t _low_pc, uint64_t _high_pc,
		  int _file, int _line, int _level);
  void dump ();
  int64_t abstract_origin;
  uint64_t low_pc;
  uint64_t high_pc;
  int file;
  int line;
  int level;
};

class DwrLineRegs
{
public:
  DwrLineRegs (DwrSec *_secp, char *dirName);
  ~DwrLineRegs ();
  char *getPath (int fn);
  Vector<DwrLine *> *get_lines ();
  void dump ();

  Vector<DwrFileName *> *file_names;

private:
  void DoExtendedOpcode ();
  void DoStandardOpcode (int opcode);
  void DoSpecialOpcode (int opcode);
  void EmitLine ();
  void reset ();

  char *fname;
  uint64_t dir_index;
  uint64_t timestamp;
  uint64_t file_size;
  uint64_t address;
  int file;
  int line;
  int column;
  Dwarf_Half version;
  uint64_t op_index_register;
  Dwarf_Small maximum_operations_per_instruction;
  Dwarf_Small minimum_instruction_length;
  Dwarf_Small default_is_stmt;
  Dwarf_Small line_range;
  Dwarf_Small opcode_base;
  signed char line_base;
  bool is_stmt;
  bool basic_block;
  bool end_sequence;
  Vector<DwrLine *> *lines;
  Vector<char *> *include_directories;
  Dwarf_Small *standard_opcode_length;
  DwrSec *debug_lineSec;
  uint64_t header_length;
  uint64_t opcode_start;
};

typedef struct Dwr_Attr
{
  union
  {
    char *str;
    unsigned char *block;
    uint64_t offset;
    int64_t val;
  } u;
  uint64_t len;     // length of u.str
  int at_form;
  int at_name;
} Dwr_Attr;

typedef struct Dwr_Tag
{
public:
  Dwr_Attr *get_attr (Dwarf_Half attr);
  void dump ();

  DbeArray<Dwr_Attr> *abbrevAtForm;
  int64_t die;
  int64_t offset;
  int firstAttribute;
  int lastAttribute;
  int tag;
  int hasChild;
  int num;
  int level;
} Dwr_Tag;

enum
{
  DW_DLV_OK,
  DW_DLV_NO_ENTRY,
  DW_DLV_ERROR,
  DW_DLV_BAD_ELF,
  DW_DLV_NO_DWARF,
  DW_DLV_WRONG_ARG
};

typedef struct DwrLocation
{
  uint64_t offset;
  uint64_t lc_number;
  uint64_t lc_number2;
  uint32_t op;
} DwrLocation;

typedef struct DwrAbbrevTable
{
  int64_t offset;
  int firstAtForm;
  int lastAtForm;
  int code;
  int tag;
  bool hasChild;
} DwrAbbrevTable;

class Dwarf_cnt
{
public:
  Dwarf_cnt ();
  int64_t cu_offset;
  int64_t parent;
  int64_t size;
  Module *module;
  char *name;
  Function *func;
  Function *fortranMAIN;
  datatype_t *dtype;
  DwrInlinedSubr *inlinedSubr;
  DefaultMap <int64_t, Dwr_type*> *dwr_types;
  int level;

  Dwr_type *get_dwr_type (int64_t cu_die_offset);
  Dwr_type *put_dwr_type (int64_t cu_die_offset, int tag);
  Dwr_type *put_dwr_type (Dwr_Tag *dwrTag);
};

class DwrCU
{
public:
  DwrCU (Dwarf *_dwarf);
  ~DwrCU ();
  Module *parse_cu_header (LoadObject *lo);
  void parseChild (Dwarf_cnt *ctx);
  void read_hwcprof_info (Dwarf_cnt *ctx);
  void map_dwarf_lines (Module *mod);
  int set_die (Dwarf_Die die);
  DwrLineRegs *get_dwrLineReg ();

  static char *at2str (int tag);
  static char *form2str (int tag);
  static char *tag2str (int tag);

  uint64_t cu_header_offset;
  uint64_t cu_offset;
  uint64_t next_cu_offset;
  Vector<DwrInlinedSubr*> *dwrInlinedSubrs;
  Vector<SourceFile *> *srcFiles;
  bool isMemop;
  bool isGNU;

private:
  void build_abbrevTable (DwrSec *debug_abbrevSec, uint64_t stmt_list_offset);
  Function *append_Function (Dwarf_cnt *ctx);
  void parse_inlined_subroutine (Dwarf_cnt *ctx);
  uint64_t get_low_pc ();
  uint64_t get_high_pc (uint64_t low_pc);
  DwrLocation *dwr_get_location (DwrSec *secp, DwrLocation *lp);
  int read_data_attr (Dwarf_Half attr, int64_t *retVal);
  int read_ref_attr (Dwarf_Half attr, int64_t *retVal);
  char *get_linkage_name ();
  char *Dwarf_string (Dwarf_Half attr);
  int64_t Dwarf_data (Dwarf_Half attr);
  int64_t Dwarf_ref (Dwarf_Half attr);
  DwrSec *Dwarf_block (Dwarf_Half attr);
  Dwarf_Addr Dwarf_addr (Dwarf_Half attr);
  Dwarf_Addr Dwarf_location (Dwarf_Attribute attr);
  Sp_lang_code Dwarf_lang ();

  Dwarf *dwarf;
  DwrSec *debug_infoSec;
  uint64_t debug_abbrev_offset;
  uint64_t stmt_list_offset;  // offset in .debug_line section (DW_AT_stmt_list)
  char *comp_dir;             // compilation directory (DW_AT_comp_dir)
  Module *module;
  Dwarf_Half version;
  Dwarf_Small address_size;
  Dwr_Tag dwrTag;
  DwrLineRegs *dwrLineReg;
  DbeArray<DwrAbbrevTable> *abbrevTable;
  DbeArray<Dwr_Attr> *abbrevAtForm;
};

#endif /* _DWARFLIB_H_ */
