/* Copyright (c) 2013-2019, David Anderson
All rights reserved.

Redistribution and use in source and binary forms, with
or without modification, are permitted provided that the
following conditions are met:

    Redistributions of source code must retain the above
    copyright notice, this list of conditions and the following
    disclaimer.

    Redistributions in binary form must reproduce the above
    copyright notice, this list of conditions and the following
    disclaimer in the documentation and/or other materials
    provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/
#ifndef READELFOBJ_H
#define READELFOBJ_H

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */


/*  Use this for .rel. too. */
struct generic_rela {
    Dwarf_Unsigned gr_offset;
    Dwarf_Unsigned gr_info;
    Dwarf_Unsigned gr_sym; /* From info */
    Dwarf_Unsigned gr_type; /* From info */
    Dwarf_Signed   gr_addend;
    unsigned char  gr_type2; /*MIPS64*/
    unsigned char  gr_type3; /*MIPS64*/
    /*  The following TRUE if .rela. and FALSE if .rel.
        if FALSE, gr_addend will be zero. */
    int            gr_is_rela;
};

/*  The following are generic to simplify handling
    Elf32 and Elf64.  Some fields added where
    the two sizes have different extraction code. */
struct generic_ehdr {
    unsigned char ge_ident[EI_NIDENT];
    Dwarf_Unsigned ge_type;
    Dwarf_Unsigned ge_machine;
    Dwarf_Unsigned ge_version;
    Dwarf_Unsigned ge_entry;
    Dwarf_Unsigned ge_phoff;
    Dwarf_Unsigned ge_shoff;
    Dwarf_Unsigned ge_flags;
    Dwarf_Unsigned ge_ehsize;
    Dwarf_Unsigned ge_phentsize;
    Dwarf_Unsigned ge_phnum;
    Dwarf_Unsigned ge_shentsize;
    Dwarf_Unsigned ge_shnum;
    Dwarf_Unsigned ge_shstrndx;
};
struct generic_phdr {
    Dwarf_Unsigned gp_type;
    Dwarf_Unsigned gp_flags;
    Dwarf_Unsigned gp_offset;
    Dwarf_Unsigned gp_vaddr;
    Dwarf_Unsigned gp_paddr;
    Dwarf_Unsigned gp_filesz;
    Dwarf_Unsigned gp_memsz;
    Dwarf_Unsigned gp_align;
};
struct generic_shdr {
    Dwarf_Unsigned gh_secnum;
    Dwarf_Unsigned gh_name;
    const char * gh_namestring;
    Dwarf_Unsigned gh_type;
    Dwarf_Unsigned gh_flags;
    Dwarf_Unsigned gh_addr;
    Dwarf_Unsigned gh_offset;
    Dwarf_Unsigned gh_size;
    Dwarf_Unsigned gh_link;
    /*  Section index (in an SHT_REL or SHT_RELA section)
        of the target section from gh_link. Otherwise 0. */
    Dwarf_Unsigned gh_reloc_target_secnum;
    Dwarf_Unsigned gh_info;
    Dwarf_Unsigned gh_addralign;
    Dwarf_Unsigned gh_entsize;

    /*  Zero unless content read in. Malloc space
        of size gh_size,  in bytes. For dwarf
        and strings mainly. free() this if not null*/
    char *       gh_content;

    /*  If a .rel or .rela section this will point
        to generic relocation records if such
        have been loaded.
        free() this if not null. */
    Dwarf_Unsigned          gh_relcount;
    struct generic_rela * gh_rels;

    /*  For SHT_GROUP based  grouping, which
        group is this section in. 0 unknown,
        1 DW_GROUP_NUMBER_BASE base DWARF,
        2 DW_GROUPNUMBER_DWO  dwo sections, 3+
        are in an SHT_GROUP. GNU uses this.
        set with group number (3+) from SHT_GROUP
        and the flags should have SHF_GROUP set
        if in SHT_GROUP. Must only be in one group? */
    Dwarf_Unsigned gh_section_group_number;

    /*  Content of an SHT_GROUP section as an array
        of integers. [0] is the version, which
        can only be one(1) . */
    Dwarf_Unsigned * gh_sht_group_array;
    /*  Number of elements in the gh_sht_group_array. */
    Dwarf_Unsigned   gh_sht_group_array_count;

    /*   TRUE if .debug_info .eh_frame etc. */
    char  gh_is_dwarf;
};

struct generic_dynentry {
    Dwarf_Unsigned  gd_tag;
    /*  gd_val stands in for d_ptr and d_val union,
        the union adds nothing in practice since
        we expect ptrsize <= ulongest. */
    Dwarf_Unsigned  gd_val;
    Dwarf_Unsigned  gd_dyn_file_offset;
};

