/*
** ppc.library emulation
** (c)1998-2001 Frank Wille <frank@phoenix.owl.de>
** Special guest star: Harry "Piru" Sintonen <sintonen@iki.fi>
**
** ELF LoadSeg Patch. Direct start of ELF objects from Shell or Workbench.
**
** V0.8e (16.12.2000) phx
**       LoadSeg patch won't be installed when PPCLibF_NoLoadSegPatch is
**       set.
**       Added some debugging output.
** V0.8c (21.10.2000) phx
**       Forgot to free Semaphore when patch is removed.
** V0.7e (24.05.2000) piru
**       Fixed bug from LoadSeg() patch that could have broken AmigaOS
**       overlay system (it was pure luck it had worked before). Also
**       UnLoadSeg() patch decremented patchcallcount before calling oldfunc,
**       for obvious reasons this could have caused problems. Added neat
**       ifdef system to turn on/off few new things. **MAJOR BUGFIX** to
**       V0.7e: ELFLoadSegPatch always returned FALSE due missing {} in
**       else statement. Guess why I *always* write {} even for single
**       operation statements? ;-) Fixed major bug from elfsegment hack:
**       if the library would have got flushed before the segment is
**       executed terrible crash would have occured. This has been a
**       fundamental design flaw that was introduced when ppc.library was
**       made possible to expunge. Now refuces to expunge until all
**       elfsegments are UnLoadSeg()ed.
** V0.7e (14.03.2000) piru
**       Fixed FindSemaphore() call without Forbid() in ELFLoadSegPatch().
**       RemLoadSegPatch() didn't have this problem, it is called within
**       Forbid(). Disabled cli module clear code from ELFLoadSegPatch().
**       Removed unnececcary OpenLibrary/CloseLibrary from patches. As a
**       side effect removing patches is safer now.
** V0.7d (10.03.2000) piru
**       Fixed wrong ioerr in LoadSeg patches in certain cases.
** V0.7c (07.01.2000) phx
**       Taglist for starting a PPC task from Workbench was corrupt
**       (found by Jacek Cybularczyk).
** V0.6c (23.04.1999) phx
**       Enforcer hit fixed: pr_CLI may be NULL.
** V0.5b (18.02.1999) phx
**       Increased default stack size from 16k to 64k.
** V0.5  (24.01.1999) phx
**       Created.
**       Parts are taken from ElfLoadSeg.c by Ralph Schmidt, Phase 5 1998.
*/


/* enable the following to use global ppcbase. -piru */
#define GLOBALPPCBASE

/* enable the following if you want ppc.library to clear
  cli->cli_Module -piru */
/*#define CLEARCLIMODULE*/


#include "ppclibemu.h"
#include "supp.h"
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <workbench/icon.h>
#include <powerup/ppclib/object.h>
#include <powerup/ppclib/tasks.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/powerpc.h>


#define	PATCHNAME       "ELFLoadSeg-Patch"
#define	LVO_LoadSeg     ((void *)-150)
#define	LVO_UnLoadSeg   ((void *)-156)
#define	LVO_NewLoadSeg  ((void *)-768)
#define DEFSTKSIZE      0x10000


struct HunkSegment
{
  ULONG Size;
  struct HunkSegment *Next;
};


static struct ExecBase *sysbase;
static const char *patchname = PATCHNAME;
static APTR OldNewLoadSeg;
static APTR OldLoadSeg;
static APTR OldUnLoadSeg;

/* ppcsegmentcount is used to keep track of number of
   loaded ppcsegments that are using the library code. We
   can not expunge the library until this counter reaches
   zero again (that is, all ppcsegments are unloaded).
   -piru
*/
static ULONG ppcsegmentcount;

#ifdef GLOBALPPCBASE
/* glob_ppcbase is used by patches instead of opening
   ppc.library with OpenLibrary() per call. this speeds
   up and simplifies patches a bit. -piru */
static struct PPCLibBase *glob_ppcbase;
#endif

/* patchcallcount is used by patches to keep track of
   number of tasks calling patches. using counter like
   this we know if our patch code is still being executed,
   and thus know if it is really safe to remove the
   patches and unload the library. -piru */
