Подтвердить что ты не робот

Удаление разделяемых библиотек linux

Недавно нам было предложено отправить 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, ld 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;
}

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

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: с оптимизацией вам нужно убедиться, что вектор фактически используется, поэтому компилятор не оптимизирует неиспользуемые символы.

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

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

В частности:

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

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

Я знаю другие подобные вопросы по SO:

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

Если вы завершаете свою частную часть в анонимном пространстве имен, то в таблице символов не может отображаться ни 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

Ответ 5

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

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

Здесь вам не понравится решение:

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

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