#ifndef MR4TH_ELF_H
#define MR4TH_ELF_H

/*
** ELF total layout summary:
**
** TODO
*/

////////////////////////////////
// Types: ELF

// identification

#define ELF_NUM_IDENT 16

#define ELF_IdentificationIdx_XList(X)\
X(MAG0,       0)\
X(MAG1,       1)\
X(MAG2,       2)\
X(MAG3,       3)\
X(CLASS,      4)\
X(DATA,       5)\
X(VERSION,    6)\
X(OSABI,      7)\
X(ABIVERSION, 8)\
X(PAD0,       9)\
X(PAD1,      10)\
X(PAD2,      11)\
X(PAD3,      12)\
X(PAD4,      13)\
X(PAD5,      14)\
X(PAD6,      15)

typedef enum ELF_IdentificationIdx{
#define X(N,C) ELF_IdentificationIdx_##N = C,
  ELF_IdentificationIdx_XList(X)
#undef X
} ELF_IdentificationIdx;

#define ELF_Magic_Byte0 0x7F
#define ELF_Magic_Byte1  'E'
#define ELF_Magic_Byte2  'L'
#define ELF_Magic_Byte3  'F'

// elf class

#define ELF_Class_XList(X)\
X(NONE, 0)\
X(32,   1)\
X(64,   2)

typedef enum ELF_Class{
#define X(N,C) ELF_Class_##N = C,
  ELF_Class_XList(X)
#undef X
} ELF_Class;

// elf encoding

#define ELF_Encoding_XList(X)\
X(NONE, 0)\
X(2LSB, 1)\
X(2MSB, 2)

typedef enum ELF_Encoding{
#define X(N,C) ELF_Encoding_##N = C,
  ELF_Encoding_XList(X)
#undef X
} ELF_Encoding;

// elf os/abi extension

#define ELF_OsAbiExtension_XList(X)\
X(NONE,      0)\
X(HPUX,      1)\
X(NETBSD,    2)\
X(GNU,       3)\
X(SOLARIS,   6)\
X(AIX,       7)\
X(IRIX,      8)\
X(FREEBSD,   9)\
X(TRU64,    10)\
X(MODESTO,  11)\
X(OPENBSD,  12)\
X(OPENVMS,  13)\
X(NSK,      14)\
X(AROS,     15)\
X(FENIXOS,  16)\
X(CLOUDABI, 17)\
X(OPENVOS,  18)

typedef enum ELF_OsAbiExtension{
#define X(N,C) ELF_OsAbiExtension_##N = C,
  ELF_OsAbiExtension_XList(X)
#undef X
} ELF_OsAbiExtension;

// file type

#define ELF_FileType_XList(X)\
X(NONE,   0)\
X(REL,    1)\
X(EXEC,   2)\
X(DYN,    3)\
X(CORE,   4)\
X(LOOS,   0xfe00)\
X(HIOS,   0xfeff)\
X(LOPROC, 0xff00)\
X(HIPROC, 0xffff)

typedef U16 ELF_FileType;
enum{
#define X(N,C) ELF_FileType_##N = C,
  ELF_FileType_XList(X)
#undef X
};

// machine type

