include 'x64.inc'
use64

ELF64 = 0
PE64  = 1

PHEAD_INDEX = 0
SHEAD_INDEX = 0

;To be used...
PHEAD_GLOBAL_OFFSET = 0
SHEAD_GLOBAL_OFFSET = 0

PHEAD_LOCAL_OFFSET = 0
SHEAD_LOCAL_OFFSET = 0

SEG_BASE        := 0x400000
SEG_PREV        = 0
SEG_INDEX       = 0

;Internal (Header-only)
macro pheader name, flags
	eval 'PHEAD.loc',PHEAD_INDEX+48,':'
	
	.p_type   dd PT_LOAD									;p_type
	.p_flags  dd flags										;p_flags
	.p_offset dq PHEAD_LOCAL_OFFSET							;p_offset
	.p_vaddr  dq SEG_BASE + PHEAD_GLOBAL_OFFSET				;p_vaddr
	.p_paddr  dq SEG_BASE + PHEAD_GLOBAL_OFFSET				;p_paddr
	.p_filesz dq PHEAD_LOCAL_OFFSET							;p_filesz
	.p_memsz  dq PHEAD_LOCAL_OFFSET							;p_memsz
	.p_align  dq 0x1000										;p_align
	
	PHEAD_INDEX = PHEAD_INDEX + 1
end macro
macro endseg
	PHEAD_GLOBAL_OFFSET = PHEAD_GLOBAL_OFFSET + PHEAD_LOCAL_OFFSET
	PHEAD_LOCAL_OFFSET = ($% - SEG_PREV)
	
	if (SEG_INDEX > 1)
		eval 'store PHEAD_GLOBAL_OFFSET : qword at PHEAD.loc',(SEG_INDEX - 1)+48,'.p_offset'
		eval 'store SEG_BASE + PHEAD_GLOBAL_OFFSET + ((SEG_INDEX - 1) * 0x1000) : qword at PHEAD.loc',(SEG_INDEX - 1)+48,'.p_vaddr'
		eval 'store SEG_BASE + PHEAD_GLOBAL_OFFSET + ((SEG_INDEX - 1) * 0x1000) : qword at PHEAD.loc',(SEG_INDEX - 1)+48,'.p_paddr'
		eval 'store PHEAD_LOCAL_OFFSET : qword at PHEAD.loc',(SEG_INDEX - 1)+48,'.p_filesz'
		eval 'store PHEAD_LOCAL_OFFSET : qword at PHEAD.loc',(SEG_INDEX - 1)+48,'.p_memsz'
	else
		PHEAD_GLOBAL_OFFSET = PHEAD_GLOBAL_OFFSET + 0xb0
		eval 'store SEG_BASE: qword at PHEAD.loc',(SEG_INDEX - 1)+48,'.p_vaddr'
		eval 'store SEG_BASE: qword at PHEAD.loc',(SEG_INDEX - 1)+48,'.p_paddr'
		eval 'store PHEAD_LOCAL_OFFSET + 0xb0 : qword at PHEAD.loc',(SEG_INDEX - 1)+48,'.p_filesz'
		eval 'store PHEAD_LOCAL_OFFSET + 0xb0: qword at PHEAD.loc',(SEG_INDEX - 1)+48,'.p_memsz'
	end if
end macro

