четверг, 22 декабря 2011 г.

Загрузка двух нативных библиотек через JNI под линуксом


Недавно по работе появилась редкая для меня задача, излагаю. Скажу сразу, решения в лоб пока не нашел, а workaround-ы мне известны, но для них требуется помощь от поставщика одной из сторонних библиотек, а потому задача более чем актуальна. 
Знатоки JVM И юникса - VERY welcome..
Итак, задача. Есть две нативные либы, А  и Б, или, в нейминга линукса, это libA.so и libB.so. А зависит от Б (должна вызывать функции из нее). А - написана мной, Б - предоставлена сторонними разработчиками в виде одного жалкого .so файла, доступа к исходникам нет.
Мне нужно загрузить эти две либы в JVM через JNI и вызвать функцию из библиотеки А, которая, в свою очередь, использует функцию из Б.
Следующий код:
static {
    System.loadLibrary(B);
    System.loadLibrary(A);
}
Отлично работает в Windows (для .dll файлов, соотетственно), при условии, что обе библиотеки лежат в java.library.path (я тут кстати раньше в блоге писал, как можно менять java.library.path в рантайме).
Как это работает в Windows - загружается B.dll, дальше A.dll пробует загрузиться, видит, что надо бы загрузить B.dll, от которой она зависит, видит, что B.dll в память процесса уже загружена, и не пытается найти и загрузить B самостоятельно, ища ее в PATH.
Теперь, что, по видимому, происходит в линуксе.
Дополнительный логгинг показывает, что libB.so корректно находится в java.library.path и загружается. Затем, рантайм-линкер пытается загрузить libA.so, и замечает, что ВНЕЗАПНО, libB.so в LD_LIBRARY_PATH нет! А менять LD_LIBRARY_PATH я не хочу, не то что неспортивно, но хочется локально решать проблемы. Чтобы уточнить - обе библиотеки находятся в директории, которая входит в java.library.path, но НЕ входит в LD_LIBRARY_PATH).
У кого-нибудь есть какие-нибудь идеи?
Перепробовал немало хаков уже, включая линковку libA.so с уничтожением таблицы символов, загрузку libB.so динамически из libA.so, получение указателя на нужную функцию (манглированную) и вызов ее - не работает ничего из этого так, как мне надо.
Еще раз уточню условия - я не хочу модифицировать LD_LIBRARY_PATH, я не хочу класть свои либы в "общеизвестные" места типа /usr/lib.
Вариант добыть вместо libB.so статически либу (libB.a) и влинковать ее жестко в libA.so мне вариант кажется хорошим, но исходников нет.
Сконвертировать libB.so -> libB.a? Теоритически наверное, можно.

2 комментария:

  1. А посмотри в сторону JNA. В смысле -- у них ведь модель похожая на твою -- есть их собственная JNI-прокладка, которая лежит где-то в java..lib.path. И она зовет функции из системных библиотек, которые лежат где им положено. Похоже на твои требования?

    ОтветитьУдалить
  2. Я, собственно, уже разобрался со своей проблемой..Спасибо в том числе бывшему сотруднику Oracle c Хабра - apangin.

    Дело все было в том, что обе библиотеки (обе, включая third party) должны быть слинкованы с флагом -Wl,soname, чтобы прописать soname библиотеки в .so файл вместо пути к этому файлу. И тогда все это работает как надо.

    Т.е. мне пришлось убедить вендоры этой сторонней либы послать мне объектные файлы (потому что он, по его словам, в линукс-программировании не особенно силен), узнать у него что там от чего зависит, и слинковать из них .so файл самому.

    ОтветитьУдалить