/* elf-common.pk - ELF definitions common to 32 and 64 bits.  */

/* Copyright (C) 2019, 2020, 2021, 2022, 2023 Jose E. Marchesi.  */

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

/* Basic types.  */

type Elf_Half = uint<16>,
     Elf_Word = uint<32>,
     Elf_Sword = int<32>;

/* ELF versions.  */

var EV_NONE = 0, /* Invalid ELF version.  */
    EV_CURRENT = 1;

/* The different kind of ELF files.  */

var ET_NONE = 0,  /* No file type.  */
    ET_REL  = 1,  /* Relocatable file.  */
    ET_EXEC = 2,  /* Position-dependent executable.  */
    ET_DYN  = 3,  /* Position-independent executable or shared object.  */
    ET_CORE = 4;  /* Core file.  */

/* ELF machine codes.  */

var EM_NONE = 0,
    EM_RISCV = 243;

/* ELF symbol bindings.  */

var STB_LOCAL = 0,
    STB_GLOBAL = 1,
    STB_WEAK = 2,
    STB_LOOS = 10,
    STB_HIOS = 12,
    STB_LOPROC = 13,
    STB_HIPROC = 15;

var elf_stb_names =
  ["local","global","weak",.[9]="reserved",.[12]="os",.[15]="proc"];

/* ELF symbol types.  */

var STT_NOTYPE = 0,
    STT_OBJECT = 1,
    STT_FUNC = 2,
    STT_SECTION = 3,
    STT_FILE = 4,
    STT_COMMON = 5,
    STT_TLS = 6,
    STT_NUM = 7,
    STT_LOOS = 10,
    STT_GNU_IFUNC = 10,
    STT_HIOS = 12,
    STT_LOPROC = 13,
    STT_HIPROC = 15;

var elf_stt_names =
  ["notype", "object", "func", "section", "file", "common",
   "tls", "num", "8", "9", "gnu_ifunc", "os11", "os12",
   "loproc13", "loproc14", "loproc15"];

/* ELF symbol visibility.  */

var STV_DEFAULT = 0,
    STV_INTERNAL = 1,
    STV_HIDDEN = 2,
    STV_PROTECTED = 3;

var elf_stv_names =
  ["default", "internal", "hidden", "protected"];

/* Types for ELF symbol information, including binding, type and
   visibility.  */

type Elf_Sym_Info =
  struct uint<8>
  {
    uint<4> st_bind;
    uint<4> st_type;

    method _print = void:
    {
      printf ("#<stb:%s,stt:%s>",
              elf_stb_names[st_bind], elf_stt_names[st_type]);
    }
  };

type Elf_Sym_Other_Info =
  struct uint<8>
  {
    uint<5>;
    uint<3> st_visibility;

    method _print = void:
      {
        print "#<stv:" + elf_stv_names[st_visibility] + ">";
      }
  };

/* Compression algorithm identificators.  */

var ELFCOMPRESS_ZLIB = 1,
    ELFCOMPRESS_LOOS = 0x6000_0000,
    ELFCOMPRESS_HIOS = 0x6fff_ffff,
    ELFCOMPRESS_LOPROC = 0x7000_0000,
    ELFCOMPRESS_HIPROC = 0x7fff_ffff;

fun elf_compress_algorithm_p = (uint<64> ch_type) int:
{
  return (ch_type == ELFCOMPRESS_ZLIB
          || (ch_type >= ELFCOMPRESS_LOOS && ch_type <= ELFCOMPRESS_HIOS)
          || (ch_type >= ELFCOMPRESS_LOPROC && ch_type <= ELFCOMPRESS_HIPROC));
}

/* ELF hashing function.  */

fun elf_hash = (string input) Elf_Word:
{
  var h = 0 as uint<32>;
  var high = 0 as uint<32>;

  for (c in input)
  {
    h = (h <<. 4) + c;
    high = h & 0xf0000000;
    if (high)
      h = h ^ (high .>> 24);
    h = h & ~high;
  };

 return h;
}

/* Dynamic tags.  */

