/*
** ppc.library emulation
** (c)1998-99 Frank Wille <frank@phoenix.owl.de>
**
** PPC object functions (loading, unloading, info, tasks)
**
** V0.7  (11.12.1999) phx
**       Pass a lock to PROGDIR in TaskObject for asynchronous PPC tasks.
**       They will have neither a valid PROGDIR nor a current directory
**       under WarpOS otherwise.
** V0.6g (05.12.1999) phx
**       PPCCreateTask() allows spawning new threads, which use the
**       ELF object structure of their parent task. No reloading or
**       relocating is required. This function is used when passing
**       a PC != 0 to the PowerUp-kernel's PPCCreateTask() function.
** V0.6d (13.05.1999) phx
**       Don't allocate new memory for a second PPC task invocation
**       of the same ELF object. Only PROGBITS sections are reloaded
**       from disk to ensure data consistency.
**       Fix for SAS/C HunkObj-format, which uses the illegal version 0
**       in e_ident[EI_VERSION].
**       ELF sections are already loaded and relocated on PPCLoadObject().
**       This guarantees that PPCGetObjectAttrs() works as expected
**       before PPCCreateTask() was called.
** V0.5d (09.04.1999) phx
**       PPCCreateTask:
**        PPCTASKTAG_INPUT/OUTPUT/ERRORHANDLE overrule NP_xxx tags. It
**        was the other way around before.
**        New tags: PPCTASKTAG_STACKSIZE,STARTUP_MSG,STARTUP_MSGDATA,
**                  STARTUP_MSGLENGTH,STARTUP_MSGID,MSGPORT.
**       TaskObject got an MPSemaphore for multi-processor write access.
**       Support for asynchronous PPC tasks.
**       PPCDeleteTask().
**       CPU-specific semaphores were replaced by MPSemaphore LibMPS.
** V0.4c (23.01.1999) phx
**       No longer use alloc4()/allocp4(), but alloc32()/allocp32(),
**       which guarantee PPC-compliant 32-byte alignment.
**       LoadELFLib68k(), RemoveELFLib68k() are responsible for loading
**       and removing shared ELF libraries (PowerUp Kernel).
**       Argument pointer of PPCRunObject() was not recognized!
**       PPCCreateTask() redesigned. A PPC startup function "kernel_launch"
**       will be called with r2=TaskObject. It will store the TaskObject
**       pointer into tc_UserData, build a V.4 stack a call the ELF
**       object's entry point.
**       New tags supported by PPCCreateTask(): PPCTASKTAG_ERROR,
**        BREAKSIGNAL,INPUTHANDLE,OUTPUTHANDLE,ERRRORHANDLE,NAME,
**        PRIORITY,ARG5-ARG8, NP_Input,Output,Error,CloseInput,CloseOutput,
**        CloseError.
** V0.4  (27.11.1998) phx
**       PPCCreateTask: r3 and r4 default to zero to avoid startup
**       code crashes when checking for args via r3 (PPCLoad-fix).
** V0.1  (15.11.1998) phx
**       First partially working ppc.library emulation. Synchronous PPC
**       tasks, started by runelf, which only use the basic PowerUp kernel
**       functions for I/O, memory and context-switch seem to work fine.
**       Implemented library functions:
**        PPCLoadObject,PPCUnloadObject,PPCRunObject,
**        PPCCreateTask(only synchronous),PPCGetObjectAttrs,
**        PPCLoadObjectTagList
** V0.0  (31.10.1998) phx
**       created
*/

#include "ppclibemu.h"
#include "taskobject.h"
#include "elflibraries.h"
#include "errors.h"
#include "supp.h"
#include "mpsema_protos.h"
#include <powerup/ppclib/object.h>
#include <powerup/ppclib/tasks.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>
#include <proto/powerpc.h>

/* Bug fix for powerup/ppclib/object.h */
#ifdef R_PPC_ADDR16_L
#ifndef R_PPC_ADDR16_LO
#define R_PPC_ADDR16_LO R_PPC_ADDR16_L
#endif
#endif


static void freetaskobj(__reg("a0")struct PPCLibBase *,
                        __reg("a1")struct TaskObject *);
static void freeelfobj(struct ElfObject *);
static BOOL loadelf32(struct ElfObject *,struct Elf32_Shdr *);
static BOOL getsyminfo(struct ElfObject *,struct PPCObjectInfo *,
                       struct Elf32_Sym *);
static APTR loadprogram(struct ElfObject *);
static void freeprogram(struct ElfObject *);
static BOOL relocate(struct ElfObject *);
static BOOL dynamic_linker(int,struct ElfObject *,uint8 *);
static LONG fileLoadHook(__reg("a0")struct Hook *,__reg("a2")APTR,
                         __reg("a1")struct ElfStreamMsg *);
static LONG memLoadHook(__reg("a0")struct Hook *,__reg("a2")unsigned char *,
                        __reg("a1")struct ElfStreamMsg *);
static APTR opstream(struct ElfObject *,APTR);
static BOOL clstream(struct ElfObject *);
static BOOL rdstream(struct ElfObject *,void *,long);
static long skstream(struct ElfObject *,long,long);
static BOOL prstream(struct ElfObject *,long,void *,long);
static void *readsection(struct ElfObject *,struct Elf32_Shdr *);
static struct ELFSection *progsection(struct ElfObject *,struct Elf32_Shdr *);
static BOOL common_symbols(struct ElfObject *);
static BOOL getrelocs(struct ElfObject *,struct Elf32_Shdr *);


static const char ELFid[4] = {
  0x7f,'E','L','F'
};


extern void kernel_entry(void);
extern void kernel_jumptab(void);
extern void kernel_jumptab_end(void);
extern void kernel_launch(void);
extern void kernel_exit(void);
extern BOOL kernel_deletetask(struct PPCLibBase *,struct TaskObject *);
extern const char *kernel_syms[]; /* PowerUp kernel symbols */
extern const char *reloc_name[];  /* names of ELF-PPC relocations */



