Разделение Linux совместно использовало библиотеки

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

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

Взятие простого примера затем:

test.cpp

#include 

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

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

#include 
struct private_struct
{
    float f;
};

void other_private_function()
{
    std::vector v;
}

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

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

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

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

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

В особенности:

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

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

Я знаю о других подобных вопросах на ТАК:

но имели мало успеха с ответами.

33
задан Community 23 May 2017 в 12:10
поделиться

4 ответа

-

Так что решение, которое у нас есть сейчас, заключается в следующем:

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 заставляет их уходить в моих простых тестах, но довольно ядерное решение.

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

7
ответ дан 27 November 2019 в 19:33
поделиться

Ваше использование атрибута видимости по умолчанию и -Fisisibility = Hidden должен быть дополнен - ​​скрытыми - скрытыми.

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

Кроме того, если у вас есть все ваши общедоступные символы в конкретных заголовках, вы можете обернуть их в #PRAGMA GCC видимости видимости (по умолчанию) и #PRAGMA GCC видимости POP вместо Использование атрибутов. Хотя, если вы создаете пересеченную платформу библиотеку, посмотрите на , контролирующие экспортируемые символы общих библиотек для техники, чтобы объединить вашу экспортную стратегию Windows DLL и Linux DSO.

6
ответ дан 27 November 2019 в 19:33
поделиться

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

Это приводит к некоторым довольно трудоемким альтернативам. Например, мы переименовываем STL, чтобы жить в _STL вместо std, чтобы избежать конфликтов из-за STL, и используем пространства имен high, low и in-between, чтобы удержать наши символы подальше от возможных конфликтов с чужими символами.

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

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

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

2
ответ дан 27 November 2019 в 19:33
поделиться

Если вы завершаете свою частную часть в анонимном пространстве имен, то ни один 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
4
ответ дан 27 November 2019 в 19:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: