/* Copyright (C) 2007-2010 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** 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.
*/

/*
 * Contains declaration of a class DwarfCU, that encapsulates a compilation
 * unit in the .debug_info section of the mapped ELF file.
 */

#ifndef ELFF_DWARF_CU_H_
#define ELFF_DWARF_CU_H_

#include "dwarf_defs.h"
#include "dwarf_die.h"

/* Address information descriptor. */
typedef struct Dwarf_AddressInfo {
  /* Routine DIE containing the address. */
  const DIEObject*  die_obj;

  /* Source file name for the address. */
  const char*       file_name;

  /* Source file directory path for the address. */
  const char*       dir_name;

  /* Source file line number for the address. */
  Elf_Word          line_number;
} Dwarf_AddressInfo;

/* STMTL header cached by compilation unit. This header is contained in
 * the .debug_line section of the ELF file. */
typedef struct Dwarf_STMTL_Hdr {
  /* The size in bytes of the line number information for this compilation
   * unit, not including the unit_length field itself. */
  Elf_Xword                   unit_length;

  /* A version number. This number is specific to the line number information
   * and is independent of the DWARF version number. */
  Elf_Half                    version;

  /* The number of bytes following the header_length field to the beginning of
   * the first byte of the line number program itself. In the 32-bit DWARF
   * format, this is a 4-byte unsigned length; in the 64-bit DWARF format,
   * this field is an 8-byte unsigned length. */
  Elf_Xword                   header_length;

  /* The size in bytes of the smallest target machine instruction. Line number
   * program opcodes that alter the address register first multiply their
   * operands by this value. */
  Elf_Byte                    min_instruction_len;

  /* The initial value of the is_stmt register. */
  Elf_Byte                    default_is_stmt;

  /* This parameter affects the meaning of the special opcodes. */
  Elf_Sbyte                   line_base;

  /* This parameter affects the meaning of the special opcodes. */
  Elf_Byte                    line_range;

  /* The number assigned to the first special opcode. */
  Elf_Byte                    opcode_base;

  /* Points to standard_opcode_lengths array in the actual STMTL header in
   * the mapped .debug_line section. */
  const Elf_Byte*             standard_opcode_lengths;

  /* Pointer to the beginning of the list of include directories in the mapped
   * .debug_line section. */
  const char*                 include_directories;

  /* Number of include directories in the list that begins with
   * include_directories. */
  Elf_Word                    inc_dir_num;

  /* Pointer to the beginning of the list of file information in the mapped
   * .debug_line section. Each entry in this list begins with zero-terminated
   * file name, followed by ULEB128 encoding directory index for the file,
   * followed by ULEB128 encoding last modification time, followed by ULEB128
   * encoding length of file in bytes. */
  const Dwarf_STMTL_FileDesc* file_infos;

  /* Start of the "Line Number Program" in the mapped .debug_line section. */
  const Elf_Byte*             start;

  /* End of the "Line Number Program" in the mapped .debug_line section. */
  const Elf_Byte*             end;
} Dwarf_STMTL_Hdr;

/* Encapsulates architecture-independent functionality of a
 * compilation unit.
 */
class DwarfCU : public DwarfAllocBase {
friend class ElfFile;
 public:
  /* Constructs DwarfCU instance.
   * Param:
   *  elf - Instance of ElfFile containing this compilation unit.
   */
  explicit DwarfCU(ElfFile* elf);

  /* Destructs DwarfCU instance. */
  virtual ~DwarfCU();

  /* Creates DwarfCUImpl instance, depending on DWARF format.
   * Param:
   *  elf - Instance of ElfFile containing this compilation unit.
   *  hdr - Pointer to compilation unit header inside mapped .debug_info
   *    section of the ELF file. Actual data addressed by this pointer
   *    must be Dwarf32_CUHdr for 32 bit DWARFs, or Dwarf64_CUHdr for
   *    64 bit DWARFs.
   * Return:
   *  Created DwarfCUImpl instance (typecasted back to DwarfCU) on success,
   *  or NULL on failure.
   */
  static DwarfCU* create_instance(ElfFile* elf, const void* hdr);

