agentleFS
mohitmishra786mohitmishra786/low-level-dev-skills/skills/binaries/dynamic-linking/SKILL.md

dynamic-linking

Skill290 starsChanged 7 months agoMITView on GitHub

Dynamic linking skill for Linux/ELF shared libraries. Use when debugging library loading failures, configuring RPATH vs RUNPATH, understanding soname versioning, using dlopen/dlsym for plugin systems, LD_PRELOAD interposition, or controlling symbol visibility. Activates on queries about shared libraries, dlopen, LD_LIBRARY_PATH, RPATH, soname, LD_PRELOAD, symbol visibility, or "cannot open shared object file" errors.

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 0 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use skills/binaries/elf-inspection to inspect shared library sections and symbols

    line 237
  2. Very specific:

    Use skills/binaries/linkers-lto for linker flags and symbol resolution

    line 238
  3. Very specific:

    Use skills/binaries/binutils for nm, objdump, strip on shared libs

    line 239
  4. Very specific:

    Use skills/compilers/gcc for -fPIC, -shared and related compiler flags

    line 240

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Dynamic Linking

Purpose

Guide agents through Linux dynamic linking: shared library creation, RPATH/RUNPATH configuration, soname versioning, dlopen/dlsym plugin patterns, LD_PRELOAD interposition, and symbol visibility control.

Triggers

  • "Cannot open shared object file: No such file or directory"
  • "How do I set RPATH so my binary finds its shared library?"
  • "How do I use dlopen/dlsym for a plugin system?"
  • "What's the difference between RPATH and RUNPATH?"
  • "How do I use LD_PRELOAD to intercept a function?"
  • "How do I version my shared library with soname?"

Workflow

1. Creating a shared library

# Compile with -fPIC (position-independent code)
gcc -fPIC -c src/mylib.c -o mylib.o

# Link shared library with soname
gcc -shared -Wl,-soname,libmylib.so.1 \
    mylib.o -o libmylib.so.1.2.3

# Create symlinks (standard convention)
ln -s libmylib.so.1.2.3 libmylib.so.1   # soname link (used by ldconfig)
ln -s libmylib.so.1     libmylib.so      # link link (used at compile time)

# Register with ldconfig (system-wide)
sudo cp libmylib.so.1.2.3 /usr/local/lib/
sudo ldconfig

2. Soname versioning convention

libfoo.so.MAJOR.MINOR.PATCH
         │
         └── soname = libfoo.so.MAJOR
Version bumpWhen
PATCHBug fix, ABI unchanged
MINORNew symbols added, backwards compatible
MAJORABI break — existing binaries will break

Inspect soname:

readelf -d libmylib.so.1.2.3 | grep SONAME
objdump -p libmylib.so.1.2.3 | grep SONAME

3. RPATH vs RUNPATH

Both embed a library search path in the binary.

RPATH  → searched BEFORE LD_LIBRARY_PATH
RUNPATH → searched AFTER LD_LIBRARY_PATH (controllable at runtime)

Recommendation: prefer RUNPATH (-Wl,--enable-new-dtags)
                for deployment flexibility.
# Embed RPATH (old default)
gcc main.c -L./lib -lmylib \
    -Wl,-rpath,'$ORIGIN/../lib' -o myapp

# Embed RUNPATH (new default with --enable-new-dtags)
gcc main.c -L./lib -lmylib \
    -Wl,-rpath,'$ORIGIN/../lib' \
    -Wl,--enable-new-dtags -o myapp

# Inspect
readelf -d myapp | grep -E 'RPATH|RUNPATH'
chrpath -l myapp        # show
chrpath -r '/new/path' myapp  # modify existing

$ORIGIN resolves to the directory of the binary at runtime — use it for relocatable installations.

4. Library search order

1. DT_RPATH (if no DT_RUNPATH present)
2. LD_LIBRARY_PATH (env var, ignored for suid binaries)
3. DT_RUNPATH
4. /etc/ld.so.cache  (populated by ldconfig from /etc/ld.so.conf)
5. /lib, /usr/lib

Debug with:

LD_DEBUG=libs ./myapp      # trace library loading decisions
ldd myapp                  # show resolved libraries
ldd -v myapp               # verbose with version requirements

5. dlopen / dlsym plugin pattern

#include <dlfcn.h>

typedef int (*plugin_fn_t)(const char *input);

void load_plugin(const char *path) {
    // RTLD_NOW: resolve all symbols immediately (fail fast)
    // RTLD_LAZY: resolve on first call (default)
    // RTLD_LOCAL: symbols not visible to other loaded libs
    // RTLD_GLOBAL: symbols visible globally
    void *handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
    if (!handle) {
        fprintf(stderr, "dlopen: %s\n", dlerror());
        return;
    }

    // Clear previous errors
    dlerror();

    plugin_fn_t fn = (plugin_fn_t)dlsym(handle, "plugin_run");
    const char *err = dlerror();
    if (err) {
        fprintf(stderr, "dlsym: %s\n", err);
        dlclose(handle);
        return;
    }

    fn("hello");
    dlclose(handle);
}

Link with -ldl:

gcc main.c -ldl -o myapp

6. LD_PRELOAD interposition

LD_PRELOAD loads a library before all others — its symbols override the application's.

// myinterpose.c — intercept malloc
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>

void *malloc(size_t size) {
    static void *(*real_malloc)(size_t) = NULL;
    if (!real_malloc)
        real_malloc = dlsym(RTLD_NEXT, "malloc");  // find next malloc in chain

    void *ptr = real_malloc(size);
    fprintf(stderr, "malloc(%zu) = %p\n", size, ptr);
    return ptr;
}
gcc -shared -fPIC -o myinterpose.so myinterpose.c -ldl

# Apply to any binary
LD_PRELOAD=./myinterpose.so ./myapp
LD_PRELOAD=/path/to/libfaketime.so ./myapp  # time manipulation

7. Symbol visibility control

Limit exported symbols to reduce binary size and avoid clashes:

// Mark default: visible to linker
__attribute__((visibility("default")))
int public_api(void) { return 42; }

// Hidden: internal, not exported
__attribute__((visibility("hidden")))
static int internal_helper(void) { return 0; }

Or use a linker version script:

# mylib.map
MYLIB_1.0 {
    global:
        mylib_init;
        mylib_process;
    local:
        *;          # hide everything else
};
gcc -shared -fPIC -Wl,--version-script=mylib.map \
    -o libmylib.so mylib.c

# Check exported symbols
nm -D --defined-only libmylib.so
objdump -T libmylib.so

Build with -fvisibility=hidden by default and explicitly mark public API:

gcc -shared -fPIC -fvisibility=hidden \
    mylib.c -o libmylib.so

8. Common errors

ErrorCauseFix
cannot open shared object fileLibrary not in search pathSet RPATH, LD_LIBRARY_PATH, or run ldconfig
symbol lookup error: undefined symbolMissing library or wrong versionCheck ldd, add -l flag or fix link order
FATAL: kernel too oldVersion requirement mismatchRebuild against older glibc
relocation R_X86_64_32 against .rodataNon-PIC code in shared libAdd -fPIC to compilation
version 'GLIBC_2.29' not foundBinary built on newer glibcRebuild on older system or use -static

For RPATH, soname, and ld.so configuration details, see references/ld-rpath-soname.md.

Related skills

  • Use skills/binaries/elf-inspection to inspect shared library sections and symbols
  • Use skills/binaries/linkers-lto for linker flags and symbol resolution
  • Use skills/binaries/binutils for nm, objdump, strip on shared libs
  • Use skills/compilers/gcc for -fPIC, -shared and related compiler flags

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.