Day 33 — There are so many LD variables!

Using strace to spy on ghostscript's system calls made me wonder if there's a way to print the names of all function calls (along with their file paths) that a compiled executable makes after you invoke it on the command-line. This would be awesome to navigate large C codebases like ghostscript. I asked this on the #C stream on Zulip and Ori pointed me to ltrace, which can be used just like strace! I wonder how many more trace commands are there?!

When I used it on the same gs command from two days ago, it gave me 812477 lines of output with all the library calls! It might take some time to find which ones are useful to find relevant code that does the PDF to PNG conversion. Looks like Julia Evans has written a lot about ltrace. I guess it's time to read all the posts!

Ori also mentioned how you can do horrific hacks with the LD_PRELOAD environment variable and the dynamic linker. It can let you replace any function call with your own version of it. He said that you can do things like tracing malloc/free, and redirecting attempts to open certain files! Julian pointed out that libfaketime is another nice LD_PRELOAD hack, and how the hack can be really useful when writing tests to reproduce certain conditions. LD stands for either "Linker" or "Loader" based on this SO answer.

Today I also learned about the LD_LIBRARY_PATH environment variable which can be used to make "shared libraries" discoverable on Linux. It is not normally set.


  $ echo $LD_LIBRARY_PATH

And there's this ldd command that can be used to list all the shared library dependencies for a program. One of the dependencies of ls is libc which the dynamic linker looks for, and finds in one of the default paths when ls is run.


  $ ldd /bin/ls
          ...
          libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc6c57a8000)
          ...

But we can use LD_LIBRARY_PATH to ask the linker to look in another directory first, and use a libc that it might find there!


  $ mkdir /libtest
  $ cp /lib/x86_64-linux-gnu/libc.so.6 /libtest
  $ export LD_LIBRARY_PATH=/libtest
  $ ldd /bin/ls
          ...
          libc.so.6 => /libtest/libc.so.6 (0x00007fc6c57a8000)
          ...

We can unset LD_LIBRARY_PATH to go back to the default behavior.


  $ unset LD_LIBRARY_PATH
  $ ldd /bin/ls
          ...
          libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc6c57a8000)
          ...

The default directories where a linker should look for shared libraries are defined in some config files. If you make changes to any config file, you must run the ldconfig command afterwards to update things.


  $ cat /etc/ld.so.conf
  include /etc/ld.so.conf.d/*.conf
  $ cat /etc/ld.so.conf.d/libc.conf
  # libc default configuration
  /usr/local/lib

There's also this LD_TRACE_LOADED_OBJECTS variable which can be used just like ldd to list all the shared library dependencies for a program:


  $ LD_TRACE_LOADED_OBJECTS=1 /bin/ls
          linux-vdso.so.1 (0x00007ffde81bd000)
          libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fdefaf54000)
          libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdefad62000)
          libpcre2-8.so.0 => /usr/lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007fdefacd2000)
          libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fdefaccc000)
          /lib64/ld-linux-x86-64.so.2 (0x00007fdefafa1000)
          libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fdefaca9000)

An ldd on ghostscript shows that is has a lot of shared library dependencies!


  $ ldd ./gs
          linux-vdso.so.1 (0x00007ffd99787000)
          libXt.so.6 => /usr/lib/x86_64-linux-gnu/libXt.so.6 (0x00007f87b9e93000)
          libX11.so.6 => /usr/lib/x86_64-linux-gnu/libX11.so.6 (0x00007f87b9d56000)
          libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f87b9c07000)
          libfontconfig.so.1 => /usr/lib/x86_64-linux-gnu/libfontconfig.so.1 (0x00007f87b9bc0000)
          libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f87b9b9d000)
          libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f87b99ab000)
          libSM.so.6 => /usr/lib/x86_64-linux-gnu/libSM.so.6 (0x00007f87b999e000)
          libICE.so.6 => /usr/lib/x86_64-linux-gnu/libICE.so.6 (0x00007f87b9980000)
          libxcb.so.1 => /usr/lib/x86_64-linux-gnu/libxcb.so.1 (0x00007f87b9956000)
          libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f87b9950000)
          /lib64/ld-linux-x86-64.so.2 (0x00007f87bba4f000)
          libfreetype.so.6 => /usr/lib/x86_64-linux-gnu/libfreetype.so.6 (0x00007f87b9891000)
          libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f87b9863000)
          libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007f87b9858000)
          libbsd.so.0 => /usr/lib/x86_64-linux-gnu/libbsd.so.0 (0x00007f87b983e000)
          libXau.so.6 => /usr/lib/x86_64-linux-gnu/libXau.so.6 (0x00007f87b9838000)
          libXdmcp.so.6 => /usr/lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007f87b9830000)
          libpng16.so.16 => /usr/lib/x86_64-linux-gnu/libpng16.so.16 (0x00007f87b97f8000)
          libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f87b97da000)

You can read about all the LD variables in this manual entry:


  $ man ld.so

There are so many LD variables! Gotta Catch 'Em All!