Linking libraries with incompatible dependecies

2019-04-06 02:08发布

问题:

I'm working on a C++ project that needs two third party libraries (libfoo.so and libbar.so). My operating system is Linux.

libfoo.so is dynamically linked to libpng14.so.14 (1.4.8) (EDIT 1)

libbar.so seems to be statically linked to an unknwon version of libpng libpng 1.2.8 (EDIT 1)

I say "seems to be" because:

  • ldd libbar.so doesn't show nothing about png
  • nm -D libbar.so | grep png_read_png says "004f41b0 T png_read_png"
  • less libbar.so | grep png_read_png says "4577: 004f41b0 738 FUNC GLOBAL DEFAULT 10 png_read_png"

When i start my program, it abort:

terminate called after throwing an instance of 'char const*'

This is gdb backtrace:

#0  0xb7ffd424 in __kernel_vsyscall ()
#1  0xb5e776a1 in raise () from /lib/libc.so.6
#2  0xb5e78de2 in abort () from /lib/libc.so.6
#3  0xb60a997f in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/gcc/i686-pc-linux-gnu/4.4.5/libstdc++.so.6
#4  0xb60a78a5 in ?? () from /usr/lib/gcc/i686-pc-linux-gnu/4.4.5/libstdc++.so.6
#5  0xb60a78e2 in std::terminate() () from /usr/lib/gcc/i686-pc-linux-gnu/4.4.5/libstdc++.so.6
#6  0xb60a7a21 in __cxa_throw () from /usr/lib/gcc/i686-pc-linux-gnu/4.4.5/libstdc++.so.6
#7  0xb5abf76d in ?? () from /usr/lib/libfreeimage.so.3
#8  0xb6fb9346 in png_error () from lib/libfsdk.so
#9  0xb6fa2a59 in png_create_read_struct_2 () from lib/libfsdk.so
#10 0xb6fa2b7a in png_create_read_struct () from lib/libfsdk.so
#11 0xb5abfa44 in ?? () from /usr/lib/libfoo.so
#12 0xb5aa766b in FreeImage_LoadFromHandle () from /usr/lib/libfreeimage.so.3
#13 0xb5aa59f6 in FreeImage_LoadFromMemory () from /usr/lib/libfreeimage.so.3
#14 0xb68a94a5 in Foo::Image::load (this=0xb4eff560, input=...)

As you can see, exception is thrown in Foo::Image::load which belong to libfoo.so

Disabling the part of my code that uses libbar.so and removing linking to it, Foo::Image::load doesn't throw any exception and works fine.

So I guess it can be due to some ambiguity in symbol table. How can I fix it?

EDIT 1

png_access_version_number()

  • With libbar.so linked, png_access_version_number() return 10208: version 1.2.8
  • Without libbar.so linked, png_access_version_number() return 10408: version 1.4.8

回答1:

Since you can't rebuild either of the libraries, and since the libraries can not be allowed to reside in the same "dynamic linker namespace" due to conflicting symbols, your only choice is to isolate them.

You can achieve that by using dlopen("lib*.so", RTLD_LOCAL) (for either or both of the libraries), instead of linking to them directly.

This could be workable if you only need a few symbols from e.g. libfoo.so -- you can simply use dlsym instead of calling the functions directly.

If you have "too many" dependencies on both libraries, your other solution may be to build an "interposer" library. Let's say you want to interpose libbar.so, and you need bar1(), bar2(), ... bar1000() from it.

Write (or generate with a simple Perl script) a source file that looks like this:

static void *handle;
void *bar1()
{
   static void* (*pfn)(void *arg1, void *arg2, void *arg3, ..., argN);
   if (pfn == NULL) {
      if (handle == NULL)
        handle = dlopen("libbar.so", RTLD_LOCAL|RTLD_LAZY);
      pfn = dlsym(handle, "bar1");
   }
   return (*pfn)(arg1, arg2, ..., argN);
}
... repeat for all other libbar functions you depend on

Now compile and link this source into libbar_interposer.so and link your application against it (this will not work for C++ due to name mangling, only for plain-C). Voila, no source changes to the application, and you still have isolated libbar.so so its symbols will not be visible to the rest of the application, and in particular will not conflict with any symbols in libpng.