struct ElfObject *PPCLoadObjectTagList(__reg("a0")struct TagItem *ti,
                                       __reg("a6")struct PPCLibBase *ppcbase)
{
  static const char *FN = "PPCLoadObjectTagList(): ";
  struct ElfObject *elfobj = NULL;
  struct Hook loadDefHook,*ldhook;
  APTR handle;
  char *objname = (char *)__GetTagData(PPCELFLOADTAG_ELFNAME,
                                       (ULONG)"No-Name",ti,
                                       ppcbase->UtilityBase);

  Dprintf(ppcbase,"%sfile name = %s\n",FN,objname);
  _memset(&loadDefHook,sizeof(struct Hook),0);
  ldhook = (struct Hook *)__GetTagData(PPCELFLOADTAG_HOOK,
                                       (ULONG)&loadDefHook,ti,
                                       ppcbase->UtilityBase);

  if (handle = (APTR)__GetTagData(PPCELFLOADTAG_ELFADDRESS,NULL,ti,
                                  ppcbase->UtilityBase)) {
    loadDefHook.h_Entry = (HOOKFUNC)memLoadHook;
    loadDefHook.h_SubEntry = (HOOKFUNC)handle; /* hack: current 'file' ptr */
    loadDefHook.h_Data = (APTR)ppcbase->SysBase;
  }
  else {
    loadDefHook.h_Entry = (HOOKFUNC)fileLoadHook;
    loadDefHook.h_SubEntry = (HOOKFUNC)objname; /* hack: file name for open */
    loadDefHook.h_Data = (APTR)ppcbase->DOSBase;
    handle = (APTR)__GetTagData(PPCELFLOADTAG_FILE,NULL,ti,
                                ppcbase->UtilityBase);
  }

  /* allocate ElfObject structure */
  if (elfobj = alloc32(sizeof(struct ElfObject),ppcbase)) {
    _memset(elfobj,sizeof(struct ElfObject),0);
    elfobj->ppcbase = ppcbase;
    if (elfobj->pool = newpool32(1024,ppcbase)) {
      if ((elfobj->n.ln_Name = allocpstr(elfobj->pool,objname,ppcbase)) &&
          (elfobj->header = allocp32(elfobj->pool,sizeof(struct Elf32_Ehdr),
                                     ppcbase))) {
        if (ldhook == &loadDefHook) {
          _memcpy(&elfobj->defaultHook,ldhook,sizeof(struct Hook));
          elfobj->hook = &elfobj->defaultHook;
        }
        else
          elfobj->hook = ldhook;

        /* open ELF stream for reading */
        if (elfobj->handle = opstream(elfobj,handle)) {
          Dprintf(ppcbase,"%sELF stream opened\n",FN);

          /* read and identify ELF 32bit PowerPC BigEndian header */
          if (rdstream(elfobj,elfobj->header,sizeof(struct Elf32_Ehdr))) {
            struct Elf32_Ehdr *hdr = elfobj->header;

            if (!_strncmp(hdr->e_ident,ELFid,4) &&
                hdr->e_ident[EI_CLASS]==ELFCLASS32 &&
                hdr->e_ident[EI_DATA]==ELFDATA2MSB &&
#if 0  /* SAS/C HunkObj Format uses illegal version 0 */
                hdr->e_ident[EI_VERSION]==1 &&
#endif
                hdr->e_version==1 &&
                (hdr->e_machine==EM_PPC || hdr->e_machine==EM_PPC_OLD ||
                 hdr->e_machine==EM_CYGNUS_POWERPC) && hdr->e_type==ET_REL) {
              struct Elf32_Shdr *shdrs;
              ULONG shdrsize = (ULONG)hdr->e_shnum*(ULONG)hdr->e_shentsize;

              Dprintf(ppcbase,"%self32ppcbe format recognized\n",FN);
              if (shdrs = alloc32(shdrsize,ppcbase)) {
                /* read section header table and parse rest of object */
                if (prstream(elfobj,hdr->e_shoff,shdrs,shdrsize)) {
                  if (loadelf32(elfobj,shdrs)) {
                    Dprintf(ppcbase,"%sELF object loaded (0x%08lx)\n",
                            FN,elfobj);
                    free32(shdrs,ppcbase);
                    /* Init ElfObject semaphore */
                    elfobj->ElfSS.ss_Link.ln_Type = NT_SEMAPHORE;
                    elfobj->ElfSS.ss_Link.ln_Name = elfobj->n.ln_Name;
                    __InitSemaphore(&elfobj->ElfSS,ppcbase->SysBase);
                    /* Add new object to the global ELF-objects list */
                    obtainMPSemaphore68k(ppcbase,&ppcbase->LibMPS);
                    _addtail(&ppcbase->ElfObjects,&elfobj->n);
                    releaseMPSemaphore68k(ppcbase,&ppcbase->LibMPS);
                    loadprogram(elfobj);  /* relocate */
                    return (elfobj);
                  }
                }
                free32(shdrs,ppcbase);
              }
            }
            else
              error2(ppcbase,"Not an ELF32-PPC-BE\nrelocatable object.");
          }
        }
      }
      freeelfobj(elfobj);
    }
  }

  return (NULL);
}


struct ElfObject *PPCLoadObject(__reg("a0")char *filename,
                                __reg("a6")struct PPCLibBase *ppcbase)
{
  struct TagItem ti[2];

  ti[0].ti_Tag = PPCELFLOADTAG_ELFNAME;
  ti[0].ti_Data = (ULONG)filename;
  ti[1].ti_Tag = TAG_END;
  return (PPCLoadObjectTagList(ti,ppcbase));
}


void PPCUnLoadObject(__reg("a0")struct ElfObject *elfobj,
                     __reg("a6")struct PPCLibBase *ppcbase)
{
  Dprintf(ppcbase,"PPCUnLoadObject(): 0x%08lx (%s)\n",
          elfobj,elfobj->n.ln_Name);
  /* remove object from the global ELF-objects list and free its resources */
  obtainMPSemaphore68k(ppcbase,&ppcbase->LibMPS);
  _remove(&elfobj->n);
  releaseMPSemaphore68k(ppcbase,&ppcbase->LibMPS);
  if (elfobj->sections && elfobj->nsects>0)
    freeprogram(elfobj);
  freeelfobj(elfobj);
}


ULONG PPCGetObjectAttrs(__reg("a0")struct ElfObject *elfobj,
                        __reg("a1")struct PPCObjectInfo *info,
                        __reg("a2")struct TagItem *ti,
                        __reg("a6")struct PPCLibBase *ppcbase)
{
  static const char *FN = "PPCGetObjectAttrs(): ";
  BOOL ret = TRUE;
  struct TagItem *elfnametag;

  Dprintf(ppcbase,"%sobj=0x%08lx, info=0x%08lx, tags=0x%08lx\n",
          FN,elfobj,info,ti);
  if (elfobj) {
    if (elfnametag = __FindTagItem(PPCELFINFOTAG_NAME,ti,
                                   ppcbase->UtilityBase))
      return ((ULONG)elfobj->n.ln_Name);
    if (__GetTagData(PPCELFINFOTAG_GLOBAL,FALSE,ti,ppcbase->UtilityBase))
      elfobj = NULL;  /* search global */
  }

  if (info) {
    BOOL relmode = __GetTagData(PPCELFINFOTAG_RELOC,FALSE,ti,
                                ppcbase->UtilityBase);
    struct Hook *scanhook = (struct Hook *)
                            __GetTagData(PPCELFINFOTAG_SCANSYMBOLHOOK,
                                         NULL,ti,ppcbase->UtilityBase);

    Dprintf(ppcbase,"%s%s, relmode=%ld, hook=0x%08lx\n",
            FN,elfobj?"local":"global",relmode,scanhook);
    if (!elfobj) {
      struct ElfObject *nextelf;

      obtainMPSemaphore68k(ppcbase,&ppcbase->LibMPS);
      elfobj = (struct ElfObject *)ppcbase->ElfObjects.lh_Head;
      while (nextelf = (struct ElfObject *)elfobj->n.ln_Succ) {
        ret = scanElfSymbols(elfobj,info,scanhook,relmode);
        if (!scanhook && ret)
          break;
        elfobj = nextelf;
      }
      releaseMPSemaphore68k(ppcbase,&ppcbase->LibMPS);
    }
    else
      ret = scanElfSymbols(elfobj,info,scanhook,relmode);
    if (scanhook)
      ret = TRUE;
  }

  return (ret);
}


