Альтернативы 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

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

РЕДАКТИРОВАТЬ:
Вы можете использовать эту же технику со сторонней библиотекой. Создать 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. из этого объекта вы можете получить доступ ко всем своим функциям JST как obj.fun1().....

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

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

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

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

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

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

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

Другие вопросы по тегам