#define ELF_MachineType_XList(X)\
X(NONE,  0)\
X(M32,   1)\
X(SPARC, 2)\
X(386,   3)\
X(68K,   4)\
X(88K,   5)\
X(IAMCU, 6)\
X(860,   7)\
X(MIPS,  8)\
X(S370,  9)\
X(MIPS_RS3_LE, 10)\
X(PARISC, 15)\
X(VPP500, 17)\
X(SPARC32PLUS, 18)\
X(960,   19)\
X(PPC,   20)\
X(PPC64, 21)\
X(S390,  22)\
X(SPU,   23)\
X(V800,  36)\
X(FR20,  37)\
X(RH32,  38)\
X(RCE,   39)\
X(ARM,   40)\
X(ALPHA, 41)\
X(SH,    42)\
X(SPARCV9, 43)\
X(TRICORE, 44)\
X(ARC,     45)\
X(H8_300,  300)\
X(H8S,    48)\
X(H8_500, 49)\
X(IA_64,  50)\
X(MIPS_X, 51)\
X(COLDFIRE, 52)\
X(68HC12, 53)\
X(MMA, 54)\
X(PCP, 55)\
X(NCPU, 56)\
X(NDR1, 57)\
X(STARCORE, 58)\
X(ME16,   59)\
X(ST100,  60)\
X(TINYJ,  61)\
X(X86_64, 62)\
X(PDSP,   63)\
X(PDP10,  64)\
X(PDP11,  65)\
X(FX66,   66)\
X(ST9PLUS, 67)\
X(ST7,    68)\
X(68HC16, 69)\
X(68HC11, 70)\
X(68HC08, 71)\
X(68HC05, 72)\
X(SVX,  73)\
X(ST19, 74)\
X(VAX,  75)\
X(CRIS, 76)\
X(JAVELIN, 77)\
X(FIREPATH, 78)\
X(ZSP,   79)\
X(MMIX,  80)\
X(HUANY, 81)\
X(PRISM, 82)\
X(AVR,   83)\
X(FR30,  84)\
X(D10V,  85)\
X(D30V,  86)\
X(V850,  87)\
X(M32R,  88)\
X(MN10300, 89)\
X(MN10200, 90)\
X(PJ,      91)\
X(OPENRISC, 92)\
X(ARC_COMPACT, 93)\
X(XTENSA, 94)\
X(VIDEOCORE, 95)\
X(TMM_GPP, 96)\
X(NS32K,   97)\
X(TPC,     98)\
X(SNP1K,   99)\
X(ST200,  100)\
X(IP2K,   101)\
X(MAX,    102)\
X(CR,     103)\
X(F2MC16, 104)\
X(MSP430, 105)\
X(BLACKFIN, 106)\
X(SE_C33, 107)\
X(SEP,  108)\
X(ARCA, 109)\
X(UNICORE, 110)\
X(EXCESS, 111)\
X(DXP,    112)\
X(ALTERA_NIOS2, 113)\
X(CRX,   114)\
X(XGATE, 115)\
X(C166,  116)\
X(M16C,  117)\
X(DSPIC30F, 118)\
X(CE,   119)\
X(M32C, 120)\
X(TSK3000, 131)\
X(RS08,   132)\
X(SHARC,  133)\
X(ECOG2,  134)\
X(SCORE7, 135)\
X(DSP24,  136)\
X(VIDEOCORE3, 137)\
X(LATTICEMICO32, 138)\
X(SE_C17,   139)\
X(TI_C6000, 140)\
X(TI_C2000, 141)\
X(TI_C5500, 142)\
X(TI_ARP32, 143)\
X(TI_PRU,   144)\
X(MMDSP_PLUS, 160)\
X(CYPRESS_M8C, 161)\
X(R32C,     162)\
X(TRIMEDIA, 163)\
X(QDSP6, 164)\
X(8051, 165)\
X(STXP7X, 166)\
X(NDS32, 167)\
X(ECOG1, 168)\
X(MAXQ30, 169)\
X(XIMO16, 170)\
X(MANIK, 171)\
X(CRAYNV2, 172)\
X(RX, 173)\
X(METAG, 174)\
X(MCST_ELBRUS, 175)\
X(ECOG16, 176)\
X(CR16, 177)\
X(ETPU, 178)\
X(SLE9X, 179)\
X(L10M, 180)\
X(K10M, 181)\
X(AARCH64, 183)\
X(AVR32, 185)\
X(STM8, 186)\
X(TILE64, 187)\
X(TILEPRO, 188)\
X(MICROBLAZE, 189)\
X(CUDA, 190)\
X(TILEGX, 191)\
X(CLOUDSHIELD, 192)\
X(COREA_1ST, 193)\
X(COREA_2ND, 194)\
X(ARC_COMPACT2, 195)\
X(OPEN8, 196)\
X(RL78, 197)\
X(VIDEOCORE5, 198)\
X(78KOR, 199)\
X(56800EX, 200)\
X(BA1, 201)\
X(BA2, 202)\
X(XCORE, 203)\
X(MCHP_PIC, 204)\
X(INTEL205, 205)\
X(INTEL206, 206)\
X(INTEL207, 207)\
X(INTEL208, 208)\
X(INTEL209, 209)\
X(KM32, 210)\
X(KMX32, 211)\
X(KMX16, 212)\
X(KMX8, 213)\
X(KVARC, 214)\
X(CDP, 215)\
X(COGE, 216)\
X(COOL, 217)\
X(NORC, 218)\
X(CSR_KALIMBA, 219)\
X(Z80, 220)\
X(VISIUM, 221)\
X(FT32, 222)\
X(MOXIE, 223)\
X(AMDGPU, 224)\
X(RISCV, 243)

