Связывание с несколькими версиями библиотеки

У меня есть приложение, которое статически связывается с версией X библиотеки libfoo от стороннего поставщика VENDOR1. Он также связывается с динамической (общей) библиотекой libbar от другого поставщика третьей стороны VENDOR2, который статически связывает версию Y libfoo с VENDOR1.

Итак, libbar.so содержит версию Y libfoo.a, а мой исполняемый файл содержит версию X libfoo.a libbar использует только libfoo внутри, и нет никаких объектов libfoo, переданных из моего приложения в libbar.

Во время сборки ошибок нет, но во время выполнения приложения seg faults. Причина, по-видимому, заключается в том, что в версии X используются структуры, размер которых отличается от версии Y, и компоновщик времени исполнения, кажется, смешивается, и к ним привыкнуть.

Оба VENDOR1 и VENDOR2 являются закрытыми, поэтому я не могу их перестроить.

Есть ли способ создать/связать мое приложение так, чтобы он всегда разрешал версию X и libbar всегда разрешал версию Y, а два никогда не смешивались?

Ответ 1

Спасибо за все ответы. У меня есть решение, которое, похоже, работает. Вот проблема подробно с примером.

В main.c мы имеем:

#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());
}

В foo.c мы имеем:

extern int bar();

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

В bar.c мы имеем:

#include <stdio.h>

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

Скомпилируйте bar.c и foo.c:

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

Добавить bar.o в статическую библиотеку:

$ ar r libbar.a bar.o

Теперь создайте общую библиотеку, используя foo.o и ссылку со статическим libbar.a

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

Скомпилируйте main.c и ссылку с общей библиотекой libfoo.so

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

Задайте LD_LIBRARY_PATH, чтобы найти libfoo.so и запустить 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

Обратите внимание, что вызывается версия bar в main.c, а не версия, связанная с общей библиотекой.

В main2.c мы имеем:

#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());
}

Скомпилируйте и запустите main2.c(обратите внимание, что нам не нужно явно ссылаться на 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

Теперь foo в панели вызовов разделяемых библиотек в общей библиотеке и главной панели вызовов в main.c

Я не думаю, что это поведение является интуитивным, и больше работать с dlopen/dlsym, но оно устраняет мою проблему.

Еще раз спасибо за комментарии.

Ответ 2

Попробуйте частичную ссылку, чтобы у вас был объектный файл "partial.o" с libbar и libfoo-Y. Используйте objcopy с "--localize-symbols", чтобы сделать символы в partial.o из libfoo-Y локальными. Вы должны иметь возможность генерировать, запустив nm на libfoo-Y и массируя вывод. Затем возьмите измененный partial.o и привяжите его к своему приложению.

Я сделал что-то подобное с gcc toolchain на vxWorks, где динамические библиотеки не являются сложностью, но две версии одной и той же библиотеки, необходимые для чистой связи в монолитном приложении.

Ответ 3

Извините, нет. Мое понимание того, как Linux (и, возможно, большинство * nixes) заключается в том, что это невозможно. Единственное "решение" для вашей проблемы, о котором я могу думать, - это создать прокси-приложение, которое раскрывает то, что вам нужно, от libbar в виде некоторого IPC. Затем вы можете заставить этот прокси загрузить правильную версию, используя LD_LIBRARY_PATH или что-то похожее.