ffi 1.9.24 → 1.9.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c08414eed21b4a4c2dbb89687f9004a970ce2b997fc419566221ee01384e5eb
4
- data.tar.gz: 4849cae15554e04a89322a7e1738b804be57f09e8606170803655c4804ba03a3
3
+ metadata.gz: cf13d7fccdcd65b73b5286e04fc25a174b7b855ab1803995eaa4564d5d65456a
4
+ data.tar.gz: 187c8d21dd770d08e7e123ead2a727fc3e9e384d35ff2b5d37fc3a0df712f37e
5
5
  SHA512:
6
- metadata.gz: a4984c832d1358e61c05b50b936e6f757cb301202d441a686bbd2b2a963f77fd1371bfcb2644e75522ded093d0f9055843d28bf7593ef7f037c349cd443efab5
7
- data.tar.gz: cae427a16110ac965beb4e999f9f5a8950a69f321a09d641343dfd2b3ec4f3fcd5ad428047f25e1b87800b4f0dd2827eb5cd143e51c50af9944ce2c0f38eb892
6
+ metadata.gz: 06eb5d5d2c033e0b7ade6d75957f835d7831aca9b492ac752e7d2b4760707ecb07981e1cd366f0059b67c1b2f3a9b5964e70388d123f4997efa0f5c691baede8
7
+ data.tar.gz: e050bf8673f3bb19afbe78646f18f452c4fa2d70fc2a8f9f8efb073cd0658f6a7e0f1737dd6a05076422533ecf22e9952f08d407bdd0480bbde31fd55eef52de
@@ -1,3 +1,11 @@
1
+ 1.9.25 / 2018-06-03
2
+ -------------------
3
+
4
+ Changed:
5
+ * Revert closures via libffi.
6
+ This re-adds ClosurePool and fixes compat with SELinux enabled systems. #621
7
+
8
+
1
9
  1.9.24 / 2018-06-02
2
10
  -------------------
3
11
 
data/README.md CHANGED
@@ -43,6 +43,8 @@ At a minimum, you will need:
43
43
  * A C compiler (e.g. Xcode on OSX, gcc on everything else)
44
44
  * libffi development library - this is commonly in the libffi-dev or libffi-devel
45
45
 