  /* Process a DIE attribute.
   * Param:
   *  attr - Attribute list inside the mapped .debug_info section of the ELF
   *    file.
   *  form - Attribute's form, definig representation of attribute value in the
   *    mapped .debug_info section of the ELF file.
   *  attr_value - Upon return contains attribute value.
   * Return:
   *  Pointer to the next DIE attribute inside the mapped .debug_info section
   *  of the ELF file.
   */
  const Elf_Byte* process_attrib(const Elf_Byte* attr,
                                 Dwarf_Form form,
                                 Dwarf_Value* attr_value) const;

  /* Dumps this compilation unit to the stdout. */
  void dump() const;

  /* Gets instance of ElfFile containing this compilation unit. */
  ElfFile* elf_file() const {
    return elf_file_;
  }

  /* Gets DIE object for this CU. */
  DIEObject* cu_die() const {
    return cu_die_;
  }

  /* Gets byte size of the pointer type for this compilation unit. */
  Elf_Byte addr_sizeof() const {
    return addr_sizeof_;
  }

  /* Gets full path to the compilation directory (DW_AT_comp_dir attribute).
   * Return:
   *  Full path to the compilation directory (DW_AT_comp_dir attribute),
   *  or NULL, if that attribute was missing in CU's attribute list.
   */
  const char* comp_dir_path() const {
    DIEAttrib attr;
    return cu_die()->get_attrib(DW_AT_comp_dir, &attr) ? attr.value()->str :
                                                         NULL;
  }

  /* Gets relative (from the compilation directory) path to the compiled file.
   * (DW_AT_name attribute).
   * Return:
   *  Relative path to the compiled file (DW_AT_name attribute), or NULL, if
   *  that attribute was missing in CU's attribute list.
   */
  const char* rel_cu_path() const {
    DIEAttrib attr;
    return cu_die()->get_attrib(DW_AT_name, &attr) ? attr.value()->str :
                                                     NULL;
  }

  /* Gets next compilation unit in the list. NULL indicates the last CU. */
  DwarfCU* prev_cu() const {
    return prev_cu_;
  }

  /* Links this CU to the list of prevoiusly discovered CUs. */
  void set_prev_cu(DwarfCU* prev) {
    prev_cu_ = prev;
  }

  /* Checks if DWARF version for this CU is higher than 2. */
  bool is_DWARF3_or_higher() const {
    return version_ >= 3;
  }

  /* Gets DIE abbreviation for given abbreviation number.
   * See DwarfAbbrDieArray::get() */
  const Dwarf_Abbr_DIE* get_die_abbr(Dwarf_AbbrNum num) const {
    return abbrs_.get(num);
  }

  /* Gets DIE object containing given address.
   * DIE address ranges may overlap (for instance, address range for an inlined
   * routine will be contained within the address range of a routine where it
   * was inlined). This method will return a DIE object that is a "leaf" in
   * that inlining tree. I.e the returned DIE object represents the last DIE
   * in the branch of all DIEs containing given address.
   * Param:
   *  address - Address to get a DIE for. NOTE: for the sake of simplicity we
   *    explicitly use 64-bit type for an address.
   * Return:
   *  Leaf DIE containing given address, or NULL if this CU doesn't contain
   *  the given address.
   */
  DIEObject* get_leaf_die_for_address(Elf_Xword address) const {
    return cu_die_->get_leaf_for_address(address);
  }

  /* Checks if this CU contains 64, or 32-bit addresses. */
  bool is_CU_address_64() const {
    return addr_sizeof_ == 8;
  }
  bool is_CU_address_32() const {
    return addr_sizeof_ == 4;
  }

//=============================================================================
// DWARF format dependent methods
//=============================================================================

