1/*
2 * ext/dl/dl.c
3 *
4 * doumentation:
5 * - Vincent Batts (vbatts@hashbangbash.com)
6 *
7 */
8#include <ruby/ruby.h>
9#include <ruby/io.h>
10#include <ctype.h>
11#include "dl.h"
12
13VALUE rb_mDL;
14VALUE rb_eDLError;
15VALUE rb_eDLTypeError;
16
17ID rbdl_id_cdecl;
18ID rbdl_id_stdcall;
19
20#ifndef DLTYPE_SSIZE_T
21# if SIZEOF_SIZE_T == SIZEOF_INT
22#   define DLTYPE_SSIZE_T DLTYPE_INT
23# elif SIZEOF_SIZE_T == SIZEOF_LONG
24#   define DLTYPE_SSIZE_T DLTYPE_LONG
25# elif defined HAVE_LONG_LONG && SIZEOF_SIZE_T == SIZEOF_LONG_LONG
26#   define DLTYPE_SSIZE_T DLTYPE_LONG_LONG
27# endif
28#endif
29#define DLTYPE_SIZE_T (-1*SIGNEDNESS_OF_SIZE_T*DLTYPE_SSIZE_T)
30
31#ifndef DLTYPE_PTRDIFF_T
32# if SIZEOF_PTRDIFF_T == SIZEOF_INT
33#   define DLTYPE_PTRDIFF_T DLTYPE_INT
34# elif SIZEOF_PTRDIFF_T == SIZEOF_LONG
35#   define DLTYPE_PTRDIFF_T DLTYPE_LONG
36# elif defined HAVE_LONG_LONG && SIZEOF_PTRDIFF_T == SIZEOF_LONG_LONG
37#   define DLTYPE_PTRDIFF_T DLTYPE_LONG_LONG
38# endif
39#endif
40
41#ifndef DLTYPE_INTPTR_T
42# if SIZEOF_INTPTR_T == SIZEOF_INT
43#   define DLTYPE_INTPTR_T DLTYPE_INT
44# elif SIZEOF_INTPTR_T == SIZEOF_LONG
45#   define DLTYPE_INTPTR_T DLTYPE_LONG
46# elif defined HAVE_LONG_LONG && SIZEOF_INTPTR_T == SIZEOF_LONG_LONG
47#   define DLTYPE_INTPTR_T DLTYPE_LONG_LONG
48# endif
49#endif
50#define DLTYPE_UINTPTR_T (-DLTYPE_INTPTR_T)
51
52/*
53 * call-seq: DL.dlopen(so_lib)
54 *
55 * An interface to the dynamic linking loader
56 *
57 * This is a shortcut to DL::Handle.new and takes the same arguments.
58 *
59 * Example:
60 *
61 *   libc_so = "/lib64/libc.so.6"
62 *   => "/lib64/libc.so.6"
63 *
64 *   libc = DL.dlopen(libc_so)
65 *   => #<DL::Handle:0x00000000e05b00>
66 */
67VALUE
68rb_dl_dlopen(int argc, VALUE argv[], VALUE self)
69{
70    return rb_class_new_instance(argc, argv, rb_cDLHandle);
71}
72
73/*
74 * call-seq: DL.malloc(size)
75 *
76 * Allocate +size+ bytes of memory and return the integer memory address
77 * for the allocated memory.
78 */
79VALUE
80rb_dl_malloc(VALUE self, VALUE size)
81{
82    void *ptr;
83
84    rb_secure(4);
85    ptr = (void*)ruby_xmalloc(NUM2INT(size));
86    return PTR2NUM(ptr);
87}
88
89/*
90 * call-seq: DL.realloc(addr, size)
91 *
92 * Change the size of the memory allocated at the memory location +addr+ to
93 * +size+ bytes.  Returns the memory address of the reallocated memory, which
94 * may be different than the address passed in.
95 */
96VALUE
97rb_dl_realloc(VALUE self, VALUE addr, VALUE size)
98{
99    void *ptr = NUM2PTR(addr);
100
101    rb_secure(4);
102    ptr = (void*)ruby_xrealloc(ptr, NUM2INT(size));
103    return PTR2NUM(ptr);
104}
105
106/*
107 * call-seq: DL.free(addr)
108 *
109 * Free the memory at address +addr+
110 */
111VALUE
112rb_dl_free(VALUE self, VALUE addr)
113{
114    void *ptr = NUM2PTR(addr);
115
116    rb_secure(4);
117    ruby_xfree(ptr);
118    return Qnil;
119}
120
121/*
122 * call-seq: DL.dlunwrap(addr)
123 *
124 * Returns the hexadecimal representation of a memory pointer address +addr+
125 *
126 * Example:
127 *
128 *   lib = DL.dlopen('/lib64/libc-2.15.so')
129 *   => #<DL::Handle:0x00000001342460>
130 *
131 *   lib['strcpy'].to_s(16)
132 *   => "7f59de6dd240"
133 *
134 *   DL.dlunwrap(DL.dlwrap(lib['strcpy'].to_s(16)))
135 *   => "7f59de6dd240"
136 */
137VALUE
138rb_dl_ptr2value(VALUE self, VALUE addr)
139{
140    rb_secure(4);
141    return (VALUE)NUM2PTR(addr);
142}
143
144/*
145 * call-seq: DL.dlwrap(val)
146 *
147 * Returns a memory pointer of a function's hexadecimal address location +val+
148 *
149 * Example:
150 *
151 *   lib = DL.dlopen('/lib64/libc-2.15.so')
152 *   => #<DL::Handle:0x00000001342460>
153 *
154 *   DL.dlwrap(lib['strcpy'].to_s(16))
155 *   => 25522520
156 */
157VALUE
158rb_dl_value2ptr(VALUE self, VALUE val)
159{
160    return PTR2NUM((void*)val);
161}
162
163static void
164rb_dl_init_callbacks(VALUE dl)
165{
166    static const char cb[] = "dl/callback.so";
167
168    rb_autoload(dl, rb_intern_const("CdeclCallbackAddrs"), cb);
169    rb_autoload(dl, rb_intern_const("CdeclCallbackProcs"), cb);
170#ifdef FUNC_STDCALL
171    rb_autoload(dl, rb_intern_const("StdcallCallbackAddrs"), cb);
172    rb_autoload(dl, rb_intern_const("StdcallCallbackProcs"), cb);
173#endif
174}
175
176void
177Init_dl(void)
178{
179    void Init_dlhandle(void);
180    void Init_dlcfunc(void);
181    void Init_dlptr(void);
182
183    rbdl_id_cdecl = rb_intern_const("cdecl");
184    rbdl_id_stdcall = rb_intern_const("stdcall");
185
186    /* Document-module: DL
187     *
188     * A bridge to the dlopen() or dynamic library linker function.
189     *
190     * == Example
191     *
192     *   bash $> cat > sum.c <<EOF
193     *   double sum(double *arry, int len)
194     *   {
195     *           double ret = 0;
196     *           int i;
197     *           for(i = 0; i < len; i++){
198     *                   ret = ret + arry[i];
199     *           }
200     *           return ret;
201     *   }
202     *
203     *   double split(double num)
204     *   {
205     *           double ret = 0;
206     *           ret = num / 2;
207     *           return ret;
208     *   }
209     *   EOF
210     *   bash $> gcc -o libsum.so -shared sum.c
211     *   bash $> cat > sum.rb <<EOF
212     *   require 'dl'
213     *   require 'dl/import'
214     *
215     *   module LibSum
216     *           extend DL::Importer
217     *           dlload './libsum.so'
218     *           extern 'double sum(double*, int)'
219     *           extern 'double split(double)'
220     *   end
221     *
222     *   a = [2.0, 3.0, 4.0]
223     *
224     *   sum = LibSum.sum(a.pack("d*"), a.count)
225     *   p LibSum.split(sum)
226     *   EOF
227     *   bash $> ruby sum.rb
228     *   4.5
229     *
230     * WIN! :-)
231     */
232    rb_mDL = rb_define_module("DL");
233
234    /*
235     * Document-class: DL::DLError
236     *
237     * standard dynamic load exception
238     */
239    rb_eDLError = rb_define_class_under(rb_mDL, "DLError", rb_eStandardError);
240
241    /*
242     * Document-class: DL::DLTypeError
243     *
244     * dynamic load incorrect type exception
245     */
246    rb_eDLTypeError = rb_define_class_under(rb_mDL, "DLTypeError", rb_eDLError);
247
248    /* Document-const: MAX_CALLBACK
249     *
250     * Maximum number of callbacks
251     */
252    rb_define_const(rb_mDL, "MAX_CALLBACK", INT2NUM(MAX_CALLBACK));
253
254    /* Document-const: DLSTACK_SIZE
255     *
256     * Dynamic linker stack size
257     */
258    rb_define_const(rb_mDL, "DLSTACK_SIZE", INT2NUM(DLSTACK_SIZE));
259
260    rb_dl_init_callbacks(rb_mDL);
261
262    /* Document-const: RTLD_GLOBAL
263     *
264     * rtld DL::Handle flag.
265     *
266     * The symbols defined by this library will be made available for symbol
267     * resolution of subsequently loaded libraries.
268     */
269    rb_define_const(rb_mDL, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
270
271    /* Document-const: RTLD_LAZY
272     *
273     * rtld DL::Handle flag.
274     *
275     * Perform lazy binding.  Only resolve symbols as the code that references
276     * them is executed.  If the  symbol is never referenced, then it is never
277     * resolved.  (Lazy binding is only performed for function references;
278     * references to variables are always immediately bound when the library
279     * is loaded.)
280     */
281    rb_define_const(rb_mDL, "RTLD_LAZY",   INT2NUM(RTLD_LAZY));
282
283    /* Document-const: RTLD_NOW
284     *
285     * rtld DL::Handle flag.
286     *
287     * If this value is specified or the environment variable LD_BIND_NOW is
288     * set to a nonempty string, all undefined symbols in the library are
289     * resolved before dlopen() returns.  If this cannot be done an error is
290     * returned.
291     */
292    rb_define_const(rb_mDL, "RTLD_NOW",    INT2NUM(RTLD_NOW));
293
294    /* Document-const: TYPE_VOID
295     *
296     * DL::CFunc type - void
297     */
298    rb_define_const(rb_mDL, "TYPE_VOID",  INT2NUM(DLTYPE_VOID));
299
300    /* Document-const: TYPE_VOIDP
301     *
302     * DL::CFunc type - void*
303     */
304    rb_define_const(rb_mDL, "TYPE_VOIDP",  INT2NUM(DLTYPE_VOIDP));
305
306    /* Document-const: TYPE_CHAR
307     *
308     * DL::CFunc type - char
309     */
310    rb_define_const(rb_mDL, "TYPE_CHAR",  INT2NUM(DLTYPE_CHAR));
311
312    /* Document-const: TYPE_SHORT
313     *
314     * DL::CFunc type - short
315     */
316    rb_define_const(rb_mDL, "TYPE_SHORT",  INT2NUM(DLTYPE_SHORT));
317
318    /* Document-const: TYPE_INT
319     *
320     * DL::CFunc type - int
321     */
322    rb_define_const(rb_mDL, "TYPE_INT",  INT2NUM(DLTYPE_INT));
323
324    /* Document-const: TYPE_LONG
325     *
326     * DL::CFunc type - long
327     */
328    rb_define_const(rb_mDL, "TYPE_LONG",  INT2NUM(DLTYPE_LONG));
329
330#if HAVE_LONG_LONG
331    /* Document-const: TYPE_LONG_LONG
332     *
333     * DL::CFunc type - long long
334     */
335    rb_define_const(rb_mDL, "TYPE_LONG_LONG",  INT2NUM(DLTYPE_LONG_LONG));
336#endif
337
338    /* Document-const: TYPE_FLOAT
339     *
340     * DL::CFunc type - float
341     */
342    rb_define_const(rb_mDL, "TYPE_FLOAT",  INT2NUM(DLTYPE_FLOAT));
343
344    /* Document-const: TYPE_DOUBLE
345     *
346     * DL::CFunc type - double
347     */
348    rb_define_const(rb_mDL, "TYPE_DOUBLE",  INT2NUM(DLTYPE_DOUBLE));
349
350    /* Document-const: TYPE_SIZE_T
351     *
352     * DL::CFunc type - size_t
353     */
354    rb_define_const(rb_mDL, "TYPE_SIZE_T",  INT2NUM(DLTYPE_SIZE_T));
355
356    /* Document-const: TYPE_SSIZE_T
357     *
358     * DL::CFunc type - ssize_t
359     */
360    rb_define_const(rb_mDL, "TYPE_SSIZE_T", INT2NUM(DLTYPE_SSIZE_T));
361
362    /* Document-const: TYPE_PTRDIFF_T
363     *
364     * DL::CFunc type - ptrdiff_t
365     */
366    rb_define_const(rb_mDL, "TYPE_PTRDIFF_T", INT2NUM(DLTYPE_PTRDIFF_T));
367
368    /* Document-const: TYPE_INTPTR_T
369     *
370     * DL::CFunc type - intptr_t
371     */
372    rb_define_const(rb_mDL, "TYPE_INTPTR_T", INT2NUM(DLTYPE_INTPTR_T));
373
374    /* Document-const: TYPE_UINTPTR_T
375     *
376     * DL::CFunc type - uintptr_t
377     */
378    rb_define_const(rb_mDL, "TYPE_UINTPTR_T", INT2NUM(DLTYPE_UINTPTR_T));
379
380    /* Document-const: ALIGN_VOIDP
381     *
382     * The alignment size of a void*
383     */
384    rb_define_const(rb_mDL, "ALIGN_VOIDP", INT2NUM(ALIGN_VOIDP));
385
386    /* Document-const: ALIGN_CHAR
387     *
388     * The alignment size of a char
389     */
390    rb_define_const(rb_mDL, "ALIGN_CHAR",  INT2NUM(ALIGN_CHAR));
391
392    /* Document-const: ALIGN_SHORT
393     *
394     * The alignment size of a short
395     */
396    rb_define_const(rb_mDL, "ALIGN_SHORT", INT2NUM(ALIGN_SHORT));
397
398    /* Document-const: ALIGN_INT
399     *
400     * The alignment size of an int
401     */
402    rb_define_const(rb_mDL, "ALIGN_INT",   INT2NUM(ALIGN_INT));
403
404    /* Document-const: ALIGN_LONG
405     *
406     * The alignment size of a long
407     */
408    rb_define_const(rb_mDL, "ALIGN_LONG",  INT2NUM(ALIGN_LONG));
409
410#if HAVE_LONG_LONG
411    /* Document-const: ALIGN_LONG_LONG
412     *
413     * The alignment size of a long long
414     */
415    rb_define_const(rb_mDL, "ALIGN_LONG_LONG",  INT2NUM(ALIGN_LONG_LONG));
416#endif
417
418    /* Document-const: ALIGN_FLOAT
419     *
420     * The alignment size of a float
421     */
422    rb_define_const(rb_mDL, "ALIGN_FLOAT", INT2NUM(ALIGN_FLOAT));
423
424    /* Document-const: ALIGN_DOUBLE
425     *
426     * The alignment size of a double
427     */
428    rb_define_const(rb_mDL, "ALIGN_DOUBLE",INT2NUM(ALIGN_DOUBLE));
429
430    /* Document-const: ALIGN_SIZE_T
431     *
432     * The alignment size of a size_t
433     */
434    rb_define_const(rb_mDL, "ALIGN_SIZE_T", INT2NUM(ALIGN_OF(size_t)));
435
436    /* Document-const: ALIGN_SSIZE_T
437     *
438     * The alignment size of a ssize_t
439     */
440    rb_define_const(rb_mDL, "ALIGN_SSIZE_T", INT2NUM(ALIGN_OF(size_t))); /* same as size_t */
441
442    /* Document-const: ALIGN_PTRDIFF_T
443     *
444     * The alignment size of a ptrdiff_t
445     */
446    rb_define_const(rb_mDL, "ALIGN_PTRDIFF_T", INT2NUM(ALIGN_OF(ptrdiff_t)));
447
448    /* Document-const: ALIGN_INTPTR_T
449     *
450     * The alignment size of a intptr_t
451     */
452    rb_define_const(rb_mDL, "ALIGN_INTPTR_T", INT2NUM(ALIGN_OF(intptr_t)));
453
454    /* Document-const: ALIGN_UINTPTR_T
455     *
456     * The alignment size of a uintptr_t
457     */
458    rb_define_const(rb_mDL, "ALIGN_UINTPTR_T", INT2NUM(ALIGN_OF(uintptr_t)));
459
460    /* Document-const: SIZEOF_VOIDP
461     *
462     * size of a void*
463     */
464    rb_define_const(rb_mDL, "SIZEOF_VOIDP", INT2NUM(sizeof(void*)));
465
466    /* Document-const: SIZEOF_CHAR
467     *
468     * size of a char
469     */
470    rb_define_const(rb_mDL, "SIZEOF_CHAR",  INT2NUM(sizeof(char)));
471
472    /* Document-const: SIZEOF_SHORT
473     *
474     * size of a short
475     */
476    rb_define_const(rb_mDL, "SIZEOF_SHORT", INT2NUM(sizeof(short)));
477
478    /* Document-const: SIZEOF_INT
479     *
480     * size of an int
481     */
482    rb_define_const(rb_mDL, "SIZEOF_INT",   INT2NUM(sizeof(int)));
483
484    /* Document-const: SIZEOF_LONG
485     *
486     * size of a long
487     */
488    rb_define_const(rb_mDL, "SIZEOF_LONG",  INT2NUM(sizeof(long)));
489
490#if HAVE_LONG_LONG
491    /* Document-const: SIZEOF_LONG_LONG
492     *
493     * size of a long long
494     */
495    rb_define_const(rb_mDL, "SIZEOF_LONG_LONG",  INT2NUM(sizeof(LONG_LONG)));
496#endif
497
498    /* Document-const: SIZEOF_FLOAT
499     *
500     * size of a float
501     */
502    rb_define_const(rb_mDL, "SIZEOF_FLOAT", INT2NUM(sizeof(float)));
503
504    /* Document-const: SIZEOF_DOUBLE
505     *
506     * size of a double
507     */
508    rb_define_const(rb_mDL, "SIZEOF_DOUBLE",INT2NUM(sizeof(double)));
509
510    /* Document-const: SIZEOF_SIZE_T
511     *
512     * size of a size_t
513     */
514    rb_define_const(rb_mDL, "SIZEOF_SIZE_T",  INT2NUM(sizeof(size_t)));
515
516    /* Document-const: SIZEOF_SSIZE_T
517     *
518     * size of a ssize_t
519     */
520    rb_define_const(rb_mDL, "SIZEOF_SSIZE_T",  INT2NUM(sizeof(size_t))); /* same as size_t */
521
522    /* Document-const: SIZEOF_PTRDIFF_T
523     *
524     * size of a ptrdiff_t
525     */
526    rb_define_const(rb_mDL, "SIZEOF_PTRDIFF_T",  INT2NUM(sizeof(ptrdiff_t)));
527
528    /* Document-const: SIZEOF_INTPTR_T
529     *
530     * size of a intptr_t
531     */
532    rb_define_const(rb_mDL, "SIZEOF_INTPTR_T",  INT2NUM(sizeof(intptr_t)));
533
534    /* Document-const: SIZEOF_UINTPTR_T
535     *
536     * size of a uintptr_t
537     */
538    rb_define_const(rb_mDL, "SIZEOF_UINTPTR_T",  INT2NUM(sizeof(uintptr_t)));
539
540    rb_define_module_function(rb_mDL, "dlwrap", rb_dl_value2ptr, 1);
541    rb_define_module_function(rb_mDL, "dlunwrap", rb_dl_ptr2value, 1);
542
543    rb_define_module_function(rb_mDL, "dlopen", rb_dl_dlopen, -1);
544    rb_define_module_function(rb_mDL, "malloc", rb_dl_malloc, 1);
545    rb_define_module_function(rb_mDL, "realloc", rb_dl_realloc, 2);
546    rb_define_module_function(rb_mDL, "free", rb_dl_free, 1);
547
548    /* Document-const: RUBY_FREE
549     *
550     * Address of the ruby_xfree() function
551     */
552    rb_define_const(rb_mDL, "RUBY_FREE", PTR2NUM(ruby_xfree));
553
554    /* Document-const: BUILD_RUBY_PLATFORM
555     *
556     * Platform built against (i.e. "x86_64-linux", etc.)
557     *
558     * See also RUBY_PLATFORM
559     */
560    rb_define_const(rb_mDL, "BUILD_RUBY_PLATFORM", rb_str_new2(RUBY_PLATFORM));
561
562    /* Document-const: BUILD_RUBY_VERSION
563     *
564     * Ruby Version built. (i.e. "1.9.3")
565     *
566     * See also RUBY_VERSION
567     */
568    rb_define_const(rb_mDL, "BUILD_RUBY_VERSION",  rb_str_new2(RUBY_VERSION));
569
570    Init_dlhandle();
571    Init_dlcfunc();
572    Init_dlptr();
573}
574