#!/bin/sh

# -- Constants -------------------------------------------------------------

ELFCLASSNONE=0
ELFCLASS32=1
ELFCLASS64=2

ELFDATANONE=0
ELFDATA2LSB=1
ELFDATA2MSB=2

EV_NONE=0
EV_CURRENT=1

ELFOSABI_NONE=0

ET_NONE=0
ET_REL=1
ET_EXEC=2
ET_DYN=3
ET_CORE=4

EM_NONE=0
EM_386=3
EM_MIPS=8
EM_ARM=40
EM_X86_64=62

STB_LOCAL=0
STB_GLOBAL=1

STT_NOTYPE=0
STT_FUNC=2
STT_SECTION=3

STV_DEFAULT=0
STV_HIDDEN=2

SHT_NULL=0
SHT_PROGBITS=1
SHT_SYMTAB=2
SHT_STRTAB=3
SHT_NOBITS=8

SHF_WRITE=1
SHF_ALLOC=2
SHF_EXEC=4

PT_LOAD=1

PF_X=1
PF_W=2
PF_R=4


# -- Directives ------------------------------------------------------------

OFFSET=0

usage()
{
	echo "usage: shelf PLAN ..."
}

error()
{
	echo "$1" 1>&2
}

align()
{
	local width=$1
	local offset=$(( ($width - ($OFFSET & ($width - 1))) ))

	while [ $offset -gt 0 ]
	do
		u8 0
		offset=$(($offset - 1))
	done
}

u8()
{
	for byte
	do
		printf $(printf "\\\%03o" $byte)
		OFFSET=$(($OFFSET + 1))
	done
}

u16()
{
	local mask8=$(( $((1 << 8)) - 1 ))

	for word
	do
		u8 $(( word & $mask8 )) $(( word >> 8 & $mask8 ))
	done
}

u32()
{
	local mask16=$(( $((1 << 16)) - 1 ))

	for long
	do
		u16 $(( long & $mask16 )) $(( long >> 16 & $mask16 ))
	done
}

u64()
{
	local mask32=$(( $((1 << 32)) - 1 ))

	for quad
	do
		u32 $(( quad & $mask32 )) $(( quad >> 32 & $mask32 ))
	done
}

string()
{
	for string 
	do
		printf "${string}\0"
		OFFSET=$(($OFFSET + ${#string} + 1))
	done
}

magic()
{
	u8 0177 0105 0114 0106
}

ident()
{
	local class=$1
	local data=$2
	local version=$3
	local os_abi=$4

	magic
	u8 $class
	u8 $data
	u8 $version
	u8 $os_abi
	u8 0000 0000 0000 0000 0000 0000 0000 0000
}

header32()
{
	local type=$1
	local machine=$2
	local version=$3
	local entry=$4
	local phoff=$5
	local shoff=$6
	local flags=$7
	local ehsize=$8
	local phentsize=$9
	local phnum=${10}
	local shentsize=${11}
	local shnum=${12}
	local shstrndx=${13}

	u16 $type $machine
	u32 $version
	u32 $entry $phoff $shoff
	u32 $flags
	u16 $ehsize
	u16 $phentsize $phnum
	u16 $shentsize $shnum $shstrndx
}

header64()
{
	local type=$1
	local machine=$2
	local version=$3
	local entry=$4
	local phoff=$5
	local shoff=$6
	local flags=$7
	local ehsize=$8
	local phentsize=$9
	local phnum=${10}
	local shentsize=${11}
	local shnum=${12}
	local shstrndx=${13}

	u16 $type $machine
	u32 $version
	u64 $entry $phoff $shoff
	u32 $flags
	u16 $ehsize
	u16 $phentsize $phnum
	u16 $shentsize $shnum $shstrndx
}

file()
{
	local file=$1
	cat $file
	OFFSET=$(($OFFSET + $(wc -c < $file)))
}

sym32()
{
	local name=$1
	local info=$2
	local other=$3
	local shndx=$4
	local value=$5
	local size=$6

	u32 $name
	u32 $value
	u32 $size
	u8  $info
	u8  $other
	u16 $shndx
}

sym64()
{
	local name=$1
	local info=$2
	local other=$3
	local shndx=$4
	local value=$5
	local size=$6

	u32 $name
	u8  $info
	u8  $other
	u16 $shndx
	u64 $value
	u64 $size
}

shdr32()
{
	local name=$1
	local type=$2
	local flags=$3
	local addr=$4
	local offset=$5
	local size=$6
	local link=$7
	local info=$8
	local addralign=$9
	local entsize=${10}

	u32 $name
	u32 $type
	u32 $flags
	u32 $addr
	u32 $offset
	u32 $size
	u32 $link
	u32 $info
	u32 $addralign
	u32 $entsize
}

shdr64()
{
	local name=$1
	local type=$2
	local flags=$3
	local addr=$4
	local offset=$5
	local size=$6
	local link=$7
	local info=$8
	local addralign=$9
	local entsize=${10}

	u32 $name
	u32 $type
	u64 $flags
	u64 $addr
	u64 $offset
	u64 $size
	u32 $link
	u32 $info
	u64 $addralign
	u64 $entsize
}

phdr32()
{
	local type=$1
	local flags=$2
	local offset=$3
	local vaddr=$4
	local paddr=$5
	local filesz=$6
	local memsz=$7
	local align=$8

	u32 $type
	u32 $offset
	u32 $vaddr
	u32 $paddr
	u32 $filesz
	u32 $memsz
	u32 $flags
	u32 $align
}

phdr64()
{
	local type=$1
	local flags=$2
	local offset=$3
	local vaddr=$4
	local paddr=$5
	local filesz=$6
	local memsz=$7
	local align=$8

	u32 $type
	u32 $flags
	u64 $offset
	u64 $vaddr
	u64 $paddr
	u64 $filesz
	u64 $memsz
	u64 $align
}

pass()
{
	local plan=$1
	shift 1

	. $plan
}

main()
{
	if [ $# -eq 0 ]
	then
		usage
		exit 1
	fi

	pass "$@" > /dev/null
	pass "$@"
}

main "$@"
