C++
dlopen
dynamic libraries
shared libraries
Unix

C How to use dlopen in c?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

dlopen lets a Unix-like program load a shared library at runtime instead of linking it at build time. That is useful for plugin systems, optional features, and tools that need to discover symbols dynamically, but it only works reliably if you handle symbol lookup and error reporting carefully.

The Core Functions

The dynamic loading API usually involves four functions from dlfcn.h:

  • 'dlopen to load the shared object'
  • 'dlsym to find a symbol'
  • 'dlerror to read loader errors'
  • 'dlclose to release the handle'

The usual include is:

c
#include <dlfcn.h>

On Linux you typically link with -ldl.

Build a Small Shared Library

First create a shared library that exports a function.

c
1/* mathlib.c */
2int add(int a, int b) {
3    return a + b;
4}

Build it:

bash
cc -fPIC -shared -o libmathlib.so mathlib.c

-fPIC generates position-independent code, and -shared produces a shared object instead of a normal executable.

Load the Library and Call a Function

Now write a program that opens the library and resolves the symbol.

c
1#include <dlfcn.h>
2#include <stdio.h>
3#include <stdlib.h>
4
5typedef int (*add_fn)(int, int);
6
7int main(void) {
8    void *handle = dlopen("./libmathlib.so", RTLD_NOW);
9    if (!handle) {
10        fprintf(stderr, "dlopen failed: %s\n", dlerror());
11        return 1;
12    }
13
14    dlerror(); /* clear any old error */
15    add_fn add = (add_fn)dlsym(handle, "add");
16    const char *error = dlerror();
17    if (error) {
18        fprintf(stderr, "dlsym failed: %s\n", error);
19        dlclose(handle);
20        return 1;
21    }
22
23    printf("%d\n", add(2, 3));
24    dlclose(handle);
25    return 0;
26}

Compile the main program:

bash
cc -o main main.c -ldl

If both files are in the current directory, running ./main should print 5.

Why the Function Pointer Cast Matters

dlsym returns void *, so you need to cast it to the correct function-pointer type before calling it. That type must match the real function signature exactly. If the signature is wrong, the call may compile but behave incorrectly at runtime.

This is why defining a typedef such as add_fn is a good habit. It makes the intended signature explicit and easier to review.

RTLD_LAZY Versus RTLD_NOW

The second argument to dlopen controls symbol resolution behavior.

  • 'RTLD_NOW resolves symbols immediately'
  • 'RTLD_LAZY defers some resolution until the symbol is used'

RTLD_NOW is often easier for debugging because failures happen at load time instead of later during execution.

Use Cases That Actually Justify dlopen

dlopen is useful when:

  • plugins should be discovered at runtime
  • a dependency is optional
  • the program supports extension points without recompilation
  • different implementations may be loaded based on configuration

If the dependency is mandatory and fixed, normal linking is usually simpler and safer.

Error Handling Rules

There are two loader rules worth keeping strict:

  1. Check the return value of dlopen.
  2. Clear dlerror before calling dlsym, then check it after.

That second rule matters because dlsym itself does not return errors through an out parameter. The loader keeps the last error message in internal state.

Common Pitfalls

The most common mistake is forgetting -ldl when linking the main executable. On many Linux systems the code compiles but fails at link time without it.

Another issue is using the wrong function-pointer type for a resolved symbol. That can lead to crashes or subtle undefined behavior.

A third problem is assuming the loader will find the library automatically. If the library path is not on the runtime search path, use an absolute path, a relative path such as ./libmathlib.so, or configure the environment correctly.

Summary

  • Use dlopen to load shared libraries at runtime on Unix-like systems.
  • Resolve functions with dlsym and cast them to the correct function-pointer type.
  • Check dlerror carefully after symbol lookup.
  • Link the executable with -ldl on Linux.
  • Prefer normal linking unless you actually need runtime loading behavior.

Course illustration
Course illustration

All Rights Reserved.