Ruby 2.0.0p247(2013-06-27revision41674)

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

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