erl_ddll and dlopen

In Erlang we can use module erl_ddll to dynamically load a native driver
library. The process is very complicated. It is implemented in these files:


Both erl_unix_sys_dll.c and erl_win32_sys_ddll.c have implemented a common
interface which is used in erl_bif_ddll.c. The interface consists of the
following functions:

// Invoke this function before we can use other functions
void erl_sys_ddll_init(void);
// Open a shared object
int erts_sys_ddll_open2(char *full_name, void **handle, ErtsSysDdllError* err);
int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err);
// Find a symbol in the shared object
int erts_sys_ddll_sym2(void *handle, char *func_name, void **function, ErtsSysDdllError* err);
// Load the driver init function, might appear under different names
// depending on object arch...
int erts_sys_ddll_load_driver_init(void *handle, void **function);
int erts_sys_ddll_load_nif_init(void *handle, void **function, ErtsSysDdllError* err);
// Call the driver_init function, whatever it's really called, simple
// on unix...
void *erts_sys_ddll_call_init(void *function);
void *erts_sys_ddll_call_nif_init(void *function);
// Close a shared object
int erts_sys_ddll_close2(void *handle, ErtsSysDdllError* err);
// Return string that describes the (current) error
char *erts_sys_ddll_error(int code);
void erts_sys_ddll_free_error(ErtsSysDdllError* err);

These functions are implemented over the standard dynamic library loading
interface of unix. It defines a small set of API calls including dlopen,
dlclose, dlsym, dlerror, dlinfo, etc. Below is a hello-world example.

// foo.c: the souce of
int num = 10000;

int add(int a, int b)
    return a + b;

char *str = "hello, world!";
// end of foo.c
// test.c: a program that uses
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

typedef int(*IntBinFuncPtr)(int,int);

int main()
    void *handle;
    IntBinFuncPtr add_p;
    int aa = 123, *bb_p, cc;
    char **str_p, *str;

    handle = dlopen("/home/zq2010/", RTLD_LAZY);
    if(handle == NULL) {
        fprintf(stderr, "cannot load\n");

    add_p = (IntBinFuncPtr) dlsym(handle, "add");
    if(add_p == NULL) {
        fprintf(stderr, "cannot find symbol 'add'\n");

    bb_p = (int*) dlsym(handle, "num");
    if(bb_p == NULL) {
        fprintf(stderr, "cannot find symbol 'num'\n");

    cc = (*add_p)(aa, *bb_p);
    printf("cc = %d\n", cc);

    str_p = (char**) dlsym(handle, "str");
    if(str_p == NULL) {
        fprintf(stderr, "cannot find symbol 'str'\n");

    str = *str_p;
    printf("str = %s\n", str);

    if(dlclose(handle) > 0) {
        fprintf(stderr, "cannot close\n");

    return 0;
// end of test.c

## Makefile
all: test foo.o
    gcc -shared -fpic foo.o -o
foo.o: foo.c
    gcc -c foo.c -o foo.o
test.o: test.c
    gcc -c test.c -o test.o
test: test.o
    gcc test.o -ldl -o test
## end of Makefile

And from erl_unix_sys_dll.c and erl_win32_sys_ddll.c We can see that the
interface of dynamic object file loading in unix is similar to that in win32
environment. There is a bijection between them:

    dlopen     <—>    LoadLibrary
    dlsym      <—>    GetProcAddress
    dlclose    <—>    FreeLibrary
    dlerror    <—>    GetLastError*


1.Dynamic Object File Loading

2.Windows vs. Unix: Linking dynamic load modules

3.dlopen(3) – Linux man page

4.Port drivers

5.erl_ddll man page

This entry was posted in erlang and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s