3

I've been trying to upgrade some programs on an old Linux box (Debian 7.5 wheezy). I'd like to keep default system libs untouched and to add some custom builds alongside them. It works fine for most libs, only OpenSSL gives me a hard time (libssl.so + libcrypt.so).

Here is my OpenSSL configure line:

# ./config --prefix=/usr/local --openssldir=/usr/local/openssl no-gost shared zlib no-ssl2 -fPIC

Then for other programs, for example cURL:

# ./configure LDFLAGS="-L/usr/local/ssl/lib -ldl" --with-ssl --enable-shared

But then I get the wrong OpenSSL version:

# curl --version
curl 7.44.0 (x86_64-unknown-linux-gnu) libcurl/7.44.0 OpenSSL/1.0.1e

I know libssl.so has copies in the following folders on this system:

/usr/lib/libssl.so
/usr/lib/x86_64-linux-gnu/libssl.so
/usr/local/lib/libssl.so
/usr/local/ssl/lib/libssl.so

All of them are the same custom-compiled version, except /usr/lib/x86_64-linux-gnu/libssl.so which appears to be my default system lib (v1.0.5e).

I'd like to know how I can instruct configure scripts to avoid looking inside /lib/x86_64-linux-gnu? Can I just change the /usr/lib/x86_64-linux-gnu/libssl.so symlink and point it to my builds? Is it safe?

Thanks,

3 Answers3

1

There are two different linker operations involved, build time (used to find libraries and "symbols", performed by ld or equivalent), and run time (performed by ld.so) which loads libraries into process memory and performs relocations.

When you use -L during build, ld only confirms the libraries and required symbols (typically functions which comprise the API) are available. At run time, libraries with different paths can be used. LD_LIBRARY_PATH, LD_PRELOAD and RPATH (-R or -rpath) are some of the ways of controlling that – the last one being the way to specify this at build time (so it's a property of the binary, and less dependent on the user environment).

With any library, the features, API, and ABI can change, leading to errors or instability if the linker finds or loads the "wrong" binary. Even though the symbols can match as expected (or you would get an error before execution), the wheels may come off during process execution. There are solutions to this, versioning of the library filename, and versioning of the symbols in a library, though those won't be of much use here. (A complication is that the SHLIB_MAJOR/SHLIB_MINOR in all the OpenSSL 1.0.x series is "1.0.0", so those libraries cannot be readily distinguished unless you hack the Makefile.)

Curl itself checks for this kind of problem, it compares the build-time libcurl version to the run-time version, and may issue:

WARNING: curl and libcurl versions do not match. Functionality may be affected.

A contemporary ld will do similar at build time when it notes potentially conflicting libraries in dependencies.

For a clean build, with no run time environment trickery, you can:

  1. build OpenSSL making sure an RPATH is explicit, this is because libssl.so links against libcrypto.so, and install to a specific path
  2. build the package (i.e. curl) with the correct link-time and run-time specification so that it finds the specific libssl.so/libcrypto.so

A cheap and nasty way to fix the problem would be to usr chrpath to alter the ELF RPATH in binaries after building, I don't recommend this generally, and definitely not for curl, read on.

Now for the complications: curl supports lots of protocols, some of those protocols are provided via other libraries, and where the protocol supports TLS (or uses crypto functions) it will tend to also link against libssl.so and/or libcrypto.so, e.g. for LDAP and SSH. What you do not want is:

  curl ↦ libcurl.so → libldap.so → libssl.so
       ↦ lib...
       ↦ libcrypto.so
       ↳ libssl.so

where curl itself would use one version, but libldap.so may expect a different version to load; this may also fail at run-time if there's an ABI incompatibility. You can be unlucky with symbol or load ordering, and end up with two (or more!) versions of a library loaded.

There are generally three ways to try to work around that:

  1. turn off all the features you don't need when building, for curl at least LDAP and SSH2 will have to go
  2. if that's not possible, build new versions of the dependent libraries which also link against the new OpenSSL
  3. if that's not possible, you might be able to use .a libraries for some static linking. For curl, this is not the same thing as just using --enable-static. It can work, I've done it in the distant past, but it may not work here so I won't mention it further; and it can cause many headaches, not least with with PIC/PIE.

GnuTLS can further muddy the water, though it's not a problem to use both libraries concurrently (at least, not that I've seen so far); that doesn't hold for other libraries that aim for ABI compatibility, like LibreSSL. Curl does go to some trouble to use only a single SSL library in its build (it currently supports 8, AFAIK), but it doesn't know what for example, libssh2 might be linked against.

