[英]Stripping linux shared libraries
我們最近被要求發布我們的一個庫的 Linux 版本,以前我們是在 Linux 下開發並為 Windows 發布的,在 Windows 中部署庫通常要容易得多。 我們遇到的問題是將導出的符號剝離為僅暴露接口中的符號。 想要這樣做有三個很好的理由
那么舉一個簡單的例子:
測試.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;
}
我們最終從 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()
注意:通過優化,您需要確保實際使用了向量,以便編譯器不會優化未使用的符號。
我相信我的同事已經設法構建了一個涉及版本文件和修改 STL 標頭 (!) 的臨時解決方案,但我想問:
有沒有一種干凈的方法可以從 linux 共享庫中去除所有不必要的符號(IE 那些不屬於公開庫功能的符號)? 我已經對 g++ 和 ld 嘗試了很多選擇,但收效甚微,所以我更喜歡已知有效而不是相信有效的答案。
特別是:
我們導出的接口是C。
我知道關於 SO 的其他類似問題:
但對答案收效甚微。
所以我們現在的解決方案如下:
測試.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
這與我們正在尋找的非常接近。 但是有一些問題:
我很高興接受任何人提出的任何更好的解決方案!
您對默認可見性屬性和 -fvisibility=hidden 的使用應使用 -fvisibility-inlines-hidden 進行擴充。
您還應該忘記嘗試隱藏 stdlib 導出,請參閱此 GCC 錯誤以了解原因。
此外,如果您在特定標題中擁有所有公共符號,則可以將它們包裝在#pragma GCC visibility push(default)
和#pragma GCC visibility pop
而不是使用屬性。 但是,如果您要創建跨平台庫,請查看控制共享庫的導出符號以了解統一 Windows DLL 和 Linux DSO 導出策略的技術。
如果您將私有部分包裝在匿名命名空間中,那么在符號表中將看不到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
一般來說,跨多個 Linux 和 Unix 系統,這里的答案是鏈接時這里沒有答案。 這是 ld.so 如何工作的基礎。
這導致了一些相當勞動密集型的替代方案。 例如,我們將 STL 重命名為 _STL 而不是std
以避免與 STL 發生沖突,並且我們使用命名空間 high、low 和 in-between 來防止我們的符號與其他人的符號可能發生沖突。
這是您不會喜歡的解決方案:
只要您不使用 RTLD_GLOBAL,即使不是特別保密,您現在也可以完全隔離.. -Bsymbolic 也可能是可取的。
實際上,在 ELF 結構中有 2 個符號表:“symtab”和“dynsym”-> 請參閱: 在庫中隱藏符號名稱
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.