var DT_NULL = 0,  /* Tags the end of the dynamic array.  */
    /* d_val holds the string table offset of a null-terminated
       string, giving the name of a needed library.  The dynamic array
       may.  contain multiple entries with this type.  */
    DT_NEEDED = 1,
    /* d_val holds the total size, in bytes, of the relocation
       entries associated with the procedure linkage table.  If an
       entry of type DT_JMPREL is present, a DT_PLTRELSZ must
       accompany it.  */
    DT_PLTRELSZ = 2,
    /* d_ptr holds an address associated with the procedure linkage
       table and/or the global offset table.  */
    DT_PLTGOT = 3,
    /* d_ptr holds the address of the symbol hash table.  This hash
       table refers to the symbol table referenced by the DT_SYMTAB
       element.  */
    DT_HASH = 4,
    /* d_ptr holds the address of the string table.  Symbol names,
       library names, and other strings reside in this table.  */
    DT_STRTAB = 5,
    /* d_ptr holds the address of the symbol table.  */
    DT_SYMTAB = 6,
    /* d_ptr holds the address of a relocation table.  Entries in the
       table have explicit addends.  An object file may have multiple
       relocation sections.  If this element is present, the dynamic
       vector must also have DT_RELASZ and DT_RELAENT elements.  */
    DT_RELA = 7,
    /* d_val holds the total size, in bytes, of the DT_RELA relocation
       entry.  */
    DT_RELASZ = 8,
    /* d_val holds the size, in bytes, of the DT_RELA relocation
       entry.  */
    DT_RELAENT = 9,
    /* d_val holds the size, in bytes, of the string table.  */
    DT_STRSZ = 10,
    /* d_val holds the size, in bytes, of a symbol table entry.  */
    DT_SYMENT = 11,
    /* d_ptr holds the address of the initialization function.  */
    DT_INIT = 12,
    /* d_ptr holds the address of the finalization function.  */
    DT_FINI = 13,
    /* d_val holds the string table offset of a null-terminated
       string, giving the name of the shared object.  The offset is an
       index into the table recorded in the DT_STRTAB entry.  */
    DT_SONAME = 14,
    /* d_val holds the string table offset of a null-terminated search
       library search path string.  The offset is an index into the
       table recorded in the DT_STRTAB entry.  This entry is at level
       2.  Its use has been superseded by DT_RUNPATH.  */
    DT_RPATH = 15,
    /* This element's presence in a shared object library alters the
       dynamic linker's symbol resolution algorithm for references
       within the library.  */
    DT_SYMBOLIC = 16,
    /* d_ptr holds the address of a relocation table containing rela
       relocations.  If this element is present, the dynamic structure
       must also have DT_RELSZ and DT_RELENT elements.  */
    DT_REL = 17,
    /* d_val holds the total size, in bytes, of the DT_REL relocation
       table.  */
    DT_RELSZ = 18,
    /* d_val holds the size, in bytes, of the DT_REL relocation
       entry. */
    DT_RELENT = 19,
    /* d_val specifies the type of relocation entry to which the
       procedure linkage table refers: DT_REL or DT_RELA.  */
    DT_PLTREL = 20,
    /* This tag is used for debugging.  Its contents are not
       specified for the ABI.  */
    DT_DEBUG = 21,
    /* This tag's absence signifies that no relocation entry should
       cause a modification to a non-writable segment, as specified
       by the segment permissions in the program header table.  If
       this tag is present, one or more relocation entries might
       request modifications to a non-writable segment, and the
       dynamic linker can prepare accordingly.  This entry is at
       level 2.  Its use has been superseded by the DF_TEXTREL
       flag.  */
    DT_TEXTREL = 22,
    /* d_ptr holds the address of relocation entries associated
       solely with the procedure linkage table.  If this tag is
       present, the related entries of types DT_PLTRELSZ and
       DT_PLTREL must also be present.  */
    DT_JMPREL = 23,
    /* If present, this tag instructs the dynamic linker to process
       all relocations for the object containing this entry before
       transferring control to the program.  */
    DT_BIND_NOW = 24,
    /* d_ptr holds the address of the array of pointers to
         initialization functions.  */
    DT_INIT_ARRAY = 25,
    /* d_ptr holds the address of the array of pointers to
         finalization functions.  */
    DT_FINI_ARRAY = 26,
    /* d_val holds the size in bytes of the array of initialization
       functions pointed to by the DT_INIT_ARRAY entry.  If an
       object has a DT_INIT_ARRAY entry, it must also have a
       DT_INIT_ARRAYSZ entry.  */
    DT_INIT_ARRAYSZ = 27,
    /* d_val holds the size in bytes of the array of termination
       functions pointed to by the DT_FINI_ARRAY entry.  If an
       object has a DT_fINI_ARRAY entry, it must also have a
       DT_FINI_ARRAYSZ entry.  */
    DT_FINI_ARRAYSZ = 28,
    /* d_val holds the string table offset of a null-terminated
       library search path string.  The offset is an index into the
       table recorded in the DT_STRTAB entry.  */
    DT_RUNPATH = 29,
    /* d_val holds flag values specific to the object being
       loaded.  */
    DT_FLAGS = 30,
    DT_ENCODING = 32,
    /* d_ptr holds the address of the array of pointers to
       pre-initialization functions.  */
    DT_PREINIT_ARRAY = 32,
    /* d_val holds the size in bytes of the array of
       pre-initialization functions pointed to by the
       DT_PREINIT_ARRAY entry.  */
    DT_PREINIT_ARRAYSZ = 33,
    /* d_ptr holds the address of the SHT_SYMTAB_SHNDX section
       associated with the dynamic symbol table referenced by the
       DT_SYMTAB element.  */
    DT_SYMTAB_SHNDX = 34,
    DT_LOOS = 0x6000_000d,
    DT_HIOS = 0x6fff_f000,
    DT_LOPROC = 0x7000_0000,
    DT_SPARC_REGISTER = 0x7000_0001,
    DT_HIPROC = 0x7fff_ffff;

