Редактировать: позже я узнал, что описанный ниже метод работает только в ограниченном числе случаев. В частности, ваши общие библиотеки должны содержать только функции без каких-либо глобальных переменных. Если внутри библиотек, к которым вы хотите выполнить диспетчеризацию, есть глобальные переменные, вы получите ошибку динамического компоновщика во время выполнения. Это происходит поскольку глобальные переменные перемещаются до вызова конструкторов разделяемой библиотеки< /а>. Таким образом, компоновщик должен разрешить эти ссылки заранее, прежде чем описанная здесь схема диспетчеризации сможет запуститься.
Один из способов добиться того, чего вы хотите, - это (ab) использовать поле DT_SONAME
в заголовке ELF вашей общей библиотеки. Это можно использовать для изменения имени файла, который динамический загрузчик (ld-linux-so*
) загружает во время выполнения, чтобы устранить зависимость от общей библиотеки. Это лучше всего объяснить на примере. Скажем, я компилирую разделяемую библиотеку libtest.so
с помощью следующей командной строки:
g++ test.cc -shared -o libtest.so -Wl,-soname,libtest_dispatch.so
Это создаст разделяемую библиотеку с именем файла libtest.so
, но в поле DT_SONAME
установлено значение libtest_dispatch.so
. Давайте посмотрим, что произойдет, когда мы свяжем с ним программу:
g++ testprog.cc -o test -ltest
Давайте рассмотрим зависимости библиотеки времени выполнения для получившегося двоичного файла приложения test
:
> ldd test
linux-vdso.so.1 => (0x00007fffcc5fe000)
libtest_dispatch.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd1e4a55000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd1e4e4f000)
Обратите внимание, что вместо поиска libtest.so
динамический загрузчик вместо этого хочет загрузить libtest_dispatch.so
. Вы можете использовать это для реализации необходимых функций диспетчеризации. Вот как бы я это сделал:
Создавайте различные версии вашей общей библиотеки. Я предполагаю, что существует какая-то «общая» версия, которую всегда можно использовать, а другие оптимизированные версии используются во время выполнения по мере необходимости. Я бы назвал универсальную версию «простым» именем библиотеки libtest.so
, а остальные назвал бы так, как вы выберете (например, libtest_sse2.so
, libtest_avx.so
и т. д.).
При связывании универсальной версии библиотеки замените ее DT_SONAME
на что-то другое, например libtest_dispatch.so
.
Создайте библиотеку диспетчера с именем libtest_dispatch.so
. Когда диспетчер загружается при запуске приложения, он отвечает за загрузку соответствующей реализации библиотеки. Вот псевдокод того, как может выглядеть реализация libtest_dispatch.so
:
#include <dlfcn.h>
#include <stdlib.h>
// the __attribute__ ensures that this function is called when the library is loaded
__attribute__((constructor)) void init()
{
// manually load the appropriate shared library based upon what the CPU supports
// at runtime
if (avx_is_available) dlopen("libtest_avx.so", RTLD_NOW | RTLD_GLOBAL);
else if (sse2_is_available) dlopen("libtest_sse2.so", RTLD_NOW | RTLD_GLOBAL);
else dlopen("libtest.so", RTLD_NOW | RTLD_GLOBAL);
// NOTE: this is just an example; you should check the return values from
// dlopen() above and handle errors accordingly
}
Связывая приложение с вашей библиотекой, свяжите его с «ванильным» libtest.so
, у которого DT_SONAME
переопределено, чтобы указать на библиотеку диспетчера. Это делает диспетчеризацию практически прозрачной для любых авторов приложений, использующих вашу библиотеку.
Это должно работать, как описано выше, в Linux. В Mac OS общие библиотеки имеют «установочное имя», аналогичное DT_SONAME
, используемому в общих библиотеках ELF, поэтому вместо этого можно использовать процесс, очень похожий на описанный выше. Я не уверен, можно ли использовать что-то подобное в Windows.
Примечание. Выше было сделано одно важное допущение: совместимость ABI между различными реализациями библиотеки. То есть ваша библиотека должна быть спроектирована таким образом, чтобы было безопасно связываться с наиболее общей версией во время компоновки, используя оптимизированную версию (например, libtest_avx.so
) во время выполнения.
person
Jason R
schedule
10.04.2015