/*
     RUN_ELF - Load and execute big ending ix86 ELF binaries
     Copyright (C) 2001-2002 Martin Blom <martin@blom.org>
     
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License
     as published by the Free Software Foundation; either version 2
     of the License, or (at your option) any later version.
     
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
     
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/resident.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>

#include <stdio.h>
#include <Amielf.h>

#include "run_elf86.h"

// This flag is called RTF_PPC in MorphOS.
#define RTF_NATIVE (1<<3)

#ifndef FUNCARRAY_32BIT_NATIVE
#define FUNCARRAY_32BIT_NATIVE 0xfffefffe
#endif

#include <CompilerSpecific.h>

#include "elfloader.h"

#define bswap_long(x) \
     ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) | \
      (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))

//#define DEBUG

const char version_str[] = "$VER: run_elf 1.6 (29.5.2002)";

struct Library* _amithlon = NULL;

void* Regs = NULL;

/*** SegTracker stuff ********************************************************/

typedef char* ASMCALL
SegTrack( REG( a0, ULONG  Address ),
	  REG( a1, ULONG* SegNum ),
	  REG( a2, ULONG* Offset ) );

struct  SegSem
{
    struct  SignalSemaphore seg_Semaphore;
    SegTrack*               seg_Find;
};

/*** Stuff that should really have been in a link library ********************/

static UWORD rawputchar_m68k[] __attribute__ ((aligned (4))) = 
{
  0x2C4B,             // MOVEA.L A3,A6
  0x4EAE, 0xFDFC,     // JSR     -$0204(A6)
  0x4E75              // RTS
};

void
KPrintFArgs( UBYTE* fmt, 
             ULONG* args )
{
  RawDoFmt( fmt, args, (void(*)(void)) rawputchar_m68k, SysBase );
}

#define KPrintF( fmt, ... )        \
({                                 \
  ULONG _args[] = { __VA_ARGS__ }; \
  KPrintFArgs( (fmt), _args );     \
})


/*** Display a requester *****************************************************/

void
ReqA( const char* text, APTR args )
{
  struct EasyStruct es = 
  {
    sizeof (struct EasyStruct),
    0,
    (STRPTR) "run_elf",
    (STRPTR) text,
    "OK"
  };

  EasyRequestArgs( NULL, &es, NULL, args );
}

#define Req( fmt, ... )            \
({                                 \
  ULONG _args[] = { __VA_ARGS__ }; \
  ReqA( (fmt), _args );            \
})

/*** A couple of native x86 functiond ****************************************/

typedef APTR ASMCALL
CallNativeInitFunction_proto( REG( d0, struct Library*  library ),
			      REG( d1, BPTR             segList ),
			      REG( d2, struct ExecBase* execbase),
			      REG( a0, APTR             init ) );

typedef struct GuardContext* ASMCALL
GuardEnable_proto( void );

typedef void ASMCALL
GuardDisable_proto( REG( d0, struct GuardContext* ) );

CallNativeInitFunction_proto* CallNativeInitFunction = NULL;
GuardEnable_proto*            GuardEnable            = NULL;
GuardDisable_proto*           GuardDisable           = NULL;


/*** Autoinit funtion definitions ********************************************/

typedef APTR ASMCALL
InitFunction_proto( REG( d0, struct Library*  library ),
                    REG( a0, BPTR             segList ),
                    REG( a6, struct ExecBase* execbase) );

/*** exec.library patch definitions ******************************************/

#define _LVOMakeLibrary     (-84)
#define _LVOMakeFunctions   (-90)
#define _LVOInitResident    (-102)

ULONG ASMCALL
MyMakeFunctions( REG( a0, APTR target ),
                 REG( a1, APTR functionArray ),
                 REG( a2, APTR funcDispBase ) );

struct Library* ASMCALL
MyMakeLibrary( REG( a0, APTR  vectors ),
               REG( a1, APTR  structure ),
               REG( a2, APTR  init ),
               REG( d0, ULONG dSize ),
               REG( d1, BPTR  segList ) );

