1

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 SSL_new@libssl.so.10 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?

Girish K
  • 758
  • 1
  • 13
  • 24
abligh
  • 24,573
  • 4
  • 47
  • 84

2 Answers2

6

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 SSL_new@libssl.so.10 on CentOS, and with SSL_new@Base on CloudLinux.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • This appears to be the way forward. I had to build the shared libray via a .o file for it to work: `gcc -c -Wall -Werror -fPIC -o dummy.o dummy.c && gcc -shared -o dummy.so -Wl,--soname='libssl.so.1.0.0' dummy.o && gcc -o objtest objtest.c -l:./dummy.so`. This approach has the advantage that (a) the resultant binary works anywhere as it produces unversioned symbols, (b) you don't need to worry about the function prototypes, (c) the C file dummy.c can be automatically produced with a little perl from objdump -T of the .so file. – abligh Jan 11 '14 at 17:22
  • @abligh "I had to build the shared libray via a .o file for it to work" -- it *should* work just as well without split compile/link step. I am curious why it didn't. – Employed Russian Jan 11 '14 at 17:38
  • No idea, but objdump on the result produced 'not a dynamic binary' or some similar error. – abligh Jan 12 '14 at 11:55
1

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
Thomas Leonard
  • 7,068
  • 2
  • 36
  • 40
  • Unfortunately, that's far from ideal as I am trying to automatically build this so I have a single binary that works on both systems (or worse case 2 binaries one that works in each). If I went this way, I'd need to remove the -devel package and reinsert another one between builds, and would still have two binaries. – abligh Jan 09 '14 at 17:52
  • I don't understand what you mean. The above is only needed on the build machine. You should end up with one binary that runs on both real systems. – Thomas Leonard Jan 09 '14 at 19:34
  • I think you are looking at the wrong thing. The version references you have in your post are the DT_NEEDED versioning, which is already correct. My issue is the per-symbol versioning. What I was saying was that the per-symbol versioning will be set in the version map controlled by the ld line for the libssl-devel package that you've just built. Try objdump -T on it. I suppose I could attempt to download libssl-devel and modify it so it has no symbol versioning, build with that and see if that helps, but that seems pretty heavyweight to remove one tag from an ELF file. – abligh Jan 10 '14 at 09:20
  • Yeah, I'd like to find a better way too, but that's what worked for me. Disabling symbols in openssl should be easy enough, since upstream doesn't include them by default (so I guess it's turned on by a patch in the SRPM). – Thomas Leonard Jan 11 '14 at 16:29