fiddle 1.0.0.beta1

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