///                MentOS, The Mentoring Operating system project
/// @file elf.h
/// @brief Function for managing the Executable and Linkable Format (ELF).
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.

#pragma once

#include "process/process.h"
#include "stdint.h"

/// @defgroup header_segment_types Program Header Segment Types
/// @brief List of numeric defines which identify segment entries types.
/// @{

/// @brief Unused.
#define PT_NULL 0
/// @brief Specifies a loadable segment, described by p_filesz and p_memsz.
/// The bytes from the file are mapped to the beginning of the memory segment.
/// If the segment's memory size (p_memsz) is larger than the file
/// size (p_filesz), the extra bytes are defined to hold the value 0
/// and to follow the segment's initialized area. The file size can not
/// be larger than the memory size. Loadable segment entries in the program
/// header table appear in ascending order, sorted on the p_vaddr member.
#define PT_LOAD 1
/// @brief Specifies dynamic linking information.
#define PT_DYNAMIC 2
/// @brief Specifies the location and size of a null-terminated path name
/// to invoke as an interpreter. This segment type is mandatory for dynamic
/// executable files and can occur in shared objects. It cannot occur more
/// than once in a file. This type, if present, it must precede any loadable
/// segment entry.
#define PT_INTERP 3
/// @brief Specifies the location and size of auxiliary information.
#define PT_NOTE 4
/// @brief Reserved but has unspecified semantics.
#define PT_SHLIB 5
/// @brief Specifies the location and size of the program header table
/// itself, both in the file and in the memory image of the program.
/// This segment type cannot occur more than once in a file. Moreover,
/// it can occur only if the program header table is part of the memory
/// image of the program. This type, if present, must precede any loadable
/// segment entry.
#define PT_PHDR 6
/// @brief Section for supporting exception handling routines.
/// @details
/// The .eh_frame section has the same structure with .debug_frame,
/// which follows DWARF format. It represents the table that describes
/// how to set registers to restore the previous call frame at runtime.
#define PT_EH_FRAME 0x6474E550
/// @brief Is a program header which tells the system how to control
/// the stack when the ELF is loaded into memory.
#define PT_GNU_STACK 0x6474E551
/// @brief This segment indicates the memory region which should be made
/// Read-Only after relocation is done. This segment usually appears in a
/// dynamic link library and it contains .ctors, .dtors, .dynamic, .got s
/// ections. See paragraph below.
#define PT_GNU_RELRO 0x6474E552
/// @brief TODO: Document.
#define PT_LOPROC 0x70000000
/// @brief TODO: Document.
#define PT_HIPROC 0x7FFFFFFF

/// @}

/// Elf header ident size.
#define EI_NIDENT 16

/// @brief The elf starting section.
typedef struct elf_header {
    /// Elf header identity bits.
    uint8_t ident[EI_NIDENT];
    /// Identifies object file type.
    uint16_t type;
    /// Specifies target instruction set architecture.
    uint16_t machine;
    /// Set to 1 for the original version of ELF.
    uint32_t version;
    /// This is the memory address of the entry point from where the process starts executing.
    uint32_t entry;
    /// Points to the start of the program header table.
    uint32_t phoff;
    /// TODO: Comment.
    uint32_t shoff;
    /// TODO: Comment.
    uint32_t flags;
    /// TODO: Comment.
    uint16_t ehsize;
    /// TODO: Comment.
    uint16_t phentsize;
    /// TODO: Comment.
    uint16_t phnum;
    /// TODO: Comment.
    uint16_t shentsize;
    /// TODO: Comment.
    uint16_t shnum;
    /// TODO: Comment.
    uint16_t shstrndx;
} elf_header_t;

/// @brief The elf program header, holding program layout information
typedef struct elf_program_header {
    /// Identifies the type of the segment.
    uint32_t type;
    /// Offset of the segment in the file image.
    uint32_t offset;
    /// Virtual address of the segment in memory.
    uint32_t vaddr;
    /// On systems where physical address is relevant, reserved for
    /// segment's physical address.
    uint32_t paddr;
    /// Size in bytes of the segment in the file image. May be 0.
    uint32_t filesz;
    /// Size in bytes of the segment in memory. May be 0.
    uint32_t memsz;
    /// Segment-dependent flags.
    uint32_t flags;
    /// Values of 0 or 1 specify no alignment. Otherwise should be a positive,
    /// integral power of 2, with p_vaddr equating p_offset modulus p_align.
    uint32_t align;
} elf_program_header_t;

/// @brief A section header with all kinds of useful information
typedef struct elf_section_header {
    /// TODO: Comment.
    uint32_t name;
    /// TODO: Comment.
    uint32_t type;
    /// TODO: Comment.
    uint32_t flags;
    /// TODO: Comment.
    uint32_t addr;
    /// TODO: Comment.
    uint32_t offset;
    /// TODO: Comment.
    uint32_t size;
    /// TODO: Comment.
    uint32_t link;
    /// TODO: Comment.
    uint32_t info;
    /// TODO: Comment.
    uint32_t addralign;
    /// TODO: Comment.
    uint32_t entsize;
} elf_section_header_t;

/// @brief A symbol itself.
typedef struct elf_symbol {
    /// TODO: Comment.
    uint32_t name;
    /// TODO: Comment.
    uint32_t value;
    /// TODO: Comment.
    uint32_t size;
    /// TODO: Comment.
    uint8_t info;
    /// TODO: Comment.
    uint8_t other;
    /// TODO: Comment.
    uint16_t ndx;
} elf_symbol_t;

