/*
 * Copyright (c) 2023 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 */
#ifndef ZEPHYR_LLEXT_ELF_H
#define ZEPHYR_LLEXT_ELF_H

#include <stdint.h>

/**
 * @brief ELF types and parsing
 *
 * Reference documents can be found here https://refspecs.linuxfoundation.org/elf/
 *
 * @defgroup elf ELF data types and defines
 * @ingroup llext
 * @{
 */

#ifdef __cplusplus
extern "C" {
#endif

/** Unsigned program address */
typedef uint32_t elf32_addr;
/** Unsigned medium integer */
typedef uint16_t elf32_half;
/** Unsigned file offset */
typedef uint32_t elf32_off;
/** Signed integer */
typedef int32_t elf32_sword;
/** Unsigned integer */
typedef uint32_t elf32_word;

/** Unsigned program address */
typedef uint64_t elf64_addr;
/** Unsigned medium integer */
typedef uint16_t elf64_half;
/** Unsigned file offset */
typedef uint64_t elf64_off;
/** Signed integer */
typedef int32_t elf64_sword;
/** Unsigned integer */
typedef uint32_t elf64_word;
/** Signed long integer */
typedef int64_t elf64_sxword;
/** Unsigned long integer */
typedef uint64_t elf64_xword;


/**
 * @brief ELF identifier block
 *
 * 4 byte magic (.ELF)
 * 1 byte class (Invalid, 32 bit, 64 bit)
 * 1 byte endianness (Invalid, LSB, MSB)
 * 1 byte version (1)
 * 1 byte OS ABI (0 None, 1 HP-UX, 2 NetBSD, 3 Linux)
 * 1 byte ABI (0)
 * 7 bytes padding
 */
#define EI_NIDENT 16

/**
 * @brief ELF Header(32-bit)
 */
struct elf32_ehdr {
	/** Magic string identifying ELF binary */
	unsigned char e_ident[EI_NIDENT];
	/** Type of ELF */
	elf32_half e_type;
	/** Machine type */
	elf32_half e_machine;
	/** Object file version */
	elf32_word e_version;
	/** Virtual address of entry */
	elf32_addr e_entry;
	/** Program header table offset */
	elf32_off e_phoff;
	/** Section header table offset */
	elf32_off e_shoff;
	/** Processor specific flags */
	elf32_word e_flags;
	/** ELF header size */
	elf32_half e_ehsize;
	/** Program header count */
	elf32_half e_phentsize;
	/** Program header count */
	elf32_half e_phnum;
	/** Section header size */
	elf32_half e_shentsize;
	/** Section header count */
	elf32_half e_shnum;
	/** Section header containing section header string table */
	elf32_half e_shstrndx;
};

/**
 * @brief ELF Header(64-bit)
 */
struct elf64_ehdr {
	/** Magic string identifying ELF binary */
	unsigned char e_ident[EI_NIDENT];
	/** Type of ELF */
	elf64_half e_type;
	/** Machine type */
	elf64_half e_machine;
	/** Object file version */
	elf64_word e_version;
	/** Virtual address of entry */
	elf64_addr e_entry;
	/** Program header table offset */
	elf64_off e_phoff;
	/** Section header table offset */
	elf64_off e_shoff;
	/** Processor specific flags */
	elf64_word e_flags;
	/** ELF header size */
	elf64_half e_ehsize;
	/** Program header size */
	elf64_half e_phentsize;
	/** Program header count */
	elf64_half e_phnum;
	/** Section header size */
	elf64_half e_shentsize;
	/** Section header count */
	elf64_half e_shnum;
	/** Section header containing section header string table */
	elf64_half e_shstrndx;
};

/** Relocatable (unlinked) ELF */
#define ET_REL  1

/** Executable (without PIC/PIE) ELF */
#define ET_EXEC 2

/** Dynamic (executable with PIC/PIE or shared lib) ELF */
#define ET_DYN  3

/** Core Dump */
#define ET_CORE 4

/**
 * @brief Section Header(32-bit)
 */
struct elf32_shdr {
	/** Section header name index in section header string table */
	elf32_word sh_name;
	/** Section type */
	elf32_word sh_type;
	/** Section header attributes */
	elf32_word sh_flags;
	/** Address of section in the image */
	elf32_addr sh_addr;
	/** Location of section in the ELF binary in bytes */
	elf32_off sh_offset;
	/** Section size in bytes */
	elf32_word sh_size;
	/** Section header table link index, depends on section type */
	elf32_word sh_link;
	/** Section info, depends on section type */
	elf32_word sh_info;
	/** Section address alignment */
	elf32_word sh_addralign;
	/** Section contains table of fixed size entries sh_entsize bytes large */
	elf32_word sh_entsize;
};

/**
 * @brief Section Header(64-bit)
 */
struct elf64_shdr {
	/** Section header name index in section header string table */
	elf64_word sh_name;
	/** Section type */
	elf64_word sh_type;
	/** Section header attributes */
	elf64_xword sh_flags;
	/** Address of section in the image */
	elf64_addr sh_addr;
	/** Location of section in the ELF binary in bytes */
	elf64_off sh_offset;
	/** Section size in bytes */
	elf64_xword sh_size;
	/** Section header table link index, depends on section type */
	elf64_word sh_link;
	/** Section info, depends on section type */
	elf64_word sh_info;
	/** Section address alignment */
	elf64_xword sh_addralign;
	/** Section contains table of fixed size entries sh_entsize bytes large */
	elf64_xword sh_entsize;
};

#define SHT_PROGBITS 0x1
#define SHT_SYMTAB 0x2
#define SHT_STRTAB 0x3
#define SHT_RELA 0x4
#define SHT_NOBITS 0x8
#define SHT_REL 0x9
#define SHT_DYNSYM 0xB

#define SHF_WRITE 0x1
#define SHF_ALLOC 0x2
#define SHF_EXECINSTR 0x4

/**
 * @brief Symbol table entry(32-bit)
 */
struct elf32_sym {
	/** Name of the symbol as an index into the symbol string table */
	elf32_word st_name;
	/** Value or location of the symbol */
	elf32_addr st_value;
	/** Size of the symbol */
	elf32_word st_size;
	/** Symbol binding and type information */
	unsigned char st_info;
	/** Symbol visibility */
	unsigned char st_other;
	/** Symbols related section given by section header index */
	elf32_half st_shndx;
};

/**
 * @brief Symbol table entry(64-bit)
 */
struct elf64_sym {
	/** Name of the symbol as an index into the symbol string table */
	elf64_word st_name;
	/** Symbol binding and type information */
	unsigned char st_info;
	/** Symbol visibility */
	unsigned char st_other;
	/** Symbols related section given by section header index */
	elf64_half st_shndx;
	/** Value or location of the symbol */
	elf64_addr st_value;
	/** Size of the symbol */
	elf64_xword st_size;
};

#define SHN_UNDEF 0
#define SHN_ABS 0xfff1
#define SHN_COMMON 0xfff2

#define STT_NOTYPE 0
#define STT_OBJECT 1
#define STT_FUNC 2
#define STT_SECTION 3
#define STT_FILE 4
#define STT_COMMON 5
#define STT_LOOS 10
#define STT_HIOS 12
#define STT_LOPROC 13
#define STT_HIPROC 15

#define STB_LOCAL 0
#define STB_GLOBAL 1
#define STB_WEAK 2
#define STB_LOOS 10
#define STB_HIOS 12
#define STB_LOPROC 13
#define STB_HIPROC 15

/**
 * @brief Symbol binding from 32bit st_info
 *
 * @param i Value of st_info
 */
#define ELF32_ST_BIND(i) ((i) >> 4)

/**
 * @brief Symbol type from 32bit st_info
 *
 * @param i Value of st_info
 */
#define ELF32_ST_TYPE(i) ((i) & 0xf)

/**
 * @brief Symbol binding from 32bit st_info
 *
 * @param i Value of st_info
 */
#define ELF64_ST_BIND(i) ((i) >> 4)


/**
 * @brief Symbol type from 32bit st_info
 *
 * @param i Value of st_info
 */
#define ELF64_ST_TYPE(i) ((i) & 0xf)

/**
 * @brief Relocation entry for 32-bit ELFs
 */
struct elf32_rel {
	/** Offset in the section to perform a relocation */
	elf32_addr r_offset;

	/** Information about the relocation, related symbol and type */
	elf32_word r_info;
};

struct elf32_rela {
	elf32_addr r_offset;
	elf32_word r_info;
	elf32_sword r_addend;
};

/**
 * @brief Relocation symbol index from r_info
 *
 * @param i Value of r_info
 */
#define ELF32_R_SYM(i) ((i) >> 8)

/**
 * @brief Relocation type from r_info
 *
 * @param i Value of r_info
 */
#define ELF32_R_TYPE(i) ((i) & 0xff)

/**
 * @brief Relocation entry for 64-bit ELFs
 */
struct elf64_rel {
	/** Offset in section to perform a relocation */
	elf64_addr r_offset;
	/** Information about relocation, related symbol and type */
	elf64_xword r_info;
};

struct elf64_rela {
	elf64_addr r_offset;
	elf64_xword r_info;
	elf64_sxword r_addend;
};

/** @brief Relocation symbol from r_info
 *
 * @param i Value of r_info
 */
#define ELF64_R_SYM(i) ((i) >> 32)

/**
 * @brief Relocation type from r_info
 *
 * @param i Value of r_info
 */
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)

