#ifndef _ELF_LOADER_H_
#define _ELF_LOADER_H_

#if defined(__linux__)

#include "utils.h"
#include <elf.h>
#include <stdint.h>

#if !defined(PAGE_SIZE)
#define PAGE_SIZE 4096
#endif

#if !defined(OS_FREEBSD)
//
// FreeBSD has this already defined
//
#if INTPTR_MAX == INT32_MAX
typedef Elf32_Ehdr Elf_Ehdr;
typedef Elf32_Phdr Elf_Phdr;
typedef Elf32_Sym Elf_Sym;
typedef Elf32_Shdr Elf_Shdr;
typedef Elf32_Rel Elf_Rel;
typedef Elf32_Rela Elf_Rela;
typedef Elf32_Dyn Elf_Dyn;
typedef Elf32_Word Elf_Word;
#define ELF_R_SYM(x) ELF32_R_SYM(x)
#define ELF_R_TYPE(x) ELF32_R_TYPE(x)
#elif INTPTR_MAX == INT64_MAX
typedef Elf64_Ehdr Elf_Ehdr;
typedef Elf64_Phdr Elf_Phdr;
typedef Elf64_Sym Elf_Sym;
typedef Elf64_Shdr Elf_Shdr;
typedef Elf64_Rel Elf_Rel;
typedef Elf64_Rela Elf_Rela;
typedef Elf64_Dyn Elf_Dyn;
typedef Elf64_Word Elf_Word;
#define ELF_R_SYM(x) ELF64_R_SYM(x)
#define ELF_R_TYPE(x) ELF64_R_TYPE(x)
#else
#error ("Are you a wizard?")
#endif
#endif

#define STACK_SIZE (8 * 1024 * 1024)
#define STACK_STORAGE_SIZE 0x5000
#define STACK_STRING_SIZE 0x5000

#define ROUND_UP(v, s) ((v + s - 1) & -s)
#define ROUND_DOWN(v, s) (v & -s)

// Not all archs have these defined
#ifndef AT_RANDOM
#define AT_RANDOM 25
#endif
#ifndef AT_PLATFORM
#define AT_PLATFORM 15
#endif
#ifndef AT_SECURE
#define AT_SECURE 23
#endif

#ifndef AT_CLKTCK
#define AT_CLKTCK 17
#endif
#ifndef AT_HWCAP
#define AT_HWCAP 16
#endif
#ifndef AT_HWCAP2
#define AT_HWCAP2 26
#endif

struct ATENTRY {
  size_t id;
  size_t value;
} __attribute__((packed));

/*!
 * \brief Find ELF section by name.
 */
Elf_Shdr *elf_get_section(char *name, void *elf_start);

/*!
 * \brief Find ELF symbol by name.
 */
void *elf_get_symbol(void *elf_start, char *sym_name);

/*!
 * \brief Map the ELF into memory.
 */
// Returns the required memory size and bounds for the ELF
int elf_get_memory_bounds(char *elf_start, size_t *min_vaddr, size_t *max_vaddr);

// pre_mapped: if true, assume memory at base_addr is already mapped and writable
int elf_load(char *elf_start, void *stack, int stack_size, size_t *base_addr,
             size_t *entry, size_t *mapped_size, int pre_mapped, const char *module_path);

int elf_run(void *buf, char **argv, char **env, int pre_mapped, const char *module_path, size_t base_addr);

#endif // _ELF_LOADER_H_

#endif // __linux__