/// @brief Holds information about relocation object (that do not need an addend).
typedef struct elf_rel_t {
    /// TODO: Comment.
    uint32_t r_offset;
    /// TODO: Comment.
    uint32_t r_info;
} elf_rel_t;

/// @brief Holds information about relocation object (that need an addend).
typedef struct elf_rela_t {
    /// TODO: Comment.
    uint32_t r_offset;
    /// TODO: Comment.
    uint32_t r_info;
    /// TODO: Comment.
    int32_t r_addend;
} elf_rela_t;

/// @brief Fields index of ELF_IDENT.
enum Elf_Ident {
    EI_MAG0       = 0, ///< 0x7F
    EI_MAG1       = 1, ///< 'E'
    EI_MAG2       = 2, ///< 'L'
    EI_MAG3       = 3, ///< 'F'
    EI_CLASS      = 4, ///< Architecture (32/64)
    EI_DATA       = 5, ///< Set to either 1 or 2 to signify little or big endianness.
    EI_VERSION    = 6, ///< ELF Version
    EI_OSABI      = 7, ///< OS Specific
    EI_ABIVERSION = 8, ///< OS Specific
    EI_PAD        = 9  ///< Padding
};

#define ELFMAG0 0x7F ///< e_ident[EI_MAG0]
#define ELFMAG1 'E' ///< e_ident[EI_MAG1]
#define ELFMAG2 'L' ///< e_ident[EI_MAG2]
#define ELFMAG3 'F' ///< e_ident[EI_MAG3]

#define ELFDATA2LSB 1 ///< Little Endian
#define ELFCLASS32  1 ///< 32-bit Architecture

/// @brief Type of ELF files.
typedef enum Elf_Type {
    ET_NONE = 0, ///< Unkown Type
    ET_REL  = 1, ///< Relocatable File
    ET_EXEC = 2  ///< Executable File
} Elf_Type;

#define EM_386     3 ///< x86 Machine Type.
#define EV_CURRENT 1 ///< ELF Current Version.

/// @brief Defines a number of different types of sections, which correspond
/// to values stored in the field sh_type in the section header
typedef enum ShT_Types {
    SHT_NULL     = 0, ///< Null section
    SHT_PROGBITS = 1, ///< Program information
    SHT_SYMTAB   = 2, ///< Symbol table
    SHT_STRTAB   = 3, ///< String table
    SHT_RELA     = 4, ///< Relocation (w/ addend)
    SHT_NOBITS   = 8, ///< Not present in file
    SHT_REL      = 9, ///< Relocation (no addend)
} ShT_Types;

/// @brief ShT_Attributes corresponds to the field sh_flags, but are bit
/// flags rather than stand-alone values.
enum ShT_Attributes {
    SHF_WRITE = 0x01, ///< Writable section
    SHF_ALLOC = 0x02  ///< Exists in memory
};

/// @brief Provide access to teh symbol biding.
#define ELF32_ST_BIND(INFO) ((INFO) >> 4)
/// @brief Provide access to teh symbol type.
#define ELF32_ST_TYPE(INFO) ((INFO)&0x0F)

/// @brief Provides possible symbol bindings.
enum StT_Bindings {
    STB_LOCAL  = 0, ///< Local scope
    STB_GLOBAL = 1, ///< Global scope
    STB_WEAK   = 2  ///< Weak, (ie. __attribute__((weak)))
};

/// @brief Provides a number of possible symbol types.
enum StT_Types {
    STT_NOTYPE = 0, ///< No type
    STT_OBJECT = 1, ///< Variables, arrays, etc.
    STT_FUNC   = 2  ///< Methods or functions
};

/// @brief Loads an ELF file into the memory of task.
/// @param task  The task for which we load the ELF.
/// @param file  The ELF file.
/// @param entry The ELF binary entry.
/// @return 0 if fails, 1 if succeed.
int elf_load_file(task_struct *task, vfs_file_t *file, uint32_t *entry);

/// @brief Checks if the file is a valid ELF.
/// @param file The file to check.
/// @param type The type of ELF file we expect.
/// @return 0 if fails, 1 if succeed.
int elf_check_file_type(vfs_file_t *file, Elf_Type type);

/// @brief Checks the correctness of the ELF header.
/// @param hdr The header to check.
/// @return 0 if fails, 1 if succeed.
int elf_check_file_header(elf_header_t *hdr);

/// @brief Checks the correctness of the ELF header magic number.
/// @param hdr The header to check.
/// @return 0 if fails, 1 if succeed.
int elf_check_magic_number(elf_header_t *hdr);

/// @brief Transforms the passed ELF type to string.
/// @param type The integer representing the ELF type.
/// @return The string representing the ELF type.
const char *elf_type_to_string(int type);

/// @brief Transforms the passed ELF section header type to string.
/// @param type The integer representing the ELF section header type.
/// @return The string representing the ELF section header type.
const char *elf_section_header_type_to_string(int type);

/// @brief Transforms the passed ELF symbol type to string.
/// @param type The integer representing the ELF symbol type.
/// @return The string representing the ELF symbol type.
const char *elf_symbol_type_to_string(int type);

/// @brief Transforms the passed ELF symbol bind to string.
/// @param bind The integer representing the ELF symbol bind.
/// @return The string representing the ELF symbol bind.
const char *elf_symbol_bind_to_string(int bind);