#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>

#include <elf.h>

#define EIGHTMEM (32 * 1024 * 1024)

int main(int argc, char **argv)
{
	int fd;
	struct stat statbuf;
	unsigned char *data; // ELF file
	unsigned char *mapping; // target memory location
	Elf32_Ehdr *ehdr;
	Elf32_Phdr *phdr;
	int i;
	int used = 0;
	unsigned char *source, *dest;
	int len;
	Elf32_Addr base = (Elf32_Addr)NULL;

	if(argc < 3) {
		printf("elf2bin [input file] [output file]\n");
		exit(EXIT_FAILURE);
	}

	fd = open(argv[1], O_RDONLY);
	if(fd == -1) {
		printf("Failed to open %s: %s\n", argv[1], strerror(errno));
		exit(EXIT_FAILURE);
	}

	if(fstat(fd, &statbuf) == -1) {
		printf("Failed to fstat(fd): %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	data = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	if(data == MAP_FAILED) {
		printf("Unable to read ELF file in: %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	close(fd);

	mapping = mmap(NULL, EIGHTMEM, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
	if(mapping == MAP_FAILED) {
		printf("Failed to mmap(): %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	ehdr = (Elf32_Ehdr *)data;
	phdr = (Elf32_Phdr *)(data + ehdr->e_phoff);

	printf("data @ %p, mapping @ %p\n", data, mapping);

	for(i = 0; i < ehdr->e_phnum; i++, phdr++) {
		if(phdr->p_type == PT_LOAD) {
			if(base == (Elf32_Addr)NULL) {
				base = phdr->p_vaddr & ~4095;
			}

			source = data + (phdr->p_offset & ~4095);
			dest = mapping + ((phdr->p_vaddr - base) & ~4095);
			len = phdr->p_filesz + (phdr->p_vaddr & 4095);
			printf("memcpy(%p, %p, %08x)\n", dest, source, len);
			memcpy(dest, source, len);

			used += (phdr->p_memsz + (phdr->p_vaddr & 4095) + 4095) & ~4095 ;
		}
	}

	fd = open(argv[2], O_RDWR|O_TRUNC|O_CREAT, 0644);
	if(fd == -1) {
		printf("Unable to dump memory: %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	if(write(fd, mapping, used) != used) {
		printf("Unable to complete memory dump\n");
		exit(EXIT_FAILURE);
	}

	close(fd);

}