LINUX.ORG.RU

Как пользоваться динамическими библиотеками?

 , ,


0

2

Здравствуйте

Прочитал ман по dlfcn.h, dlopen. Так и не понял, возможно ли просто сдедать dlopen(какие_то_флаги) и пользоваться функциями из либы? Или обязательно надо заводить ссылку ка каждую функцию и инициализировать ее через dlsym?

★★★★★

Или обязательно надо заводить ссылку ка каждую функцию и инициализировать ее через dlsym?

Ага. Хотя чаще создают структуру с указателями на нужные функции и экспортируют уже её. Это самый простой вариант, если ты хочешь систему плагинов запилить.

hateyoufeel ★★★★★ ()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Хотя чаще создают структуру с указателями на нужные функции и экспортируют уже её. Это самый простой вариант

Все-равно сложна. Хотя может и нет, пока не получилось обернуть свой мозг вогруг этой штуки)

если ты хочешь систему плагинов запилить

У меня 2 so-файла с одинаковым набором функций. Один файл с реальной програмой. Второй - макет, эмулилирующий работу низлежащего оборудования.

Хочется, в зависимости от опции в конфиг-файле пользоваться тем или другим. Но не хочется мудрить с dlsym.

makoven ★★★★★ ()

Сделай один экспорт. Через экспорт передавай указатель на структуру с указателями на функцию в либу, а либа пусть заполняет. Самый простой метод.

Есть С++ метод — пусть экспорт возвращает указатель на объект класса с pure virtual методами.

a1batross ★★★★★ ()

Можно нагенерить заглушек в своём коде, объявляя их __attribute__((weak)). При загрузке библиотеки её символы перекроют заглушки и вызовы будут перенаправляться в библиотеку. Решение кривое, и скорее всего поломается, если стрипнуть бинарник.

i-rinat ★★★★★ ()
Ответ на: комментарий от CYB3R

подозреваю, у меня пара месяцев уйдет на освоение glib и его объектной системы)

makoven ★★★★★ ()
Ответ на: комментарий от makoven

У меня 2 so-файла с одинаковым набором функций. Один файл с реальной програмой. Второй - макет, эмулилирующий работу низлежащего оборудования. Хочется, в зависимости от опции в конфиг-файле пользоваться тем или другим. Но не хочется мудрить с dlsym.

Тогда есть ещё вариант сделать враппер - структуру с указателями на функции, инициализирующуюся в зависимости от конфига. (В случае C++ это была бы иерархия из 3 классов.) Вызываться будут либо обёртки для импортированных из реальной программы функций, либо функции макета. Сам макет при таком подходе является частью вызывающей программы. Если это неприемлемо, то увы. Но работа с .so будет намного проще...

hobbit ★★★★★ ()
Ответ на: комментарий от makoven

Экспорт — это то, что ты получишь из библиотеки A в библиотеке B. (Для этого еще надо атрибут проставить у функции —

__attribute__((visibility("default")))
)

Ты получишь этот экспорт в библиотеке B посредством dlsym/GetProcAddress, в аргументы засунешь ему структуру с указателями на функцию, а библиотека А заполнит ее собственными функциями.

a1batross ★★★★★ ()
Ответ на: комментарий от makoven

У меня 2 so-файла с одинаковым набором функций. Один файл с реальной програмой. Второй - макет, эмулилирующий работу низлежащего оборудования.

Хочется, в зависимости от опции в конфиг-файле пользоваться тем или другим. Но не хочется мудрить с dlsym.

Можно сделать хак: слинковать программу обычным способом без dlopen, но в зависимости от опции в конфиг-файле перезапускать её с нужным LD_LIBRARY_PATH или LD_PRELOAD.

proud_anon ★★★★★ ()
Ответ на: комментарий от proud_anon

перезапускать её с нужным LD_LIBRARY_PATH или LD_PRELOAD.

для этого у библиотек должен быть одинаковый SONAME.

i-rinat ★★★★★ ()
Ответ на: комментарий от a1batross

Для этого еще надо атрибут проставить у функции

По умолчанию они все экспортируются. Этот атрибут нужен, только если компилятор запускается с ключом -fvisibility=hidden.

i-rinat ★★★★★ ()
Ответ на: комментарий от i-rinat

Ну, не помешает. Подумаешь, потратит чуть больше времени компилятор на парсинг атрибута.

a1batross ★★★★★ ()
Ответ на: комментарий от makoven

а что будет, если в ld_preload указать макет, и при этом слинковать програму с реальной so-шкой?

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

Если ты динамически слинкуешь программу с библиотекой, то она слинкуется не с каким-то конкретным файлом, а с библиотекой, идентифицируемой её SONAME, который вписывается в исполняемый файл. При запуске исполняемого файла линкер будет искать эту библиотеку по определённому алгоритму. Если в LD_PRELOAD (или даже в LD_LIBRARY_PATH) будет твой «макет» с нужным SONAME, то этот макет и будет загружен.

