Заполнение глобальных указателей на функции в общей библиотеке (Solaris, Sun Studio)

#c #shared-libraries #solaris #sunstudio #dlsym

#c #общие библиотеки #solaris #sunstudio #dlsym

Вопрос:

Я создаю небольшую разделяемую библиотеку-оболочку C вокруг библиотеки Fortran 95. Поскольку символы Fortran содержатся . в имени символа, я должен использовать dlsym для загрузки функции Fortran в указатель функции C .

В настоящее время у меня есть куча глобальных указателей на функции в заголовочных файлах:

 // test.h
extern void (*f)(int* arg);
  

и я заполняю их в соответствующем файле C :

 // test.cc
void (*f))(int* = reinterpret_cast<void(*)(int*>(dlsym(RTLD_DEFAULT, "real_f.symbol_name_");
  

Вопросы:

  1. Если я сделаю это таким образом, когда эти указатели будут заполнены?
  2. Могу ли я предположить, что они будут загружены в мой исполняемый файл, который загружает эту библиотеку?
  3. В частности, могу ли я использовать эти функции в статически созданных объектах в моем исполняемом файле или других библиотеках? Или это страдает от фиаско статического порядка инициализации?
  4. Если приведенный выше способ неверен, каков наиболее элегантный способ заполнения этих указателей, чтобы их можно было использовать в статических объектах в исполняемых файлах и других библиотеках?

Я использую компилятор Sun Studio на Solaris, если это имеет значение, но меня также заинтересовало бы решение для GCC в Linux.

Ответ №1:

Где строка

 f = reinterpret_cast<void(*)(int*)>(dlsym(RTLD_DEFAULT, "real_f.symbol_name_"));
  

происходит в test.cc ? Указатель будет инициализирован при выполнении строки
(что, конечно, зависит от того, когда вызывается функция, которая его содержит
). Или вы хотели написать

 void (*f)(int* ) = reinterpret_cast<void(*)(int*>(dlsym(RTLD_DEFAULT, "real_f.symbol_name_");
  

? В этом случае указатель будет инициализирован во время статической
инициализации. Это означает, что у вас все еще возникают
проблемы с порядком инициализации, если вы пытаетесь использовать указатели в конструкторе статического
объекта.

Классическим решением для этого было бы использовать какой-то синглтон:

 struct LibraryPointers
{
    void (*f)(int* );
    //  ...
    static LibraryPointers constamp; instance()
private:
    LibraryPointers();
};

LibraryPointers constamp;
LibraryPointers::instance()
{
    static LibraryPointers theOneAndOnly;
    return theOneAndOnly;
}

LibraryPointers::LibraryPointers()
    : f( reinterpret_cast<void(*)(int*)>(dlsym(RTLD_DEFAULT, "real_f.symbol_name_")) )
    , //  initialization of other pointers...
{
}
  

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

И последнее замечание: reinterpret_cast то, что вы пытаетесь сделать, не является законным, по крайней мере, формально. (Я думаю, что и Sun CC, и g примут это, однако.) Согласно Posix, правильный способ получить указатель на функцию из dlsym будет:

 void (*f)(int* );
*reinterpret_cast<void**>(amp;f) = dlsym(...);
  

Однако это не поддается инициализации.

Комментарии:

1. Да, спасибо, я действительно хотел написать это так, как вы сказали. Я исправлю вопрос. Я знаю, что у меня будут проблемы с другими статическими объектами в той же библиотеке, но верно ли это и в других библиотеках, которые используют эту библиотеку?

2. Мне бы очень хотелось избежать одноэлементного решения, я тоже думал об этом..

3. @lytenyn У других библиотек не должно возникнуть проблем, если они загружаются после загрузки вашей библиотеки. (И, конечно, если они были загружены раньше, глобальный символ с указателем на функцию недоступен.)