Month: March 2025
MySQL and MariaDB client .so libraries on Linux
The MySQL library is libmysqlclient.so, the MariaDB library is libmariadbclient.so or libmariadb.so. I’ll list some quirks that I think are relatively unknown but good to know. I’ll end with a pointer to a function that’s good to have.
mysql and mariadb clients don’t themselves use .so libraries
To illustrate, here’s the file mysql.dir/link.txt that I got when building MySQL’s source with -DWITHOUT_SERVER=ON.
/usr/bin/c++ -std=c++20 -fno-omit-frame-pointer -ftls-model=initial-exec -g -O2 -ffile-prefix-map=/home/pgulutzan/Downloads/mysql-9.2.0=. -flto=auto -ffat-lto-objects -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -Wall -Wextra -Wformat-security -Wvla -Wundef -Wmissing-format-attribute -Woverloaded-virtual -Wcast-qual -Wimplicit-fallthrough=5 -Wstringop-truncation -Wsuggest-override -Wmissing-include-dirs -Wextra-semi -Wlogical-op -ffunction-sections -fdata-sections -O2 -g -DNDEBUG -g1 -Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -flto=auto -Wl,-z,relro -Wl,--build-id=sha1 CMakeFiles/mysql.dir/__/sql-common/net_ns.cc.o CMakeFiles/mysql.dir/completion_hash.cc.o CMakeFiles/mysql.dir/mysql.cc.o CMakeFiles/mysql.dir/pattern_matcher.cc.o CMakeFiles/mysql.dir/readline.cc.o CMakeFiles/mysql.dir/client_query_attributes.cc.o CMakeFiles/mysql.dir/multi_factor_passwordopt-vars.cc.o CMakeFiles/mysql.dir/multi_option.cc.o CMakeFiles/mysql.dir/common/user_registration.cc.o CMakeFiles/mysql.dir/__/sql-common/sql_string.cc.o -o ../runtime_output_directory/mysql ../archive_output_directory/libmysqlclient.a ../extra/libedit/libedit-20240808-3.1/src/libedit.a /usr/lib/x86_64-linux-gnu/libssl.so /usr/lib/x86_64-linux-gnu/libcrypto.so /usr/lib/x86_64-linux-gnu/libresolv.so -lm -lcurses
As you can see if you squint, it’s bringing in libmysqlclient.a, the static library.
This affects not only mysql but other executables that MySQL provides. And effects are similar with MariaDB’s source.
MySQL manual says LD_RUN_PATH decides the client .so library
Specifically the Environment Variables” section says in a chart beside LD_RUN_PATH: “Used to specify the location of libmysqlclient.so.” and the “Problems Using the Perl DBI/DBD Interface” section says “Add the path name of the directory where libmysqlclient.so is located to the LD_RUN_PATH environment variable. Some systems use LD_LIBRARY_PATH instead.”
These are not falsehoods but I think they could mislead Linux users.
First, as I mentioned earlier, it doesn’t necessarily apply for what they supply.
Second, as I’ll mention later, on “some systems” — e.g. Linux, eh? — LD_RUN_PATH only has an effect at build time (as when you run gcc) and Linux package developers deprecate it.
Third, as I’ll also mention later, there are several other factors that the dynamic loader will examine though the MySQL manual doesn’t mention them all.
As a client developer I have to pay attention to this advice and look at LD_RUN_PATH when calling dlopen, but it’s not my favourite advice.
MariaDB library isn’t always the same
I said at the start that “the MariaDB library is libmariadbclient.so or libmariadb.so” but it’s not so simple.
There’s a chance that symlinks will exist between libmysqlclient.so and a MariaDB .so.
There’s also a chance that both libmariadbclient.so and libmariadb.so will be available, and libmariadbclient.so will have a symlink to libmariadb.so which is the real target. However, this is not always the case.
Illustration #1: This is what the latest Debian distro says in the sid libmaria-dev list of x32 files:
/usr/lib/x86_64-linux-gnux32/libmariadb.so /usr/lib/x86_64-linux-gnux32/libmariadbclient.so
I’ve left out the irrelevant stuff. The point is, both .so names are there.
Illustration #2: This is what the MariaDB Connector/C contains after a recent download:
~/connector-c/usr/local/lib/mariadb$ ls libmariadb.a libmariadbclient.a libmariadb.so libmariadb.so.3 plugin
I’ve left out the irrelevant stuff. The point is, only one .so name is there. So usually you’ll have no trouble linking to libmariadbclient.so but it’s not guaranteed any more.
The server can display the .so
To see this you’ll have to run the server with performance_schema on, which is the default for MySQL but not for MariaDB, you’ll have to ask for it.
Example pasted from an ocelotgui session:
Notice the name and the version number. But if the library was statically linked, and not an .so, this won’t tell you.
mysql_config can display the .so path
For example,
$ bin/mysql_config --libs -L/home/pgulutzan/mysql-8.3.0-linux-glibc2.28-x86_64/lib -Wl,-R,/home/pgulutzan/mysql-8.3.0-linux-glibc2.28-x86_64/lib -L/home/pgulutzan/mysql-8.3.0-linux-glibc2.28-x86_64/lib/private -Wl,-R,/home/pgulutzan/mysql-8.3.0-linux-glibc2.28-x86_64/lib/private -lmysqlclient -lpthread -ldl -lssl -lcrypto -lresolv -lm -lrt
And mariadb_config would do something similar. It is correct on my machine that the first specified directory indeed contains libmysqlclient.so.
If you follow MySQL’s instructions exactly in section “Building C API Client Programs” this is what you can expect.
But if the client program chooses a different path, this won’t tell you.
Linux utilities can display the .so and the .so path
As well as the programs that come with MySQL or MariaDB that show .so files, there are programs that come with Linux that show .so files. They work with any MySQL or MariaDB executable, but in this section I’ll show examples with common utilities instead because they’re shorter.
I admit that I’m depending on common Linux and ELF format for the examples, if it had to be general I’d perhaps think that libbfd would help me, but I’ve never seen the need.
FIND!
Of course one of the programs is the simple
sudo find / -name “libmy*.so*”
or
sudo find / -name “libmaria*.so*”
but it takes too long and it doesn’t show the files in the context of the caller. So the value is small.
LD!
For example
ld –verbose | grep SEARCH_DIR
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");
… These are some standard system paths that ld the GNU linker will look at on a multiarch machine. But they are not necessarily what the dynamic loader will look at so the value is small.
LDD!
For example
$ ldd ./mysql linux-vdso.so.1 (0x00007fffd2772000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f23b3b8b000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f23b3b85000) libssl.so.3 => /home/pgulutzan/mysql-8.3.0-linux-glibc2.28-x86_64/bin/./../lib/private/libssl.so.3 (0x00007f23b38dd000) libcrypto.so.3 => /home/pgulutzan/mysql-8.3.0-linux-glibc2.28-x86_64/bin/./../lib/private/libcrypto.so.3 (0x00007f23b32a5000) libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f23b3289000) librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f23b327f000) libncurses.so.6 => /lib/x86_64-linux-gnu/libncurses.so.6 (0x00007f23b3254000) libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007f23b3224000) libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f23b3042000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f23b2ef3000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f23b2ed8000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f23b2ce6000) /lib64/ld-linux-x86-64.so.2 (0x00007f23b3bcc000)
Most utilities have a man page like this one.
STRACE!
For example, if you have ocelotgui,
strace ocelotgui 2>/dev/stdout| grep openat
will display
openat(AT_FDCWD, "/usr/mysql/lib/tls/x86_64/x86_64/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/mysql/lib/tls/x86_64/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/mysql/lib/tls/x86_64/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/mysql/lib/tls/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/mysql/lib/x86_64/x86_64/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/mysql/lib/x86_64/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/mysql/lib/x86_64/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/mysql/lib/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/lib/tls/x86_64/x86_64/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/lib/tls/x86_64/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
… And so on, for failed attempts to open .so files. The failures are expected, by the way. Subdirectory tls (thread local storage) tends to be absent.
LTRACE!
Example:
$ ltrace cp strrchr("cp", '/') = nil setlocale(LC_ALL, "") = "/usr/share/locale" textdomain("coreutils") = "coreutils" __cxa_atexit(0x560a7dde6ce0, 0, 0x560a7ddfe008, 0x736c6974756572) = 0 is_selinux_enabled(0x478e4b21b3007f54, 0x7fd612321ca0, 32, 0) 0 geteuid() = 1000 getenv("POSIXLY_CORRECT") = nil
… and so on for all the system calls that cp makes till it’s done. This could find calls that open libraries but the value is small.
READELF!
For example:
$ readelf -a /usr/bin/mariadb | grep NEEDED 0x0000000000000001 (NEEDED) Shared library: [libreadline.so.5] 0x0000000000000001 (NEEDED) Shared library: [libncurses.so.6] 0x0000000000000001 (NEEDED) Shared library: [libtinfo.so.6] 0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0] 0x0000000000000001 (NEEDED) Shared library: [libssl.so.1.1] 0x0000000000000001 (NEEDED) Shared library: [libcrypto.so.1.1] 0x0000000000000001 (NEEDED) Shared library: [libz.so.1] 0x0000000000000001 (NEEDED) Shared library: [libdl.so.2] 0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
(Remember the MariaDB library is statically linked so it doesn’t appear here.)
OBJDUMP!
For example:
$ objdump -p /usr/bin/mariadb | grep NEEDED NEEDED libreadline.so.5 NEEDED libncurses.so.6 NEEDED libtinfo.so.6 NEEDED libpthread.so.0 NEEDED libssl.so.1.1 NEEDED libcrypto.so.1.1 NEEDED libz.so.1 NEEDED libdl.so.2 NEEDED libstdc++.so.6 NEEDED libc.so.6
This objdump list is the same as the readelf list. You can read stories like readelf vs. objdump: why are both needed to know you don’t need both.
LD_DEBUG!
Now for examples I’ll need any program that happens to use the dynamic loader, /bin/cp will do.
Start with the readelf utility to see what a program’s dynamic loader is.
Example:
$readelf -l /bin/cp | grep interpreter [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
Okay, in this case the dynamic loader is /lib64/ld-linux-x86-64.so.2. (The same name appears in the earlier ldd example above but I think that’s undependable.) It differs depending on the platform and the program, so always check.
First make sure that all LD_ environment variables are unset. Then:
LD_DEBUG=libs /lib64/ld-linux-x86-64.so.2 –inhibit-cache /bin/cp 2>/dev/stdout | grep search
You’ll see something like
28805: find library=libselinux.so.1 [0]; searching 28805: search path=/lib/x86_64-linux-gnu/tls/x86_64/x86_64: /lib/x86_64-linux-gnu/tls/x86_64:/lib/x86_64-linux-gnu/tls/x86_64: /lib/x86_64-linux-gnu/tls:/lib/x86_64-linux-gnu/x86_64/x86_64:/lib/x86_64-linux-gnu/x86_64: /lib/x86_64-linux-gnu/x86_64:/lib/x86_64-linux-gnu: /usr/lib/x86_64-linux-gnu/tls/x86_64/x86_64:/usr/lib/x86_64-linux-gnu/tls/x86_64: /usr/lib/x86_64-linux-gnu/tls/x86_64:/usr/lib/x86_64-linux-gnu/tls: /usr/lib/x86_64-linux-gnu/x86_64/x86_64:/usr/lib/x86_64-linux-gnu/x86_64: /usr/lib/x86_64-linux-gnu/x86_64:/usr/lib/x86_64-linux-gnu:/lib/tls/x86_64/x86_64: /lib/tls/x86_64:/lib/tls/x86_64:/lib/tls:/lib/x86_64/x86_64:/lib/x86_64:/lib/x86_64: /lib:/usr/lib/tls/x86_64/x86_64:/usr/lib/tls/x86_64:/usr/lib/tls/x86_64: /usr/lib/tls:/usr/lib/x86_64/x86_64:/usr/lib/x86_64:/usr/lib/x86_64:/usr/lib (system search path)
… and so on for all the .so files of cp.
LDCONFIG!
For example,
$ /sbin/ldconfig -p | grep -e 'libmysql' -e 'libmaria' libmysqlclient.so.21 (libc6,x86-64) => /lib/x86_64-linux-gnu/libmysqlclient.so.21 libmarias3.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libmarias3.so libmariadbd.so.19 (libc6,x86-64) => /lib/x86_64-linux-gnu/libmariadbd.so.19 libmariadbd.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libmariadbd.so libmariadb.so.3 (libc6,x86-64) => /lib/x86_64-linux-gnu/libmariadb.so.3 libmariadb.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libmariadb.so liblibmysql_client.so (libc6,x86-64) => /lib/x86_64-linux-gnu/liblibmysql_client.so
Vaguely speaking, sudo make install can change /etc/ld.so.conf and ldconfig can take from /etc/ld.so.conf to the cache and ldconfig -p can show what’s in the cache. This is good for administrator-approved libraries, as opposed to ones that have been installed in a non-system local directory.
DPKG OR WHATPROVIDES!
For example,
$ dpkg -S libmysqlclient.so libmysqlclient21:amd64: /usr/lib/x86_64-linux-gnu/libmysqlclient.so.21 libmysqlclient18: /usr/lib/x86_64-linux-gnu/libmysqlclient.so.18 libmysqlclient21:amd64: /usr/lib/x86_64-linux-gnu/libmysqlclient.so.21.2.41 libmariadb-dev-compat:amd64: /usr/lib/x86_64-linux-gnu/libmysqlclient.so
The dpkg package manager can be on Debian-based distros. On rpm-based distros the equivalent is whatprovides. This is telling you what is supposed to be there, as opposed to what is actually there.
All of the above utilities are fine for their purposes but I’m after something different — a function I can call to find libraries at runtime, loaded or unloaded. I’ll describe the solution at the end of this blog post.
There are ways to force the path
The usual dynamic loader (often on /lib64/ld-linux-x86-64.so.2 or /lib/ld-linux.so.2 but read the previous section to check) is going to search in this order:
LD_AUDIT — environment variable
LD_PRELOAD — environment variable
DT_RPATH — set by -Wl non-default option
LD_LIBRARY_PATH — environment variable
DT_RUNPATH — set by -Wl default option or LD_RUN_PATH environment variable during build
ld.so.cache and defaults.
LD_AUDIT: specialized and difficult. I mention it but don’t expect it.
LD_PRELOAD: I’ve seen this recommended for the MySQL or MariaDB server but for clients it’s probably only a good choice if you need to force a specific .so file, as opposed to a specific path containing .so files. Oracle used to suggest this for their drop-in replacement.
DT_RPATH: set before runtime. With gcc the way to force DT_RPATH is
gcc … -Wl,-rpath,,-rpath,<library_path>,-disable-new-dtag
The -disable-new-dtag is necessary nowadays because DT_RPATH is set with the old tag. With the default new tag, -Wl,-rpath sets DT_RUNPATH instead. In other words, you’ll probably never see this.
LD_LIBRARY_PATH: or actually “ld_library_paths” plural, because you can put multiple paths delimited by colons or semicolons.
DT_RUNPATH: set before runtime. With gcc the way to force DT_RUNPATH is
gcc … -Wl,-rpath,<library_path>,-rpath,<library_path>
or
LD_RUN_PATH=<library_path> gcc … but setting the LD_RUN_PATH environment variable has no effect at runtime.
ld.so.cache: This is a list of the .so files that the dynamic loader will look for by default. It’s visible with
ldconfig -r
Typically the list is changed when you sudo make install something, so although ldconfig’s not the top choice it’s the most reliable one. You know that some package installer thought that the .so in this list is an appropriate one.
You’ll notice that DT_RPATH and DT_RUNPATH don’t get in unless they’re specified before runtime. And if you specify them yourself and then try to make a .deb package, lintian will tell you: they’re deprecated. For another package type, SUSE will tell you they’re forbidden. So they don’t matter so much.
Since environment variables do matter so much, it can be a good idea to check whether they’re already set, for example you put something in .bashrc and forgot. The best way to check environment variables is printenv (not echo). To check all the environment variables that start with LD_, say
printenv | grep LD_
Since environment variables matter so much, it can be a good idea to set them in a canonical way. For example, to say you want the dynamic loader to look in /tmp/A and /tmp/B, say
LD_LIBRARY_PATH=/tmp/A:/tmp/B program-name
and to temporarily turn it off, say
env -u LD_LIBRARY_PATH program-name
This is slightly better than
export LD_LIBRARY_PATH=program-name
because export has a permanent effect rather than an effect specific for program-name, and because export can only set the environment variable to blank rather than getting rid of it. Another thing is that the delimiter between path names is a colon — yes a semicolon would work here but it wouldn’t work with -Wl,-rpath and you might as well have the same delimiter in all possible lists. I didn’t surround the path names with ”s or “”s in the example, but if there’s a $ then their absence could cause confusion. For example:
export LD_LIBRARY_PATH=/$LIB
printenv LD_LIBRARY_PATH
… You’ll see only the / character, because $ is a signal that an environment variable name follows, unless it’s quoted or escaped.
$LIB and $PLATFORM and $ORIGIN
The dynamic loader will replace occurrences of $LIB or $PLATFORM or $ORIGIN within path names.
$LIB: According to a “Linux manual page” and according to
man ld.so
on my computer, “$LIB (or equivalently ${LIB}) This expands to lib or lib64 depending on the architecture (e.g., on x86-64, it expands to lib64 and on x86-32, it expands to lib).”
Hah! Just kidding. Pilot Logic has a page indicating that’s true for what they call “Red Hat Base” and “Mandriva Base” but not for “Debian Base” or “Arch Base” or “Gentoo Base”. (It’s actually the distro’s glibc that’s responsible.) And there might be more differences in the future.
On top of that, if you go back and look at the example of LD_DEBUG use, you’ll see a bunch of /tls (thread local storage) subdirectories. Luckily, although $LIB can expand to include them, they won’t exist.
$PLATFORM: expect something like “x86_64” on Debian-like 64-bit platforms. Hah! Just kidding again. Debian’s “ArchitectureSpecificsMemo” indicates other possibilities.
$ORIGIN: This is supposed to be the path of the program that’s running. I’ve seen complaints that it’s actually the path of the library in the program that’s running, but I haven’t understood the difference. So if you say
/usr/bin/wombat
then the $ORIGIN is /usr/bin. And if you build with
gcc … -Wl,-z,origin,-rpath,./lib,-rpath,\$ORIGIN
that’s what you’ll see — though notice the escaping of the $ here.
How ocelotgui handles it
There’s no reliable way to decide at install time what the client library will be, because the name varies, the location varies, and the appropriate library might depend on the server that ocelotgui is connecting to.
The
CMakeLists.txt for ocelotgui
has, for 64-bit builds,
set(TMP_INSTALL_RPATH "${TMP_INSTALL_RPATH}/usr/mysql/lib: /usr/lib:/usr/lib/mysql:/usr/local:/usr/local/lib: /usr/local/lib/mysql:/usr/local/mysql:/usr/local/mysql/lib: /usr:/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib64/mysql: /usr/mariadb/lib:/usr/lib/mariadb:/usr/local/lib/mariadb: /usr/local/mariadb:/usr/local/mariadb/lib:/usr/lib64/mariadb")
which is reasonable.
However, it’s disabled for the .deb or .rpm packages.
By default it looks first for libmysqlclient then libmariadbclient then libmariadb,
but that’s easy to change with command-line options or in the .cfg file.
And if there’s no non-default specification then the standard-loader paths matter.
This means runtime flexibility is required, and that means we have to use dlopen(). Therefore ldd ocelotgui will tell you nothing, but in this case it’s because we’re looking everwhere, which is quite different from the mysql/mariadb client case because they’re bringing in a .a library.
Finally, Help|About and Help|libmysqlclient will say more about what’s actually connected or could be.
The ocelotgui GUI for MySQL and MariaDB is still version 2.5, which means it hasn’t kept up with recent changes of the servers.
There has to be a new version that takes into account the points that I’ve described here. It will incorporate the code of a new github project: pgfindlib.
pgfindlib
I’ve created a small new GitHub project, pgfindlib. It has major advantages over all the things I’ve described in this post, if the objective is to find relevant .so files at runtime, without cruft, from within a program.
I’ll just quote the first bit of its README here:
…
Version 0.9.7
The pgfindlib function finds dynamic libraries (.so files) in the order the loader would find them.
Copyright (c) 2025 by Peter Gulutzan. All rights reserved.
What pgfindlib is good for
Knowing what .so files the loader would load, but loading them yourself with dlopen(), means you can customize at runtime.
Or, as part of –help you can tell users what the loader picked up, and from where, and what choices it ignored.
An example: Using files supplied with the package:
mkdir /tmp/pgfindlib_example echo "Dummy .so" >> /tmp/pgfindlib_example/libutil.so gcc -o main main.c pgfindlib.c -Wl,-rpath,/tmp/pgfindlib_example LD_LIBRARY_PATH='/$LIB' ./main 'where libutil.so, libcurl.so, libgcc_s.so'
The result might look like this:
1,,,002 pgfindlib,001 version 0.9.7,003 https://github.com/pgulutzan/pgfindlib,, 2,,,005 $LIB=lib/x86_64-linux-gnu,006 $PLATFORM=x86_64,007 $ORIGIN=/home/pgulutzan/pgfindlib,, 3,,,012 in source LD_LIBRARY_PATH replaced /$LIB with /lib/x86_64-linux-gnu,,,, 4,/lib/x86_64-linux-gnu/libcurl.so,LD_LIBRARY_PATH,013 symlink,,,, 5,/lib/x86_64-linux-gnu/libcurl.so.4,LD_LIBRARY_PATH,013 symlink,,,, 6,/lib/x86_64-linux-gnu/libcurl.so.4.6.0,LD_LIBRARY_PATH,,,,, 7,/lib/x86_64-linux-gnu/libgcc_s.so.1,LD_LIBRARY_PATH,,,,, 8,/lib/x86_64-linux-gnu/libutil.so,LD_LIBRARY_PATH,013 symlink,,,, 9,/lib/x86_64-linux-gnu/libutil.so.1,LD_LIBRARY_PATH,013 symlink,,,, 10,/tmp/pgfindlib_example/libutil.so,DT_RUNPATH,071 elf read failed,,,, 11,/lib/libgcc_s.so.1,ld.so.cache,,,,, 12,/lib/x86_64-linux-gnu/libcurl.so,ld.so.cache,013 symlink,014 duplicate of 4,,, 13,/lib/x86_64-linux-gnu/libcurl.so.4,ld.so.cache,013 symlink,014 duplicate of 5,,, 14,/lib/x86_64-linux-gnu/libgcc_s.so.1,ld.so.cache,014 duplicate of 7,,,, 15,/lib/x86_64-linux-gnu/libutil.so,ld.so.cache,013 symlink,014 duplicate of 8,,, 16,/lib/x86_64-linux-gnu/libutil.so.1,ld.so.cache,013 symlink,014 duplicate of 9,,, 17,/lib32/libgcc_s.so.1,ld.so.cache,075 elf machine does not match,,,, 18,/lib32/libutil.so.1,ld.so.cache,013 symlink,075 elf machine does not match,,, 19,/lib/libgcc_s.so.1,default_paths,014 duplicate of 11,,,, 20,/usr/lib/libgcc_s.so.1,default_paths,014 duplicate of 11,,,, rval=0
This means: the loader would look first in /lib/x86_64-linux-gnu because of LD_LIBRARY_PATH. This takes precedence over DT_RUNPATH, which is where the first occurrence of libutil.so appears (this appears because of the -rpath option in the gcc command). Finally there are some .so libraries in ld.so.cache and the system libraries, which is where the loader would go if there was no prior. But some of the lines contain warnings, for example “071 elf read failed” because /tmp/pgfindlib_example/libutil.so is not a loadable file, or for example “075 elf machine does not match” because main is 64-bit and /lib32/libgcc_s.so.1 is 32-bit.
That’s all you need to know in order to decide if you’re interested. If you are, read on, there are many options and a few warnings.
…
Yes, the input is [FROM clause] WHERE clause, the output is table.
Now go over to the pgfindlib repository https://github.com/pgulutzan/pgfindlib and clone it. Since you’ve read all this, you’ll want that.