module Fiddle

A libffi wrapper for Ruby.

Description

Fiddle is an extension to translate a foreign function interface (FFI) with ruby.

It wraps libffi, a popular C library which provides a portable interface that allows code written in one language to call code written in another language.

Example

Here we will use Fiddle::Function to wrap floor(3) from libm

require 'fiddle'

libm = Fiddle.dlopen('/lib/libm.so.6')

floor = Fiddle::Function.new(
  libm['floor'],
  [Fiddle::TYPE_DOUBLE],
  Fiddle::TYPE_DOUBLE
)

puts floor.call(3.14159) #=> 3.0

Constants

ALIGN_BOOL

ALIGN_BOOL

The alignment size of a bool

ALIGN_CHAR

ALIGN_CHAR

The alignment size of a char

ALIGN_DOUBLE

ALIGN_DOUBLE

The alignment size of a double

ALIGN_FLOAT

ALIGN_FLOAT

The alignment size of a float

ALIGN_INT

ALIGN_INT

The alignment size of an int

ALIGN_INT16_T

ALIGN_INT16_T

The alignment size of a int16_t

ALIGN_INT32_T

ALIGN_INT32_T

The alignment size of a int32_t

ALIGN_INT64_T

ALIGN_INT64_T

The alignment size of a int64_t

ALIGN_INT8_T

ALIGN_INT8_T

The alignment size of a int8_t

ALIGN_INTPTR_T

ALIGN_INTPTR_T

The alignment size of a intptr_t

ALIGN_LONG

ALIGN_LONG

The alignment size of a long

ALIGN_LONG_LONG

ALIGN_LONG_LONG

The alignment size of a long long

ALIGN_PTRDIFF_T

ALIGN_PTRDIFF_T

The alignment size of a ptrdiff_t

ALIGN_SHORT

ALIGN_SHORT

The alignment size of a short

ALIGN_SIZE_T

ALIGN_SIZE_T

The alignment size of a size_t

ALIGN_SSIZE_T

ALIGN_SSIZE_T

The alignment size of a ssize_t

ALIGN_UINTPTR_T

ALIGN_UINTPTR_T

The alignment size of a uintptr_t

ALIGN_VOIDP

ALIGN_VOIDP

The alignment size of a void*

BUILD_RUBY_PLATFORM

BUILD_RUBY_PLATFORM

Platform built against (i.e. “x86_64-linux”, etc.)

See also RUBY_PLATFORM

NULL
Qfalse

Qfalse

The value of Qfalse

Qnil

Qnil

The value of Qnil

Qtrue

Qtrue

The value of Qtrue

Qundef

Qundef

The value of Qundef

RUBY_FREE

RUBY_FREE

Address of the ruby_xfree() function

SIZEOF_BOOL

SIZEOF_BOOL

size of a bool

SIZEOF_CHAR

SIZEOF_CHAR

size of a char

SIZEOF_CONST_STRING

SIZEOF_CONST_STRING

size of a const char*

SIZEOF_DOUBLE

SIZEOF_DOUBLE

size of a double

SIZEOF_FLOAT

SIZEOF_FLOAT

size of a float

SIZEOF_INT

SIZEOF_INT

size of an int

SIZEOF_INT16_T

SIZEOF_INT16_T

size of a int16_t

SIZEOF_INT32_T

SIZEOF_INT32_T

size of a int32_t

SIZEOF_INT64_T

SIZEOF_INT64_T

size of a int64_t

SIZEOF_INT8_T

SIZEOF_INT8_T

size of a int8_t

SIZEOF_INTPTR_T

SIZEOF_INTPTR_T

size of a intptr_t

SIZEOF_LONG

SIZEOF_LONG

size of a long

SIZEOF_LONG_LONG

SIZEOF_LONG_LONG

size of a long long

SIZEOF_PTRDIFF_T

SIZEOF_PTRDIFF_T

size of a ptrdiff_t

SIZEOF_SHORT

SIZEOF_SHORT

size of a short

SIZEOF_SIZE_T

SIZEOF_SIZE_T

size of a size_t

