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_GLBL_VAR)
        i=tmpex(CHashGlblVar *)->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,cnt,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 {
          cnt=i;
          for (j=0;j<cnt;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);
        }
        cnt=i;
        for (j=0;j<cnt;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);
        }
        cnt=i;
        for (j=0;j<cnt;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=ExtDft(filename,"BIN.Z");
  if (!(bfh=FileRead(fbuf,&size))) {
    Free(fbuf);
    return NULL;
  }

  //See Patch Table Generation
  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;
  }
  MemCpy(bfh_addr,bfh,size);
  Free(bfh);

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

  absname=FileNameAbs(fbuf);
  Free(fbuf);
  fbuf=StrNew(absname);
  FileExtRem(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 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);
}