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

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

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

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

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

П. С. доступа к функциям API индивидуально, используя функция dlsym и указатели не само по себе привести к повреждению памяти и сбоях. Скорее всего, у вас просто есть ошибки.

EDIT:
Вы можете использовать точно такую же технику с библиотекой 3rd-party. Создать libdrmaa_wrapper.so и поставить api_table в нее. Свяжите обертку непосредственно с libdrmaa.so.

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


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


использовать ниже код типа

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

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

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

Это ofcource стиль c++ метода структуры, предложенный ранее.


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

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

$ implib-gen.py libxyz.so
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ...

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

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

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