Ruby  2.5.0dev(2017-10-22revision60238)
handle.c
Go to the documentation of this file.
1 #include <ruby.h>
2 #include <fiddle.h>
3 
4 #define SafeStringValueCStr(v) (rb_check_safe_obj(rb_string_value(&v)), StringValueCStr(v))
5 
7 
8 struct dl_handle {
9  void *ptr;
10  int open;
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 
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);
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);
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);
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);
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
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  */
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: */
#define SafeStringValueCStr(v)
Definition: handle.c:4
void rb_bug(const char *fmt,...)
Definition: error.c:521
VALUE mFiddle
Definition: fiddle.c:3
size_t strlen(const char *)
#define INT2NUM(x)
Definition: ruby.h:1538
int open
Definition: handle.c:10
#define NUM2INT(x)
Definition: ruby.h:684
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
Definition: class.c:1716
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:835
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2284
#define Qtrue
Definition: ruby.h:437
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: ruby.h:1183
#define OBJ_FREEZE(x)
Definition: ruby.h:1306
#define UNREACHABLE
Definition: ruby.h:46
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:693
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
#define DATA_PTR(dta)
Definition: ruby.h:1106
VALUE rb_ensure(VALUE(*b_proc)(ANYARGS), VALUE data1, VALUE(*e_proc)(ANYARGS), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1035
int rb_w32_map_errno(DWORD)
Definition: win32.c:273
#define sym(x)
Definition: date_core.c:3721
VALUE rb_eFiddleError
Definition: fiddle.c:4
VALUE rb_cHandle
Definition: handle.c:6
RUBY_EXTERN VALUE rb_cObject
Definition: ruby.h:1893
#define RTLD_DEFAULT
Definition: handle.c:293
void Init_fiddle_handle(void)
Definition: handle.c:378
#define NIL_P(v)
Definition: ruby.h:451
int enable_close
Definition: handle.c:11
void rb_define_const(VALUE, const char *, VALUE)
Definition: variable.c:2691
int argc
Definition: ruby.c:187
#define Qfalse
Definition: ruby.h:436
int err
Definition: win32.c:135
void * ptr
Definition: handle.c:9
VALUE rb_yield(VALUE)
Definition: vm_eval.c:973
int errno
#define RTLD_NEXT
Definition: handle.c:290
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:1908
#define PRIsVALUE
Definition: ruby.h:135
#define Qnil
Definition: ruby.h:438
unsigned long VALUE
Definition: ruby.h:85
#define PTR2NUM(x)
Definition: conversions.h:36
register unsigned int len
Definition: zonetab.h:51
#define xmalloc
Definition: defines.h:183
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: ruby.h:1175
const char * name
Definition: nkf.c:208
#define xrealloc
Definition: defines.h:186
#define STRCASECMP(s1, s2)
Definition: ruby.h:2158
void void xfree(void *)
#define NULL
Definition: _sdbm.c:102
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1515
char ** argv
Definition: ruby.c:188
#define CHECK_DLERROR