SIZEOF_SSIZE_T

SIZEOF_SSIZE_T

size of a ssize_t

SIZEOF_UCHAR

SIZEOF_UCHAR

size of a unsigned char

SIZEOF_UINT

SIZEOF_UINT

size of an unsigned int

SIZEOF_UINT16_T

SIZEOF_UINT16_T

size of a uint16_t

SIZEOF_UINT32_T

SIZEOF_UINT32_T

size of a uint32_t

SIZEOF_UINT64_T

SIZEOF_UINT64_T

size of a uint64_t

SIZEOF_UINT8_T

SIZEOF_UINT8_T

size of a uint8_t

SIZEOF_UINTPTR_T

SIZEOF_UINTPTR_T

size of a uintptr_t

SIZEOF_ULONG

SIZEOF_ULONG

size of a unsigned long

SIZEOF_ULONG_LONG

SIZEOF_ULONG_LONG

size of a unsigned long long

SIZEOF_USHORT

SIZEOF_USHORT

size of a unsigned short

SIZEOF_VOIDP

SIZEOF_VOIDP

size of a void*

VERSION
WINDOWS

Returns a boolean regarding whether the host is WIN32

Public Class Methods

dlopen(library) → Fiddle::Handle click to toggle source

Creates a new handler that opens library, and returns an instance of Fiddle::Handle.

If nil is given for the library, Fiddle::Handle::DEFAULT is used, which is the equivalent to RTLD_DEFAULT. See man 3 dlopen for more.

lib = Fiddle.dlopen(nil)

The default is dependent on OS, and provide a handle for all libraries already loaded. For example, in most cases you can use this to access libc functions, or ruby functions like rb_str_new.

See Fiddle::Handle.new for more.

# File fiddle/lib/fiddle.rb, line 91
def dlopen library
  begin
    Fiddle::Handle.new(library)
  rescue DLError => error
    case RUBY_PLATFORM
    when /linux/
      case error.message
      when /\A(\/.+?): (?:invalid ELF header|file too short)/
        # This may be a linker script:
        # https://sourceware.org/binutils/docs/ld.html#Scripts
        path = $1
      else
        raise
      end
    else
      raise
    end

    File.open(path) do |input|
      input.each_line do |line|
        case line
        when /\A\s*(?:INPUT|GROUP)\s*\(\s*([^\s,\)]+)/
          # TODO: Should we support multiple files?
          first_input = $1
          if first_input.start_with?("-l")
            first_input = "lib#{first_input[2..-1]}.so"
          end
          return dlopen(first_input)
        end
      end
    end

    # Not found
    raise
  end
end
dlunwrap(addr) click to toggle source

Returns the Ruby object stored at the memory address addr

Example:

x = Object.new
# => #<Object:0x0000000107c7d870>
Fiddle.dlwrap(x)
# => 4425504880
Fiddle.dlunwrap(_)
# => #<Object:0x0000000107c7d870>
VALUE
rb_fiddle_ptr2value(VALUE self, VALUE addr)
{
    return (VALUE)NUM2PTR(addr);
}
dlwrap(val) click to toggle source

Returns the memory address of the Ruby object stored at val

Example:

x = Object.new
# => #<Object:0x0000000107c7d870>
Fiddle.dlwrap(x)
# => 4425504880

In the case val is not a heap allocated object, this method will return the tagged pointer value.

Example:

Fiddle.dlwrap(123)
# => 247
static VALUE
rb_fiddle_value2ptr(VALUE self, VALUE val)
{
    return PTR2NUM((void*)val);
}
free(addr) click to toggle source

Free the memory at address addr

VALUE
rb_fiddle_free(VALUE self, VALUE addr)
{
    void *ptr = NUM2PTR(addr);

    ruby_xfree(ptr);
    return Qnil;
}
last_error() click to toggle source

Returns the last Error of the current executing Thread or nil if none

# File fiddle/lib/fiddle.rb, line 57
def self.last_error
  if RUBY_ENGINE == 'jruby'
    errno = FFI.errno
    errno == 0 ? nil : errno
  else
    Thread.current[:__FIDDLE_LAST_ERROR__]
  end
