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;
        }
    }
}

CZXE *Load(U8 *filename, I64 ld_flags=0, CZXE *zxe_addr=INVALID_PTR)
{//Load a .ZXE file module into memory.
//zxe_addr==INVALID_PTR means don't care what load address.
    U8          *fbuf, *module_base, *absname;
    I64          size, module_align, misalignment;
    CZXE        *zxe;

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

    //See Patch Table Generation
    module_align = 1 << zxe->module_align_bits;
    if (!module_align || zxe->signature != ZXE_SIGNATURE_VAL)
    {
        Free(zxe);
        Free(fbuf);
        throw('ZXEModul');
    }

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

    lo_skip:
    module_base = zxe_addr(U8 *) + sizeof(CZXE);

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

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

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

    Free(absname);
    Free(fbuf);

    return zxe_addr;
}

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

    //Abs patches done here 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);
}