typedef U16 ELF_MachineType;
enum{
#define X(N,C) ELF_MachineType_##N = C,
  ELF_MachineType_XList(X)
#undef X
};

// elf header

typedef struct ELF_Header32{
  U8  ident[ELF_NUM_IDENT];
  ELF_FileType    type;
  ELF_MachineType machine;
  U32 version;
  U32 entry;
  U32 segment_table_foff;
  U32 section_table_foff;
  U32 flags;
  U16 header_size;
  U16 segment_size;
  U16 segment_count;
  U16 section_size;
  U16 section_count;
  U16 string_section_index;
} ELF_Header32;

typedef struct ELF_Header64{
  U8  ident[ELF_NUM_IDENT];
  ELF_FileType    type;
  ELF_MachineType machine;
  U32 version;
  U64 entry;
  U64 segment_table_foff;
  U64 section_table_foff;
  U32 flags;
  U16 header_size;
  U16 segment_size;
  U16 segment_count;
  U16 section_size;
  U16 section_count;
  U16 string_section_index;
} ELF_Header64;

// elf section type

#define ELF_SectionType_XList(X)\
X(NULL,            0)\
X(PROGBITS,        1)\
X(SYMTAB,          2)\
X(STRTAB,          3)\
X(RELA,            4)\
X(HASH,            5)\
X(DYNAMIC,         6)\
X(NOTE,            7)\
X(NOBITS,          8)\
X(REL,             9)\
X(SHLIB,          10)\
X(DYNSYM,         11)\
X(INIT_ARRAY,     14)\
X(FINI_ARRAY,     15)\
X(PREINIT_ARRAY,  16)\
X(GROUP,          17)\
X(SYMTAB_SHNDX,   18)\
X(LOOS,   0x60000000)\
X(HIOS,   0x6fffffff)\
X(LOPROC, 0x70000000)\
X(HIPROC, 0x7fffffff)\
X(LOUSER, 0x80000000)\
X(HIUSER, 0xffffffff)

typedef U32 ELF_SectionType;
enum{
#define X(N,C) ELF_SectionType_##N = C,
  ELF_SectionType_XList(X)
#undef X
};

// elf section flags

#define ELF_SectionFlags_XList(X)\
X(WRITE,              0x1)\
X(ALLOC,              0x2)\
X(EXECINSTR,          0x4)\
X(MERGE,             0x10)\
X(STRINGS,           0x20)\
X(INFO_LINK,         0x40)\
X(LINK_ORDER,        0x80)\
X(OS_NONCONFORMING, 0x100)\
X(GROUP,            0x200)\
X(TLS,              0x400)\
X(COMPRESSED,       0x800)\
X(MASKOS,      0x0ff00000)\
X(MASKPROC,    0xf0000000)

