Удаление общих библиотек Linux - PullRequest
33 голосов
/ 18 января 2010

Недавно нас попросили отправить версию одной из наших библиотек для Linux, ранее мы разрабатывали ее для Linux и поставляли для Windows, где развертывание библиотек, как правило, намного проще. Проблема, с которой мы столкнулись, заключается в разделении экспортированных символов до тех, которые находятся в открытом интерфейсе. Есть три веские причины для этого

  • Для защиты запатентованных аспектов нашей технологии от воздействия через экспортируемые символы.
  • Для предотвращения проблем с конфликтующими именами пользователей.
  • Чтобы ускорить загрузку библиотеки (по крайней мере, мне так сказали).

Возьмем простой пример:

test.cpp

#include <cmath>

float private_function(float f)
{
    return std::abs(f);
}

extern "C" float public_function(float f)
{
    return private_function(f);
}

скомпилировано с (g ++ 4.3.2, л.д. 2.18.93.20081009)

g++ -shared -o libtest.so test.cpp -s

и проверка символов с помощью

nm -DC libtest.so

дает

         w _Jv_RegisterClasses
0000047c T private_function(float)
000004ba W std::abs(float)
0000200c A __bss_start
         w __cxa_finalize
         w __gmon_start__
0000200c A _edata
00002014 A _end
00000508 T _fini
00000358 T _init
0000049b T public_function

явно неадекватно. Итак, затем мы переименуем публичную функцию как

extern "C" float __attribute__ ((visibility ("default"))) 
    public_function(float f)

и скомпилируйте с

g++ -shared -o libtest.so test.cpp -s -fvisibility=hidden

, что дает

         w _Jv_RegisterClasses
0000047a W std::abs(float)
0000200c A __bss_start
         w __cxa_finalize
         w __gmon_start__
0000200c A _edata
00002014 A _end
000004c8 T _fini
00000320 T _init
0000045b T public_function

что хорошо, за исключением того, что выставлен std :: abs. Более проблематично, когда мы начинаем связывать в других (статических) библиотеках вне нашего контроля, все символы, которые мы используем из этих библиотек, экспортируются . Кроме того, когда мы начинаем использовать контейнеры STL:

#include <vector>
struct private_struct
{
    float f;
};

void other_private_function()
{
    std::vector<private_struct> v;
}

мы получаем множество дополнительных экспортов из библиотеки C ++

00000b30 W __gnu_cxx::new_allocator<private_struct>::deallocate(private_struct*, unsigned int)
00000abe W __gnu_cxx::new_allocator<private_struct>::new_allocator()
00000a90 W __gnu_cxx::new_allocator<private_struct>::~new_allocator()
00000ac4 W std::allocator<private_struct>::allocator()
00000a96 W std::allocator<private_struct>::~allocator()
00000ad8 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_impl::_Vector_impl()
00000aaa W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_impl::~_Vector_impl()
00000b44 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_M_deallocate(private_struct*, unsigned int)
00000a68 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_M_get_Tp_allocator()
00000b08 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_base()
00000b6e W std::_Vector_base<private_struct, std::allocator<private_struct> >::~_Vector_base()
00000b1c W std::vector<private_struct, std::allocator<private_struct> >::vector()
00000bb2 W std::vector<private_struct, std::allocator<private_struct> >::~vector()

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

Я полагаю, что моему коллеге удалось создать специальное решение, включающее файлы версий и изменение заголовков STL (!), Которое, кажется, работает, но я хотел бы спросить:

Есть ли чистый способ убрать все ненужные символы (те IE, которые не являются частью предоставляемой библиотеки функциональности) из общей библиотеки linux? Я пробовал довольно много вариантов для обоих g ++ и с небольшим успехом, поэтому я бы предпочел ответы, которые, как известно, работают, а не верят.

В частности:

  • Символы из статических библиотек (с закрытым исходным кодом) не экспортируются.
  • Символы из стандартной библиотеки не экспортируются.
  • Непубличные символы из объектных файлов не экспортируются.

Наш экспортируемый интерфейс - C.

Мне известны другие подобные вопросы по SO:

, но с ответами имели небольшой успех.

Ответы [ 5 ]

