Ruby/DL

Ruby/DL provides an interface to the dynamic linker such as dlopen() on UNIX and LoadLibrary() on Windows.

Building and Installing

$ ruby extconf.rb    # to create the Makefile
$ make               # to build the library 'dl.so'
$ make libtest.so    # to build the C library 'libtest.so' for the test script
$ make test          # to run the test script
$ make install       # to install the library
$ make clean         # to remove the created files without Makefile
$ make distclean     # to remove the all created files

Using Ruby/DL

We should usually use DL::Importable module provided by "dl/import.rb". It has high-level functions to access library functions. We use DL::Importable module to extend a module as follows:

require "dl/import"
module LIBC
  extend DL::Importable
end

Now we can use methods dlload and extern in this module. We load the libraries using dlload, and define wrapper methods to library functions using extern respectively as follows:

module LIBC
  extend DL::Importable
  dlload "libc.so.6","libm.so.6"
  extern "int strlen(char*)"
end
# Note that we should not include the module LIBC from some reason.

We can call the library function strlen() using LIBC.strlen. If the first character of given function name is an uppercase, the first character of the defined method name becomes lowercase. We can also construct memory images of structures and unions using functions struct and union which are defined in "dl/struct.rb" as follows:

require "dl/import"
require "dl/struct"
module LIBC
  extend DL::Importable
  Timeval = struct [       # define timeval structure.
    "long tv_sec",
    "long tv_uses",
  ]
end
val = LIBC::Timeval.malloc # allocate memory.

Notice that the above example takes LIBC::Timeval.malloc to allocate memory, rather than LIBC::Timeval.new. It is because DL::Timeval.new is for wrapping an object, PtrData, which has already been created.

We can define a callback using the module function "callback" as follows:

module Foo
  extend DL::Importable
  def my_comp(str1,str2)
    str1 <=> str2
  end
  COMPARE = callback "int my_comp(char*,char*)"
end

where Foo::COMPARE is a Symbol object which invokes the method "my_comp".

DL::Importable module is very useful. However, we sometimes encounter a case that we must directly use low-level functions such as dlsym(). In such case, we would use DL module functions. They are described in next section.

DL module

Module DL consists of three classes, a few module functions and constants. The class Symbol represents the symbol we can call. The class PtrData indicates a memory block such as a pointer in C. An object instantiated from the class Handle keeps a handle to opened library.

Constants

Functions

Handle class

Symbol class

PtrData class

Type specifiers

the prototype consists of the following type specifiers, first element of prototype represents the type of return value, and remaining elements represent the type of each argument.

C : a character (char)
c : a pointer to a character (char *)
H : a short integer (short)
h : a pointer to a short integer (short *)
I : an integer (char, short, int)
i : a pointer to an integer (char *, short *, int *)
L : a long integer (long)
l : a pointer to a long integer (long *)
F : a real (float)
f : a pointer to a real (float *)
D : a real (double)
d : a pointer to a real (double *)
S : an immutable string (const char *)
s : a mutable string (char *)
A : an array (const type[])
a : a mutable array (type[])
P : a pointer (void *)
p : a mutable object (void *)
0 : void function (this must be a first character of the prototype)

the cbtype consists of type specifiers 0, C, I, H, L, F, D, S and P. for example:

DL.callback('IPP'){|ptr1,ptr2|
  str1 = ptr1.ptr.to_s
  str2 = ptr2.ptr.to_s
  str1 <=> str2
}