/* Trampoline construction */

/*
 * Copyright 1995 Bruno Haible, <haible@ma2s2.mathematik.uni-karlsruhe.de>
 *
 * This is free software distributed under the GNU General Public Licence
 * described in the file COPYING. Contact the author if you don't have this
 * or can't live with it. There is ABSOLUTELY NO WARRANTY, explicit or implied,
 * on this software.
 */


#include "config.h"
#include "trampoline.h"

#if defined(__rs6000__)
/*
 * A function pointer is a pointer to a data area whose first word contains
 * the actual address of the function.
 */
extern void (*tramp) (); /* trampoline prototype */
/* We don't need to take any special measures to make the code executable
 * since the actual instructions are in the text segment.
 */
#ifndef CODE_EXECUTABLE
#define CODE_EXECUTABLE
#endif
#endif

#if defined(AMIGA)
#include <exec/types.h>
#include <exec/execbase.h>
#include <proto/exec.h>
/* We just need to clear the caches to make the code executable.
 * No need for EXECUTABLE_VIA_ kludges, CODE_EXECUTABLE comes closest.
 */
#ifndef CODE_EXECUTABLE
#define CODE_EXECUTABLE
#endif
#endif

#ifndef CODE_EXECUTABLE
/* How do we make the trampoline's code executable? */
#if defined(HAVE_MACH_VM) || defined(__convex__) || defined(HAVE_WORKING_MPROTECT) || defined(HAVE_SYS_M88KBCS_H)
/* mprotect() [or equivalent] the malloc'ed area. */
#define EXECUTABLE_VIA_MPROTECT
#else
#ifdef HAVE_MMAP
/* Use an mmap'ed page. */
#define EXECUTABLE_VIA_MMAP
#ifdef HAVE_MMAP_ANONYMOUS
/* Use mmap with the MAP_ANONYMOUS or MAP_ANON flag. */
#define EXECUTABLE_VIA_MMAP_ANONYMOUS
#else
/* Use mmap on /dev/zero. */
#define EXECUTABLE_VIA_MMAP_DEVZERO
#endif
#else
#ifdef HAVE_SHM
/* Use an shmat'ed page. */
#define EXECUTABLE_VIA_SHM
#else
??
#endif
#endif
#endif
#endif

#ifndef FOR_AMIGA_CLISP
#include <stdio.h> /* declares fprintf() */
#else
extern BPTR Input_handle;
extern struct ExecBase * const SysBase;
#define stderr Input_handle
#define fprintf BPTRfprintf
#endif

#include <sys/types.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

/* Declare malloc(), free(). */
#ifndef malloc
#ifdef __STDC__
extern RETMALLOCTYPE malloc (MALLOC_SIZE_T size);
#else
extern RETMALLOCTYPE malloc();
#endif
#endif
#ifndef free
#ifdef __STDC__
extern RETFREETYPE free (RETMALLOCTYPE ptr);
#else
extern RETFREETYPE free();
#endif
#endif

/* Declare abort(). */
#ifdef __STDC__
extern ABORT_VOLATILE RETABORTTYPE abort (void);
#else
extern ABORT_VOLATILE RETABORTTYPE abort();
#endif

/* Declare getpagesize(). */
#ifdef HAVE_GETPAGESIZE
#ifdef __STDC__
extern RETGETPAGESIZETYPE getpagesize (void);
#else
extern RETGETPAGESIZETYPE getpagesize ();
#endif
#else
#include <sys/param.h>
#define getpagesize() PAGESIZE
#endif

/* Declare mprotect() or equivalent. */
#ifdef EXECUTABLE_VIA_MPROTECT
#ifdef HAVE_MACH_VM
#include <sys/resource.h>
#include <mach/mach_interface.h>
#ifdef NeXT
#include <mach/mach_init.h>
#endif
#ifdef __osf__
#include <mach_init.h>
#endif
#include <mach/machine/vm_param.h>
#else
#ifdef HAVE_SYS_M88KBCS_H
#include <sys/m88kbcs.h>
#define getpagesize()  4096  /* ?? */
#else
#include <sys/types.h>
#include <sys/mman.h>
#if !defined(__convex__)
#ifdef __STDC__
extern int mprotect (MPROTECT_CONST MMAP_ADDR_T addr, MMAP_SIZE_T len, int prot);
#else
extern int mprotect ();
#endif
#endif
#endif
#endif
#endif