APTR ASMCALL
MyInitResident( REG( a1, struct Resident* resident ),
                REG( d1, ULONG            segList ) );


typedef ULONG ASMCALL
MakeFunctions_proto( REG( a0, APTR target ),
                     REG( a1, APTR functionArray ),
                     REG( a2, APTR funcDispBase ) );

typedef struct Library* ASMCALL
MakeLibrary_proto( REG( a0, APTR  vectors ),
                   REG( a1, APTR  structure ),
                   REG( a2, APTR  init ),
                   REG( d0, ULONG dSize ),
                   REG( d1, BPTR  segList ) );

typedef APTR ASMCALL
InitResident_proto( REG( a1, struct Resident* resident ),
                    REG( d1, ULONG            segList ) );


MakeFunctions_proto* OldMakeFunctions = NULL;
MakeLibrary_proto*   OldMakeLibrary   = NULL;
InitResident_proto*  OldInitResident  = NULL;


/*** dos.library patch definitions *******************************************/

#define _LVOLoadSeg         (-150)
#define _LVOInternalLoadSeg (-756)
#define _LVONewLoadSeg      (-768)


BPTR ASMCALL
MyInternalLoadSeg( REG( d0, BPTR  fh ),
                   REG( a0, BPTR  table ),
                   REG( a1, LONG* functionarray ),
                   REG( a2, LONG* stack ) );

BPTR ASMCALL
MyLoadSeg( REG( d1, STRPTR name ) );

BPTR ASMCALL
MyNewLoadSeg( REG( d1, STRPTR name ),
              REG( d2, struct TagItem* tags ) );


typedef BPTR ASMCALL 
InternalLoadSeg_proto( REG( d0, BPTR  fh ),
                       REG( a0, BPTR  table ),
                       REG( a1, LONG* functionarray ),
                       REG( a2, LONG* stack ) );

typedef BPTR ASMCALL
LoadSeg_proto( REG( d1, STRPTR name ) );


typedef BPTR ASMCALL
NewLoadSeg_proto( REG( d1, STRPTR name ),
                  REG( d2, struct TagItem* tags ) );


InternalLoadSeg_proto* OldInternalLoadSeg = NULL;
LoadSeg_proto*         OldLoadSeg         = NULL;
NewLoadSeg_proto*      OldNewLoadSeg      = NULL;


/*** misc support code *******************************************************/

static struct ELFDynSymbols SymTab[] = 
{
  { "_CallFunc68k", NULL },
  { "_CallLib68k",  NULL },
  { NULL,           NULL }
};

static BPTR 
LoadELFSeg( STRPTR          name, 
            struct TagItem* tags )
{
  BPTR  s = NULL;
  void* o = NULL;

  (void) tags; // Remove warning

  o = ELFLoadObject( name, &s, SymTab );

  if( o == NULL || s == NULL )
  {
    s = NULL;
  }
  else
  {
    CacheClearU();
  }

  ELFUnLoadObject( o );

  return s;
}


void __chkabort(void)
{
  // Disable it, just in case
}


static struct Node*
FindNode( struct List* list,
          struct Node* node )
{
  struct Node* currentnode;

  for( currentnode = list->lh_Head;
       currentnode->ln_Succ != NULL;
       currentnode = currentnode->ln_Succ )
  {
    if( currentnode == node )
    {
      return currentnode;
    }
  }
  
  return NULL;
}


