/* Trampoline construction */

/*
 * Copyright 1995-1999, 2001-2006 Bruno Haible, <bruno@clisp.org>
 *
 * 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_r.h"

#if defined(__hppa__)
#if 0
#define __hppaold__  /* Old trampoline, real machine code. */
#else
#define __hppanew__  /* New trampoline, just a closure. */
#endif
#endif
#if defined(__powerpc__) && !defined(__powerpc64__)
#if !defined(_AIX)
#define __powerpcsysv4__  /* SysV.4 ABI, real machine code. */
#else
#define __powerpcaix__  /* AIX ABI, just a closure. */
#endif
#endif
#if defined(__powerpc64__)
/* The only ABI on powerpc64 known so far is the AIX ABI. */
#define __powerpc64aix__  /* AIX ABI, just a closure. */
#endif
#if defined(__hppanew__)
/*
 * A function pointer is a biased pointer to a data area whose first word
 * contains the actual address of the function.
 */
extern void tramp_r (); /* 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(__powerpcaix__) || defined(__powerpc64aix__) || defined(__ia64__)
/*
 * A function pointer is a pointer to a data area whose first word contains
 * the actual address of the function.
 */
extern void (*tramp_r) (); /* 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(__m68k__)
#if defined(AMIGA) /* Amiga running AmigaOS, not Linux */
#ifndef CODE_EXECUTABLE /* configure guesses wrong?? */
#define CODE_EXECUTABLE
#endif
#endif
#endif

/* As of Windows XP SP 2, malloc'd memory is not executable */
#ifdef _WIN32
#undef CODE_EXECUTABLE
#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
#ifdef _WIN32
/* Use VirtualAlloc */
#else
??
#endif
#endif
#endif
#endif
#endif

#include <stdio.h> /* declares fprintf() */

#include <sys/types.h>
#include <stdlib.h> /* declares abort(), malloc(), free() */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

/* Declare getpagesize(). */
#ifdef HAVE_GETPAGESIZE
#ifdef __cplusplus
extern "C" RETGETPAGESIZETYPE getpagesize (void);
#else
extern RETGETPAGESIZETYPE getpagesize (void);
#endif
#else
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#else
/* Not Unix, e.g. mingw32 */
#define PAGESIZE 4096
#endif
#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>
#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
#endif

/* Declare open(). */
#ifdef EXECUTABLE_VIA_MMAP_DEVZERO
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#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
#endif

/* Support for instruction cache flush. */
#ifdef __i386__
#if defined(_WIN32) /* WindowsNT or Windows95 */
#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRA_LEAN
#include <windows.h>
#endif
#endif
#ifdef __m68k__
#if defined(AMIGA) /* Amiga running AmigaOS, not Linux */
#include <exec/types.h>
#include <exec/execbase.h>
#include <proto/exec.h>
#endif
#ifdef hpux
#include <sys/cache.h>
#endif
#endif
#if defined(__mips__) || defined(__mipsn32__) || defined(__mips64__)
#ifdef ultrix
#include <mips/cachectl.h>
#else
#ifdef linux
#include <asm/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(__sparc__) || defined(__sparc64__) || defined(__alpha__) || defined(__hppaold__) || defined(__powerpcsysv4__) || defined(__convex__)
#ifdef __GNUC__
extern inline
#if defined(__sparc__) || defined(__sparc64__)
#include "cache-sparc.c"
#endif
#ifdef __alpha__
#include "cache-alpha.c"
#endif
#ifdef __hppa__
#include "cache-hppa.c"
#endif
#if defined(__powerpc__) && !defined(__powerpc64__)
#include "cache-powerpc.c"
#endif
#ifdef __convex__
#include "cache-convex.c"
#endif
#else
#if defined(__sparc__) || defined(__sparc64__)
extern void __TR_clear_cache_4();
#else
extern void __TR_clear_cache();
#endif
#endif
#endif

