Use when explaining System V AMD64, ARM AAPCS, RISC-V psABI, stack frames, variadic calls, or FFI register rules. Not for the Rust FFI binding layer: use rust-ffi.
日本語の概要は準備中です。原文の説明を表示しています。
Use when debugging shared library load failures, setting RPATH or RUNPATH, applying soname versioning, writing dlopen plugins, or intercepting with LD_PRELOAD. Not for static archives: use binutils.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
Linux loads shared libraries at startup or on demand through ld.so. This skill builds versioned shared objects, places them where the loader finds them, and diagnoses the failures in between.
| Field | Bound contract |
|---|---|
| Trigger | A build or run fails with cannot open shared object file or symbol lookup error, the task sets RPATH or RUNPATH, versions a library with a soname, writes a dlopen plugin, or interposes a function with LD_PRELOAD. |
| Authority | Reversible local: writes only built .so files, symlinks, source, and the loader cache entries the procedure names (ldconfig needs root); rollback is version control, removing created symlinks, and re-running ldconfig. No remote mutation. |
| Side effect | Local writes to build outputs, symlinks, and /etc/ld.so.cache when ldconfig runs. Environment variables and loader flags stay inside the session. |
| Done | The binary runs against the intended library, proven by ldd resolving every dependency to the intended path and by a clean LD_DEBUG=libs trace or the plugin loading end to end. |
$ORIGIN, RPATH, or RUNPATH.ldconfig.ldconfig maintains. Done when: readelf -d libmylib.so.1.2.3 prints the intended SONAME.gcc -fPIC -c src/mylib.c -o mylib.o
gcc -shared -Wl,-soname,libmylib.so.1 mylib.o -o libmylib.so.1.2.3
ln -s libmylib.so.1.2.3 libmylib.so.1 # loader name
ln -s libmylib.so.1 libmylib.so # linker name for -lmylib
| Bump | When |
|---|---|
| PATCH | Bug fix, ABI unchanged |
| MINOR | Symbols added, backwards compatible: keep the soname, refresh the .so.1 symlink |
| MAJOR | ABI breaks: new soname, old .so.1 files stay installed for existing binaries |
LD_LIBRARY_PATH; RUNPATH after it. Prefer RUNPATH for deployed binaries because environment variables then keep control. -Wl,--enable-new-dtags selects RUNPATH and is the modern linker default. $ORIGIN expands to the directory holding the binary, which makes an install tree relocatable. Done when: readelf -d myapp shows the intended tag and value.gcc main.c -L./lib -lmylib \
-Wl,-rpath,'$ORIGIN/../lib' -Wl,--enable-new-dtags -o myapp
readelf -d myapp | grep -E 'RPATH|RUNPATH'
chrpath -l myapp # show
chrpath -r '/new/path' myapp # rewrite on an existing binary
ld.so searches, in order: DT_RPATH when no DT_RUNPATH exists, then LD_LIBRARY_PATH (ignored for setuid binaries), then DT_RUNPATH, then the /etc/ld.so.cache built by ldconfig, then /lib and /usr/lib. Done when: the failing library is placed at a search step that the deployment controls.LD_DEBUG=libs ./myapp # trace each resolution decision
ldd -v ./myapp # resolved paths plus version requirements
dlopen and dlsym. Clear dlerror() before each call; dlsym reports success through a null return from it, not from the pointer. Link with -ldl on glibc before 2.34; glibc 2.34 and later fold dlfcn into libc. Done when: the plugin loads, its entry point runs, and dlclose releases it.#include <dlfcn.h>
typedef int (*plugin_fn_t)(const char *input);
void load_plugin(const char *path) {
void *handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
if (!handle) {
fprintf(stderr, "dlopen: %s\n", dlerror());
return;
}
dlerror(); // clear any error state before dlsym
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);
}
LD_PRELOAD. The preloaded library is searched first, so its symbols win. RTLD_NEXT finds the next definition in the chain. Done when: running with LD_PRELOAD=... shows the interception and the real call still works.#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
void *malloc(size_t size) {
static void *(*real_malloc)(size_t) = NULL;
if (!real_malloc)
real_malloc = (void *(*)(size_t))dlsym(RTLD_NEXT, "malloc");
void *ptr = real_malloc(size);
fprintf(stderr, "malloc(%zu) = %p\n", size, ptr);
return ptr;
}
gcc -shared -fPIC -o myinterpose.so myinterpose.c -ldl
LD_PRELOAD=./myinterpose.so ./myapp
-fvisibility=hidden and mark the public API visibility("default"), or restrict exports with a version script. Done when: nm -D --defined-only libmylib.so lists the public API and nothing else.__attribute__((visibility("default"))) int public_api(void) { return 42; }
# mylib.map
MYLIB_1.0 {
global:
mylib_init;
mylib_process;
local:
*;
};
gcc -shared -fPIC -fvisibility=hidden -Wl,--version-script=mylib.map \
mylib.c -o libmylib.so
| Error | Cause | Fix |
|---|---|---|
cannot open shared object file | Library outside the search path | Set RUNPATH, extend LD_LIBRARY_PATH, or run ldconfig |
symbol lookup error: undefined symbol | Missing library or version mismatch | Check ldd, fix link order, or add the missing -l |
relocation R_X86_64_32 against .rodata | Non-PIC code in a shared object | Compile that object with -fPIC |
version 'GLIBC_2.xx' not found | Built on a newer glibc than the runtime | Build on the older host or link statically |
| Failure class | Behavior |
|---|---|
ldd shows not found after a correct RUNPATH | The dependency of a dependency needs its own RUNPATH. Trace with LD_DEBUG=libs and fix the library that records the path. |
chrpath refuses a longer path | chrpath cannot grow an existing string. Rebuild with the correct -Wl,-rpath, or use patchelf --set-rpath. |
| Interposition breaks a setuid binary | The loader ignores LD_PRELOAD and LD_LIBRARY_PATH for setuid executables. This is loader policy, not a bug. |
| Plugin symbols clash across libraries | Reopen the plugin with RTLD_LOCAL, or hide symbols per step 7. |
ldconfig not runnable | Root is required. Ship the RUNPATH inside the binary instead, and skip the system-wide registration. |
The running binary or loaded plugin, plus the resolving evidence: ldd output, the SONAME and RUNPATH lines from readelf -d, or the interposition trace. Deep details on search paths, $ORIGIN, and version scripts are in references/ld-rpath-soname.md.
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Use when explaining System V AMD64, ARM AAPCS, RISC-V psABI, stack frames, variadic calls, or FFI register rules. Not for the Rust FFI binding layer: use rust-ffi.
日本語の概要は準備中です。原文の説明を表示しています。
Use when configuring ADC sampling time, DMA-driven ADC, calibration, or DAC channel setup on bare-metal MCUs. Not for the DMA stream itself: use dma-baremetal.
日本語の概要は準備中です。原文の説明を表示しています。
Use when creating AF_XDP sockets, configuring UMEM and XSK rings, writing an XDP redirect program, or choosing copy versus zero-copy mode. Not for full kernel bypass: use dpdk.
日本語の概要は準備中です。原文の説明を表示しています。
Use when a completed session needs an agent-environment retrospective. Not for an engineering retrospective from telemetry: use engineering-retrospective.
日本語の概要は準備中です。原文の説明を表示しています。
Use when a redacted, trimmed agent transcript must be appended to a GitHub PR or issue body, with human approval and preview. Not for automated or model-initiated insertion.
日本語の概要は準備中です。原文の説明を表示しています。
Use when a repo needs agent setup, AGENTS.md added or made lean, CLAUDE.md audited, or agent instructions scored or pruned. Not for remote, credential, publish, deploy, or irreversible changes.
日本語の概要は準備中です。原文の説明を表示しています。