/* Declare mmap(). */
#ifdef EXECUTABLE_VIA_MMAP
#include <sys/types.h>
#include <sys/mman.h>
#if !defined(PROT_EXEC) && defined(PROT_EXECUTE) /* Irix 4.0.5 needs this */
#define PROT_EXEC PROT_EXECUTE
#endif
#ifdef __STDC__
extern RETMMAPTYPE mmap (MMAP_ADDR_T addr, MMAP_SIZE_T len, int prot, int flags, int fd, off_t off);
#else
extern RETMMAPTYPE mmap ();
#endif
#endif

/* Declare open(). */
#ifdef EXECUTABLE_VIA_MMAP_DEVZERO
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#ifdef NEED_SYS_FILE_H
#include <sys/file.h>
#endif
#ifdef __STDC__
#ifdef OPEN_DOTS
extern int open (OPEN_CONST char* path, int flags, ...);
#else
extern int open (OPEN_CONST char* path, int flags, MODE_T mode);
#endif
#else
extern int open ();
#endif
#endif

/* Declare shmget(), shmat(), shmctl(). */
#ifdef EXECUTABLE_VIA_SHM
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#ifdef HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#endif
#ifdef __STDC__
extern int shmget (key_t key, SHMGET_SIZE_T size, int shmflg);
extern RETSHMATTYPE shmat (int shmid, SHMAT_CONST RETSHMATTYPE shmaddr, int shmflg);
#ifdef SHMCTL_DOTS
extern int shmctl (int shmid, int cmd, ...);
#else
extern int shmctl (int shmid, int cmd, struct shmid_ds * buf);
#endif
#else
extern int shmget ();
extern RETSHMATTYPE shmat ();
extern int shmctl ();
#endif
#endif

/* Support for instruction cache flush. */
#ifdef __mips__
#ifdef ultrix
#include <mips/cachectl.h>
#else
#ifdef linux
#include <linux/cachectl.h>
#else
#ifdef HAVE_SYS_CACHECTL_H
#include <sys/cachectl.h>
#endif
#endif
#endif
#endif
#ifdef __m88k__
#include <sys/syslocal.h>
#endif
/* Inline assembly function for instruction cache flush. */
#if defined(__hppa__) || defined(__convex__)
#ifdef __GNUC__
extern inline
#ifdef __hppa__
#include "cache-hppa.c"
#endif
#ifdef __convex__
#include "cache-convex.c"
#endif
#else
extern void __TR_clear_cache();
#endif
#endif

/* Length and alignment of trampoline */
#ifdef __i386__
#define TRAMP_LENGTH 15
#define TRAMP_ALIGN 16  /* 4 for a i386, 16 for a i486 */
#endif
#ifdef __m68k__
#define TRAMP_LENGTH 18
#define TRAMP_ALIGN 16
#endif
#ifdef __mips__
#define TRAMP_LENGTH 32
#define TRAMP_ALIGN 4
#endif
#ifdef __sparc__
#define TRAMP_LENGTH 28
#define TRAMP_ALIGN 16
#endif
#ifdef __alpha__
#define TRAMP_LENGTH 48
#define TRAMP_ALIGN 8
#endif
#ifdef __hppa__
#define TRAMP_LENGTH 56
#define TRAMP_ALIGN 16
#endif
#ifdef __arm__
#define TRAMP_LENGTH 44
#define TRAMP_ALIGN 4
#endif
#ifdef __rs6000__
#define TRAMP_LENGTH 24
#define TRAMP_ALIGN 4
#endif
#ifdef __m88k__
#define TRAMP_LENGTH 32
#define TRAMP_ALIGN 8
#endif
#ifdef __convex__
#define TRAMP_LENGTH 20
#define TRAMP_ALIGN 4
#endif

#ifdef FOR_AMIGA_CLISP
#define malloc(c) 0
#define free(a)
#define abort() fehler_sint64(0)	/* do anything */
#endif

#if !defined(CODE_EXECUTABLE) && !defined(EXECUTABLE_VIA_MPROTECT)
/* AIX doesn't support mprotect() in malloc'ed memory. Must get pages of
 * memory with execute permission via mmap(). Then keep a free list of
 * free trampolines.
 */
static char* freelist = NULL;
#endif

