ffi 1.9.24 → 1.9.25

Sign up to get free protection for your applications and to get access to all the features.
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
- }