static void
ShowHit( struct Hit* hit )
{
  int i;

  KPrintF( "\nILLEGAL %s %s %08lx %s %08lx (TCB: %08lx)\n",
	   (ULONG) (hit->cpu == CPU_M68K ? "M68K" : "IX86"),
	   (ULONG) (hit->mode == MODE_READ ? "READ from" : "WRITE to"),
	   (ULONG) hit->addr,
	   (ULONG) (hit->cpu == CPU_M68K ? "near" : "at" ),
	   hit->pc,
	   (ULONG) hit->task );


  if( hit->cpu == CPU_IX86 )
  {
    KPrintF( "EFLAGS: %08lx\n", hit->c.ix86.eflags );
    KPrintF( "      EAX      EBX      ECX      EDX"
	     "      ESI      EDI      ESP      EBP\n" );
    KPrintF( "Regs: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
	     hit->c.ix86.eax, hit->c.ix86.ebx, hit->c.ix86.ecx, hit->c.ix86.edx,
	     hit->c.ix86.esi, hit->c.ix86.edi, hit->c.ix86.esp, hit->c.ix86.ebp );

    KPrintF( "Stck:" );

    for( i = 0; i < 8; ++i )
    {
      KPrintF( " %08lx", bswap_long( hit->stack[ i ] ) );
    }

    KPrintF( "\n     " );

    for( i = 8; i < 16; ++i )
    {
      KPrintF( " %08lx", bswap_long( hit->stack[ i ] ) );
    }

    KPrintF( "\nStck:" );

    for( i = 0; i < 8; ++i )
    {
      KPrintF( " %08lx", hit->stack[ i ] );
    }

    KPrintF( "\n[BE] " );

    for( i = 8; i < 16; ++i )
    {
      KPrintF( " %08lx", hit->stack[ i ] );
    }
  }
  else
  {
    KPrintF( "SR:%04lx\n", hit->c.m68k.sr );
    KPrintF( "Data:" );

    for( i = 0; i < 8; ++i )
    {
      KPrintF( " %08lx", hit->c.m68k.dregs[ i ] );
    }
    
    KPrintF( "\nAddr:" );

    for( i = 0; i < 8; ++i )
    {
      KPrintF( " %08lx", hit->c.m68k.aregs[ i ] );
    }

    KPrintF( "\nStck:" );

    for( i = 0; i < 8; ++i )
    {
      KPrintF( " %08lx", hit->stack[ i ] );
    }

    KPrintF( "\n     " );

    for( i = 8; i < 16; ++i )
    {
      KPrintF( " %08lx", hit->stack[ i ] );
    }
  }
  
  KPrintF( "\n%s", (ULONG) hit->disasm );

  Forbid();

  {
    struct SegSem* seg_sem;
    ULONG seg_num   = 0;
    ULONG offset    = 0;
    char* file_name = NULL;

    seg_sem = (struct SegSem*) FindSemaphore( "SegTracker" );

    if( seg_sem != NULL )
    {
      if( seg_sem->seg_Find != NULL )
      {
	file_name = seg_sem->seg_Find( hit->pc, &seg_num, &offset );

	if( file_name != NULL )
	{
	  KPrintF( "----> %08lx - \"%s\"  Hunk %04lx Offset %08lx\n",
		   hit->pc, (ULONG) file_name, seg_num, offset );
	}
      }
    }

    // See if the task is still around

    if( FindNode( &SysBase->TaskReady, (struct Node*) hit->task ) != NULL ||
	FindNode( &SysBase->TaskWait, (struct Node*) hit->task ) != NULL )
    {
      struct Process* proc = (struct Process*) hit->task;

      KPrintF( "Name: \"%s\"", (ULONG) proc->pr_Task.tc_Node.ln_Name );

      if( proc->pr_Task.tc_Node.ln_Type == NT_PROCESS &&
	  proc->pr_TaskNum != 0 )
      {
	struct CommandLineInterface* cli = BADDR( proc->pr_CLI );

	if( cli->cli_Module != NULL )
	{
	  KPrintF( "  CLI: \"%b\"", cli->cli_CommandName );
	}

	if( file_name != NULL )
	{
	  KPrintF( "  Hunk %04lx Offset %08lx\n",
		   seg_num, offset );
	  
	}
      }

      KPrintF( "\n" );
    }
  }
  
  Permit();
  
}
  


/*** main() ******************************************************************/