struct TaskObject *PPCCreateTask(__reg("a0")struct ElfObject *elfobj,
                                 __reg("a1")struct TagItem *ti,
                                 __reg("a2")struct PPCThread *thrd,
                                 __reg("a6")struct PPCLibBase *ppcbase)
{
  static const char *FN = "PPCCreateTask(): ";
  static const char *nil = "NIL:";
  struct Library *utilitybase = ppcbase->UtilityBase;
  struct TaskObject *to;
  ULONG *err;

  if (elfobj == NULL) {
    error2(ppcbase,"%sELF object\npointer is NULL!",FN);
    return (NULL);
  }
  if ((ULONG)elfobj == THREAD_MAGIC && thrd != NULL) {
    if (thrd->magicID == THREAD_MAGIC) {
      /* a new thread has to be spawned from the current task */
      elfobj = thrd->parentTask->elf;
    }
    else
      thrd = NULL;
  }
  else
    thrd = NULL;

  Dprintf(ppcbase,"%sobj=0x%08lx, tags=0x%08lx, thread=0x%08lx\n",
          FN,elfobj,ti,thrd);
  if (err = (ULONG *)__GetTagData(PPCTASKTAG_ERROR,0,ti,utilitybase))
    *err = ERROR_OK;

  /* allocate and init TaskObject */
  if (to = alloc32c((sizeof(struct TaskObject)+31)&~31,ppcbase)) {
    char *name;
    ULONG i;

    Dprintf(ppcbase,"%sTaskObject (0x%08lx) allocated\n",FN,to);

    /* init MPSemaphore */
    if (!initMPSemaphore68k(ppcbase,&to->mpSema,to,
                            (sizeof(struct TaskObject)+31)&~31)) {
      Dprintf(ppcbase,"%sMPSemaphore init failed!\n",FN);
      free32(to,ppcbase);
      if (err)
        *err = ERROR_MEMORY;
      goto cto_error;
    }

    /* set name, type, priority, etc. */
    to->elf = elfobj;
    to->PPCBase = (struct Library *)ppcbase;
    name = (char *)__GetTagData(PPCTASKTAG_NAME,(ULONG)elfobj->n.ln_Name,ti,
                                utilitybase);
    if (!(to->n.ln_Name = allocpstr(elfobj->pool,name,ppcbase)))
      to->n.ln_Name = elfobj->n.ln_Name;
    to->id = TskObj_idMagic;
    to->n.ln_Type = NT_TASK;
    to->n.ln_Pri = (BYTE)__GetTagData(PPCTASKTAG_PRIORITY,0,ti,utilitybase);

    /* Input Handle */
    to->inputhandle = (BPTR)__GetTagData(NP_Input,0,ti,utilitybase);
    if (!(to->inputhandle = (BPTR)__GetTagData(PPCTASKTAG_INPUTHANDLE,
                                               (ULONG)to->inputhandle,ti,
                                               utilitybase))) {
      if (!(to->inputhandle = __Open((STRPTR)nil,MODE_OLDFILE,
                                     ppcbase->DOSBase))) {
        freetaskobj(ppcbase,to);
        if (err)
          *err = ERROR_INPUTHANDLE;
        goto cto_error;
      }
      to->flags |= TskObjF_closeinp;
    }
    else if (__GetTagData(NP_CloseInput,FALSE,ti,utilitybase))
      to->flags |= TskObjF_closeinp;
    Dprintf(ppcbase,"%sInputHandle: 0x%08lx  close: %ld\n",
            FN,to->inputhandle,(int)(to->flags&TskObjF_closeinp)!=0);

    /* Output Handle */
    to->outputhandle = (BPTR)__GetTagData(NP_Output,0,ti,utilitybase);
    if (!(to->outputhandle = (BPTR)__GetTagData(PPCTASKTAG_OUTPUTHANDLE,
                                                (ULONG)to->outputhandle,ti,
                                                utilitybase))) {
      if (!(to->outputhandle = __Open((STRPTR)nil,MODE_NEWFILE,
                                      ppcbase->DOSBase))) {
        freetaskobj(ppcbase,to);
        if (err)
          *err = ERROR_OUTPUTHANDLE;
        goto cto_error;
      }
      to->flags |= TskObjF_closeout;
    }
    else if (__GetTagData(NP_CloseOutput,FALSE,ti,utilitybase))
      to->flags |= TskObjF_closeout;
    Dprintf(ppcbase,"%sOutputHandle: 0x%08lx  close: %ld\n",
            FN,to->outputhandle,(int)(to->flags&TskObjF_closeout)!=0);

    /* Error Handle */
    to->errorhandle = (BPTR)__GetTagData(NP_Error,0,ti,utilitybase);
    if (!(to->errorhandle = (BPTR)__GetTagData(PPCTASKTAG_ERRORHANDLE,
                                               (ULONG)to->errorhandle,ti,
                                               utilitybase))) {
      if (!(to->errorhandle = __Open((STRPTR)nil,MODE_NEWFILE,
                                     ppcbase->DOSBase))) {
        freetaskobj(ppcbase,to);
        if (err)
          *err = ERROR_INPUTHANDLE;
        goto cto_error;
      }
      to->flags |= TskObjF_closeerr;
    }
    else if (__GetTagData(NP_CloseError,FALSE,ti,utilitybase))
      to->flags |= TskObjF_closeerr;
    Dprintf(ppcbase,"%sErrorHandle: 0x%08lx  close: %ld\n",
            FN,to->errorhandle,(int)(to->flags&TskObjF_closeerr)!=0);

    /* Break Signals */
    if (__GetTagData(PPCTASKTAG_BREAKSIGNAL,FALSE,ti,utilitybase))
      to->flags |= TskObjF_breaksigs;
    Dprintf(ppcbase,"%sBreak signals %sabled\n",
            FN,(to->flags&TskObjF_breaksigs)?"en":"dis");

    /* Stack Size */
    to->stacksize = __GetTagData(PPCTASKTAG_STACKSIZE,8192,ti,utilitybase);

    /* MsgPort and Startup Messages */
    to->startup = (struct PPCMessage *)
                   __GetTagData(PPCTASKTAG_STARTUP_MSG,NULL,ti,
                                utilitybase);
    to->msg_data = (void *)__GetTagData(PPCTASKTAG_STARTUP_MSGDATA,NULL,ti,
                                        utilitybase);
    to->msg_length = __GetTagData(PPCTASKTAG_STARTUP_MSGLENGTH,0,ti,
                                  utilitybase);
    to->msg_id = __GetTagData(PPCTASKTAG_STARTUP_MSGID,0,ti,utilitybase);
    if (__GetTagData(PPCTASKTAG_MSGPORT,FALSE,ti,utilitybase))
      to->flags |= TskObjF_MsgPort;

    /* get arguments */
    for (i=3; i<=10; i++)
      to->args[i-3] = __GetTagData(PPCTASKTAG_ARG1+(i-3),
                                   (i<5) ? 0 : (i<<24)|(i<<16)|(i<<8)|i,
                                   ti,utilitybase);

    /* parent process */
    if (thrd)
      to->m68kParent = thrd->parentTask->m68kParent;
    else
      to->m68kParent = __FindTask(NULL,ppcbase->SysBase);

    /* load and relocate ELF object */
    if (thrd)
      to->start_addr = thrd->pc;
    else
      to->start_addr = loadprogram(elfobj);

    if (to->start_addr) {
      if (__GetTagData(PPCTASKTAG_WAITFINISH,FALSE,ti,utilitybase) &&
          thrd == NULL) {
        /* synchronous PPC task */
        struct PPCArgs pa;

        Dprintf(ppcbase,"%sLaunching synchronous PPC task\n",FN);

        /* init PPCArgs structure */
        _memset(&pa,sizeof(struct PPCArgs),0);
        pa.PP_Code = (APTR)kernel_launch;
        pa.PP_Regs[PPREG_A4] = (ULONG)to;  /* pass TaskObject in r2 */
        pa.PP_Regs[PPREG_D0] = (ULONG)freetaskobj;  /* task-remove in r3 */
        pa.PP_Regs[PPREG_D1] = (ULONG)-1;  /* don't signal us */

        obtainMPSemaphore68k(ppcbase,&ppcbase->LibMPS);
        _addtail(&ppcbase->TaskObjects,&to->n);
        to->flags |= TskObjF_Enq;
        releaseMPSemaphore68k(ppcbase,&ppcbase->LibMPS);
        elfobj->flags |= ElfObjF_Executed;
        __RunPPC(&pa,ppcbase->PowerPCBase);  /* kernel_launch() */
        /* @@@ check for error? */

        Dprintf(ppcbase,"%sSynchronous PPC task %s returns with %ld\n",
                FN,elfobj->n.ln_Name,pa.PP_Regs[0]);
        freetaskobj(ppcbase,to);
        return ((struct TaskObject *)pa.PP_Regs[0]);  /* return exit code */
      }

      else {
        /* asynchronous PPC task */
        struct TaskPPC *ppctsk;
        struct TagItem ti[8];
        BYTE startup_sig = __AllocSignal(-1,ppcbase->SysBase);

        Dprintf(ppcbase,"%sLaunching asynchronous PPC task\n",FN);

        obtainMPSemaphore68k(ppcbase,&ppcbase->LibMPS);
        _addtail(&ppcbase->TaskObjects,&to->n);
        to->flags |= TskObjF_Enq | TskObjF_Async;
        to->progdirLock = __GetProgramDir(ppcbase->DOSBase);

        ti[0].ti_Tag = TASKATTR_CODE;
        ti[0].ti_Data = (ULONG)kernel_launch;
        ti[1].ti_Tag = TASKATTR_NAME;
        ti[1].ti_Data = (ULONG)to->n.ln_Name;
        ti[2].ti_Tag = TASKATTR_PRI;
        ti[2].ti_Data = (ULONG)((LONG)to->n.ln_Pri);
        ti[3].ti_Tag = TASKATTR_STACKSIZE;
        ti[3].ti_Data = to->stacksize;
        ti[4].ti_Tag = TASKATTR_R2;
        ti[4].ti_Data = (ULONG)to;  /* pass TaskObject in r2 */
        ti[5].ti_Tag = TASKATTR_R3;
        ti[5].ti_Data = (ULONG)freetaskobj;  /* task-remove in r3 */
        ti[6].ti_Tag = TASKATTR_R4;
        ti[6].ti_Data = (ULONG)((LONG)startup_sig);
        ti[7].ti_Tag = TAG_END;

        /* launch PPC task asynchronously */
        elfobj->flags |= ElfObjF_Executed;
        ppctsk = __CreatePPCTask(ti,ppcbase->PowerPCBase);
        /* @@@ check for error? */
        releaseMPSemaphore68k(ppcbase,&ppcbase->LibMPS);

        /* wait until PPC task startup completed */
        if (startup_sig >= 0) {
          __Wait(1L<<startup_sig,ppcbase->SysBase);
          __FreeSignal(startup_sig,ppcbase->SysBase);
        }
        else
          __Delay(100,ppcbase->DOSBase);  /* no signal... wait 2s */
        Dprintf(ppcbase,"%sAsynchronous PPC task %s (0x%08lx) "
                "launched => WarpOS task 0x%08lx)\n",
                FN,to->n.ln_Name,to,ppctsk);
        return (to);
      }

    }
    else {
      freetaskobj(ppcbase,to);
      if (err)
        *err = ERROR_NOPC;
      goto cto_error;
    }

  }
  else {
    error2(ppcbase,"Allocation of %ld bytes for TaskObject failed.\n",
           (sizeof(struct TaskObject)+31)&~31);
    if (err)
      *err = ERROR_MEMORY;
  }

cto_error:
  Dprintf(ppcbase,"%sfailed\n",FN);
  return (NULL);
}


