Ruby: ext/fiddle/handle.c Source File

Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)

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
14static void *
15w32_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
24static int
25w32_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
36static void
37fiddle_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
46static size_t
47fiddle_handle_memsize(const void *ptr)
48{
49 return sizeof(struct dl_handle);
50}
51
52static 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 */
64static VALUE
65rb_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
86 UNREACHABLE;
87}
88
89static VALUE
90rb_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
103static VALUE
104predefined_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 */
130static VALUE
131rb_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_eFiddleDLError, "%s", err);
181 }
182#else
183 if( !ptr ){
184 err = dlerror();
185 rb_raise(rb_eFiddleDLError, "%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 */
208static VALUE
209rb_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 */
223static VALUE
224rb_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 */
240static VALUE
241rb_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 */
256static VALUE
257rb_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
265static 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 */
274static VALUE
275rb_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_eFiddleDLError, "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 */
304static VALUE
305rb_fiddle_handle_s_sym(VALUE self, VALUE sym)
306{
307 return fiddle_handle_sym(RTLD_NEXT, sym);
308}
309
310static VALUE
311fiddle_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_eFiddleDLError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
370 }
371
372 return PTR2NUM(func);
373}
374
375void
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: */
UNREACHABLE
#define UNREACHABLE
Definition: assume.h:30
PTR2NUM
#define PTR2NUM(x)
Definition: conversions.h:45
STRCASECMP
#define STRCASECMP
Definition: ctype.h:52
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:653
rb_define_singleton_method
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition: cxxanyargs.hpp:668
ptr
struct RIMemo * ptr
Definition: debug.c:88
sym
#define sym(name)
Definition: enumerator.c:4007
len
uint8_t len
Definition: escape.c:17
memcpy
#define memcpy(d, s, n)
Definition: ffi_common.h:55
mFiddle
VALUE mFiddle
Definition: fiddle.c:3
rb_eFiddleDLError
VALUE rb_eFiddleDLError
Definition: fiddle.c:4
fiddle.h
PRIsVALUE
#define PRIsVALUE
Definition: function.c:10
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:797
rb_scan_args
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:2296
rb_block_given_p
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:935
OBJ_FREEZE
#define OBJ_FREEZE
Definition: fl_type.h:134
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2917
rb_bug
void rb_bug(const char *fmt,...)
Definition: error.c:768
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:1148
rb_cObject
VALUE rb_cObject
Object class.
Definition: object.c:49
RTLD_NEXT
#define RTLD_NEXT
Definition: handle.c:288
RTLD_DEFAULT
#define RTLD_DEFAULT
Definition: handle.c:291
CHECK_DLERROR
#define CHECK_DLERROR
rb_cHandle
VALUE rb_cHandle
Definition: handle.c:4
Init_fiddle_handle
void Init_fiddle_handle(void)
Definition: handle.c:376
rb_define_alloc_func
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
rb_define_const
void rb_define_const(VALUE, const char *, VALUE)
Definition: variable.c:3150
NUM2INT
#define NUM2INT
Definition: int.h:44
INT2NUM
#define INT2NUM
Definition: int.h:43
int
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
rb_yield
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1341
name
const char * name
Definition: nkf.c:208
DATA_PTR
#define DATA_PTR(obj)
Definition: rdata.h:56
NULL
#define NULL
Definition: regenc.h:69
StringValueCStr
#define StringValueCStr(v)
Definition: rstring.h:52
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: rtypeddata.h:130
TypedData_Make_Struct
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: rtypeddata.h:122
ruby.h
argc
int argc
Definition: ruby.c:240
argv
char ** argv
Definition: ruby.c:241
Qtrue
#define Qtrue
Definition: special_consts.h:52
Qnil
#define Qnil
Definition: special_consts.h:51
Qfalse
#define Qfalse
Definition: special_consts.h:50
NIL_P
#define NIL_P
Definition: special_consts.h:46
strlen
size_t strlen(const char *)
dl_handle
Definition: handle.c:6
dl_handle::open
int open
Definition: handle.c:8
dl_handle::ptr
void * ptr
Definition: handle.c:7
dl_handle::enable_close
int enable_close
Definition: handle.c:9
rb_data_type_struct
Definition: rtypeddata.h:70
VALUE
unsigned long VALUE
Definition: value.h:38
err
int err
Definition: win32.c:142
rb_w32_map_errno
int rb_w32_map_errno(DWORD)
Definition: win32.c:280
xfree
#define xfree
Definition: xmalloc.h:49
xrealloc
#define xrealloc
Definition: xmalloc.h:47
xmalloc
#define xmalloc
Definition: xmalloc.h:44