static ULONG patchcallcount;

/* Library functions */
#undef PPCLoadObject
#undef PPCUnLoadObject
#undef PPCCacheClearE
#undef PPCCreateTask
extern void *PPCLoadObject(__reg("a0")char *,__reg("a6")struct PPCLibBase *);
extern void PPCUnLoadObject(__reg("a0")struct ElfObject *,
                            __reg("a6")struct PPCLibBase *);
extern void PPCCacheClearE(__reg("a0")APTR,__reg("d0")ULONG,
                           __reg("d1")ULONG,__reg("a6")struct PPCLibBase *);
extern void *PPCCreateTask(__reg("a0")void *,__reg("a1")struct TagItem *,
                           __reg("a6")struct PPCLibBase *);

/* elfsegment.asm */
extern void SegmentCode(void);
extern void SegmentCode_Object(void);
extern void SegmentCode_ID(void);
extern void SegmentCode_End(void);


#ifdef __VBCC__
#define SysBase sysbase
#define DOSBase dosbase
#define PowerPCBase ppcbase->ppc_PowerPCBase
#else
#error You have to implement a compatible OS-call technique for your compiler!
#endif



BPTR ELFNewLoadSeg(__reg("d1")char *name,
                   __reg("d2")struct TagItem *lstags,
                   __reg("a6")struct Library *dosbase)
{
  static const char *FN = "NewLoadSeg(): ";
  BPTR (*oldfunc)(__reg("d1")char *,__reg("d2")struct TagItem *,
                  __reg("a6")struct Library *) = OldNewLoadSeg;
  BPTR SegList = oldfunc(name,lstags,dosbase);

  ++patchcallcount;
  if (SegList==NULL) {

    /* NewLoadSeg() failed, save ioerr, we might need it */
    LONG ioerr = IoErr();

#ifdef GLOBALPPCBASE
    struct PPCLibBase *ppcbase = glob_ppcbase;
#else
    /* try to open ppc.library */
    struct PPCLibBase *ppcbase = (struct PPCLibBase *)
                                  OpenLibrary("ppc.library",46);

    if (ppcbase) {
#endif

    /* check for ELF format */
    void *elfobj;
    struct HunkSegment *seg;
    ULONG codesize,allocsize;
    ULONG *objptr;

    if (elfobj = PPCLoadObject(name,ppcbase)) {
      /* Ok, ELF object loaded! */

      codesize = (ULONG)(SegmentCode_End - SegmentCode);
      allocsize = codesize + sizeof(struct HunkSegment);

      if (seg = alloc32(allocsize,ppcbase)) {
        seg->Size = allocsize;
        seg->Next = NULL;
        CopyMemQuick((APTR)SegmentCode,(APTR)&seg[1],codesize);

        /* patch ELF-Object pointer into start-code */
        objptr = (ULONG *)((ULONG)&seg[1] +
                           ((ULONG)(SegmentCode_Object-SegmentCode)+2));
        *objptr = (ULONG)elfobj;
        SegList = MKBADDR(&seg->Next);
        PPCCacheClearE(seg,allocsize,CACRF_ClearI|CACRF_ClearD,ppcbase);

        /* increment global ppcsegment counter! -piru */
        ppcsegmentcount++;

        Dprintf(ppcbase,"%sLoaded %s as ELF object\n",FN,name);
        SetIoErr(RETURN_OK);
      }

      else {
        /* loading ELF object failed - not enough memory */
        Dprintf(ppcbase,"%sLoading %s as ELF object failed\n",FN,name);
        PPCUnLoadObject(elfobj,ppcbase);
        SetIoErr(ERROR_NO_FREE_STORE);
      }
    }

    else {
      /* both NewLoadSeg() and PPCLoadObject() failed - determine
         correct error code. we only trust NewLoadSeg() ioerr for
         certain error codes. it would be nice if PPCLoadObject*
         would set the ioerr correctly. -piru */
      if ((ioerr!=ERROR_NO_FREE_STORE) &&
          (ioerr!=ERROR_OBJECT_IN_USE) &&
          (ioerr!=ERROR_OBJECT_NOT_FOUND) &&
          (ioerr!=ERROR_OBJECT_WRONG_TYPE) &&
          (ioerr!=ERROR_DISK_NOT_VALIDATED) &&
          (ioerr!=ERROR_DEVICE_NOT_MOUNTED) &&
          (ioerr!=ERROR_READ_PROTECTED) &&
          (ioerr!=ERROR_NOT_A_DOS_DISK) &&
          (ioerr!=ERROR_NO_DISK) &&
          (ioerr!=ERROR_BAD_HUNK)) {
        /* neither Hunk- nor ELF-format */
        ioerr = ERROR_OBJECT_WRONG_TYPE;
      }
      SetIoErr(ioerr);
    }
#ifndef GLOBALPPCBASE
      CloseLibrary((struct Library *)ppcbase);
    }
#endif
  }

  patchcallcount--;
  return(SegList);
}