ULONG PPCRunObject(__reg("a0")struct ElfObject *elfobj,
                   __reg("a1")void *args,
                   __reg("a6")struct PPCLibBase *ppcbase)
{
  struct TagItem ti[3];

  ti[0].ti_Tag = PPCTASKTAG_ARG1;
  ti[0].ti_Data = (ULONG)args;
  ti[1].ti_Tag = PPCTASKTAG_WAITFINISH;
  ti[1].ti_Data = TRUE;
  ti[2].ti_Tag = TAG_END;
  return ((ULONG)PPCCreateTask(elfobj,ti,NULL,ppcbase));
}


BOOL PPCDeleteTask(__reg("a0")struct TaskObject *to,
                   __reg("a6")struct PPCLibBase *ppcbase)
{
  static const char *FN = "PPCDeleteTask(): ";
  struct PPCArgs pa;

  Dprintf(ppcbase,"%sdeleting task 0x%08lx (%s)\n",FN,to,to->n.ln_Name);

  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)kernel_deletetask;
  pa.PP_Regs[PPREG_D0] = (ULONG)ppcbase;
  pa.PP_Regs[PPREG_D1] = (ULONG)to;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  __RunPPC(&pa,ppcbase->PowerPCBase);  /* kernel_deletetask(ppcbase,to) */

  Dprintf(ppcbase,"%sok, free TaskObject\n",FN);
  freetaskobj(ppcbase,to);  /* free TaskObject */
  return (TRUE);
}


struct ElfLibrary *LoadELFLib68k(__reg("a0")char *name,
                                 __reg("a6")struct PPCLibBase *ppcbase)
/* Load 'name' as an ELF object into memory and relocate it. */
/* Search in current directory first, then in LIBS:PPC/ */
/* NOTE This function will usually be called by Run68k() from a PPC task */
{
  static const char *FN = "LoadELFLib(): ";
  struct ElfObject *elf;
  struct ElfLibrary *lib = NULL;

  if (!(elf = PPCLoadObject(name,ppcbase))) {
    char path[256];

    _strcpy(path,"LIBS:PPC/");
    _strncat(path,name,246);
    if (elf = PPCLoadObject(path,ppcbase))
      Dprintf(ppcbase,"%s%s, ElfObject=0x%08lx\n",FN,path,elf);
    else
      Dprintf(ppcbase,"%sELF library doesn't exist!\n",FN);
  }
  else
    Dprintf(ppcbase,"%s%s, ElfObject=0x%08lx\n",FN,name,elf);

  if (elf) {
    /* ELFObject was found, now load and relocate it into memory */
    APTR start;

    if ((start = loadprogram(elf)) &&
        (lib = (struct ElfLibrary *)
                alloc32c(sizeof(struct ElfLibrary),ppcbase))) {
      if (lib->n.ln_Name = allocpstr(elf->pool,name,ppcbase)) {
        Dprintf(ppcbase,"%sELF library loaded at 0x%08lx  "
                "LibBase=0x%08lx\n",FN,start,lib);
        lib->n.ln_Type = NT_LIBRARY;
        lib->elf = elf;
        __CacheClearU(ppcbase->SysBase);
      }
      else {
        free32(lib,ppcbase);
        lib = NULL;
      }
    }
  }

  if (!lib)
    Dprintf(ppcbase,"%sloading failed!\n",FN);
  return (lib);
}


void RemoveELFLib68k(__reg("a0")struct ElfLibrary *lib,
                     __reg("a6")struct PPCLibBase *ppcbase)
/* Remove library from list, free all memory, unload ELF object */
{
  if (lib) {
    struct ElfObject *elf = lib->elf;

    __CacheClearU(ppcbase->SysBase);  /* @@@ */
    if (elf->sections && elf->nsects>0)
      freeprogram(elf);
    freeelfobj(elf);
    free32(lib,ppcbase);
    Dprintf(ppcbase,"RemoveELFLib() successful\n");
  }
}


static void freetaskobj(__reg("a0")struct PPCLibBase *ppcbase,
                        __reg("a1")struct TaskObject *to)
/* free task object, close handles, etc. */
{
  if (to) {
    if (to->flags & TskObjF_Enq) {
      obtainMPSemaphore68k(ppcbase,&ppcbase->LibMPS);
      _remove(&to->n);
      to->flags &= ~TskObjF_Enq;
      releaseMPSemaphore68k(ppcbase,&ppcbase->LibMPS);
    }
    if ((to->flags & TskObjF_closeerr) && to->errorhandle)
      __Close(to->errorhandle,ppcbase->DOSBase);
    if ((to->flags & TskObjF_closeout) && to->outputhandle)
      __Close(to->outputhandle,ppcbase->DOSBase);
    if ((to->flags & TskObjF_closeinp) && to->inputhandle)
      __Close(to->inputhandle,ppcbase->DOSBase);

    freeMPSemaphore68k(ppcbase,&to->mpSema);
    free32(to,ppcbase);
  }
}


static void freeelfobj(struct ElfObject *elfobj)
/* free all memory connected to an ElfObject */
{
  if (elfobj) {
    if (elfobj->handle)
      clstream(elfobj);
    if (elfobj->pool)
      freepool32(elfobj->pool,elfobj->ppcbase);
    free32(elfobj,elfobj->ppcbase);
  }
}


static BOOL loadelf32(struct ElfObject *eo,struct Elf32_Shdr *shdrs)
/* parse ELF object, initialize ElfObject structure */
{
  struct Elf32_Ehdr *hdr = eo->header;
  struct ELFSection *s;
  uint16 i;

  if ((eo->secnames = readsection(eo,&shdrs[hdr->e_shstrndx])) &&
      (eo->sections = allocp32(eo->pool,(hdr->e_shnum+1)*sizeof(void *),
                               eo->ppcbase))) {
    _memset(eo->sections,(hdr->e_shnum+1)*sizeof(void *),0);
    eo->nsects = hdr->e_shnum + 1;  /* +1 section for COMMON symbols */
    for (i=1; i<hdr->e_shnum; i++) {
      switch (shdrs[i].sh_type) {
        case SHT_PROGBITS:
        case SHT_NOBITS:
          if (!(eo->sections[i] = progsection(eo,&shdrs[i])))
            return (FALSE);
          break;
        case SHT_SYMTAB:
          if (!(eo->symnames = readsection(eo,&shdrs[shdrs[i].sh_link]))
              || !(eo->symtab = readsection(eo,&shdrs[i])))
            return (FALSE);
          eo->nsyms = shdrs[i].sh_size / sizeof(struct Elf32_Sym);
          eo->gsyms = shdrs[i].sh_info;
          break;
        default:
          break;
      }
    }
    for (i=1; i<hdr->e_shnum; i++) {
      switch (shdrs[i].sh_type) {
        case SHT_REL:
        case SHT_RELA:
          if (!getrelocs(eo,&shdrs[i]))
            return (FALSE);
          break;
        default:
          break;
      }
    }
    /* allocate space for Common symbols */
    return (common_symbols(eo));
  }
  return (FALSE);
}


static BOOL scanElfSymbols(struct ElfObject *eo,struct PPCObjectInfo *info,
                          struct Hook *hook,BOOL relmode)