int
main( void )
{
  int rc = 0;

  if( sizeof( struct Hit ) != 1024 )
  {
    Printf( "sizeof( struct Hit ) == %ld\n", sizeof( struct Hit ) );
    return 20;
  }
  
  _amithlon = OpenResource( "amithlon.resource" );

  if( _amithlon == NULL )
  {
    Printf( "This program is for Amithlon only.\n" );
    rc = 20;
  }
  else
  {
    void* run_elf86;

    run_elf86 = open_elf( "PROGDIR:run_elf86" );

    if( run_elf86 == NULL )
    {
      Printf( "Unable to load ix86 native functions from 'run_elf86'.\n" );
      rc = 20;
    }
    else
    {
      struct ELFDynSymbols* stab = SymTab;

      // Get the emulations register storage address

      Regs = find_symbol( NULL, "regs" );

      if( Regs == NULL )
      {
	Printf( "Unable to find symbol 'regs'.\n" );
	rc = 20;
      }

      // Load symbols from 'run_elf86'

      CallNativeInitFunction = (CallNativeInitFunction_proto*)
	find_slowcall( run_elf86, "CallNativeInitFunction" );

      GuardEnable = (GuardEnable_proto*)
	find_slowcall( run_elf86, "GuardEnable" );
      
      GuardDisable = (GuardDisable_proto*)
	find_slowcall( run_elf86, "GuardDisable" );

      if( CallNativeInitFunction == NULL )
      {
	Printf( "Unable to find symbol 'CallNativeInitFunction'.\n" );
	rc = 20;
      }

      if( GuardEnable == NULL )
      {
	Printf( "Unable to find symbol 'GuardEnable'.\n" );
	rc = 20;
      }

      if( GuardDisable == NULL )
      {
	Printf( "Unable to find symbol 'GuardDisable'.\n" );
	rc = 20;
      }

      // Load all symbols that will be resolved by the ELF loader
      
      while( stab->name != NULL )
      {
	stab->addr = find_symbol( run_elf86, stab->name );

	if( stab->addr == NULL )
	{
	  Printf( "Unable to find symbol '%s'.\n",
		  (ULONG) stab->name );
	  rc = 20;
	}
	  
	++stab;
      }

      if( rc == 0 )
      {
	struct RDArgs* ra = NULL;
	struct
	{
	    STRPTR file;
	    ULONG  guard;
	    ULONG  patch;
	    STRPTR args;
	} args = { NULL, FALSE, FALSE, "" };

	ra = ReadArgs( "FILE,GUARD/S,PATCH/S,ARGS/F", (ULONG*) &args, NULL );

	if( ra == NULL || 
	    ( args.file == NULL && ! args.patch) || 
	    ( args.file != NULL && args.patch ) )
	{
	  Printf( "Usage: run_elf [GUARD] <program> [<arguments>]\n" );
	  Printf( "   or: run_elf [GUARD] PATCH\n" );
	  rc = 20;
	}
	else
	{
	  struct GuardContext* guard_context = NULL;
	  
	  if( args.guard )
	  {
	    guard_context = GuardEnable();

	    if( guard_context == NULL )
	    {
	      Printf( "Unable to install guard.\n" );
	    }
	  }
	  
	  if( args.patch )
	  {
	    // Avoid race conditions when patching:
	    Forbid();

	    // Ah, I just love SetFunction(). Don't you agree, Frieden?
	    // SCNR ... :-)

	    OldMakeFunctions = (MakeFunctions_proto*)
	      SetFunction( (struct Library*) SysBase,
			   _LVOMakeFunctions,
			   (ULONG (*)(void)) MyMakeFunctions );

	    OldMakeLibrary = (MakeLibrary_proto*)
	      SetFunction( (struct Library*) SysBase,
			   _LVOMakeLibrary,
			   (ULONG (*)(void)) MyMakeLibrary );

	    OldInitResident = (InitResident_proto*)
	      SetFunction( (struct Library*) SysBase,
			   _LVOInitResident,
			   (ULONG (*)(void)) MyInitResident );

//          OldInternalLoadSeg = (InternalLoadSeg_proto*)
//            SetFunction( (struct Library*) DOSBase, 
//                         _LVOInternalLoadSeg,
//                         (ULONG (*)(void)) MyInternalLoadSeg );

	    OldLoadSeg = (LoadSeg_proto*)
	      SetFunction( (struct Library*) DOSBase, 
			   _LVOLoadSeg,
			   (ULONG (*)(void)) MyLoadSeg );

	    OldNewLoadSeg = (NewLoadSeg_proto*)
	      SetFunction( (struct Library*) DOSBase, 
			   _LVONewLoadSeg,
			   (ULONG (*)(void)) MyNewLoadSeg );

	    while( TRUE )
	    {
	      ULONG signals;

	      Delay( 1 );

	      signals = SetSignal( 0,
				   SIGBREAKF_CTRL_C |
				   SIGBREAKF_CTRL_E |
				   SIGBREAKF_CTRL_F );

	      if( signals & SIGBREAKF_CTRL_C )
	      {
#ifdef DEBUG
		break;
#else
		Printf( "I will not! Go away yourself.\n" );
#endif
	      }

	      if( signals & SIGBREAKF_CTRL_E )
	      {
		if( guard_context == NULL )
		{
		  guard_context = GuardEnable();

		  if( guard_context != NULL )
		  {
		    Printf( "Guard installed.\n" );
		  }
		  else
		  {
		    Printf( "Unable to install guard.\n" );
		  }
		}
		else
		{
		  Printf( "Guard already installed.\n" );
		}
	      }

	      if( signals & SIGBREAKF_CTRL_F )
	      {
		if( guard_context != NULL )
		{
		  GuardDisable( guard_context );
		  guard_context = NULL;
		  Printf( "Guard removed.\n" );
		}
		else
		{
		  Printf( "Guard already removed.\n" );
		}
	      }

	      if( guard_context != NULL )
	      {
		while( guard_context->read_index != guard_context->write_index )
		{
		  ShowHit( &guard_context->hits[ guard_context->read_index ] );
		  
		  ++guard_context->read_index;
		}
	      }
	      
	    }

	    SetFunction( (struct Library*) SysBase,
			 _LVOMakeFunctions,
			 (ULONG (*)(void)) OldMakeFunctions );

	    SetFunction( (struct Library*) SysBase,
			 _LVOMakeLibrary,
			 (ULONG (*)(void)) OldMakeLibrary );

	    SetFunction( (struct Library*) SysBase,
			 _LVOInitResident,
			 (ULONG (*)(void)) OldInitResident );

//          SetFunction( (struct Library*) DOSBase,
//                       _LVOInternalLoadSeg,
//                       (ULONG (*)(void)) OldInternalLoadSeg );

	    SetFunction( (struct Library*) DOSBase,
			 _LVOLoadSeg,
			 (ULONG (*)(void)) OldLoadSeg );

	    SetFunction( (struct Library*) DOSBase,
			 _LVONewLoadSeg,
			 (ULONG (*)(void)) OldNewLoadSeg );

	    Permit();
	  }
	  else
	  {
	    BPTR s = NULL;

	    s = LoadELFSeg( args.file, NULL );

	    if( s == 0 )
	    {
	      Printf( "Unable to load ELF object %s\n", (ULONG) args.file );
	      rc = 20;
	    }
	    else
	    {
	      int   cmdlen  = strlen( args.args ) + 1;
	      char* cmdline;

	      cmdline = AllocVec( cmdlen + 1, MEMF_ANY );

	      if( cmdline == NULL )
	      {
		Printf( "Out of memory.\n" );
		rc = 20;
	      }
	      else
	      {
		struct Task* me = FindTask( NULL );

		strcpy( cmdline, args.args );
		cmdline[ cmdlen - 1 ] = '\n';
		cmdline[ cmdlen ] = 0;

		SetProgramName( args.file );
		rc = RunCommand( s, 
				 me->tc_SPUpper - me->tc_SPLower, 
				 cmdline, cmdlen );

		FreeVec( cmdline );
	      }

	      UnLoadSeg( s );
	    }
	  }

	  if( guard_context != NULL )
	  {
	    GuardDisable( guard_context );
	  }
	}

	FreeArgs( ra );
      }

      close_elf( run_elf86 );
    }
  }

  return rc;
}


