// Mach object file format definitions
// Copyright (C) Florian Negele

// This file is part of the Eigen Compiler Suite.

// The ECS is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// The ECS is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// Under Section 7 of the GNU General Public License version 3,
// the copyright holder grants you additional permissions to use
// this file as described in the ECS Runtime Support Exception.

// You should have received a copy of the GNU General Public License
// and a copy of the ECS Runtime Support Exception along with
// the ECS.  If not, see <https://www.gnu.org/licenses/>.

#ifndef ECS_DEF_MACHO_HEADER_INCLUDED
#define ECS_DEF_MACHO_HEADER_INCLUDED

#include "assembly"

#define ARM_THREAD_STATE64 6
#define CPU_ARCH_ABI64 0x01000000
#define CPU_SUBTYPE_ARM64_ALL 0
#define CPU_SUBTYPE_LIB64 0x80000000
#define CPU_SUBTYPE_X86_ALL 3
#define CPU_TYPE_ARM 12
#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
#define CPU_TYPE_X86 7
#define CPU_TYPE_X86_64	(CPU_TYPE_X86 | CPU_ARCH_ABI64)
#define LC_DYSYMTAB 0xb
#define LC_LOAD_DYLIB 0xc
#define LC_LOAD_DYLINKER 0xe
#define LC_SEGMENT_32 0x1
#define LC_SEGMENT_64 0x19
#define LC_SYMTAB 0x2
#define LC_UNIXTHREAD 0x5
#define MAGIC_32 0xfeedface
#define MAGIC_64 0xfeedfacf
#define MH_EXECUTE 0x2
#define MH_NOUNDEFS 0x1
#define N_EXT 0x01
#define NO_SECT 0
#define S_ATTR_DEBUG 0x02000000
#define SEG_LINKEDIT "__LINKEDIT"
#define SEG_PAGEZERO "__PAGEZERO"
#define SEG_TEXT "__TEXT"
#define VM_PROT_ALL (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE)
#define VM_PROT_EXECUTE 0x04
#define VM_PROT_READ 0x01
#define VM_PROT_WRITE 0x02
#define X86_THREAD_STATE32 1
#define X86_THREAD_STATE64 4

#define DYLINKER_COMMAND_SIZE (LOAD_COMMAND_SIZE + 4)
#define LOAD_COMMAND_SIZE 8
#define SYMTAB_COMMAND_SIZE (LOAD_COMMAND_SIZE + 16)
#define THREAD_COMMAND_SIZE (LOAD_COMMAND_SIZE + 8)
#define DYSYMTAB_COMMAND_SIZE 80
#define DYLIB_COMMAND_SIZE 24

#define LOAD_COMMAND(cmd, cmdsize) \
	QBYTE (cmd, cmdsize)

#define THREAD_COMMAND(cmd, cmdsize, flavor, count) \
	LOAD_COMMAND (cmd, cmdsize) QBYTE (flavor) QBYTE (count)

#define DYLINKER_COMMAND(cmd, cmdsize, name) \
	LOAD_COMMAND (cmd, cmdsize) QBYTE (offset)

#define SYMTAB_COMMAND(cmd, cmdsize, symoff, nsyms, stroff, strsize) \
	LOAD_COMMAND (cmd, cmdsize) QBYTE (symoff) QBYTE (nsyms) QBYTE (stroff) QBYTE (strsize)

#define DYSYMTAB_COMMAND(cmd, cmdsize, ilocalsym, nlocalsym, iextdefsym, nextdefsym, iundefsym, nundefsym, tocoff, ntoc, modtaboff, nmodtab, extrefsymoff, nextrefsyms, indirectsymoff, nindirectsyms, extreloff, nextrel, locreloff, nlocrel) \
	LOAD_COMMAND (cmd, cmdsize) QBYTE (ilocalsym) QBYTE (nlocalsym) QBYTE (iextdefsym) QBYTE (nextdefsym) QBYTE (iundefsym) QBYTE (nundefsym) QBYTE (tocoff) QBYTE (ntoc) QBYTE (modtaboff) \
	QBYTE (nmodtab) QBYTE (extrefsymoff) QBYTE (nextrefsyms) QBYTE (indirectsymoff) QBYTE (nindirectsyms) QBYTE (extreloff) QBYTE (nextrel) QBYTE (locreloff) QBYTE (nlocrel)

