LINUX.ORG.RU

Библиотеки .so, их использование и разработка


0

0

Всем доброго дня!

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

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

Во-вторых, исследуя получающиеся на выходе компилятора .so с помощью readelf обнаружил, что среди символов лежат все присутствующие в коде библиотеки функции. Это не есть хорошо: они занимают лишнее место, да и незачем выставлять на показ внутренние функции. В этом вопросе мне вспоминается опыт написания dll в NT - там я помечал функции, которые я хочу экспортировать. Более того, мне вспоминается, что при ручном импортировании функций из библиотеки в NT как-то можно было вообще не использовать имена функций, а лишь ее номер. Мне кажется в данном случае это было бы даже оправданно. И выкинуть имена всех символов, чтобы кто не знает - не пользовался. Можно ли сделать экспорт только нужных функций и по возможности с доступом не по именам, а по номерам?

В-третьих, в статье "Анатомия динамических библиотек Linux" прочитал, что при подгрузке библиотеки, у нее автоматически вызывается функция инициализации. Иногда ее иметь действительно полезно. Но, действуя по описанию, фактически я только добавил ключ компиляции у gcc и не создавал никаких процедур. Компилятор не заругался. Можно ли добавить процедуру инициализации и деинициализации в библиотеку или в статье наврали?

И, наверное, тупой вопрос. Можно ли загрузить библиотеку из ее mmap образа или напрямую из фрагмента файла без сохранения на диск во временный файл? Это для быстрого холодного старта из архива поставки продукта.

Re: Библиотеки .so, их использование и разработка

во-первых:

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

во-вторых:

для неэкспортирования существует static

в-третьих:

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

И:

да, вопрос тупой.

Deleted ()

Re: Библиотеки .so, их использование и разработка

1 и 3 делается так.

struct CoreSingleton {
   virtual void core_function1() = 0;
   virtual void core_function2() = 0;
...
};


...
void load_module(const char * module_name)
{
   void * h = dlopen(module_name);
   func_t init = dlsym(h, "init_function");
   init(core_singleton);
....
}

И тем самым мы убили сразу двух зайцев.

2. делается установкой атрибута visibility. Но тем самым ты привязываешь себя к gcc. http://gcc.gnu.org/wiki/Visibility

Reset ★★★★★ ()

Re: Библиотеки .so, их использование и разработка

а static теперь не в моде?

А если so линкуется из нескольких объектных файлов, использующих одну и туже функцию, но которая не должна быть видна в наружу so?

Dead ★★★★ ()

Re: Библиотеки .so, их использование и разработка

>А если so линкуется из нескольких объектных файлов, использующих одну и туже функцию, но которая не должна быть видна в наружу so?

в этом случае нужно избавляться от причины, а не бороться со следствием.

Deleted ()

Re: Библиотеки .so, их использование и разработка

Брррр... или я чего-то не понял, или Вы :) если будет две одинаковые ф-ции в разных объектниках и при этом они собираются в одну .so, это разве не грабли? или это нормально по-вашему?

Deleted ()

Re: Библиотеки .so, их использование и разработка

Брррр... или я чего-то не понял, или Вы :) если будет две одинаковые ф-ции в разных объектниках и при этом они собираются в одну .so, это разве не грабли? или это нормально по-вашему?

Мне кажется, вы не совсем верно поняли вопрос топикпастера. Ему нужен был аналог директивы dllexport из винды. static вещь полезная, но она не совсем для этого.

Dead ★★★★ ()

Re: Библиотеки .so, их использование и разработка

Во-первых мы говорим о разных вещах, а во-вторых это надо еще умудриться так сделать:

$ g++ file1.cpp file2.cpp -shared -o 1.so -fpic
/tmp/ccQSTXzO.o: In function `func()':
file2.cpp:(.text+0x0): multiple definition of `func()'
/tmp/cc4WBWbh.o:file1.cpp:(.text+0x0): first defined here
collect2: ld returned 1 exit status
Reset ★★★★★ ()

Re: Библиотеки .so, их использование и разработка

Можно ли добавить процедуру инициализации и деинициализации в библиотеку или в статье наврали?

можно

m.h

#include <stdio.h>

typedef struct {
  char *name;
} plugin_data_t;

void add_plugin (plugin_data_t *data);
void del_plugin (plugin_data_t *data);

