2010-01-18 1 views
32

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

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

Принимая простой пример, то:

test.cpp

#include <cmath> 

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

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

собран с (г ++ 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 

что хорошо, за исключением того, что станд :: абс подвергается. Более проблематично, когда мы начинаем связываться в других (статических) библиотеках вне нашего контроля, все символы, которые мы используем из этих библиотек, экспортируются. Кроме того, когда мы начинаем использовать 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 ++ и ld с небольшим успехом, поэтому предпочел бы ответы, которые, как известно, работают, а не полагаются.

В частности:

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

Наш экспортироваться интерфейс C.

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

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

+1

О статической привязке системных библиотек: для вас это незаконно. То есть, поскольку [(e)] (http://www.eglibc.org/) [GLIBC] (http://www.gnu.org/software/libc/) лицензируется в соответствии с [LGPL] (http://opensource.org/licenses/LGPL-3.0), и поскольку эта лицензия применяется ко всему используемому ему коду, за исключением случаев, когда она связана динамически, путем связывания статически вы делаете свой код, охватываемым LGPL, и должны предоставлять источники (любому, кому вы дали двоичный код и они запрашивают источники). Это не относится к libgcc и libstdC++, которые специально не применяются к любому коду, используя только открытый API, независимо от того, как он связан. –

+0

Я знаю об этом и не имел в виду символы из glibc, все символы, указанные выше, генерируются путем создания шаблона из стандартной библиотеки C++ и по необходимости генерируются в моих объектных файлах (поскольку экземпляры шаблонов могут " t быть в библиотеке!). –

ответ

6

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

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); 
} 

экспорт.версия

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

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

+0

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

+0

Этот метод также скрывает символы от других статических библиотек? Я сталкиваюсь с одной и той же проблемой с большим количеством трещин из различных внешних зависимостей, экспортируемых. –

+0

Кажется, да. Единственными символами были наш экспорт и некоторые символы, с которыми мы связывались в библиотеках C/C++. –

2

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

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

Вот решение, которое вы не будете любить:

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

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

+0

К сожалению, скрытие деталей нашего алгоритма - половина проблемы для нас. –

+0

Ну, вы можете заниматься переименованием оптовых символов, если используете мой подход с двумя общими объектами. @ вещи Джошперри могли бы выполнить эту работу. – bmargulies

5

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

Вы также должны забыть о попытке скрыть экспорт stdlib, см. this GCC bug.

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

+0

Спасибо, что нашли время ответить, ссылки, сделанные для интересного чтения. 1. Мы раскрываем чистый интерфейс C (для большей совместимости), почему мы должны раскрывать детали нашей реализации? Просто потому, что мы * могли * делиться RTTI и т. Д. Через границы библиотек, не означает, что мы * будем *. 2. В моих простых примерах -fvisibility-inlines-hidden ничего не изменилось, я не думаю, что это повлияет на наш интерфейс вообще (или решить наши проблемы), но может быть полезно в будущем. 3.К сожалению, ссылка на статью не предлагает решения каких-либо из наших проблем (помимо того, что у нас есть). –

+0

Я вижу, что вы подразумеваете под private_struct в векторной конкретизации, которая экспортируется. Какое изменение вызывает ваш коллега в заголовках, которые заставляют их уйти? – joshperry

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); 
} 

составителя (г ++ 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 
+1

Хотя он работает для простого примера и является хорошим способом скрытия локальных конструкций, частные пространства имен не будут масштабироваться до полного проекта. –

5

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

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