Modify program behavior with LD_PRELOAD
Customizing Program Behavior
A typical scenario for using LD_PRELOAD
would be to change undesirable behavior in one of your applications, where the application's source code is too complex to search for the right location or is simply not available. As an alternative, you can use strace
[2] or ltrace
[3] (see the "strace and ltrace" box) to check the order in which the application tries to open files or network connections.
strace and ltrace
The strace
and ltrace
command-line tools from the strace and ltrace packages (which are preinstalled on most systems) let you monitor a process as it executes, logging specific events. The strace
tool takes care of system calls such as requests to the Linux kernel to perform a kernel task for the process (opening files, reading from them, closing files).
To log all system calls of a program prog
, you would need to launch it with
strace -o prog.log prog
and any other call parameters that prog
needs. The log is written to prog.log
. However, the list created in this way turns out to be very confusing because a typical program executes a large number of system calls in a short time. It makes more sense to log only certain calls with the -e trace=
option. For example, the call from the first line of Listing 5 would only show the calls to open
, openat
, and close
.
If you omit the -o
switch, the output is displayed on the terminal. However, this is only useful for programs with very little output of their own or for graphical applications, because otherwise the program and strace
's output will be combined.
The ltrace
tool does for library calls what strace
does for system calls. For example, the call from the second line of Listing 5 displays all function calls to open()
, openat()
, and close()
. The information is similar to that of strace
, but this time the focus is on library functions (such as open()
), which run system calls of the same name (open
, without the parentheses).
Listing 5
strace and ltrace
$ strace -o prog.log -e trace=open,openat,close prog $ ltrace -x open+openat+close prog
It may turn out that a program sources information that it can't handle from a global configuration file. However, you do not want to change the global file because other applications access it and need this information.
In this case, it would be useful to point the problematic program to another configuration file, which you would populate with suitable content to let the program run correctly. To do this, you need to manipulate the open()
function so that it responds to any attempt to open a specific file by opening the alternate file. Listing 6 shows how to redirect all attempts to open the /etc/fstab
file in /tmp/fstab.test
.
Listing 6
openother.c
#define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> #include <limits.h> #include <stdlib.h> #include <string.h> #define SEARCH_PATH "/etc/fstab" #define REPLACE_PATH "/tmp/fstab.test" int (*true_open)(const char *pathname, int flags, va_list mode); int open (const char *pathname, int flags, va_list mode) { true_open = dlsym (RTLD_NEXT, "open"); char *longpath = realpath (pathname, NULL); if (!strncmp (longpath, SEARCH_PATH, PATH_MAX)) pathname = REPLACE_PATH; int fd = true_open (pathname, flags, mode); free (longpath); return fd; }
The new version of open()
first creates an absolute path specification for the specified file name with realpath()
. You can also call open()
with relative paths, which can then look very different depending on the working directory. The change ensures that only a single path is checked. The strncmp()
function then compares the path with a search term (in my example, /etc/fstab
) and replaces it with the name of the alternative file (/tmp/fstab.test
) if there is a match.
After that, it continues as usual with true_open()
opening the file. The call to free()
at the end of Listing 6 is necessary because longpath()
has reserved memory for storing the path specification. You need to release this before leaving the function. Again, the compile process is a one-liner (Listing 7, line 1).
Listing 7
Tests
01 $ gcc openother.c -o openother.so -fPIC -shared -ldl 02 $ echo "This is not /etc/fstab" > /tmp/fstab.test 03 $ LD_PRELOAD=$PWD/openother.so cat /etc/fstab 04 This is not /etc/fstab
Now when you create a file with the call from line 2 and access /etc/fstab
with cat
and the library enabled via LD_PRELOAD
, you will be opening the file created in /tmp
instead (line 4).
Prohibiting Access
Instead of redirecting file access, the solution to the problem could be to completely prevent access. To do this, you would abort without calling the original function in certain cases, set the errno
global error variable, and return an exit code of -1
.
Listing 8 shows the code that terminates open()
upon accessing the /etc/fstab
file with an error code of ENOENT
and an exit code of -1
. The attempt to open the file then causes the program to abort as desired (Listing 9).
Listing 8
dontopen.c
#define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> #include <limits.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define SEARCH_PATH "/etc/fstab" int (*true_open)(const char *pathname, int flags, va_list mode); int open (const char *pathname, int flags, va_list mode) { true_open = dlsym (RTLD_NEXT, "open"); char *longpath = realpath (pathname, NULL); int check = strncmp (longpath, SEARCH_PATH, PATH_MAX); free (longpath); if (!check) { errno = ENOENT; // file not found return -1; } return true_open (pathname, flags, mode); }
Listing 9
Program Abort
$ LD_PRELOAD=$PWD/dontopen.so cat /etc/fstab cat: /etc/fstab: File or directory not found
ENOENT
stands for "File or directory not found." The definitions for error codes can be found in errno-base.h
and errno.h
in the /usr/include/asm-generic/
folder; you can also select other codes.
If you test access blocking with other programs, you may notice that some editors like Vim, mcedit, or Nano are also blocked, but gedit can open the file. An analysis with strace
shows that gedit uses openat
instead of open()
to open files. Consequently, you will need an alternative implementation for gedit.
More Examples
You can find more potential uses on GitHub [4]. For example, libfaketime
lets you fool processes into believing that the system time is different and, in particular, that the date is different. This proves useful if you want to start a program whose usage license has expired. The change only applies to processes that are started by libfaketime
. The tool uses LD_PRELOAD
to load a library that replaces various functions, including time()
, ftime()
, and gettimeofday()
(Figure 3).
The stderred
library (the name combines stderr
for standard error and the color "red") colors all output on the terminal that a process sends to the standard error channel. This means that error messages can be easily distinguished from other output.
The fsatrace
monitors file access and detects read and write access, moves, deletions, and status queries. However, you can easily achieve the same behavior with strace
.
« Previous 1 2 3 Next »
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Gnome 48 Debuts New Audio Player
To date, the audio player found within the Gnome desktop has been meh at best, but with the upcoming release that all changes.
-
Plasma 6.3 Ready for Public Beta Testing
Plasma 6.3 will ship with KDE Gear 24.12.1 and KDE Frameworks 6.10, along with some new and exciting features.
-
Budgie 10.10 Scheduled for Q1 2025 with a Surprising Desktop Update
If Budgie is your desktop environment of choice, 2025 is going to be a great year for you.
-
Firefox 134 Offers Improvements for Linux Version
Fans of Linux and Firefox rejoice, as there's a new version available that includes some handy updates.
-
Serpent OS Arrives with a New Alpha Release
After months of silence, Ikey Doherty has released a new alpha for his Serpent OS.
-
HashiCorp Cofounder Unveils Ghostty, a Linux Terminal App
Ghostty is a new Linux terminal app that's fast, feature-rich, and offers a platform-native GUI while remaining cross-platform.
-
Fedora Asahi Remix 41 Available for Apple Silicon
If you have an Apple Silicon Mac and you're hoping to install Fedora, you're in luck because the latest release supports the M1 and M2 chips.
-
Systemd Fixes Bug While Facing New Challenger in GNU Shepherd
The systemd developers have fixed a really nasty bug amid the release of the new GNU Shepherd init system.
-
AlmaLinux 10.0 Beta Released
The AlmaLinux OS Foundation has announced the availability of AlmaLinux 10.0 Beta ("Purple Lion") for all supported devices with significant changes.
-
Gnome 47.2 Now Available
Gnome 47.2 is now available for general use but don't expect much in the way of newness, as this is all about improvements and bug fixes.