static plugin_data_t *plugin_data;

static void ctor (void) __attribute__ ((constructor));
static void ctor (void) { add_plugin (plugin_data); }
static void dtor (void) __attribute__ ((destructor));
static void dtor (void) { del_plugin (plugin_data); }

m.c

#include <dlfcn.h>

#include "m.h"

void 
add_plugin (plugin_data_t *data)
{
  if (data)
    printf ("add plugin %s\n", data->name);
}

void 
del_plugin (plugin_data_t *data)
{
  if (data)
    printf ("remove plugin %s\n", data->name);
}

int 
main ()
{
  void *h;

  h = dlopen ("p.so", RTLD_LAZY);
  dlclose (h);
  
  return 0;
}

p.c

#include "m.h"

static plugin_data_t descr = {
  .name = "SimplePlugin"
};

static plugin_data_t *plugin_data = (plugin_data_t *) & descr;

(victor@toshiba)~/ttt $> gcc -l dl -rdynamic -o m m.c                                                [sh]
(victor@toshiba)~/ttt $> gcc -shared -o p.so p.c                                                     [sh]
(victor@toshiba)~/ttt $> ./m                                                                         [sh]
add plugin SimplePlugin
remove plugin SimplePlugin
(victor@toshiba)~/ttt $>                                                                             [sh]
ananas ★★★★★ ()

Re: Библиотеки .so, их использование и разработка

>static void ctor (void) __attribute__ ((constructor));
>static void ctor (void) { add_plugin (plugin_data); }

>static void dtor (void) __attribute__ ((destructor));

>static void dtor (void) { del_plugin (plugin_data); }


абалдеть :) и не знал. я правильно понимаю, что сие точит приложение под gcc? или так это где-то еще есть?

Deleted ()

Re: Библиотеки .so, их использование и разработка

> я правильно понимаю, что сие точит приложение под gcc?

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

ananas ★★★★★ ()

Re: Библиотеки .so, их использование и разработка

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

в http://ladweb.net/ говорится, что если экспортируется _init, то он выполнится до возвращения управления от dlopen

dimon555 ★★★★★ ()

Re: Библиотеки .so, их использование и разработка

Это как раз-таки то, о чем привел пример ananas. Вот выдержка из мана по dlopen:

The obsolete symbols _init and _fini

The linker recognizes special symbols _init and _fini. If a dynamic library exports a routine named _init, then that code is executed after the loading, before dlopen() returns. If the dynamic library exports a routine named _fini, then that routine is called just before the library is unloaded. In case you need to avoid linking against the system startup files, this can be done by giving gcc the "-nostartfiles" parameter on the command line. Using these routines, or the gcc -nostartfiles or -nostdlib options, is not recommended. Their use may result in undesired behavior, since the constructor/destructor routines will not be executed (unless special measures are taken).

Instead, libraries should export routines using the __attribute__((constructor)) and __attribute__((destructor)) function attributes. See the gcc info pages for information on these. Constructor routines are executed before dlopen() returns, and destructor routines are executed before dlclose() returns.

Deleted ()

Re: Библиотеки .so, их использование и разработка

Спасибо за ответы! с использованием функции _init и методики из http://www.linux.org.ru/view-message.jsp?msgid=3984098 получается действительно довольно элегантное решение! Про _init почему-то в документации проглядел, виноват:(. Останется только видимость функций прикрутить и будет загляденье.

codergeneration ()

Re: Библиотеки .so, их использование и разработка

Очевидно, что если пишем на Си, то надо делать структуру с указателями на функции, а не структуру с виртуальными функциями.

Reset ★★★★★ ()

Re: Библиотеки .so, их использование и разработка

То есть ты своими руками предложил человеку не написать extern "C"? Он же следующим ходом спросит: "А почему dlsym("core") возвращает NULL?".

linuxfan ()

Re: Библиотеки .so, их использование и разработка

А адрес экземпляра структуры получается при помощи святого духа? Или ты просто демонстрировал ручную инициализацию? Но тогда к чему фрагмент виртуальных методов тут?

linuxfan ()

Re: Библиотеки .so, их использование и разработка

Структура создается в основной программе и передается модулю с помощью init_function. Виртуальные методы нужны, чтобы не было проблем с линковкой модуля.

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