;External
macro header h
	if h = ELF64
		e_ident:
			.ei_magic      db 0x7f, 'ELF'
			.ei_class      db EI_64BITS
			.ei_data       db EI_ENDIAN_LITTLE
			.ei_version    db ELF_VERSION_CURRENT
			.ei_osabi      db EI_OSABI_DEFAULT
			.ei_abiversion db 0
			.ei_pad        rb (16 - $)
		
		
		e_type      dw ET_EXEC
		e_machine   dw EM_AMD64
		e_version   dd ELF_VERSION_CURRENT
		e_entry     dq ENTRY_LOCATION_FINAL
		e_phoff     dq PHEAD_OFFSET_FINAL
		e_shoff     dq SHEAD_OFFSET_FINAL
		e_flags     dd 0
		e_ehsize    dw EEH_64BITS
		e_phentsize	dw sizeof elf_phdr
		e_phnum 	dw PHEAD_INDEX_FINAL
		e_shentsize	dw sizeof elf_shdr
		e_shnum 	dw SHEAD_INDEX_FINAL
		e_shstrndx	dw 0

	else if h = PE64
	end if
	
	PHEAD_OFFSET_FINAL := $
	
	;Program headers
	if defined data
		pheader data, PF_WRITE
	end if
	if defined text
		pheader text, PF_EXEC
	end if
end macro

macro segment name
	if SEG_PREV > 0
		endseg
	end if
	
	$$% = $%
	name:
	SEG_INDEX = SEG_INDEX + 1
	SEG_PREV = name
end macro

macro entry name
	ENTRY_LOCATION := name
end macro

macro align n
	rb $ mod n
end macro

macro eof
	ENTRY_LOCATION_FINAL := ENTRY_LOCATION + SEG_BASE + ((SEG_INDEX - 1) * 0x1000)

	endseg

	EOF := $%
	if PHEAD_INDEX > 0
		PHEAD_INDEX_FINAL := PHEAD_INDEX
	else 
		PHEAD_INDEX_FINAL := 0
		PHEAD_OFFSET_FINAL := 0
	end if
	
	if SHEAD_INDEX > 0
		SHEAD_INDEX_FINAL := SHEAD_INDEX
	else
		SHEAD_INDEX_FINAL := 0
		SHEAD_OFFSET_FINAL := 0
	end if
end macro


macro struct? name
	macro end?.struct?!
			end namespace
		esc end struc
		virtual at 0
			name name
			sizeof.name = $
		end virtual
		purge end?.struct?
	end macro
	esc struc name
		label . : sizeof.name
		namespace .
end macro

struct elf64_hdr
	ei_magic      db ?
	ei_class      db ?
	ei_data       db ?
	ei_version    db ?
	ei_osabi      db ?
	ei_abiversion db ?
	ei_pad        rb (16 - $%)
	e_type        dw ?
	e_machine   dw ?
	e_version   dd ?
	e_entry     dq ?
	e_phoff     dq ?
	e_shoff     dq ?
	e_flags     dd ?
	e_ehsize    dw ?
	e_phentsize	dw ?
	e_phnum 	dw ?
	e_shentsize	dw ?
	e_shnum 	dw ?
	e_shstrndx	dw ?
end struct

struct elf_shdr
	sh_name 	dd ?
	sh_type 	dd ?
	sh_flags	dq ?
	sh_addr 	dq ?
	sh_offset	dq ?
	sh_size 	dq ?
	sh_link 	dd ?
	sh_info 	dd ?
	sh_addralign dq ?
	sh_entsize	dq ?
end struct

struct elf_sym
	st_name 	dd ?
	st_info 	db ?
	st_other	db ?
	st_shndx	dw ?
	st_value	dq ?
	st_size 	dq ?
end struct

struct elf_rel
	r_offset	dq ?
	r_info		dq ?
end struct

struct elf_rela
	r_offset	dq ?
	r_info		dq ?
	r_addend	dq ?
end struct

struct elf_phdr
	p_type		dd ?
	p_flags 	dd ?
	p_offset	dq ?
	p_vaddr 	dq ?
	p_paddr 	dq ?
	p_filesz	dq ?
	p_memsz 	dq ?
	p_align 	dq ?
end struct

;e_ident.ei_class (Byte size)
EI_32BITS = 1
EI_64BITS = 2

;e_data.ei_class (Endianness)
EI_ENDIAN_LITTLE = 1
EI_ENDIAN_BIG    = 2

