Уменьшите количество экспортированных символов сторонней библиотеки

У меня есть код, который отлично работает, но если я свяжу свой проект со сторонней библиотекой libabc.so (источник недоступен), то внезапно я получу ошибку сегментации.

У меня есть главная, которая выглядит так

#include <opencv2/imgcodecs.hpp>
#include "Abc.h"

int main(int argc, char **argv)
{
    Abc dummyAbc;
    auto img = cv::imread("dummy.png");
    cv::imwrite("123.png", img);
    return 0;
}

CMakeList.txt выглядит следующим образом

cmake_minimum_required(VERSION 3.1)
set(CMAKE_C_STANDARD 11)
find_package(OpenCV COMPONENTS core highgui imgcodecs)
include_directories(${OpenCV_INCLUDE_DIR})

add_executable(my_project Main.cpp)
target_link_libraries(my_project ${OpenCV_LIBRARIES} abc)

Это хорошо компилируется, но segfault при запуске. Если я удалю строку

Abc dummyAbc;

тогда все работает нормально (т.е. нет проблем с отсутствующим файлом или opencv).

Если я проверю стек segfault, я увижу, что:

Thread 1 "my_project" received signal SIGSEGV, Segmentation fault.
0x00007fdea96836b3 in png_destroy_write_struct () from /usr/local/lib/libabc.so

где png_destroy_write_struct вызывается cv::imwrite.

И libpng.so, и libabc.so (!!) export png_destroy_write_struct на самом деле экспортируют весь API libpng (с которым, я полагаю, он был статически связан?). Я предполагаю, что это проблема? Я не хочу, чтобы openCV видел все, что экспортирует libabc.so... Как я могу это сделать?

Пробовал использовать objcopy --prefix-symbols abc_ libabc.so но как-то не помогло, теперь вылет происходит на abc_png_destroy_write_struct.


person Al Wld    schedule 24.11.2019    source источник


Ответы (3)


Я так понимаю проблема в этом?

Да: это очень вероятно: libabc.so имеет статически связанную (вероятно, другую версию) libpng и вводит конфликт символов.

Я не хочу, чтобы openCV видел, что экспортирует libabc.so... Как я могу это сделать?

Вы не можете. Вы должны связаться с libabc.so разработчиком и попросить их скрыть libpng символов.

Единственный другой вариант (для выполнения одного процесса) — динамически загружать libabc.so.

Это можно сделать с помощью dlopen("liabc.so.", RTLD_LOCAL), и даже это может не сработать (в зависимости от того, как именно было связано libabc.so) -- это может привести к тому, что libabc.so привяжется к вашей версии libpng и произойдет сбой.

В Linux вы также можете использовать dlmopen(LM_ID_NEWLM, "libabc.so", ...), который будет полностью изолировать libabc.so от остального кода и может работать, если libabc.so будет связан для включения всех его зависимостей (или вы можете явно перенести их в новое пространство имен загрузчика).

Наконец, как прокомментировал здесь Элджей, вы можете использовать межпроцессное взаимодействие и иметь совершенно отдельную загрузку процесса libabc.so. Это будет иметь гораздо худшую производительность, чем использование libabc.so напрямую, но это лучше, чем ничего.

person Employed Russian    schedule 24.11.2019
comment
Спасибо за Ваш ответ. Можно ли каким-то образом создать мою оболочку вокруг libabc.so, чтобы я вручную скрывал/выставлял некоторые методы (скажем, только те, что в abc.h) и ссылался на эту новую оболочку? - person Al Wld; 24.11.2019
comment
@AlWld Возможно ли .... Нет. - person Employed Russian; 24.11.2019
comment
Это было бы возможно, но это требует запуска его вне процесса и использования межпроцессного взаимодействия. - person Eljay; 24.11.2019
comment
Почему бы не упомянуть ручную модификацию .dynsym, как указано в вашей другой ответ? - person yugr; 25.11.2019
comment
@yugr Другой ответ (stackoverflow.com/questions/36273404/) применяется только тогда, когда вы можете повторно связать файл .so. Изменение самого .so может привести к тому, что хеш-таблицы станут несогласованными, что приведет к сбою в ld-linux. - person Employed Russian; 25.11.2019

Чтобы добавить к ответу EmployedRussian об использовании подхода на основе dlmopen для изоляции libabc.so от остального кода: чтобы избежать возни с dlsym и указателями функций, в этом случае вы можете автоматически создавать оболочки для необходимых библиотечных функций через Implib.so:

$ cat mysymbols.txt
foo
bar
$ cat mycallback.c
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C"
#endif

// Dlopen callback that loads library to dedicated namespace
void *mycallback() {
  void *h = dlmopen(LM_ID_NEWLM, "libabc.so", RTLD_LAZY | RTLD_DEEPBIND);
  if (h)
    return h;
  fprintf(stderr, "dlmopen failed: %s\n", dlerror());
  exit(1);
}

$ implib-gen.py --dlopen-callback=mycallback --symbol-list=mysymbols.txt libabc.so
$ ... # Link your app with libabc.tramp.S, libabc.init.c and mycallback.c
person yugr    schedule 25.11.2019