/* changed LoadSeg to also pass table and fh argument (d2 & d3, overlay
   loaders use these), it could have failed easily (before it worked just
   by pure luck, as compiler didn't trash d2 or d3. - piru
*/

BPTR ELFLoadSeg(__reg("d1")char *name,
                __reg("d2")APTR table,
                __reg("d3")BPTR fh,
                __reg("a6")struct Library *dosbase)
{
  static const char *FN = "LoadSeg(): ";
  BPTR (*oldfunc)(__reg("d1")char *,
                  __reg("d2")APTR,
                  __reg("d3")BPTR,
                  __reg("a6")struct Library *) = OldLoadSeg;
  BPTR SegList = oldfunc(name,table,fh,dosbase);

  ++patchcallcount;
  if (SegList==NULL) {

    /* LoadSeg() failed, save ioerr, we might need it */
    LONG ioerr = IoErr();

#ifdef GLOBALPPCBASE
    struct PPCLibBase *ppcbase = glob_ppcbase;
#else
    /* try to open ppc.library */
    struct PPCLibBase *ppcbase = (struct PPCLibBase *)
                                  OpenLibrary("ppc.library",46);

    if (ppcbase) {
#endif

    /* check for ELF format */
    void *elfobj;
    struct HunkSegment *seg;
    ULONG codesize,allocsize;
    ULONG *objptr;

    if (elfobj = PPCLoadObject(name,ppcbase)) {
      /* Ok, ELF object loaded! */

      codesize = (ULONG)(SegmentCode_End - SegmentCode);
      allocsize = codesize + sizeof(struct HunkSegment);

      if (seg = alloc32(allocsize,ppcbase)) {
        seg->Size = allocsize;
        seg->Next = NULL;
        CopyMemQuick((APTR)SegmentCode,(APTR)&seg[1],codesize);

        /* patch ELF-Object pointer into start-code */
        objptr = (ULONG *)((ULONG)&seg[1] +
                           ((ULONG)(SegmentCode_Object-SegmentCode)+2));
        *objptr = (ULONG)elfobj;
        SegList = MKBADDR(&seg->Next);
        PPCCacheClearE(seg,allocsize,CACRF_ClearI|CACRF_ClearD,ppcbase);

        /* increment global ppcsegment counter! -piru */
        ppcsegmentcount++;

        Dprintf(ppcbase,"%sLoaded %s as ELF object\n",FN,name);
        SetIoErr(RETURN_OK);
      }

      else {
        /* loading ELF object failed - not enough memory */
        Dprintf(ppcbase,"%sLoading %s as ELF object failed\n",FN,name);
        PPCUnLoadObject(elfobj,ppcbase);
        SetIoErr(ERROR_NO_FREE_STORE);
      }
    }

    else {
      /* both LoadSeg() and PPCLoadObject() failed - determine
         correct error code. we only trust LoadSeg() ioerr for
         certain error codes. it would be nice if PPCLoadObject*
         would set the ioerr correctly. -piru */
      if ((ioerr!=ERROR_NO_FREE_STORE) &&
          (ioerr!=ERROR_OBJECT_IN_USE) &&
          (ioerr!=ERROR_OBJECT_NOT_FOUND) &&
          (ioerr!=ERROR_OBJECT_WRONG_TYPE) &&
          (ioerr!=ERROR_DISK_NOT_VALIDATED) &&
          (ioerr!=ERROR_DEVICE_NOT_MOUNTED) &&
          (ioerr!=ERROR_READ_PROTECTED) &&
          (ioerr!=ERROR_NOT_A_DOS_DISK) &&
          (ioerr!=ERROR_NO_DISK) &&
          (ioerr!=ERROR_BAD_HUNK)) {
        /* neither Hunk- nor ELF-format */
        ioerr = ERROR_OBJECT_WRONG_TYPE;
      }
      SetIoErr(ioerr);
    }
#ifndef GLOBALPPCBASE
      CloseLibrary((struct Library *)ppcbase);
    }
#endif
  }

  patchcallcount--;
  return(SegList);
}


