Introduction to fiddle.gem in 2022 #1

Uchio KONDO
5 min readFeb 18, 2022
Image from pixabay: https://pixabay.com/images/id-516023/

fiddle.gem(fiddle) is a useful library for us Rubyists, but the concepts involved are a bit complex and there seems to be little information about it.

In this article, I will try to explain the basics of fiddle and FFI.

What is fiddle? And what is the FFI? I’m going to make these topics clear. fiddle is a gem for FFI (Foreign Function Interface) in Ruby. BTW there is a gem called “ffi”, so some people use that.

However, since fiddle is now a standard library, it is included in your Ruby installation and is maintained by the Ruby core team. It’s better to choose fiddle to achieve FFI implementations, in my opinion.

Now, let’s talk about what FFI is. It is often explained as “a mechanism for using functions defined in one programming language from another”, but this expression may not be clear to you (especially in case you are not familiar with the C language).

Writing small C code examples will make you understand FFIs a bit more clearly.

Let us write some Cs, and create a “shared library”. Don’t worry, you only need to know the very basic this time….

The recommended environment to run codes on this article is Linux, Ubuntu 20.04 or later, and although I have tried it in a VM via Vagrant, I think it will run fine in Docker.

First, I wrote a function to display “Hello, world” in English or Spanish. If the argument is_spanish is non-zero, it will be in Spanish. Also, the value of the argument is used as the return value.

#include <stdio.h>

int sayhello(int is_spanish) {
if (is_spanish == 0) {
printf("Hello, world\n");
} else {
printf("Hola, mundo\n");
}
return is_spanish;
}

We will make this a “shared library”. First, install a compiler called gcc. And then executing the following command.

$ sudo apt install build-essential
$ gcc -shared -fPIC -o libhello.so hello.c

A file named libhello.so has been created. If you look up the file command, you will see that it is a “shared object” of “ELF”.

$ file libhello.so 
libhello.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked,
BuildID[sha1]=fb5672164962190d34e40742522b51796ee8d855, not stripped

Since this is a shared library, the function in this file cannot be used unless it is called from another program.

In the case of the C language, below is the example how you call it.

int sayhello(int is_spanish);

int main(int argc, char** argv) {
if (argc > 1 && *argv[1] == 's') {
sayhello(1);
} else {
sayhello(0);
}
return 0;
}

First you declare only the definition of the function you want to call, then you call the sayhello() in the main function. The C program looks at the first character of the first command line argument and calls sayhello(1)if it starts with s, otherwise it calls sayhello(0).

OK gcc, build it.

$ gcc main.c -L . -l hello -o hello.bin
$ file ./hello.bin
./hello.bin: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=66176d0e95e756416e84690375d9fa98a07bf071, for GNU/Linux 3.2.0, not stripped

There are some “good luck charms” for this comannd line… Set -L .to specify the directory where the shared library is located, and -l hello to specify the name of the shared library that contains the function, without lib and without the extension. That’s the convention.

In this case, we will end up with an executable called hello.bin.

For further execution, we need to tell hello.bin the directory where the shared library is located, so we give it an environment variable LD_LIBRARY_PATH .

$ export LD_LIBRARY_PATH=`pwd`

Finally, let’s run it:

$ ./hello.bin 
Hello, world
$ ./hello.bin spanish
Hola, mundo

You now have a bilingual binary that says “Hello world” in English when there are no command line arguments, and in Spanish when there are command line arguments starting with “s”.

As a matter of fact, many programs consist of calling functions stored in shared libraries; when you create a program in C or other languages, you need to specify which shared library and function to use, and embed the information of that library in your program. This is sometimes called “(dynamic) linking”.

You can find out what your program is dynamically linking with the command ldd.

$ ldd ./hello.bin 
linux-vdso.so.1 (0x00007ffe0d54f000)
libhello.so => /home/vagrant/hello/libhello.so (0x00007f2b01d4d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2b01b57000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2b01d59000)

It is all OK using C. But what should we do when we want to call functions in a shared library at runtime, without linking them in advance? Ruby’s fiddle and Python’s ctypes are libraries that satisfy such requirements, and the term “FFI” is often used to refer to such functionalities in scripting languages.

So, let’s call the it sayhello(int); dynamically from Ruby.

Here is the version of Ruby to use.

$ ruby -v
ruby 3.0.3p157 (2021-11-24 revision 3fb7d2cadc) [x86_64-linux]

You can use fiddle.gem in the following code.

require 'fiddle/import'

module Hello
extend Fiddle::Importer

# With `dlload`, specify the file name of the shared library
dlload './libadcal.so'
# Use `extern` to specify the function to call and its arguments
# fiddle will convert Ruby classes and C types to each other.
extern 'int sayhello(int is_spanish);'
end

arg = ARGV[0]

if arg && arg.start_with?('s')
Hello.sayhello(1)
else
Hello.sayhello(0)
end

Run this Ruby script and make sure that the result is the same as the C program we have seen before. Note that we have not “compiled” any additional C source code.

$ ruby hello.rb 
Hello, world
$ ruby hello.rb spanish
Hola, mundo

In additon, note that the return value of the C function can also be treated as a Ruby object (Integer in this case). You can try to display the return value with the Kernel#p method.

if arg && arg.start_with?('s')
p Hello.sayhello(1)
else
p Hello.sayhello(0)
end

Result is:

$ ruby hello.rb spanish
Hola, mundo
1

As an advanced topic, I will show you how to dynamically call functions from other existing libraries from Ruby, as well as codes for more realistic and complex cases.

Please wait for the next one!

--

--

Uchio KONDO

A Rubyist, from Fukuoka, Japan. Author of “Introduction to Systems Programming using mruby” (Japanese version only), Interest: Containers, eBPF.