struct generic_symentry {
    Dwarf_Unsigned gs_name;
    Dwarf_Unsigned gs_value;
    Dwarf_Unsigned gs_size;
    Dwarf_Unsigned gs_info;
    Dwarf_Unsigned gs_other;
    Dwarf_Unsigned gs_shndx;
    /* derived */
    Dwarf_Unsigned gs_bind;
    Dwarf_Unsigned gs_type;
};

struct location {
    const char *g_name;
    Dwarf_Unsigned g_offset;
    Dwarf_Unsigned g_count;
    Dwarf_Unsigned g_entrysize;
    Dwarf_Unsigned g_totalsize;
};

typedef struct elf_filedata_s {
    /*  f_ident[0] == 'E' means it is elf and
        elf_filedata_s is the struct involved.
        Other means error/corruption of some kind.
        f_ident[1] is a version number.
        Only version 1 is defined. */
    char           f_ident[8];
    char *         f_path; /* non-null if known. Must be freed */
    int            f_fd;
    int            f_machine; /* EM_* */
    int            f_destruct_close_fd;
    int            f_is_64bit;
    unsigned       f_endian;
    Dwarf_Unsigned f_filesize;
    /* Elf size, not DWARF. 32 or 64 */
    Dwarf_Small    f_offsetsize;
    Dwarf_Small    f_pointersize;
    int            f_ftype;

    Dwarf_Unsigned f_max_secdata_offset;
    Dwarf_Unsigned f_max_progdata_offset;

    void (*f_copy_word) (void *, const void *, unsigned long);

    struct location      f_loc_ehdr;
    struct generic_ehdr* f_ehdr;

    struct location      f_loc_shdr;
    struct generic_shdr* f_shdr;

    struct location      f_loc_phdr;
    struct generic_phdr* f_phdr;

    char *f_elf_shstrings_data; /* section name strings */
    /* length of currentsection.  Might be zero..*/
    Dwarf_Unsigned  f_elf_shstrings_length;
    /* size of malloc-d space */
    Dwarf_Unsigned  f_elf_shstrings_max;

    /* This is the .dynamic section */
    struct location      f_loc_dynamic;
    struct generic_dynentry * f_dynamic;
    Dwarf_Unsigned f_dynamic_sect_index;

    /* .dynsym, .dynstr */
    struct location      f_loc_dynsym;
    struct generic_symentry* f_dynsym;
    char  *f_dynsym_sect_strings;
    Dwarf_Unsigned f_dynsym_sect_strings_max;
    Dwarf_Unsigned f_dynsym_sect_strings_sect_index;
    Dwarf_Unsigned f_dynsym_sect_index;

    /* .symtab .strtab */
    struct location      f_loc_symtab;
    struct generic_symentry* f_symtab;
    char * f_symtab_sect_strings;
    Dwarf_Unsigned f_symtab_sect_strings_max;
    Dwarf_Unsigned f_symtab_sect_strings_sect_index;
    Dwarf_Unsigned f_symtab_sect_index;

    /* Starts at 3. 0,1,2 used specially. */
    Dwarf_Unsigned f_sg_next_group_number;
    /*  Both the following will be zero unless there
        are explicit Elf groups. */
    Dwarf_Unsigned f_sht_group_type_section_count;
    Dwarf_Unsigned f_shf_group_flag_section_count;
    Dwarf_Unsigned f_dwo_group_section_count;
} dwarf_elf_object_access_internals_t;

int dwarf_construct_elf_access(int fd,
    const char *path,
    dwarf_elf_object_access_internals_t **ep,int *errcode);
int dwarf_destruct_elf_access(dwarf_elf_object_access_internals_t *ep,int *errcode);
int _dwarf_load_elf_header(dwarf_elf_object_access_internals_t *ep,int *errcode);
int _dwarf_load_elf_sectheaders(dwarf_elf_object_access_internals_t* ep,int *errcode);

int _dwarf_load_elf_symtab_symbols(dwarf_elf_object_access_internals_t *ep,int *errcode);
int _dwarf_load_elf_symstr( dwarf_elf_object_access_internals_t *ep, int *errcode);

/*  These two enums used for type safety in passing
    values. */
enum RelocRela {
    RelocIsRela = 1,
    RelocIsRel = 2
};
enum RelocOffsetSize {
    RelocOffset32 = 4,
    RelocOffset64 = 8
};

int _dwarf_load_elf_relx(dwarf_elf_object_access_internals_t *ep,
    Dwarf_Unsigned secnum,enum RelocRela,int *errcode);

#ifndef EI_NIDENT
#define EI_NIDENT 16
#endif /* EI_NIDENT */

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* READELFOBJ_H */
