From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by sourceware.org (Postfix) with ESMTP id 1FBD6385043C for ; Tue, 24 Nov 2020 20:02:44 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 1FBD6385043C Received: from [192.168.254.32] (unknown [47.187.219.45]) by linux.microsoft.com (Postfix) with ESMTPSA id 60CD320B717A; Tue, 24 Nov 2020 12:02:42 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 60CD320B717A Subject: Re: [RFC PATCH v1 1/4] Static Trampolines To: Anthony Green Cc: libffi-discuss , fw@deneb.enyo.de References: <20201124193206.10289-1-madvenka@linux.microsoft.com> <20201124193206.10289-2-madvenka@linux.microsoft.com> From: "Madhavan T. Venkataraman" Message-ID: <0cc74b04-f839-6007-dec3-1b7048d12081@linux.microsoft.com> Date: Tue, 24 Nov 2020 14:02:41 -0600 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.10.0 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-26.2 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, ENV_AND_HDR_SPF_MATCH, GIT_PATCH_0, KAM_ASCII_DIVIDERS, NICE_REPLY_A, SPF_HELO_PASS, SPF_PASS, TXREP, USER_IN_DEF_DKIM_WL, USER_IN_DEF_SPF_WL autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libffi-discuss@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libffi-discuss mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 24 Nov 2020 20:02:49 -0000 Good idea! I will take care of this. Madhavan On 11/24/20 1:49 PM, Anthony Green wrote: > Thank you for your submission, Madhavan.  This will be interesting reading! > > In the meantime, it would be helpful if you submitted this as a PR on GitHub.  This will trigger CI testing for over 20 different systems so we can have an initial look at any build/test problems. > > AG > > On Tue, Nov 24, 2020 at 2:32 PM madvenka--- via Libffi-discuss > wrote: > > From: "Madhavan T. Venkataraman" > > > Closure Trampoline Security Issue > ================================= > > Currently, the trampoline code used in libffi is not statically defined in > a source file (except for MACH). The trampoline is either pre-defined > machine code in a data buffer. Or, it is generated at runtime. In order to > execute a trampoline, it needs to be placed in a page with executable > permissions. > > Executable data pages are attack surfaces for attackers who may try to > inject their own code into the page and contrive to have it executed. The > security settings in a system may prevent various tricks used in user land > to write code into a page and to have it executed somehow. On such systems, > libffi trampolines would not be able to run. > > Static Trampoline > ================= > > To solve this problem, the trampoline code needs to be defined statically > in a source file, compiled and placed in the text segment so it can be > mapped and executed naturally without any tricks. However, the trampoline > needs to be able to access the closure pointer at runtime. > > PC-relative data referencing > ============================ > > The solution implemented in this patch set uses PC-relative data references. > The trampoline is mapped in a code page. Adjacent to the code page, a data > page is mapped that contains the parameters of the trampoline: > >         - the closure pointer >         - pointer to the ABI handler to jump to > > The trampoline code uses an offset relative to its current PC to access its > data. > > Some architectures support PC-relative data references in the ISA itself. > E.g., X64 supports RIP-relative references. For others, the PC has to > somehow be loaded into a general purpose register to do PC-relative data > referencing. To do this, we need to define a get_pc() kind of function and > call it to load the PC in a desired register. > > There are two cases: > > 1. The call instruction pushes the return address on the stack. > >    In this case, get_pc() will extract the return address from the stack >    and load it in the desired register and return. > > 2. The call instruction stores the return address in a designated register. > >    In this case, get_pc() will copy the return address to the desired >    register and return. > > Either way, the PC next to the call instruction is obtained. > > Scratch register > ================ > > In order to do its job, the trampoline code would be required to use a > scratch register. Depending on the ABI, there may not be a register > available for scratch. This problem needs to be solved so that all ABIs > will work. > > The trampoline will save two values on the stack: > >         - the closure pointer >         - the original value of the scratch register > > This is what the stack will look like: > >         sp before trampoline ------>    -------------------- >                                         | closure pointer  | >                                         -------------------- >                                         | scratch register | >         sp after trampoline ------->    -------------------- > > The ABI handler can do the following as needed by the ABI: > >         - the closure pointer can be loaded in a desired register > >         - the scratch register can be restored to its original value > >         - the stack pointer can be restored to its original value >           (when the trampoline was invoked) > > Thus the ABI handlers will have a couple of lines of code at the very > beginning to do this so that all ABIs will work. > > NOTE: >         The documentation for this feature will contain information on: > >         - the name of the scratch register for each architecture > >         - the stack offsets at which the closure and the scratch register >           will be copied > > Trampoline Table > ================ > > In order to reduce the trampoline memory footprint, the trampoline code > would be defined as a code array in the text segment. This array would be > mapped into the address space of the caller. The mapping would, therefore, > contain a trampoline table. > > Adjacent to the trampoline table, there will be a data mapping that contains > a parameter table, one parameter block for each trampoline. The parameter > table will contain: > >         - a pointer to the closure >         - a pointer to the ABI handler > > The trampoline code would finally look like this: > >         - Make space on the stack for the closure and the scratch register >           by moving the stack pointer down >         - Store the original value of the scratch register on the stack >         - Using PC-relative reference, get the closure pointer >         - Store the closure pointer on the stack >         - Using PC-relative reference, get the ABI handler pointer >         - Jump to the ABI handler > > Trampoline API > ============== > > There is a lot of dynamic code out there. They all have the same security > issue. Dynamic code can be re-written into static code provided the data > required by the static code can be passed to it just like we pass the > closure pointer to an ABI handler. > > So, the same trampoline functions used by libffi internally need to be > made available to the rest of the world in the form of an API. The > following API has been defined in this solution: > > int ffi_tramp_is_supported(void); > >         To support static trampolines, code needs to be added to each >         architecture. Also, the feature itself can be enabled via a >         configuration option. So, this function tells us if the feature >         is supported and enabled in the current libffi or not. > > void *ffi_tramp_alloc (int flags); > >         Allocate a trampoline. Currently, flags are zero. An opaque >         trampoline structure pointer is returned. > >         Internally, libffi manages trampoline tables and individual >         trampolines in each table. > > int ffi_tramp_set_parms (void *tramp, void *target, void *data); > >         Initialize the parameters of a trampoline. That is, the target code >         that the trampoline should jump to and the data that needs to be >         passed to the target code. > > void *ffi_tramp_get_addr (void *tramp); > >         Return the address of the trampoline to invoke the trampoline with. >         The trampoline can be invoked in one of two ways: > >                 - Simply branch to the trampoline address >                 - Treat the trampoline address as a function pointer and >                   call it. > >         Which method is used depends on the target code. > > void ffi_tramp_free (void *tramp); > >         Free a trampoline. > > Configuration > ============= > > A new configuration option, --enable-static-tramp has been added to enable > the use of static trampolines. > > Mapping size > ============ > > The size of the code mapping that contains the trampoline table needs to be > determined on a per architecture basis. If a particular architecture > supports multiple base page sizes, then the largest base page size needs to > be chosen. E.g., we choose 16K for ARM64. > > Trampoline allocation and free > ============================== > > Static trampolines are allocated in ffi_closure_alloc() and freed in > ffi_closure_free(). > > Normally, applications use these functions. But there are some cases out > there where the user of libffi allocates and manages its own closure > memory. In such cases, the static trampoline API cannot be used. These > will fall back to using legacy trampolines. The user has to make sure > that the memory is executable. > > ffi_closure structure > ===================== > > I did not want to make any changes to the size of the closure structure for > this feature to guarantee compatibility. But the opaque static trampoline > handle needs to be stored in the closure. I have defined it as follows: > > -  char tramp[FFI_TRAMPOLINE_SIZE]; > +  union { > +    char tramp[FFI_TRAMPOLINE_SIZE]; > +    void *ftramp; > +  }; > > If static trampolines are used, then tramp[] is not needed to store a > dynamic trampoline. That space can be reused to store the handle. Hence, > the union. > > Signed-off-by: Madhavan T. Venkataraman > > --- >  Makefile.am                                 |   3 +- >  configure.ac                                 |   7 + >  include/ffi.h.in                             |  13 +- >  include/ffi_common.h                        |   4 + >  libffi.map.in                                |  11 + >  src/closures.c                              |  54 +- >  src/tramp.c                                 | 563 ++++++++++++++++++++ >  testsuite/libffi.closures/closure_loc_fn0.c |   3 + >  8 files changed, 654 insertions(+), 4 deletions(-) >  create mode 100644 src/tramp.c > > diff --git a/Makefile.am b/Makefile.am > index 7654bf5..1b18198 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -38,7 +38,8 @@ toolexeclib_LTLIBRARIES = libffi.la >  noinst_LTLIBRARIES = libffi_convenience.la > >  libffi_la_SOURCES = src/prep_cif.c src/types.c \ > -               src/raw_api.c src/java_raw_api.c src/closures.c > +               src/raw_api.c src/java_raw_api.c src/closures.c \ > +               src/tramp.c > >  if FFI_DEBUG >  libffi_la_SOURCES += src/debug.c > diff --git a/configure.ac b/configure.ac > index 790274e..898ede6 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -360,6 +360,13 @@ AC_ARG_ENABLE(raw-api, >      AC_DEFINE(FFI_NO_RAW_API, 1, [Define this if you do not want support for the raw API.]) >    fi) > > +AC_ARG_ENABLE(static-tramp, > +[  --enable-static-tramp        use statically defined trampolines], > +  if test "$enable_static_tramp" = "yes"; then > +    AC_DEFINE(FFI_EXEC_STATIC_TRAMP, 1, > +      [Define this if you want to use statically defined trampolines.]) > +  fi) > + >  AC_ARG_ENABLE(purify-safety, >  [  --enable-purify-safety  purify-safe mode], >    if test "$enable_purify_safety" = "yes"; then > diff --git a/include/ffi.h.in b/include/ffi.h.in > index 38885b0..c6a21d9 100644 > --- a/include/ffi.h.in > +++ b/include/ffi.h.in > @@ -310,7 +310,10 @@ typedef struct { >    void *trampoline_table; >    void *trampoline_table_entry; >  #else > -  char tramp[FFI_TRAMPOLINE_SIZE]; > +  union { > +    char tramp[FFI_TRAMPOLINE_SIZE]; > +    void *ftramp; > +  }; >  #endif >    ffi_cif   *cif; >    void     (*fun)(ffi_cif*,void*,void**,void*); > @@ -457,6 +460,14 @@ FFI_API void ffi_call_go (ffi_cif *cif, void (*fn)(void), void *rvalue, > >  #endif /* FFI_GO_CLOSURES */ > > +/* ---- Static Trampoline Definitions -------------------------------------- */ > + > +FFI_API int ffi_tramp_is_supported(void); > +FFI_API void *ffi_tramp_alloc (int flags); > +FFI_API int ffi_tramp_set_parms (void *tramp, void *data, void *code); > +FFI_API void *ffi_tramp_get_addr (void *tramp); > +FFI_API void ffi_tramp_free (void *tramp); > + >  /* ---- Public interface definition -------------------------------------- */ > >  FFI_API > diff --git a/include/ffi_common.h b/include/ffi_common.h > index 76b9dd6..8743126 100644 > --- a/include/ffi_common.h > +++ b/include/ffi_common.h > @@ -103,6 +103,10 @@ ffi_status ffi_prep_cif_core(ffi_cif *cif, >     some targets.  */ >  void *ffi_data_to_code_pointer (void *data) FFI_HIDDEN; > > +/* The arch code calls this to set the code and data parameters for a > +   closure's static trampoline, if any. */ > +int ffi_closure_tramp_set_parms (void *closure, void *code); > + >  /* Extended cif, used in callback from assembly routine */ >  typedef struct >  { > diff --git a/libffi.map.in b/libffi.map.in > index de8778a..049c73e 100644 > --- a/libffi.map.in > +++ b/libffi.map.in > @@ -74,3 +74,14 @@ LIBFFI_GO_CLOSURE_8.0 { >         ffi_prep_go_closure; >  } LIBFFI_CLOSURE_8.0; >  #endif > + > +#if FFI_EXEC_STATIC_TRAMP > +LIBFFI_STATIC_TRAMP_8.0 { > +  global: > +       ffi_tramp_is_supported; > +       ffi_tramp_alloc; > +       ffi_tramp_set_parms; > +       ffi_tramp_get_addr; > +       ffi_tramp_free; > +} LIBFFI_BASE_8.0; > +#endif > diff --git a/src/closures.c b/src/closures.c > index 4fe6158..cf1d3de 100644 > --- a/src/closures.c > +++ b/src/closures.c > @@ -109,6 +109,12 @@ ffi_closure_free (void *ptr) >    munmap(dataseg, rounded_size); >    munmap(codeseg, rounded_size); >  } > + > +int > +ffi_closure_tramp_set_parms (void *ptr, void *code) > +{ > +  return 0; > +} >  #else /* !NetBSD with PROT_MPROTECT */ > >  #if !FFI_MMAP_EXEC_WRIT && !FFI_EXEC_TRAMPOLINE_TABLE > @@ -843,6 +849,12 @@ dlmmap (void *start, size_t length, int prot, >           && flags == (MAP_PRIVATE | MAP_ANONYMOUS) >           && fd == -1 && offset == 0); > > +  if (execfd == -1 && ffi_tramp_is_supported ()) > +    { > +      ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset); > +      return ptr; > +    } > + >    if (execfd == -1 && is_emutramp_enabled ()) >      { >        ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset); > @@ -922,7 +934,7 @@ segment_holding_code (mstate m, char* addr) >  void * >  ffi_closure_alloc (size_t size, void **code) >  { > -  void *ptr; > +  void *ptr, *ftramp; > >    if (!code) >      return NULL; > @@ -934,6 +946,17 @@ ffi_closure_alloc (size_t size, void **code) >        msegmentptr seg = segment_holding (gm, ptr); > >        *code = add_segment_exec_offset (ptr, seg); > +      if (!ffi_tramp_is_supported ()) > +        return ptr; > + > +      ftramp = ffi_tramp_alloc (0); > +      if (ftramp == NULL) > +      { > +        dlfree (FFI_RESTORE_PTR (ptr)); > +        return NULL; > +      } > +      *code = ffi_tramp_get_addr (ftramp); > +      ((ffi_closure *) ptr)->ftramp = ftramp; >      } > >    return ptr; > @@ -943,12 +966,17 @@ void * >  ffi_data_to_code_pointer (void *data) >  { >    msegmentptr seg = segment_holding (gm, data); > + >    /* We expect closures to be allocated with ffi_closure_alloc(), in >       which case seg will be non-NULL.  However, some users take on the >       burden of managing this memory themselves, in which case this >       we'll just return data. */ >    if (seg) > -    return add_segment_exec_offset (data, seg); > +    { > +      if (!ffi_tramp_is_supported ()) > +        return add_segment_exec_offset (data, seg); > +      return ffi_tramp_get_addr (((ffi_closure *) data)->ftramp); > +    } >    else >      return data; >  } > @@ -966,10 +994,26 @@ ffi_closure_free (void *ptr) >    if (seg) >      ptr = sub_segment_exec_offset (ptr, seg); >  #endif > +  if (ffi_tramp_is_supported ()) > +    ffi_tramp_free (((ffi_closure *) ptr)->ftramp); > >    dlfree (FFI_RESTORE_PTR (ptr)); >  } > > +int > +ffi_closure_tramp_set_parms (void *ptr, void *code) > +{ > +  void *ftramp; > + > +  msegmentptr seg = segment_holding (gm, ptr); > +  if (!seg || !ffi_tramp_is_supported()) > +    return 0; > + > +  ftramp = ((ffi_closure *) ptr)->ftramp; > +  ffi_tramp_set_parms (ftramp, code, ptr); > +  return 1; > +} > + >  # else /* ! FFI_MMAP_EXEC_WRIT */ > >  /* On many systems, memory returned by malloc is writable and > @@ -998,6 +1042,12 @@ ffi_data_to_code_pointer (void *data) >    return data; >  } > > +int > +ffi_closure_tramp_set_parms (void *ptr, void *code) > +{ > +  return 0; > +} > + >  # endif /* ! FFI_MMAP_EXEC_WRIT */ >  #endif /* FFI_CLOSURES */ > > diff --git a/src/tramp.c b/src/tramp.c > new file mode 100644 > index 0000000..3170152 > --- /dev/null > +++ b/src/tramp.c > @@ -0,0 +1,563 @@ > +/* ----------------------------------------------------------------------- > +   tramp.c - Copyright (c) 2020 Madhavan T. Venkataraman > + > +   API and support functions for managing statically defined closure > +   trampolines. > + > +   Permission is hereby granted, free of charge, to any person obtaining > +   a copy of this software and associated documentation files (the > +   ``Software''), to deal in the Software without restriction, including > +   without limitation the rights to use, copy, modify, merge, publish, > +   distribute, sublicense, and/or sell copies of the Software, and to > +   permit persons to whom the Software is furnished to do so, subject to > +   the following conditions: > + > +   The above copyright notice and this permission notice shall be included > +   in all copies or substantial portions of the Software. > + > +   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, > +   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > +   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND > +   NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT > +   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, > +   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > +   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > +   DEALINGS IN THE SOFTWARE. > +   ----------------------------------------------------------------------- */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#if defined(FFI_EXEC_STATIC_TRAMP) > + > +#if !defined(__linux__) > +#error "FFI_EXEC_STATIC_TRAMP is currently only supported on Linux" > +#endif > + > +#if !defined _GNU_SOURCE > +#define _GNU_SOURCE 1 > +#endif > + > +/* > + * Each architecture defines static code for a trampoline code table. The > + * trampoline code table is mapped into the address space of a process. > + * > + * The following architecture specific function returns: > + * > + *     - the address of the trampoline code table in the text segment > + *     - the size of each trampoline in the trampoline code table > + *     - the size of the mapping for the whole trampoline code table > + */ > +void __attribute__((weak)) *ffi_tramp_arch (size_t *tramp_size, > +  size_t *map_size); > + > +/* ------------------------- Trampoline Data Structures --------------------*/ > + > +static pthread_mutex_t tramp_lock = PTHREAD_MUTEX_INITIALIZER; > + > +struct tramp; > + > +/* > + * Trampoline table. Manages one trampoline code table and one trampoline > + * parameter table. > + * > + * prev, next  Links in the global trampoline table list. > + * code_table  Trampoline code table mapping. > + * parm_table  Trampoline parameter table mapping. > + * array       Array of trampolines malloced. > + * free                List of free trampolines. > + * nfree       Number of free trampolines. > + */ > +struct tramp_table > +{ > +  struct tramp_table *prev; > +  struct tramp_table *next; > +  void *code_table; > +  void *parm_table; > +  struct tramp *array; > +  struct tramp *free; > +  int nfree; > +}; > + > +/* > + * Parameters for each trampoline. > + * > + * data > + *     Data for the target code that the trampoline jumps to. > + * target > + *     Target code that the trampoline jumps to. > + */ > +struct tramp_parm > +{ > +  void *data; > +  void *target; > +}; > + > +/* > + * Trampoline structure for each trampoline. > + * > + * prev, next  Links in the trampoline free list of a trampoline table. > + * table       Trampoline table to which this trampoline belongs. > + * code                Address of this trampoline in the code table mapping. > + * parm                Address of this trampoline's parameters in the parameter > + *             table mapping. > + */ > +struct tramp > +{ > +  struct tramp *prev; > +  struct tramp *next; > +  struct tramp_table *table; > +  void *code; > +  struct tramp_parm *parm; > +}; > + > +/* > + * Trampoline globals. > + * > + * fd > + *     File descriptor of binary file that contains the trampoline code table. > + * offset > + *     Offset of the trampoline code table in that file. > + * map_size > + *     Size of the trampoline code table mapping. > + * size > + *     Size of one trampoline in the trampoline code table. > + * ntramp > + *     Total number of trampolines in the trampoline code table. > + * tables > + *     List of trampoline tables that contain free trampolines. > + * ntables > + *     Number of trampoline tables that contain free trampolines. > + */ > +struct tramp_global > +{ > +  int fd; > +  off_t offset; > +  size_t map_size; > +  size_t size; > +  int ntramp; > +  struct tramp_table *tables; > +  int ntables; > +}; > + > +static struct tramp_global gtramp = { -1 }; > + > +/* ------------------------ Trampoline Initialization ----------------------*/ > + > +static int ffi_tramp_get_fd_offset (void *tramp_text, off_t *offset); > + > +/* > + * Initialize the static trampoline feature. > + */ > +static int > +ffi_tramp_init (void) > +{ > +  if (ffi_tramp_arch == NULL) > +    return 0; > + > +  if (gtramp.fd == -1) > +    { > +      void *tramp_text; > + > +      gtramp.tables = NULL; > +      gtramp.ntables = 0; > + > +      /* > +       * Get trampoline code table information from the architecture. > +       */ > +      tramp_text = ffi_tramp_arch (>ramp.size, >ramp.map_size); > +      gtramp.ntramp = gtramp.map_size / gtramp.size; > + > +      /* > +       * Get the binary file that contains the trampoline code table and also > +       * the offset of the table within the file. These are used to mmap() > +       * the trampoline code table. > +       */ > +      gtramp.fd = ffi_tramp_get_fd_offset (tramp_text, >ramp.offset); > +    } > +  return gtramp.fd != -1; > +} > + > +/* > + * From the address of the trampoline code table in the text segment, find the > + * binary file and offset of the trampoline code table from /proc//maps. > + */ > +static int > +ffi_tramp_get_fd_offset (void *tramp_text, off_t *offset) > +{ > +  FILE *fp; > +  char file[PATH_MAX], line[PATH_MAX+100], perm[10], dev[10]; > +  unsigned long start, end, inode; > +  uintptr_t addr = (uintptr_t) tramp_text; > +  int nfields, found; > + > +  snprintf (file, PATH_MAX, "/proc/%d/maps", getpid()); > +  fp = fopen (file, "r"); > +  if (fp == NULL) > +    return -1; > + > +  found = 0; > +  while (feof (fp) == 0) { > +    if (fgets (line, sizeof (line), fp) == 0) > +      break; > + > +    nfields = sscanf (line, "%lx-%lx %s %lx %s %ld %s", > +      &start, &end, perm, offset, dev, &inode, file); > +    if (nfields != 7) > +      continue; > + > +    if (addr >= start && addr < end) { > +      *offset += (addr - start); > +      found = 1; > +      break; > +    } > +  } > +  fclose (fp); > + > +  if (!found) > +    return -1; > + > +  return open (file, O_RDONLY); > +} > + > +/* ---------------------- Trampoline Table functions ---------------------- */ > + > +static int tramp_table_map (char **code_table, char **parm_table); > +static void tramp_add (struct tramp *tramp); > + > +/* > + * Allocate and initialize a trampoline table. > + */ > +static int > +tramp_table_alloc (void) > +{ > +  char *code_table, *parm_table; > +  struct tramp_table *table; > +  struct tramp *tramp_array, *tramp; > +  size_t size; > +  char *code, *parm; > +  int i; > + > +  /* > +   * If we already have tables with free trampolines, there is no need to > +   * allocate a new table. > +   */ > +  if (gtramp.ntables > 0) > +    return 1; > + > +  /* > +   * Allocate a new trampoline table structure. > +   */ > +  table = malloc (sizeof (*table)); > +  if (table == NULL) > +    return 0; > + > +  /* > +   * Allocate new trampoline structures. > +   */ > +  tramp_array = malloc (sizeof (*tramp) * gtramp.ntramp); > +  if (tramp_array == NULL) > +    goto free_table; > + > +  /* > +   * Map a code table and a parameter table into the caller's address space. > +   */ > +  if (!tramp_table_map (&code_table, &parm_table)) > +    goto free_tramp_array; > + > +  /* > +   * Initialize the trampoline table. > +   */ > +  table->code_table = code_table; > +  table->parm_table = parm_table; > +  table->array = tramp_array; > +  table->free = NULL; > +  table->nfree = 0; > + > +  /* > +   * Populate the trampoline table free list. This will also add the trampoline > +   * table to the global list of trampoline tables. > +   */ > +  size = gtramp.size; > +  code = code_table; > +  parm = parm_table; > +  for (i = 0; i < gtramp.ntramp; i++) > +    { > +      tramp = &tramp_array[i]; > +      tramp->table = table; > +      tramp->code = code; > +      tramp->parm = (struct tramp_parm *) parm; > +      tramp_add (tramp); > + > +      code += size; > +      parm += size; > +    } > +  return 1; > + > +free_tramp_array: > +  free (tramp_array); > +free_table: > +  free (table); > +  return 0; > +} > + > +/* > + * Create a trampoline code table mapping and a trampoline parameter table > + * mapping. The two mappings must be adjacent to each other for PC-relative > + * access. > + * > + * For each trampoline in the code table, there is a corresponding parameter > + * block in the parameter table. The size of the parameter block is the same > + * as the size of the trampoline. This means that the parameter block is at > + * a fixed offset from its trampoline making it easy for a trampoline to find > + * its parameters using PC-relative access. > + * > + * The parameter block will contain a struct tramp_parm. This means that > + * sizeof (struct tramp_parm) cannot exceed the size of a parameter block. > + */ > +static int > +tramp_table_map (char **code_table, char **parm_table) > +{ > +  char *addr; > + > +  /* > +   * Create an anonymous mapping twice the map size. The top half will be used > +   * for the code table. The bottom half will be used for the parameter table. > +   */ > +  addr = mmap (NULL, gtramp.map_size * 2, PROT_READ | PROT_WRITE, > +    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); > +  if (addr == MAP_FAILED) > +    return 0; > + > +  /* > +   * Replace the top half of the anonymous mapping with the code table mapping. > +   */ > +  *code_table = mmap (addr, gtramp.map_size, PROT_READ | PROT_EXEC, > +    MAP_PRIVATE | MAP_FIXED, gtramp.fd, gtramp.offset); > +  if (*code_table == MAP_FAILED) > +    { > +      (void) munmap (addr, gtramp.map_size * 2); > +      return 0; > +    } > +  *parm_table = *code_table + gtramp.map_size; > +  return 1; > +} > + > +/* > + * Free a trampoline table. > + */ > +static void > +tramp_table_free (struct tramp_table *table) > +{ > +  (void) munmap (table->code_table, gtramp.map_size); > +  (void) munmap (table->parm_table, gtramp.map_size); > +  free (table->array); > +  free (table); > +} > + > +/* > + * Add a new trampoline table to the global table list. > + */ > +static void > +tramp_table_add (struct tramp_table *table) > +{ > +  table->next = gtramp.tables; > +  table->prev = NULL; > +  if (gtramp.tables != NULL) > +    gtramp.tables->prev = table; > +  gtramp.tables = table; > +  gtramp.ntables++; > +} > + > +/* > + * Delete a trampoline table from the global table list. > + */ > +static void > +tramp_table_del (struct tramp_table *table) > +{ > +  gtramp.ntables--; > +  if (table->prev != NULL) > +    table->prev->next = table->next; > +  if (table->next != NULL) > +    table->next->prev = table->prev; > +  if (gtramp.tables == table) > +    gtramp.tables = table->next; > +} > + > +/* ------------------------- Trampoline functions ------------------------- */ > + > +/* > + * Add a trampoline to its trampoline table. > + */ > +static void > +tramp_add (struct tramp *tramp) > +{ > +  struct tramp_table *table = tramp->table; > + > +  tramp->next = table->free; > +  tramp->prev = NULL; > +  if (table->free != NULL) > +    table->free->prev = tramp; > +  table->free = tramp; > +  table->nfree++; > + > +  if (table->nfree == 1) > +    tramp_table_add (table); > + > +  /* > +   * We don't want to keep too many free trampoline tables lying around. > +   */ > +  if (table->nfree == gtramp.ntramp && gtramp.ntables > 1) > +    { > +      tramp_table_del (table); > +      tramp_table_free (table); > +    } > +} > + > +/* > + * Remove a trampoline from its trampoline table. > + */ > +static void > +tramp_del (struct tramp *tramp) > +{ > +  struct tramp_table *table = tramp->table; > + > +  table->nfree--; > +  if (tramp->prev != NULL) > +    tramp->prev->next = tramp->next; > +  if (tramp->next != NULL) > +    tramp->next->prev = tramp->prev; > +  if (table->free == tramp) > +    table->free = tramp->next; > + > +  if (table->nfree == 0) > +    tramp_table_del (table); > +} > + > +/* ------------------------ Trampoline API functions ------------------------ */ > + > +int > +ffi_tramp_is_supported(void) > +{ > +  int ret; > + > +  pthread_mutex_lock (&tramp_lock); > +  ret = ffi_tramp_init (); > +  pthread_mutex_unlock (&tramp_lock); > +  return ret; > +} > + > +/* > + * Allocate a trampoline and return its opaque address. > + */ > +void * > +ffi_tramp_alloc (int flags) > +{ > +  struct tramp *tramp; > + > +  pthread_mutex_lock (&tramp_lock); > + > +  if (!ffi_tramp_init () || flags != 0) > +    { > +      pthread_mutex_unlock (&tramp_lock); > +      return NULL; > +    } > + > +  if (!tramp_table_alloc ()) > +    { > +      pthread_mutex_unlock (&tramp_lock); > +      return NULL; > +    } > + > +  tramp = gtramp.tables->free; > +  tramp_del (tramp); > + > +  pthread_mutex_unlock (&tramp_lock); > + > +  return tramp; > +} > + > +/* > + * Set the parameters for a trampoline. > + */ > +void > +ffi_tramp_set_parms (void *arg, void *target, void *data) > +{ > +  struct tramp *tramp = arg; > + > +  pthread_mutex_lock (&tramp_lock); > +  tramp->parm->target = target; > +  tramp->parm->data = data; > +  pthread_mutex_unlock (&tramp_lock); > +} > + > +/* > + * Get the invocation address of a trampoline. > + */ > +void * > +ffi_tramp_get_addr (void *arg) > +{ > +  struct tramp *tramp = arg; > +  void *addr; > + > +  pthread_mutex_lock (&tramp_lock); > +  addr = tramp->code; > +  pthread_mutex_unlock (&tramp_lock); > + > +  return addr; > +} > + > +/* > + * Free a trampoline. > + */ > +void > +ffi_tramp_free (void *arg) > +{ > +  struct tramp *tramp = arg; > + > +  pthread_mutex_lock (&tramp_lock); > +  tramp_add (tramp); > +  pthread_mutex_unlock (&tramp_lock); > +} > + > +/* ------------------------------------------------------------------------- */ > + > +#else /* !FFI_EXEC_STATIC_TRAMP */ > + > +int > +ffi_tramp_is_supported(void) > +{ > +  return 0; > +} > + > +void * > +ffi_tramp_alloc (int flags) > +{ > +  return NULL; > +} > + > +void > +ffi_tramp_set_parms (void *arg, void *target, void *data) > +{ > +} > + > +void * > +ffi_tramp_get_addr (void *arg) > +{ > +  return NULL; > +} > + > +void > +ffi_tramp_free (void *arg) > +{ > +} > + > +#endif /* FFI_EXEC_STATIC_TRAMP */ > diff --git a/testsuite/libffi.closures/closure_loc_fn0.c b/testsuite/libffi.closures/closure_loc_fn0.c > index b3afa0b..ad488ac 100644 > --- a/testsuite/libffi.closures/closure_loc_fn0.c > +++ b/testsuite/libffi.closures/closure_loc_fn0.c > @@ -83,7 +83,10 @@ int main (void) >    CHECK(ffi_prep_closure_loc(pcl, &cif, closure_loc_test_fn0, >                          (void *) 3 /* userdata */, codeloc) == FFI_OK); > > +#ifndef FFI_EXEC_STATIC_TRAMP > +  /* With static trampolines, the codeloc does not point to closure */ >    CHECK(memcmp(pcl, codeloc, sizeof(*pcl)) == 0); > +#endif > >    res = (*((closure_loc_test_type0)codeloc)) >      (1LL, 2, 3LL, 4, 127, 429LL, 7, 8, 9.5, 10, 11, 12, 13, > -- > 2.25.1 >