enum{
#define X(N,C) ELF_SectionFlag_##N = C,
  ELF_SectionFlags_XList(X)
#undef X
};

// elf section indexes

#define ELF_SectionIndex_XList(X)\
X(UNDEF,          0)\
X(LORESERVE, 0xff00)\
X(LOPROC,    0xff00)\
X(HIPROC,    0xff1f)\
X(LOOS,      0xff20)\
X(HIOS,      0xff3f)\
X(ABS,       0xfff1)\
X(COMMON,    0xfff2)\
X(XINDEX,    0xffff)\
X(HIRESERVE, 0xffff)

typedef enum ELF_SectionIndex{
#define X(N,C) ELF_SectionIndex_##N = C,
  ELF_SectionIndex_XList(X)
#undef X
} ELF_SectionIndex;


// elf section

typedef struct ELF_Section32{
	U32 name;
  ELF_SectionType type;
	U32 flags;
  U32 addr;
  U32 offset;
	U32 size;
	U32 link;
	U32 info;
	U32 addralign;
	U32 entsize;
} ELF_Section32;

typedef struct ELF_Section64{
	U32 name;
  ELF_SectionType type;
	U64 flags;
  U64 addr;
  U64 offset;
	U64 size;
	U32 link;
	U32 info;
	U64 addralign;
	U64 entsize;
} ELF_Section64;

// elf compression type

#define ELF_CompressionType_XList(X)\
X(ZLIB,   1)\
X(LOOS,   0x60000000)\
X(HIOS,   0x6fffffff)\
X(LOPROC, 0x70000000)\
X(HIPROC, 0x7fffffff)

typedef enum ELF_CompressionType{
#define X(N,C) ELF_CompressionType_##N = C,
  ELF_CompressionType_XList(X)
#undef X
} ELF_CompressionType;

// elf compression header

typedef struct ELF_CompressionHeader32{
  ELF_CompressionType type;
	U32 size;
	U32 addralign;
} ELF_CompressionHeader32;

typedef struct ELF_CompressionHeader64{
  ELF_CompressionType type;
	U32 reserved;
  U64 size;
  U64 addralign;
} ELF_CompressionHeader64;

// elf section group flags

#define ELF_SectionGroupFlags_XList(X)\
X(COMDAT,          0x1)\
X(MASKOS,   0x0ff00000)\
X(MASKPROC, 0xf0000000)

typedef U32 ELF_SectionGroupFlags;
enum{
#define X(N,C) ELF_SectionGroupFlag_##N = C,
  ELF_SectionGroupFlags_XList(X)
#undef X
};

// elf symbol

typedef struct ELF_Symbol32{
  U32 name;
  U32 value;
  U32 size;
  U8  info;
  U8  other;
  U16 section_index;
} ELF_Symbol32;

typedef struct ELF_Symbol64{
  U32 name;
  U8  info;
  U8  other;
  U16 section_index;
  U64 value;
  U64 size;
} ELF_Symbol64;

#define ELF_Symbol_BindFromInfo(inf) ((inf)>>4)
#define ELF_Symbol_TypeFromInfo(inf) ((inf)&0xF)
#define ELF_Symbol_InfoFromBindType(bin,typ) (((bin)<<4)|((type)&0xF))

#define ELF_Symbol_VisFromOther(oth) ((oth)&0x3)
#define ELF_Symbol_OtherFromVis(vis) (((vis)&0x3))

// elf symbol binding

#define ELF_SymbolBinding_XList(X)\
X(LOCAL,   0)\
X(GLOBAL,  1)\
X(WEAK,    2)\
X(LOOS,   10)\
X(HIOS,   12)\
X(LOPROC, 13)\
X(HIPROC, 15)

typedef enum ELF_SymbolBinding{
#define X(N,C) ELF_SymbolBinding_##N = C,
  ELF_SymbolBinding_XList(X)
#undef X
} ELF_SymbolBinding;

