Ruby  3.1.0dev(2021-09-10revisionb76ad15ed0da636161de0243c547ee1e6fc95681)
handle.c
Go to the documentation of this file.
1 #include <ruby.h>
2 #include <fiddle.h>
3 
5 
6 struct dl_handle {
7  void *ptr;
8  int open;
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  xfree(ptr);
44 }
45 
46 static size_t
47 fiddle_handle_memsize(const void *ptr)
48 {
49  return sizeof(struct dl_handle);
50 }
51 
52 static const rb_data_type_t fiddle_handle_data_type = {
53  "fiddle/handle",
54  {0, fiddle_handle_free, fiddle_handle_memsize,},
55 };
56 
57 /*
58  * call-seq: close
59  *
60  * Close this handle.
61  *
62  * Calling close more than once will raise a Fiddle::DLError exception.
63  */
64 static VALUE
65 rb_fiddle_handle_close(VALUE self)
66 {
67  struct dl_handle *fiddle_handle;
68 
69  TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
70  if(fiddle_handle->open) {
71  int ret = dlclose(fiddle_handle->ptr);
72  fiddle_handle->open = 0;
73 
74  /* Check dlclose for successful return value */
75  if(ret) {
76 #if defined(HAVE_DLERROR)
77  rb_raise(rb_eFiddleDLError, "%s", dlerror());
78 #else
79  rb_raise(rb_eFiddleDLError, "could not close handle");
80 #endif
81  }
82  return INT2NUM(ret);
83  }
84  rb_raise(rb_eFiddleDLError, "dlclose() called too many times");
85 
87 }
88 
89 static VALUE
90 rb_fiddle_handle_s_allocate(VALUE klass)
91 {
92  VALUE obj;
93  struct dl_handle *fiddle_handle;
94 
95  obj = TypedData_Make_Struct(rb_cHandle, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
96  fiddle_handle->ptr = 0;
97  fiddle_handle->open = 0;
98  fiddle_handle->enable_close = 0;
99 
100  return obj;
101 }
102 
103 static VALUE
104 predefined_fiddle_handle(void *handle)
105 {
106  VALUE obj = rb_fiddle_handle_s_allocate(rb_cHandle);
107  struct dl_handle *fiddle_handle = DATA_PTR(obj);
108 
109  fiddle_handle->ptr = handle;
110  fiddle_handle->open = 1;
111  OBJ_FREEZE(obj);
112  return obj;
113 }
114 
115 /*
116  * call-seq:
117  * new(library = nil, flags = Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
118  *
119  * Create a new handler that opens +library+ with +flags+.
120  *
121  * If no +library+ is specified or +nil+ is given, DEFAULT is used, which is
122  * the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more.
123  *
124  * lib = Fiddle::Handle.new
125  *
126  * The default is dependent on OS, and provide a handle for all libraries
127  * already loaded. For example, in most cases you can use this to access +libc+
128  * functions, or ruby functions like +rb_str_new+.
129  */
130 static VALUE
131 rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self)
132 {
133  void *ptr;
134  struct dl_handle *fiddle_handle;
135  VALUE lib, flag;
136  char *clib;
137  int cflag;
138  const char *err;
139 
140  switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){
141  case 0:
142  clib = NULL;
143  cflag = RTLD_LAZY | RTLD_GLOBAL;
144  break;
145  case 1:
146  clib = NIL_P(lib) ? NULL : StringValueCStr(lib);
147  cflag = RTLD_LAZY | RTLD_GLOBAL;
148  break;
149  case 2:
150  clib = NIL_P(lib) ? NULL : StringValueCStr(lib);
151  cflag = NUM2INT(flag);
152  break;
153  default:
154  rb_bug("rb_fiddle_handle_new");
155  }
156 
157 #if defined(_WIN32)
158  if( !clib ){
159  HANDLE rb_libruby_handle(void);
160  ptr = rb_libruby_handle();
161  }
162  else if( STRCASECMP(clib, "libc") == 0
163 # ifdef RUBY_COREDLL
164  || STRCASECMP(clib, RUBY_COREDLL) == 0
165  || STRCASECMP(clib, RUBY_COREDLL".dll") == 0
166 # endif
167  ){
168 # ifdef _WIN32_WCE
169  ptr = dlopen("coredll.dll", cflag);
170 # else
171  (void)cflag;
172  ptr = w32_coredll();
173 # endif
174  }
175  else
176 #endif
177  ptr = dlopen(clib, cflag);
178 #if defined(HAVE_DLERROR)
179  if( !ptr && (err = dlerror()) ){
181  }
182 #else
183  if( !ptr ){
184  err = dlerror();
186  }
187 #endif
188  TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
189  if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){
190  dlclose(fiddle_handle->ptr);
191  }
192  fiddle_handle->ptr = ptr;
193  fiddle_handle->open = 1;
194  fiddle_handle->enable_close = 0;
195 
196  if( rb_block_given_p() ){
197  rb_ensure(rb_yield, self, rb_fiddle_handle_close, self);
198  }
199 
200  return Qnil;
201 }
202 
203 /*
204  * call-seq: enable_close
205  *
206  * Enable a call to dlclose() when this handle is garbage collected.
207  */
208 static VALUE
209 rb_fiddle_handle_enable_close(VALUE self)
210 {
211  struct dl_handle *fiddle_handle;
212 
213  TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
214  fiddle_handle->enable_close = 1;
215  return Qnil;
216 }
217 
218 /*
219  * call-seq: disable_close
220  *
221  * Disable a call to dlclose() when this handle is garbage collected.
222  */
223 static VALUE
224 rb_fiddle_handle_disable_close(VALUE self)
225 {
226  struct dl_handle *fiddle_handle;
227 
228  TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
229  fiddle_handle->enable_close = 0;
230  return Qnil;
231 }
232 
233 /*
234  * call-seq: close_enabled?
235  *
236  * Returns +true+ if dlclose() will be called when this handle is garbage collected.
237  *
238  * See man(3) dlclose() for more info.
239  */
240 static VALUE
241 rb_fiddle_handle_close_enabled_p(VALUE self)
242 {
243  struct dl_handle *fiddle_handle;
244 
245  TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
246 
247  if(fiddle_handle->enable_close) return Qtrue;
248  return Qfalse;
249 }
250 
251 /*
252  * call-seq: to_i
253  *
254  * Returns the memory address for this handle.
255  */
256 static VALUE
257 rb_fiddle_handle_to_i(VALUE self)
258 {
259  struct dl_handle *fiddle_handle;
260 
261  TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
262  return PTR2NUM(fiddle_handle->ptr);
263 }
264 
265 /*
266  * call-seq: to_ptr
267  *
268  * Returns the Fiddle::Pointer of this handle.
269  */
270 static VALUE
271 rb_fiddle_handle_to_ptr(VALUE self)
272 {
273  struct dl_handle *fiddle_handle;
274 
275  TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
276  return rb_fiddle_ptr_new_wrap(fiddle_handle->ptr, 0, 0, self, 0);
277 }
278 
279 static VALUE fiddle_handle_sym(void *handle, VALUE symbol);
280 
281 /*
282  * Document-method: sym
283  *
284  * call-seq: sym(name)
285  *
286  * Get the address as an Integer for the function named +name+.
287  */
288 static VALUE
289 rb_fiddle_handle_sym(VALUE self, VALUE sym)
290 {
291  struct dl_handle *fiddle_handle;
292 
293  TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
294  if( ! fiddle_handle->open ){
295  rb_raise(rb_eFiddleDLError, "closed handle");
296  }
297 
298  return fiddle_handle_sym(fiddle_handle->ptr, sym);
299 }
300 
301 #ifndef RTLD_NEXT
302 #define RTLD_NEXT NULL
303 #endif
304 #ifndef RTLD_DEFAULT
305 #define RTLD_DEFAULT NULL
306 #endif
307 
308 /*
309  * Document-method: sym
310  *
311  * call-seq: sym(name)
312  *
313  * Get the address as an Integer for the function named +name+. The function
314  * is searched via dlsym on RTLD_NEXT.
315  *
316  * See man(3) dlsym() for more info.
317  */
318 static VALUE
319 rb_fiddle_handle_s_sym(VALUE self, VALUE sym)
320 {
321  return fiddle_handle_sym(RTLD_NEXT, sym);
322 }
323 
324 static VALUE
325 fiddle_handle_sym(void *handle, VALUE symbol)
326 {
327 #if defined(HAVE_DLERROR)
328  const char *err;
329 # define CHECK_DLERROR if ((err = dlerror()) != 0) { func = 0; }
330 #else
331 # define CHECK_DLERROR
332 #endif
333  void (*func)();
334  const char *name = StringValueCStr(symbol);
335 
336 #ifdef HAVE_DLERROR
337  dlerror();
338 #endif
339  func = (void (*)())(VALUE)dlsym(handle, name);
341 #if defined(FUNC_STDCALL)
342  if( !func ){
343  int i;
344  int len = (int)strlen(name);
345  char *name_n;
346 #if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__)
347  {
348  char *name_a = (char*)xmalloc(len+2);
349  strcpy(name_a, name);
350  name_n = name_a;
351  name_a[len] = 'A';
352  name_a[len+1] = '\0';
353  func = dlsym(handle, name_a);
355  if( func ) goto found;
356  name_n = xrealloc(name_a, len+6);
357  }
358 #else
359  name_n = (char*)xmalloc(len+6);
360 #endif
361  memcpy(name_n, name, len);
362  name_n[len++] = '@';
363  for( i = 0; i < 256; i += 4 ){
364  sprintf(name_n + len, "%d", i);
365  func = dlsym(handle, name_n);
367  if( func ) break;
368  }
369  if( func ) goto found;
370  name_n[len-1] = 'A';
371  name_n[len++] = '@';
372  for( i = 0; i < 256; i += 4 ){
373  sprintf(name_n + len, "%d", i);
374  func = dlsym(handle, name_n);
376  if( func ) break;
377  }
378  found:
379  xfree(name_n);
380  }
381 #endif
382  if( !func ){
383  rb_raise(rb_eFiddleDLError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
384  }
385 
386  return PTR2NUM(func);
387 }
388 
389 /*
390  * call-seq: file_name
391  *
392  * Returns the file name of this handle.
393  */
394 static VALUE
395 rb_fiddle_handle_file_name(VALUE self)
396 {
397  struct dl_handle *fiddle_handle;
398 
399  TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
400 
401 #if defined(HAVE_DLINFO) && defined(HAVE_CONST_RTLD_DI_LINKMAP)
402  {
403  struct link_map *lm = NULL;
404  int res = dlinfo(fiddle_handle->ptr, RTLD_DI_LINKMAP, &lm);
405  if (res == 0 && lm != NULL) {
406  return rb_str_new_cstr(lm->l_name);
407  }
408  else {
409 #if defined(HAVE_DLERROR)
410  rb_raise(rb_eFiddleDLError, "could not get handle file name: %s", dlerror());
411 #else
412  rb_raise(rb_eFiddleDLError, "could not get handle file name");
413 #endif
414  }
415  }
416 #elif defined(HAVE_GETMODULEFILENAME)
417  {
418  char filename[MAX_PATH];
419  DWORD res = GetModuleFileName(fiddle_handle->ptr, filename, MAX_PATH);
420  if (res == 0) {
421  rb_raise(rb_eFiddleDLError, "could not get handle file name: %s", dlerror());
422  }
423  return rb_str_new_cstr(filename);
424  }
425 #else
426  (void)fiddle_handle;
427  return Qnil;
428 #endif
429 }
430 
431 void
433 {
434  /*
435  * Document-class: Fiddle::Handle
436  *
437  * The Fiddle::Handle is the manner to access the dynamic library
438  *
439  * == Example
440  *
441  * === Setup
442  *
443  * libc_so = "/lib64/libc.so.6"
444  * => "/lib64/libc.so.6"
445  * @handle = Fiddle::Handle.new(libc_so)
446  * => #<Fiddle::Handle:0x00000000d69ef8>
447  *
448  * === Setup, with flags
449  *
450  * libc_so = "/lib64/libc.so.6"
451  * => "/lib64/libc.so.6"
452  * @handle = Fiddle::Handle.new(libc_so, Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
453  * => #<Fiddle::Handle:0x00000000d69ef8>
454  *
455  * See RTLD_LAZY and RTLD_GLOBAL
456  *
457  * === Addresses to symbols
458  *
459  * strcpy_addr = @handle['strcpy']
460  * => 140062278451968
461  *
462  * or
463  *
464  * strcpy_addr = @handle.sym('strcpy')
465  * => 140062278451968
466  *
467  */
469  rb_define_alloc_func(rb_cHandle, rb_fiddle_handle_s_allocate);
470  rb_define_singleton_method(rb_cHandle, "sym", rb_fiddle_handle_s_sym, 1);
471  rb_define_singleton_method(rb_cHandle, "[]", rb_fiddle_handle_s_sym, 1);
472 
473  /* Document-const: NEXT
474  *
475  * A predefined pseudo-handle of RTLD_NEXT
476  *
477  * Which will find the next occurrence of a function in the search order
478  * after the current library.
479  */
480  rb_define_const(rb_cHandle, "NEXT", predefined_fiddle_handle(RTLD_NEXT));
481 
482  /* Document-const: DEFAULT
483  *
484  * A predefined pseudo-handle of RTLD_DEFAULT
485  *
486  * Which will find the first occurrence of the desired symbol using the
487  * default library search order
488  */
489  rb_define_const(rb_cHandle, "DEFAULT", predefined_fiddle_handle(RTLD_DEFAULT));
490 
491  /* Document-const: RTLD_GLOBAL
492  *
493  * rtld Fiddle::Handle flag.
494  *
495  * The symbols defined by this library will be made available for symbol
496  * resolution of subsequently loaded libraries.
497  */
498  rb_define_const(rb_cHandle, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
499 
500  /* Document-const: RTLD_LAZY
501  *
502  * rtld Fiddle::Handle flag.
503  *
504  * Perform lazy binding. Only resolve symbols as the code that references
505  * them is executed. If the symbol is never referenced, then it is never
506  * resolved. (Lazy binding is only performed for function references;
507  * references to variables are always immediately bound when the library
508  * is loaded.)
509  */
510  rb_define_const(rb_cHandle, "RTLD_LAZY", INT2NUM(RTLD_LAZY));
511 
512  /* Document-const: RTLD_NOW
513  *
514  * rtld Fiddle::Handle flag.
515  *
516  * If this value is specified or the environment variable LD_BIND_NOW is
517  * set to a nonempty string, all undefined symbols in the library are
518  * resolved before Fiddle.dlopen returns. If this cannot be done an error
519  * is returned.
520  */
521  rb_define_const(rb_cHandle, "RTLD_NOW", INT2NUM(RTLD_NOW));
522 
523  rb_define_method(rb_cHandle, "initialize", rb_fiddle_handle_initialize, -1);
524  rb_define_method(rb_cHandle, "to_i", rb_fiddle_handle_to_i, 0);
525  rb_define_method(rb_cHandle, "to_ptr", rb_fiddle_handle_to_ptr, 0);
526  rb_define_method(rb_cHandle, "close", rb_fiddle_handle_close, 0);
527  rb_define_method(rb_cHandle, "sym", rb_fiddle_handle_sym, 1);
528  rb_define_method(rb_cHandle, "[]", rb_fiddle_handle_sym, 1);
529  rb_define_method(rb_cHandle, "file_name", rb_fiddle_handle_file_name, 0);
530  rb_define_method(rb_cHandle, "disable_close", rb_fiddle_handle_disable_close, 0);
531  rb_define_method(rb_cHandle, "enable_close", rb_fiddle_handle_enable_close, 0);
532  rb_define_method(rb_cHandle, "close_enabled?", rb_fiddle_handle_close_enabled_p, 0);
533 }
534 
535 /* vim: set noet sws=4 sw=4: */
rb_w32_map_errno
int rb_w32_map_errno(DWORD)
Definition: win32.c:281
rb_define_singleton_method
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition: cxxanyargs.hpp:670
mFiddle
VALUE mFiddle
Definition: fiddle.c:3
rb_block_given_p
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:939
rb_eFiddleDLError
VALUE rb_eFiddleDLError
Definition: fiddle.c:4
rb_yield
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1377
PRIsVALUE
#define PRIsVALUE
Definition: inttypes.h:77
fiddle.h
xfree
#define xfree
Definition: xmalloc.h:49
rb_cHandle
VALUE rb_cHandle
Definition: handle.c:4
DWORD
IUnknown DWORD
Definition: win32ole.c:33
argv
char ** argv
Definition: ruby.c:243
ptr
struct RIMemo * ptr
Definition: debug.c:87
NUM2INT
#define NUM2INT
Definition: int.h:44
TypedData_Make_Struct
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: rtypeddata.h:122
rb_fiddle_ptr_new_wrap
VALUE rb_fiddle_ptr_new_wrap(void *ptr, long size, rb_fiddle_freefunc_t func, VALUE wrap0, VALUE wrap1)
Definition: pointer.c:145
OBJ_FREEZE
#define OBJ_FREEZE
Definition: fl_type.h:143
strlen
size_t strlen(const char *)
STRCASECMP
#define STRCASECMP
Definition: ctype.h:52
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:3022
NIL_P
#define NIL_P
Definition: special_consts.h:46
xrealloc
#define xrealloc
Definition: xmalloc.h:47
INT2NUM
#define INT2NUM
Definition: int.h:43
xmalloc
#define xmalloc
Definition: xmalloc.h:44
rb_define_alloc_func
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
dl_handle
Definition: handle.c:6
rb_cObject
VALUE rb_cObject
Object class.
Definition: object.c:50
sym
#define sym(name)
Definition: enumerator.c:4043
Qfalse
#define Qfalse
Definition: special_consts.h:50
len
uint8_t len
Definition: escape.c:17
UNREACHABLE
#define UNREACHABLE
Definition: missing.h:46
ruby.h
Qnil
#define Qnil
Definition: special_consts.h:51
CHECK_DLERROR
#define CHECK_DLERROR
NULL
#define NULL
Definition: regenc.h:69
memcpy
#define memcpy
Definition: memory.h:278
RTLD_DEFAULT
#define RTLD_DEFAULT
Definition: handle.c:305
rb_scan_args
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:2347
dl_handle::enable_close
int enable_close
Definition: handle.c:9
Qtrue
#define Qtrue
Definition: special_consts.h:52
RTLD_NEXT
#define RTLD_NEXT
Definition: handle.c:302
VALUE
unsigned long VALUE
Definition: value.h:38
rb_bug
void rb_bug(const char *fmt,...)
Definition: error.c:796
dl_handle::ptr
void * ptr
Definition: handle.c:7
ret
return ret
Definition: memory.h:232
rb_define_const
void rb_define_const(VALUE, const char *, VALUE)
Definition: variable.c:3168
argc
int argc
Definition: ruby.c:242
PTR2NUM
#define PTR2NUM(x)
Definition: conversions.h:45
err
int err
Definition: win32.c:143
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: rtypeddata.h:130
rb_data_type_struct
Definition: rtypeddata.h:70
rb_str_new_cstr
#define rb_str_new_cstr(str)
Definition: string.h:219
rb_define_class_under
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:809
DATA_PTR
#define DATA_PTR(obj)
Definition: rdata.h:55
StringValueCStr
#define StringValueCStr(v)
Definition: rstring.h:52
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:655
rb_ensure
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1144
dl_handle::open
int open
Definition: handle.c:8
Init_fiddle_handle
void Init_fiddle_handle(void)
Definition: handle.c:432
name
const char * name
Definition: nkf.c:208