U0 LoadOneImport(U8 **_src, U8 *module_base, I64 ld_flags)
{
	U8			*src = *_src, *ptr2, *st_ptr;
	I64			 i, etype;
	CHashExport	*tmpex = NULL;
	CHashImport	*tmpiss;
	Bool		 first = TRUE;

	while (etype = *src++)
	{
		i = *src(U32 *)++;
		st_ptr = src;
		src += StrLen(st_ptr) + 1;
		if (*st_ptr)
		{
			if (!first)
			{
				*_src = st_ptr - 5;
				return;
			}
			else
			{
				first = FALSE;
				if (!(tmpex = HashFind(st_ptr, Fs->hash_table, HTG_ALL - HTT_IMPORT_SYS_SYM)))
				{
					if (!(ld_flags & LDF_SILENT))
						"Unresolved Reference:%s\n", st_ptr;
					tmpiss = CAlloc(sizeof(CHashImport));
					tmpiss->str					= StrNew(st_ptr);
					tmpiss->type				= HTT_IMPORT_SYS_SYM;
					tmpiss->module_header_entry	= st_ptr - 5;
					tmpiss->module_base			= module_base;
					HashAdd(tmpiss, Fs->hash_table);
				}
			}
		}
		if (tmpex)
		{
			ptr2 = module_base+i;
			if (tmpex->type & HTT_FUN)
				i = tmpex(CHashFun *)->exe_addr;
			else if (tmpex->type & HTT_GLOBAL_VAR)
				i = tmpex(CHashGlobalVar *)->data_addr;
			else
				i = tmpex->val;
			switch (etype)
			{
				case IET_REL_I8:  *ptr2(U8 *)  = i - ptr2 - 1;	break;
				case IET_IMM_U8:  *ptr2(U8 *)  = i;				break;
				case IET_REL_I16: *ptr2(U16 *) = i - ptr2 - 2;	break;
				case IET_IMM_U16: *ptr2(U16 *) = i;				break;
				case IET_REL_I32: *ptr2(U32 *) = i - ptr2 - 4;	break;
				case IET_IMM_U32: *ptr2(U32 *) = i;				break;
				case IET_REL_I64: *ptr2(I64 *) = i - ptr2 - 8;	break;
				case IET_IMM_I64: *ptr2(I64 *) = i;				break;
			}
		}
	}
	*_src = src - 1;
}

U0 SysSymImportsResolve(U8 *st_ptr, I64 ld_flags)
{
	CHashImport	*tmpiss;
	U8			*ptr;

	while (tmpiss = HashSingleTableFind(st_ptr, Fs->hash_table, HTT_IMPORT_SYS_SYM))
	{
		ptr = tmpiss->module_header_entry;
		LoadOneImport(&ptr, tmpiss->module_base, ld_flags);
		tmpiss->type = HTT_INVALID;
	}
}

U0 LoadPass1(U8 *src, U8 *module_base, I64 ld_flags)
{
	U8			*ptr2, *ptr3, *st_ptr;
	I64			 i, j, count, etype;
	CHashExport	*tmpex = NULL;

	while (etype = *src++)
	{
		i = *src(U32 *)++;
		st_ptr = src;
		src += StrLen(st_ptr) + 1;
		switch (etype)
		{
			case IET_REL32_EXPORT:
			case IET_IMM32_EXPORT:
			case IET_REL64_EXPORT:
			case IET_IMM64_EXPORT:
				tmpex = CAlloc(sizeof(CHashExport));
				tmpex->str  = StrNew(st_ptr);
				tmpex->type = HTT_EXPORT_SYS_SYM | HTF_IMM;
				if (etype == IET_IMM32_EXPORT || etype == IET_IMM64_EXPORT)
					tmpex->val = i;
				else
					tmpex->val = i + module_base;
				HashAdd(tmpex, Fs->hash_table);
				SysSymImportsResolve(st_ptr, ld_flags);
				break;

			case IET_REL_I0 ... IET_IMM_I64:
				src = st_ptr - 5;
				LoadOneImport(&src, module_base, ld_flags);
				break;

			case IET_ABS_ADDR:
				if (ld_flags & LDF_NO_ABSS)
					src += i * sizeof(U32);
				else
				{
					count = i;
					for (j = 0; j < count; j++)
					{
						ptr2 = module_base + *src(U32 *)++;
						*ptr2(U32 *) += module_base;
					}
				}
				break;

			start:
				case IET_CODE_HEAP:
					ptr3 = MAlloc(*src(I32 *)++, Fs->code_heap);
					break;

				case IET_ZEROED_CODE_HEAP:
					ptr3 = CAlloc(*src(I32 *)++, Fs->code_heap);
					break;
			end:
				if (*st_ptr)
				{
					tmpex = CAlloc(sizeof(CHashExport));
					tmpex->str	= StrNew(st_ptr);
					tmpex->type	= HTT_EXPORT_SYS_SYM | HTF_IMM;
					tmpex->val	= ptr3;
					HashAdd(tmpex, Fs->hash_table);
				}
				count = i;
				for (j = 0; j < count; j++)
				{
					ptr2 = module_base + *src(U32 *)++;
					*ptr2(I32 *) += ptr3;
				}
				break;

			start:
				case IET_DATA_HEAP:
					ptr3 = MAlloc(*src(I64 *)++);
					break;

				case IET_ZEROED_DATA_HEAP:
					ptr3 = CAlloc(*src(I64 *)++);
					break;
			end:
				if (*st_ptr)
				{
					tmpex = CAlloc(sizeof(CHashExport));
					tmpex->str	= StrNew(st_ptr);
					tmpex->type	= HTT_EXPORT_SYS_SYM | HTF_IMM;
					tmpex->val	= ptr3;
					HashAdd(tmpex, Fs->hash_table);
				}
				count = i;
				for (j = 0; j < count; j++)
				{
					ptr2 = module_base + *src(U32 *)++;
					*ptr2(I64 *) += ptr3;
				}
				break;
		}
	}
}