/* Definitions related to section groups.  */

var GRP_COMDAT = 1,
    GRP_MASKOS = 0x0ff0_0000,
    GRP_MASKPROC = 0xf000_0000;

/* Section types.  */

var SHT_NULL = 0U,
    SHT_PROGBITS = 1U,
    SHT_SYMTAB = 2U,
    SHT_STRTAB = 3U,
    SHT_RELA = 4U,
    SHT_HASH = 5U,
    SHT_DYNAMIC = 6U,
    SHT_NOTE = 7U,
    SHT_NOBITS = 8U,
    SHT_REL = 9U,
    SHT_SHLIB = 10U,
    SHT_DYNSYM = 11U,
    SHT_INIT_ARRAY = 14U,
    SHT_FINI_ARRAY = 15U,
    SHT_PREINIT_ARRAY = 16U,
    SHT_GROUP = 17U,
    SHT_SYMTAB_SHNDX = 18U,
    SHT_LOOS = 0x6000_0000U,
    SHT_HIOS = 0x6fff_ffffU,
    SHT_LOUSER = 0x8000_0000U,
    SHT_HIUSER = 0xffff_ffffU;

/* Section Attribute Flags.  */

var SHF_WRITE = 0x1,
    SHF_ALLOC = 0x2,
    SHF_EXECINSTR = 0x4,
    SHF_MERGE = 0x10,
    SHF_STRINGS = 0x20,
    SHF_INFO_LINK = 0x40,
    SHF_LINK_ORDER = 0x80,
    SHF_OS_NONCONFORMING = 0x100,
    SHF_GROUP = 0x200,
    SHF_TLS = 0x400,
    SHF_COMPRESSED = 0x800,
    SHF_MASKOS = 0x0ff0_0000,
    SHF_MASKPROC = 0xf000_0000;