/* Find an ELF symbol by its name or address and return all infos  */
/* in the supplied PPCObjectInfo structure. Return FALSE if symbol */
/* doesn't exist. */
/* ATTENTION: PPCLibBase may be locked at that stage! */
{
  ULONG addr = info->Address;
  char *name = info->Name;

  if (relmode) {
    int i,j;
    struct ELFSection *es;
    struct Elf32_Rela *r;

    for (i=1; i<(eo->nsects-1); i++) {
      if (es = eo->sections[i]) {
        for (j=0,r=es->relocs; j<es->nrel; j++,r++) {
          if (getsyminfo(eo,info,&eo->symtab[ELF32_R_SYM(r->r_info)])) {
            info->Address = (ULONG)es->address + r->r_offset;
            info->Type = PPCELFINFOTYPE_RELOC;
            info->SubType = (ULONG)ELF32_R_TYPE(r->r_info);
            if (!hook) {
              if (info->Address == addr) {
                if (name) {
                  if (!_strcmp(name,info->Name))
                    return (TRUE);
                }
                else
                  return (TRUE);
              }
            }
            else
              _callhook(hook,(APTR)eo,(APTR)info);
          }
        }
      }
    }
  }

  else {
    struct Elf32_Sym *stab = eo->symtab;
    int i = eo->nsyms;

    while (--i) {
      if (getsyminfo(eo,info,++stab)) {
        if (!hook) {
          if (!name) {
            if (info->Size) {
              if (addr>=info->Address && addr<(info->Address+info->Size))
                return (TRUE);
            }
            else {
              if (addr == info->Address)
                return (TRUE);
            }
          }
          else {
            if (!_strcmp(name,info->Name))
              return (TRUE);
          }
        }
        else
          _callhook(hook,(APTR)eo,(APTR)info);
      }
    }
  }
  return (FALSE);
}


static BOOL getsyminfo(struct ElfObject *eo,struct PPCObjectInfo *info,
                       struct Elf32_Sym *stab)
{
  struct ELFSection *es;
  ULONG subtype;

  subtype = (ULONG)ELF32_ST_TYPE(stab->st_info);
  if (subtype < STT_FILE) {
    info->Type = subtype==STT_SECTION ? PPCELFINFOTYPE_SECTION
                                      : PPCELFINFOTYPE_SYMBOL;
    switch (stab->st_shndx) {
      case SHN_UNDEF:
        /* undefined symbols will disappear after relocation */
      case SHN_ABS:
        info->Address = stab->st_value;
        break;
      case SHN_COMMON:
        /* @@@ common symbols should have disappeared after common_symbols() */
        info->Type = PPCELFINFOTYPE_COMSYMBOL;
        info->Address = 0;
        break;
      default:
        if (es = eo->sections[stab->st_shndx])
          info->Address = (ULONG)es->address + stab->st_value;
        else
          info->Address = stab->st_value;
        break;
    }
    info->Name = eo->symnames + stab->st_name;
    info->SubType = subtype;
    info->Binding = (ULONG)ELF32_ST_BIND(stab->st_info);
    info->Size = stab->st_size;
    return (TRUE);
  }
  return (FALSE);
}


static APTR loadprogram(struct ElfObject *eo)
/* load all sections into memory and relocate them */
{
  static const char *FN = "loadprogram(): ";
  struct ELFSection *s;
  uint8 *p,*entry=NULL;
  int i;

  __ObtainSemaphore(&eo->ElfSS,eo->ppcbase->SysBase);
  for (i=0; i<(eo->nsects-1); i++) {
    if (s = eo->sections[i]) {
      BOOL text = !_strcmp(s->name,".text");   /* .text section flag */
      ULONG size = text ? (s->size + (kernel_jumptab_end-kernel_entry))
                        : s->size;

      /* align to 32 bytes and allocate 32-byte aligned memory */
      size = (size+31)&~31;
      if ((p = (uint8 *)s->address) == NULL)
        s->address = p = alloc32c(size,eo->ppcbase);
      if (p) {
        if (!(s->flags & ElfSecF_NOBITS)) {
          /* a PROGBITS section - load it from file */

          if ((eo->flags&ElfObjF_Executed) ||
              !(eo->flags&ElfObjF_Relocated)) {
            Dprintf(eo->ppcbase,"%sreading section %s\n",FN,s->name);
            if (!prstream(eo,s->offset,p,s->size)) {
              entry = NULL;
              break;
            }
          }
          if (text) {
            /* get start address of PPC program in .text */
            entry = p;
            if ((*entry & 0xfc) == 0) {
              Dprintf(eo->ppcbase,"%sgcc traceback status word "
                      "detected\n",FN);
              entry += 4;  /* 1st long reserved for gcc traceback word */
            }
            if ((eo->flags&ElfObjF_Executed) ||
                !(eo->flags&ElfObjF_Relocated)) {
              /* copy kernel stubs */
               Dprintf(eo->ppcbase,"%sentry=0x%08lx, "
                       "invoking dynamic linker\n",FN,entry);
              if (!dynamic_linker(i,eo,p)) {
                entry = NULL;
                break;
              }
            }
          }
        }
        else {
          /* a NOBITS section (.bss, .sbss, etc.) - clear memory */
          if (s->size!=0 && (eo->flags&ElfObjF_Relocated))
            _memset(p,s->size,0);
        }
      }
      else {
        error2(eo->ppcbase,"Failed to allocate %ld bytes\n"
                           "for PPC %s section.",size,s->name);
        entry = NULL;
        break;
      }
    }
  }

  if (entry) {
    if ((eo->flags&ElfObjF_Executed) || !(eo->flags&ElfObjF_Relocated)) {
      if (relocate(eo)) {  /* relocate sections */
        eo->flags |= ElfObjF_Relocated;
      }
      else {
        entry = NULL;
        freeprogram(eo);
      }
    }
  }
  else
    freeprogram(eo);

  __ReleaseSemaphore(&eo->ElfSS,eo->ppcbase->SysBase);
  Dprintf(eo->ppcbase,"%sreturning with entry=0x%08lx\n",FN,entry);
  return (entry);
}


static void freeprogram(struct ElfObject *eo)
{
  struct ELFSection *s;
  int i;

  for (i=0; i<eo->nsects; i++) {
    if (s = eo->sections[i]) {
      if (s->address) {
        free32(s->address,eo->ppcbase);
        s->address = NULL;
      }
    }
  }
}