 public:
  /* Parses this compilation unit in .debug_info section, collecting children
   * DIEs of this compilation unit.
   * Param:
   *  parse_context - Parsing context that lists tags for DIEs that should be
   *    collected during parsing. NULL passed in this parameter indicates DIEs
   *    for all tags should be collected.
   *  next_cu_die - Upon successful return contains pointer to the next
   *    compilation unit descriptor inside mapped .debug_info section of
   *    the ELF file.
   * Return:
   *  true on success, false on failure.
   */
  virtual bool parse(const DwarfParseContext* parse_context,
                     const void** next_cu_die) = 0;

  /* Gets a DIE object referenced by an offset from the beginning of
   * this CU in the mapped .debug_info section.
   */
  virtual DIEObject* get_referenced_die_object(Elf_Word ref) const = 0;

  /* Gets a reference to a DIE object (offset of the DIE from the
   * beginning of this CU in the mapped .debug_info section.
   */
  virtual Elf_Word get_die_reference(const Dwarf_DIE* die) const = 0;

  /* Gets PC address information.
   * Param:
   *  address - PC address to get information for.
   *  info - Upon success contains information about routine that belongs to
   *    this compilation unit, containing the given address.
   * Return:
   *  true on success, or false if this CU doesn't contain the given address.
   */
  virtual bool get_pc_address_file_info(Elf_Xword address,
                                        Dwarf_AddressInfo* info) = 0;

  /* Gets file descriptor in the mapped .debug_line section of ELF file for a
   * given index in the file descriptor list.
   * Param:
   *  index - 1-based index of file descriptor in the file descriptor list.
   * Return:
   *  File descriptor for the given index, or NULL if index was too big.
   *  NOTE: pointer returned from this method addressed mapped section of
   *  ELF file.
   */
  virtual const Dwarf_STMTL_FileDesc* get_stmt_file_info(Elf_Word index) = 0;

  /* Gets directory name by an index in the mapped .debug_line section of
   * ELF file.
   * Param:
   *  dir_index - Index of the directory in the file descriptor list. If this
   *    parameter is zero, compilation directory (DW_AT_comp_dir) for this CU
   *    will be returned.
   * Return:
   *  Directory name for the given index, or NULL if index was too big.
   *  NOTE: pointer returned from this method addressed mapped section of
   *  ELF file.
   */
  virtual const char* get_stmt_dir_name(Elf_Word dir_index) = 0;

 protected:
  /* DIE abbreviation descriptors, cached for this compilation unit. */
  DwarfAbbrDieArray   abbrs_;

  /* Instance of an ELF file that contains this compilation unit. */
  ElfFile*            elf_file_;

  /* DIE object for this CU. */
  DIEObject*          cu_die_;

  /* Next compilation unit in the list (previous in the order they've been
   * discovered during ELF file parsing).
   */
  DwarfCU*            prev_cu_;

  /* DWARF version for this CU. */
  Elf_Half            version_;

  /* Byte size of the pointer type for this compilation unit. */
  Elf_Byte            addr_sizeof_;
};

/* Encapsulates architecture-dependent functionality of a compilation unit.
 * Template param:
 *  Dwarf_CUHdr - type compilation unit header in the mapped .debug_info
 *    section of ELF file. Must be:
 *    - Dwarf32_CUHdr for 32-bit DWARF, or
 *    - Dwarf64_CUHdr for 64-bit DWARF.
 *  Dwarf_Off - type for an offset field in DWARF data format. Must be:
 *    - Dwarf32_Off for 32-bit DWARF, or
 *    - Dwarf64_Off for 64-bit DWARF.
 */
template <typename Dwarf_CUHdr, typename Dwarf_Off>
class DwarfCUImpl : public DwarfCU {
 public:
  /* Constructs DwarfCU instance.
   * Param:
   *  elf - Instance of ElfFile containing this compilation unit.
   *  hdr - Pointer to compilation unit header inside mapped .debug_info
   *    section of the ELF file.
   */
  DwarfCUImpl(ElfFile* elf, const Dwarf_CUHdr* hdr);