BOOL ELFUnLoadSeg(__reg("d1")BPTR seglist,
                  __reg("a6")struct Library *dosbase)
{
  BOOL success;
  BOOL (*oldfunc)(__reg("d1")BPTR,  /* was BOOL, fixed. - piru */
                  __reg("a6")struct Library *) = OldUnLoadSeg;

  ++patchcallcount;

  if (seglist) {
    UBYTE *segment_addr;
    char *idptr;

    /* check if it's an ELF object */
    segment_addr = (UBYTE *)BADDR(seglist)+sizeof(ULONG);
    idptr = (char *)&segment_addr[(ULONG)(SegmentCode_ID-SegmentCode)];

    if (!_strcmp(idptr,(char *)SegmentCode_ID)) {

#ifdef GLOBALPPCBASE
      struct PPCLibBase *ppcbase = glob_ppcbase;
#else
      /* try to open ppc.library */
      struct PPCLibBase *ppcbase = (struct PPCLibBase *)
                                    OpenLibrary("ppc.library",46);

      if (ppcbase) {
#endif

      /* unload ELF */
      void *elfobj = (void *) *((ULONG *)
            (&segment_addr[(ULONG)(SegmentCode_Object-SegmentCode)+2]));

      Dprintf(ppcbase,"UnLoadSeg(): Unload ELF\n");
      if (elfobj) {
        PPCUnLoadObject(elfobj,ppcbase);
      }

      /* decrement global ppcsegment counter! -piru */
      ppcsegmentcount--;

#ifndef GLOBALPPCBASE
        CloseLibrary((struct Library *)ppcbase);
      }
#endif
    }
  }

  success = oldfunc(seglist,dosbase);

  patchcallcount--;
  return(success);
}


BOOL ELFLoadSegPatch(__reg("a0")struct PPCLibBase *ppcbase)
/* LoadSeg() patch to allow ELF objects to be started directly. */
{
  struct SignalSemaphore *ss;
  char *ssname;

  if (ppcbase->Flags & PPCLibF_NoLoadSegPatch)
    return (TRUE);
  sysbase = ppcbase->ppc_SysBase;

  /* arbitrate access to the semaphore list */
  Forbid();

  if (!FindSemaphore((char *)patchname)) {
    if ((ss = alloc32c(sizeof(struct SignalSemaphore),ppcbase))
        && (ssname = alloc32(_strlen((char *)patchname)+1,ppcbase))) {
#ifdef CLEARCLIMODULE
      /* see below. -piru */
      struct CommandLineInterface *cli;
#endif

      /* initialize the global ppcsegment counter -piru */
      ppcsegmentcount = 0;

#ifdef GLOBALPPCBASE
      /* store a global copy of ppcbase. -piru */
      glob_ppcbase = ppcbase;
#endif
      /* initialize patchcallcount for patches. -piru */
      patchcallcount = 0;

      _strcpy(ssname,(char *)patchname);
      InitSemaphore(ss);
      ss->ss_Link.ln_Name = ssname;
      ss->ss_Link.ln_Pri = -127;
      AddSemaphore(ss);

      /* patch LVOs of LoadSeg(), UnLoadSeg() and  NewLoadSeg() */
      OldNewLoadSeg = SetFunction(ppcbase->ppc_DOSBase,
                                  LVO_NewLoadSeg,
                                  (unsigned long (*)())ELFNewLoadSeg);
      OldLoadSeg = SetFunction(ppcbase->ppc_DOSBase,
                               LVO_LoadSeg,
                               (unsigned long (*)())ELFLoadSeg);
      OldUnLoadSeg = SetFunction(ppcbase->ppc_DOSBase,
                                 LVO_UnLoadSeg,
                                 (unsigned long (*)())ELFUnLoadSeg);
#ifdef CLEARCLIMODULE
      /* hey phx: what is this code doing here? this is not a
         executable patch, but a library. library is loaded by
         ramlib, and ramlib doesn't have CLI and even if it
         would have it, it shouldn't be touched. -piru */

      if (cli = (struct CommandLineInterface *)BADDR((((struct Process *)
                 FindTask(NULL))->pr_CLI))) {
        cli->cli_Module = NULL;
      }
#endif

    }
    else {     /* BUG: missed {} in 0.7e -piru */
      Permit();
      return (FALSE);  /* out of memory */
    }
  }
  /* patch already installed */
  Permit();
  Dprintf(ppcbase,"LoadSeg-patch installed\n");
  return (TRUE);
}