/*** exec.library patches follows ********************************************/

static APTR
CallInitFunc( struct Resident* resident,
              APTR             init,
              struct Library*  library,
              BPTR             segList )
{
  APTR res;
  
#ifdef DEBUG
  KPrintF( "CallInitFunc( 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx )\n",
	   (ULONG) resident, (ULONG) init, (ULONG) library, segList );
#endif

  if( resident->rt_Flags & RTF_NATIVE )
  {
//  __asm__("illegal");
    res = CallNativeInitFunction( library, segList, SysBase, init );
//  __asm__("illegal");
  }
  else
  {
    InitFunction_proto* i = (InitFunction_proto*) init;

    res = i( library, segList, SysBase );
  }

#ifdef DEBUG
  KPrintF( "CallInitFunc -> 0x%08lx\n", (ULONG) res );
#endif

  return res;
}


struct EntryTrampoline
{
    UBYTE mov;
    ULONG regs;
    UBYTE jmp;
    ULONG addr;
    UBYTE nop1;
    UBYTE nop2;
} __attribute__((packed));


#define SET_ENTRYTRAMPOLINE(tp,funcptr)                                 \
 ((struct EntryTrampoline*) tp)->mov  = 0xb8;                           \
 ((struct EntryTrampoline*) tp)->regs = bswap_long( (ULONG) Regs );     \
 ((struct EntryTrampoline*) tp)->jmp  = 0xe9;                           \
 ((struct EntryTrampoline*) tp)->addr = bswap_long( (ULONG) (funcptr) - \
                                                    10 - (ULONG) (tp) );\
 ((struct EntryTrampoline*) tp)->nop1 = 0x90;                           \
 ((struct EntryTrampoline*) tp)->nop2 = 0x90;

