Автоматически выполняемые функции при загрузке общих библиотек - PullRequest
39 голосов
/ 18 марта 2012

При загрузке общих библиотек в Windows, вызов LoadLibrary() заставляет DllMain в библиотеке выполняться для каждого нового процесса, к которому присоединяется библиотека потоков, и для каждого процесса и отсоединения библиотеки потоков от.

Есть лианалогичный механизм для Mac OS X, Linux и, возможно, других POSIX-совместимых ОС?

Ответы [ 4 ]

52 голосов
/ 18 марта 2012

Вы можете определить функцию загрузки для библиотеки linux, используя механизм .init.Это то же самое, что и указание точки входа во время загрузки для двоичного файла (например, использование в качестве точки входа для программы чего-то отличного от main).

При прямом связывании с использованием ld вы используете:

-init <function name>

или если вы используете cc / gcc для ссылки, вы используете:

-Wl,-init,<function name>

Это на самом простом уровне.

Редактировать Для деструкторов / финализаторов вы используете механизм .fini.Это работает так же, как опция init, и вы используете:

-fini <function name>

при вызове ld.Доступность ограничена опцией -init на платформе Mac OSX.

Вы также можете использовать синтаксис __attribute__((constructor)) для gcc:

static void con() __attribute__((constructor));

void con() {
    printf("I'm a constructor\n");
}

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

Edit 2 Использование семантики __attribute__((constructor))/__attribute__((destructor)) является наиболее предпочтительным механизмом для языка программирования C / C ++.

Для языка программирования D вам действительно следует использовать конструктор статического модуля /деструктор:

static this() {
    printf("static this for mymodule\n");
}
static ~this() {
    printf("static ~this for mymodule\n");
}

Или конструктор статического класса:

class Foo {
    static this() {
        printf("static this for Foo\n");
    }
}

На это настоятельно указывают в написании win32 DLLS и в спецификации языка относящиеся к статическим конструкторам / деструкторам .

Edit 3 Вам нужно будет связать в .o, который экспортирует подпрограммы конструктора / деструктора, которые позволят использовать статические инициализаторы,Как все, что он должен сделать, это вызвать Runtime.initialize (), это фактически вызывает все статические конструкторы / деструкторы в коде D.

Код заглушки для инициализатора (в файле с именем myshared.d):

import core.runtime;

extern (C) {
    void attach();
    void detach();
}

export void attach() {
    Runtime.initialize();
}

export void detach() {
    Runtime.terminate();
}

Создайте .o для этой заглушки:

 dmd -m32 -c myshared.d

Проверьте имена функций присоединения / отсоединения:

nm myshared.o

Показывает (среди другихвывод):

0000001c S _D8myshared6attachFZv
00000034 S _D8myshared6detachFZv

образец кода .c для вызова этого (в данном случае он называется export.c), мы ссылаемся на имена экспортированных подпрограмм из файла my shared.o:

extern void D8myshared6attachFZv(void);
extern void D8myshared6detachFZv(void);

void __attach(void) __attribute__((constructor));
void __detach(void) __attribute__((destructor));

void __attach(void)
{
    D8myshared6attachFZv();
}

void __detach(void)
{
    D8myshared6detachFZv();
}

Обратите внимание, что ссылки extern void должны использовать искаженное имя экспортируемой функции.Они должны совпадать, иначе код не будет связываться.

скомпилируйте код C, используя:

gcc -m32 -c export.c

свяжите файлы .co и .do вместе, используя:

cc -o libmyshared.dylib -m32 -shared myshared.o export.o -lphobos2

Предполагается, что библиотека phobos2 находится в вашем стандартном пути поиска компоновщика.Значения параметров -m32 для компилятора и компоновщика связаны с тем, что версия компилятора D, которую я создал локально, поддерживает только 32-битную версию.

Это создает файл .dylib, с которым можно связать.Кажется, это работает на основе ограниченного тестирования, которое я провел.Похоже, что поддержка общих объектов / динамических библиотек очень ограничена, поэтому есть большая вероятность, что будет преодолено еще одно препятствие.

11 голосов
/ 18 марта 2012

Чтобы функция выполнялась всякий раз, когда совместно используемая библиотека загружается или выгружается, вы можете пометить функцию конструктора и деструктора, используя синтаксис атрибута GCC:

__attribute__((constructor)) void init(void) { ... }
__attribute__((destructor))  void fini(void) { ... }

Поскольку различные части среды C зависят от того, что инициализируется в стандартном коде .init, добавленном GCC за кулисами, непосредственное использование -Wl,-init,<function name> может привести к сбою вашей программы.

Для получения дополнительной информации см. Libary HOWTO по Функции конструктора и деструктора библиотеки .

6 голосов
/ 18 марта 2012

GCC, а также лязг AFAIK, поддерживают атрибуты конструктора GCC и деструктора.Подробнее см. Как именно работает __attribute __ ((конструктор))?

0 голосов
/ 10 июля 2016

Для C ++ вы можете создать класс и использовать его конструктор и деструктор для инициализации библиотеки.

После этого вам нужно только определить переменную для этого класса.

Пример инициализации openssl в библиотеке:

class InitLibrary {
public:
  InitLibrary() {
    CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
    SSL_library_init(); // Initialize OpenSSL's SSL libraries
    SSL_load_error_strings(); // Load SSL error strings
    ERR_load_BIO_strings(); // Load BIO error strings
    OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
  }

  ~InitLibrary() {
    ERR_remove_state(0);
    CRYPTO_cleanup_all_ex_data();
    ENGINE_cleanup();
  }
};

и добавьте только эту строку в файл cpp: InitLibrary InitLib;

...