Ruby 2.7.0p0(2019-12-25revision647ee6f091eafcce70ffb75ddf7e121e192ab217)

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 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_eFiddleError, "%s", dlerror());
78 #else
79 rb_raise(rb_eFiddleError, "could not close handle");
80 #endif
81 }
82 return INT2NUM(ret);
83 }
84 rb_raise(rb_eFiddleError, "dlclose() called too many times");
85
86 UNREACHABLE;
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()) ){
180 rb_raise(rb_eFiddleError, "%s", err);
181 }
182 #else
183 if( !ptr ){
184 err = dlerror();
185 rb_raise(rb_eFiddleError, "%s", err);
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);
263 }
264
265 static VALUE fiddle_handle_sym(void *handle, VALUE symbol);
266
267 /*
268 * Document-method: sym
269 *
270 * call-seq: sym(name)
271 *
272 * Get the address as an Integer for the function named +name+.
273 */
274 static VALUE
275 rb_fiddle_handle_sym(VALUE self, VALUE sym)
276 {
277 struct dl_handle *fiddle_handle;
278
279 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
280 if( ! fiddle_handle->open ){
281 rb_raise(rb_eFiddleError, "closed handle");
282 }
283
284 return fiddle_handle_sym(fiddle_handle->ptr, sym);
285 }
286
287 #ifndef RTLD_NEXT
288 #define RTLD_NEXT NULL
289 #endif
290 #ifndef RTLD_DEFAULT
291 #define RTLD_DEFAULT NULL
292 #endif
293
294 /*
295 * Document-method: sym
296 *
297 * call-seq: sym(name)
298 *
299 * Get the address as an Integer for the function named +name+. The function
300 * is searched via dlsym on RTLD_NEXT.
301 *
302 * See man(3) dlsym() for more info.
303 */
304 static VALUE
305 rb_fiddle_handle_s_sym(VALUE self, VALUE sym)
306 {
307 return fiddle_handle_sym(RTLD_NEXT, sym);
308 }
309
310 static VALUE
311 fiddle_handle_sym(void *handle, VALUE symbol)
312 {
313 #if defined(HAVE_DLERROR)
314 const char *err;
315 # define CHECK_DLERROR if ((err = dlerror()) != 0) { func = 0; }
316 #else
317 # define CHECK_DLERROR
318 #endif
319 void (*func)();
320 const char *name = StringValueCStr(symbol);
321
322 #ifdef HAVE_DLERROR
323 dlerror();
324 #endif
325 func = (void (*)())(VALUE)dlsym(handle, name);
326 CHECK_DLERROR;
327 #if defined(FUNC_STDCALL)
328 if( !func ){
329 int i;
330 int len = (int)strlen(name);
331 char *name_n;
332 #if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__)
333 {
334 char *name_a = (char*)xmalloc(len+2);
335 strcpy(name_a, name);
336 name_n = name_a;
337 name_a[len] = 'A';
338 name_a[len+1] = '\0';
339 func = dlsym(handle, name_a);
340 CHECK_DLERROR;
341 if( func ) goto found;
342 name_n = xrealloc(name_a, len+6);
343 }
344 #else
345 name_n = (char*)xmalloc(len+6);
346 #endif
347 memcpy(name_n, name, len);
348 name_n[len++] = '@';
349 for( i = 0; i < 256; i += 4 ){
350 sprintf(name_n + len, "%d", i);
351 func = dlsym(handle, name_n);
352 CHECK_DLERROR;
353 if( func ) break;
354 }
355 if( func ) goto found;
356 name_n[len-1] = 'A';
357 name_n[len++] = '@';
358 for( i = 0; i < 256; i += 4 ){
359 sprintf(name_n + len, "%d", i);
360 func = dlsym(handle, name_n);
361 CHECK_DLERROR;
362 if( func ) break;
363 }
364 found:
365 xfree(name_n);
366 }
367 #endif
368 if( !func ){
369 rb_raise(rb_eFiddleError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
370 }
371
372 return PTR2NUM(func);
373 }
374
375 void
376 Init_fiddle_handle(void)
377 {
378 /*
379 * Document-class: Fiddle::Handle
380 *
381 * The Fiddle::Handle is the manner to access the dynamic library
382 *
383 * == Example
384 *
385 * === Setup
386 *
387 * libc_so = "/lib64/libc.so.6"
388 * => "/lib64/libc.so.6"
389 * @handle = Fiddle::Handle.new(libc_so)
390 * => #<Fiddle::Handle:0x00000000d69ef8>
391 *
392 * === Setup, with flags
393 *
394 * libc_so = "/lib64/libc.so.6"
395 * => "/lib64/libc.so.6"
396 * @handle = Fiddle::Handle.new(libc_so, Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
397 * => #<Fiddle::Handle:0x00000000d69ef8>
398 *
399 * See RTLD_LAZY and RTLD_GLOBAL
400 *
401 * === Addresses to symbols
402 *
403 * strcpy_addr = @handle['strcpy']
404 * => 140062278451968
405 *
406 * or
407 *
408 * strcpy_addr = @handle.sym('strcpy')
409 * => 140062278451968
410 *
411 */
412 rb_cHandle = rb_define_class_under(mFiddle, "Handle", rb_cObject);
413 rb_define_alloc_func(rb_cHandle, rb_fiddle_handle_s_allocate);
414 rb_define_singleton_method(rb_cHandle, "sym", rb_fiddle_handle_s_sym, 1);
415 rb_define_singleton_method(rb_cHandle, "[]", rb_fiddle_handle_s_sym, 1);
416
417 /* Document-const: NEXT
418 *
419 * A predefined pseudo-handle of RTLD_NEXT
420 *
421 * Which will find the next occurrence of a function in the search order
422 * after the current library.
423 */
424 rb_define_const(rb_cHandle, "NEXT", predefined_fiddle_handle(RTLD_NEXT));
425
426 /* Document-const: DEFAULT
427 *
428 * A predefined pseudo-handle of RTLD_DEFAULT
429 *
430 * Which will find the first occurrence of the desired symbol using the
431 * default library search order
432 */
433 rb_define_const(rb_cHandle, "DEFAULT", predefined_fiddle_handle(RTLD_DEFAULT));
434
435 /* Document-const: RTLD_GLOBAL
436 *
437 * rtld Fiddle::Handle flag.
438 *
439 * The symbols defined by this library will be made available for symbol
440 * resolution of subsequently loaded libraries.
441 */
442 rb_define_const(rb_cHandle, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
443
444 /* Document-const: RTLD_LAZY
445 *
446 * rtld Fiddle::Handle flag.
447 *
448 * Perform lazy binding. Only resolve symbols as the code that references
449 * them is executed. If the symbol is never referenced, then it is never
450 * resolved. (Lazy binding is only performed for function references;
451 * references to variables are always immediately bound when the library
452 * is loaded.)
453 */
454 rb_define_const(rb_cHandle, "RTLD_LAZY", INT2NUM(RTLD_LAZY));
455
456 /* Document-const: RTLD_NOW
457 *
458 * rtld Fiddle::Handle flag.
459 *
460 * If this value is specified or the environment variable LD_BIND_NOW is
461 * set to a nonempty string, all undefined symbols in the library are
462 * resolved before Fiddle.dlopen returns. If this cannot be done an error
463 * is returned.
464 */
465 rb_define_const(rb_cHandle, "RTLD_NOW", INT2NUM(RTLD_NOW));
466
467 rb_define_method(rb_cHandle, "initialize", rb_fiddle_handle_initialize, -1);
468 rb_define_method(rb_cHandle, "to_i", rb_fiddle_handle_to_i, 0);
469 rb_define_method(rb_cHandle, "close", rb_fiddle_handle_close, 0);
470 rb_define_method(rb_cHandle, "sym", rb_fiddle_handle_sym, 1);
471 rb_define_method(rb_cHandle, "[]", rb_fiddle_handle_sym, 1);
472 rb_define_method(rb_cHandle, "disable_close", rb_fiddle_handle_disable_close, 0);
473 rb_define_method(rb_cHandle, "enable_close", rb_fiddle_handle_enable_close, 0);
474 rb_define_method(rb_cHandle, "close_enabled?", rb_fiddle_handle_close_enabled_p, 0);
475 }
476
477 /* vim: set noet sws=4 sw=4: */
rb_w32_map_errno
int rb_w32_map_errno(DWORD)
Definition: win32.c:273
void
void
Definition: rb_mjit_min_header-2.7.0.h:13273
TypedData_Make_Struct
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: ruby.h:1244
mFiddle
VALUE mFiddle
Definition: fiddle.c:3
memset
void * memset(void *, int, size_t)
rb_block_given_p
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:897
rb_scan_args
#define rb_scan_args(argc, argvp, fmt,...)
Definition: rb_mjit_min_header-2.7.0.h:6372
fiddle.h
VALUE
unsigned long VALUE
Definition: ruby.h:102
int
__inline__ int
Definition: rb_mjit_min_header-2.7.0.h:2839
rb_cHandle
VALUE rb_cHandle
Definition: handle.c:4
rb_define_singleton_method
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
Definition: class.c:1755
rb_define_method
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1551
INT2NUM
#define INT2NUM(x)
Definition: ruby.h:1609
ptr
struct RIMemo * ptr
Definition: debug.c:74
Qfalse
#define Qfalse
Definition: ruby.h:467
NULL
#define NULL
Definition: _sdbm.c:101
PRIsVALUE
#define PRIsVALUE
Definition: ruby.h:166
strlen
size_t strlen(const char *)
OBJ_FREEZE
#define OBJ_FREEZE(x)
Definition: ruby.h:1377
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2669
obj
const VALUE VALUE obj
Definition: rb_mjit_min_header-2.7.0.h:5742
memcpy
void * memcpy(void *__restrict, const void *__restrict, size_t)
DATA_PTR
#define DATA_PTR(dta)
Definition: ruby.h:1175
dl_handle
Definition: handle.c:6
rb_eFiddleError
VALUE rb_eFiddleError
Definition: fiddle.c:4
i
uint32_t i
Definition: rb_mjit_min_header-2.7.0.h:5464
sym
#define sym(x)
Definition: date_core.c:3716
ruby.h
sprintf
int sprintf(char *__restrict, const char *__restrict,...) __attribute__((__format__(__printf__
strcpy
char * strcpy(char *__restrict, const char *__restrict)
CHECK_DLERROR
#define CHECK_DLERROR
RTLD_DEFAULT
#define RTLD_DEFAULT
Definition: handle.c:291
dl_handle::enable_close
int enable_close
Definition: handle.c:9
StringValueCStr
#define StringValueCStr(v)
Definition: ruby.h:604
RTLD_NEXT
#define RTLD_NEXT
Definition: handle.c:288
rb_cObject
RUBY_EXTERN VALUE rb_cObject
Definition: ruby.h:2010
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: ruby.h:1252
rb_bug
void rb_bug(const char *fmt,...)
Definition: error.c:634
argv
char ** argv
Definition: ruby.c:223
xmalloc
#define xmalloc
Definition: defines.h:211
xrealloc
#define xrealloc
Definition: defines.h:214
UNREACHABLE
#define UNREACHABLE
Definition: ruby.h:63
klass
VALUE klass
Definition: rb_mjit_min_header-2.7.0.h:13254
dl_handle::ptr
void * ptr
Definition: handle.c:7
STRCASECMP
#define STRCASECMP(s1, s2)
Definition: ruby.h:2323
NIL_P
#define NIL_P(v)
Definition: ruby.h:482
argc
int argc
Definition: ruby.c:222
PTR2NUM
#define PTR2NUM(x)
Definition: conversions.h:36
rb_define_const
void rb_define_const(VALUE, const char *, VALUE)
Definition: variable.c:2880
err
int err
Definition: win32.c:135
rb_data_type_struct
Definition: ruby.h:1148
xfree
#define xfree
Definition: defines.h:216
Qtrue
#define Qtrue
Definition: ruby.h:468
errno
int errno
len
uint8_t len
Definition: escape.c:17
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:698
rb_yield
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1237
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:1114
NUM2INT
#define NUM2INT(x)
Definition: ruby.h:715
Qnil
#define Qnil
Definition: ruby.h:469
dl_handle::open
int open
Definition: handle.c:8
rb_define_alloc_func
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
Init_fiddle_handle
void Init_fiddle_handle(void)
Definition: handle.c:376
name
const char * name
Definition: nkf.c:208