fiddle 1.0.0.beta1

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.
@@ -0,0 +1,138 @@
1
+ #ifndef FIDDLE_H
2
+ #define FIDDLE_H
3
+
4
+ #include <ruby.h>
5
+ #include <errno.h>
6
+
7
+ #if defined(_WIN32)
8
+ #include <windows.h>
9
+ #endif
10
+
11
+ #ifdef HAVE_SYS_MMAN_H
12
+ #include <sys/mman.h>
13
+ #endif
14
+
15
+ #if defined(HAVE_DLFCN_H)
16
+ # include <dlfcn.h>
17
+ # /* some stranger systems may not define all of these */
18
+ #ifndef RTLD_LAZY
19
+ #define RTLD_LAZY 0
20
+ #endif
21
+ #ifndef RTLD_GLOBAL
22
+ #define RTLD_GLOBAL 0
23
+ #endif
24
+ #ifndef RTLD_NOW
25
+ #define RTLD_NOW 0
26
+ #endif
27
+ #else
28
+ # if defined(_WIN32)
29
+ # include <windows.h>
30
+ # define dlopen(name,flag) ((void*)LoadLibrary(name))
31
+ # define dlerror() strerror(rb_w32_map_errno(GetLastError()))
32
+ # define dlsym(handle,name) ((void*)GetProcAddress((handle),(name)))
33
+ # define RTLD_LAZY -1
34
+ # define RTLD_NOW -1
35
+ # define RTLD_GLOBAL -1
36
+ # endif
37
+ #endif
38
+
39
+ #ifdef USE_HEADER_HACKS
40
+ #include <ffi/ffi.h>
41
+ #else
42
+ #include <ffi.h>
43
+ #endif
44
+
45
+ #undef ffi_type_uchar
46
+ #undef ffi_type_schar
47
+ #undef ffi_type_ushort
48
+ #undef ffi_type_sshort
49
+ #undef ffi_type_uint
50
+ #undef ffi_type_sint
51
+ #undef ffi_type_ulong
52
+ #undef ffi_type_slong
53
+
54
+ #if CHAR_BIT == 8
55
+ # define ffi_type_uchar ffi_type_uint8
56
+ # define ffi_type_schar ffi_type_sint8
57
+ #else
58
+ # error "CHAR_BIT not supported"
59
+ #endif
60
+
61
+ # if SIZEOF_SHORT == 2
62
+ # define ffi_type_ushort ffi_type_uint16
63
+ # define ffi_type_sshort ffi_type_sint16
64
+ # elif SIZEOF_SHORT == 4
65
+ # define ffi_type_ushort ffi_type_uint32
66
+ # define ffi_type_sshort ffi_type_sint32
67
+ # else
68
+ # error "short size not supported"
69
+ # endif
70
+
71
+ # if SIZEOF_INT == 2
72
+ # define ffi_type_uint ffi_type_uint16
73
+ # define ffi_type_sint ffi_type_sint16
74
+ # elif SIZEOF_INT == 4
75
+ # define ffi_type_uint ffi_type_uint32
76
+ # define ffi_type_sint ffi_type_sint32
77
+ # elif SIZEOF_INT == 8
78
+ # define ffi_type_uint ffi_type_uint64
79
+ # define ffi_type_sint ffi_type_sint64
80
+ # else
81
+ # error "int size not supported"
82
+ # endif
83
+
84
+ # if SIZEOF_LONG == 4
85
+ # define ffi_type_ulong ffi_type_uint32
86
+ # define ffi_type_slong ffi_type_sint32
87
+ # elif SIZEOF_LONG == 8
88
+ # define ffi_type_ulong ffi_type_uint64
89
+ # define ffi_type_slong ffi_type_sint64
90
+ # else
91
+ # error "long size not supported"
92
+ # endif
93
+
94
+ #if HAVE_LONG_LONG
95
+ # if SIZEOF_LONG_LONG == 8
96
+ # define ffi_type_slong_long ffi_type_sint64
97
+ # define ffi_type_ulong_long ffi_type_uint64
98
+ # else
99
+ # error "long long size not supported"
100
+ # endif
101
+ #endif
102
+
103
+ #include <closure.h>
104
+ #include <conversions.h>
105
+ #include <function.h>
106
+
107
+ #define TYPE_VOID 0
108
+ #define TYPE_VOIDP 1
109
+ #define TYPE_CHAR 2
110
+ #define TYPE_SHORT 3
111
+ #define TYPE_INT 4
112
+ #define TYPE_LONG 5
113
+ #if HAVE_LONG_LONG
114
+ #define TYPE_LONG_LONG 6
115
+ #endif
116
+ #define TYPE_FLOAT 7
117
+ #define TYPE_DOUBLE 8
118
+
119
+ #define ALIGN_OF(type) offsetof(struct {char align_c; type align_x;}, align_x)
120
+
121
+ #define ALIGN_VOIDP ALIGN_OF(void*)
122
+ #define ALIGN_SHORT ALIGN_OF(short)
123
+ #define ALIGN_CHAR ALIGN_OF(char)
124
+ #define ALIGN_INT ALIGN_OF(int)
125
+ #define ALIGN_LONG ALIGN_OF(long)
126
+ #if HAVE_LONG_LONG
127
+ #define ALIGN_LONG_LONG ALIGN_OF(LONG_LONG)
128
+ #endif
129
+ #define ALIGN_FLOAT ALIGN_OF(float)
130
+ #define ALIGN_DOUBLE ALIGN_OF(double)
131
+
132
+ extern VALUE mFiddle;
133
+ extern VALUE rb_eFiddleError;
134
+
135
+ VALUE rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type);
136
+
137
+ #endif
138
+ /* vim: set noet sws=4 sw=4: */
@@ -0,0 +1,315 @@
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
+
13
+ VALUE cFiddleFunction;
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
+
53
+ const rb_data_type_t function_data_type = {
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,
85
+ RB_OBJ_STRING(key));
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
+
148
+ struct nogvl_ffi_call_args {
149
+ ffi_cif *cif;
150
+ void (*fn)(void);
151
+ void **values;
152
+ fiddle_generic retval;
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
232
+ Init_fiddle_function(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
+ */
268
+ cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject);
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
+
288
+ rb_define_alloc_func(cFiddleFunction, allocate);
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: */