/*
** ppc.library emulation
** (c)1998-99 Frank Wille <frank@phoenix.owl.de>
**
** PowerUP ELF libraries (loading, unloading, get symbols)
**
** V0.5d (09.04.1999) phx
**       Use FindNamePPC() instead of scanning the library-list ourselves.
**       Use MPSemaphore LibMPS.
** V0.4c (21.01.1999) phx
**       created
*/

#include "ppclibemu.h"
#include "elflibraries.h"
#include "warpos_protos.h"


/* M68k library support functions */
extern struct ElfLibrary *LoadELFLib68k();
extern void RemoveELFLib68k();

static struct ElfLibrary *(*loadelflib68k)() = LoadELFLib68k;
static void (*removeelflib68k)() = RemoveELFLib68k;



void *GetELFSym(struct ElfObject *eo,char *symname)
/* get value of a symbol in this ELF object */
{
  struct Elf32_Sym *stab = eo->symtab;
  struct ELFSection *es;
  int i = eo->nsyms;

  while (--i) {
    ++stab;
    if ((ELF32_ST_TYPE(stab->st_info) < STT_FILE) &&
        !_strcmp(eo->symnames+stab->st_name,symname)) {
      switch (stab->st_shndx) {
        case SHN_UNDEF:
        case SHN_COMMON:
          return (0);
        case SHN_ABS:
          return ((void *)stab->st_value);
        default:
          if (es = eo->sections[stab->st_shndx])
            return ((void *)((char *)es->address + stab->st_value));
          else
            return ((void *)stab->st_value);
      }
    }
  }
  return (0);
}


static void remlib68k(struct PPCLibBase *ppcbase,struct ElfLibrary *lib)
{
  struct PPCArgs pa;

  pa.PP_Code = (APTR)removeelflib68k;
  pa.PP_Offset = 0;
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_A0] = (ULONG)lib;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase;
  __Run68K(ppcbase->PowerPCBase,&pa);  /* RemoveELFLib68k(lib,ppcbase); */
}


struct ElfLibrary *LoadELFLib(struct PPCLibBase *ppcbase,char *name)
/* Load a library ELF object and relocate it. Initialize library. */
{
  struct PPCBase *wosbase = ppcbase->PowerPCBase;
  struct ElfLibrary *lib;
  struct PPCArgs pa;

  pa.PP_Code = (APTR)loadelflib68k;
  pa.PP_Offset = 0;
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_A0] = (ULONG)name;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase;
  __Run68K(wosbase,&pa);  /* LoadELFLib68k(name,ppcbase); */

  if (lib = (struct ElfLibrary *)pa.PP_Regs[PPREG_D0]) {
    /* ok, initialize library */
    struct ElfLibrary *(*init)(struct ElfLibrary *);

    lib->version = (ULONG)GetELFSym(lib->elf,"__LIB_Version");
    lib->revision = (ULONG)GetELFSym(lib->elf,"__LIB_Revision");
    lib->open_cnt = 0;
    if (init = GetELFSym(lib->elf,"__LIB_Init")) {
      if (init(lib) != lib) {
        /* initialization failed */
        remlib68k(ppcbase,lib);
        lib = NULL;
      }
    }
    if (lib) {
      obtainMPSemaphorePPC(ppcbase,&ppcbase->LibMPS);
      _addtail(&ppcbase->ElfLibraries,&lib->n);
      releaseMPSemaphorePPC(ppcbase,&ppcbase->LibMPS);
    }
  }

  return (lib);
}


void RemoveELFLib(struct PPCLibBase *ppcbase,struct ElfLibrary *lib)
/* Call a library's Expunge function, then remove and free it */
{
  struct ElfLibrary *(*expunge)(struct ElfLibrary *);

  obtainMPSemaphorePPC(ppcbase,&ppcbase->LibMPS);
  if (expunge = GetELFSym(lib->elf,"__LIB_Expunge")) {
    if (expunge(lib) != lib) {
      releaseMPSemaphorePPC(ppcbase,&ppcbase->LibMPS);
      return;
    }
  }
  _remove(&lib->n);
  releaseMPSemaphorePPC(ppcbase,&ppcbase->LibMPS);
  remlib68k(ppcbase,lib);
}


struct ElfLibrary *FindELFLib(struct PPCLibBase *ppcbase,char *name)
/* Find a library in the list of already loaded ELF libraries */
{
  struct PPCBase *wosbase = ppcbase->PowerPCBase;
  struct ElfLibrary *lib;

  obtainMPSemaphorePPC(ppcbase,&ppcbase->LibMPS);
  lib = (struct ElfLibrary *)__FindNamePPC(wosbase,
                                           &ppcbase->ElfLibraries,name);
  releaseMPSemaphorePPC(ppcbase,&ppcbase->LibMPS);
  return (lib);
}