// elf symbol types

#define ELF_SymbolType_XList(X)\
X(NOTYPE,  0)\
X(OBJECT,  1)\
X(FUNC,    2)\
X(SECTION, 3)\
X(FILE,    4)\
X(COMMON,  5)\
X(TLS,     6)\
X(LOOS,   10)\
X(HIOS,   12)\
X(LOPROC, 13)\
X(HIPROC, 15)

typedef enum ELF_SymbolType{
#define X(N,C) ELF_SymbolType_##N = C,
  ELF_SymbolType_XList(X)
#undef X
} ELF_SymbolType;

// elf symbol visibility

#define ELF_SymbolVis_XList(X)\
X(DEFAULT,   0)\
X(INTERNAL,  1)\
X(HIDDEN,    2)\
X(PROTECTED, 3)

typedef enum ELF_SymbolVis{
#define X(N,C) ELF_SymbolVis_##N = C,
  ELF_SymbolVis_XList(X)
#undef X
} ELF_SymbolVis;

// elf relocations

typedef struct ELF_Relocation32{
  U32 offset;
  U32 info;
} ELF_Relocation32;

typedef struct ELF_RelocationAdd32{
  U32 offset;
  U32 info;
	S32 addend;
} ELF_RelocationAdd32;

typedef struct ELF_Relocation64{
  U64 offset;
  U64 info;
} ELF_Relocation64;

typedef struct ELF_RelocationAdd64{
  U64 offset;
  U64 info;
	S64 addend;
} ELF_RelocationAdd64;

// elf segment type

#define ELF_SegmentType_XList(X)\
X(NULL,    0)\
X(LOAD,    1)\
X(DYNAMIC, 2)\
X(INTERP,  3)\
X(NOTE,    4)\
X(SHLIB,   5)\
X(PHDR,    6)\
X(TLS,     7)\
X(LOOS,    0x60000000)\
X(HIOS,    0x6fffffff)\
X(LOPROC,  0x70000000)\
X(HIPROC,  0x7fffffff)

typedef U32 ELF_SegmentType;
enum{
#define X(N,C) ELF_SegmentType_##N = C,
  ELF_SegmentType_XList(X)
#undef X
};

// elf segment flags

#define ELF_SegmentFlags_XList(X)\
X(X, 0x1)\
X(W, 0x2)\
X(R, 0x4)\
X(MASKOS,   0x0ff00000)\
X(MASKPROC, 0xf0000000)\

typedef U32 ELF_SegmentFlags;
enum{
#define X(N,C) ELF_SegmentFlag_##N = C,
  ELF_SegmentFlags_XList(X)
#undef X
};

// elf segment

typedef struct ELF_Segment32{
  ELF_SegmentType type;
  U32 offset;
  U32 vaddr;
  U32 paddr;
  U32 file_size;
  U32 memory_size;
  ELF_SegmentFlags flags;
  U32 align;
} ELF_Segment32;

typedef struct ELF_Segment64{
  ELF_SegmentType type;
  ELF_SegmentFlags flags;
  U64 offset;
  U64 vaddr;
  U64 paddr;
  U64 file_size;
  U64 memory_size;
  U64 align;
} ELF_Segment64;

////////////////////////////////
// Functions: ELF

// normalizing

MR4TH_SYMBOL_STATIC U16 elf_u16_decode(void *ptr, B32 flip);
MR4TH_SYMBOL_STATIC U32 elf_u32_decode(void *ptr, B32 flip);
MR4TH_SYMBOL_STATIC U64 elf_u64_decode(void *ptr, B32 flip);

MR4TH_SYMBOL_STATIC void elf_header64_from_header32(ELF_Header64 *dst, ELF_Header32 *src, ELF_Encoding src_encoding);
MR4TH_SYMBOL_STATIC void elf_header64_from_header64(ELF_Header64 *dst, ELF_Header64 *src, ELF_Encoding src_encoding);

