#include <stdio.h>
#include <elf.h>

#include "hash_lookup.c"

void *get_base(uint32_t addr) {
	addr &= ~4095;
	while (*(uint32_t*)addr != 0x464c457fUL)
		addr -= 4096;
	return (void*)addr;
}

void get_dyn_file_base(uint8_t *elf, Elf32_Dyn **dynamic, uint32_t *delta)
{
	int i;
	Elf32_Phdr *phdr;
	uint32_t dyn, low = 0xffffffff;
	phdr = (Elf32_Phdr*)(elf + ((Elf32_Ehdr*)elf)->e_phoff);
	for (i = 0; i < ((Elf32_Ehdr*)elf)->e_phnum; i++) {
		if (phdr[i].p_type == PT_LOAD && low > phdr[i].p_vaddr)
			low = phdr[i].p_vaddr;
		if (phdr[i].p_type == PT_DYNAMIC)
			dyn = phdr[i].p_vaddr;
	}
	*delta = (uint32_t)elf - low;
	*dynamic = (Elf32_Dyn*)(dyn + *delta);
}

void *get_from_dyn(Elf32_Dyn *dynamic, uint32_t tag)
{
	Elf32_Dyn *dyn;
	for (dyn = dynamic; dyn->d_tag != DT_NULL; ++dyn)
		if (dyn->d_tag == tag)
			return (uint32_t*)dyn->d_un.d_ptr;
	return NULL;
}

void unwind(uint32_t addr, uint32_t **got, Elf32_Dyn **dyn, uint32_t *delta)
{
	get_dyn_file_base(get_base(addr), dyn, delta);
	*got = get_from_dyn(*dyn, DT_PLTGOT);
}

int main(int argc, char **argv, char **envp)
{
	uint32_t *got, delta;
	Elf32_Dyn *dyn;
	unwind((uint32_t)main, &got, &dyn, &delta);
	if (got[2])
		unwind(got[2], &got, &dyn, &delta);
	unwind(got[4], &got, &dyn, &delta);
	
	Elf32_Sym *dynsym;
	char *dynstr;
	uint32_t *hash;
	void (*printf_ptr)(char*,...);

	dynsym = (Elf32_Sym*)get_from_dyn(dyn, DT_SYMTAB);
	dynstr = get_from_dyn(dyn, DT_STRTAB);
	hash = get_from_dyn(dyn, DT_HASH);
	printf_ptr = (void(*)(char*,...))(lookup("printf", hash, dynsym, dynstr) + delta);
	printf_ptr("Hello, world! printf = %08x\n", (uint32_t)printf_ptr);
}
