35

Without implementing a linker or using ldd, how can I find the full path to a library? Is there a standard library available for that on Linux? (POSIX maybe?)

Using ldd and grep on a file that is knowingly using libGL.so.1, it looks like:

$ ldd /usr/bin/glxinfo | grep libGL
libGL.so.1 => /usr/lib/libGL.so.1 (0x00007f34ff796000)

Given a library name like libGL.so.1, how can I find the full path /usr/lib/libGL.so.1?. Preferably accepting an option for finding 32-bit and 64-bit libraries. If no library does that, does a program exist to do this? Something like find-library-path libGL.so.1. The locate libGL.so.1 command does not count.

I don't want to actually load the library using dlopen or something if it executes code from that library.

Lekensteyn
  • 64,486
  • 22
  • 159
  • 192
  • Executable format dependent, but for Mach-O/OS X, have a look at the source of [install_name_tool](https://developer.apple.com/library/mac/ipad/#documentation/Darwin/Reference/ManPages/man1/install_name_tool.1.html) –  Oct 30 '12 at 22:42
  • 1
    Do any of the answers here help? [unix.stackexchange.com: "which” equivalent for shared libraries](http://unix.stackexchange.com/questions/22178/which-equivalent-for-shared-libraries) – Sterling Christensen Oct 30 '12 at 22:58
  • @user1744516 Unfortunately not, those rely on `ldd` and an existing binary. – Lekensteyn Oct 30 '12 at 23:20

5 Answers5

21

Use ldconfig which is the tool that manages link space.

The -p flag lets you browse all available linkable libraries.

HonkyTonk
  • 1,961
  • 11
  • 11
  • 2
    Thanks for your answer ,but `-p` prints cached entries from a file, not the current state (directories in `$LD_LIBRARY_PATH` are not respected for example) – Lekensteyn Oct 31 '12 at 14:21
  • @Lekensteyn If you want to find the library in _any_ location and not rely on any mechanic that is actually used to manage libraries, `find` or the `locate` DB might be your only options. As your questions is stated, looking at the source for the loader might also be an option. – HonkyTonk Oct 31 '12 at 14:44
18

Expanding on Honky Tonk's answer, the command echo "$(ldconfig -p | grep libGL.so.1 | tr ' ' '\n' | grep /)" will give you the path alone.

cLupus
  • 535
  • 6
  • 16
2

If you don't mind actually loading the library and using some nonstandard but widely-available functions, calling dladdr on any symbol from the library will return information containing the full pathname that was loaded.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • I'll have a look at the manpage, I also added another requirement to the question: the library should not execute code of the library that is being checked. EDIT: It looks like that `dladdr` is not going to help me as it needs `dlopen` first. – Lekensteyn Oct 30 '12 at 22:53
  • Well you can't (portably) avoid executing global constructors in the library that's loaded. There might be some non-portable hacks to get back control before the constructors run, using library dependencies and the implementation-specific order in which constructors run to call `_exit` (doing all of this from a forked child process) from another library's constructor beforehand, but I think that's getting into far-fetched hack territory. Ideally `dlopen` would have a `RTLD_NOEXEC` flag to prevent any code execution in the library, useful for tasks like yours. – R.. GitHub STOP HELPING ICE Oct 30 '12 at 22:57
  • Yeah, that is wishful thinking. There is a RTLD_NOLOAD, but that just returns NULL when the library is not loaded before. I don't think there is even a solution available that takes 32-bit/64-bit into account (without building a 32-bit and 64-bit program designed to check this, that is). – Lekensteyn Oct 30 '12 at 22:58
  • Well there's another ugly hack -- you could fork a child process and `ptrace` it, and terminate it on the first `mmap` system call made. The prior `open` call should give you the pathname. :-) By the way, **why** do you need to know the pathname? I suspect there's a better way to do what you're trying to achieve. – R.. GitHub STOP HELPING ICE Oct 30 '12 at 23:03
  • [primus](https://github.com/amonakov/primus) intercepts GL calls and splits rendering from accelerating. These libraries do not have to sit in `$LD_LIBRARY_PATH`, hence I cannot pass `libGL.so.1` to the `dlopen` function in that program and therefore I have to pass the absolute path to the libraries. In a second program, I want to lookup the libraries as stated in the question so I can pass it to the the `primus` program. – Lekensteyn Oct 30 '12 at 23:18
1

For systems with GNU libc and Python the following is the closest I found. It uses LD_DEBUG (described in the man page of ld.so(8)).

LD_DEBUG=libs python3 -c "import ctypes; ctypes.CDLL('libssl.so.1.0.0')" 2>&1 | \
    grep -A 1000 "initialize program: python" | grep -A 3 "find library"

The output (for libssl.so.1.0.0) is the following:

 15370: find library=libssl.so.1.0.0 [0]; searching
 15370:  search cache=/etc/ld.so.cache
 15370:   trying file=/lib/x86_64-linux-gnu/libssl.so.1.0.0
 15370: 
 15370: find library=libcrypto.so.1.0.0 [0]; searching
 15370:  search cache=/etc/ld.so.cache
 15370:   trying file=/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
 15370: 
saaj
  • 23,253
  • 3
  • 104
  • 105
1

I implemented such a script here:

#!/usr/bin/env python3

"""
Like `type` but for libs.
"""

import sys
import os
from argparse import ArgumentParser
from glob import glob


def parse_ld_conf_file(fn):
    paths = []
    for l in open(fn).read().splitlines():
        l = l.strip()
        if not l:
            continue
        if l.startswith("#"):
            continue
        if l.startswith("include "):
            for sub_fn in glob(l[len("include "):]):
                paths.extend(parse_ld_conf_file(sub_fn))
            continue
        paths.append(l)
    return paths


def get_ld_paths():
    # To be very correct, see man-page of ld.so.
    # And here: http://unix.stackexchange.com/questions/354295/what-is-the-default-value-of-ld-library-path/354296
    # Short version, not specific to an executable, in this order:
    # - LD_LIBRARY_PATH
    # - /etc/ld.so.cache (instead we will parse /etc/ld.so.conf)
    # - /lib, /usr/lib (or maybe /lib64, /usr/lib64)
    paths = []
    if "LD_LIBRARY_PATH" in os.environ:
        paths.extend(os.environ["LD_LIBRARY_PATH"].split(":"))
    paths.extend(parse_ld_conf_file("/etc/ld.so.conf"))
    paths.extend(["/lib", "/usr/lib", "/lib64", "/usr/lib64"])
    return paths


def main():
    arg_parser = ArgumentParser()
    arg_parser.add_argument("lib")
    args = arg_parser.parse_args()

    paths = get_ld_paths()
    for p in paths:
        fn = "%s/%s" % (p, args.lib)
        if os.path.exists(fn):
            print(fn)
            return

    print("Did not found %r in %r." % (args.lib, paths), file=sys.stderr)
    sys.exit(1)


if __name__ == "__main__":
    main()
Albert
  • 65,406
  • 61
  • 242
  • 386