fun elf_pretty_print_section_flags = (uint<64> flags) void:
{
  var s = "";

  if (flags & SHF_WRITE)
    s = s + "WRITE,";
  if (flags & SHF_ALLOC)
    s = s + "ALLOC,";
  if (flags & SHF_EXECINSTR)
    s = s + "EXECINSTR,";
  if (flags & SHF_MERGE)
    s = s + "MERGE,";
  if (flags & SHF_STRINGS)
    s = s + "STRINGS,";
  if (flags & SHF_INFO_LINK)
    s = s + "INFO_LINK,";
  if (flags & SHF_LINK_ORDER)
    s = s + "LINK_ORDER,";
  if (flags & SHF_OS_NONCONFORMING)
    s = s + "OS_NONCONFORMING,";
  if (flags & SHF_GROUP)
    s = s + "GROUP,";
  if (flags & SHF_TLS)
    s = s + "TLS,";
  if (flags & SHF_COMPRESSED)
    s = s + "COMPRESSED,";
  if (flags & SHF_MASKOS)
   /* XXX call os-specific printer in elf-OS.pk */
    ;
  if (flags & SHF_MASKPROC)
   /* XXX call arch-specific printer in elf-ARCH.pk */
    ;

  print "#<" + rtrim (s, ",") + ">";
}

/* Segment types.  */

var PT_NULL = 0,
    PT_LOAD = 1,
    PT_DYNAMIC = 2,
    PT_INTERP = 3,
    PT_NOTE = 4,
    PT_SHLIB = 5,
    PT_PHDR = 6,
    PT_TLS = 7,
    PT_LOOS = 0x6000_0000,
    PT_HIOS = 0x6fff_ffff,
    PT_LOPROC = 0x7000_0000,
    PT_HIPROC = 0x7fff_ffff;

/* Segment Flags.  */

var PF_X = 0x1,
    PF_W = 0x2,
    PF_R = 0x4,
    PF_MASKOS = 0x0ff0_0000,
    PF_MASKPROC = 0xf000_0000;

type Elf_SegmentFlags =
  struct
  {
    Elf_Word flags;

    method _print = void:
      {
        var s = "";

        if (flags & PF_R)
          s = s + "R,";
        if (flags & PF_W)
          s = s + "W,";
        if (flags & PF_X)
          s = s + "X,";
        if (flags & PF_MASKOS)
          /* XXX call os-specific printer in elf-OS.pk */
          ;
        if (flags & PF_MASKPROC)
          /* XXX call arch-specific printer in elf-ARCH.pk */
          ;

        print "#<" + rtrim (s, ",") + ">";
      }
  };

/* Encoding of the ELF file.  */

var ELFDATANONE = 0,
    ELFDATA2LSB = 1,
    ELFDATA2MSB = 2;

/* ELF hash table.  */

type Elf_Hash_Table =
  struct
  {
    Elf_Word nbucket;
    Elf_Word nchain;

    Elf_Word[nbucket] bucket;
    Elf_Word[nchain] chain;
  };

/* ELF notes.  */

/* Note that the type below can be used for both ELF32 and ELF64
   files.  The SYSV ABI says that 64-bit notes must be aligned to 8
   bytes, but binutils is known to generate 64-bit notes aligned to 4
   bytes instead.  */

type Elf_Note =
  struct
  {
    /* The first `namesz' bytes in `name' contain a NULL-terminated
       character representation of the entry's owner or originator.  */
    Elf_Word namesz;

    /* The first `descsz' bytes in `desc' hold the note descriptor.
       The ABI places no constraints on a descriptor's contents.  */
    Elf_Word descsz;

    /* This word gives the interpretation of the descriptor.  Each
       originator controls its own types.  They must be non-negative.
       The ABI does not define what descriptors mean.  */
    Elf_Word _type;

    byte[namesz] name;
    byte[alignto (OFFSET, 4#B)];

    byte[descsz] desc;
    byte[alignto (OFFSET, 4#B)];
};

/* ELF identification struct.

   This is the first component in the ELF header for both ELF32 and
   ELF64.  */

type Elf_Ident =
  struct
  {
    byte[4] ei_mag == [0x7fUB, 'E', 'L', 'F'];
    byte ei_class;
    byte ei_data :
        (ei_data == ELFDATA2LSB) ? set_endian (ENDIAN_LITTLE) : set_endian (ENDIAN_BIG);
    byte ei_version;
    byte ei_osabi;
    byte ei_abiversion;
    byte[6] ei_pad;
    offset<byte,B> ei_nident;
  };
