/*
 *  TCCMEOS.C - KolibriOS/MenuetOS file output for the TinyC Compiler
 * 
 *  Copyright (c) 2006 Andrey Khalyavin
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

typedef struct {
	char magic[8];
	int version;
	int entry_point;
	int image_size;
	int memory_size;
	int stack;
	int params;
	int argv;
} IMAGE_MEOS_FILE_HEADER,*PIMAGE_MEOS_FILE_HEADER;
typedef struct _meos_section_info{
	int sh_addr;
	void* data;
	int data_size;
	int sec_num;
	struct _meos_section_info* next;
} meos_section_info;
typedef struct {
	TCCState* s1;
	IMAGE_MEOS_FILE_HEADER header;
	meos_section_info* code_sections;
	meos_section_info* data_sections;
	meos_section_info* bss_sections;
} me_info;

meos_section_info* findsection(me_info* me,int num)
{
	meos_section_info* si;
	for(si=me->code_sections;si;si=si->next)
	{
		if (si->sec_num==num)
			return si;
	}
	for (si=me->data_sections;si;si=si->next)
	{
		if (si->sec_num==num)
			return si;
	}
	for (si=me->bss_sections;si;si=si->next)
	{
		if (si->sec_num==num)
			return si;
	}
	return (meos_section_info*)0;
}

void build_reloc(me_info* me)
{
	int flag;
	Elf32_Rel *rel, *rel_, *rel_end;
	Section *sr;
	meos_section_info* s;
	meos_section_info* ss;
	s=me->code_sections;
	rel=0;
	rel_end=0;
	flag=0;
	for(;;)
	{
		sr=me->s1->sections[s->sec_num]->reloc;
		if (sr)
		{
			rel = (Elf32_Rel *) sr->data;
			rel_end = (Elf32_Rel *) (sr->data + sr->data_offset);
		}
		rel_=rel;
		while (rel_<rel_end){
			rel=rel_;
			int type = ELF32_R_TYPE(rel->r_info);
			rel_=rel+1;
			if (type != R_386_PC32 && type != R_386_32)
				continue;
			int sym = ELF32_R_SYM(rel->r_info);
			if (sym>symtab_section->data_offset/sizeof(Elf32_Sym))
				continue;
			Elf32_Sym* esym = ((Elf32_Sym *)symtab_section->data)+sym;
			int sect=esym->st_shndx;
			ss=findsection(me,sect);
			if (ss==0) continue;
			if (rel->r_offset>s->data_size)
				continue;
			if (type==R_386_PC32)
				*(int*)(rel->r_offset+s->data)+=ss->sh_addr+esym->st_value-rel->r_offset-s->sh_addr;
			else if (type==R_386_32)
				*(int*)(rel->r_offset+s->data)+=ss->sh_addr+esym->st_value;
		}
        rel=rel_;
		s=s->next;
		if (s==0)
		{
			if (flag) break;
			s=me->data_sections;
			if (s==0) break;
			flag=1;
			continue;
		}
	}
}

void assign_addresses(me_info* me)
{
	int i;
	meos_section_info* si;
	for (i=1;i<me->s1->nb_sections;i++)
	{
		Section* s=me->s1->sections[i];
		if (strcmp(".text",s->name)==0)
		{
			si=tcc_malloc(sizeof(meos_section_info));
			si->data=s->data;
			si->data_size=s->data_offset;
			si->next=me->code_sections;
			si->sec_num=i;
			me->code_sections=si;
			continue;
		}
		if (strcmp(".data",s->name)==0)
		{
			si=tcc_malloc(sizeof(meos_section_info));
			si->data=s->data;
			si->data_size=s->data_offset;
			si->next=me->data_sections;
			si->sec_num=i;
			me->data_sections=si;
			continue;
		}
		if (strcmp(".bss",s->name)==0)
		{
			si=tcc_malloc(sizeof(meos_section_info));
			si->data_size=s->data_offset;
			si->next=me->bss_sections;
			si->sec_num=i;
			me->bss_sections=si;
			continue;
		}
	}
	int addr;
	addr=sizeof(IMAGE_MEOS_FILE_HEADER);
	for (si=me->code_sections;si;si=si->next)
	{
		si->sh_addr=addr;
		addr+=si->data_size;
	}
	for (si=me->data_sections;si;si=si->next)
	{
		si->sh_addr=addr;
		addr+=si->data_size;
	}
	me->header.image_size=addr;
	for (si=me->bss_sections;si;si=si->next)
	{
		si->sh_addr=addr;
		addr+=si->data_size;
	}
	addr+=4096;
	addr=(addr+4)&(~3);
	me->header.stack=addr;
	me->header.memory_size=addr;
	build_reloc(me);
}
int tcc_find_symbol_me(me_info* me, const char *sym_name)
{
	int i;
	int symtab;
	int strtab;
	symtab=0;
	strtab=0;
	for (i=1;i<me->s1->nb_sections;i++)
	{
		Section* s;
		s=me->s1->sections[i];
		if (strcmp(s->name,".symtab")==0)
		{
			symtab=i;
		}
		if (strcmp(s->name,".strtab")==0)
		{
			strtab=i;
		}
	}
	if (symtab==0 || strtab==0)
		return 0;
	Elf32_Sym* s,*se;
	char* name;
	s=(Elf32_Sym*)me->s1->sections[symtab]->data;
	se=(Elf32_Sym*)(((void*)s)+me->s1->sections[symtab]->data_offset);
	name=(char*)me->s1->sections[strtab]->data;
	while (s<se)
	{
		if (strcmp(name+s->st_name,sym_name)==0)
		{
			return s->st_value+findsection(me,s->st_shndx)->sh_addr;
		}
		s++;
	}
	return 0;
}
const char* me_magic="MENUET01";
int tcc_output_me(TCCState* s1,const char *filename)
{
	me_info me;
    int i;
    FILE* f;
    //printf("%d\n",s1->nb_sections);
	memset(&me,0,sizeof(me));
	me.s1=s1;
	relocate_common_syms();
	assign_addresses(&me);
	me.header.entry_point=tcc_find_symbol_me(&me,"start");
	me.header.params= tcc_find_symbol_me(&me,"__argv"); // <--
	me.header.argv= tcc_find_symbol_me(&me,"__path"); // <--
	
	f=fopen(filename,"wb");
    for (i=0;i<8;i++)
        me.header.magic[i]=me_magic[i];
	/*me.header.magic[0]='M';me.header.magic[1]='E';
	me.header.magic[2]='N';me.header.magic[3]='U';
	me.header.magic[4]='E';me.header.magic[5]='T';
	me.header.magic[6]='0';me.header.magic[7]='1';*/
	fwrite(&me.header,1,sizeof(IMAGE_MEOS_FILE_HEADER),f);
	meos_section_info* si;
	for(si=me.code_sections;si;si=si->next)
		fwrite(si->data,1,si->data_size,f);
	for (si=me.data_sections;si;si=si->next)
		fwrite(si->data,1,si->data_size,f);
	fclose(f);
	return 0;
}