proud_anon ★★★★★ ()
Ответ на: комментарий от makoven

У меня 2 so-файла с одинаковым набором функций. Один файл с реальной програмой. Второй - макет, эмулилирующий работу низлежащего оборудования.

Хочется, в зависимости от опции в конфиг-файле пользоваться тем или другим. Но не хочется мудрить с dlsym.

Одинаковые наборы функций: тогда как сказал proud_anon с замечанием от i-rinat.

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

Если набор функций тот же, то всё в норме. А если нет, то это уже другая задача, и без ещё одной библиотеки, которая будет абстрагировать эти три, используя dlsym(), похоже, не обойтись.

gag ★★★★★ ()
Последнее исправление: gag (всего исправлений: 1)
Ответ на: комментарий от makoven

возможно, будет и третья so-шка

Ну да, тогда лучше сразу завязываться на плагиноподобную систему и динамическую загрузку.

Хотя тут интересный вопрос... Про третью so-шку информация есть, у неё API такой же, как у первой хардварной? Если первое, то однозначно плагины. Если нет - то возможно, вариант с враппером таки предпочтительнее, если всё равно под каждую свои функции объявлять...

hobbit ★★★★★ ()
Ответ на: комментарий от gag

А если нет, то это уже другая задача, и без ещё одной библиотеки, которая будет абстрагировать эти три, используя dlsym(), похоже, не обойтись.

Есть ещё вариант - прикладная программа грузит плагины-переходники по единому интерфейсу через dlopen, а уже те подгружают искомые so средствами ОС и нивелируют различия в наборе функций. ЕМНИП, так некоторые плееры работают: собственные плагины с единым API, а те уже вызывают кодеки форматов, которые по API могут быть довольно разнородными. Кода получается многовато, но по архитектуре вполне изящно и единообразно.

Но я бы в случае задачи ТС-а и предполагая, что различия между so придётся учитывать, всё-таки сделал в вызывающей программе структуру-надстройку: и отлаживать проще, и абстракций поменьше.

hobbit ★★★★★ ()
Последнее исправление: hobbit (всего исправлений: 1)
Ответ на: комментарий от hobbit

hobbit, gag

Спасибо. Буду пробовать разные варианты, выбирать что больше понравится.

у неё API такой же, как у первой хардварной?

Да, пара шлюзов для шины cbus. Один по tty, второй по tcp, но протокол более-менее одинаковый. Оберну в стандартный интерфейс и буду подсовывать или то или другое (или макет, для демки)

makoven ★★★★★ ()
Ответ на: комментарий от hobbit

Сложно. Неужели техническии было невозможно сделать, чтобы dlopen автоматически связывала имена функций в экзешнике с именами функций в открываемой so-шке

makoven ★★★★★ ()
Ответ на: комментарий от makoven

Мне такой способ неизвестен, и даже если он есть, он очевидно будет 1) кривой; 2) непереносимый; 3) неочевиден для восприятия любым, кто через полгода будет копаться в коде, включая автора.

Кроме того, если so-шки писали разные люди в разных проектах, то у них почти наверняка даже в случае «идейной схожести» кроме разных имён функций будут и «чуть-чуть разные» параметры, а тут уже никакое «автоматическое связывание» не поможет. А если их пишет один человек или разные люди, но по одному руководящему документу - то что мешает и имена функций унифицировать?

Короче, если имена/параметры функций идентичны, можно сделать dlopen с плагинами и dlsym на каждую функцию. А если разные - то либо выбор (например, через враппер) в вызывающей программе (dlopen тогда вообще не нужен, если so друг с другом не конфликтуют самим фактом загрузки, их можно средствами ОС грузить)... либо опять плагины с so-переходниками. Сложно? Да, сложно, унификация разных интерфейсов по определению непростая задача, даже если они отличаются какой-то мелочью.

hobbit ★★★★★ ()
Последнее исправление: hobbit (всего исправлений: 2)
Ответ на: комментарий от makoven

чтобы dlopen автоматически связывала

Но компилятору это не видно: именно ему нужно как-то разрулить

имена функций в экзешнике

чтобы этот исполняемый файл получить.

А вообще если хочется автоматизации, так это возможно. И, наверняка, уже кто-то такое сделал. Библиотечка для подгрузки .so, которая проходит по всем доступным именам и сохраняет их вместе с указателем в map. В итоге вместо невозможного:

int foo1(int);

int f = foo1(123);
получаем что-то вроде:
typedef int (*foo1_tt)(int);

int f = ((foo1_tt)get_func_from_map("foo1"))(123);

Ну или кодогенератор.

gag ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.