So, depending on versions, you can try building OpenSSL and curl respectively:

# OpenSSL
./config --prefix=/usr/local --openssldir=/usr/local/openssl no-gost \
          shared zlib no-ssl2 -fPIC -Wl,-R,/usr/local/openssl
 make depend && make && make install_sw

# curl
./configure --with-ssl=/usr/local/openssl --enable-shared \
            --enable-ldap=no --without-libssh2 \
            CFLAGS=-Wl,-R,/usr/local/openssl/lib
make && make install

(Despite documentation to the contrary, --with-ldap=no and --without-ldap have no useful effect, use --enable-ldap=no. libssh2 (for scp) is not usually on by default.)

After install, ldd /usr/local/bin/curl should contain no surprises: exactly one reference to each of the expected libssl.so and libcrypto.so.

OpenSSL build (non-autoconf) will set the RPATH correctly on the openssl binary, but it does not set it on the libraries by default. There are platform specific variations in how libraries are located (much of the RPATH handling in the OpenSSL build is a decoy: it applies to platforms other than Linux). This might not be required for curl, but it won't cause problems.

When you use autoconf's configure there is no guarantee that non-default library locations will result in a matching RPATH in the binaries, CFLAGS/LDFLAGS can usually fix that. Here though, with OpenSSL's two-step process, config doesn't quite get along with variables like LDFLAGS, so add at the end of the config line instead, and Configure handles it correctly when creating the Makefile.

You can check an ELF executable or library run-time path with:

readelf --dynamic elfbinary | grep PATH

You can debug library loading with:

LD_DEBUG=files,libs /usr/local/bin/curl ...

(Note the original question is old, and OpenSSL 1.0.x is no longer supported officially, though distributions may vary. The instructions herein are generally applicable to building special versions of OpenSSL 1.0.x for specific purposes. Building an stunnel binary to reverse-proxy an old network device so that it can work with a contemporary browser is a good example. )

You can find some further explanation and instructions in my answer to this question: Get ld to pick the correct library.

mr.spuratic
  • 3,400
  • 19
  • 14
0

One solution I found that was labelled "not the best way to do it" but worked for me, was to edit the LD_LIBRARY_PATH system variable, for example in <user>/.bashrc:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
0

I tried to do what you suggest, resulting in a segmentation fault! So, not safe!

/usr/lib/i686/cmov$ sudo ln -s /usr/lib/libssl.so.1.0.0 libssl.so.0.9.8
/usr/lib/i686/cmov$ ls -la
total 1684
drwxr-xr-x 2 root root    4096 Jun 16 14:15 .
drwxr-xr-x 3 root root    4096 Oct 22  2012 ..
-rw-r--r-- 1 root root 1393308 Feb 11  2013 libcrypto.so.0.9.8
lrwxrwxrwx 1 root root      24 Jun 16 14:15 libssl.so.0.9.8 -> /usr/lib/libssl.so.1.0.0
-rw-r--r-- 1 root root  310296 Feb 11  2013 libssl.so.0.9.8.bk
:/usr/lib/i686/cmov$ sudo /etc/init.d/nginx restart
Restarting nginx: nginx/usr/sbin/nginx: /usr/lib/i686/cmov/libssl.so.0.9.8: no version information available (required by /usr/sbin/nginx)
Segmentation fault
daigorocub
  • 249
  • 2
  • 10
  • This is not what I was suggesting. In my setup, I modified one way the system loads the libs so as to make the compiled libs a priority over the system libs, but I kept both files separate. Also, this works if the other tools are built also on the system, or else they will surely fail. – Guillaume Rossolini Jun 18 '16 at 12:20