struct JumpVec
{
    unsigned short jmp;
    unsigned char vec[4];
};

#define __AROS_ASMJMP                   0x4EF9
#define __AROS_SET_VEC(v,a)             (*(ULONG*)(v)->vec=(ULONG)(a))
#define __AROS_GET_VEC(v)               ((APTR)(*(ULONG*)(v)->vec))
#define __AROS_GETJUMPVEC(lib,n)        ((struct JumpVec *)(((UBYTE *)lib)-(n*LIB_VECTSIZE)))
#define __AROS_GETVECADDR(lib,n)        (__AROS_GET_VEC(__AROS_GETJUMPVEC(lib,n)))
#define __AROS_SETVECADDR(lib,n,addr)   (__AROS_SET_VEC(__AROS_GETJUMPVEC(lib,n),(APTR)(addr)))
#define __AROS_INITVEC(lib,n)           __AROS_GETJUMPVEC(lib,n)->jmp = __AROS_ASMJMP, \
                                        __AROS_SETVECADDR(lib,n,0)
ULONG ASMCALL
MyMakeFunctions( REG( a0, APTR target ),
                 REG( a1, APTR functionArray ),
                 REG( a2, APTR funcDispBase ) )
{
  /* This code is from AROS, except for the FUNCARRAY_32BIT_NATIVE part. */

  long n;
//  APTR lastvec;

#ifdef DEBUG
  KPrintF( "MyMakeFunctions( 0x%08lx, 0x%08lx, 0x%08lx )\n",
           (ULONG) target, (ULONG) functionArray, (ULONG) funcDispBase );
#endif

  n = 1;

  if (funcDispBase!=NULL)
  {
    /* If FuncDispBase is non-NULL it's an array of relative offsets */
    WORD *fp=(WORD *)functionArray;

    /* -1 terminates the array */
    while(*fp!=-1)
    {
      /* Decrement vector pointer by one and install vector */
      __AROS_INITVEC(target,n);
      if (*fp)
        __AROS_SETVECADDR(target,n,funcDispBase+*fp);

      /* Use next array entry */
      fp++;
      n++;
    }
  }
  else
  {
    /* If FuncDispBase is NULL it's an array of function pointers */
    void **fp=(void **)functionArray;

    if( *fp == (void*) FUNCARRAY_32BIT_NATIVE )
    {
      struct EntryTrampoline* tp = (struct EntryTrampoline*)
          ( (ULONG) target - ((struct Library*) target)->lib_NegSize );

      ++fp;
      
      /* -1 terminates the array */
      while(*fp!=(void *)-1)
      {
        /* Decrement vector pointer by one and install vector */
        __AROS_INITVEC(target,n);
	if( _amithlon->lib_Version < 2 )
	{
	  if (*fp)
	    __AROS_SETVECADDR(target,n,(ULONG)tp|0x00000001); // "slowcall"

	  /* Install trampoline */
	  SET_ENTRYTRAMPOLINE(tp,*fp);

	  tp++;
	}
	else
	{
	  if (*fp)
	    __AROS_SETVECADDR(target,n,(ULONG)*fp|0x80000001); // "bslowcall"
	}
	  

        /* Use next array entry */
        fp++;
        n++;
      }
    }
    else
    {
      /* -1 terminates the array */
      while(*fp!=(void *)-1)
      {
        /* Decrement vector pointer by one and install vector */
        __AROS_INITVEC(target,n);
        if (*fp)
          __AROS_SETVECADDR(target,n,*fp);

        /* Use next array entry */
        fp++;
        n++;
      }
    }
  }

  n = ((struct Library*) target)->lib_NegSize;

  /* Clear instruction cache for the whole jumptable and trampolines */
  CacheClearE(target - n, n, CACRF_ClearI|CACRF_ClearD);

#ifdef DEBUG
  KPrintF( "MyMakeFunctions -> 0x%ld\n", n );
#endif
  
  /* Return size of jumptable */
  return n;
}

