An MCP (Model Context Protocol) server that exposes Linux dynamic-linker
controls — LD_PRELOAD, LD_LIBRARY_PATH, LD_AUDIT, LD_DEBUG,
/etc/ld.so.preload, ldconfig, ldd, readelf, and nm — as callable
tools, plus helpers to generate and compile preload shared objects.
Intended for debugging linker issues, library-interposition development, observability work, and authorized security research.
- Linux (uses
/procand glibc-specificLD_*behavior) - Python ≥ 3.10
binutils(readelf,nm),glibcutilities (ldd,ldconfig),gccfor compilation tools
python3 -m venv .venv
.venv/bin/pip install -e .This installs a console script ld-preload-mcp that speaks MCP over stdio.
claude mcp add ld-preload -- /absolute/path/to/.venv/bin/ld-preload-mcp{
"mcpServers": {
"ld-preload": {
"command": "/absolute/path/to/.venv/bin/ld-preload-mcp"
}
}
}| Tool | Purpose |
|---|---|
run_with_preload |
Launch a command with any combination of LD_PRELOAD, LD_LIBRARY_PATH, LD_AUDIT, LD_DEBUG, LD_BIND_NOW, plus arbitrary extra env vars. Validates every preload/audit path exists before exec. |
trace_library_loads |
Run a command under LD_DEBUG=<categories> (libs, symbols, bindings, reloc, files, versions, statistics, all, or help) and capture the linker's diagnostic stream. |
get_dynamic_linker_env |
Report which LD_* variables are set in the server's own environment. |
| Tool | Purpose |
|---|---|
inspect_process |
From /proc/<pid>: exe path, cmdline, selected status fields, and the LD_* entries from environ. Deliberately does not return the full environment. |
list_loaded_libraries |
Unique file-backed shared objects mapped into a PID via /proc/<pid>/maps. |
| Tool | Purpose |
|---|---|
ldd_binary |
Default: readelf -d (safe, does not execute). use_unsafe=true: real ldd, which executes the binary. |
readelf_dynamic |
Dump .dynamic and program headers — DT_NEEDED, RPATH, RUNPATH, SONAME, PT_INTERP, etc. |
list_exported_symbols |
nm -D against a shared object; optional --defined-only and C++ demangling. |
| Tool | Purpose |
|---|---|
ldconfig_list |
Filtered view of ldconfig -p. |
resolve_library |
Resolve a soname against extra_search_paths, LD_LIBRARY_PATH, the standard default directories, and the ldconfig cache. Reports every hit with its source. |
| Tool | Purpose |
|---|---|
get_ld_so_preload |
Read /etc/ld.so.preload. Absence is reported, not treated as error. |
set_ld_so_preload |
Write /etc/ld.so.preload. Refuses unless confirm=true and requires root. |
| Tool | Purpose |
|---|---|
generate_preload_template |
Emit a C skeleton that wraps the named symbols with dlsym(RTLD_NEXT) pass-through stubs. Prototypes are generic void *name(void) — fix them to match the real signatures before compiling. |
compile_preload_library |
gcc -shared -fPIC -o <out.so> <src.c> -ldl plus any extra CFLAGS/LDFLAGS you pass. |
Trace every call to a custom symbol in a target binary:
generate_preload_template(functions=["my_unused_hook"], output_path="/tmp/hook.c")
compile_preload_library(source_path="/tmp/hook.c", output_path="/tmp/libhook.so")
run_with_preload(command="/bin/true", preload_libs=["/tmp/libhook.so"])
# → stderr: "[preload] loaded into pid=<n>"
Diagnose why a binary isn't finding a library:
readelf_dynamic(path="/path/to/app") # inspect RPATH / RUNPATH / NEEDED
resolve_library(libname="libfoo.so.1") # where would the loader find it?
trace_library_loads(command="/path/to/app", categories="libs")
Audit a running process for unexpected interposition:
inspect_process(pid=1234) # any LD_PRELOAD / LD_AUDIT set?
list_loaded_libraries(pid=1234) # anything outside /usr and /lib?
get_ld_so_preload() # system-wide preload present?
run_with_preloadvalidates everypreload_libsandld_auditpath exists and is a regular file before callingexecve.ldd_binarydefaults toreadelf, which does not execute the target. Running the binary requires an explicituse_unsafe=true.inspect_processreturns only dynamic-linker env vars, not the full environ, to avoid leaking secrets from other processes.set_ld_so_preloadrefuses unlessconfirm=trueis passed and the server runs as root. A bad entry in/etc/ld.so.preloadcan make the system unbootable — treat writes as a privileged administrative action.LD_PRELOADandLD_AUDITare ignored by the loader for setuid binaries regardless of what this server sets.
pyproject.toml
src/ld_preload_mcp/
__init__.py
server.py # all 14 tools, FastMCP-based
No explicit license yet — add one before external distribution.