Friday, January 02, 2009

COLA: Shared Libraries

Over the Christmas holidays, I wanted to do some programming with COLA. (See my earlier tutorial on this marvellous piece of software.) This included implementing an object library in Pepsi, which was then to be used from a Jolt program. While doing the former was as easy as programming and compiling it as a shared library, the latter would not work right away and required some twiddling. As I think it's a nice thing to be able to use Pepsi shared libraries in Jolt code, I want to share my solution.

This Does Not Work

The first thing is, as mentioned above, to implement and compile the shared library. Here's an example of a very simple hello world library, providing an object that can be sent the sayHello message:

{ import: Object }
Hello : Object ()
Hello sayHello [ 'Hello, world!' putln ]
This can be compiled to a shared library using idc -s hellolib.st. Looking at some Jolt code for Cairo bindings, I found that a shared library can be loaded and initialised like this:
(define mylibhandle (dlopen "hellolib"))
((_dlsym mylibhandle "__id__init__"))
The code above is supposed to open the library and then invoke its __id__init__() function, which is required to get all the objects it contains set up and make them visible to the "object namespace". It does not work, though, because __id__init__(), for shared libraries, requires a parameter, namely a pointer to the _libid variable representing the "object world". Failing to pass this parameter will lead to a crash when the shared library is initialised using the above code.

Patching libid

The _libid variable is normally not exported from libid, so an API extension is required that makes this feasible. Extending libid is very simple in this case, and only requires modifications in three places.

1. The API extension must be declared in id.h, located in the object/id subdirectory of the main COLA directory. This header defines struct __libid, which declares the libid interface. It is divided into several "categories", one of which is called environment, and I thought this would be the place to put a function returning the pointer to _libid. In that category, there is an entry

void *unused31;
which I replaced with the declaration of my new accessor function:
struct __libid *(*libid_p)(void);

2. Said function obviously needs to be implemented. This takes place in libid.c (found next to id.h):

struct __libid *_libid_libid_p(void) {
return &_libid;
}
It simply returns the address of the _libid variable (which is, by the way, declared static struct __libid _libid).

3. The pointer to this function in the _libid structure must be initialised. This is done by adding the following code to the _libid_init() function:

_libid.libid_p = _libid_libid_p;
That's all. Of course, everything needs to be recompiled. I decided to play safe and did a complete rebuild (using make spotless ; make in the COLA directory).

This Also Does Not Work

Some convenience functionality for loading and initialising a shared library from within Jolt can look like this:

(define libid-p ((dlsym "_libid_libid_p")))
(define dloi (lambda (libname)
(let ((libhandle (dlopen libname)))
((_dlsym libhandle "__id__init__") libid-p)
libhandle)))
The dloi function accepts a library name, opens and initialises the library, and returns the library handle.

With this available, the following should work:

(dloi "hellolib")
(define Hello (import "Hello")) ; import the Hello object
[Hello sayHello]
However, it does not. Initialisation of the library fails because it contains some references to the st80 library that the Pepsi compiler, idc, links against by default. Instead, idc must link against the Jolt object library located in the function/objects directory.

Linking Against The Jolt Object Library

Happily, it is very easy to link against the correct object library. The only requirement is to pass (via the -I command line switch) the directory where the objects are stored to idc:

idc -IMY_PATH_TO/function/objects/ -s hellolib.st
The shared library generated from this will be linked against the Jolt library, and with this library, the Jolt code for printing a hello world message finally works.

No comments:

Post a Comment