Menu

- Learn
- Documentation
  - Language Reference
  - Library Reference
  - Command-line Reference
  - Feature Overview
  - Articles
- Downloads
- Packages
- Community
  - Blog
  - Orgs using D
  - Twitter
  - Calendar
  - Forums
  - IRC
  - Community Discord
  - Wiki
  - GitHub
  - Issues
  - Get involved
  - Contributors
  - Foundation
  - Security Team
  - Donate
  - Sponsors
- Resources
  - Tour
  - Books
  - Tutorials
  - Tools
  - Editors
  - IDEs
  - run.dlang.io
  - Visual D
  - Acknowledgments
  - D Style
  - Glossary
  - Sitemap

Search
Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

# Writing Shared Libraries With D On Linux

Contents

1. Statically Linking C
2. Statically Loading a Shared library in C
3. Dynamically Loading a Shared library in C
4. Dynamically Loading a C++ Shared library in C
5. Statically Linking D Program With libphobos2.a
6. Statically Linking D Program With libphobos2.so
7. Creating a D Shared Library and Statically Linking With libphobos2.so
8. Dynamically Loading a C++ DLL From a D Program
9. Dynamically Loading a D DLL From a C Program
10. Dynamically Loading a D DLL From a D Program

For comparison purposes and looking at the mechanics, here's how it's done in C first.

## Statically Linking C

To statically link a C module with a C program,

main.c:

    #include <stdio.h>

    extern int dll();

    int main()
    {
        printf("+main()\n");
        dll();
        printf("-main()\n");
        return 0;
    }

dll.c:

    #include <stdio.h>

    int dll()
    {
        printf("dll()\n");
        return 0;
    }

Build:

    gcc -c dll.c
    gcc -c main.c
    gcc -o main main.o dll.o

    ./main

Results:

    +main()
    dll()
    -main()

## Statically Loading a Shared library in C

Build:

    gcc -c dll.c -fpic
    gcc -shared -o libdll.so dll.o
    gcc -c main.c
    gcc -L/home/username/tmp -Wl,-rpath=/home/username/tmp main.o -o main -ldll

(The source files, run, and results should be identical.)

-rpath is used to embed the path to libdll.so into main, so that it can be found at runtime. Other ways to do it are (but are not discussed further here):

1. Setting the environment variable LD_LIBRARY_PATH.
2. Copy shared library to a standard location and use ldconfig to modify ld.so.

## Dynamically Loading a Shared library in C

Add code to main.c to load the library at runtime:

main.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include <dlfcn.h>

    int main()
    {
        printf("+main()\n");

        void *lh = dlopen("/home/walter/tmp/libdll.so", RTLD_LAZY);
        if (!lh)
        {
            fprintf(stderr, "dlopen error: %s\n", dlerror());
            exit(1);
        }
        printf("libdll.so is loaded\n");

        int (*fn)() = dlsym(lh, "dll");
        char *error = dlerror();
        if (error)
        {
            fprintf(stderr, "dlsym error: %s\n", error);
            exit(1);
        }
        printf("dll() function is found\n");

        (*fn)();

        printf("unloading libdll.so\n");
        dlclose(lh);

        printf("-main()\n");
        return 0;
    }

Build:

    gcc -c dll.c -fpic
    gcc -shared -o libdll.so dll.o
    gcc -c main.c
    gcc -rdynamic main.o -o main -ldl

    ./main

Results:

    +main()
    libdll.so is loaded
    dll() function is found
    dll()
    unloading libdll.so
    -main()

## Dynamically Loading a C++ Shared library in C

The complication here is noting when the shared library's static constructors and destructors are run. Instrument dll.c, which is now C++ code, with those static constructors and destructors:

dll.c:

    #include <stdio.h>

    extern "C" int dll()
    {
        printf("dll()\n");
        return 0;
    }

    struct S
    {
        S() { printf("libdll.so construction\n"); }
        ~S() { printf("~libdll.so destruction\n"); }
    };

    S ss;

Build:

    g++ -c dll.c -fpic
    g++ -shared -o libdll.so dll.o
    gcc -c main.c
    gcc -rdynamic main.o -o main -ldl

    ./main

Results:

    +main()
    libdll.so construction
    libdll.so is loaded
    dll() function is found
    dll()
    unloading libdll.so
    libdll.so destruction
    -main()

## Statically Linking D Program With libphobos2.a

main.d:

    import core.stdc.stdio;
    import dll;

    int main()
    {
        printf("+main()\n");
        dll.dll();
        printf("-main()\n");
        return 0;
    }

dll.d:

    import core.stdc.stdio;

    extern (C) int dll()
    {
        printf("dll()\n");
        return 0;
    }

Build:

    dmd -c dll.d
    dmd -c main.d
    dmd  main.o dll.o

    ./main

Results:

    +main()
    dll()
    -main()

## Statically Linking D Program With libphobos2.so

Build:

    dmd -c dll.d
    dmd -c main.d
    dmd  main.o dll.o -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/library/is

    ./main

## Creating a D Shared Library and Statically Linking With libphobos2.so

When using D shared libraries, the code must be linked with libphobos2.so, not libphobos2.a. This is so that there will be only one instance of the garbage collector.