BOOL RemLoadSegPatch(__reg("a0")struct PPCLibBase *ppcbase)
/* Remove LoadSeg()-patch, when expunging the library */
{
  struct Library *dosbase = ppcbase->ppc_DOSBase;
  struct SignalSemaphore *ss;
  APTR currLoadSeg,currUnLoadSeg,currNewLoadSeg;
  BOOL ret = FALSE;

  if (ppcbase->Flags & PPCLibF_NoLoadSegPatch)
    return (TRUE);

  if (ss = FindSemaphore((char *)patchname)) {

    /* check if someone is still executing our patch code:
       note that this check is not 100% bulletproof, but
       at least as safe as the old OpenLibrary/CloseLibrary
       method. probably safer. -piru */

    if (!patchcallcount) {

      /* if there are ppcsegment(s) in the memory refuce to
         unload! This prevents potential crash, just give
         it a thought and you'll figure out why. Note that
         it is *essential* that patchcallcount is tested
         before ppcsegmentcount. -piru */
      if (!ppcsegmentcount) {

        currNewLoadSeg = SetFunction(dosbase,LVO_NewLoadSeg,OldNewLoadSeg);
        currLoadSeg = SetFunction(dosbase,LVO_LoadSeg,OldLoadSeg);
        currUnLoadSeg = SetFunction(dosbase,LVO_UnLoadSeg,OldUnLoadSeg);
        if (currNewLoadSeg==(APTR)ELFNewLoadSeg &&
            currLoadSeg==(APTR)ELFLoadSeg &&
            currUnLoadSeg==(APTR)ELFUnLoadSeg) {
          /* successful! */
          RemSemaphore(ss);
          free32(ss->ss_Link.ln_Name,ppcbase);
          free32(ss,ppcbase);
#ifdef GLOBALPPCBASE
          /*glob_ppcbase = NULL;*/   /* not needed..-piru  */
#endif
          ret = TRUE;
        }
        else {
          /* patch removal failed - there's another patch active */
          SetFunction(dosbase,LVO_NewLoadSeg,currNewLoadSeg);
          SetFunction(dosbase,LVO_LoadSeg,currLoadSeg);
          SetFunction(dosbase,LVO_UnLoadSeg,currUnLoadSeg);
        }
      }
    }
  }

  if (ret)
    Dprintf(ppcbase,"LoadSeg-patch removed\n");
  return (ret);
}