/* Length and alignment of trampoline */
#ifdef __i386__
#define TRAMP_LENGTH 16
#define TRAMP_ALIGN 16  /* 4 for a i386, 16 for a i486 */
#endif
#ifdef __m68k__
#define TRAMP_LENGTH 14
#define TRAMP_ALIGN 16
#endif
#if defined(__mips__) || defined(__mipsn32__) && !defined(__mips64__)
#define TRAMP_LENGTH 24
#define TRAMP_ALIGN 4
#endif
#ifdef __mips64old__
#define TRAMP_LENGTH 56
#define TRAMP_ALIGN 4
#endif
#ifdef __mips64__
#define TRAMP_LENGTH 32
#define TRAMP_ALIGN 8
#endif
#if defined(__sparc__) && !defined(__sparc64__)
#define TRAMP_LENGTH 16
#define TRAMP_ALIGN 16
#endif
#ifdef __sparc64__
#define TRAMP_LENGTH 32
#define TRAMP_ALIGN 16
#endif
#ifdef __alpha__
#define TRAMP_LENGTH 32
#define TRAMP_ALIGN 8
#endif
#ifdef __hppaold__
#define TRAMP_LENGTH 48
#define TRAMP_ALIGN 16
#endif
#ifdef __hppanew__
#define TRAMP_LENGTH 16
#define TRAMP_ALIGN 16
#define TRAMP_BIAS 2
#endif
#ifdef __arm__
#define TRAMP_LENGTH 16
#define TRAMP_ALIGN 4
#endif
#ifdef __powerpcsysv4__
#define TRAMP_LENGTH 24
#define TRAMP_ALIGN 4
#endif
#ifdef __powerpcaix__
#define TRAMP_LENGTH 20
#define TRAMP_ALIGN 4
#endif
#ifdef __powerpc64aix__
#define TRAMP_LENGTH 40
#define TRAMP_ALIGN 8
#endif
#ifdef __m88k__
#define TRAMP_LENGTH 20
#define TRAMP_ALIGN 8
#endif
#ifdef __convex__
#define TRAMP_LENGTH 14
#define TRAMP_ALIGN 4
#endif
#ifdef __ia64__
#define TRAMP_LENGTH 32
#define TRAMP_ALIGN 16
#endif
#ifdef __x86_64__
#define TRAMP_LENGTH 22
#define TRAMP_ALIGN 16
#endif
#ifdef __s390__
#define TRAMP_LENGTH 22
#define TRAMP_ALIGN 2
#endif

#ifndef TRAMP_BIAS
#define TRAMP_BIAS 0
#endif

#define TRAMP_TOTAL_LENGTH (TRAMP_LENGTH + 2*sizeof(void*))

#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_r (__TR_function address, void* data0, void* data1)
{
  char* function;
  char* data;

#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)
  /* Note: This memory allocation is not multithread-safe. We might need
   * to add special (platform dependent) code for locking.
   * Fortunately, most modern systems where multithread-safety matters
   * have EXECUTABLE_VIA_MPROTECT, and those which don't (AIX on powerpc and
   * HP-UX on hppa) have CODE_EXECUTABLE. Thus no locking code is needed
   * for the moment.
   */
  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
#ifdef _WIN32
      page = VirtualAlloc(NULL, pagesize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
      if (page == 0)
      	{ page = (char*)(-1); }
#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_TOTAL_LENGTH <= page_end)
          { *last = page; last = (char**)page; page += TRAMP_TOTAL_LENGTH; }
        *last = NULL;
    } }
  function = freelist; freelist = *(char**)freelist;