MR4TH_SYMBOL_STATIC void elf_section64_from_section32(ELF_Section64 *dst, ELF_Section32 *src, ELF_Encoding src_encoding);
MR4TH_SYMBOL_STATIC void elf_section64_from_section64(ELF_Section64 *dst, ELF_Section64 *src, ELF_Encoding src_encoding);

MR4TH_SYMBOL_STATIC void elf_segment64_from_segment32(ELF_Segment64 *dst, ELF_Segment32 *src, ELF_Encoding src_encoding);
MR4TH_SYMBOL_STATIC void elf_segment64_from_segment64(ELF_Segment64 *dst, ELF_Segment64 *src, ELF_Encoding src_encoding);

MR4TH_SYMBOL_STATIC void elf_symbol64_from_symbol32(ELF_Symbol64 *dst, ELF_Symbol32 *src, ELF_Encoding src_encoding);
MR4TH_SYMBOL_STATIC void elf_symbol64_from_symbol64(ELF_Symbol64 *dst, ELF_Symbol64 *src, ELF_Encoding src_encoding);

MR4TH_SYMBOL_STATIC void elf_reloca64_from_reloc32(ELF_RelocationAdd64 *dst, ELF_Relocation32 *src, ELF_Encoding src_encoding);
MR4TH_SYMBOL_STATIC void elf_reloca64_from_reloc64(ELF_RelocationAdd64 *dst, ELF_Relocation64 *src, ELF_Encoding src_encoding);
MR4TH_SYMBOL_STATIC void elf_reloca64_from_reloca32(ELF_RelocationAdd64 *dst, ELF_RelocationAdd32 *src, ELF_Encoding src_encoding);
MR4TH_SYMBOL_STATIC void elf_reloca64_from_reloca64(ELF_RelocationAdd64 *dst, ELF_RelocationAdd64 *src, ELF_Encoding src_encoding);

// strings

MR4TH_SYMBOL_STATIC String8 elf_str8_from_identification_idx(ELF_IdentificationIdx idx);
MR4TH_SYMBOL_STATIC String8 elf_str8_from_class(ELF_Class elf_class);
MR4TH_SYMBOL_STATIC String8 elf_str8_from_encoding(ELF_Encoding elf_encoding);
MR4TH_SYMBOL_STATIC String8 elf_str8_from_osabi(ELF_OsAbiExtension elf_osabi);
MR4TH_SYMBOL_STATIC String8 elf_str8_from_file_type(ELF_FileType file_type);
MR4TH_SYMBOL_STATIC String8 elf_str8_from_machine_type(ELF_FileType machine_type);

MR4TH_SYMBOL_STATIC String8 elf_str8_from_section_type(ELF_SectionType section_type);
MR4TH_SYMBOL_STATIC void    elf_str8list_from_section_flags(Arena *arena, String8List *out, U32 flags);
MR4TH_SYMBOL_STATIC String8 elf_str8_from_section_flags(Arena *arena, U32 flags);

MR4TH_SYMBOL_STATIC String8 elf_str8_from_segment_type(ELF_SegmentType segment_type);
MR4TH_SYMBOL_STATIC void    elf_str8list_from_segment_flags(Arena *arena, String8List *out, ELF_SegmentFlags flags);
MR4TH_SYMBOL_STATIC String8 elf_str8_from_segment_flags(Arena *arena, ELF_SegmentFlags flags);

MR4TH_SYMBOL_STATIC String8 elf_str8_from_symbol_binding(ELF_SymbolBinding bind);
MR4TH_SYMBOL_STATIC String8 elf_str8_from_symbol_type(ELF_SymbolType type);
MR4TH_SYMBOL_STATIC String8 elf_str8_from_symbol_vis(ELF_SymbolVis vis);

#endif //MR4TH_ELF_H
