/* ctf.pk - CTF implementation for GNU poke.  */

/* Copyright (C) 2019-2022 Oracle Inc.  */

/* 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/>.
 */

/* This file contains a Poke description of the CTF format, version 3.

   The Compact ANSI-C Type Format is described in
   https://github.com/oracle/libdtrace-ctf  */

load elf;

var CTF_MAX_SIZE = 0xfffffffe;
var CTF_LSIZE_SENT = 0xffffffff;

var CTF_MAGIC = 0xdff2;
var CTF_VERSION = 3;

var CTF_K_UNKNOWN  = 0U,
    CTF_K_INTEGER  = 1U,
    CTF_K_FLOAT    = 2U,
    CTF_K_POINTER  = 3U,
    CTF_K_ARRAY    = 4U,
    CTF_K_FUNCTION = 5U,
    CTF_K_STRUCT   = 6U,
    CTF_K_UNION    = 7U,
    CTF_K_ENUM     = 8U,
    CTF_K_FORWARD  = 9U,
    CTF_K_TYPEDEF  = 10U,
    CTF_K_VOLATILE = 11U,
    CTF_K_CONST    = 12U,
    CTF_K_RESTRICT = 13U,
    CTF_K_MAX = 63U;

var CTF_STRTAB_0 = 0; /* Name is in the in-CTF string table.  */
var CTF_STRTAB_1 = 1; /* Name is in an ELF strtab.  */

var CTF_KIND_UNKNOWN  = 0 as uint<6>,
    CTF_KIND_INTEGER  = 1 as uint<6>,
    CTF_KIND_FLOAT    = 2 as uint<6>,
    CTF_KIND_POINTER  = 3 as uint<6>,
    CTF_KIND_ARRAY    = 4 as uint<6>,
    CTF_KIND_FUNCTION = 5 as uint<6>,
    CTF_KIND_STRUCT   = 6 as uint<6>,
    CTF_KIND_UNION    = 7 as uint<6>,
    CTF_KIND_ENUM     = 8 as uint<6>,
    CTF_KIND_FORWARD  = 9 as uint<6>,
    CTF_KIND_TYPEDEF  = 10 as uint<6>,
    CTF_KIND_VOLATILE = 11 as uint<6>,
    CTF_KIND_CONST    = 12 as uint<6>,
    CTF_KIND_RESTRICT = 13 as uint<6>,
    CTF_KIND_SLICE    = 14 as uint<6>;

var ctf_kind_str =
  ["unknown", "integer", "float", "pointer", "array",
   "function", "struct", "union", "enum", "forward",
   "typedef", "volatile", "const", "restrict", "slice"];

type CTF_Name =
  struct uint<32>
  {
    uint<1> stid;
    offset<uint<31>,B> offset;

    method _print = void:
      {
       print "#<";
       printf "%u1d,%u31d", stid, offset/#B;
       print ">";
      }
  };

type CTF_Type_Id =
  struct uint<32>
  {
    uint<31> index;
    uint<1> is_parent;
  };

type CTF_Preamble =
  struct
  {
    uint<16> ctp_magic == CTF_MAGIC;
    byte ctp_version;
    byte ctp_flags;

    method _print = void:
      {
        print "#<\n";
        printf " Magic Number: %v\n", ctp_magic;
        printf " Version: %v\n", ctp_version;
        printf " Flags: %v\n", ctp_flags;
        print ">\n";
      }
  };

type CTF_Header =
  struct
  {
    CTF_Preamble cth_preamble;
    CTF_Name cth_parlabel;              /* Ref to name of parent lbl uniq'd against.  */
    CTF_Name cth_parname;               /* Ref to basename of parent.  */
    CTF_Name cth_cuname;                /* Ref to CU name (may be 0).  */
    offset<uint32,B> cth_lbloff;        /* Offset of label section.  */
    offset<uint32,B> cth_objtoff;       /* Offset of object section.  */
    offset<uint32,B> cth_funcoff;       /* Offset of function section.  */
    offset<uint32,B> cth_objtidxoff;    /* Offset of object index section.  */
    offset<uint32,B> cth_funcidxoff;    /* Offset of function index section.  */
    offset<uint32,B> cth_varoff;        /* Offset of variable section.  */
    offset<uint32,B> cth_typeoff;       /* Offset of type section.  */
    offset<uint32,B> cth_stroff;        /* Offset of string section.  */
    offset<uint32,B> cth_strlen;        /* Length of string section in bytes.  */

    method _print = void:
      {
        printf "%v", cth_preamble;

        print "#<\n";
        printf " Type Section: %u32d bytes\n", (cth_stroff-cth_typeoff)/#B;
        printf " String Section: %u32d bytes\n", cth_strlen/#B;
        print ">\n";
      }
  };

type CTF_Lblent =
  struct
  {
    CTF_Name ctl_label;
    CTF_Type_Id ctl_type;
  };

type CTF_Varent =
  struct
  {
    CTF_Name ctv_name;
    CTF_Type_Id ctv_type;
  };

type CTF_Info =
  struct uint<32>
  {
    uint<6> kind;
    uint<1> isroot;
    uint<25> vlen;

    method _print = void:
      {
       print "#<";
       printf "%s", ctf_kind_str[kind];
       if (isroot)
         print ",root";
       if (kind in [CTF_KIND_STRUCT,
                    CTF_KIND_UNION,
                    CTF_KIND_FUNCTION])
         printf ",len=%u25d", vlen;
       print ">";
      }
  };