#else
  { char* room = (char*) malloc(sizeof(void*) + TRAMP_TOTAL_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 */
  data = function + TRAMP_LENGTH;
  /* Knowing that data = function + TRAMP_LENGTH, we could certainly optimize
   * the trampolines a little bit more, using PC relative addressing modes.
   * But I doubt it's really worth it.
   */
#ifdef __i386__
  /* function:
   *    popl %ecx			59
   *    pushl $<data>			68 <data>
   *    pushl %ecx			51
   *    jmp <address>			E9 <address>-<here>
   * here:
   *    nop				90
   *    nop				90
   *    nop				90
   *    nop				90
   */
  *(short *) (function + 0) = 0x6859;
  *(long *)  (function + 2) = (long) data;
  *(short *) (function + 6) = 0xE951;
  *(long *)  (function + 8) = (long) address - (long) (function + 12);
  *(long *)  (function +12) = 0x90909090;   /* nop nop nop nop, for alignment */
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x6859 && \
  *(unsigned short *) (function + 6) == 0xE951
#define tramp_address(function)  \
  *(long *)  (function + 8) + (long) (function + 12)
#define tramp_data(function)  \
  *(long *)  (function + 2)
#endif
#ifdef __m68k__
#ifdef __NetBSD__
  /* function:
   *    movel #<data>,a1		22 7C <data>
   *    jmp <address>			4E F9 <address>
   *    nop				4E 71
   */
  *(short *) (function + 0) = 0x227C;
  *(long *)  (function + 2) = (long) data;
  *(short *) (function + 6) = 0x4EF9;
  *(long *)  (function + 8) = (long) address;
  *(short *) (function +12) = 0x4E71;
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x227C && \
  *(unsigned short *) (function + 6) == 0x4EF9 && \
  *(unsigned short *) (function +12) == 0x4E71
#else
  /* function:
   *    movel #<data>,a0		20 7C <data>
   *    jmp <address>			4E F9 <address>
   *    nop				4E 71
   */
  *(short *) (function + 0) = 0x207C;
  *(long *)  (function + 2) = (long) data;
  *(short *) (function + 6) = 0x4EF9;
  *(long *)  (function + 8) = (long) address;
  *(short *) (function +12) = 0x4E71;
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x207C && \
  *(unsigned short *) (function + 6) == 0x4EF9 && \
  *(unsigned short *) (function +12) == 0x4E71
#endif
#define tramp_address(function)  \
  *(long *)  (function + 8)
#define tramp_data(function)  \
  *(long *)  (function + 2)
#endif
#if defined(__mips__) || defined(__mipsn32__) && !defined(__mips64__)
  /* function:
   *    lw $2,16($25)			8F 22 00 10
   *    lw $25,20($25)			8F 39 00 14
   *    j $25				03 20 00 08
   *    nop				00 00 00 00
   *    .word <data>			<data>
   *    .word <address>			<address>
   */
  *(unsigned int *) (function + 0) = 0x8F220010;
  *(unsigned int *) (function + 4) = 0x8F390014;
  *(unsigned int *) (function + 8) = 0x03200008;
  *(unsigned int *) (function +12) = 0x00000000;
  *(unsigned int *) (function +16) = (unsigned int) data;
  *(unsigned int *) (function +20) = (unsigned int) address;
#define is_tramp(function)  \
  *(unsigned int *) (function + 0) == 0x8F220010 && \
  *(unsigned int *) (function + 4) == 0x8F390014 && \
  *(unsigned int *) (function + 8) == 0x03200008 && \
  *(unsigned int *) (function +12) == 0x00000000
#define tramp_address(function)  \
  *(unsigned int *) (function +20)
#define tramp_data(function)  \
  *(unsigned int *) (function +16)
#endif
#ifdef __mips64old__
  /* function:
   *    dli $2,<data>			3C 02 hi16(hi32(<data>))
   *					34 42 lo16(hi32(<data>))
   *					00 02 14 38
   *					34 42 hi16(lo32(<data>))
   *					00 02 14 38
   *					34 42 lo16(lo32(<data>))
   *    dli $25,<address>		3C 19 hi16(hi32(<address>))
   *					37 39 lo16(hi32(<address>))
   *					00 19 CC 38
   *					37 39 hi16(lo32(<address>))
   *					00 19 CC 38
   *					37 39 lo16(lo32(<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 >> 48;
  *(short *) (function + 4) = 0x3442;
  *(short *) (function + 6) = ((unsigned long) data >> 32) & 0xffff;
  *(int *)   (function + 8) = 0x00021438;
  *(short *) (function +12) = 0x3442;
  *(short *) (function +14) = ((unsigned long) data >> 16) & 0xffff;
  *(int *)   (function +16) = 0x00021438;
  *(short *) (function +20) = 0x3442;
  *(short *) (function +22) = (unsigned long) data & 0xffff;
  *(short *) (function +24) = 0x3C19;
  *(short *) (function +26) = (unsigned long) address >> 48;
  *(short *) (function +28) = 0x3739;
  *(short *) (function +30) = ((unsigned long) address >> 32) & 0xffff;
  *(int *)   (function +32) = 0x0019CC38;
  *(short *) (function +36) = 0x3739;
  *(short *) (function +38) = ((unsigned long) address >> 16) & 0xffff;
  *(int *)   (function +40) = 0x0019CC38;
  *(short *) (function +44) = 0x3739;
  *(short *) (function +46) = (unsigned long) address & 0xffff;
  *(int *)   (function +48) = 0x03200008;
  *(int *)   (function +52) = 0x00000000;
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x3C02 && \
  *(unsigned short *) (function + 4) == 0x3442 && \
  *(unsigned int *)   (function + 8) == 0x00021438 && \
  *(unsigned short *) (function +12) == 0x3442 && \
  *(unsigned int *)   (function +16) == 0x00021438 && \
  *(unsigned short *) (function +20) == 0x3442 && \
  *(unsigned short *) (function +24) == 0x3C19 && \
  *(unsigned short *) (function +28) == 0x3739 && \
  *(unsigned int *)   (function +32) == 0x0019CC38 && \
  *(unsigned short *) (function +36) == 0x3739 && \
  *(unsigned int *)   (function +40) == 0x0019CC38 && \
  *(unsigned short *) (function +44) == 0x3739 && \
  *(unsigned int *)   (function +48) == 0x03200008 && \
  *(unsigned int *)   (function +52) == 0x00000000
#define hilo(word3,word2,word1,word0)  \
  (((unsigned long) (word3) << 48) | ((unsigned long) (word2) << 32) | \
   ((unsigned long) (word1) << 16) | (unsigned long) (word0))
#define tramp_address(function)  \
  hilo(*(unsigned short *) (function +26), \
       *(unsigned short *) (function +30), \
       *(unsigned short *) (function +38), \
       *(unsigned short *) (function +46))
#define tramp_data(function)  \
  hilo(*(unsigned short *) (function + 2), \
       *(unsigned short *) (function + 6), \
       *(unsigned short *) (function +14), \
       *(unsigned short *) (function +22))
#endif
#ifdef __mips64__
  /* function:
   *    ld $2,16($25)			DF 22 00 10
   *    ld $25,24($25)			DF 39 00 18
   *    j $25				03 20 00 08
   *    nop				00 00 00 00
   *    .dword <data>			<data>
   *    .dword <address>		<address>
   */
  *(unsigned int *)  (function + 0) = 0xDF220010;
  *(unsigned int *)  (function + 4) = 0xDF390018;
  *(unsigned int *)  (function + 8) = 0x03200008;
  *(unsigned int *)  (function +12) = 0x00000000;
  *(unsigned long *) (function +16) = (unsigned long) data;
  *(unsigned long *) (function +24) = (unsigned long) address;
#define is_tramp(function)  \
  *(unsigned int *)  (function + 0) == 0xDF220010 && \
  *(unsigned int *)  (function + 4) == 0xDF390018 && \
  *(unsigned int *)  (function + 8) == 0x03200008 && \
  *(unsigned int *)  (function +12) == 0x00000000
#define tramp_address(function)  \
  *(unsigned long *) (function +24)
#define tramp_data(function)  \
  *(unsigned long *) (function +16)
#endif
#if defined(__sparc__) && !defined(__sparc64__)
  /* function:
   *    sethi %hi(<data>),%g2		05000000 | (<data> >> 10)
   *    sethi %hi(<address>),%g1	03000000 | (<address> >> 10)
   *    jmp %g1+%lo(<address>)		81C06000 | (<address> & 0x3ff)
   *    or %g2,%lo(<data>),%g2		8410A000 | (<data> & 0x3ff)
   */
#define hi(word)  ((unsigned long) (word) >> 10)
#define lo(word)  ((unsigned long) (word) & 0x3ff)
  *(long *) (function + 0) = 0x05000000 | hi(data);
  *(long *) (function + 4) = 0x03000000 | hi(address);
  *(long *) (function + 8) = 0x81C06000 | lo(address);
  *(long *) (function +12) = 0x8410A000 | lo(data);
#define is_tramp(function)  \
  (*(long *) (function + 0) & 0xffc00000) == 0x05000000 && \
  (*(long *) (function + 4) & 0xffc00000) == 0x03000000 && \
  (*(long *) (function + 8) & 0xfffffc00) == 0x81C06000 && \
  (*(long *) (function +12) & 0xfffffc00) == 0x8410A000
#define hilo(hiword,loword)  (((hiword) << 10) | ((loword) & 0x3ff))
#define tramp_address(function)  \
  hilo(*(long *) (function + 4), *(long *) (function + 8))
#define tramp_data(function)  \
  hilo(*(long *) (function + 0), *(long *) (function +12))
#endif
#ifdef __sparc64__
  /* function:
   *    rd %pc,%g1			83414000
   *    ldx [%g1+24],%g2		C4586018
   *    jmp %g2				81C08000
   *    ldx [%g1+16],%g5		CA586010
   *    .long high32(<data>)		<data> >> 32
   *    .long low32(<data>)		<data> & 0xffffffff
   *    .long high32(<address>)		<address> >> 32
   *    .long low32(<address>)		<address> & 0xffffffff
   */
  *(int *)  (function + 0) = 0x83414000;
  *(int *)  (function + 4) = 0xC4586018;
  *(int *)  (function + 8) = 0x81C08000;
  *(int *)  (function +12) = 0xCA586010;
  *(long *) (function +16) = (long) data;
  *(long *) (function +24) = (long) address;
#define is_tramp(function)  \
  *(int *)  (function + 0) == 0x83414000 && \
  *(int *)  (function + 4) == 0xC4586018 && \
  *(int *)  (function + 8) == 0x81C08000 && \
  *(int *)  (function +12) == 0xCA586010
#define tramp_address(function)  \
  *(long *) (function +24)
#define tramp_data(function)  \
  *(long *) (function +16)
#endif
#ifdef __alpha__
  /* function:
   *    br $1,function..ng	00 00 20 C0
   * function..ng:
   *    ldq $27,20($1)		14 00 61 A7
   *    ldq $1,12($1)		0C 00 21 A4
   *    jmp $31,($27),0		00 00 FB 6B
   *    .quad <data>		<data>
   *    .quad <address>		<address>
   */
  { static int code [4] =
      { 0xC0200000, 0xA7610014, 0xA421000C, 0x6BFB0000 };
    int i;
    for (i=0; i<4; i++) { ((int *) function)[i] = code[i]; }
    ((long *) function)[2] = (long) data;
    ((long *) function)[3] = (long) address;
  }
#define is_tramp(function)  \
  ((int *) function)[0] == 0xC0200000 && \
  ((int *) function)[1] == 0xA7610014 && \
  ((int *) function)[2] == 0xA421000C && \
  ((int *) function)[3] == 0x6BFB0000
#define tramp_address(function)  \
  ((long *) function)[3]
#define tramp_data(function)  \
  ((long *) function)[2]
#endif
#ifdef __hppaold__
  /* function:
   *    ldil L'<data>,%r29		23A00000 | hi(<data>)
   *    ldil L'<address>,%r21		22A00000 | hi(<address>)
   *    ldo R'<data>(%r29),%r29		37BD0000 | lo(<data>)
   *    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) = 0x23A00000 | hi(data);
  *(long *) (function + 4) = 0x22A00000 | hi(address);
  *(long *) (function + 8) = 0x37BD0000 | lo(data);
  *(long *) (function +12) = 0x36B50000 | lo(address);
  *(long *) (function +16) = 0xC7D5C012;
  *(long *) (function +20) = 0xD6A01C1E;
  *(long *) (function +24) = 0x4AB30008;
  *(long *) (function +28) = 0x4AB50000;
  *(long *) (function +32) = 0x02A010A1;
  *(long *) (function +36) = 0x00011820;
  *(long *) (function +40) = 0xE2A00002;
  *(long *) (function +44) = 0x08000240;
#define is_tramp(function)  \
  ((long) function & 3) == 0 && \
  (*(long *) (function + 0) & 0xffe00000) == 0x23A00000 && \
  (*(long *) (function + 4) & 0xffe00000) == 0x22A00000 && \
  (*(long *) (function + 8) & 0xfffff000) == 0x37BD0000 && \
  (*(long *) (function +12) & 0xfffff000) == 0x36B50000 && \
  *(long *) (function +16) == 0xC7D5C012 && \
  *(long *) (function +20) == 0xD6A01C1E && \
  *(long *) (function +24) == 0x4AB30008 && \
  *(long *) (function +28) == 0x4AB50000 && \
  *(long *) (function +32) == 0x02A010A1 && \
  *(long *) (function +36) == 0x00011820 && \
  *(long *) (function +40) == 0xE2A00002 && \
  *(long *) (function +44) == 0x08000240
#define hilo(hiword,loword)  \
  ((assemble21((unsigned long) (hiword)) << 11) | \
   (((unsigned long) (loword) & 0xFFE) >> 1) \
  )
#define tramp_address(function)  \
  hilo(*(long *) (function + 4), *(long *) (function +12))
#define tramp_data(function)  \
  hilo(*(long *) (function + 0), *(long *) (function + 8))
#endif
#ifdef __hppanew__
  /* function:
   *    .long   tramp_r
   *    .long   closure
   * closure:
   *    .long   <data>
   *    .long   <address>
   */
  { /* work around a bug in gcc 3.* */
    void* tramp_r_address = &tramp_r;
    *(long *) (function + 0) = ((long *) ((char*)tramp_r_address-2))[0];
    *(long *) (function + 4) = (long) (function + 8);
    *(long *) (function + 8) = (long) data;
    *(long *) (function +12) = (long) address;
  }
#define is_tramp(function)  \
  ((long *) function)[0] == ((long *) ((char*)tramp_r_address-2))[0]
#define tramp_address(function)  \
  ((long *) function)[3]
#define tramp_data(function)  \
  ((long *) function)[2]
#endif
#ifdef __arm__
  /* function:
   *	add	r12,pc,#8			E28FC008
   *	ldr	pc,[pc]				E59FF000
   * _data:
   *	.word	<data>
   * _function:
   *	.word	<address>
   */
  {
    ((long *) function)[0] = 0xE28FC008;
    ((long *) function)[1] = 0xE59FF000;
    ((long *) function)[2] = (long) data;
    ((long *) function)[3] = (long) address;
  }
#define is_tramp(function)  \
  ((long *) function)[0] == 0xE28FC008 && \
  ((long *) function)[1] == 0xE59FF000
#define tramp_address(function)  \
  ((long *) function)[3]
#define tramp_data(function)  \
  ((long *) function)[2]
#endif
#ifdef __powerpcsysv4__
#ifdef __NetBSD__
  /* function:
   *    {liu|lis} 13,hi16(<data>)		3D A0 hi16(<data>)
   *    {oril|ori} 13,13,lo16(<data>)		61 AD lo16(<data>)
   *    {liu|lis} 0,hi16(<address>)		3C 00 hi16(<address>)
   *    {oril|ori} 0,0,lo16(<address>)		60 00 lo16(<address>)
   *    mtctr 0					7C 09 03 A6
   *    bctr					4E 80 04 20
   */
  *(short *) (function + 0) = 0x3DA0;
  *(short *) (function + 2) = (unsigned long) data >> 16;
  *(short *) (function + 4) = 0x61AD;
  *(short *) (function + 6) = (unsigned long) data & 0xffff;
  *(short *) (function + 8) = 0x3C00;
  *(short *) (function +10) = (unsigned long) address >> 16;
  *(short *) (function +12) = 0x6000;
  *(short *) (function +14) = (unsigned long) address & 0xffff;
  *(long *)  (function +16) = 0x7C0903A6;
  *(long *)  (function +20) = 0x4E800420;
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x3DA0 && \
  *(unsigned short *) (function + 4) == 0x61AD && \
  *(unsigned short *) (function + 8) == 0x3C00 && \
  *(unsigned short *) (function +12) == 0x6000 && \
  *(unsigned long *)  (function +16) == 0x7C0903A6 && \
  *(unsigned long *)  (function +20) == 0x4E800420
#else
  /* function:
   *    {liu|lis} 11,hi16(<data>)		3D 60 hi16(<data>)
   *    {oril|ori} 11,11,lo16(<data>)		61 6B lo16(<data>)
   *    {liu|lis} 0,hi16(<address>)		3C 00 hi16(<address>)
   *    {oril|ori} 0,0,lo16(<address>)		60 00 lo16(<address>)
   *    mtctr 0					7C 09 03 A6
   *    bctr					4E 80 04 20
   */
  *(short *) (function + 0) = 0x3D60;
  *(short *) (function + 2) = (unsigned long) data >> 16;
  *(short *) (function + 4) = 0x616B;
  *(short *) (function + 6) = (unsigned long) data & 0xffff;
  *(short *) (function + 8) = 0x3C00;
  *(short *) (function +10) = (unsigned long) address >> 16;
  *(short *) (function +12) = 0x6000;
  *(short *) (function +14) = (unsigned long) address & 0xffff;
  *(long *)  (function +16) = 0x7C0903A6;
  *(long *)  (function +20) = 0x4E800420;
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x3D60 && \
  *(unsigned short *) (function + 4) == 0x616B && \
  *(unsigned short *) (function + 8) == 0x3C00 && \
  *(unsigned short *) (function +12) == 0x6000 && \
  *(unsigned long *)  (function +16) == 0x7C0903A6 && \
  *(unsigned long *)  (function +20) == 0x4E800420
#endif
#define hilo(hiword,loword)  \
  (((unsigned long) (hiword) << 16) | (unsigned long) (loword))
#define tramp_address(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 __powerpcaix__
  /* function:
   *    .long .tramp_r
   *    .long .mytoc
   *    .long 0
   * .mytoc:
   *    .long <data>
   *    .long <address>
   */
  *(long *)  (function + 0) = ((long *) &tramp_r)[0];
  *(long *)  (function + 4) = (long) (function + 12);
  *(long *)  (function + 8) = 0;
  *(long *)  (function +12) = (long) data;
  *(long *)  (function +16) = (long) address;
#define is_tramp(function)  \
  ((long *) function)[0] == ((long *) &tramp_r)[0]
#define tramp_address(function)  \
  ((long *) function)[4]
#define tramp_data(function)  \
  ((long *) function)[3]
#endif
#ifdef __powerpc64aix__
  /* function:
   *    .quad .tramp_r
   *    .quad .mytoc
   *    .quad 0
   * .mytoc:
   *    .quad <data>
   *    .quad <address>
   */
  *(long *)  (function + 0) = ((long *) &tramp_r)[0];
  *(long *)  (function + 8) = (long) (function + 24);
  *(long *)  (function +16) = 0;
  *(long *)  (function +24) = (long) data;
  *(long *)  (function +32) = (long) address;
#define is_tramp(function)  \
  ((long *) function)[0] == ((long *) &tramp_r)[0]
#define tramp_address(function)  \
  ((long *) function)[4]
#define tramp_data(function)  \
  ((long *) function)[3]
#endif
#ifdef __m88k__
  /* function:
   *    or.u    #r11,#r0,hi16(<data>)		5D 60 hi16(<data>)
   *    or      #r11,#r11,lo16(<data>)		59 6B lo16(<data>)
   *    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) = 0x5D60;
  *(short *) (function + 2) = (unsigned long) data >> 16;
  *(short *) (function + 4) = 0x596B;
  *(short *) (function + 6) = (unsigned long) data & 0xffff;
  *(short *) (function + 8) = 0x5DA0;
  *(short *) (function +10) = (unsigned long) address >> 16;
  *(short *) (function +12) = 0x59AD;
  *(short *) (function +14) = (unsigned long) address & 0xffff;
  *(long *)  (function +16) = 0xF400C00D;
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x5D60 && \
  *(unsigned short *) (function + 4) == 0x596B && \
  *(unsigned short *) (function + 8) == 0x5DA0 && \
  *(unsigned short *) (function +12) == 0x59AD && \
  *(unsigned long *)  (function +16) == 0xF400C00D
#define hilo(hiword,loword)  \
  (((unsigned long) (hiword) << 16) | (unsigned long) (loword))
#define tramp_address(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 __convex__
  /* function:
   *    ld.w #<data>,s0				???? <data>
   *    jmp <address>				0140 <address>
   *    ds.h 0					0000
   */
  *(short *) (function + 0) = 0x????;
  *(long *)  (function + 2) = (unsigned long) data;
  *(short *) (function + 6) = 0x0140;
  *(long *)  (function + 8) = (unsigned long) address;
  *(short *) (function +12) = 0x0000;
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0x???? && \
  *(unsigned short *) (function + 6) == 0x0140 && \
  *(unsigned short *) (function +12) == 0x0000
#define tramp_address(function)  \
  *(long *)  (function + 8)
#define tramp_data(function)  \
  *(long *)  (function + 2)
#endif
#ifdef __ia64__
  /* function:
   *    data8   tramp_r
   *    data8   closure
   * closure:
   *    data8   <address>
   *    data8   <data>
   */
  *(long *) (function + 0) = (long) &tramp_r;
  *(long *) (function + 8) = (long) (function + 16);
  *(long *) (function +16) = (long) address;
  *(long *) (function +24) = (long) data;
#define is_tramp(function)  \
  ((long *) function)[0] == (long) &tramp_r
#define tramp_address(function)  \
  ((long *) function)[2]
#define tramp_data(function)  \
  ((long *) function)[3]
#endif
#ifdef __x86_64__
  /* function:
   *    movabsq $<data>,%r10		49 BA <data>
   *    movabsq $<address>,%rax		48 B8 <address>
   *    jmp *%rax			FF E0
   */
  *(short *) (function + 0) = 0xBA49;
  *(short *) (function + 2) = (unsigned long) data & 0xffff;
  *(int *)   (function + 4) = ((unsigned long) data >> 16) & 0xffffffff;
  *(short *) (function + 8) = ((unsigned long) data >> 48) & 0xffff;
  *(short *) (function +10) = 0xB848;
  *(int *)   (function +12) = (unsigned long) address & 0xffffffff;
  *(int *)   (function +16) = ((unsigned long) address >> 32) & 0xffffffff;
  *(short *) (function +20) = 0xE0FF;
#define is_tramp(function)  \
  *(unsigned short *) (function + 0) == 0xBA49 && \
  *(unsigned short *) (function +10) == 0xB848 && \
  *(unsigned short *) (function +20) == 0xE0FF
#define hilo(hiword,loword)  \
  (((unsigned long) (hiword) << 32) | (unsigned long) (loword))
#define himidlo(hishort,midword,loshort)  \
  (((unsigned long) (hishort) << 48) | (unsigned long) (midword) << 16 \
   | (unsigned long) (loshort))
#define tramp_address(function)  \
  hilo(*(unsigned int *) (function +16), *(unsigned int *) (function +12))
#define tramp_data(function)  \
  himidlo(*(unsigned short *) (function + 8), \
          *(unsigned int *)   (function + 4), \
          *(unsigned short *) (function + 2))
#endif
#ifdef __s390__
  /* function:

        bras    %r1,.LTN0_0
.LT0_0:
.LC0:
        .long   0x73554711
.LC1:
        .long   0xbabebec0
.LTN0_0:
        l       %r0,.LC0-.LT0_0(%r1)
        l       %r1,.LC1-.LT1_0(%r1)
        br      %r1
  */
  /* What about big endian / little endian ?? */
  *(unsigned int *)   (function + 0) = 0xA7150006;
  *(unsigned int *)   (function + 4) = (unsigned int) data;
  *(unsigned int *)   (function + 8) = (unsigned int) address;
  *(unsigned int *)   (function +12) = 0x58001000;
  *(unsigned int *)   (function +16) = 0x58101004;
  *(unsigned short *) (function +20) = 0x07f1;
#define is_tramp(function)  \
  *(int *)            (function + 0) == 0xA7150006 && \
  *(int *)            (function +12) == 0x58001000 && \
  *(int *)            (function +16) == 0x58101004 && \
  *(short *)          (function +20) == 0x07f1
#define tramp_address(function)  \
  *(unsigned int *) (function +8)
#define tramp_data(function)  \
  *(unsigned int *) (function +4)
#endif
  /*
   * data:
   *    <data0>				<data0>
   *    <data1>				<data1>
   */
  *(long *)  (data + 0*sizeof(void*)) = (long) data0;
  *(long *)  (data + 1*sizeof(void*)) = (long) data1;

  /* 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((void*)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(__hppanew__) || defined(__powerpcaix__) || defined(__powerpc64aix__) || defined(__ia64__))
  /* Only needed if we really set up machine instructions. */
#ifdef __i386__
#if defined(_WIN32)
  while (!FlushInstructionCache(GetCurrentProcess(),function,TRAMP_LENGTH))
    continue;
#endif
#endif
#ifdef __m68k__
#if defined(NeXT) && defined(__GNUC__)
  __asm__ __volatile__ ("trap #2");
#endif
#if defined(AMIGA)
  CacheClearE(function,TRAMP_LENGTH,CACRF_ClearI|CACRF_ClearD);
#endif
#if defined(apollo)
  cache_$clear();
#endif
#if defined(hpux)
  cachectl(CC_IPURGE,function,TRAMP_LENGTH);
#endif
#if defined(__NetBSD__) && defined(__GNUC__)
  { register unsigned long _beg __asm__ ("%a1") = (unsigned long) function;
    register unsigned long _len __asm__ ("%d1") = TRAMP_LENGTH;
    __asm__ __volatile__ (
      "move%.l %#0x80000004,%/d0\n\t" /* CC_EXTPURGE | C_IPURGE */
      "trap #12"                      /* kernel call `cachectl' */
      :
      : "a" (_beg), "d" (_len)
      : "%a0", "%a1", "%d0", "%d1"    /* call-used registers */
      );
  }
#endif
#if defined(__linux__) && defined(__GNUC__)
  { register unsigned long _beg __asm__ ("%d1") = (unsigned long) function;
    register unsigned long _len __asm__ ("%d4") = TRAMP_LENGTH + 32;
    __asm__ __volatile__ (
      "move%.l %#123,%/d0\n\t"
      "move%.l %#1,%/d2\n\t"
      "move%.l %#3,%/d3\n\t"
      "trap %#0"
      :
      : "d" (_beg), "d" (_len)
      : "%d0", "%d2", "%d3"
      );
  }
#endif
#if defined(AUX) && defined(__GNUC__)
  /* sysm68k(105, addr, scope, cache, len) */
  __asm__ __volatile__ (
    "move%.l %1,%/sp@-\n\t"
    "move%.l %#3,%/sp@-\n\t"
    "move%.l %#1,%/sp@-\n\t"
    "move%.l %0,%/sp@-\n\t"
    "move%.l %#105,%/sp@-\n\t"
    "move%.l %#0,%/sp@-\n\t"
    "move%.l %#38,%/sp@-\n\t"
    "trap %#0\n\t"
    "add%.l %#24,%/sp"
    :
    : "r" (function), "g" ((int)TRAMP_LENGTH)
    : "%d0"
    );
#endif
#endif
#if defined(__mips__) || defined(__mipsn32__) || defined(__mips64__)
  cacheflush(function,TRAMP_LENGTH,ICACHE);
  /* gforth-0.3.0 uses BCACHE instead of ICACHE. Why?? */
#endif
#if defined(__sparc__) || defined(__sparc64__)
  /* This assumes that the trampoline fits in at most two cache lines. */
  __TR_clear_cache_2(function,function+TRAMP_LENGTH-1);
#endif
#ifdef __alpha__
  __TR_clear_cache();
#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 __arm__
  __TR_clear_cache(function,function+TRAMP_LENGTH);
#endif
#if defined(__powerpc__) && !defined(__powerpc64__)
  __TR_clear_cache(function);
#endif
#ifdef __m88k__
  sysmot(S88CACHEFLUSHPAGE, (unsigned long)function & -pagesize);
#endif
#ifdef __convex__
  __TR_clear_cache();
#endif
#endif

  /* 5. Return. */
  return (__TR_function) (function + TRAMP_BIAS);
}

void free_trampoline_r (__TR_function function)
{
#if TRAMP_BIAS
  function = (__TR_function)((char*)function - TRAMP_BIAS);
#endif
#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_r (void* function)
{
#if defined(is_tramp) && defined(tramp_data)
#ifdef __hppanew__
  void* tramp_r_address = &tramp_r;
  if (!(((long)function & 3) == (TRAMP_BIAS & 3))) return 0;
#endif
  return
   ((is_tramp(((char*)function - TRAMP_BIAS)))
    && ((tramp_data(((char*)function - TRAMP_BIAS))) == (long)((char*)function - TRAMP_BIAS + TRAMP_LENGTH))
    ? 1 : 0
   );
#else
  abort();
#endif
}

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

void* trampoline_r_data0 (void* function)
{
#ifdef tramp_data
  return ((void**)((char*)function-TRAMP_BIAS+TRAMP_LENGTH))[0];
#else
  abort();
#endif
}

void* trampoline_r_data1 (void* function)
{
#ifdef tramp_data
  return ((void**)((char*)function-TRAMP_BIAS+TRAMP_LENGTH))[1];
#else
  abort();
#endif
}
