Automatically executed functions when loading shared libraries

You can define an on-load function for a linux library using the .init mechanism. This is the same as specifying the load-time entry point for a binary (e.g. using something other than main as the entry point for a program).

When linking using ld directly you use the:

-init <function name>

or if you’re using cc/gcc to link, you use:

-Wl,-init,<function name>

This is at it’s most simple level.

Edit
For destructors/finalizers, you use the .fini mechanism. This operates in the same manner as the init option, and you use:

-fini <function name>

when invoking ld. Availability is limited to the -init option on the Mac OSX platform.

You should also be able to use the __attribute__((constructor)) syntax for gcc:

static void con() __attribute__((constructor));

void con() {
    printf("I'm a constructor\n");
}

Which is probably a more portable way rather than screwing with the linker options. All constructors should be invoked at load-time, but don’t depend on the order of their initialization, that leads to insanity and unreproducible bugs that cost time and effort to debug.

Edit 2 The use of the __attribute__((constructor))/__attribute__((destructor)) semantic is the most preferable mechanism for the C/C++ programming language.

For the D programming language you should really use the static module constructor/destructor:

static this() {
    printf("static this for mymodule\n");
}
static ~this() {
    printf("static ~this for mymodule\n");
}

Or the static class constructor:

class Foo {
    static this() {
        printf("static this for Foo\n");
    }
}

This is strongly hinted at in the writing win32 DLLS and in the language specification relating to static constructors/destructors.

Edit 3 You will need to link in a .o that exports constructor/destructor routines, that will allow the use of the static initializers. As all it should do is call Runtime.initialize(), this actually invokes all the static constructors/destructors in the D code.

Stub d code for the initializer (in a file called myshared.d):

import core.runtime;

extern (C) {
    void attach();
    void detach();
}

export void attach() {
    Runtime.initialize();
}

export void detach() {
    Runtime.terminate();
}

Create the .o for this stub:

 dmd -m32 -c myshared.d

Check the names of the attach/detach functions:

nm myshared.o

Shows (among other output):

0000001c S _D8myshared6attachFZv
00000034 S _D8myshared6detachFZv

sample .c code for invoking this (called export.c in this case), we reference the names of the exported routines from the my shared.o file:

extern void D8myshared6attachFZv(void);
extern void D8myshared6detachFZv(void);

void __attach(void) __attribute__((constructor));
void __detach(void) __attribute__((destructor));

void __attach(void)
{
    D8myshared6attachFZv();
}

void __detach(void)
{
    D8myshared6detachFZv();
}

Note that the extern void references need to use the mangled name of the exported function. These must match or the code will not link.

compile the C code using:

gcc -m32 -c export.c

link the .c.o and the .d.o files together using:

cc -o libmyshared.dylib -m32 -shared myshared.o export.o -lphobos2

Assuming that the phobos2 library is in your standard linker search path. The smatterings of -m32 options for the compiler and linker are because the version of the D compiler that I built locally only supported 32bit.

This produces a .dylib that can be linked to. It seems to work based on the limited testing I performed. It looks like support for shared objects/dynamic libraries is very limited, so there is a good chance that there will be another hurdle to overcome.

Leave a Comment

tech