#define R_386_NONE 0
#define R_386_32 1
#define R_386_PC32 2
#define R_386_GOT32 3
#define R_386_PLT32 4
#define R_386_COPY 5
#define R_386_GLOB_DAT 6
#define R_386_JMP_SLOT 7
#define R_386_RELATIVE 8
#define R_386_GOTOFF 9

#define R_ARM_NONE 0
#define R_ARM_PC24 1
#define R_ARM_ABS32 2
#define R_ARM_REL32 3
#define R_ARM_COPY 4
#define R_ARM_THM_CALL 10
#define R_ARM_CALL 28
#define R_ARM_V4BX 40

#define R_XTENSA_NONE 0
#define R_XTENSA_32 1
#define R_XTENSA_SLOT0_OP 20

/**
 * @brief Program header(32-bit)
 */
struct elf32_phdr {
	elf32_word p_type;
	elf32_off p_offset;
	elf32_addr p_vaddr;
	elf32_addr p_paddr;
	elf32_word p_filesz;
	elf32_word p_memsz;
	elf32_word p_flags;
	elf32_word p_align;
};

/**
 * @brief Program header(64-bit)
 */
struct elf64_phdr {
	elf64_word p_type;
	elf64_off p_offset;
	elf64_addr p_vaddr;
	elf64_addr p_paddr;
	elf64_xword p_filesz;
	elf64_xword p_memsz;
	elf64_word p_flags;
	elf64_xword p_align;
};

