Ruby  1.9.3p551(2014-11-13revision48407)
handle.c
Go to the documentation of this file.
1 /* -*- C -*-
2  * $Id: handle.c 32983 2011-08-16 00:51:58Z drbrain $
3  */
4 
5 #include <ruby.h>
6 #include "dl.h"
7 
9 
10 #ifdef _WIN32
11 # ifndef _WIN32_WCE
12 static void *
13 w32_coredll(void)
14 {
15  MEMORY_BASIC_INFORMATION m;
16  memset(&m, 0, sizeof(m));
17  if( !VirtualQuery(_errno, &m, sizeof(m)) ) return NULL;
18  return m.AllocationBase;
19 }
20 # endif
21 
22 static int
23 w32_dlclose(void *ptr)
24 {
25 # ifndef _WIN32_WCE
26  if( ptr == w32_coredll() ) return 0;
27 # endif
28  if( FreeLibrary((HMODULE)ptr) ) return 0;
29  return errno = rb_w32_map_errno(GetLastError());
30 }
31 #define dlclose(ptr) w32_dlclose(ptr)
32 #endif
33 
34 static void
35 dlhandle_free(void *ptr)
36 {
37  struct dl_handle *dlhandle = ptr;
38  if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){
39  dlclose(dlhandle->ptr);
40  }
41 }
42 
43 static size_t
44 dlhandle_memsize(const void *ptr)
45 {
46  return ptr ? sizeof(struct dl_handle) : 0;
47 }
48 
50  "dl/handle",
52 };
53 
54 /*
55  * call-seq: close
56  *
57  * Close this DL::Handle. Calling close more than once will raise a
58  * DL::DLError exception.
59  */
60 VALUE
62 {
63  struct dl_handle *dlhandle;
64 
65  TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
66  if(dlhandle->open) {
67  int ret = dlclose(dlhandle->ptr);
68  dlhandle->open = 0;
69 
70  /* Check dlclose for successful return value */
71  if(ret) {
72 #if defined(HAVE_DLERROR)
73  rb_raise(rb_eDLError, "%s", dlerror());
74 #else
75  rb_raise(rb_eDLError, "could not close handle");
76 #endif
77  }
78  return INT2NUM(ret);
79  }
80  rb_raise(rb_eDLError, "dlclose() called too many times");
81 }
82 
83 VALUE
85 {
86  VALUE obj;
87  struct dl_handle *dlhandle;
88 
90  dlhandle->ptr = 0;
91  dlhandle->open = 0;
92  dlhandle->enable_close = 0;
93 
94  return obj;
95 }
96 
97 static VALUE
98 predefined_dlhandle(void *handle)
99 {
101  struct dl_handle *dlhandle = DATA_PTR(obj);
102 
103  dlhandle->ptr = handle;
104  dlhandle->open = 1;
105  OBJ_FREEZE(obj);
106  return obj;
107 }
108 
109 /*
110  * call-seq:
111  * initialize(lib = nil, flags = DL::RTLD_LAZY | DL::RTLD_GLOBAL)
112  *
113  * Create a new handler that opens library named +lib+ with +flags+. If no
114  * library is specified, RTLD_DEFAULT is used.
115  */
116 VALUE
118 {
119  void *ptr;
120  struct dl_handle *dlhandle;
121  VALUE lib, flag;
122  char *clib;
123  int cflag;
124  const char *err;
125 
126  switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){
127  case 0:
128  clib = NULL;
129  cflag = RTLD_LAZY | RTLD_GLOBAL;
130  break;
131  case 1:
132  clib = NIL_P(lib) ? NULL : StringValuePtr(lib);
133  cflag = RTLD_LAZY | RTLD_GLOBAL;
134  break;
135  case 2:
136  clib = NIL_P(lib) ? NULL : StringValuePtr(lib);
137  cflag = NUM2INT(flag);
138  break;
139  default:
140  rb_bug("rb_dlhandle_new");
141  }
142 
143  rb_secure(2);
144 
145 #if defined(_WIN32)
146  if( !clib ){
147  HANDLE rb_libruby_handle(void);
148  ptr = rb_libruby_handle();
149  }
150  else if( STRCASECMP(clib, "libc") == 0
151 # ifdef RUBY_COREDLL
152  || STRCASECMP(clib, RUBY_COREDLL) == 0
153  || STRCASECMP(clib, RUBY_COREDLL".dll") == 0
154 # endif
155  ){
156 # ifdef _WIN32_WCE
157  ptr = dlopen("coredll.dll", cflag);
158 # else
159  ptr = w32_coredll();
160 # endif
161  }
162  else
163 #endif
164  ptr = dlopen(clib, cflag);
165 #if defined(HAVE_DLERROR)
166  if( !ptr && (err = dlerror()) ){
167  rb_raise(rb_eDLError, "%s", err);
168  }
169 #else
170  if( !ptr ){
171  err = dlerror();
172  rb_raise(rb_eDLError, "%s", err);
173  }
174 #endif
175  TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
176  if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){
177  dlclose(dlhandle->ptr);
178  }
179  dlhandle->ptr = ptr;
180  dlhandle->open = 1;
181  dlhandle->enable_close = 0;
182 
183  if( rb_block_given_p() ){
184  rb_ensure(rb_yield, self, rb_dlhandle_close, self);
185  }
186 
187  return Qnil;
188 }
189 
190 /*
191  * call-seq: enable_close
192  *
193  * Enable a call to dlclose() when this DL::Handle is garbage collected.
194  */
195 VALUE
197 {
198  struct dl_handle *dlhandle;
199 
200  TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
201  dlhandle->enable_close = 1;
202  return Qnil;
203 }
204 
205 /*
206  * call-seq: disable_close
207  *
208  * Disable a call to dlclose() when this DL::Handle is garbage collected.
209  */
210 VALUE
212 {
213  struct dl_handle *dlhandle;
214 
215  TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
216  dlhandle->enable_close = 0;
217  return Qnil;
218 }
219 
220 /*
221  * call-seq: close_enabled?
222  *
223  * Returns +true+ if dlclose() will be called when this DL::Handle is
224  * garbage collected.
225  */
226 static VALUE
228 {
229  struct dl_handle *dlhandle;
230 
231  TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
232 
233  if(dlhandle->enable_close) return Qtrue;
234  return Qfalse;
235 }
236 
237 /*
238  * call-seq: to_i
239  *
240  * Returns the memory address for this handle.
241  */
242 VALUE
244 {
245  struct dl_handle *dlhandle;
246 
247  TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
248  return PTR2NUM(dlhandle);
249 }
250 
251 static VALUE dlhandle_sym(void *handle, const char *symbol);
252 
253 /*
254  * Document-method: sym
255  * Document-method: []
256  *
257  * call-seq: sym(name)
258  *
259  * Get the address as an Integer for the function named +name+.
260  */
261 VALUE
263 {
264  struct dl_handle *dlhandle;
265 
266  TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
267  if( ! dlhandle->open ){
268  rb_raise(rb_eDLError, "closed handle");
269  }
270 
271  return dlhandle_sym(dlhandle->ptr, StringValueCStr(sym));
272 }
273 
274 #ifndef RTLD_NEXT
275 #define RTLD_NEXT NULL
276 #endif
277 #ifndef RTLD_DEFAULT
278 #define RTLD_DEFAULT NULL
279 #endif
280 
281 /*
282  * Document-method: sym
283  * Document-method: []
284  *
285  * call-seq: sym(name)
286  *
287  * Get the address as an Integer for the function named +name+. The function
288  * is searched via dlsym on RTLD_NEXT. See man(3) dlsym() for more info.
289  */
290 VALUE
292 {
293  return dlhandle_sym(RTLD_NEXT, StringValueCStr(sym));
294 }
295 
296 static VALUE
297 dlhandle_sym(void *handle, const char *name)
298 {
299 #if defined(HAVE_DLERROR)
300  const char *err;
301 # define CHECK_DLERROR if( err = dlerror() ){ func = 0; }
302 #else
303 # define CHECK_DLERROR
304 #endif
305  void (*func)();
306 
307  rb_secure(2);
308 #ifdef HAVE_DLERROR
309  dlerror();
310 #endif
311  func = (void (*)())(VALUE)dlsym(handle, name);
313 #if defined(FUNC_STDCALL)
314  if( !func ){
315  int i;
316  int len = (int)strlen(name);
317  char *name_n;
318 #if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__)
319  {
320  char *name_a = (char*)xmalloc(len+2);
321  strcpy(name_a, name);
322  name_n = name_a;
323  name_a[len] = 'A';
324  name_a[len+1] = '\0';
325  func = dlsym(handle, name_a);
327  if( func ) goto found;
328  name_n = xrealloc(name_a, len+6);
329  }
330 #else
331  name_n = (char*)xmalloc(len+6);
332 #endif
333  memcpy(name_n, name, len);
334  name_n[len++] = '@';
335  for( i = 0; i < 256; i += 4 ){
336  sprintf(name_n + len, "%d", i);
337  func = dlsym(handle, name_n);
339  if( func ) break;
340  }
341  if( func ) goto found;
342  name_n[len-1] = 'A';
343  name_n[len++] = '@';
344  for( i = 0; i < 256; i += 4 ){
345  sprintf(name_n + len, "%d", i);
346  func = dlsym(handle, name_n);
348  if( func ) break;
349  }
350  found:
351  xfree(name_n);
352  }
353 #endif
354  if( !func ){
355  rb_raise(rb_eDLError, "unknown symbol \"%s\"", name);
356  }
357 
358  return PTR2NUM(func);
359 }
360 
361 void
363 {
364  /*
365  * Document-class: DL::Handle
366  *
367  * The DL::Handle is the manner to access the dynamic library
368  *
369  * == Example
370  *
371  * === Setup
372  *
373  * libc_so = "/lib64/libc.so.6"
374  * => "/lib64/libc.so.6"
375  * @handle = DL::Handle.new(libc_so)
376  * => #<DL::Handle:0x00000000d69ef8>
377  *
378  * === Setup, with flags
379  *
380  * libc_so = "/lib64/libc.so.6"
381  * => "/lib64/libc.so.6"
382  * @handle = DL::Handle.new(libc_so, DL::RTLD_LAZY | DL::RTLD_GLOBAL)
383  * => #<DL::Handle:0x00000000d69ef8>
384  *
385  * === Addresses to symbols
386  *
387  * strcpy_addr = @handle['strcpy']
388  * => 140062278451968
389  *
390  * or
391  *
392  * strcpy_addr = @handle.sym('strcpy')
393  * => 140062278451968
394  *
395  */
400 
401  /* Document-const: NEXT
402  *
403  * A predefined pseudo-handle of RTLD_NEXT
404  *
405  * Which will find the next occurrence of a function in the search order
406  * after the current library.
407  */
409 
410  /* Document-const: DEFAULT
411  *
412  * A predefined pseudo-handle of RTLD_DEFAULT
413  *
414  * Which will find the first occurrence of the desired symbol using the
415  * default library search order
416  */
426 }
427 
428 /* mode: c; tab-with=8; sw=4; ts=8; noexpandtab: */
429