  /* Destructs DwarfCU instance. */
  ~DwarfCUImpl() {
  }

  /* Parses this compilation unit in .debug_info section, collecting children
   * DIEs of this compilation unit. This is an implementation of DwarfCU's
   * abstract metod.
   * See DwarfCU::parse().
   */
  bool parse(const DwarfParseContext* parse_context,
             const void** next_cu_die);

  /* Gets PC address information.
   * This is an implementation of DwarfCU's abstract metod.
   * See DwarfCU::get_pc_address_file_info().
   */
  bool get_pc_address_file_info(Elf_Xword address, Dwarf_AddressInfo* info);

  /* Gets file descriptor in the mapped .debug_line section of ELF file for a
   * given index in the file descriptor list.
   * This is an implementation of DwarfCU's abstract metod.
   * See DwarfCU::get_stmt_file_info().
   */
  const Dwarf_STMTL_FileDesc* get_stmt_file_info(Elf_Word index);

  /* Gets directory name by an index in the mapped .debug_line section of
   * ELF file.
   * This is an implementation of DwarfCU's abstract metod.
   * See DwarfCU::get_stmt_dir_name().
   */
  const char* get_stmt_dir_name(Elf_Word dir_index);

  /* Gets a DIE object referenced by an offset from the beginning of
   * this CU. This is an implementation of DwarfCU's abstract metod.
   */
  DIEObject* get_referenced_die_object(Elf_Word ref) const {
    const Dwarf_DIE* die = get_referenced_die(ref);
    return cu_die_->find_die_object(die);
  }

  /* Gets a reference to a DIE object (offset of the DIE from the
   * beginning of this CU in the mapped .debug_info section.
   * This is an implementation of DwarfCU's abstract metod.
   */
  Elf_Word get_die_reference(const Dwarf_DIE* die) const {
    return static_cast<Elf_Word>(diff_ptr(cu_header_, die));
  }

 protected:
  /* Process a child DIE (and all its children) in this compilation unit.
   * Param:
   *  parse_context - See DwarfCU::parse().
   *  die - DIE descriptor of the child to process in this method.
   *  parent_obj - Parent object of the child to process in this method.
   *    NOTE: this parameter can be NULL only for a DIE that represents this
   *    compilation unit itself.
   * Return:
   *  Pointer to the end of child's attribute list in the mapped .debug_info
   *  section on success, or NULL on failure. Usually, pointer returned from
   *  this method is simply discarded, since parent calculates address of the
   *  next sibling's DIE based on DW_AT_sibling attribute of the DIE preceding
   *  child's DIE.
   */
  const Elf_Byte* process_DIE(const DwarfParseContext* parse_context,
                              const Dwarf_DIE* die,
                              DIEObject* parent_obj);

  /* Creates a DIE object for the given DIE.
   * Param:
   *  parse_context See DwarfCU::parse().
   *  die - DIE to create an object for.
   *  parent - Parent DIE object for the one that's being created in this
   *    method.
   *  tag - Tag of the DIE object that's being created in this method.
   * Return:
   *  Created DIE object. This method may returns NULL in two cases:
   *    - We're not interested in this DIE (decided by looking at 'tag'
   *      parameter. In this case errno should be set to zero.
   *    - Memory allocation has failed. In this case errno should be
   *      set to ENOMEM.
   */
  DIEObject* create_die_object(const DwarfParseContext* parse_context,
                               const Dwarf_DIE* die,
                               DIEObject* parent,
                               Dwarf_Tag tag);

  /* Initializes (caches) STMT lines header for this CU. */
  bool init_stmtl();

  /* Saves current source file information, collected in the state machine by
   * the "Line Number Program".
   * Param:
   *  state - State machine collected "Line Number Program" results.
   *  info - Upon success contains source file information, copied over from
   *    the state machine.
   * Return:
   *  true on success, or false on failure.
   */
  bool set_source_info(const DwarfStateMachine* state,
                       Dwarf_AddressInfo* info);