46
+ On Linux systems running with [PaX](https://en.wikipedia.org/wiki/PaX) (Gentoo, Alpine, etc.) FFI may trigger `mprotect` errors. You may need to disable [mprotect](https://en.wikibooks.org/wiki/Grsecurity/Appendix/Grsecurity_and_PaX_Configuration_Options#Restrict_mprotect.28.29) for ruby (`paxctl -m [/path/to/ruby]`) for the time being until a solution is found.
47
+
46
48
  ## Installation
47
49
 
48
50
  From rubygems:
@@ -65,7 +65,28 @@
65
65
  #include "Thread.h"
66
66
  #include "LongDouble.h"
67
67
 
68
- #define ADJ(p, a) (++(p))
68
+ #ifdef USE_RAW
69
+ # ifndef __i386__
70
+ # error "RAW argument packing only supported on i386"
71
+ # endif
72
+
73
+ #define INT8_ADJ (4)
74
+ #define INT16_ADJ (4)
75
+ #define INT32_ADJ (4)
76
+ #define INT64_ADJ (8)
77
+ #define LONG_ADJ (sizeof(long))
78
+ #define FLOAT32_ADJ (4)
79
+ #define FLOAT64_ADJ (8)
80
+ #define ADDRESS_ADJ (sizeof(void *))
81
+ #define LONGDOUBLE_ADJ (ffi_type_longdouble.alignment)
82
+
83
+ #endif /* USE_RAW */
84
+
85
+ #ifdef USE_RAW
86
+ # define ADJ(p, a) ((p) = (FFIStorage*) (((char *) p) + a##_ADJ))
87
+ #else
88
+ # define ADJ(p, a) (++(p))
89
+ #endif
69
90
 
70
91
  static void* callback_param(VALUE proc, VALUE cbinfo);
71
92
  static inline void* getPointer(VALUE value, int type);
@@ -39,7 +39,16 @@
39
39
  extern "C" {
40
40
  #endif
41
41
 
42
+ #if defined(__i386__) && \
43
+ (defined(HAVE_RAW_API) || defined(USE_INTERNAL_LIBFFI)) && \
44
+ !defined(_WIN32) && !defined(__WIN32__)
45
+ # define USE_RAW
46
+ #endif
42
47
 
48
+ #if (defined(__i386__) || defined(__x86_64__)) && !(defined(_WIN32) || defined(__WIN32__))
49
+ # define BYPASS_FFI 1
50
+ #endif
51
+
43
52
  typedef union {
44
53
  #ifdef USE_RAW
45
54
  signed int s8, s16, s32;
@@ -0,0 +1,283 @@
1
+ /*
2
+ * Copyright (c) 2009, 2010 Wayne Meissner
3
+ * Copyright (c) 2008-2013, Ruby FFI project contributors
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions are met:
8
+ * * Redistributions of source code must retain the above copyright
9
+ * notice, this list of conditions and the following disclaimer.
10
+ * * Redistributions in binary form must reproduce the above copyright
11
+ * notice, this list of conditions and the following disclaimer in the
12
+ * documentation and/or other materials provided with the distribution.
13
+ * * Neither the name of the Ruby FFI project nor the
14
+ * names of its contributors may be used to endorse or promote products
15
+ * derived from this software without specific prior written permission.
16
+ *
17
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
21
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
29
+ #ifndef _MSC_VER
30
+ #include <sys/param.h>
31
+ #endif
32
+ #include <sys/types.h>
33
+ #if defined(__CYGWIN__) || !defined(_WIN32)
34
+ # include <sys/mman.h>
35
+ #endif
36
+ #include <stdio.h>
37
+ #ifndef _MSC_VER
38
+ # include <stdint.h>
39
+ # include <stdbool.h>
40
+ #else
41
+ # include "win32/stdbool.h"
42
+ # include "win32/stdint.h"
43
+ #endif
44
+ #if defined(__CYGWIN__) || !defined(_WIN32)
45
+ # include <unistd.h>
46
+ #else
47
+ # include <winsock2.h>
48
+ # define _WINSOCKAPI_
49
+ # include <windows.h>
50
+ #endif
51
+ #include <errno.h>
52
+ #include <ruby.h>
53
+
54
+ #if defined(_MSC_VER) && !defined(INT8_MIN)
55
+ # include "win32/stdint.h"
56
+ #endif
57
+ #include <ffi.h>
58
+ #include "rbffi.h"
59
+ #include "compat.h"
60
+
61
+ #include "Function.h"
62
+ #include "Types.h"
63
+ #include "Type.h"
64
+ #include "LastError.h"
65
+ #include "Call.h"
66
+
67
+ #include "ClosurePool.h"
68
+
69
+
70
+ #ifndef roundup
71
+ # define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
72
+ #endif
73
+ #ifdef _WIN32
74
+ typedef char* caddr_t;
75
+ #endif
76
+
77
+ typedef struct Memory {
78
+ void* code;
79
+ void* data;
80
+ struct Memory* next;
81
+ } Memory;
82
+
83
+ struct ClosurePool_ {
84
+ void* ctx;
85
+ int closureSize;
86
+ bool (*prep)(void* ctx, void *code, Closure* closure, char* errbuf, size_t errbufsize);
87
+ struct Memory* blocks; /* Keeps track of all the allocated memory for this pool */
88
+ Closure* list;
89
+ long refcnt;
90
+ };
91
+
92
+ static long pageSize;
93
+
94
+ static void* allocatePage(void);
95
+ static bool freePage(void *);
96
+ static bool protectPage(void *);
97
+
98
+ ClosurePool*
99
+ rbffi_ClosurePool_New(int closureSize,
100
+ bool (*prep)(void* ctx, void *code, Closure* closure, char* errbuf, size_t errbufsize),
101
+ void* ctx)
102
+ {
103
+ ClosurePool* pool;
104
+
105
+ pool = xcalloc(1, sizeof(*pool));
106
+ pool->closureSize = closureSize;
107
+ pool->ctx = ctx;
108
+ pool->prep = prep;
109
+ pool->refcnt = 1;
110
+
111
+ return pool;
112
+ }
113
+
114
+ void
115
+ cleanup_closure_pool(ClosurePool* pool)
116
+ {
117
+ Memory* memory;
118
+
119
+ for (memory = pool->blocks; memory != NULL; ) {
120
+ Memory* next = memory->next;
121
+ freePage(memory->code);
122
+ free(memory->data);
123
+ free(memory);
124
+ memory = next;
125
+ }
126
+ xfree(pool);
127
+ }
128
+
129
+ void
130
+ rbffi_ClosurePool_Free(ClosurePool* pool)
131
+ {
132
+ if (pool != NULL) {
133
+ long refcnt = --(pool->refcnt);
134
+ if (refcnt == 0) {
135
+ cleanup_closure_pool(pool);
136
+ }
137
+ }
138
+ }
139
+
140
+ Closure*
141
+ rbffi_Closure_Alloc(ClosurePool* pool)
142
+ {
143
+ Closure *list = NULL;
144
+ Memory* block = NULL;
145
+ caddr_t code = NULL;
146
+ char errmsg[256];
147
+ int nclosures;
148
+ long trampolineSize;
149
+ int i;
150
+
151
+ if (pool->list != NULL) {
152
+ Closure* closure = pool->list;
153
+ pool->list = pool->list->next;
154
+ pool->refcnt++;
155
+
156
+ return closure;
157
+ }
158
+
159
+ trampolineSize = roundup(pool->closureSize, 8);
160
+ nclosures = (int) (pageSize / trampolineSize);
161
+ block = calloc(1, sizeof(*block));
162
+ list = calloc(nclosures, sizeof(*list));
163
+ code = allocatePage();
164
+
165
+ if (block == NULL || list == NULL || code == NULL) {
166
+ snprintf(errmsg, sizeof(errmsg), "failed to allocate a page. errno=%d (%s)", errno, strerror(errno));
167
+ goto error;
168
+ }
169
+
170
+ for (i = 0; i < nclosures; ++i) {
171
+ Closure* closure = &list[i];
172
+ closure->next = &list[i + 1];
173
+ closure->pool = pool;
174
+ closure->code = (code + (i * trampolineSize));
175
+
176
+ if (!(*pool->prep)(pool->ctx, closure->code, closure, errmsg, sizeof(errmsg))) {
177
+ goto error;
178
+ }
179
+ }
180
+
181
+ if (!protectPage(code)) {
182
+ goto error;
183
+ }
184
+
185
+ /* Track the allocated page + Closure memory area */
186
+ block->data = list;
187
+ block->code = code;
188
+ block->next = pool->blocks;
189
+ pool->blocks = block;
190
+
191
+ /* Thread the new block onto the free list, apart from the first one. */
192
+ list[nclosures - 1].next = pool->list;
193
+ pool->list = list->next;
194
+ pool->refcnt++;
195
+
196
+ /* Use the first one as the new handle */
197
+ return list;
198
+
199
+ error:
200
+ free(block);
201
+ free(list);
202
+ if (code != NULL) {
203
+ freePage(code);
204
+ }
205
+
206
+
207
+ rb_raise(rb_eRuntimeError, "%s", errmsg);
208
+ return NULL;
209
+ }
210
+
211
+ void
212
+ rbffi_Closure_Free(Closure* closure)
213
+ {
214
+ if (closure != NULL) {
215
+ ClosurePool* pool = closure->pool;
216
+ long refcnt;
217
+ /* Just push it on the front of the free list */
218
+ closure->next = pool->list;
219
+ pool->list = closure;
220
+ refcnt = --(pool->refcnt);
221
+ if (refcnt == 0) {
222
+ cleanup_closure_pool(pool);
223
+ }
224
+ }
225
+ }
226
+
227
+ void*
228
+ rbffi_Closure_CodeAddress(Closure* handle)
229
+ {
230
+ return handle->code;
231
+ }
232
+
233
+
234
+ static long
235
+ getPageSize()
236
+ {
237
+ #if !defined(__CYGWIN__) && (defined(_WIN32) || defined(__WIN32__))
238
+ SYSTEM_INFO si;
239
+ GetSystemInfo(&si);
240
+ return si.dwPageSize;
241
+ #else
242
+ return sysconf(_SC_PAGESIZE);
243
+ #endif
244
+ }
245
+
246
+ static void*
247
+ allocatePage(void)
248
+ {
249
+ #if !defined(__CYGWIN__) && (defined(_WIN32) || defined(__WIN32__))
250
+ return VirtualAlloc(NULL, pageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
251
+ #else
252
+ caddr_t page = mmap(NULL, pageSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
253
+ return (page != (caddr_t) -1) ? page : NULL;
254
+ #endif
255
+ }
256
+
257
+ static bool
258
+ freePage(void *addr)
259
+ {
260
+ #if !defined(__CYGWIN__) && (defined(_WIN32) || defined(__WIN32__))
261
+ return VirtualFree(addr, 0, MEM_RELEASE);
262
+ #else
263
+ return munmap(addr, pageSize) == 0;
264
+ #endif
265
+ }
266
+
267
+ static bool
268
+ protectPage(void* page)
269
+ {
270
+ #if !defined(__CYGWIN__) && (defined(_WIN32) || defined(__WIN32__))
271
+ DWORD oldProtect;
272
+ return VirtualProtect(page, pageSize, PAGE_EXECUTE_READ, &oldProtect);
273
+ #else
274
+ return mprotect(page, pageSize, PROT_READ | PROT_EXEC) == 0;
275
+ #endif
276
+ }
277
+
278
+ void
279
+ rbffi_ClosurePool_Init(VALUE module)
280
+ {
281
+ pageSize = getPageSize();
282
+ }
283
+
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Copyright (c) 2009, 2010 Wayne Meissner
3
- * Copyright (c) 2008-2016, Ruby FFI project contributors
3
+ * Copyright (c) 2008-2013, Ruby FFI project contributors
4
4
  * All rights reserved.
5
5
  *
6
6
  * Redistribution and use in source and binary forms, with or without
@@ -26,22 +26,32 @@
26
26
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
- #ifndef RUBYFFI_CLOSURE_H
30
- #define RUBYFFI_CLOSURE_H
29
+ #ifndef RUBYFFI_CLOSUREPOOL_H
30
+ #define RUBYFFI_CLOSUREPOOL_H
31
31
 
32
- #include <ruby.h>
32
+ typedef struct ClosurePool_ ClosurePool;
33
+ typedef struct Closure_ Closure;
33
34
 
34
- typedef struct Closure_ {
35
- void* info; /* Data to pass when calling */
36
- void* function; /* Function to call */
37
- void* libffi_trampoline; /* Calls .function */
38
- void *libffi_closure; /* Allocates .libffi_trampoline */
39
- } Closure;
35
+ struct Closure_ {
36
+ void* info; /* opaque handle for storing closure-instance specific data */
37
+ void* function; /* closure-instance specific function, called by custom trampoline */
38
+ void* code; /* The native trampoline code location */
39
+ struct ClosurePool_* pool;
40
+ Closure* next;
41
+ };
40
42
 
41
- Closure* rbffi_Closure_Alloc();
43
+ void rbffi_ClosurePool_Init(VALUE module);
44
+
45
+ ClosurePool* rbffi_ClosurePool_New(int closureSize,
46
+ bool (*prep)(void* ctx, void *code, Closure* closure, char* errbuf, size_t errbufsize),
47
+ void* ctx);
48
+
49
+ void rbffi_ClosurePool_Free(ClosurePool *);
50
+
51
+ Closure* rbffi_Closure_Alloc(ClosurePool *);
42
52
  void rbffi_Closure_Free(Closure *);
43
53
 
44
- void rbffi_Closure_Init(VALUE ffiModule);
54
+ void* rbffi_Closure_GetCodeAddress(Closure *);
45
55
 
46
- #endif /* RUBYFFI_CLOSURE_H */
56
+ #endif /* RUBYFFI_CLOSUREPOOL_H */
47
57
 
@@ -67,7 +67,7 @@
67
67
  #include "Type.h"
68
68
  #include "LastError.h"
69
69
  #include "Call.h"
70
- #include "Closure.h"
70
+ #include "ClosurePool.h"
71
71
  #include "MappedType.h"
72
72
  #include "Thread.h"
73
73
  #include "LongDouble.h"
@@ -300,7 +300,6 @@ static VALUE
300
300
  function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc)
301
301
  {
302
302
  Function* fn = NULL;
303
- ffi_status ffiStatus;
304
303
 
305
304
  Data_Get_Struct(self, Function, fn);
306
305
 
@@ -315,6 +314,13 @@ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc)
315
314
  fn->base.rbParent = rbProc;
316
315
 
317
316
  } else if (rb_obj_is_kind_of(rbProc, rb_cProc) || rb_respond_to(rbProc, id_call)) {
317
+ if (fn->info->closurePool == NULL) {
318
+ fn->info->closurePool = rbffi_ClosurePool_New(sizeof(ffi_closure), callback_prep, fn->info);
319
+ if (fn->info->closurePool == NULL) {
320
+ rb_raise(rb_eNoMemError, "failed to create closure pool");
321
+ }
322
+ }
323
+
318
324
  #if defined(DEFER_ASYNC_CALLBACK)
319
325
  if (async_cb_thread == Qnil) {
320
326
  #if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) && defined(_WIN32)
@@ -329,22 +335,10 @@ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc)
329
335
 
330
336
  #endif
331
337
 
332
- fn->closure = rbffi_Closure_Alloc();
338
+ fn->closure = rbffi_Closure_Alloc(fn->info->closurePool);
333
339
  fn->closure->info = fn;
334
-
335
- ffiStatus = ffi_prep_closure_loc(fn->closure->libffi_closure,
336
- &fn->info->ffi_cif, /* callback signature */
337
- callback_invoke,
338
- fn->closure, /* user_data for callback_invoke */
339
- fn->closure->libffi_trampoline);
340
- if (ffiStatus != FFI_OK) {
341
- rb_raise(rb_eRuntimeError, "ffi_prep_closure_loc in function_init failed. status=%#x",
342
- ffiStatus);
343
- }
344
-
345
- fn->base.memory.address = fn->closure->libffi_trampoline;
340
+ fn->base.memory.address = fn->closure->code;
346
341
  fn->base.memory.size = sizeof(*fn->closure);
347
-
348
342
  fn->autorelease = true;
349
343
 
350
344
  } else {
@@ -948,6 +942,21 @@ save_callback_exception(void* data, VALUE exc)
948
942
  return Qnil;
949
943
  }
950
944
 
945
+ static bool
946
+ callback_prep(void* ctx, void* code, Closure* closure, char* errmsg, size_t errmsgsize)
947
+ {
948
+ FunctionType* fnInfo = (FunctionType *) ctx;
949
+ ffi_status ffiStatus;
950
+
951
+ ffiStatus = ffi_prep_closure(code, &fnInfo->ffi_cif, callback_invoke, closure);
952
+ if (ffiStatus != FFI_OK) {
953
+ snprintf(errmsg, errmsgsize, "ffi_prep_closure failed. status=%#x", ffiStatus);
954
+ return false;
955
+ }
956
+
957
+ return true;
958
+ }
959
+
951
960
  void
952
961
  rbffi_Function_Init(VALUE moduleFFI)
953
962
  {
@@ -46,7 +46,7 @@ typedef struct FunctionType_ FunctionType;
46
46
 
47
47
  #include "Type.h"
48
48
  #include "Call.h"
49
- #include "Closure.h"
49
+ #include "ClosurePool.h"
50
50
 
51
51
  struct FunctionType_ {
52
52
  Type type; /* The native type of a FunctionInfo object */
@@ -60,6 +60,7 @@ struct FunctionType_ {
60
60
  ffi_type** ffiParameterTypes;
61
61
  ffi_cif ffi_cif;
62
62
  Invoker invoke;
63
+ ClosurePool* closurePool;
63
64
  int parameterCount;
64
65
  int flags;
65
66
  ffi_abi abi;
@@ -72,6 +72,7 @@ fntype_allocate(VALUE klass)
72
72
  fnInfo->rbParameterTypes = Qnil;
73
73
  fnInfo->rbEnums = Qnil;
74
74
  fnInfo->invoke = rbffi_CallFunction;
75
+ fnInfo->closurePool = NULL;
75
76
 
76
77
  return obj;
77
78
  }
@@ -94,6 +95,9 @@ fntype_free(FunctionType* fnInfo)
94
95
  xfree(fnInfo->ffiParameterTypes);
95
96
  xfree(fnInfo->nativeParameterTypes);
96
97
  xfree(fnInfo->callbackParameters);
98
+ if (fnInfo->closurePool != NULL) {
99
+ rbffi_ClosurePool_Free(fnInfo->closurePool);
100
+ }
97
101
  xfree(fnInfo);
98
102
  }
99
103
 
@@ -26,6 +26,30 @@
26
26
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
+ #ifndef _MSC_VER
30
+ #include <sys/param.h>
31
+ #endif
32
+ #include <sys/types.h>
33
+ #ifndef _WIN32
34
+ # include <sys/mman.h>
35
+ #endif
36
+ #include <stdio.h>
37
+ #ifndef _MSC_VER
38
+ # include <stdint.h>
39
+ # include <stdbool.h>
40
+ #else
41
+ # include "win32/stdint.h"
42
+ # include "win32/stdbool.h"
43
+ #endif
44
+ #ifndef _WIN32
45
+ # include <unistd.h>
46
+ #endif
47
+ #include <errno.h>
48
+ #include <ruby.h>
49
+ #if defined(HAVE_NATIVETHREAD) && !defined(_WIN32) && !defined(__WIN32__)
50
+ # include <pthread.h>
51
+ #endif
52
+
29
53
  #include <ffi.h>
30
54
  #include "rbffi.h"
31
55
  #include "compat.h"
@@ -35,16 +59,79 @@
35
59
  #include "Type.h"
36
60
  #include "LastError.h"
37
61
  #include "Call.h"
38
- #include "Closure.h"
62
+ #include "ClosurePool.h"
39
63
  #include "MethodHandle.h"
40
64
 
41
- #define METHOD_CLOSURE ffi_closure
42
- #define METHOD_PARAMS void**
65
+
66
+ #define MAX_METHOD_FIXED_ARITY (6)
67
+
68
+ #ifndef roundup
69
+ # define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
70
+ #endif
71
+ #ifdef _WIN32
72
+ typedef char* caddr_t;
73
+ #endif
74
+
75
+ #ifdef USE_RAW
76
+ # define METHOD_CLOSURE ffi_raw_closure
77
+ # define METHOD_PARAMS ffi_raw*
78
+ #else
79
+ # define METHOD_CLOSURE ffi_closure
80
+ # define METHOD_PARAMS void**
81
+ #endif
82
+
83
+
84
+
85
+ static bool prep_trampoline(void* ctx, void* code, Closure* closure, char* errmsg, size_t errmsgsize);
86
+ static long trampoline_size(void);
87
+
88
+ #if defined(__x86_64__) && (defined(__linux__) || defined(__APPLE__))
89
+ # define CUSTOM_TRAMPOLINE 1
90
+ #endif
91
+
43
92
 
44
93
  struct MethodHandle {
45
94
  Closure* closure;
46
95
  };
47
96
 
97
+ static ClosurePool* defaultClosurePool;
98
+
99
+
100
+ MethodHandle*
101
+ rbffi_MethodHandle_Alloc(FunctionType* fnInfo, void* function)
102
+ {
103
+ MethodHandle* handle;
104
+ Closure* closure = rbffi_Closure_Alloc(defaultClosurePool);
105
+ if (closure == NULL) {
106
+ rb_raise(rb_eNoMemError, "failed to allocate closure from pool");
107
+ return NULL;
108
+ }
109
+
110
+ handle = xcalloc(1, sizeof(*handle));
111
+ handle->closure = closure;
112
+ closure->info = fnInfo;
113
+ closure->function = function;
114
+
115
+ return handle;
116
+ }
117
+
118
+ void
119
+ rbffi_MethodHandle_Free(MethodHandle* handle)
120
+ {
121
+ if (handle != NULL) {
122
+ rbffi_Closure_Free(handle->closure);
123
+ }
124
+ }
125
+
126
+ void*
127
+ rbffi_MethodHandle_CodeAddress(MethodHandle* handle)
128
+ {
129
+ return handle->closure->code;
130
+ }
131
+
132
+ #ifndef CUSTOM_TRAMPOLINE
133
+ static void attached_method_invoke(ffi_cif* cif, void* retval, METHOD_PARAMS parameters, void* user_data);
134
+
48
135
  static ffi_type* methodHandleParamTypes[] = {
49
136
  &ffi_type_sint,
50
137
  &ffi_type_pointer,
@@ -53,71 +140,219 @@ static ffi_type* methodHandleParamTypes[] = {
53
140
 
54
141
  static ffi_cif mh_cif;
55
142
 
143
+ static bool
144
+ prep_trampoline(void* ctx, void* code, Closure* closure, char* errmsg, size_t errmsgsize)
145
+ {
146
+ ffi_status ffiStatus;
147
+
148
+ #if defined(USE_RAW)
149
+ ffiStatus = ffi_prep_raw_closure(code, &mh_cif, attached_method_invoke, closure);
150
+ #else
151
+ ffiStatus = ffi_prep_closure(code, &mh_cif, attached_method_invoke, closure);
152
+ #endif
153
+ if (ffiStatus != FFI_OK) {
154
+ snprintf(errmsg, errmsgsize, "ffi_prep_closure failed. status=%#x", ffiStatus);
155
+ return false;
156
+ }
157
+
158
+ return true;
159
+ }
160
+
161
+
162
+ static long
163
+ trampoline_size(void)
164
+ {
165
+ return sizeof(METHOD_CLOSURE);
166
+ }
167
+
168
+ /*
169
+ * attached_method_invoke is used functions with more than 6 parameters, or
170
+ * with struct param or return values
171
+ */
56
172
  static void
57
173
  attached_method_invoke(ffi_cif* cif, void* mretval, METHOD_PARAMS parameters, void* user_data)
58
174
  {
59
175
  Closure* handle = (Closure *) user_data;
60
176
  FunctionType* fnInfo = (FunctionType *) handle->info;
61
177
 
178
+ #ifdef USE_RAW
179
+ int argc = parameters[0].sint;
180
+ VALUE* argv = *(VALUE **) &parameters[1];
181
+ #else
62
182
  int argc = *(int *) parameters[0];
63
183
  VALUE* argv = *(VALUE **) parameters[1];
184
+ #endif
64
185
 
65
186
  *(VALUE *) mretval = (*fnInfo->invoke)(argc, argv, handle->function, fnInfo);
66
187
  }
67
188
 
68
- MethodHandle*
69
- rbffi_MethodHandle_Alloc(FunctionType* fnInfo, void* function)
189
+ #endif
190
+
191
+
192
+
193
+ #if defined(CUSTOM_TRAMPOLINE)
194
+ #if defined(__x86_64__)
195
+
196
+ static VALUE custom_trampoline(int argc, VALUE* argv, VALUE self, Closure*);
197
+
198
+ #define TRAMPOLINE_CTX_MAGIC (0xfee1deadcafebabe)
199
+ #define TRAMPOLINE_FUN_MAGIC (0xfeedfacebeeff00d)
200
+
201
+ /*
202
+ * This is a hand-coded trampoline to speedup entry from ruby to the FFI translation
203
+ * layer for x86_64 arches.
204
+ *
205
+ * Since a ruby function has exactly 3 arguments, and the first 6 arguments are
206
+ * passed in registers for x86_64, we can tack on a context pointer by simply
207
+ * putting a value in %rcx, then jumping to the C trampoline code.
208
+ *
209
+ * This results in approx a 30% speedup for x86_64 FFI dispatch
210
+ */
211
+ __asm__(
212
+ ".text\n\t"
213
+ ".globl ffi_trampoline\n\t"
214
+ ".globl _ffi_trampoline\n\t"
215
+ "ffi_trampoline:\n\t"
216
+ "_ffi_trampoline:\n\t"
217
+ "movabsq $0xfee1deadcafebabe, %rcx\n\t"
218
+ "movabsq $0xfeedfacebeeff00d, %r11\n\t"
219
+ "jmpq *%r11\n\t"
220
+ ".globl ffi_trampoline_end\n\t"
221
+ "ffi_trampoline_end:\n\t"
222
+ ".globl _ffi_trampoline_end\n\t"
223
+ "_ffi_trampoline_end:\n\t"
224
+ );
225
+
226
+ static VALUE
227
+ custom_trampoline(int argc, VALUE* argv, VALUE self, Closure* handle)
70
228
  {
71
- ffi_status ffiStatus;
72
- MethodHandle* handle;
73
- Closure* closure;
229
+ FunctionType* fnInfo = (FunctionType *) handle->info;
230
+ VALUE rbReturnValue;
74
231
 
75
- closure = rbffi_Closure_Alloc();
76
- if (closure == NULL) {
77
- rb_raise(rb_eNoMemError, "failed to allocate closure from pool");
78
- return NULL;
232
+ RB_GC_GUARD(rbReturnValue) = (*fnInfo->invoke)(argc, argv, handle->function, fnInfo);
233
+ RB_GC_GUARD(self);
234
+
235
+ return rbReturnValue;
236
+ }
237
+
238
+ #elif defined(__i386__) && 0
239
+
240
+ static VALUE custom_trampoline(caddr_t args, Closure*);
241
+ #define TRAMPOLINE_CTX_MAGIC (0xfee1dead)
242
+ #define TRAMPOLINE_FUN_MAGIC (0xbeefcafe)
243
+
244
+ /*
245
+ * This is a hand-coded trampoline to speed-up entry from ruby to the FFI translation
246
+ * layer for i386 arches.
247
+ *
248
+ * This does not make a discernible difference vs a raw closure, so for now,
249
+ * it is not enabled.
250
+ */
251
+ __asm__(
252
+ ".text\n\t"
253
+ ".globl ffi_trampoline\n\t"
254
+ ".globl _ffi_trampoline\n\t"
255
+ "ffi_trampoline:\n\t"
256
+ "_ffi_trampoline:\n\t"
257
+ "subl $12, %esp\n\t"
258
+ "leal 16(%esp), %eax\n\t"
259
+ "movl %eax, (%esp)\n\t"
260
+ "movl $0xfee1dead, 4(%esp)\n\t"
261
+ "movl $0xbeefcafe, %eax\n\t"
262
+ "call *%eax\n\t"
263
+ "addl $12, %esp\n\t"
264
+ "ret\n\t"
265
+ ".globl ffi_trampoline_end\n\t"
266
+ "ffi_trampoline_end:\n\t"
267
+ ".globl _ffi_trampoline_end\n\t"
268
+ "_ffi_trampoline_end:\n\t"
269
+ );
270
+
271
+ static VALUE
272
+ custom_trampoline(caddr_t args, Closure* handle)
273
+ {
274
+ FunctionType* fnInfo = (FunctionType *) handle->info;
275
+ return (*fnInfo->invoke)(*(int *) args, *(VALUE **) (args + 4), handle->function, fnInfo);
276
+ }
277
+
278
+ #endif /* __x86_64__ else __i386__ */
279
+
280
+ extern void ffi_trampoline(int argc, VALUE* argv, VALUE self);
281
+ extern void ffi_trampoline_end(void);
282
+ static int trampoline_offsets(long *, long *);
283
+
284
+ static long trampoline_ctx_offset, trampoline_func_offset;
285
+
286
+ static long
287
+ trampoline_offset(int off, const long value)
288
+ {
289
+ caddr_t ptr;
290
+ for (ptr = (caddr_t) &ffi_trampoline + off; ptr < (caddr_t) &ffi_trampoline_end; ++ptr) {
291
+ if (*(long *) ptr == value) {
292
+ return ptr - (caddr_t) &ffi_trampoline;
293
+ }
79
294
  }
80
- closure->info = fnInfo;
81
- closure->function = function;
82
295
 
83
- ffiStatus = ffi_prep_closure_loc(closure->libffi_closure,
84
- &mh_cif, /* method signature */
85
- attached_method_invoke,
86
- closure, /* user_data for attached_method_invoke */
87
- closure->libffi_trampoline);
88
- if (ffiStatus != FFI_OK) {
89
- rb_raise(rb_eRuntimeError, "ffi_prep_closure_loc failed. status=%#x", ffiStatus);
90
- return false;
296
+ return -1;
297
+ }
298
+
299
+ static int
300
+ trampoline_offsets(long* ctxOffset, long* fnOffset)
301
+ {
302
+ *ctxOffset = trampoline_offset(0, TRAMPOLINE_CTX_MAGIC);
303
+ if (*ctxOffset == -1) {
304
+ return -1;
91
305
  }
92
306
 
93
- handle = xcalloc(1, sizeof(*handle));
94
- handle->closure = closure;
307
+ *fnOffset = trampoline_offset(0, TRAMPOLINE_FUN_MAGIC);
308
+ if (*fnOffset == -1) {
309
+ return -1;
310
+ }
95
311
 
96
- return handle;
312
+ return 0;
97
313
  }
98
314
 
99
- void
100
- rbffi_MethodHandle_Free(MethodHandle* handle)
315
+ static bool
316
+ prep_trampoline(void* ctx, void* code, Closure* closure, char* errmsg, size_t errmsgsize)
101
317
  {
102
- if (handle != NULL) {
103
- rbffi_Closure_Free(handle->closure);
104
- }
318
+ caddr_t ptr = (caddr_t) code;
319
+
320
+ memcpy(ptr, &ffi_trampoline, trampoline_size());
321
+ /* Patch the context and function addresses into the stub code */
322
+ *(intptr_t *)(ptr + trampoline_ctx_offset) = (intptr_t) closure;
323
+ *(intptr_t *)(ptr + trampoline_func_offset) = (intptr_t) custom_trampoline;
324
+
325
+ return true;
105
326
  }
106
327
 
107
- void*
108
- rbffi_MethodHandle_CodeAddress(MethodHandle* handle)
328
+ static long
329
+ trampoline_size(void)
109
330
  {
110
- return handle->closure->libffi_trampoline;
331
+ return (caddr_t) &ffi_trampoline_end - (caddr_t) &ffi_trampoline;
111
332
  }
112
333
 
334
+ #endif /* CUSTOM_TRAMPOLINE */
335
+
336
+
113
337
  void
114
338
  rbffi_MethodHandle_Init(VALUE module)
115
339
  {
340
+ #ifndef CUSTOM_TRAMPOLINE
116
341
  ffi_status ffiStatus;
342
+ #endif
117
343
 
344
+ defaultClosurePool = rbffi_ClosurePool_New((int) trampoline_size(), prep_trampoline, NULL);
345
+
346
+ #if defined(CUSTOM_TRAMPOLINE)
347
+ if (trampoline_offsets(&trampoline_ctx_offset, &trampoline_func_offset) != 0) {
348
+ rb_raise(rb_eFatal, "Could not locate offsets in trampoline code");
349
+ }
350
+ #else
118
351
  ffiStatus = ffi_prep_cif(&mh_cif, FFI_DEFAULT_ABI, 3, &ffi_type_ulong,
119
352
  methodHandleParamTypes);
120
353
  if (ffiStatus != FFI_OK) {
121
354
  rb_raise(rb_eFatal, "ffi_prep_cif failed. status=%#x", ffiStatus);
122
355
  }
356
+
357
+ #endif
123
358
  }
@@ -46,7 +46,7 @@
46
46
  #include "Types.h"
47
47
  #include "LastError.h"
48
48
  #include "Function.h"
49
- #include "Closure.h"
49
+ #include "ClosurePool.h"
50
50
  #include "MethodHandle.h"
51
51
  #include "Call.h"
52
52
  #include "ArrayType.h"
@@ -79,7 +79,7 @@ Init_ffi_c(void)
79
79
  rbffi_ArrayType_Init(moduleFFI);
80
80
  rbffi_LastError_Init(moduleFFI);
81
81
  rbffi_Call_Init(moduleFFI);
82
- rbffi_Closure_Init(moduleFFI);
82
+ rbffi_ClosurePool_Init(moduleFFI);
83
83
  rbffi_MethodHandle_Init(moduleFFI);
84
84
  rbffi_Platform_Init(moduleFFI);
85
85
  rbffi_AbstractMemory_Init(moduleFFI);
@@ -1,3 +1,3 @@
1
1
  module FFI
2
- VERSION = '1.9.24'
2
+ VERSION = '1.9.25'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ffi
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.24
4
+ version: 1.9.25
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wayne Meissner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-02 00:00:00.000000000 Z
11
+ date: 2018-06-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -106,8 +106,8 @@ files:
106
106
  - ext/ffi_c/Buffer.c
107
107
  - ext/ffi_c/Call.c
108
108
  - ext/ffi_c/Call.h
109
- - ext/ffi_c/Closure.c
110
- - ext/ffi_c/Closure.h
109
+ - ext/ffi_c/ClosurePool.c
110
+ - ext/ffi_c/ClosurePool.h
111
111
  - ext/ffi_c/DataConverter.c
112
112
  - ext/ffi_c/DynamicLibrary.c
113
113
  - ext/ffi_c/DynamicLibrary.h
@@ -1,54 +0,0 @@
1
- /*
2
- * Copyright (c) 2009, 2010 Wayne Meissner
3
- * Copyright (c) 2008-2016, Ruby FFI project contributors
4
- * All rights reserved.
5
- *
6
- * Redistribution and use in source and binary forms, with or without
7
- * modification, are permitted provided that the following conditions are met:
8
- * * Redistributions of source code must retain the above copyright
9
- * notice, this list of conditions and the following disclaimer.
10
- * * Redistributions in binary form must reproduce the above copyright
11
- * notice, this list of conditions and the following disclaimer in the
12
- * documentation and/or other materials provided with the distribution.
13
- * * Neither the name of the Ruby FFI project nor the
14
- * names of its contributors may be used to endorse or promote products
15
- * derived from this software without specific prior written permission.
16
- *
17
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
- * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
21
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
- */
28
-
29
- #include <ffi.h>
30
- #include "Closure.h"
31
- #include "rbffi.h"
32
-
33
- Closure* rbffi_Closure_Alloc(void)
34
- {
35
- Closure *self;
36
-
37
- self = xmalloc(sizeof(Closure));
38
- self->libffi_closure = ffi_closure_alloc(sizeof(ffi_closure), &self->libffi_trampoline);
39
- if (!self->libffi_closure) {
40
- return NULL;
41
- }
42
-
43
- return self;
44
- }
45
-
46
- void rbffi_Closure_Free(Closure *self)
47
- {
48
- ffi_closure_free(self->libffi_closure);
49
- free(self);
50
- }
51
-
52
- void rbffi_Closure_Init(VALUE moduleFFI)
53
- {
54
- }