#define DYLIB_COMMAND(cmd, cmdsize, name, timestamp, current_version, compatibility_version) \
	LOAD_COMMAND (cmd, cmdsize) QBYTE (name) QBYTE (timestamp) QBYTE (current_version) QBYTE (compatibility_version)

#define RELOCATION_INFO(r_address, r_symbolnum, r_pcrel, r_length, r_extern, r_type) \
	QBYTE (r_address) TBYTE (r_symbolnum) BYTE ((r_pcrel) << 0 | (r_length) << 1 | (r_extern) << 3 | (r_type) << 4)

#if MAGIC == MAGIC_32

	#define ADDR_SIZE 4
	#define LC_SEGMENT LC_SEGMENT_32
	#define SECTION_SIZE 68
	#define SEGMENT_COMMAND_SIZE 56

	#define MACH_HEADER(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags) \
		QBYTE (magic) QBYTE (cputype) QBYTE (cpusubtype) QBYTE (filetype) QBYTE (ncmds) QBYTE (sizeofcmds) QBYTE (flags)

	#define SEGMENT_COMMAND(cmd, cmdsize, segname, vmaddr, vmsize, fileoff, filesize, maxprot, initprot, nsects, flags) \
		LOAD_COMMAND (cmd, cmdsize) BYTE (segname) PAD (24) QBYTE (vmaddr) QBYTE (vmsize) QBYTE (fileoff) QBYTE (filesize) QBYTE (maxprot) QBYTE (initprot) QBYTE (nsects) QBYTE (flags)

	#define SECTION(sectname, segname, addr, size, offset, align, reloff, nreloc, flags) \
		BYTE (sectname, 0) PAD (16) BYTE (segname, 0) PAD (32) QBYTE (addr) QBYTE (size) QBYTE (offset) QBYTE (align) QBYTE (reloff) QBYTE (nreloc) QBYTE (flags) QBYTE (0) QBYTE (0)

	#define NLIST(n_strx, n_type, n_sect, n_desc, n_value) \
		QBYTE (n_strx) BYTE (n_type) BYTE (n_sect) DBYTE (n_desc) QBYTE (n_value)

#elif MAGIC == MAGIC_64

	#define ADDR_SIZE 8
	#define LC_SEGMENT LC_SEGMENT_64
	#define SECTION_SIZE 80
	#define SEGMENT_COMMAND_SIZE 72

	#define MACH_HEADER(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags) \
		QBYTE (magic) QBYTE (cputype) QBYTE (cpusubtype) QBYTE (filetype) QBYTE (ncmds) QBYTE (sizeofcmds) QBYTE (flags) QBYTE (0)

	#define SEGMENT_COMMAND(cmd, cmdsize, segname, vmaddr, vmsize, fileoff, filesize, maxprot, initprot, nsects, flags) \
		LOAD_COMMAND (cmd, cmdsize) BYTE (segname) PAD (24) OBYTE (vmaddr) OBYTE (vmsize) OBYTE (fileoff) OBYTE (filesize) QBYTE (maxprot) QBYTE (initprot) QBYTE (nsects) QBYTE (flags)

	#define SECTION(sectname, segname, addr, size, offset, align, reloff, nreloc, flags) \
		BYTE (sectname, 0) PAD (16) BYTE (segname, 0) PAD (32) OBYTE (addr) OBYTE (size) QBYTE (offset) QBYTE (align) QBYTE (reloff) QBYTE (nreloc) QBYTE (flags) QBYTE (0) QBYTE (0) QBYTE (0)

	#define NLIST(n_strx, n_type, n_sect, n_desc, n_value) \
		QBYTE (n_strx) BYTE (n_type) BYTE (n_sect) DBYTE (n_desc) OBYTE (n_value)

#else

	#error Mach object file format not supported

#endif

#endif // ECS_DEF_MACHO_HEADER_INCLUDED
