Ruby  2.5.0dev(2017-10-22revision60238)
function.c
Go to the documentation of this file.
1 #include <fiddle.h>
2 #include <ruby/thread.h>
3 
4 #ifdef PRIsVALUE
5 # define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj)
6 # define RB_OBJ_STRING(obj) (obj)
7 #else
8 # define PRIsVALUE "s"
9 # define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj)
10 # define RB_OBJ_STRING(obj) StringValueCStr(obj)
11 #endif
12 
14 
15 #define MAX_ARGS (SIZE_MAX / (sizeof(void *) + sizeof(fiddle_generic)) - 1)
16 
17 #define Check_Max_Args(name, len) \
18  Check_Max_Args_(name, len, "")
19 #define Check_Max_Args_Long(name, len) \
20  Check_Max_Args_(name, len, "l")
21 #define Check_Max_Args_(name, len, fmt) \
22  if ((size_t)(len) < MAX_ARGS) { \
23  /* OK */ \
24  } \
25  else { \
26  rb_raise(rb_eTypeError, \
27  name" is so large that it can cause integer overflow (%"fmt"d)", \
28  (len)); \
29  }
30 
31 static void
32 deallocate(void *p)
33 {
34  ffi_cif *ptr = p;
35  if (ptr->arg_types) xfree(ptr->arg_types);
36  xfree(ptr);
37 }
38 
39 static size_t
40 function_memsize(const void *p)
41 {
42  /* const */ffi_cif *ptr = (ffi_cif *)p;
43  size_t size = 0;
44 
45  size += sizeof(*ptr);
46 #if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
47  size += ffi_raw_size(ptr);
48 #endif
49 
50  return size;
51 }
52 
54  "fiddle/function",
55  {0, deallocate, function_memsize,},
56 };
57 
58 static VALUE
59 allocate(VALUE klass)
60 {
61  ffi_cif * cif;
62 
63  return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif);
64 }
65 
66 VALUE
67 rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type)
68 {
69  VALUE argv[3];
70 
71  argv[0] = address;
72  argv[1] = arg_types;
73  argv[2] = ret_type;
74 
75  return rb_class_new_instance(3, argv, cFiddleFunction);
76 }
77 
78 static int
79 parse_keyword_arg_i(VALUE key, VALUE value, VALUE self)
80 {
81  if (key == ID2SYM(rb_intern("name"))) {
82  rb_iv_set(self, "@name", value);
83  } else {
84  rb_raise(rb_eArgError, "unknown keyword: %"PRIsVALUE,
86  }
87  return ST_CONTINUE;
88 }
89 
90 static VALUE
91 initialize(int argc, VALUE argv[], VALUE self)
92 {
93  ffi_cif * cif;
94  ffi_type **arg_types, *rtype;
95  ffi_status result;
96  VALUE ptr, args, ret_type, abi, kwds, ary;
97  int i, len;
98  int nabi;
99  void *cfunc;
100 
101  rb_scan_args(argc, argv, "31:", &ptr, &args, &ret_type, &abi, &kwds);
102  ptr = rb_Integer(ptr);
103  cfunc = NUM2PTR(ptr);
104  PTR2NUM(cfunc);
105  nabi = NIL_P(abi) ? FFI_DEFAULT_ABI : NUM2INT(abi);
106  abi = INT2FIX(nabi);
107  i = NUM2INT(ret_type);
108  rtype = INT2FFI_TYPE(i);
109  ret_type = INT2FIX(i);
110 
111  Check_Type(args, T_ARRAY);
112  len = RARRAY_LENINT(args);
113  Check_Max_Args("args", len);
114  ary = rb_ary_subseq(args, 0, len);
115  for (i = 0; i < RARRAY_LEN(args); i++) {
116  VALUE a = RARRAY_PTR(args)[i];
117  int type = NUM2INT(a);
118  (void)INT2FFI_TYPE(type); /* raise */
119  if (INT2FIX(type) != a) rb_ary_store(ary, i, INT2FIX(type));
120  }
121  OBJ_FREEZE(ary);
122 
123  rb_iv_set(self, "@ptr", ptr);
124  rb_iv_set(self, "@args", args);
125  rb_iv_set(self, "@return_type", ret_type);
126  rb_iv_set(self, "@abi", abi);
127 
128  if (!NIL_P(kwds)) rb_hash_foreach(kwds, parse_keyword_arg_i, self);
129 
130  TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
131 
132  arg_types = xcalloc(len + 1, sizeof(ffi_type *));
133 
134  for (i = 0; i < RARRAY_LEN(args); i++) {
135  int type = NUM2INT(RARRAY_AREF(args, i));
136  arg_types[i] = INT2FFI_TYPE(type);
137  }
138  arg_types[len] = NULL;
139 
140  result = ffi_prep_cif(cif, nabi, len, rtype, arg_types);
141 
142  if (result)
143  rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
144 
145  return self;
146 }
147 
149  ffi_cif *cif;
150  void (*fn)(void);
151  void **values;
153 };
154 
155 static void *
156 nogvl_ffi_call(void *ptr)
157 {
158  struct nogvl_ffi_call_args *args = ptr;
159 
160  ffi_call(args->cif, args->fn, &args->retval, args->values);
161 
162  return NULL;
163 }
164 
165 static VALUE
166 function_call(int argc, VALUE argv[], VALUE self)
167 {
168  struct nogvl_ffi_call_args args = { 0 };
169  fiddle_generic *generic_args;
170  VALUE cfunc, types, cPointer;
171  int i;
172  VALUE alloc_buffer = 0;
173 
174  cfunc = rb_iv_get(self, "@ptr");
175  types = rb_iv_get(self, "@args");
176  cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
177 
178  Check_Max_Args("number of arguments", argc);
179  if (argc != (i = RARRAY_LENINT(types))) {
180  rb_error_arity(argc, i, i);
181  }
182 
183  TypedData_Get_Struct(self, ffi_cif, &function_data_type, args.cif);
184 
185  if (rb_safe_level() >= 1) {
186  for (i = 0; i < argc; i++) {
187  VALUE src = argv[i];
188  if (OBJ_TAINTED(src)) {
189  rb_raise(rb_eSecurityError, "tainted parameter not allowed");
190  }
191  }
192  }
193 
194  generic_args = ALLOCV(alloc_buffer,
195  (size_t)(argc + 1) * sizeof(void *) + (size_t)argc * sizeof(fiddle_generic));
196  args.values = (void **)((char *)generic_args +
197  (size_t)argc * sizeof(fiddle_generic));
198 
199  for (i = 0; i < argc; i++) {
200  VALUE type = RARRAY_AREF(types, i);
201  VALUE src = argv[i];
202  int argtype = FIX2INT(type);
203 
204  if (argtype == TYPE_VOIDP) {
205  if(NIL_P(src)) {
206  src = INT2FIX(0);
207  } else if(cPointer != CLASS_OF(src)) {
208  src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
209  }
210  src = rb_Integer(src);
211  }
212 
213  VALUE2GENERIC(argtype, src, &generic_args[i]);
214  args.values[i] = (void *)&generic_args[i];
215  }
216  args.values[argc] = NULL;
217  args.fn = NUM2PTR(cfunc);
218 
219  (void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0);
220 
221  rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
222 #if defined(_WIN32)
223  rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno));
224 #endif
225 
226  ALLOCV_END(alloc_buffer);
227 
228  return GENERIC2VALUE(rb_iv_get(self, "@return_type"), args.retval);
229 }
230 
231 void
233 {
234  /*
235  * Document-class: Fiddle::Function
236  *
237  * == Description
238  *
239  * A representation of a C function
240  *
241  * == Examples
242  *
243  * === 'strcpy'
244  *
245  * @libc = Fiddle.dlopen "/lib/libc.so.6"
246  * #=> #<Fiddle::Handle:0x00000001d7a8d8>
247  * f = Fiddle::Function.new(
248  * @libc['strcpy'],
249  * [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP],
250  * Fiddle::TYPE_VOIDP)
251  * #=> #<Fiddle::Function:0x00000001d8ee00>
252  * buff = "000"
253  * #=> "000"
254  * str = f.call(buff, "123")
255  * #=> #<Fiddle::Pointer:0x00000001d0c380 ptr=0x000000018a21b8 size=0 free=0x00000000000000>
256  * str.to_s
257  * => "123"
258  *
259  * === ABI check
260  *
261  * @libc = Fiddle.dlopen "/lib/libc.so.6"
262  * #=> #<Fiddle::Handle:0x00000001d7a8d8>
263  * f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
264  * #=> #<Fiddle::Function:0x00000001d8ee00>
265  * f.abi == Fiddle::Function::DEFAULT
266  * #=> true
267  */
269 
270  /*
271  * Document-const: DEFAULT
272  *
273  * Default ABI
274  *
275  */
276  rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI));
277 
278 #ifdef HAVE_CONST_FFI_STDCALL
279  /*
280  * Document-const: STDCALL
281  *
282  * FFI implementation of WIN32 stdcall convention
283  *
284  */
285  rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL));
286 #endif
287 
289 
290  /*
291  * Document-method: call
292  *
293  * Calls the constructed Function, with +args+.
294  * Caller must ensure the underlying function is called in a
295  * thread-safe manner if running in a multi-threaded process.
296  *
297  * For an example see Fiddle::Function
298  *
299  */
300  rb_define_method(cFiddleFunction, "call", function_call, -1);
301 
302  /*
303  * Document-method: new
304  * call-seq: new(ptr, args, ret_type, abi = DEFAULT)
305  *
306  * Constructs a Function object.
307  * * +ptr+ is a referenced function, of a Fiddle::Handle
308  * * +args+ is an Array of arguments, passed to the +ptr+ function
309  * * +ret_type+ is the return type of the function
310  * * +abi+ is the ABI of the function
311  *
312  */
313  rb_define_method(cFiddleFunction, "initialize", initialize, -1);
314 }
315 /* vim: set noet sws=4 sw=4: */
#define VALUE2GENERIC(_type, _src, _dst)
Definition: conversions.h:31
#define TYPE_VOIDP
Definition: fiddle.h:108
#define RARRAY_LEN(a)
Definition: ruby.h:1019
VALUE mFiddle
Definition: fiddle.c:3
#define INT2NUM(x)
Definition: ruby.h:1538
#define NUM2INT(x)
Definition: ruby.h:684
#define CLASS_OF(v)
Definition: ruby.h:453
VALUE rb_ary_subseq(VALUE ary, long beg, long len)
Definition: array.c:1231
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2284
Definition: st.h:99
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: ruby.h:1183
#define OBJ_FREEZE(x)
Definition: ruby.h:1306
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
Definition: vm_eval.c:774
VALUE rb_iv_set(VALUE, const char *, VALUE)
Definition: variable.c:3095
VALUE rb_iv_get(VALUE, const char *)
Definition: variable.c:3087
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:693
ffi_cif * cif
Definition: closure.c:61
#define Check_Type(v, t)
Definition: ruby.h:562
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
VALUE rb_Integer(VALUE)
Equivalent to Kernel#Integer in Ruby.
Definition: object.c:3148
#define T_ARRAY
Definition: ruby.h:498
VALUE rb_eArgError
Definition: error.c:802
void rb_hash_foreach(VALUE hash, int(*func)(ANYARGS), VALUE farg)
Definition: hash.c:385
RUBY_EXTERN VALUE rb_cObject
Definition: ruby.h:1893
#define RB_OBJ_STRING(obj)
Definition: function.c:6
#define NIL_P(v)
Definition: ruby.h:451
void rb_define_const(VALUE, const char *, VALUE)
Definition: variable.c:2691
void rb_ary_store(VALUE ary, long idx, VALUE val)
Definition: array.c:815
VALUE cFiddleFunction
Definition: function.c:13
int argc
Definition: ruby.c:187
#define INT2FFI_TYPE(_type)
Definition: conversions.h:32
#define ALLOCV_END(v)
Definition: ruby.h:1658
void ** args
Definition: closure.c:63
void * rb_thread_call_without_gvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
VALUE rb_const_get(VALUE, ID)
Definition: variable.c:2292
int errno
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:1908
#define PRIsVALUE
Definition: ruby.h:135
#define Check_Max_Args(name, len)
Definition: function.c:17
unsigned long VALUE
Definition: ruby.h:85
#define OBJ_TAINTED(x)
Definition: ruby.h:1296
VALUE rb_eSecurityError
Definition: error.c:810
#define FIX2INT(x)
Definition: ruby.h:686
#define PTR2NUM(x)
Definition: conversions.h:36
#define RARRAY_LENINT(ary)
Definition: ruby.h:1020
#define NUM2PTR(x)
Definition: conversions.h:37
register unsigned int len
Definition: zonetab.h:51
VALUE rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type)
Definition: function.c:67
int size
Definition: encoding.c:57
#define INT2FIX(i)
Definition: ruby.h:232
int rb_safe_level(void)
Definition: safe.c:35
#define RARRAY_AREF(a, i)
Definition: ruby.h:1033
VALUE rb_eRuntimeError
Definition: error.c:800
void rb_error_arity(int argc, int min, int max)
#define RARRAY_PTR(a)
Definition: ruby.h:1041
#define ALLOCV(v, n)
Definition: ruby.h:1656
VALUE rb_class_new_instance(int, const VALUE *, VALUE)
Allocates and initializes an instance of klass.
Definition: object.c:2170
const rb_data_type_t function_data_type
Definition: function.c:53
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: ruby.h:1175
fiddle_generic retval
Definition: function.c:152
#define GENERIC2VALUE(_type, _retval)
Definition: conversions.h:33
#define ID2SYM(x)
Definition: ruby.h:383
void void xfree(void *)
void Init_fiddle_function(void)
Definition: function.c:232
#define rb_intern(str)
#define NULL
Definition: _sdbm.c:102
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1515
void(* fn)(void)
Definition: function.c:150
char ** argv
Definition: ruby.c:188
#define xcalloc
Definition: defines.h:185