/*
 * elfload.c
 * 
 * カーネルバイナリからロード用バイナリを作成する
 * 引数１　＝　カーネルＥＬＦバイナリファイル名
 * 引数２　＝　ロードバイナリファイル名
 */


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


typedef unsigned int	Elf32_Addr;
typedef unsigned short	Elf32_Half;
typedef unsigned int	Elf32_Off;
typedef int				Elf32_Sword;
typedef unsigned int	Elf32_Word;


/*
 * ELF header.
 */
enum{EI_NIDENT=16};

typedef struct {
	unsigned char	e_ident[EI_NIDENT];	/* File identification. */
	Elf32_Half		e_type;				/* File type. */
	Elf32_Half		e_machine;			/* Machine architecture. */
	Elf32_Word		e_version;			/* ELF format version. */
	Elf32_Addr		e_entry;			/* Entry point. */
	Elf32_Off		e_phoff;			/* Program header file offset. */
	Elf32_Off		e_shoff;			/* Section header file offset. */
	Elf32_Word		e_flags;			/* Architecture-specific flags. */
	Elf32_Half		e_ehsize;			/* Size of ELF header in bytes. */
	Elf32_Half		e_phentsize;		/* Size of program header entry. */
	Elf32_Half		e_phnum;			/* Number of program header entries. */
	Elf32_Half		e_shentsize;		/* Size of section header entry. */
	Elf32_Half		e_shnum;			/* Number of section header entries. */
	Elf32_Half		e_shstrndx;			/* Section name strings section. */
} Elf32_Ehdr;

/*
 * Section header.
 */
typedef struct {
	Elf32_Word	sh_name;		/* Section name (index into thesection header string table). */
	Elf32_Word	sh_type;		/* Section type. */
	Elf32_Word	sh_flags;		/* Section flags. */
	Elf32_Addr	sh_addr;		/* Address in memory image. */
	Elf32_Off	sh_offset;		/* Offset in file. */
	Elf32_Word	sh_size;		/* Size in bytes. */
	Elf32_Word	sh_link;		/* Index of a related section. */
	Elf32_Word	sh_info;		/* Depends on section type. */
	Elf32_Word	sh_addralign;	/* Alignment in bytes. */
	Elf32_Word	sh_entsize;		/* Size of each entry in section. */
} Elf32_Shdr;

enum{
	/* sh_type */
	SHT_PROGBITS	= 1,		/* program defined information */
	SHT_NOBITS		= 8,		/* no space section */
};


static void err_msg(const char *ms)
{
	printf("Error : %s!\n",ms);
	exit(0);
}


/*
 * ロードファイルへ書き込む
 */
static void write_boot(int fd,char *kern,Elf32_Ehdr *head,Elf32_Shdr *sect)
{
	static char *buf = NULL;	/* 0書き込用バッファ */
	static int end = 0;			/* 書き込み最終アドレス */
	static int buf_size = 0;	/* 0書き込用バッファサイズ */
	int begin;
	
	if (sect->sh_flags == 0)
		return;

	/* ロードファイル書き込みアドレスの設定 */
	begin = sect->sh_addr - head->e_entry;
	if (end < begin)
	{
		if (buf_size < begin - end)
		{
			buf_size = begin - end;
			buf = realloc(buf,buf_size);
			if (buf == NULL)
				err_msg("realloc error");
			memset(buf,0,buf_size);
		}
		lseek(fd,end,SEEK_SET);
		write(fd,buf,begin - end);
	}
	lseek(fd,begin,SEEK_SET);

	/* 書き込み */
	switch (sect->sh_type)
	{
		case SHT_PROGBITS:
			if (write(fd,kern + sect->sh_offset,sect->sh_size) != sect->sh_size)
				err_msg("write error");
			break;
		case SHT_NOBITS:
			if (buf_size < sect->sh_size)
			{
				buf_size = sect->sh_size;
				buf = realloc(buf,buf_size);
				if (buf == NULL)
					err_msg("realloc error");
				memset(buf,0,buf_size);
			}
			if (write(fd,buf,sect->sh_size) != sect->sh_size)
				err_msg("write error");
			break;
	}
	end = begin + sect->sh_size;
}


int main(int argc,char *argv[])
{
	void *buf;
	uint offset;
	int fd_kern,fd_load;
	struct stat stat;
	Elf32_Ehdr *e_header;
	Elf32_Shdr *e_sect;
	int i;
	
	
	if (argc < 3)
		err_msg("few args");

	/* カーネルバイナリファイルを読む */
	fd_kern = open(argv[1],O_RDONLY);
	if (fd_kern == -1)
		err_msg("Dont open kernel binary");
	fstat(fd_kern,&stat);
	buf = malloc(stat.st_size);
	if (buf == NULL)
		err_msg("malloc error");
	if (read(fd_kern,buf,stat.st_size) < stat.st_size)
		err_msg("read error");

	/* ELFヘッダー・セクションアドレスを代入 */
	e_header = buf;
	offset = (uint)buf + e_header->e_shoff;
	e_sect = (Elf32_Shdr*)offset;

	/* ロードファイルに書き込む */
	fd_load = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
	if (fd_load == -1)
		err_msg("Dont open load binary");
	for (i = 1; i < (int)e_header->e_shstrndx; ++i)
		write_boot(fd_load,buf,e_header,&e_sect[i]);

	return 0;
}