static BOOL relocate(struct ElfObject *eo)
{
  struct ELFSection *es;
  int shndx;

  for (shndx=0; shndx<(eo->nsects-1); shndx++) {
    if (es = eo->sections[shndx]) {
      BOOL rela = (es->flags & ElfSecF_RELA) != 0;
      struct Elf32_Rela *r;
      int i;

      Dprintf(eo->ppcbase,"relocate(): relocating section %s "
              "at 0x%08lx\n",es->name,es->address);
      for (i=0,r=es->relocs; i<es->nrel; i++,r++) {
        struct Elf32_Sym *sym = &eo->symtab[ELF32_R_SYM(r->r_info)];
        long s = (long)eo->sections[sym->st_shndx]->address + sym->st_value;
        uint8 *p = (uint8 *)es->address + r->r_offset;

        switch (ELF32_R_TYPE(r->r_info)) {
          case R_PPC_NONE:
            break;

          case R_PPC_ADDR32:
            if (rela)
              *(long *)p = s + r->r_addend;
            else
              *(long *)p += s;
            break;

          case R_PPC_ADDR16:
            if (rela)
              *(short *)p = s + r->r_addend;
            else
              *(short *)p += s;
            break;

          case R_PPC_ADDR16_LO:
            if (rela)
              *(short *)p = (s + r->r_addend) & 0xffff;
            else
              *(short *)p = (s + *(short *)p) & 0xffff;
            break;

          case R_PPC_ADDR16_HI:
            if (rela)
              *(short *)p = (s + r->r_addend) >> 16;
            else
              *(short *)p = (s + *(short *)p) >> 16;
            break;

          case R_PPC_ADDR16_HA:
            if (rela)
              s += r->r_addend;
            else
              s += *(short *)p;
            *(short *)p = (s>>16) + ((s&0x8000) ? 1 : 0);
            break;

          case R_PPC_REL24:
            if (rela) {
              s = (s + r->r_addend) - (long)p;
            }
            else {
              if (*p & 0x02)
                s = (s + ((*(long *)p & 0x03fffffc) - 0x04000000)) - (long)p;
              else
                s = (s + (*(long *)p & 0x03fffffc)) - (long)p;
            }
            *(unsigned long *)p = (*(unsigned long *)p & 0xfc000003) |
                                  ((unsigned long)s & 0x03fffffc);
            break;

          case R_PPC_REL32:
            if (rela)
              *(long *)p = (s + r->r_addend) - (long)p;
            else
              *(long *)p = (s + *(long *)p) - (long)p;
            break;

          default:
            error2(eo->ppcbase,"Relocation type %s\nat %s+%ld referencing\n"
                   "symbol %s+%ld\nis not supported.",
                   reloc_name[ELF32_R_TYPE(r->r_info)],es->name,r->r_offset,
                   eo->symnames+sym->st_name,sym->st_value);
            return (FALSE);
        }
      }
    }
  }
  return (TRUE);
}


static BOOL dynamic_linker(int shndx,struct ElfObject *eo,uint8 *p)
/* append kernel-call stub and jump table to .text section and */
/* try to resolve all undefined symbols */
{
  struct Elf32_Sym *sym;
  struct ELFSection *s = eo->sections[shndx];
  uint16 *relocp;
  uint32 base = (uint32)eo->ppcbase;
  uint32 stub_size = (uint32)(kernel_jumptab - kernel_entry);
  int i;

  for (i=1,sym=&eo->symtab[1]; i<eo->nsyms; i++,sym++) {
    if (sym->st_shndx == SHN_UNDEF) {
      /* try to resolve undefined symbol with PowerUp kernel symbols */
      char **ksyms = (char **)kernel_syms;
      uint32 jtab_offs = s->size + stub_size;

      while (*ksyms) {
        if (!_strcmp(*ksyms,eo->symnames+sym->st_name))
          break;
        ksyms++;
        jtab_offs += 8;  /* li r11,offs ; b kernel_entry (8 bytes) */
      }
      if (*ksyms) {  /* resolved! */
        sym->st_value = jtab_offs;
        sym->st_info = ELF32_ST_INFO(STB_GLOBAL,STT_FUNC);
        sym->st_shndx = shndx;
      }
      else {
        error1(eo->ppcbase,"Dynamic linker:\n"
               "Can't resolve symbol\n%s",eo->symnames+sym->st_name);
        return (FALSE);
      }
    }
  }

  /* copy kernel-call stubs */
  _memcpy(p+s->size,(void *)kernel_entry,kernel_jumptab_end-kernel_entry);

  /* Relocate PPCLibBase reference. The kernel_entry stub looks like this: */
  /* 0000  mflr    r0                   */
  /* 0004  stw     r0,4(r1)             */
  /* 0008  stwu    r1,-64(r1)           */
  /* 000c  lis     r12,PPCLibBase@ha    */
  /* 0010  addi    r12,r12,PPCLibBase@l */
  /*  ...                               */
  if (base & 0x8000)
    base += 0x10000;
  relocp = (uint16 *)(p + s->size + 0x000e);
  *relocp = (uint16)(base >> 16);  /* PPCLibBase@ha */
  relocp += 2;
  *relocp = (uint16)(base & 0xffff);  /* PPCLibBase@l */
  Dprintf(eo->ppcbase,"dynamic_linker(): successful, copied kernel stubs "
          "to 0x%08lx\n",p+s->size);

  return (TRUE);
}


static LONG fileLoadHook(__reg("a0")struct Hook *hook,
                         __reg("a2")APTR fh,
                         __reg("a1")struct ElfStreamMsg *esm)
/* default hook for loading an ELF object from a file */
{
  struct Library *dosbase = (struct Library *)hook->h_Data;

  switch (esm->Type) {
    case PPCELFLOADTYPE_OPEN:
      if (!fh)
        return ((ULONG)__Open((STRPTR)hook->h_SubEntry,
                MODE_OLDFILE,dosbase));
      else
        return ((LONG)fh);
    case PPCELFLOADTYPE_CLOSE:
      return ((LONG)__Close((BPTR)fh,dosbase));
    case PPCELFLOADTYPE_READ:
      return ((LONG)__Read((BPTR)fh,(APTR)esm->Arg1,(long)esm->Arg2,dosbase));
    case PPCELFLOADTYPE_SEEK:
      return ((LONG)__Seek((BPTR)fh,(long)esm->Arg1,(long)esm->Arg2,dosbase));
  }
  return (-1);
}


static LONG memLoadHook(__reg("a0")struct Hook *hook,
                        __reg("a2")unsigned char *base,
                        __reg("a1")struct ElfStreamMsg *esm)
/* default hook for loading an ELF object from memory */
{
  unsigned char *p = (unsigned char *)hook->h_SubEntry;

  switch (esm->Type) {
    case PPCELFLOADTYPE_OPEN:
      return ((LONG)p);
    case PPCELFLOADTYPE_CLOSE:
      return (TRUE);
    case PPCELFLOADTYPE_READ:
      __CopyMem((APTR)p,(APTR)esm->Arg1,esm->Arg2,hook->h_Data);
      hook->h_SubEntry = (HOOKFUNC)(p + esm->Arg2);  /* new position */
      return ((LONG)esm->Arg2);
    case PPCELFLOADTYPE_SEEK:
      switch (esm->Arg2) {
        case OFFSET_BEGINNING:
          hook->h_SubEntry = (HOOKFUNC)(base + esm->Arg1);
          break;
        case OFFSET_CURRENT:
          hook->h_SubEntry = (HOOKFUNC)(p + (LONG)esm->Arg1);
          break;
        case OFFSET_END:
          /* @@@ not supported */
        default:
          p = (unsigned char *)(-1);
          break;
      }
      return ((LONG)p);
  }
  return (-1);
}


