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,8 @@
1
+ #ifndef FIDDLE_FUNCTION_H
2
+ #define FIDDLE_FUNCTION_H
3
+
4
+ #include <fiddle.h>
5
+
6
+ void Init_fiddle_function(void);
7
+
8
+ #endif
@@ -0,0 +1,479 @@
1
+ #include <ruby.h>
2
+ #include <fiddle.h>
3
+
4
+ #define SafeStringValueCStr(v) (rb_check_safe_obj(rb_string_value(&v)), StringValueCStr(v))
5
+
6
+ VALUE rb_cHandle;
7
+
8
+ struct dl_handle {
9
+ void *ptr;
10
+ int open;
11
+ int enable_close;
12
+ };
13
+
14
+ #ifdef _WIN32
15
+ # ifndef _WIN32_WCE
16
+ static void *
17
+ w32_coredll(void)
18
+ {
19
+ MEMORY_BASIC_INFORMATION m;
20
+ memset(&m, 0, sizeof(m));
21
+ if( !VirtualQuery(_errno, &m, sizeof(m)) ) return NULL;
22
+ return m.AllocationBase;
23
+ }
24
+ # endif
25
+
26
+ static int
27
+ w32_dlclose(void *ptr)
28
+ {
29
+ # ifndef _WIN32_WCE
30
+ if( ptr == w32_coredll() ) return 0;
31
+ # endif
32
+ if( FreeLibrary((HMODULE)ptr) ) return 0;
33
+ return errno = rb_w32_map_errno(GetLastError());
34
+ }
35
+ #define dlclose(ptr) w32_dlclose(ptr)
36
+ #endif
37
+
38
+ static void
39
+ fiddle_handle_free(void *ptr)
40
+ {
41
+ struct dl_handle *fiddle_handle = ptr;
42
+ if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){
43
+ dlclose(fiddle_handle->ptr);
44
+ }
45
+ xfree(ptr);
46
+ }
47
+
48
+ static size_t
49
+ fiddle_handle_memsize(const void *ptr)
50
+ {
51
+ return sizeof(struct dl_handle);
52
+ }
53
+
54
+ static const rb_data_type_t fiddle_handle_data_type = {
55
+ "fiddle/handle",
56
+ {0, fiddle_handle_free, fiddle_handle_memsize,},
57
+ };
58
+
59
+ /*
60
+ * call-seq: close
61
+ *
62
+ * Close this handle.
63
+ *
64
+ * Calling close more than once will raise a Fiddle::DLError exception.
65
+ */
66
+ static VALUE
67
+ rb_fiddle_handle_close(VALUE self)
68
+ {
69
+ struct dl_handle *fiddle_handle;
70
+
71
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
72
+ if(fiddle_handle->open) {
73
+ int ret = dlclose(fiddle_handle->ptr);
74
+ fiddle_handle->open = 0;
75
+
76
+ /* Check dlclose for successful return value */
77
+ if(ret) {
78
+ #if defined(HAVE_DLERROR)
79
+ rb_raise(rb_eFiddleError, "%s", dlerror());
80
+ #else
81
+ rb_raise(rb_eFiddleError, "could not close handle");
82
+ #endif
83
+ }
84
+ return INT2NUM(ret);
85
+ }
86
+ rb_raise(rb_eFiddleError, "dlclose() called too many times");
87
+
88
+ UNREACHABLE;
89
+ }
90
+
91
+ static VALUE
92
+ rb_fiddle_handle_s_allocate(VALUE klass)
93
+ {
94
+ VALUE obj;
95
+ struct dl_handle *fiddle_handle;
96
+
97
+ obj = TypedData_Make_Struct(rb_cHandle, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
98
+ fiddle_handle->ptr = 0;
99
+ fiddle_handle->open = 0;
100
+ fiddle_handle->enable_close = 0;
101
+
102
+ return obj;
103
+ }
104
+
105
+ static VALUE
106
+ predefined_fiddle_handle(void *handle)
107
+ {
108
+ VALUE obj = rb_fiddle_handle_s_allocate(rb_cHandle);
109
+ struct dl_handle *fiddle_handle = DATA_PTR(obj);
110
+
111
+ fiddle_handle->ptr = handle;
112
+ fiddle_handle->open = 1;
113
+ OBJ_FREEZE(obj);
114
+ return obj;
115
+ }
116
+
117
+ /*
118
+ * call-seq:
119
+ * new(library = nil, flags = Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
120
+ *
121
+ * Create a new handler that opens +library+ with +flags+.
122
+ *
123
+ * If no +library+ is specified or +nil+ is given, DEFAULT is used, which is
124
+ * the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more.
125
+ *
126
+ * lib = Fiddle::Handle.new
127
+ *
128
+ * The default is dependent on OS, and provide a handle for all libraries
129
+ * already loaded. For example, in most cases you can use this to access +libc+
130
+ * functions, or ruby functions like +rb_str_new+.
131
+ */
132
+ static VALUE
133
+ rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self)
134
+ {
135
+ void *ptr;
136
+ struct dl_handle *fiddle_handle;
137
+ VALUE lib, flag;
138
+ char *clib;
139
+ int cflag;
140
+ const char *err;
141
+
142
+ switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){
143
+ case 0:
144
+ clib = NULL;
145
+ cflag = RTLD_LAZY | RTLD_GLOBAL;
146
+ break;
147
+ case 1:
148
+ clib = NIL_P(lib) ? NULL : SafeStringValueCStr(lib);
149
+ cflag = RTLD_LAZY | RTLD_GLOBAL;
150
+ break;
151
+ case 2:
152
+ clib = NIL_P(lib) ? NULL : SafeStringValueCStr(lib);
153
+ cflag = NUM2INT(flag);
154
+ break;
155
+ default:
156
+ rb_bug("rb_fiddle_handle_new");
157
+ }
158
+
159
+ #if defined(_WIN32)
160
+ if( !clib ){
161
+ HANDLE rb_libruby_handle(void);
162
+ ptr = rb_libruby_handle();
163
+ }
164
+ else if( STRCASECMP(clib, "libc") == 0
165
+ # ifdef RUBY_COREDLL
166
+ || STRCASECMP(clib, RUBY_COREDLL) == 0
167
+ || STRCASECMP(clib, RUBY_COREDLL".dll") == 0
168
+ # endif
169
+ ){
170
+ # ifdef _WIN32_WCE
171
+ ptr = dlopen("coredll.dll", cflag);
172
+ # else
173
+ (void)cflag;
174
+ ptr = w32_coredll();
175
+ # endif
176
+ }
177
+ else
178
+ #endif
179
+ ptr = dlopen(clib, cflag);
180
+ #if defined(HAVE_DLERROR)
181
+ if( !ptr && (err = dlerror()) ){
182
+ rb_raise(rb_eFiddleError, "%s", err);
183
+ }
184
+ #else
185
+ if( !ptr ){
186
+ err = dlerror();
187
+ rb_raise(rb_eFiddleError, "%s", err);
188
+ }
189
+ #endif
190
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
191
+ if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){
192
+ dlclose(fiddle_handle->ptr);
193
+ }
194
+ fiddle_handle->ptr = ptr;
195
+ fiddle_handle->open = 1;
196
+ fiddle_handle->enable_close = 0;
197
+
198
+ if( rb_block_given_p() ){
199
+ rb_ensure(rb_yield, self, rb_fiddle_handle_close, self);
200
+ }
201
+
202
+ return Qnil;
203
+ }
204
+
205
+ /*
206
+ * call-seq: enable_close
207
+ *
208
+ * Enable a call to dlclose() when this handle is garbage collected.
209
+ */
210
+ static VALUE
211
+ rb_fiddle_handle_enable_close(VALUE self)
212
+ {
213
+ struct dl_handle *fiddle_handle;
214
+
215
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
216
+ fiddle_handle->enable_close = 1;
217
+ return Qnil;
218
+ }
219
+
220
+ /*
221
+ * call-seq: disable_close
222
+ *
223
+ * Disable a call to dlclose() when this handle is garbage collected.
224
+ */
225
+ static VALUE
226
+ rb_fiddle_handle_disable_close(VALUE self)
227
+ {
228
+ struct dl_handle *fiddle_handle;
229
+
230
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
231
+ fiddle_handle->enable_close = 0;
232
+ return Qnil;
233
+ }
234
+
235
+ /*
236
+ * call-seq: close_enabled?
237
+ *
238
+ * Returns +true+ if dlclose() will be called when this handle is garbage collected.
239
+ *
240
+ * See man(3) dlclose() for more info.
241
+ */
242
+ static VALUE
243
+ rb_fiddle_handle_close_enabled_p(VALUE self)
244
+ {
245
+ struct dl_handle *fiddle_handle;
246
+
247
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
248
+
249
+ if(fiddle_handle->enable_close) return Qtrue;
250
+ return Qfalse;
251
+ }
252
+
253
+ /*
254
+ * call-seq: to_i
255
+ *
256
+ * Returns the memory address for this handle.
257
+ */
258
+ static VALUE
259
+ rb_fiddle_handle_to_i(VALUE self)
260
+ {
261
+ struct dl_handle *fiddle_handle;
262
+
263
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
264
+ return PTR2NUM(fiddle_handle);
265
+ }
266
+
267
+ static VALUE fiddle_handle_sym(void *handle, VALUE symbol);
268
+
269
+ /*
270
+ * Document-method: sym
271
+ *
272
+ * call-seq: sym(name)
273
+ *
274
+ * Get the address as an Integer for the function named +name+.
275
+ */
276
+ static VALUE
277
+ rb_fiddle_handle_sym(VALUE self, VALUE sym)
278
+ {
279
+ struct dl_handle *fiddle_handle;
280
+
281
+ TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
282
+ if( ! fiddle_handle->open ){
283
+ rb_raise(rb_eFiddleError, "closed handle");
284
+ }
285
+
286
+ return fiddle_handle_sym(fiddle_handle->ptr, sym);
287
+ }
288
+
289
+ #ifndef RTLD_NEXT
290
+ #define RTLD_NEXT NULL
291
+ #endif
292
+ #ifndef RTLD_DEFAULT
293
+ #define RTLD_DEFAULT NULL
294
+ #endif
295
+
296
+ /*
297
+ * Document-method: sym
298
+ *
299
+ * call-seq: sym(name)
300
+ *
301
+ * Get the address as an Integer for the function named +name+. The function
302
+ * is searched via dlsym on RTLD_NEXT.
303
+ *
304
+ * See man(3) dlsym() for more info.
305
+ */
306
+ static VALUE
307
+ rb_fiddle_handle_s_sym(VALUE self, VALUE sym)
308
+ {
309
+ return fiddle_handle_sym(RTLD_NEXT, sym);
310
+ }
311
+
312
+ static VALUE
313
+ fiddle_handle_sym(void *handle, VALUE symbol)
314
+ {
315
+ #if defined(HAVE_DLERROR)
316
+ const char *err;
317
+ # define CHECK_DLERROR if ((err = dlerror()) != 0) { func = 0; }
318
+ #else
319
+ # define CHECK_DLERROR
320
+ #endif
321
+ void (*func)();
322
+ const char *name = SafeStringValueCStr(symbol);
323
+
324
+ #ifdef HAVE_DLERROR
325
+ dlerror();
326
+ #endif
327
+ func = (void (*)())(VALUE)dlsym(handle, name);
328
+ CHECK_DLERROR;
329
+ #if defined(FUNC_STDCALL)
330
+ if( !func ){
331
+ int i;
332
+ int len = (int)strlen(name);
333
+ char *name_n;
334
+ #if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__)
335
+ {
336
+ char *name_a = (char*)xmalloc(len+2);
337
+ strcpy(name_a, name);
338
+ name_n = name_a;
339
+ name_a[len] = 'A';
340
+ name_a[len+1] = '\0';
341
+ func = dlsym(handle, name_a);
342
+ CHECK_DLERROR;
343
+ if( func ) goto found;
344
+ name_n = xrealloc(name_a, len+6);
345
+ }
346
+ #else
347
+ name_n = (char*)xmalloc(len+6);
348
+ #endif
349
+ memcpy(name_n, name, len);
350
+ name_n[len++] = '@';
351
+ for( i = 0; i < 256; i += 4 ){
352
+ sprintf(name_n + len, "%d", i);
353
+ func = dlsym(handle, name_n);
354
+ CHECK_DLERROR;
355
+ if( func ) break;
356
+ }
357
+ if( func ) goto found;
358
+ name_n[len-1] = 'A';
359
+ name_n[len++] = '@';
360
+ for( i = 0; i < 256; i += 4 ){
361
+ sprintf(name_n + len, "%d", i);
362
+ func = dlsym(handle, name_n);
363
+ CHECK_DLERROR;
364
+ if( func ) break;
365
+ }
366
+ found:
367
+ xfree(name_n);
368
+ }
369
+ #endif
370
+ if( !func ){
371
+ rb_raise(rb_eFiddleError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
372
+ }
373
+
374
+ return PTR2NUM(func);
375
+ }
376
+
377
+ void
378
+ Init_fiddle_handle(void)
379
+ {
380
+ /*
381
+ * Document-class: Fiddle::Handle
382
+ *
383
+ * The Fiddle::Handle is the manner to access the dynamic library
384
+ *
385
+ * == Example
386
+ *
387
+ * === Setup
388
+ *
389
+ * libc_so = "/lib64/libc.so.6"
390
+ * => "/lib64/libc.so.6"
391
+ * @handle = Fiddle::Handle.new(libc_so)
392
+ * => #<Fiddle::Handle:0x00000000d69ef8>
393
+ *
394
+ * === Setup, with flags
395
+ *
396
+ * libc_so = "/lib64/libc.so.6"
397
+ * => "/lib64/libc.so.6"
398
+ * @handle = Fiddle::Handle.new(libc_so, Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
399
+ * => #<Fiddle::Handle:0x00000000d69ef8>
400
+ *
401
+ * See RTLD_LAZY and RTLD_GLOBAL
402
+ *
403
+ * === Addresses to symbols
404
+ *
405
+ * strcpy_addr = @handle['strcpy']
406
+ * => 140062278451968
407
+ *
408
+ * or
409
+ *
410
+ * strcpy_addr = @handle.sym('strcpy')
411
+ * => 140062278451968
412
+ *
413
+ */
414
+ rb_cHandle = rb_define_class_under(mFiddle, "Handle", rb_cObject);
415
+ rb_define_alloc_func(rb_cHandle, rb_fiddle_handle_s_allocate);
416
+ rb_define_singleton_method(rb_cHandle, "sym", rb_fiddle_handle_s_sym, 1);
417
+ rb_define_singleton_method(rb_cHandle, "[]", rb_fiddle_handle_s_sym, 1);
418
+
419
+ /* Document-const: NEXT
420
+ *
421
+ * A predefined pseudo-handle of RTLD_NEXT
422
+ *
423
+ * Which will find the next occurrence of a function in the search order
424
+ * after the current library.
425
+ */
426
+ rb_define_const(rb_cHandle, "NEXT", predefined_fiddle_handle(RTLD_NEXT));
427
+
428
+ /* Document-const: DEFAULT
429
+ *
430
+ * A predefined pseudo-handle of RTLD_DEFAULT
431
+ *
432
+ * Which will find the first occurrence of the desired symbol using the
433
+ * default library search order
434
+ */
435
+ rb_define_const(rb_cHandle, "DEFAULT", predefined_fiddle_handle(RTLD_DEFAULT));
436
+
437
+ /* Document-const: RTLD_GLOBAL
438
+ *
439
+ * rtld Fiddle::Handle flag.
440
+ *
441
+ * The symbols defined by this library will be made available for symbol
442
+ * resolution of subsequently loaded libraries.
443
+ */
444
+ rb_define_const(rb_cHandle, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
445
+
446
+ /* Document-const: RTLD_LAZY
447
+ *
448
+ * rtld Fiddle::Handle flag.
449
+ *
450
+ * Perform lazy binding. Only resolve symbols as the code that references
451
+ * them is executed. If the symbol is never referenced, then it is never
452
+ * resolved. (Lazy binding is only performed for function references;
453
+ * references to variables are always immediately bound when the library
454
+ * is loaded.)
455
+ */
456
+ rb_define_const(rb_cHandle, "RTLD_LAZY", INT2NUM(RTLD_LAZY));
457
+
458
+ /* Document-const: RTLD_NOW
459
+ *
460
+ * rtld Fiddle::Handle flag.
461
+ *
462
+ * If this value is specified or the environment variable LD_BIND_NOW is
463
+ * set to a nonempty string, all undefined symbols in the library are
464
+ * resolved before Fiddle.dlopen returns. If this cannot be done an error
465
+ * is returned.
466
+ */
467
+ rb_define_const(rb_cHandle, "RTLD_NOW", INT2NUM(RTLD_NOW));
468
+
469
+ rb_define_method(rb_cHandle, "initialize", rb_fiddle_handle_initialize, -1);
470
+ rb_define_method(rb_cHandle, "to_i", rb_fiddle_handle_to_i, 0);
471
+ rb_define_method(rb_cHandle, "close", rb_fiddle_handle_close, 0);
472
+ rb_define_method(rb_cHandle, "sym", rb_fiddle_handle_sym, 1);
473
+ rb_define_method(rb_cHandle, "[]", rb_fiddle_handle_sym, 1);
474
+ rb_define_method(rb_cHandle, "disable_close", rb_fiddle_handle_disable_close, 0);
475
+ rb_define_method(rb_cHandle, "enable_close", rb_fiddle_handle_enable_close, 0);
476
+ rb_define_method(rb_cHandle, "close_enabled?", rb_fiddle_handle_close_enabled_p, 0);
477
+ }
478
+
479
+ /* vim: set noet sws=4 sw=4: */