Альтернативы dlsym() и dlopen() в C++

У меня есть приложение, часть которого использует общие библиотеки. Эти библиотеки компонуются во время компиляции.
Во время выполнения загрузчик ожидает, что общий объект будет в LD_LIBRARY_PATH , если он не найден, все приложение вылетает с ошибкой «невозможно загрузить общие библиотеки». Обратите внимание, что нет гарантии, что клиент будет иметь библиотеку, в этом случае я хочу, чтобы приложение оставило подходящее сообщение об ошибке, и независимая часть должна работать правильно.

Для этой цели я использую dlsym() и dlopen() для использования API в общей библиотеке. Проблема в том, что если у меня есть много функций в API, я должен получить к ним доступ по отдельности, используя dlsym() и ptrs, что в моем случае приводит к повреждению памяти и сбоям кода.

Есть ли альтернативы для этого?


person sud03r    schedule 01.07.2009    source источник


Ответы (5)


Обычное решение вашей проблемы состоит в том, чтобы объявить таблицу указателей на функции, выполнить один dlsym(), чтобы найти ее, а затем вызвать все остальные функции через указатель на эту таблицу. Пример (непроверенный):

// libfoo.h
struct APIs {
   void  (*api1)(void);
   void *(*api2)(int);
   long  (*api3)(int, void *);
};

// libfoo.cc
void fn1(void) { ... }
void *fn2(int) { ... }
long fn3(int, void *) { ... }

APIs api_table = { fn1, fn2, fn3 };


// client.cc
#include "libfoo.h"
...
  void *foo_handle = dlopen("libfoo.so", RTLD_LAZY);
  if (!foo_handle) {
     return false;            // library not present
  }
  APIs *table = dlsym(foo_handle, "api_table");
  table->api1();              // calls fn1
  void *p = table->api2(42);  // calls fn2
  long x = table->api3(1, p); // calls fn3

P.S. Индивидуальный доступ к функциям API с помощью dlsym и указателей не сам по себе приводит к повреждению памяти и сбоям. Скорее всего у вас просто баги.

РЕДАКТИРОВАТЬ:
Вы можете использовать тот же метод со сторонней библиотекой. Создайте libdrmaa_wrapper.so и поместите в него api_table. Свяжите оболочку напрямую с libdrmaa.so.

В основном исполняемом файле dlopen("libdrmaa_wrapper.so", RTLD_NOW). Этот dlopen будет успешным, если (и только если) libdrmaa.so присутствует во время выполнения и предоставляет все функции API, которые вы использовали в api_table. Если это удастся, один вызов dlsym даст вам доступ ко всему API.

person Employed Russian    schedule 01.07.2009
comment
и как мне вызвать dlopen(), чтобы получить foo_handle? Я имею в виду, будет ли он автоматически загружать общий объект API, скажем, libAPI.so ?? - person sud03r; 01.07.2009
comment
На самом деле это dlopen() для открытия библиотеки ... :) .. в любом случае, я понял вашу точку зрения ... но это работает, только если в библиотеке есть структура API ... чего вы не можете ожидать от сторонней библиотеки ..Я пытаюсь использовать sungrid api libdrmaa.so.. - person sud03r; 01.07.2009
comment
Вместо получения указателя на таблицу вы могли бы вместо этого получить указатель на функцию, которая при вызове возвращает указатель на таблицу. Это позволяет плагину инициализировать себя перед вызовом любой другой функции и даже динамически создавать таблицу. - person CesarB; 01.07.2009
comment
Например, PKCS#11 определяет для этой цели C_GetFunctionList. Вот пример его использования: stackoverflow.com/a/16132459/729705 (обычно загружать библиотеки PKCS#11 с dlopen, потому что автор приложения не будет знать, какие криптографические токены хочет использовать пользователь.Они могут даже использовать несколько разных библиотек PKCS#11 одновременно — каждая из них имеет функцию C_GetFunctionList, а дескриптор dlopen, переданный dlsym, говорит, какая библиотека вам нужна функция для.) - person armb; 24.01.2014
comment
Можно скрыть таблицу указателей функций в статической библиотеке-заглушке, аналогично тому, как это делает Windows. Это делает использование более прозрачным. Такие библиотеки могут создаваться автоматически (полный пример см. на Implib.so). - person yugr; 16.02.2018

Вы можете обернуть свое приложение другим, которое сначала проверяет наличие всех необходимых библиотек, и если чего-то не хватает, оно красиво выдает ошибку, но если все в порядке, оно выполняет настоящее приложение.

person florin    schedule 01.07.2009
comment
Приложение довольно часто имеет сценарий, который устанавливает LD_LIBRARY_PATH перед запуском приложения. - person Martin York; 01.07.2009
comment
Проблема не в этом.. проблема в том, что если библиотеки нет с клиентом, то модуль не должен работать, остальной код должен работать правильно.. но в этом случае, если библиотека не найдена, код не выполняется . - person sud03r; 01.07.2009

Используйте приведенный ниже тип кода

Class DynLib
{
    /* All your functions */
    void fun1() {};
    void fun2() {};
    .
    .
    .
}

DynLib* getDynLibPointer()
{
    DynLib* x = new Dynlib;
    return x;
}

используйте dlopen() для загрузки этой библиотеки во время выполнения. и используйте dlsym() и вызовите getDynLibPointer(), который возвращает объект DynLib. из этого объекта вы можете получить доступ ко всем своим функциям как obj.fun1().....

Это, конечно же, метод struct в стиле C++, предложенный ранее.

person Sriram.K    schedule 04.05.2013

Вы, вероятно, ищете какую-то форму загрузки библиотеки задержки в Linux. Он недоступен в готовом виде, но вы можете легко имитировать его, создав небольшую статическую библиотеку-заглушку, которая попытается dlopen получить необходимую библиотеку при первом вызове любой из ее функций (отправив диагностическое сообщение и завершив работу, если dlopen не удалось), а затем переадресация всех звонков на него.

Такие библиотеки-заглушки могут быть написаны вручную, сгенерированы скриптом, специфичным для проекта/библиотеки, или сгенерированы универсальным инструментом Implib. Итак:

$ implib-gen.py libxyz.so
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ...
person yugr    schedule 16.02.2018

Ваша проблема в том, что разрешение неразрешенных символов выполняется очень рано - я считаю, что в Linux символы данных разрешаются при запуске процесса, а функциональные символы выполняются лениво. Поэтому в зависимости от того, какие символы у вас не разрешены, и от того, какая у вас статическая инициализация, у вас может не быть возможности войти в свой код.

Мое предложение состояло бы в том, чтобы иметь приложение-оболочку, которое перехватывало бы строку кода возврата/ошибки «невозможно загрузить общие библиотеки», а затем преобразовывало бы это во что-то более значимое. Если это общий файл, его не нужно будет обновлять каждый раз, когда вы добавляете новую общую библиотеку.

В качестве альтернативы вы можете запустить сценарий-оболочку ldd, а затем проанализировать вывод, ldd сообщит обо всех библиотеках, которые не найдены для вашего конкретного приложения.

person Beano    schedule 01.07.2009