Нас недавно попросили поставить версию 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.
Я знаю о других подобных вопросах на ТАК:
но имели мало успеха с ответами.
Так что решение, которое у нас есть сейчас, заключается в следующем:
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
, который Довольно близко к тому, что мы ищем. Хотя есть несколько готов:
Я рад принять любые лучшие решения, которыми кто-то приходит!
Ваше использование атрибута видимости по умолчанию и -Fisisibility = Hidden должен быть дополнен - скрытыми - скрытыми.
Вы также должны забыть о том, чтобы попытаться скрыть экспорт STDLIB, см. Эта ошибка GCC , почему.
Кроме того, если у вас есть все ваши общедоступные символы в конкретных заголовках, вы можете обернуть их в #PRAGMA GCC видимости видимости (по умолчанию)
и #PRAGMA GCC видимости POP
вместо Использование атрибутов. Хотя, если вы создаете пересеченную платформу библиотеку, посмотрите на , контролирующие экспортируемые символы общих библиотек для техники, чтобы объединить вашу экспортную стратегию Windows DLL и Linux DSO.
В общем, во многих Linux и Unix-системах ответ здесь заключается в том, что во время соединения здесь нет ответа. это довольно фундаментально для того, как работает ld.so.
Это приводит к некоторым довольно трудоемким альтернативам. Например, мы переименовываем STL, чтобы жить в _STL вместо std
, чтобы избежать конфликтов из-за STL, и используем пространства имен high, low и in-between, чтобы удержать наши символы подальше от возможных конфликтов с чужими символами.
Вот решение, которое вам не понравится:
До тех пор, пока вы не используете RTLD_GLOBAL, теперь у вас есть полная изоляция, если не особая секретность ... -Бсимволика также может быть желательна.
Если вы завершаете свою частную часть в анонимном пространстве имен, то ни один 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