Free — no signup required

LD_PRELOAD — The Ultimate Hook

2 min read

Injecting Code Before Everything Else

The dynamic loader has a deliberate escape hatch: the LD_PRELOAD environment variable. If set, the loader loads the specified shared library before any other library — including libc. This means if your preloaded library defines a function with the same name as one in libc, your version wins. You have replaced the standard library function for that process — no recompilation, no modification of the target binary, scoped to just that one invocation since the variable only affects processes it's set for.

Why does this work?

The loader resolves symbols in load order. The first definition of a symbol wins. By loading your library first, you ensure your malloc is found before libc's malloc. The original function is not gone — you can call it explicitly using dlsym(RTLD_NEXT, "malloc") to get the next definition in the load order, enabling you to wrap rather than replace.

Legitimate Use Cases:

  1. Memory profiling: Tools like jemalloc and tcmalloc use LD_PRELOAD to replace the default allocator with a faster or more instrumented one — no recompilation needed.

  2. Time manipulation: libfaketime intercepts clock_gettime, gettimeofday, and time to return a fake timestamp. This is invaluable for testing certificate expiry, scheduled jobs, and time-sensitive business logic.

  3. Debugging and tracing: Intercept open() to log every file a program accesses, or intercept connect() to redirect network calls.

  4. Security research: Understand how a binary behaves by intercepting its library calls without modifying the binary itself.

Security Implications:

LD_PRELOAD is intentionally disabled for setuid and setgid binaries — the ones marked with the s permission bit that run with the privileges of their owner (like sudo) rather than the invoking user. If the loader honored LD_PRELOAD for these, any user could preload a malicious library into a privileged program and escalate privileges by having their code run as root. Instead, the loader checks the binary's effective UID/GID against the real UID/GID and silently ignores LD_PRELOAD whenever they differ — the privileged program simply runs normally, unhijacked.

Interview Tip

A common SRE/security interview question: "How would you replace malloc in a running application without recompiling it?" The answer is LD_PRELOAD with a shared library that defines malloc and optionally calls the original via dlsym(RTLD_NEXT, "malloc"). Follow up: "Why can't you do this with a setuid binary?" Because the loader strips LD_PRELOAD for setuid executables to prevent privilege escalation.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free