;e_data.ei_version, e_version (ELF version)
ELF_VERSION_CURRENT = 1

;e_data.ei_osabi (Operating system, ABI)
EI_OSABI_DEFAULT    = 0
EI_OSABI_SYSTEMV    = 0
EI_OSABI_HP_UX      = 1
EI_OSABI_NETBSD     = 2
EI_OSABI_LINUX      = 3
EI_OSABI_HURD       = 4
EI_OSABI_SOLARIS    = 6
EI_OSABI_IBM_AIX    = 7
EI_OSABI_SGI_IRIX   = 8
EI_OSABI_FREEBSD    = 9
EI_OSABI_TRU64      = 10
EI_OSABI_MODESTO    = 11
EI_OSABI_OPENBSD    = 12
EI_OSABI_OPENVMS    = 13
EI_OSABI_NONSTOPKRN = 14
EI_OSABI_AMIGA_AROS = 15
EI_OSABI_FENIXOS    = 16
EI_OSABI_CLOUDABI   = 17
EI_OSABI_STRATUSVOS = 18

;e_type (obj filetype)
ET_NONE   = 0x0000
ET_REL    = 0x0001
ET_EXEC   = 0x0002
ET_DYN    = 0x0003
ET_CORE   = 0x0004
ET_LOOS   = 0xfe00
ET_HIOS   = 0xfeff
ET_LOPROC = 0xff00
ET_HIPROC = 0xffff

;e_machine (CPU architecture)
EM_NONE        = 0x00
EM_ATT_BELMAC  = 0x01
EM_SPARC       = 0x02
EM_x86         = 0x03
EM_MOTO_68000  = 0x04
EM_MOTO_88000  = 0x05
EM_INTEL_MCU   = 0x06
EM_INTEL_MCU   = 0x07
EM_INTEL_80860 = 0x08
EM_MIPS        = 0x09
EM_IBM_S370    = 0x0a
EM_MIPS_RS33K  = 0x0b
EM_HP_PARISC   = 0x0e
EM_INTEL_80960 = 0x13
EM_POWERPC32   = 0x14
EM_POWERPC64   = 0x15
EM_IBM_ZARCH   = 0x16
EM_IBM_S390    = 0x16
EM_IBM_S390X   = 0x16
EM_ARMV2       = 0x28
EM_ARMV3       = 0x28
EM_ARMV4       = 0x28
EM_ARMV4T      = 0x28
EM_ARMV5       = 0x28
EM_ARMV6       = 0x28
EM_ARMV7       = 0x28
EM_AARCH32     = 0x28
EM_SUPERH      = 0x2a
EM_IA64        = 0x32
EM_AMD64       = 0x3e
EM_TMS320C6000 = 0x8c
EM_ARMV8       = 0xb7
EM_AARCH64     = 0xb7
EM_RISCV       = 0xf3

;e_ehsize (Bytes of ELF header)
EEH_32BITS = 32
EEH_64BITS = 64

;p_type (Type of segment)
PT_NULL   = 0x00000000
PT_LOAD   = 0x00000001
PT_DLINK  = 0x00000002
PT_INTER  = 0x00000003
PT_NOTE   = 0x00000004
PT_SHLIB  = 0x00000005
PT_PHDR   = 0x00000006
PT_TLS    = 0x00000007
PT_LOOS   = 0x60000000
PT_HIOS   = 0x6fffffff
PT_LOPROC = 0x70000000
PT_HIPROC = 0x7fffffff

;p_flags (State of segment)
PF_NONE  = 0
PF_EXEC  = 1
PF_WRITE = 2
PF_READ  = 4

SEG_NONE  = 0
SEG_EXEC  = 1
SEG_WRITE = 2
SEG_READ  = 4

;Header
header ELF64
entry main 

segment data
hello db "hello", 10, 0
segment text
main:
mov rax, 1
mov rsi, hello
mov rdx, 7
syscall
mov rax, 60
syscall
eof