static APTR opstream(struct ElfObject *eo,APTR handle)
{
  if (!handle) {
    struct ElfStreamMsg esm;

    esm.Type = PPCELFLOADTYPE_OPEN;
    if (!(handle = (APTR)_callhook(eo->hook,handle,&esm)))
      error2(eo->ppcbase,"Can't open %s for reading.",eo->n.ln_Name);
  }
  return (handle);
}


static BOOL clstream(struct ElfObject *eo)
{
  struct ElfStreamMsg esm;

  esm.Type = PPCELFLOADTYPE_CLOSE;
  return ((BOOL)_callhook(eo->hook,eo->handle,&esm));
}


static BOOL rdstream(struct ElfObject *eo,void *buf,long len)
{
  struct ElfStreamMsg esm;
  long r;

  esm.Type = PPCELFLOADTYPE_READ;
  esm.Arg1 = (ULONG)buf;
  esm.Arg2 = (ULONG)len;
  if ((r = (long)_callhook(eo->hook,eo->handle,&esm)) != len) {
    error2(eo->ppcbase,"Read error in ELF stream (%s)!",eo->n.ln_Name);
    return (FALSE);
  }
  return (TRUE);
}


static long skstream(struct ElfObject *eo,long offs,long mode)
{
  struct ElfStreamMsg esm;
  long r;

  esm.Type = PPCELFLOADTYPE_SEEK;
  esm.Arg1 = (ULONG)offs;
  esm.Arg2 = (ULONG)mode;
  if ((r = (long)_callhook(eo->hook,eo->handle,&esm)) == -1)
    error2(eo->ppcbase,"Seek error in ELF stream (%s)!",eo->n.ln_Name);
  return (r);
}


static BOOL prstream(struct ElfObject *eo,long offs,void *buf,long len)
/* position and read stream */
{
  if (skstream(eo,offs,OFFSET_BEGINNING) != -1)
    return (rdstream(eo,buf,len));
  return FALSE;
}


static void *readsection(struct ElfObject *eo,struct Elf32_Shdr *sh)
/* allocate memory and read section contents */
{
  void *p;

  if (p = allocp32(eo->pool,sh->sh_size,eo->ppcbase))
    if (prstream(eo,sh->sh_offset,p,sh->sh_size))
      return (p);
  return (NULL);
}


static struct ELFSection *progsection(struct ElfObject *eo,
                                      struct Elf32_Shdr *sh)
/* Create Section structure from Elf32_Shdr. The contents of these     */
/* sections will be loaded and relocated on demand, e.g. if a new task */
/* is created. */
{
  struct ELFSection *s;

  if (s = allocp32(eo->pool,sizeof(struct ELFSection),eo->ppcbase)) {
    _memset(s,sizeof(struct ELFSection),0);
    s->name = eo->secnames + sh->sh_name;
    s->flags = sh->sh_type==SHT_NOBITS ? ElfSecF_NOBITS:0;
    s->alignment = (uint8)sh->sh_addralign;
    s->offset = sh->sh_offset;
    s->size = sh->sh_size;
    return (s);
  }
  return (NULL);
}


static BOOL common_symbols(struct ElfObject *eo)
/* Create a and initialize Section structure for Common symbols. */
{
  static const char *FN = "common_symbols(): ";
  static const char *bssname = ".bss";
  struct ELFSection *s;
  struct Elf32_Sym *sym;
  uint16 *relocp;
  uint32 base = (uint32)eo->ppcbase;
  uint32 offset,idx,cnt=0;
  int i;

  /* First try to find a .bss, where Common symbols could be appended */
  for (i=0; i<(eo->nsects-1); i++) {
    if (s = eo->sections[i]) {
      if (!_strcmp(s->name,bssname) && (s->flags & ElfSecF_NOBITS)) {
        idx = i;
        offset = s->size;
        Dprintf(eo->ppcbase,"%sfound %s at index %ld with size=%ld\n",
                FN,bssname,idx,offset);
        break;
      }
      else
        s = NULL;
    }
  }

  if (!s) {
    /* No .bss section present, allocate an own one */
    if (s = allocp32(eo->pool,sizeof(struct ELFSection),eo->ppcbase)) {
      _memset(s,sizeof(struct ELFSection),0);
      s->name = allocpstr(eo->pool,(char *)bssname,eo->ppcbase);
      s->flags = ElfSecF_NOBITS;
      s->alignment = 32;
      offset = 0;
      idx = eo->nsects-1;
      eo->sections[idx] = s;
      Dprintf(eo->ppcbase,"%screated new %s at index %ld\n",FN,bssname,idx);
    }
    else
      return (FALSE);
  }

  /* Ok, search for COMMON symbols now */
  for (i=1,sym=&eo->symtab[1]; i<eo->nsyms; i++,sym++) {
    if (sym->st_shndx == SHN_COMMON) {
      offset = (offset + sym->st_value-1) & ~(sym->st_value-1);
      sym->st_value = offset;
      sym->st_shndx = idx;
      offset += sym->st_size;
      cnt++;
    }
  }
  Dprintf(eo->ppcbase,"%sassigned %ld common symbols (%ld bytes)\n",
          FN,cnt,offset-s->size);
  s->size = offset;  /* set new .bss section size */

  return (TRUE);
}


static BOOL getrelocs(struct ElfObject *eo,struct Elf32_Shdr *sh)
/* read relocation entries for a section */
{
  uint32 rsize = sh->sh_entsize;
  int nrelocs = (int)(sh->sh_size/rsize);
  struct ELFSection *s = eo->sections[sh->sh_info];


  s->nrel = nrelocs;
  if (sh->sh_type == SHT_RELA) {
    s->flags |= ElfSecF_RELA;
    if (s->relocs = readsection(eo,sh))
      return (TRUE);
  }
  else {
    struct Elf32_Rela *r;

    if ((r = s->relocs = allocp32(eo->pool,nrelocs*sizeof(struct Elf32_Rela),
                                  eo->ppcbase)) &&
        (skstream(eo,sh->sh_offset,OFFSET_BEGINNING) != -1)) {
      while (nrelocs--) {
        r->r_addend = 0;
        if (!rdstream(eo,r,sizeof(struct Elf32_Rel)))
          return (FALSE);
        r++;
      }
      return (TRUE);
    }        
  }
  return (FALSE);
}