U0 LoadPass2(U8 *src, U8 *module_base, I64)
{
	U8 *st_ptr;
	I64 i, etype;

	while (etype = *src++)
	{
		i = *src(U32 *)++;
		st_ptr = src;
		src += StrLen(st_ptr) + 1;
		switch (etype)
		{
			case IET_MAIN:
				Call(i + module_base);
				break;

			case IET_ABS_ADDR:
				src += sizeof(U32) * i;
				break;

			case IET_CODE_HEAP:
			case IET_ZEROED_CODE_HEAP:
				src += 4 + sizeof(U32) * i;
				break;

			case IET_DATA_HEAP:
			case IET_ZEROED_DATA_HEAP:
				src += 8 + sizeof(U32) * i;
				break;
		}
	}
}

CBinFile *Load(U8 *filename, I64 ld_flags=0, CBinFile *bfh_addr=INVALID_PTR)
{//Load a .BIN file module into memory.
//bfh_addr==INVALID_PTR means don't care what load addr.
	U8			*fbuf, *module_base, *absname;
	I64			 size, module_align, misalignment;
	CBinFile	*bfh;

	fbuf = ExtDefault(filename, "BIN");
	if (!(bfh = FileRead(fbuf, &size)))
	{
		Free(fbuf);
		return NULL;
	}

	//See $LK,"Patch Table Generation",A="FF:::/Compiler/CMain.CC,IET_ABS_ADDR"$
	module_align = 1 << bfh->module_align_bits;
	if (!module_align || bfh->bin_signature != BIN_SIGNATURE_VAL)
	{
		Free(bfh);
		Free(fbuf);
		throw('BinModul');
	}

	if (bfh_addr == INVALID_PTR)
	{
		if (bfh->org == INVALID_PTR)
		{
			misalignment = module_align - sizeof(CBinFile);
			if (misalignment < 0)
				misalignment &= module_align - 1;
			if (Fs->code_heap != Fs->data_heap)
			{
				if (module_align < 16)
					module_align = 16;
				bfh_addr = MAllocAligned(size, module_align, Fs->code_heap, misalignment);
			}
			else if (module_align > 8)
				bfh_addr = MAllocAligned(size, module_align,, misalignment);
			else
			{//Less than 2Gig system memory
				bfh_addr = bfh;
				goto lo_skip; //File is already in code heap area, don't copy.
			}
		}
		else
			bfh_addr = bfh->org;
	}
	MemCopy(bfh_addr, bfh, size);
	Free(bfh);

	lo_skip:
	module_base = bfh_addr(U8 *) + sizeof(CBinFile);

	absname = FileNameAbs(fbuf);
	Free(fbuf);
	fbuf = StrNew(absname);

	FileExtRemove(fbuf);
	if (fbuf[1] == ':' && StrLen(fbuf) > 2)
		HashGenericAdd(fbuf + 2, HTT_MODULE | HTF_PUBLIC, bfh_addr);

	LoadPass1(bfh_addr(U8 *) + bfh_addr->patch_table_offset, module_base, ld_flags);
	if (!(ld_flags & LDF_JUST_LOAD))
		LoadPass2(bfh_addr(U8 *) + bfh_addr->patch_table_offset, module_base, ld_flags);

	Free(absname);
	Free(fbuf);

	return bfh_addr;
}

U0 LoadKernel()
{
	HashGenericAdd(KERNEL_MODULE_NAME, HTT_MODULE | HTF_PUBLIC, mem_boot_base - sizeof(CBinFile));

	//Abs patches done here $LK,"CPatchTableAbsAddr",A="FF:::/Kernel/KStart32.CC,CPatchTableAbsAddr"$.
	LoadPass1(sys_boot_patch_table_base, mem_boot_base, LDF_NO_ABSS | LDF_SILENT);

	//No main routines
	//	LoadPass2(sys_boot_patch_table_base,mem_boot_base,0);
}