ULONG RunELF(__reg("a0")char *cmdline,__reg("a1")void *elfobj)
/* Start the ELF object on the PowerPC */
{
  static const char *FN = "RunELF(): ";
  ULONG result = ERROR_NO_FREE_STORE;
  struct PPCLibBase *ppcbase = NULL;
  struct Library *dosbase = NULL;
  struct Process *process = (struct Process *)FindTask(NULL);
  struct CommandLineInterface *cli;
  const char *cmdname;
  void *pool;
  ULONG stksize;
  struct TagItem ti[16];

  /* Note that updating patchcallcount is not needed here since
     ppcsegmentcount prevents the unloading anyways. - piru */

#ifdef GLOBALPPCBASE
  ppcbase = glob_ppcbase;

  if (dosbase = OpenLibrary("dos.library",37)) {
#else
  if ((ppcbase = (struct PPCLibBase *)OpenLibrary("ppc.library",46))
      && (dosbase = OpenLibrary("dos.library",37))) {
#endif

    Dprintf(ppcbase,"%sELF-Object=0x%08lx CmdLine=%s\n",FN,elfobj,cmdline);
    if (pool = newpool32(1024,ppcbase)) {

      if (cli = BADDR(process->pr_CLI)) {
        /* Started from CLI */
        ULONG cmdlinelen = _strlen(cmdline);
        ULONG cmdlen,oldbuf,oldpos,oldend;
        int cpyindex;
        char *p,*args,*rdargsline;
        BPTR inputBPTR;
        struct FileHandle *inputFH;
        struct TagItem infotags[1];
        struct PPCObjectInfo *info;
        BOOL ctrlC;

        cmdname = (const char *)BADDR(cli->cli_CommandName);
        cmdlen = (ULONG)cmdname[0];
        if ((stksize = cli->cli_DefaultStack*sizeof(ULONG)*2) < DEFSTKSIZE) {
          stksize = DEFSTKSIZE;
        }
        Dprintf(ppcbase,"%sCLI start (stack=%ld bytes)\n",FN,stksize);

        /* replace \n by \0 */
        /* NOTE: cmdline is guaranteed to have at least one
           char. -piru */
        p = cmdline + (cmdlinelen-1);
        if (*p == '\n') {
          *p = '\0';
        }

        if (args = allocp32(pool,cmdlinelen+(cmdname?cmdlen+6:0),ppcbase)) {
          if (cmdname) {
            args[0] = '"';
            _memcpy(&args[1],&cmdname[1],cmdlen);
            args[1+cmdlen] = '"';
            args[1+cmdlen+1] = ' ';
            cpyindex = cmdlen+3;
          }
          else {
            cpyindex = 0;
          }
          _memcpy(&args[cpyindex],cmdline,cmdlinelen);

          if (rdargsline = allocp32(pool,cmdlinelen+2,ppcbase)) {
            _memcpy(rdargsline,cmdline,cmdlinelen-1);
            rdargsline[cmdlinelen-1] = '\n';
            rdargsline[cmdlinelen] = '\0';
            Dprintf(ppcbase,"%sArgs = %s",FN,rdargsline);

            /* save I/O state */
            inputBPTR = Input();
            inputFH = (struct FileHandle *)BADDR(inputBPTR);
            oldbuf = inputFH->fh_Buf;
            oldpos = inputFH->fh_Pos;
            oldend = inputFH->fh_End;

#if 0 /* @@@ Hmmm... */
            /* ctrl-c */
            infotags[0].ti_Tag = TAG_END;
            if (PPCGetObjectAttrs(elfobj,&info,infotags)) {
              ctrlC = FALSE;
            }
            else {
              ctrlC = TRUE;
            }
#else
            ctrlC = TRUE;
#endif

            /* create synchronous task from ELF object */
            ti[0].ti_Tag  = PPCTASKTAG_STOPTASK;
            ti[0].ti_Data = FALSE;
            ti[1].ti_Tag  = PPCTASKTAG_WAITFINISH;
            ti[1].ti_Data = TRUE;
            ti[2].ti_Tag  = PPCTASKTAG_INPUTHANDLE;
            ti[2].ti_Data = (ULONG)inputBPTR;
            ti[3].ti_Tag  = PPCTASKTAG_OUTPUTHANDLE;
            ti[3].ti_Data = (ULONG)Output();
            ti[4].ti_Tag  = PPCTASKTAG_ARG1;
            ti[4].ti_Data = (ULONG)args;
            ti[5].ti_Tag  = PPCTASKTAG_ARG2;
            ti[5].ti_Data = NULL;
            ti[6].ti_Tag  = PPCTASKTAG_STACKSIZE;
            ti[6].ti_Data = stksize;
            ti[7].ti_Tag  = NP_CloseInput;
            ti[7].ti_Data = FALSE;
            ti[8].ti_Tag  = NP_CloseOutput;
            ti[8].ti_Data = FALSE;
            ti[9].ti_Tag  = NP_Cli;
            ti[9].ti_Data = TRUE;
            ti[10].ti_Tag = PPCTASKTAG_BREAKSIGNAL;
            ti[10].ti_Data= (ULONG)ctrlC;
            ti[11].ti_Tag = NP_Arguments;
            ti[11].ti_Data= (ULONG)rdargsline;
            ti[12].ti_Tag = NP_Name;
            ti[12].ti_Data= (ULONG)cmdname;
            ti[13].ti_Tag = NP_CommandName;
            ti[13].ti_Data= (ULONG)cmdname;
            ti[14].ti_Tag = NP_StackSize;
            ti[14].ti_Data= stksize;
            ti[15].ti_Tag = TAG_END;

            Dprintf(ppcbase,"%sPPCCreateTask\n",FN);
            result = (ULONG)PPCCreateTask(elfobj,ti,ppcbase);

            Dprintf(ppcbase,"%sTask returned. Clean up.\n",FN);
            UnGetC(inputBPTR,-1);
            inputFH->fh_Buf = oldbuf;
            inputFH->fh_Pos = oldpos;
            if (inputFH->fh_End) {
              inputFH->fh_End = oldend;
            }
          }
        }
      }

      else {
        /* Started from Workbench */
        struct WBStartup *wbmsg;
        struct WBArg *wbarg;
        BPTR newcd=NULL,oldcd;

        cmdname = NULL;
        if ((stksize = process->pr_StackSize * 2) < DEFSTKSIZE) {
          stksize = DEFSTKSIZE;
        }
        Dprintf(ppcbase,"%sWB start (stack=%ld bytes)\n",FN,stksize);

        /* get WB message */
        WaitPort(&process->pr_MsgPort);
        if (wbmsg = (struct WBStartup *)GetMsg(&process->pr_MsgPort)) {
          if (wbarg = wbmsg->sm_ArgList) {
            if (newcd = DupLock(wbarg->wa_Lock)) {
              oldcd = CurrentDir(newcd);
            }
            cmdname = wbarg->wa_Name;
          }

          /* create synchronous task from ELF object */
          ti[0].ti_Tag  = PPCTASKTAG_STOPTASK;
          ti[0].ti_Data = FALSE;
          ti[1].ti_Tag  = PPCTASKTAG_WAITFINISH;
          ti[1].ti_Data = TRUE;
          ti[2].ti_Tag  = PPCTASKTAG_ARG1;
          ti[2].ti_Data = NULL;
          ti[3].ti_Tag  = PPCTASKTAG_ARG2;
          ti[3].ti_Data = (ULONG)wbmsg;
          ti[4].ti_Tag  = PPCTASKTAG_STACKSIZE;
          ti[4].ti_Data = stksize;
          ti[5].ti_Tag  = PPCTASKTAG_BREAKSIGNAL;
          ti[5].ti_Data = TRUE;
          ti[6].ti_Tag  = NP_Name;
          ti[6].ti_Data = (ULONG)cmdname;
          ti[7].ti_Tag  = NP_StackSize;
          ti[7].ti_Data = stksize;
          ti[8].ti_Tag = TAG_END;

          Dprintf(ppcbase,"%sPPCCreateTask\n",FN);
          result = (ULONG)PPCCreateTask(elfobj,ti,ppcbase);

          Dprintf(ppcbase,"%sTask returned. Clean up/reply msg.\n",FN);
          if (newcd) {
            CurrentDir(oldcd);
            UnLock(newcd);
          }
          Forbid();   /* NOTE: do NOT permit! -piru */
          ReplyMsg(&wbmsg->sm_Message);
        }
      }

      freepool32(pool,ppcbase);
    }
    Dprintf(ppcbase,"%sFinished (rc=%ld). Closing libs.\n",FN,result);
    CloseLibrary(dosbase);
#ifndef GLOBALPPCBASE
    CloseLibrary((struct Library *)ppcbase);
#endif
  }

  return(result);
}
