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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +13 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/fiddle/closure.c +345 -0
- data/ext/fiddle/closure.h +8 -0
- data/ext/fiddle/conversions.c +141 -0
- data/ext/fiddle/conversions.h +44 -0
- data/ext/fiddle/extconf.rb +183 -0
- data/ext/fiddle/extlibs +2 -0
- data/ext/fiddle/fiddle.c +454 -0
- data/ext/fiddle/fiddle.h +138 -0
- data/ext/fiddle/function.c +315 -0
- data/ext/fiddle/function.h +8 -0
- data/ext/fiddle/handle.c +479 -0
- data/ext/fiddle/pointer.c +721 -0
- data/ext/fiddle/win32/fficonfig.h +29 -0
- data/ext/fiddle/win32/libffi-3.2.1-mswin.patch +191 -0
- data/ext/fiddle/win32/libffi-config.rb +48 -0
- data/ext/fiddle/win32/libffi.mk.tmpl +96 -0
- data/fiddle.gemspec +23 -0
- data/lib/fiddle.rb +56 -0
- data/lib/fiddle/closure.rb +49 -0
- data/lib/fiddle/cparser.rb +194 -0
- data/lib/fiddle/function.rb +18 -0
- data/lib/fiddle/import.rb +318 -0
- data/lib/fiddle/pack.rb +129 -0
- data/lib/fiddle/struct.rb +244 -0
- data/lib/fiddle/types.rb +72 -0
- data/lib/fiddle/value.rb +113 -0
- metadata +136 -0
data/ext/fiddle/fiddle.h
ADDED
@@ -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: */
|