views:

122

answers:

3

Hi, I have an application that statically links with version X of a library, libfoo, from thirdparty vendor, VENDOR1. It also links with a dynamic (shared) library, libbar, from a different thirdparty vendor, VENDOR2, that statically links version Y of libfoo from VENDOR1.

So libbar.so contains version Y of libfoo.a and my executable contains version X of libfoo.a libbar only uses libfoo internally and there are no libfoo objects passed from my app to libbar.

There are no errors at build time but at runtime the app seg faults. The reason seems to be that version X uses structures that have a different size they version Y and the runtime linker seems to be mixing up which get used by which.

Both VENDOR1 & VENDOR2 are closed source so I cannot rebuild them.

Is there a way to build/link my app such that it always resolves to version X and libbar alway resolves to version Y and the two never mix?

+1  A: 

Sorry no. My understanding of the way that Linux (and possibly most *nixes) is that that is not possible. The only 'solution' for your problem I can think of, is if you create a proxy app, which exposes what you need from libbar in the form of some IPC. You can then make that proxy load the correct version using LD_LIBRARY_PATH or something simmilar.

Gianni
@Gianni: "and possibly most *nixes" - except for AIX. On AIX that is actually possible and the default linker behavior does precisely what question asks.
Dummy00001
OS X also handles this gracefully. Each .so/.dylib has its own link table/references. I said *most* precisely because I know it weren't all. Anyway, Linux does not do this, AFAIK.
Gianni
+1  A: 

Try a partial link so that you have an object file "partial.o" with libbar and libfoo-Y. Use objcopy with "--localize-symbols " to make the symbols in partial.o from libfoo-Y local. You should be able to generate by running nm on libfoo-Y and massaging the output. Then take the modified partial.o and link it to your app.

I've done something similar with gcc toolchain on vxWorks where dynamic libs are not a complication but two versions of the same lib needed to link cleanly into a monolithic app.

bstpierre
+1  A: 

Thanks for all the responses. I have a solution that seem to be working. Here's the problem in detail with an example.

In main.c we have:

#include <stdio.h>

extern int foo();

int bar()
{
    printf("bar in main.c called\n");
    return 0;
}

int main()
{
    printf("result from foo is %d\n", foo());
    printf("result from bar is %d\n", bar());
}

In foo.c we have:

extern int bar();

int foo()
{
    int x = bar();
    return x;
}

In bar.c we have:

#include <stdio.h>

int bar()
{
    printf("bar in bar.c called\n");
    return 2;
}

Compile bar.c and foo.c:

$ gcc -fPIC -c bar.c
$ gcc -fPIC -c foo.c

Add bar.o to a static library:

$ ar r libbar.a bar.o

Now create a shared library using foo.o and link with static libbar.a

$ gcc -shared -o libfoo.so foo.o -L. -lbar

Compile main.c and link with shared library libfoo.so

$ gcc -o main main.c -L. -lfoo

Set LD_LIBRARY_PATH to find libfoo.so and run main:

$ setenv LD_LIBRARY_PATH `pwd`
$ ./main
bar in main.c called
result from foo is 0
bar in main.c called
result from bar is 0

Notice that the version of bar in main.c is called, not the version linked into the shared library.

In main2.c we have:

#include <stdio.h>
#include <dlfcn.h>


int bar()
{
    printf("bar in main2.c called\n");
    return 0;
}

int main()
{
    int x;
    int (*foo)();
    void *handle = dlopen("libfoo.so", RTLD_GLOBAL|RTLD_LAZY);
    foo = dlsym(handle, "foo");
    printf("result from foo is %d\n", foo());
    printf("result from bar is %d\n", bar());
}

Compile and run main2.c (notice we dont need to explicitly link with libfoo.so):

$ gcc -o main2 main2.c -ldl
$ ./main2
bar in bar.c called
result from foo is 2
bar in main2.c called
result from bar is 0

Now foo in the shared library calls bar in the shared library and main calls bar in main.c

I don't think this behaviour is intuitive and it is more work to use dlopen/dlsym, but it does resolve my problem.

Thanks again for the comments.

YerBlues