struct Library* ASMCALL
MyMakeLibrary( REG( a0, APTR  vectors ),
               REG( a1, APTR  structure ),
               REG( a2, APTR  init ),
               REG( d0, ULONG dSize ),
               REG( d1, BPTR  segList ) )
{
  /* This code is from AROS, except for the FUNCARRAY_32BIT_NATIVE part. */

  struct Library *library;
  ULONG negsize=0;

#ifdef DEBUG
  KPrintF( "MyMakeLibrary( 0x%08lx, 0x%08lx, 0x%08lx, %ld, %08lx )\n",
           (ULONG) vectors, (ULONG) structure, (ULONG) init, dSize,
	   (ULONG) segList );
#endif

  /* Calculate the jumptable's size */
  if(*(WORD *)vectors==-1)
  {
    /* Count offsets */
    WORD *fp=(WORD *)vectors+1;
    while(*fp++!=-1)
      negsize+=LIB_VECTSIZE;
  }
  else
  {
    /* Count function pointers */
    void **fp=(void **)vectors;
    
    if( *fp == (void*) FUNCARRAY_32BIT_NATIVE )
    {
      fp++;

      while( *fp != (void *) -1 )
      {
        negsize += LIB_VECTSIZE;
	if( _amithlon->lib_Version < 2 )
	{
	  negsize += sizeof( struct EntryTrampoline );
	}
        ++fp;
      }
    }
    else
    {
      while(*fp++!=(void *)-1)
        negsize+=LIB_VECTSIZE;
    }
  }

  /* Align library base */
  negsize=(negsize+3)&~3;

  /* Allocate memory */
  library=(struct Library *)AllocMem(dSize+negsize,MEMF_PUBLIC|MEMF_CLEAR);

  /* And initilize the library */
  if(library!=NULL)
  {
    /* Get real library base */
    library=(struct Library *)((char *)library+negsize);

    /* Write sizes *before* MakeFunctions!! */
    library->lib_NegSize=negsize;
    library->lib_PosSize=dSize;

    /* Build jumptable */
    if(*(WORD *)vectors==-1)
      /* offsets */
      MakeFunctions(library,(WORD *)vectors+1,(ULONG)vectors);
    else
      /* function pointers */
      MakeFunctions(library,vectors,NULL);

    /* Create structure */
    if(structure!=NULL)
      InitStruct(structure,library,0);

    /* Call init vector */
    if(init!=NULL)
    {
      InitFunction_proto* i = (InitFunction_proto*) init;

      library = i( library, segList, SysBase );
    }
  }

#ifdef DEBUG
  KPrintF( "MyMakeLibrary -> 0x%08lx\n", (ULONG) library );
#endif
  
  /* All done */
  return library;
}