__TR_function alloc_trampoline (address, variable, data)
  __TR_function address;
  void* variable;
  void* data;
{
  char* function;

#if !defined(CODE_EXECUTABLE)
  static long pagesize = 0;
#if defined(EXECUTABLE_VIA_MMAP_DEVZERO)
  static int zero_fd;
#endif
  /* First, get the page size once and for all. */
  if (!pagesize)
    {
#if defined(HAVE_MACH_VM)
      pagesize = vm_page_size;
#else
      pagesize = getpagesize();
#endif
#if defined(EXECUTABLE_VIA_MMAP_DEVZERO)
      zero_fd = open("/dev/zero",O_RDONLY,0644);
      if (zero_fd < 0)
        { fprintf(stderr,"trampoline: Cannot open /dev/zero!\n"); abort(); }
#endif
    }
#endif

  /* 1. Allocate room */

#if !defined(CODE_EXECUTABLE) && !defined(EXECUTABLE_VIA_MPROTECT)
  if (freelist == NULL)
    { /* Get a new page. */
      char* page;
#ifdef EXECUTABLE_VIA_MMAP_ANONYMOUS
      page = mmap(0, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_VARIABLE, -1, 0);
#endif
#ifdef EXECUTABLE_VIA_MMAP_DEVZERO
      page = mmap(0, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, zero_fd, 0);
#endif
#ifdef EXECUTABLE_VIA_SHM
      int shmid = shmget(IPC_PRIVATE, pagesize, 0700|IPC_CREAT);
      if (shmid<0)
        { page = (char*)(-1); }
      else
        { page = shmat(shmid, 0, 0); shmctl(shmid, IPC_RMID, 0); }
#endif
      if (page == (char*)(-1))
        { fprintf(stderr,"trampoline: Out of virtual memory!\n"); abort(); }
      /* Fill it with free trampolines. */
      { char** last = &freelist;
        char* page_end = page + pagesize;
        while (page+TRAMP_LENGTH <= page_end)
          { *last = page; last = (char**)page; page += TRAMP_LENGTH; }
        *last = NULL;
    } }
  function = freelist; freelist = *(char**)freelist;
#else
  { char* room = malloc(sizeof(void*) + TRAMP_LENGTH + TRAMP_ALIGN-1);
    if (!room)
      { fprintf(stderr,"trampoline: Out of virtual memory!\n"); abort(); }
    function = (char*)(((long)room + sizeof(void*) + TRAMP_ALIGN-1) & -TRAMP_ALIGN);
    ((char**)function)[-1] = room; /* backpointer for free_trampoline() */
  }
#endif

  /* 2. Fill out the trampoline */
#ifdef __i386__
  /* function:
   *    movl $<data>,<variable>		C7 05 <variable> <data>
   *    jmp <address>			E9 <address>-<here>
   * here:
   */
  *(short *) (function + 0) = 0x05C7;
  *(long *)  (function + 2) = (long) variable;
  *(long *)  (function + 6) = (long) data;
  *(char *)  (function +10) = 0xE9;
  *(long *)  (function +11) = (long) address - (long) (function + 15);
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x05C7 && \
  *(unsigned char *)  (function +10) == 0xE9
#define tramp_address(function)  \
  *(long *)  (function +11) + (long) (function + 15)
#define tramp_variable(function)  \
  *(long *)  (function + 2)
#define tramp_data(function)  \
  *(long *)  (function + 6)
#endif
#ifdef __m68k__
  /* function:
   *    movel #<data>,<variable>	23 FC <data> <variable>
   *    jmp <address>			4E F9 <address>
   *    nop				4E 71
   */
  *(short *) (function + 0) = 0x23FC;
  *(long *)  (function + 2) = (long) data;
  *(long *)  (function + 6) = (long) variable;
  *(short *) (function +10) = 0x4EF9;
  *(long *)  (function +12) = (long) address;
  *(short *) (function +16) = 0x4E71;
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x23FC && \
  *(unsigned short *) (function +10) == 0x4EF9 && \
  *(unsigned short *) (function +16) == 0x4E71
#define tramp_address(function)  \
  *(long *)  (function +12)
#define tramp_variable(function)  \
  *(long *)  (function + 6)
#define tramp_data(function)  \
  *(long *)  (function + 2)
#endif
#ifdef __mips__
  /* function:
   *    li $2,<data>&0xffff0000		3C 02 hi16(<data>)
   *    ori $2,$2,<data>&0xffff		34 42 lo16(<data>)
   *    sw $2,<variable>		3C 01 hi16(<variable>)
   *    				AC 22 lo16(<variable>)
   *    li $25,<address>&0xffff0000	3C 19 hi16(<address>)
   *    ori $25,$25,<address>&0xffff	37 39 lo16(<address>)
   *    j $25				03 20 00 08
   *    nop				00 00 00 00
   */
  /* What about big endian / little endian ?? */
  *(short *) (function + 0) = 0x3C02;
  *(short *) (function + 2) = (unsigned long) data >> 16;
  *(short *) (function + 4) = 0x3442;
  *(short *) (function + 6) = (unsigned long) data & 0xffff;
  *(short *) (function + 8) = 0x3C01;
  *(short *) (function +10) = (unsigned long) variable >> 16;
  *(short *) (function +12) = 0xAC22;
  *(short *) (function +14) = (unsigned long) variable & 0xffff;
  *(short *) (function +16) = 0x3C19;
  *(short *) (function +18) = (unsigned long) address >> 16;
  *(short *) (function +20) = 0x3739;
  *(short *) (function +22) = (unsigned long) address & 0xffff;
  *(long *)  (function +24) = 0x03200008;
  *(long *)  (function +28) = 0x00000000;
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x3C02 && \
  *(unsigned short *) (function + 4) == 0x3442 && \
  *(unsigned short *) (function + 8) == 0x3C01 && \
  *(unsigned short *) (function +12) == 0xAC22 && \
  *(unsigned short *) (function +16) == 0x3C19 && \
  *(unsigned short *) (function +20) == 0x3739 && \
  *(unsigned long *)  (function +24) == 0x03200008 && \
  *(unsigned long *)  (function +28) == 0x00000000
#define hilo(hiword,loword)  \
  (((unsigned long) (hiword) << 16) | (unsigned long) (loword))
#define tramp_address(function)  \
  hilo(*(unsigned short *) (function +18), *(unsigned short *) (function +22))
#define tramp_variable(function)  \
  hilo(*(unsigned short *) (function +10), *(unsigned short *) (function +14))
#define tramp_data(function)  \
  hilo(*(unsigned short *) (function + 2), *(unsigned short *) (function + 6))
#endif
#ifdef __sparc__
  /* function:
   *    sethi %hi(<variable>),%g1	03000000 | (<variable> >> 10)
   *    sethi %hi(<data>),%g2		05000000 | (<data> >> 10)
   *    or %g2,%lo(<data>),%g2		8410A000 | (<data> & 0x3ff)
   *    st %g2,[%g1+%lo(<variable>)]	C4206000 | (<variable> & 0x3ff)
   *    sethi %hi(<address>),%g1	03000000 | (<address> >> 10)
   *    jmp %g1+%lo(<address>)		81C06000 | (<address> & 0x3ff)
   *    nop				01000000
   */
#define hi(word)  ((unsigned long) (word) >> 10)
#define lo(word)  ((unsigned long) (word) & 0x3ff)
  *(long *) (function + 0) = 0x03000000 | hi(variable);
  *(long *) (function + 4) = 0x05000000 | hi(data);
  *(long *) (function + 8) = 0x8410A000 | lo(data);
  *(long *) (function +12) = 0xC4206000 | lo(variable);
  *(long *) (function +16) = 0x03000000 | hi(address);
  *(long *) (function +20) = 0x81C06000 | lo(address);
  *(long *) (function +24) = 0x01000000;
#define is_tramp(function)  \
  (*(long *) (function + 0) & 0xffc00000) == 0x03000000 && \
  (*(long *) (function + 4) & 0xffc00000) == 0x05000000 && \
  (*(long *) (function + 8) & 0xfffffc00) == 0x8410A000 && \
  (*(long *) (function +12) & 0xfffffc00) == 0xC4206000 && \
  (*(long *) (function +16) & 0xffc00000) == 0x03000000 && \
  (*(long *) (function +20) & 0xfffffc00) == 0x81C06000 && \
   *(long *) (function +24)               == 0x01000000
#define hilo(hiword,loword)  (((hiword) << 10) | ((loword) & 0x3ff))
#define tramp_address(function)  \
  hilo(*(long *) (function +16), *(long *) (function +20))
#define tramp_variable(function)  \
  hilo(*(long *) (function + 0), *(long *) (function +12))
#define tramp_data(function)  \
  hilo(*(long *) (function + 4), *(long *) (function + 8))
#endif
#ifdef __alpha__
  /* function:
   *    br $1,function..ng	00 00 20 C0
   * function..ng:
   *    ldq $2,20($1)		14 00 41 A4
   *    ldq $3,28($1)		1C 00 61 A4
   *    ldq $27,36($1)		24 00 61 A7
   *    stq $2,0($3)		00 00 43 B4
   *    jmp $31,($27),0		00 00 FB 6B
   *    .quad <data>		<data>
   *    .quad <variable>	<variable>
   *    .quad <address>		<address>
   */
  { static int code [6] =
      { 0xC0200000, 0xA4410014, 0xA461001C, 0xA7610024, 0xB4430000, 0x6BFB0000 };
    int i;
    for (i=0; i<6; i++) { ((int *) function)[i] = code[i]; }
    ((long *) function)[3] = (long) data;
    ((long *) function)[4] = (long) variable;
    ((long *) function)[5] = (long) address;
  }
#define is_tramp(function)  \
  ((int *) function)[0] == 0xC0200000 && \
  ((int *) function)[1] == 0xA4410014 && \
  ((int *) function)[2] == 0xA461001C && \
  ((int *) function)[3] == 0xA7610024 && \
  ((int *) function)[4] == 0xB4430000 && \
  ((int *) function)[5] == 0x6BFB0000
#define tramp_address(function)  \
  ((long *) function)[5]
#define tramp_variable(function)  \
  ((long *) function)[4]
#define tramp_data(function)  \
  ((long *) function)[3]
#endif
#ifdef __hppa__
  /* function:
   *    ldil L'<data>,%r20		22800000 | hi(<data>)
   *    ldil L'<variable>,%r19		22600000 | hi(<variable>)
   *    ldo R'<data>(%r20),%r20		36940000 | lo(<data>)
   *    stw %r20,R'<variable>(%r19)	6A740000 | lo(<variable>)
   *    ldil L'<address>,%r21		22A00000 | hi(<address>)
   *    ldo R'<address>(%r21),%r21	36B50000 | lo(<address>)
   *    bb,>=,n %r21,30,function2	C7D5C012
   *    depi 0,31,2,%r21		D6A01C1E
   *    ldw 4(0,%r21),%r19		4AB30008
   *    ldw 0(0,%r21),%r21		4AB50000
   * function2:
   *    ldsid (0,%r21),%r1		02A010A1
   *    mtsp %r1,%sr0			00011820
   *    be,n 0(%sr0,%r21)		E2A00002
   *    nop				08000240
   */
  /* When decoding a 21-bit argument in an instruction, the hppa performs
   * the following bit manipulation:
   * assemble21: x[20]...x[0]
   *       --> x[0] x[11]...x[1] x[15]..x[14] x[20]...x[16] x[13]..x[12]
   * When encoding a 21-bit argument into an instruction, we need the
   * to perform the reverse permutation:
   * permute21:  y[20]...y[0]
   *       --> y[6]...y[2] y[8]..y[7] y[1]..y[0] y[19]...y[9] y[20]
   */
#define assemble21(x)  \
  ((((x) & 0x1) << 20) | (((x) & 0xFFE) << 8) | \
   (((x) & 0xC000) >> 7) | (((x) & 0x1F0000) >> 14) | (((x) & 0x3000) >> 12))
#define permute21(y)  \
  ((((y) & 0x7C) << 14) | (((y) & 0x180) << 7) | (((y) & 0x3) << 12) | \
   (((y) & 0xFFE00) >> 8) | (((y) & 0x100000) >> 20))
#define hi(word)  permute21((unsigned long) (word) >> 11)
#define lo(word)  (((unsigned long) (word) & 0x7FF) << 1)
  *(long *) (function + 0) = 0x22800000 | hi(data);
  *(long *) (function + 4) = 0x22600000 | hi(variable);
  *(long *) (function + 8) = 0x36940000 | lo(data);
  *(long *) (function +12) = 0x6A740000 | lo(variable);
  *(long *) (function +16) = 0x22A00000 | hi(address);
  *(long *) (function +20) = 0x36B50000 | lo(address);
  *(long *) (function +24) = 0xC7D5C012;
  *(long *) (function +28) = 0xD6A01C1E;
  *(long *) (function +32) = 0x4AB30008;
  *(long *) (function +36) = 0x4AB50000;
  *(long *) (function +40) = 0x02A010A1;
  *(long *) (function +44) = 0x00011820;
  *(long *) (function +48) = 0xE2A00002;
  *(long *) (function +52) = 0x08000240;
#define is_tramp(function)  \
  ((long) function & 3) == 0 && \
  (*(long *) (function + 0) & 0xffe00000) == 0x22800000 && \
  (*(long *) (function + 4) & 0xffe00000) == 0x22600000 && \
  (*(long *) (function + 8) & 0xfffff000) == 0x36940000 && \
  (*(long *) (function +12) & 0xfffff000) == 0x6A740000 && \
  (*(long *) (function +16) & 0xffe00000) == 0x22A00000 && \
  (*(long *) (function +20) & 0xfffff000) == 0x36B50000 && \
  *(long *) (function +24) == 0xC7D5C012 && \
  *(long *) (function +28) == 0xD6A01C1E && \
  *(long *) (function +32) == 0x4AB30008 && \
  *(long *) (function +36) == 0x4AB50000 && \
  *(long *) (function +40) == 0x02A010A1 && \
  *(long *) (function +44) == 0x00011820 && \
  *(long *) (function +48) == 0xE2A00002 && \
  *(long *) (function +52) == 0x08000240
#define hilo(hiword,loword)  \
  ((assemble21((unsigned long) (hiword)) << 11) | \
   (((unsigned long) (loword) & 0xFFE) >> 1) \
  )
#define tramp_address(function)  \
  hilo(*(long *) (function +16), *(long *) (function +20))
#define tramp_variable(function)  \
  hilo(*(long *) (function + 4), *(long *) (function +12))
#define tramp_data(function)  \
  hilo(*(long *) (function + 0), *(long *) (function + 8))
#endif
#ifdef __arm__
  /* function:
   *    stmfd   sp!,{r0}			E92D0001
   *    ldr     r0,[pc,#_data-.-8]		E59F0014
   *    ldr     ip,[r0,#0]			E590C000
   *    ldr     r0,[pc,#_variable-.-8]		E59F0010
   *    str     ip,[r0,#0]			E580C000
   *    ldmfd   sp!,{r0}^			E8FD0001
   *    ldr     ip,[pc,#_function-.-8]		E59FC008
   *    ldr     pc,[ip,#0]			E59CF000
   * _data:
   *    .word   <data>				<data>
   * _variable:
   *    .word   <variable>			<variable>
   * _function:
   *    .word   <address>			<address>
   */
  { static long code [8] =
      { 0xE92D0001, 0xE59F0014, 0xE590C000, 0xE59F0010,
        0xE580C000, 0xE8FD0001, 0xE59FC008, 0xE59CF000
      };
    int i;
    for (i=0; i<8; i++) { ((long *) function)[i] = code[i]; }
    ((long *) function)[8] = (long) data;
    ((long *) function)[9] = (long) variable;
    ((long *) function)[10] = (long) address;
  }
#endif
#ifdef __rs6000__
#if 1
  /* function:
   *    .long .tramp
   *    .long .mytoc
   *    .long 0
   * .mytoc:
   *    .long <variable>
   *    .long <data>
   *    .long <address>
   */
  *(long *)  (function + 0) = ((long *) &tramp)[0];
  *(long *)  (function + 4) = (long) (function + 12);
  *(long *)  (function + 8) = 0;
  *(long *)  (function +12) = (long) variable;
  *(long *)  (function +16) = (long) data;
  *(long *)  (function +20) = (long) address;
#define is_tramp(function)  \
  ((long *) function)[0] == ((long *) &tramp)[0]
#define tramp_address(function)  \
  ((long *) function)[5]
#define tramp_variable(function)  \
  ((long *) function)[3]
#define tramp_data(function)  \
  ((long *) function)[4]
#else /* old try */
  /* function:
   *    .long .function			<.function>
   *    .long 0				00 00 00 00
   *    .long 0				00 00 00 00
   * .function:
   *    liu 11,hi16(<variable>)		3D 60 hi16(<variable>)
   *    oril 11,11,lo16(<variable>)	61 6B lo16(<variable>)
   *    liu 10,hi16(<data>)		3D 40 hi16(<data>)
   *    oril 10,10,lo16(<data>)		61 4A lo16(<data>)
   *    st 10,0(11)			91 4B 00 00
   *    liu 10,hi16(<address>)		3D 40 hi16(<address>)
   *    oril 10,10,lo16(<address>)	61 4A lo16(<address>)
   *    l 11,8(10)			81 6A 00 08
   *    l 2,4(10)			80 4A 00 04
   *    l 0,0(10)			80 0A 00 00
   *    mtctr 0				7C 09 03 A6
   *    bctr				4E 80 04 20
   */
  *(long *)  (function + 0) = (long) (function + 12);
  *(long *)  (function + 4) = 0;
  *(long *)  (function + 8) = 0;
  *(short *) (function +12) = 0x3D60;
  *(short *) (function +14) = (unsigned long) variable >> 16;
  *(short *) (function +16) = 0x616B;
  *(short *) (function +18) = (unsigned long) variable & 0xffff;
  *(short *) (function +20) = 0x3D40;
  *(short *) (function +22) = (unsigned long) data >> 16;
  *(short *) (function +24) = 0x614A;
  *(short *) (function +26) = (unsigned long) data & 0xffff;
  *(long *)  (function +28) = 0x914B0000;
  *(short *) (function +32) = 0x3D40;
  *(short *) (function +34) = (unsigned long) address >> 16;
  *(short *) (function +36) = 0x614A;
  *(short *) (function +38) = (unsigned long) address & 0xffff;
  *(long *)  (function +40) = 0x816A0008;
  *(long *)  (function +44) = 0x804A0004;
  *(long *)  (function +48) = 0x800A0000;
  *(long *)  (function +52) = 0x7C0903A6;
  *(long *)  (function +56) = 0x4E800420;
#define is_tramp(function)  \
  *(unsigned long *)  (function + 0) == (long) (function + 12) && \
  *(unsigned long *)  (function + 4) == 0 && \
  *(unsigned long *)  (function + 8) == 0 && \
  *(unsigned short *) (function +12) == 0x3D60 && \
  *(unsigned short *) (function +16) == 0x616B && \
  *(unsigned short *) (function +20) == 0x3D40 && \
  *(unsigned short *) (function +24) == 0x614A && \
  *(unsigned long *)  (function +28) == 0x914B0000 && \
  *(unsigned short *) (function +32) == 0x3D40 && \
  *(unsigned short *) (function +36) == 0x614A && \
  *(unsigned long *)  (function +40) == 0x816A0008 && \
  *(unsigned long *)  (function +44) == 0x804A0004 && \
  *(unsigned long *)  (function +48) == 0x800A0000 && \
  *(unsigned long *)  (function +52) == 0x7C0903A6 && \
  *(unsigned long *)  (function +56) == 0x4E800420
#define hilo(hiword,loword)  \
  (((unsigned long) (hiword) << 16) | (unsigned long) (loword))
#define tramp_address(function)  \
  hilo(*(unsigned short *) (function +34), *(unsigned short *) (function +38))
#define tramp_variable(function)  \
  hilo(*(unsigned short *) (function +14), *(unsigned short *) (function +18))
#define tramp_data(function)  \
  hilo(*(unsigned short *) (function +22), *(unsigned short *) (function +26))
#endif
#endif
#ifdef __m88k__
  /* function:
   *    or.u    #r13,#r0,hi16(<variable>)	5D A0 hi16(<variable>)
   *    or      #r13,#r13,lo16(<variable>)	59 AD lo16(<variable>)
   *    or.u    #r10,#r0,hi16(<data>)		5D 40 hi16(<data>)
   *    or      #r10,#r10,lo16(<data>)		59 4A lo16(<data>)
   *    st      #r10,#r0,#r13			F5 40 24 0D
   *    or.u    #r13,#r0,hi16(<address>)	5D A0 hi16(<address>)
   *    or      #r13,#r13,lo16(<address>)	59 AD lo16(<address>)
   *    jmp     #r13				F4 00 C0 0D
   */
  *(short *) (function + 0) = 0x5DA0;
  *(short *) (function + 2) = (unsigned long) variable >> 16;
  *(short *) (function + 4) = 0x59AD;
  *(short *) (function + 6) = (unsigned long) variable & 0xffff;
  *(short *) (function + 8) = 0x5D40;
  *(short *) (function +10) = (unsigned long) data >> 16;
  *(short *) (function +12) = 0x594A;
  *(short *) (function +14) = (unsigned long) data & 0xffff;
  *(long *)  (function +16) = 0xF540240D;
  *(short *) (function +20) = 0x5DA0;
  *(short *) (function +22) = (unsigned long) address >> 16;
  *(short *) (function +24) = 0x59AD;
  *(short *) (function +26) = (unsigned long) address & 0xffff;
  *(long *)  (function +28) = 0xF400C00D;
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x5DA0 && \
  *(unsigned short *) (function + 4) == 0x59AD && \
  *(unsigned short *) (function + 8) == 0x5D40 && \
  *(unsigned short *) (function +12) == 0x594A && \
  *(unsigned long *)  (function +16) == 0xF540240D && \
  *(unsigned short *) (function +20) == 0x5DA0 && \
  *(unsigned short *) (function +24) == 0x59AD && \
  *(unsigned long *)  (function +28) == 0xF400C00D
#define hilo(hiword,loword)  \
  (((unsigned long) (hiword) << 16) | (unsigned long) (loword))
#define tramp_address(function)  \
  hilo(*(unsigned short *) (function +22), *(unsigned short *) (function +26))
#define tramp_variable(function)  \
  hilo(*(unsigned short *) (function + 2), *(unsigned short *) (function + 6))
#define tramp_data(function)  \
  hilo(*(unsigned short *) (function +10), *(unsigned short *) (function +14))
#endif
#ifdef __convex__
  /* function:
   *    ld.w #<data>,s1				11C9 <data>
   *    st.w s1,<variable>			3641 <variable>
   *    jmp <address>				0140 <address>
   *    ds.h 0					0000
   */
  *(short *) (function + 0) = 0x11C9;
  *(long *)  (function + 2) = (unsigned long) data;
  *(short *) (function + 6) = 0x3641;
  *(long *)  (function + 8) = (unsigned long) variable;
  *(short *) (function +12) = 0x0140;
  *(long *)  (function +14) = (unsigned long) address;
  *(short *) (function +18) = 0x0000;
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x11C9 && \
  *(unsigned short *) (function + 6) == 0x3641 && \
  *(unsigned short *) (function +12) == 0x0140 && \
  *(unsigned short *) (function +18) == 0x0000
#define tramp_address(function)  \
  *(long *)  (function +14)
#define tramp_variable(function)  \
  *(long *)  (function + 8)
#define tramp_data(function)  \
  *(long *)  (function + 2)
#endif

  /* 3. Set memory protection to "executable" */

#if !defined(CODE_EXECUTABLE) && defined(EXECUTABLE_VIA_MPROTECT)
  /* Call mprotect on the pages that contain the range. */
  { unsigned long start_addr = (unsigned long) function;
    unsigned long end_addr = (unsigned long) (function + TRAMP_LENGTH);
    start_addr = start_addr & -pagesize;
    end_addr = (end_addr + pagesize-1) & -pagesize;
   {unsigned long len = end_addr - start_addr;
#if defined(HAVE_MACH_VM)
    if (vm_protect(task_self(),start_addr,len,0,VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE) != KERN_SUCCESS)
#else
#if defined(__convex__)
    /* Convex OS calls it `mremap()'. */
    mremap(start_addr, &len, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE);
    if (0)
#else
#if defined(HAVE_SYS_M88KBCS_H)
    if (memctl(start_addr, len, MCT_TEXT) == -1)
#else
    if (mprotect((MMAP_ADDR_T)start_addr, len, PROT_READ|PROT_WRITE|PROT_EXEC) < 0)
#endif
#endif
#endif
      { fprintf(stderr,"trampoline: cannot make memory executable\n"); abort(); }
  }}
#endif

  /* 4. Flush instruction cache */
  /* We need this because some CPUs have separate data cache and instruction
   * cache. The freshly built trampoline is visible to the data cache, but not
   * maybe not to the instruction cache. This is hairy.
   */
#if defined(__i386__) || defined(__sparc__) || defined(__alpha__) || defined(__arm__) || defined(__rs6000__)
  /* These CPUs have no separate instruction cache. (I think.) */
#endif
#ifdef __m68k__
#ifdef NeXT
  asm volatile ("trap #2");
#else
#ifdef AMIGA
  CacheClearE(function,TRAMP_LENGTH,CACRF_ClearI|CACRF_ClearD);
#endif
#endif
#endif
#ifdef __mips__
  cacheflush(function,TRAMP_LENGTH,ICACHE);
#endif
#ifdef __hppa__
  /* This assumes that the trampoline fits in at most two cache lines. */
  __TR_clear_cache(function,function+TRAMP_LENGTH-1);
#endif
#ifdef __m88k__
  sysmot(S88CACHEFLUSHPAGE, (unsigned long)function & -pagesize);
#endif
#ifdef __convex__
  __TR_clear_cache();
#endif

  return (__TR_function)function;
}

void free_trampoline (function)
  __TR_function function;
{
#if !defined(CODE_EXECUTABLE) && !defined(EXECUTABLE_VIA_MPROTECT)
  *(char**)function = freelist; freelist = (char*)function;
  /* It is probably not worth calling munmap() for entirely freed pages. */
#else
  free(((char**)function)[-1]);
#endif
}

int is_trampoline (function)
  void* function;
{
#ifdef is_tramp
  return ((is_tramp(((char*)function))) ? 1 : 0);
#else
  abort();
#endif
}

__TR_function trampoline_address (function)
  void* function;
{
#ifdef tramp_address
  return (__TR_function)(tramp_address(((char*)function)));
#else
  abort();
#endif
}

void* trampoline_variable (function)
  void* function;
{
#ifdef tramp_variable
  return (void*)(tramp_variable(((char*)function)));
#else
  abort();
#endif
}

void* trampoline_data (function)
  void* function;
{
#ifdef tramp_data
  return (void*)(tramp_data(((char*)function)));
#else
  abort();
#endif
}