/**
 * @brief Program segment type
 */
#define PT_LOAD 1

/**
 * @brief Dynamic section entry(32-bit)
 */
struct elf32_dyn {
	elf32_sword d_tag;
	union {
		elf32_word d_val;
		elf32_addr d_ptr;
	} d_un;
};

/**
 * @brief Dynamic section entry(64-bit)
 */
struct elf64_dyn {
	elf64_sxword d_tag;
	union {
		elf64_xword d_val;
		elf64_addr d_ptr;
	} d_un;
};

#if defined(CONFIG_64BIT) || defined(__DOXYGEN__)
/** Machine sized elf header structure */
typedef struct elf64_ehdr elf_ehdr_t;
/** Machine sized section header structure */
typedef struct elf64_shdr elf_shdr_t;
/** Machine sized program header structure */
typedef struct elf64_phdr elf_phdr_t;
/** Machine sized program address */
typedef elf64_addr elf_addr;
/** Machine sized small integer */
typedef elf64_half elf_half;
/** Machine sized integer */
typedef elf64_xword elf_word;
/** Machine sized relocation struct */
typedef struct elf64_rel elf_rel_t;
typedef struct elf64_rela elf_rela_t;
/** Machine sized symbol struct */
typedef struct elf64_sym elf_sym_t;
/** Machine sized macro alias for obtaining a relocation symbol */
#define ELF_R_SYM ELF64_R_SYM
/** Machine sized macro alias for obtaining a relocation type */
#define ELF_R_TYPE ELF64_R_TYPE
/** Machine sized macro alias for obtaining a symbol bind */
#define ELF_ST_BIND ELF64_ST_BIND
/** Machine sized macro alias for obtaining a symbol type */
#define ELF_ST_TYPE ELF64_ST_TYPE
#else
/** Machine sized elf header structure */
typedef struct elf32_ehdr elf_ehdr_t;
/** Machine sized section header structure */
typedef struct elf32_shdr elf_shdr_t;
/** Machine sized program header structure */
typedef struct elf32_phdr elf_phdr_t;
/** Machine sized program address */
typedef elf32_addr elf_addr;
/** Machine sized small integer */
typedef elf32_half elf_half;
/** Machine sized integer */
typedef elf32_word elf_word;
/** Machine sized relocation struct */
typedef struct elf32_rel elf_rel_t;
typedef struct elf32_rela elf_rela_t;
/** Machine sized symbol struct */
typedef struct elf32_sym elf_sym_t;
/** Machine sized macro alias for obtaining a relocation symbol */
#define ELF_R_SYM ELF32_R_SYM
/** Machine sized macro alias for obtaining a relocation type */
#define ELF_R_TYPE ELF32_R_TYPE
/** Machine sized macro alias for obtaining a symbol bind */
#define ELF_ST_BIND ELF32_ST_BIND
/** Machine sized macro alias for obtaining a symbol type */
#define ELF_ST_TYPE ELF32_ST_TYPE
#endif

#ifdef __cplusplus
}
#endif

/**
 * @}
 */

#endif /* ZEPHYR_LLEXT_ELF_H */