APTR ASMCALL
MyInitResident( REG( a1, struct Resident* resident ),
                REG( d1, ULONG            segList ) )
{
  APTR res = NULL;
  /* This code is from AROS, except for the RTF_NATIVE part. */

#ifdef DEBUG
  KPrintF( "MyInitResident( 0x%08lx, 0x%08lx )\n",
           (ULONG) resident, segList );
#endif

  /* Check for validity */
  if(resident->rt_MatchWord != RTC_MATCHWORD ||
     resident->rt_MatchTag != resident)
    goto end;

  /* Depending on the autoinit flag... */
  if(resident->rt_Flags & RTF_AUTOINIT)
  {
    /* ...initialize automatically... */
    struct init
    {
      ULONG dSize;
      APTR vectors;
      APTR structure;
      APTR init;
    };
    struct init *init = (struct init *)resident->rt_Init;
    struct Library *library;

    /*
        Make the library. Don't call the Init routine yet, but delay
        that until we can copy stuff from the tag to the libbase.
    */
    library = MakeLibrary(init->vectors, init->structure,
                          NULL, init->dSize, segList);

    if(library != NULL)
    {
      /*
          Copy over the interesting stuff from the ROMtag, and set the
          library state to indicate that this lib has changed and
          should be checksummed at the next opportunity.

          Don't copy the priority, because a tag's priority doesn't
          mean the same as a lib's priority.
      */
      library->lib_Node.ln_Type = resident->rt_Type;
      library->lib_Node.ln_Name = resident->rt_Name;
      library->lib_Version      = resident->rt_Version;
      library->lib_IdString     = resident->rt_IdString;
      library->lib_Flags        = LIBF_SUMUSED|LIBF_CHANGED;

      /*
          Call the library init vector, if set.
      */
      if(init->init)
      {
        library = CallInitFunc( resident, init->init, library, segList );
      }

      /*
          Test the library base, in case the init routine failed in
          some way.
      */

      if(library != NULL)
      {
        /*
            Add the initialized module to the system.
        */
        switch(resident->rt_Type)
        {
          case NT_DEVICE:
            AddDevice((struct Device *)library);
            break;
          case NT_LIBRARY:
            AddLibrary(library);
            break;
          case NT_RESOURCE:
            AddResource(library);
            break;
        }
      }
    }

    res = library;
  }
  else
  {
    /* ...or let the library do it. */

    res = CallInitFunc( resident, resident->rt_Init, NULL, segList );
  }

  end:
  
#ifdef DEBUG
  KPrintF( "MyInitResident -> 0x%08lx\n", (ULONG) res );
#endif
  return res;
}

/*** dos.library patches follows *********************************************/

BPTR ASMCALL
MyInternalLoadSeg( REG( d0, BPTR  fh ),
                   REG( a0, BPTR  table ),
                   REG( a1, LONG* functionarray ),
                   REG( a2, LONG* stack ) )
{
#ifdef DEBUG
  KPrintF( "MyInternalLoadSeg( 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx )\n",
           fh, table, (ULONG) functionarray, (ULONG) stack );
#endif

  return OldInternalLoadSeg( fh, table, functionarray, stack );
}


BPTR ASMCALL
MyLoadSeg( REG( d1, STRPTR name ) )
{
  BPTR result;

#ifdef DEBUG
  KPrintF( "MyLoadSeg( %s )\n", (ULONG) name );
#endif

  result = OldLoadSeg( name );

  if( result == 0 )
  {
    result = LoadELFSeg( name, NULL );
  }

  return result;
}


BPTR ASMCALL
MyNewLoadSeg( REG( d1, STRPTR name ),
              REG( d2, struct TagItem* tags ) )
{
  BPTR result;

#ifdef DEBUG
  KPrintF( "MyNewLoadSeg( %s, %08lx )\n", (ULONG) name, (ULONG) tags );
#endif

  result = OldNewLoadSeg( name, tags );

  if( result == 0 )
  {
    result = LoadELFSeg( name, tags );
  }

  return result;
}