  /* Gets pointer to the DIE descriptor for this CU. */
  const Dwarf_DIE* get_DIE() const {
    /* CU's DIE descriptor immediately follows CU header. */
    return INC_CPTR_T(Dwarf_DIE, cu_header_, sizeof(Dwarf_CUHdr));
  }

  /* Caches STMTL header from .debug_line section to stmtl_header_.
   * Template param:
   *  Dwarf_STMTL_Hdr - Dwarf_STMTL_Hdr32, or Dwarf_STMTL_Hdr64, depending
   *    on the header type.
   * Param:
   *  stmtl_hdr - STMTL header in the mapped .debug_line section to cache.
   */
  template <typename Dwarf_STMTL_Hdr>
  void cache_stmtl(const Dwarf_STMTL_Hdr* stmtl_hdr) {
    stmtl_header_.unit_length = elf_file()->pull_val(stmtl_hdr->unit_length.size);
    stmtl_header_.version = elf_file()->pull_val(stmtl_hdr->version);
    stmtl_header_.header_length = elf_file()->pull_val(stmtl_hdr->header_length);
    stmtl_header_.min_instruction_len = stmtl_hdr->min_instruction_len;
    stmtl_header_.default_is_stmt = stmtl_hdr->default_is_stmt;
    stmtl_header_.line_base = stmtl_hdr->line_base;
    stmtl_header_.line_range = stmtl_hdr->line_range;
    stmtl_header_.opcode_base = stmtl_hdr->opcode_base;
    stmtl_header_.standard_opcode_lengths = &stmtl_hdr->standard_opcode_lengths;
    stmtl_header_.start = INC_CPTR_T(Elf_Byte, &stmtl_hdr->min_instruction_len,
                                     stmtl_header_.header_length);
    stmtl_header_.end = INC_CPTR_T(Elf_Byte, &stmtl_hdr->version,
                                   stmtl_header_.unit_length);
    stmtl_header_.include_directories =
        INC_CPTR_T(char, stmtl_header_.standard_opcode_lengths,
                   stmtl_header_.opcode_base - 1);
    const char* dir = stmtl_header_.include_directories;
    while (*dir != '\0') {
      dir += strlen(dir) + 1;
      stmtl_header_.inc_dir_num++;
    }
    stmtl_header_.file_infos = INC_CPTR_T(Dwarf_STMTL_FileDesc, dir, 1);
  }

  /* Gets a DIE referenced by an offset from the beginning of this CU
   * in the mapped .debug_info section.
   */
  const Dwarf_DIE* get_referenced_die(Elf_Word ref) const {
    return INC_CPTR_T(Dwarf_DIE, cu_header_, ref);
  }

  /* Checks if pointer to the DIE attribute is contained within the CU's area
   * of the mapped .debug_info section.
   * Param:
   *  ptr - Pointer to the DIE attribute to check.
   * Return:
   *  true, if pointer to the DIE attribute is contained within the CU's area
   *  of the mapped .debug_info section, or false if attribute pointer goes
   *  beyond CU's area of the mapped .debug_info section.
   */
  bool is_attrib_ptr_valid(const void* ptr) const {
    return diff_ptr(cu_header_, ptr) < cu_size_;
  }

 protected:
  /* Pointer to this compilation unit header inside the mapped .debug_info
   * section of the ELF file.
   */
  const Dwarf_CUHdr*          cu_header_;

  /* Size of this compilation unit area in the mapped .debug_info section.
   * This value has been cached off the CU header in order to avoid
   * endianness conversions.
   */
  Dwarf_Off                   cu_size_;

  /* STMT lines header, cached off mapped .debug_line section. */
  Dwarf_STMTL_Hdr             stmtl_header_;
};

#endif  // ELFF_DWARF_CU_H_