7 голосов
/ 19 января 2010

Итак, решение, которое мы имеем на данный момент, выглядит следующим образом:

test.cpp

#include <cmath>
#include <vector>
#include <typeinfo>

struct private_struct
{
    float f;
};

float private_function(float f)
{
    return std::abs(f);
}

void other_private_function()
{
    std::vector<private_struct> f(1);
}

extern "C" void __attribute__ ((visibility ("default"))) public_function2()
{
    other_private_function();
}

extern "C" float __attribute__ ((visibility ("default"))) public_function1(float f)
{
    return private_function(f);
}

exports.version

LIBTEST 
{
global:
    public*;
local:
    *;
};

скомпилировано с

g++ -shared test.cpp -o libtest.so -fvisibility=hidden -fvisibility-inlines-hidden -s -Wl,--version-script=exports.version

дает

00000000 A LIBTEST
         w _Jv_RegisterClasses
         U _Unwind_Resume
         U std::__throw_bad_alloc()
         U operator delete(void*)
         U operator new(unsigned int)
         w __cxa_finalize
         w __gmon_start__
         U __gxx_personality_v0
000005db T public_function1
00000676 T public_function2

Что довольно близко к тому, что мы ищем.Однако есть несколько ошибок:

  • Мы должны убедиться, что мы не используем префикс «exported» (в этом простом примере «public», но, очевидно, что-то более полезное в нашем случае) ввнутренний код.
  • Многие имена символов по-прежнему попадают в таблицу строк, которая, по-видимому, относится к RTTI, -fno-rtti убирает их из моих простых тестов, но это довольно ядерное решение.

Я рад принять любые лучшие решения, которые кто-нибудь придумает!

6 голосов
/ 19 января 2010

Использование атрибута видимости по умолчанию и -fvisibility = hidden должно быть дополнено с -fvisibility-inlines-hidden.

Вам также следует забыть о попытке скрыть экспорт stdlib, см. эту ошибку GCC , почему.

Кроме того, если у вас есть все ваши публичные символы в определенных заголовках, вы можете заключить их в #pragma GCC visibility push(default) и #pragma GCC visibility pop вместо использования атрибутов. Хотя, если вы создаете кроссплатформенную библиотеку, взгляните на Управление экспортированными символами общих библиотек , чтобы узнать, как унифицировать вашу стратегию экспорта Windows DLL и Linux DSO.

5 голосов
/ 10 января 2012

Просто чтобы отметить, что Ульрих Дреппер написал эссе, касающееся (всех?) Аспектов написания общих библиотек для Linux / Unix, которое охватывает контроль экспортируемых символов среди многих других тем.

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

4 голосов
/ 19 января 2010

Если вы закроете свою личную часть в анонимном пространстве имен, то в таблице символов не увидите ни std::abs, ни private_function:

namespace{
#include<cmath>
  float private_function(float f)
  {
    return std::abs(f);
  }
}
extern "C" float public_function(float f)
{
        return private_function(f);
}

компиляция (g ++ 4.3.3):

g++ -shared -o libtest.so test.cpp -s

проверка:

# nm -DC libtest.so
         w _Jv_RegisterClasses
0000200c A __bss_start
         w __cxa_finalize
         w __gmon_start__
0000200c A _edata
00002014 A _end
000004a8 T _fini
000002f4 T _init
00000445 T public_function
2 голосов
/ 18 января 2010

В целом, в нескольких системах Linux и Unix ответ здесь заключается в том, что здесь нет ответа во время ссылки. это довольно фундаментально для работы ld.so.

Это приводит к некоторым довольно трудоемким альтернативам. Например, мы переименовываем STL в «_STL» вместо std, чтобы избежать конфликтов по STL, и используем пространства имен «высокий», «низкий» и промежуточный, чтобы наши символы избегали возможных конфликтов с символами других людей.

Вот решение, которое вам не понравится:

  1. Создайте небольшой .so только с вашим открытым API.
  2. Пусть откроет реальную реализацию с помощью dlopen, и свяжется с dlsym.

Пока вы не используете RTLD_GLOBAL, у вас теперь есть полная изоляция, если не особая секретность ... -Бсимволический также может быть желательным.

...