var CTF_INT_SIGNED  = 0x01UB,
    CTF_INT_CHAR    = 0x02UB,
    CTF_INT_BOOL    = 0x04UB,
    CTF_INT_VARARGS = 0x08UB;

type CTF_Integer_Type =
  struct uint<32>
  {
    uint<8> encoding : encoding <= CTF_INT_VARARGS;
    offset<uint<8>,b> offset;
    offset<uint<16>,b> bits;
  };

var CTF_FP_SINGLE = 1UB,
    CTF_FP_DOUBLE = 2UB,
    CTF_FP_CPLX   = 3UB,
    CTF_FP_DCPLX  = 4UB,
    CTF_FP_LDCPLX = 5UB,
    CTF_FP_LDOUBLE = 6UB,
    CTF_FP_INTRVL = 7UB,
    CTF_FP_DINTRVL = 8UB,
    CTF_FP_LDINTRVL = 9UB,
    CTF_FP_IMAGRY = 10UB,
    CTF_FP_DIMAGRY = 11UB,
    CTF_FP_LDIMAGRY = 12UB;

type CTF_Float_Type =
  struct uint<32>
  {
    uint<8> encoding: encoding <= CTF_FP_LDIMAGRY;
    offset<uint<8>,b> offset;
    offset<uint<16>,b> bits;
  };

type CTF_Array =
  struct
  {
    CTF_Type_Id cta_contents;
    CTF_Type_Id cta_index;
    uint<32> cta_nelems;
  };

type CTF_Member =
  struct
  {
    CTF_Name ctm_name;
    offset<uint32,b> ctm_offset;
    CTF_Type_Id ctm_type;
  };

type CTF_Lmember =
  struct
  {
    CTF_Name ctlm_name;
    uint32 ctlm_offsethi;
    CTF_Type_Id ctlm_type;
    uint32 ctlm_offsetlo;
  };

type CTF_Enum =
  struct
  {
    CTF_Name cte_name;
    int<32> cte_value;
  };

type CTF_Slice =
  struct
  {
    CTF_Type_Id cts_type;
    offset<uint<16>,b> cts_offset;
    offset<uint<16>,b> cts_bits;
  };

type CTF_Type =
  struct
  {
    CTF_Name name;
    CTF_Info info;

    /* Some types have a size.  Others refer to other types.
       CTF_KIND_FORWARD is a distinct lone case where the field ttype refers
       to the kind of forward (struct, union or enum).  */
    union
    {
      uint32 ttype : info.kind in [CTF_KIND_POINTER,
                                   CTF_KIND_FUNCTION,
                                   CTF_KIND_FORWARD,
                                   CTF_KIND_TYPEDEF,
                                   CTF_KIND_VOLATILE,
                                   CTF_KIND_CONST,
                                   CTF_KIND_RESTRICT];
      union
      {
        uint32 normal : normal <= CTF_MAX_SIZE;
        struct
        {
          uint32 sentinel : sentinel == CTF_LSIZE_SENT;
          uint32 lsizehi;
          uint32 lsizelo;

          method _print = void:
            {
             print "#<";
             printf "%v", lsizehi:::lsizelo;
             print ">";
            }
        } huge;
      } size;
    } common;

    type CTF_Func_Args =
    struct
      {
        CTF_Type_Id[info.vlen] arg_types;
        uint32[info.vlen % 2] padding;
      };

    /* Data, that depends on the kind of the type.  */
    union
    {
      CTF_Integer_Type integer         : info.kind == CTF_KIND_INTEGER;
      CTF_Float_Type float             : info.kind == CTF_KIND_FLOAT;
      CTF_Array array                  : info.kind == CTF_KIND_ARRAY;
      CTF_Slice slice                  : info.kind == CTF_KIND_SLICE;
      CTF_Member[info.vlen] members    : info.kind in [CTF_KIND_STRUCT,CTF_KIND_UNION];
      CTF_Enum[info.vlen] _enum        : info.kind == CTF_KIND_ENUM;
      CTF_Func_Args func_args          : info.kind == CTF_KIND_FUNCTION;
      struct {} nothing;
    } data;
  };

type CTF_Dictionary =
  struct
  {
    CTF_Header header;

    var type_off = OFFSET + header.cth_typeoff;
    var type_size = header.cth_stroff - header.cth_typeoff;

    var str_off = OFFSET + header.cth_stroff;

    CTF_Type[type_size] types @ type_off;
    string[header.cth_strlen] strings @ str_off;

    /* Given an offset into the CTF strings section, return the string.  */

    method get_string = (offset<uint32,B> off) string:
      {
        if (off >= header.cth_strlen)
          raise E_inval;
        return strings[off];
      }
  };

fun ctf_string = (Elf64_File elf,
                  CTF_Dictionary ctf,
                  CTF_Name name) string:
{
  if (name.stid == CTF_STRTAB_0)
    return ctf.get_string (name.offset);
  else
    return elf.get_string (name.offset);
}

fun ctf_get_dict = (Elf64_File elf) CTF_Dictionary:
{
  var ctf_sections = elf.get_sections_by_name (".ctf");

  assert (ctf_sections'length != 1);
  /* FIXME - handle ctf archives.  */
  return CTF_Dictionary @ ctf_sections[0].sh_offset;
}
