Rewrite shared library undef symbol version of compiled object

3.2k views Asked by At

I am trying to compile and link a program (let's call it myprog) which is linked against a shared library (in this instance libcryto & libssl, but the actual library is not relevant). I am building it on Centos 5.5 but want the same binary to run on other RHEL-like distributions (e.g. CloudLinux). Both have versions of the same library with the same SO_NAME (i.e. DT_NEEDED versions correspond).

My issue is this. When I compile on Centos I see:

centos$ objdump -T myprog | fgrep SSL_new
0000000000000000      DF *UND*  0000000000000000  libssl.so.10 SSL_new

which works fine because:

centos$ objdump -T /usr/lib64/libssl.so.10 | fgrep SSL_new
00000000000447d0 g    DF .text  0000000000000390  libssl.so.10 SSL_new

However, on CloudLinux:

cloudlinux$ objdump -T /usr/lib64/libssl.so.10 | fgrep SSL_new
00000033a623a120 g    DF .text  0000000000000390  Base        SSL_new

Note the version of the SSL symbol has changed from libssl.so.10 to Base.

This means when I run the binary I get:

cloudlinux$ ./myprog
./myprog: /usr/lib64/libcrypto.so.10: no version information available (required by ./myprog)
./myprog: /usr/lib64/libssl.so.10: no version information available (required by ./myprog)

I understand this is 'just a warning', but I want to get rid of it.

What I want is for my binary to run on both cloudlinux and centos without warnings. The ABI of the openssl libraries are, as far as I can tell, identical at least in the bits I use. Now I can't change the shared libraries as myprog is meant to run on any cloudlinux or centos libraries. I would accept a solution which at install time fixed up the symbols (somehow), e.g. with objcopy.

One approach would be something like introduce a weak symbol for [email protected] which is an alias for SSL_new@Base. Obviously that would be for every single symbol in the ssl library (so wrappers are not appropriate). But I can't get -Wl,--defsym=x=y to take values of x and y with @ symbols in.

Another approach that might work is simply to remove the symbol versioning information from the binary (i.e. remove the libssl.so.10 version from each of the ssl symbols, and rely on the versioning within the DT_NEEDED name). This would seem to be a simple request, but I can't see how to do it either. Is there some objcopy incantation?

Worse case, building on Centos a (separate) version that would work on CloudLinux would be fine. I tried by doing this:

#define OSLB(SYMNAME) __asm__(".symver " #SYMNAME "," #SYMNAME "@Base");
OSLB (SSL_new);

However, that fails on link complaining that SSL_new@Base is an undefined symbol (which it is, on Centos, as the Centos .so does not have that symbol).

Is there some way around this?

2

There are 2 answers

3
Employed Russian On BEST ANSWER

Another approach that might work is simply to remove the symbol versioning information from the binary (i.e. remove the libssl.so.10 version from each of the ssl symbols, and rely on the versioning within the DT_NEEDED name). This would seem to be a simple request, but I can't see how to do it either. Is there some objcopy incantation?

Removing version info post-link is infeasible -- these strings are entered into hash tables, and rebuilding these hash tables is too complicated a thing to do with objcopy.

What you want to do is create a stub libssl.so.10 with DT_SONAME set to libssl.so.10, which defines all the symbols you need from it, but without any version info. For example, this would do if you only needed the SSL_new symbol:

 echo "void SSL_new() { }" > t.c &&
 gcc -fPIC -shared -o libssl.so -Wl,--soname='libssl.so.10' t.c

Now link your program against this stub library, and it will not require any versioned symbols.

The runtime linker will then satisfy this undefined unversioned SSL_new with [email protected] on CentOS, and with SSL_new@Base on CloudLinux.

4
Thomas Leonard On

I was able to get rid of these warnings this way (with Debian/Arch rather than CentOS/Cloud Linux, but it should work for you too):

  1. Remove any libssl-devel package to avoid confusion

  2. Compile libssl from source

    ./config --prefix=/usr/local --openssldir=/usr/local/openssl shared

  3. Build your software against that.

For me, these version references then disappeared and it ran without warnings:

Version References:
  required from libcrypto.so.1.0.0:
    0x066a2b20 0x00 10 OPENSSL_1.0.0
  required from libssl.so.1.0.0:
    0x066a2b20 0x00 05 OPENSSL_1.0.0