end
last_error=(error) click to toggle source

Sets the last Error of the current executing Thread to error

# File fiddle/lib/fiddle.rb, line 67
def self.last_error= error
  if RUBY_ENGINE == 'jruby'
    FFI.errno = error || 0
  else
    Thread.current[:__DL2_LAST_ERROR__] = error
    Thread.current[:__FIDDLE_LAST_ERROR__] = error
  end
end
malloc(size) click to toggle source

Allocate size bytes of memory and return the integer memory address for the allocated memory.

static VALUE
rb_fiddle_malloc(VALUE self, VALUE size)
{
    void *ptr;
    ptr = (void*)ruby_xcalloc(1, NUM2SIZET(size));
    return PTR2NUM(ptr);
}
realloc(addr, size) click to toggle source

Change the size of the memory allocated at the memory location addr to size bytes. Returns the memory address of the reallocated memory, which may be different than the address passed in.

static VALUE
rb_fiddle_realloc(VALUE self, VALUE addr, VALUE size)
{
    void *ptr = NUM2PTR(addr);

    ptr = (void*)ruby_xrealloc(ptr, NUM2SIZET(size));
    return PTR2NUM(ptr);
}
win32_last_error() click to toggle source

Returns the last win32 Error of the current executing Thread or nil if none

# File fiddle/lib/fiddle.rb, line 16
def self.win32_last_error
  if RUBY_ENGINE == 'jruby'
    errno = FFI.errno
    errno == 0 ? nil : errno
  else
    Thread.current[:__FIDDLE_WIN32_LAST_ERROR__]
  end
end
win32_last_error=(error) click to toggle source

Sets the last win32 Error of the current executing Thread to error

# File fiddle/lib/fiddle.rb, line 26
def self.win32_last_error= error
  if RUBY_ENGINE == 'jruby'
    FFI.errno = error || 0
  else
    Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error
  end
end
win32_last_socket_error() click to toggle source

Returns the last win32 socket Error of the current executing Thread or nil if none

# File fiddle/lib/fiddle.rb, line 36
def self.win32_last_socket_error
  if RUBY_ENGINE == 'jruby'
    errno = FFI.errno
    errno == 0 ? nil : errno
  else
    Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__]
  end
end
win32_last_socket_error=(error) click to toggle source

Sets the last win32 socket Error of the current executing Thread to error

# File fiddle/lib/fiddle.rb, line 47
def self.win32_last_socket_error= error
  if RUBY_ENGINE == 'jruby'
    FFI.errno = error || 0
  else
    Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__] = error
  end
end

Private Instance Methods

dlopen(library) → Fiddle::Handle click to toggle source

Creates a new handler that opens library, and returns an instance of Fiddle::Handle.

If nil is given for the library, Fiddle::Handle::DEFAULT is used, which is the equivalent to RTLD_DEFAULT. See man 3 dlopen for more.

lib = Fiddle.dlopen(nil)

The default is dependent on OS, and provide a handle for all libraries already loaded. For example, in most cases you can use this to access libc functions, or ruby functions like rb_str_new.

See Fiddle::Handle.new for more.

# File fiddle/lib/fiddle.rb, line 91
def dlopen library
  begin
    Fiddle::Handle.new(library)
  rescue DLError => error
    case RUBY_PLATFORM
    when /linux/
      case error.message
      when /\A(\/.+?): (?:invalid ELF header|file too short)/
        # This may be a linker script:
        # https://sourceware.org/binutils/docs/ld.html#Scripts
        path = $1
      else
        raise
      end
    else
      raise
    end

    File.open(path) do |input|
      input.each_line do |line|
        case line
        when /\A\s*(?:INPUT|GROUP)\s*\(\s*([^\s,\)]+)/
          # TODO: Should we support multiple files?
          first_input = $1
          if first_input.start_with?("-l")
            first_input = "lib#{first_input[2..-1]}.so"
          end
          return dlopen(first_input)
        end
      end
    end

    # Not found
    raise
  end
end