Build:

    dmd -c dll.d -fPIC
    dmd -oflibdll.so dll.o -shared -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/library/is
    dmd -c main.d
    dmd main.o -L-l:libdll.so -defaultlib=libphobos2.so -L-rpath=.:/path/to/where/shared/library/is

    ./main

## Dynamically Loading a C++ DLL From a D Program

main.d:

    import core.stdc.stdio;
    import core.stdc.stdlib;
    import core.sys.posix.dlfcn;

    extern (C) int dll();

    int main()
    {
        printf("+main()\n");

        void* lh = dlopen("libdll.so", RTLD_LAZY);
        if (!lh)
        {
            fprintf(stderr, "dlopen error: %s\n", dlerror());
            exit(1);
        }
        printf("libdll.so is loaded\n");

        int function() fn = cast(int function())dlsym(lh, "dll");
        char* error = dlerror();
        if (error)
        {
            fprintf(stderr, "dlsym error: %s\n", error);
            exit(1);
        }
        printf("dll() function is found\n");

        fn();

        printf("unloading libdll.so\n");
        dlclose(lh);

        printf("-main()\n");
        return 0;
    }

dll.c:

    #include <stdio.h>

    extern "C" int dll()
    {
        printf("dll()\n");
        return 0;
    }

    struct S
    {
        S() { printf("libdll.so construction\n"); }
        ~S() { printf("libdll.so destruction\n"); }
    };

    S ss;

Build:

    g++ -c dll.c -fpic
    g++ -shared -o libdll.so dll.o
    dmd -c main.d
    dmd main.o -L-ldl -L-rpath=.

    ./main

Results:

    +main()
    libdll.so construction
    libdll.so is loaded
    dll() function is found
    dll()
    unloading libdll.so
    libdll.so destruction
    -main()

## Dynamically Loading a D DLL From a C Program

main.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include <dlfcn.h>

    int main()
    {
        printf("+main()\n");

        void *lh = dlopen("/home/walter/tmp/libdll.so", RTLD_LAZY);
        if (!lh)
        {
            fprintf(stderr, "dlopen error: %s\n", dlerror());
            exit(1);
        }
        printf("libdll.so is loaded\n");

        int (*fn)() = dlsym(lh, "dll");
        char *error = dlerror();
        if (error)
        {
            fprintf(stderr, "dlsym error: %s\n", error);
            exit(1);
        }
        printf("dll() function is found\n");

        (*fn)();

        printf("unloading libdll.so\n");
        dlclose(lh);

        printf("-main()\n");
        return 0;
    }

dll.d:

    import core.stdc.stdio;

    extern (C) int dll()
    {
        printf("dll()\n");
        return 0;
    }

    shared static this()
    {
        printf("libdll.so shared static this\n");
    }

    shared static ~this()
    {
        printf("libdll.so shared static ~this\n");
    }

Build:

    dmd -c dll.d -fPIC
    dmd -oflibdll.so dll.o -shared -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/library/is

    gcc -c main.c
    gcc -rdynamic main.o -o main -ldl

    ./main

Results:

    +main()
    libdll.so shared static this
    libdll.so is loaded
    dll() function is found
    dll()
    unloading libdll.so
    libdll.so shared static ~this
    -main()

Note that libphobos2.so gets automatically dynamically loaded as well.

## Dynamically Loading a D DLL From a D Program

It is important to link the main program with libphobos2.so, not libphobos2.a. Otherwise, the result will be multiple instances of the D runtime conflicting with each other.

main.d:

    import core.stdc.stdio;
    import core.stdc.stdlib;
    import core.sys.posix.dlfcn;

    extern (C) int dll();

    int main()
    {
        printf("+main()\n");

        void* lh = dlopen("libdll.so", RTLD_LAZY);
        if (!lh)
        {
            fprintf(stderr, "dlopen error: %s\n", dlerror());
            exit(1);
        }
        printf("libdll.so is loaded\n");

        int function() fn = cast(int function())dlsym(lh, "dll");
        char* error = dlerror();
        if (error)
        {
            fprintf(stderr, "dlsym error: %s\n", error);
            exit(1);
        }
        printf("dll() function is found\n");

        fn();

        printf("unloading libdll.so\n");
        dlclose(lh);

        printf("-main()\n");
        return 0;
    }

    shared static this() { printf("main shared static this\n"); }

    shared static ~this() { printf("main shared static ~this\n"); }

Build:

    dmd -c dll.d -fPIC
    dmd -oflibdll.so dll.o -shared -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/library/is

    dmd -c main.d
    dmd main.o -L-ldl -defaultlib=libphobos2.so -L-rpath=.:/path/to/where/shared/library/is -map

    ./main

Results:

    main shared static this
    +main()
    libdll.so shared static this
    libdll.so is loaded
    dll() function is found
    dll()
    unloading libdll.so
    libdll.so shared static ~this
    -main()
    main shared static ~this

Note how the DLL's static constructors are called before dlopen() returns, and the static destructors are called by dlclose().

Copyright © 1999-2023 by the D Language Foundation | Page generated by Ddoc on Sun Jan 1 02:20:44 2023