package runtime

import (
	"unsafe"
)

const debugLoader = false

const (
	rAARCH64_RELATIVE = 1027
	dtNULL            = 0 /* Terminating entry. */
	dtRELA            = 7 /* Address of ElfNN_Rela relocations. */
	dtRELASZ          = 8 /* Total size of ElfNN_Rela relocations. */
)

/* ELF64 relocations that need an addend field. */
type rela64 struct {
	Off    uint64 /* Location to be relocated. */
	Info   uint64 /* Relocation type and symbol index. */
	Addend int64  /* Addend. */
}

// ELF64 Dynamic structure. The ".dynamic" section contains an array of them.
type dyn64 struct {
	Tag int64  /* Entry type. */
	Val uint64 /* Integer/address value */
}

//export __dynamic_loader
func dynamicLoader(base uintptr, dyn *dyn64) {
	var rela *rela64
	relasz := uint64(0)

	if debugLoader {
		println("ASLR Base: ", base)
	}

	for dyn.Tag != dtNULL {
		switch dyn.Tag {
		case dtRELA:
			rela = (*rela64)(unsafe.Pointer(base + uintptr(dyn.Val)))
		case dtRELASZ:
			relasz = uint64(dyn.Val) / uint64(unsafe.Sizeof(rela64{}))
		}

		ptr := uintptr(unsafe.Pointer(dyn))
		ptr += unsafe.Sizeof(dyn64{})
		dyn = (*dyn64)(unsafe.Pointer(ptr))
	}

	if rela == nil {
		runtimePanic("bad reloc")
	}

	if debugLoader {
		println("Sections to load: ", relasz)
	}

	for relasz > 0 && rela != nil {
		switch rela.Info {
		case rAARCH64_RELATIVE:
			if debugLoader {
				println("relocating ", uintptr(rela.Addend), " to ", base+uintptr(rela.Addend))
			}
			ptr := (*uint64)(unsafe.Pointer(base + uintptr(rela.Off)))
			*ptr = uint64(base + uintptr(rela.Addend))
		default:
			if debugLoader {
				println("unknown section to load:", rela.Info)
			}
		}

		rptr := uintptr(unsafe.Pointer(rela))
		rptr += unsafe.Sizeof(rela64{})
		rela = (*rela64)(unsafe.Pointer(rptr))
		relasz--
	}
}
