(root)/
gcc-13.2.0/
libffi/
src/
tramp.c
       1  /* -----------------------------------------------------------------------
       2     tramp.c - Copyright (c) 2020 Madhavan T. Venkataraman
       3  
       4     API and support functions for managing statically defined closure
       5     trampolines.
       6  
       7     Permission is hereby granted, free of charge, to any person obtaining
       8     a copy of this software and associated documentation files (the
       9     ``Software''), to deal in the Software without restriction, including
      10     without limitation the rights to use, copy, modify, merge, publish,
      11     distribute, sublicense, and/or sell copies of the Software, and to
      12     permit persons to whom the Software is furnished to do so, subject to
      13     the following conditions:
      14  
      15     The above copyright notice and this permission notice shall be included
      16     in all copies or substantial portions of the Software.
      17  
      18     THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
      19     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      20     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
      21     NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      22     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      23     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      24     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      25     DEALINGS IN THE SOFTWARE.
      26     ----------------------------------------------------------------------- */
      27  
      28  #include <fficonfig.h>
      29  
      30  #ifdef FFI_EXEC_STATIC_TRAMP
      31  
      32  /* -------------------------- Headers and Definitions ---------------------*/
      33  /*
      34   * Add support for other OSes later. For now, it is just Linux.
      35   */
      36  
      37  #if defined __linux__
      38  #ifdef __linux__
      39  #define _GNU_SOURCE 1
      40  #endif
      41  #include <stdio.h>
      42  #include <unistd.h>
      43  #include <stdlib.h>
      44  #include <stdint.h>
      45  #include <fcntl.h>
      46  #include <pthread.h>
      47  #include <sys/mman.h>
      48  #include <tramp.h>
      49  #ifdef __linux__
      50  #include <linux/limits.h>
      51  #include <linux/types.h>
      52  #endif
      53  #endif /* __linux__ */
      54  
      55  /*
      56   * Each architecture defines static code for a trampoline code table. The
      57   * trampoline code table is mapped into the address space of a process.
      58   *
      59   * The following architecture specific function returns:
      60   *
      61   *	- the address of the trampoline code table in the text segment
      62   *	- the size of each trampoline in the trampoline code table
      63   *	- the size of the mapping for the whole trampoline code table
      64   */
      65  void __attribute__((weak)) *ffi_tramp_arch (size_t *tramp_size,
      66    size_t *map_size);
      67  
      68  /* ------------------------- Trampoline Data Structures --------------------*/
      69  
      70  struct tramp;
      71  
      72  /*
      73   * Trampoline table. Manages one trampoline code table and one trampoline
      74   * parameter table.
      75   *
      76   * prev, next	Links in the global trampoline table list.
      77   * code_table	Trampoline code table mapping.
      78   * parm_table	Trampoline parameter table mapping.
      79   * array	Array of trampolines malloced.
      80   * free		List of free trampolines.
      81   * nfree	Number of free trampolines.
      82   */
      83  struct tramp_table
      84  {
      85    struct tramp_table *prev;
      86    struct tramp_table *next;
      87    void *code_table;
      88    void *parm_table;
      89    struct tramp *array;
      90    struct tramp *free;
      91    int nfree;
      92  };
      93  
      94  /*
      95   * Parameters for each trampoline.
      96   *
      97   * data
      98   *	Data for the target code that the trampoline jumps to.
      99   * target
     100   *	Target code that the trampoline jumps to.
     101   */
     102  struct tramp_parm
     103  {
     104    void *data;
     105    void *target;
     106  };
     107  
     108  /*
     109   * Trampoline structure for each trampoline.
     110   *
     111   * prev, next	Links in the trampoline free list of a trampoline table.
     112   * table	Trampoline table to which this trampoline belongs.
     113   * code		Address of this trampoline in the code table mapping.
     114   * parm		Address of this trampoline's parameters in the parameter
     115   *		table mapping.
     116   */
     117  struct tramp
     118  {
     119    struct tramp *prev;
     120    struct tramp *next;
     121    struct tramp_table *table;
     122    void *code;
     123    struct tramp_parm *parm;
     124  };
     125  
     126  enum tramp_globals_status {
     127  	TRAMP_GLOBALS_UNINITIALIZED = 0,
     128  	TRAMP_GLOBALS_PASSED,
     129  	TRAMP_GLOBALS_FAILED,
     130  };
     131  
     132  /*
     133   * Trampoline globals.
     134   *
     135   * fd
     136   *	File descriptor of binary file that contains the trampoline code table.
     137   * offset
     138   *	Offset of the trampoline code table in that file.
     139   * text
     140   *	Address of the trampoline code table in the text segment.
     141   * map_size
     142   *	Size of the trampoline code table mapping.
     143   * size
     144   *	Size of one trampoline in the trampoline code table.
     145   * ntramp
     146   *	Total number of trampolines in the trampoline code table.
     147   * free_tables
     148   *	List of trampoline tables that contain free trampolines.
     149   * nfree_tables
     150   *	Number of trampoline tables that contain free trampolines.
     151   * status
     152   *	Initialization status.
     153   */
     154  struct tramp_globals
     155  {
     156    int fd;
     157    off_t offset;
     158    void *text;
     159    size_t map_size;
     160    size_t size;
     161    int ntramp;
     162    struct tramp_table *free_tables;
     163    int nfree_tables;
     164    enum tramp_globals_status status;
     165  };
     166  
     167  static struct tramp_globals tramp_globals;
     168  
     169  /* --------------------- Trampoline File Initialization --------------------*/
     170  
     171  /*
     172   * The trampoline file is the file used to map the trampoline code table into
     173   * the address space of a process. There are two ways to get this file:
     174   *
     175   * - From the OS. E.g., on Linux, /proc/<pid>/maps lists all the memory
     176   *   mappings for <pid>. For file-backed mappings, maps supplies the file name
     177   *   and the file offset. Using this, we can locate the mapping that maps
     178   *   libffi and get the path to the libffi binary. And, we can compute the
     179   *   offset of the trampoline code table within that binary.
     180   *
     181   * - Else, if we can create a temporary file, we can write the trampoline code
     182   *   table from the text segment into the temporary file.
     183   *
     184   * The first method is the preferred one. If the OS security subsystem
     185   * disallows mapping unsigned files with PROT_EXEC, then the second method
     186   * will fail.
     187   *
     188   * If an OS allows the trampoline code table in the text segment to be
     189   * directly remapped (e.g., MACH vm_remap ()), then we don't need the
     190   * trampoline file.
     191   */
     192  static int tramp_table_alloc (void);
     193  
     194  #if defined __linux__
     195  
     196  static int
     197  ffi_tramp_get_libffi (void)
     198  {
     199    FILE *fp;
     200    char file[PATH_MAX], line[PATH_MAX+100], perm[10], dev[10];
     201    unsigned long start, end, offset, inode;
     202    uintptr_t addr = (uintptr_t) tramp_globals.text;
     203    int nfields, found;
     204  
     205    snprintf (file, PATH_MAX, "/proc/%d/maps", getpid());
     206    fp = fopen (file, "r");
     207    if (fp == NULL)
     208      return 0;
     209  
     210    found = 0;
     211    while (feof (fp) == 0) {
     212      if (fgets (line, sizeof (line), fp) == 0)
     213        break;
     214  
     215      nfields = sscanf (line, "%lx-%lx %9s %lx %9s %ld %s",
     216        &start, &end, perm, &offset, dev, &inode, file);
     217      if (nfields != 7)
     218        continue;
     219  
     220      if (addr >= start && addr < end) {
     221        tramp_globals.offset = offset + (addr - start);
     222        found = 1;
     223        break;
     224      }
     225    }
     226    fclose (fp);
     227  
     228    if (!found)
     229      return 0;
     230  
     231    tramp_globals.fd = open (file, O_RDONLY);
     232    if (tramp_globals.fd == -1)
     233      return 0;
     234  
     235    /*
     236     * Allocate a trampoline table just to make sure that the trampoline code
     237     * table can be mapped.
     238     */
     239    if (!tramp_table_alloc ())
     240      {
     241        close (tramp_globals.fd);
     242        tramp_globals.fd = -1;
     243        return 0;
     244      }
     245    return 1;
     246  }
     247  
     248  #endif /* __linux__ */
     249  
     250  #if defined __linux__
     251  
     252  #if defined HAVE_MKSTEMP
     253  
     254  static int
     255  ffi_tramp_get_temp_file (void)
     256  {
     257    char template[12] = "/tmp/XXXXXX";
     258    ssize_t count;
     259  
     260    tramp_globals.offset = 0;
     261    tramp_globals.fd = mkstemp (template);
     262    if (tramp_globals.fd == -1)
     263      return 0;
     264  
     265    unlink (template);
     266    /*
     267     * Write the trampoline code table into the temporary file and allocate a
     268     * trampoline table to make sure that the temporary file can be mapped.
     269     */
     270    count = write(tramp_globals.fd, tramp_globals.text, tramp_globals.map_size);
     271    if (count == tramp_globals.map_size && tramp_table_alloc ())
     272      return 1;
     273  
     274    close (tramp_globals.fd);
     275    tramp_globals.fd = -1;
     276    return 0;
     277  }
     278  
     279  #else /* !defined HAVE_MKSTEMP */
     280  
     281  /*
     282   * TODO:
     283   * src/closures.c contains code for finding temp file that has EXEC
     284   * permissions. May be, some of that code can be shared with static
     285   * trampolines.
     286   */
     287  static int
     288  ffi_tramp_get_temp_file (void)
     289  {
     290    tramp_globals.offset = 0;
     291    tramp_globals.fd = -1;
     292    return 0;
     293  }
     294  
     295  #endif /* defined HAVE_MKSTEMP */
     296  
     297  #endif /* __linux__ */
     298  
     299  /* ------------------------ OS-specific Initialization ----------------------*/
     300  
     301  #if defined __linux__
     302  
     303  static int
     304  ffi_tramp_init_os (void)
     305  {
     306    if (ffi_tramp_get_libffi ())
     307      return 1;
     308    return ffi_tramp_get_temp_file ();
     309  }
     310  
     311  #endif /* __linux__ */
     312  
     313  /* --------------------------- OS-specific Locking -------------------------*/
     314  
     315  #if defined __linux__
     316  
     317  static pthread_mutex_t tramp_globals_mutex = PTHREAD_MUTEX_INITIALIZER;
     318  
     319  static void
     320  ffi_tramp_lock(void)
     321  {
     322    pthread_mutex_lock (&tramp_globals_mutex);
     323  }
     324  
     325  static void
     326  ffi_tramp_unlock()
     327  {
     328    pthread_mutex_unlock (&tramp_globals_mutex);
     329  }
     330  
     331  #endif /* __linux__ */
     332  
     333  /* ------------------------ OS-specific Memory Mapping ----------------------*/
     334  
     335  /*
     336   * Create a trampoline code table mapping and a trampoline parameter table
     337   * mapping. The two mappings must be adjacent to each other for PC-relative
     338   * access.
     339   *
     340   * For each trampoline in the code table, there is a corresponding parameter
     341   * block in the parameter table. The size of the parameter block is the same
     342   * as the size of the trampoline. This means that the parameter block is at
     343   * a fixed offset from its trampoline making it easy for a trampoline to find
     344   * its parameters using PC-relative access.
     345   *
     346   * The parameter block will contain a struct tramp_parm. This means that
     347   * sizeof (struct tramp_parm) cannot exceed the size of a parameter block.
     348   */
     349  
     350  #if defined __linux__
     351  
     352  static int
     353  tramp_table_map (struct tramp_table *table)
     354  {
     355    char *addr;
     356  
     357    /*
     358     * Create an anonymous mapping twice the map size. The top half will be used
     359     * for the code table. The bottom half will be used for the parameter table.
     360     */
     361    addr = mmap (NULL, tramp_globals.map_size * 2, PROT_READ | PROT_WRITE,
     362      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     363    if (addr == MAP_FAILED)
     364      return 0;
     365  
     366    /*
     367     * Replace the top half of the anonymous mapping with the code table mapping.
     368     */
     369    table->code_table = mmap (addr, tramp_globals.map_size, PROT_READ | PROT_EXEC,
     370      MAP_PRIVATE | MAP_FIXED, tramp_globals.fd, tramp_globals.offset);
     371    if (table->code_table == MAP_FAILED)
     372      {
     373        (void) munmap (addr, tramp_globals.map_size * 2);
     374        return 0;
     375      }
     376    table->parm_table = table->code_table + tramp_globals.map_size;
     377    return 1;
     378  }
     379  
     380  static void
     381  tramp_table_unmap (struct tramp_table *table)
     382  {
     383    (void) munmap (table->code_table, tramp_globals.map_size);
     384    (void) munmap (table->parm_table, tramp_globals.map_size);
     385  }
     386  
     387  #endif /* __linux__ */
     388  
     389  /* ------------------------ Trampoline Initialization ----------------------*/
     390  
     391  /*
     392   * Initialize the static trampoline feature.
     393   */
     394  static int
     395  ffi_tramp_init (void)
     396  {
     397    if (tramp_globals.status == TRAMP_GLOBALS_PASSED)
     398      return 1;
     399  
     400    if (tramp_globals.status == TRAMP_GLOBALS_FAILED)
     401      return 0;
     402  
     403    if (ffi_tramp_arch == NULL)
     404      {
     405        tramp_globals.status = TRAMP_GLOBALS_FAILED;
     406        return 0;
     407      }
     408  
     409    tramp_globals.free_tables = NULL;
     410    tramp_globals.nfree_tables = 0;
     411  
     412    /*
     413     * Get trampoline code table information from the architecture.
     414     */
     415    tramp_globals.text = ffi_tramp_arch (&tramp_globals.size,
     416      &tramp_globals.map_size);
     417    tramp_globals.ntramp = tramp_globals.map_size / tramp_globals.size;
     418  
     419    if (sysconf (_SC_PAGESIZE) > tramp_globals.map_size)
     420      return 0;
     421  
     422    if (ffi_tramp_init_os ())
     423      {
     424        tramp_globals.status = TRAMP_GLOBALS_PASSED;
     425        return 1;
     426      }
     427  
     428    tramp_globals.status = TRAMP_GLOBALS_FAILED;
     429    return 0;
     430  }
     431  
     432  /* ---------------------- Trampoline Table functions ---------------------- */
     433  
     434  /* This code assumes that malloc () is available on all OSes. */
     435  
     436  static void tramp_add (struct tramp *tramp);
     437  
     438  /*
     439   * Allocate and initialize a trampoline table.
     440   */
     441  static int
     442  tramp_table_alloc (void)
     443  {
     444    struct tramp_table *table;
     445    struct tramp *tramp_array, *tramp;
     446    size_t size;
     447    char *code, *parm;
     448    int i;
     449  
     450    /*
     451     * If we already have tables with free trampolines, there is no need to
     452     * allocate a new table.
     453     */
     454    if (tramp_globals.nfree_tables > 0)
     455      return 1;
     456  
     457    /*
     458     * Allocate a new trampoline table structure.
     459     */
     460    table = malloc (sizeof (*table));
     461    if (table == NULL)
     462      return 0;
     463  
     464    /*
     465     * Allocate new trampoline structures.
     466     */
     467    tramp_array = malloc (sizeof (*tramp) * tramp_globals.ntramp);
     468    if (tramp_array == NULL)
     469      goto free_table;
     470  
     471    /*
     472     * Map a code table and a parameter table into the caller's address space.
     473     */
     474    if (!tramp_table_map (table))
     475      {
     476        /*
     477         * Failed to map the code and parameter tables.
     478         */
     479        goto free_tramp_array;
     480      }
     481  
     482    /*
     483     * Initialize the trampoline table.
     484     */
     485    table->array = tramp_array;
     486    table->free = NULL;
     487    table->nfree = 0;
     488  
     489    /*
     490     * Populate the trampoline table free list. This will also add the trampoline
     491     * table to the global list of trampoline tables.
     492     */
     493    size = tramp_globals.size;
     494    code = table->code_table;
     495    parm = table->parm_table;
     496    for (i = 0; i < tramp_globals.ntramp; i++)
     497      {
     498        tramp = &tramp_array[i];
     499        tramp->table = table;
     500        tramp->code = code;
     501        tramp->parm = (struct tramp_parm *) parm;
     502        tramp_add (tramp);
     503  
     504        code += size;
     505        parm += size;
     506      }
     507    /* Success */
     508    return 1;
     509  
     510  /* Failure */
     511  free_tramp_array:
     512    free (tramp_array);
     513  free_table:
     514    free (table);
     515    return 0;
     516  }
     517  
     518  /*
     519   * Free a trampoline table.
     520   */
     521  static void
     522  tramp_table_free (struct tramp_table *table)
     523  {
     524    tramp_table_unmap (table);
     525    free (table->array);
     526    free (table);
     527  }
     528  
     529  /*
     530   * Add a new trampoline table to the global table list.
     531   */
     532  static void
     533  tramp_table_add (struct tramp_table *table)
     534  {
     535    table->next = tramp_globals.free_tables;
     536    table->prev = NULL;
     537    if (tramp_globals.free_tables != NULL)
     538      tramp_globals.free_tables->prev = table;
     539    tramp_globals.free_tables = table;
     540    tramp_globals.nfree_tables++;
     541  }
     542  
     543  /*
     544   * Delete a trampoline table from the global table list.
     545   */
     546  static void
     547  tramp_table_del (struct tramp_table *table)
     548  {
     549    tramp_globals.nfree_tables--;
     550    if (table->prev != NULL)
     551      table->prev->next = table->next;
     552    if (table->next != NULL)
     553      table->next->prev = table->prev;
     554    if (tramp_globals.free_tables == table)
     555      tramp_globals.free_tables = table->next;
     556  }
     557  
     558  /* ------------------------- Trampoline functions ------------------------- */
     559  
     560  /*
     561   * Add a trampoline to its trampoline table.
     562   */
     563  static void
     564  tramp_add (struct tramp *tramp)
     565  {
     566    struct tramp_table *table = tramp->table;
     567  
     568    tramp->next = table->free;
     569    tramp->prev = NULL;
     570    if (table->free != NULL)
     571      table->free->prev = tramp;
     572    table->free = tramp;
     573    table->nfree++;
     574  
     575    if (table->nfree == 1)
     576      tramp_table_add (table);
     577  
     578    /*
     579     * We don't want to keep too many free trampoline tables lying around.
     580     */
     581    if (table->nfree == tramp_globals.ntramp &&
     582      tramp_globals.nfree_tables > 1)
     583      {
     584        tramp_table_del (table);
     585        tramp_table_free (table);
     586      }
     587  }
     588  
     589  /*
     590   * Remove a trampoline from its trampoline table.
     591   */
     592  static void
     593  tramp_del (struct tramp *tramp)
     594  {
     595    struct tramp_table *table = tramp->table;
     596  
     597    table->nfree--;
     598    if (tramp->prev != NULL)
     599      tramp->prev->next = tramp->next;
     600    if (tramp->next != NULL)
     601      tramp->next->prev = tramp->prev;
     602    if (table->free == tramp)
     603      table->free = tramp->next;
     604  
     605    if (table->nfree == 0)
     606      tramp_table_del (table);
     607  }
     608  
     609  /* ------------------------ Trampoline API functions ------------------------ */
     610  
     611  int
     612  ffi_tramp_is_supported(void)
     613  {
     614    int ret;
     615  
     616    ffi_tramp_lock();
     617    ret = ffi_tramp_init ();
     618    ffi_tramp_unlock();
     619    return ret;
     620  }
     621  
     622  /*
     623   * Allocate a trampoline and return its opaque address.
     624   */
     625  void *
     626  ffi_tramp_alloc (int flags)
     627  {
     628    struct tramp *tramp;
     629  
     630    ffi_tramp_lock();
     631  
     632    if (!ffi_tramp_init () || flags != 0)
     633      {
     634        ffi_tramp_unlock();
     635        return NULL;
     636      }
     637  
     638    if (!tramp_table_alloc ())
     639      {
     640        ffi_tramp_unlock();
     641        return NULL;
     642      }
     643  
     644    tramp = tramp_globals.free_tables->free;
     645    tramp_del (tramp);
     646  
     647    ffi_tramp_unlock();
     648  
     649    return tramp;
     650  }
     651  
     652  /*
     653   * Set the parameters for a trampoline.
     654   */
     655  void
     656  ffi_tramp_set_parms (void *arg, void *target, void *data)
     657  {
     658    struct tramp *tramp = arg;
     659  
     660    ffi_tramp_lock();
     661    tramp->parm->target = target;
     662    tramp->parm->data = data;
     663    ffi_tramp_unlock();
     664  }
     665  
     666  /*
     667   * Get the invocation address of a trampoline.
     668   */
     669  void *
     670  ffi_tramp_get_addr (void *arg)
     671  {
     672    struct tramp *tramp = arg;
     673    void *addr;
     674  
     675    ffi_tramp_lock();
     676    addr = tramp->code;
     677    ffi_tramp_unlock();
     678  
     679    return addr;
     680  }
     681  
     682  /*
     683   * Free a trampoline.
     684   */
     685  void
     686  ffi_tramp_free (void *arg)
     687  {
     688    struct tramp *tramp = arg;
     689  
     690    ffi_tramp_lock();
     691    tramp_add (tramp);
     692    ffi_tramp_unlock();
     693  }
     694  
     695  /* ------------------------------------------------------------------------- */
     696  
     697  #else /* !FFI_EXEC_STATIC_TRAMP */
     698  
     699  #include <stddef.h>
     700  
     701  int
     702  ffi_tramp_is_supported(void)
     703  {
     704    return 0;
     705  }
     706  
     707  void *
     708  ffi_tramp_alloc (int flags)
     709  {
     710    return NULL;
     711  }
     712  
     713  void
     714  ffi_tramp_set_parms (void *arg, void *target, void *data)
     715  {
     716  }
     717  
     718  void *
     719  ffi_tramp_get_addr (void *arg)
     720  {
     721    return NULL;
     722  }
     723  
     724  void
     725  ffi_tramp_free (void *arg)
     726  {
     727  }
     728  
     729  #endif /* FFI_EXEC_STATIC_TRAMP */