web-dev-qa-db-ja.com

Linux共有ライブラリを削除する

最近、ライブラリの1つのLinuxバージョンを出荷するように依頼されました。以前は、Linuxで開発し、ライブラリの展開が一般的にはるかに簡単なWindows用に出荷しました。私たちが遭遇した問題は、エクスポートされたシンボルを、公開されたインターフェイスのシンボルのみに削除することです。これを実行したい理由は3つあります

  • エクスポートされたシンボルを介した公開から、当社のテクノロジーの専有的な側面を保護する。
  • シンボル名の競合に関する問題をユーザーが回避するため。
  • ライブラリの読み込みを高速化するため(少なくとも私はそう言われます)。

簡単な例を見てみましょう。

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

最終的に、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共有ライブラリからすべての不要なシンボル(つまり、公開されたライブラリ機能の一部ではないシンボル)を取り除くクリーンな方法はありますか?私はg ++とldの両方に対して非常に多くのオプションを試しましたが、ほとんど成功しなかったので、信じられているよりも機能することがわかっている回答を好みます。

特に:

  • (クローズドソース)静的ライブラリからのシンボルはエクスポートされません。
  • 標準ライブラリのシンボルはエクスポートされません。
  • オブジェクトファイルの非公開シンボルはエクスポートされません。

エクスポートされたインターフェースはCです。

私はSOに関する他の同様の質問を知っています:

しかし、答えにはほとんど成功していません。

36
Adam Bowen

したがって、現在の解決策は次のとおりです。

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

これは私たちが探しているものにかなり近いです。ただし、いくつかの落とし穴があります。

  • 内部コードで "exported"プレフィックス(この単純な例では "public"ですが、明らかにより便利なもの)を使用しないようにする必要があります。
  • 多くのシンボル名はまだ文字列テーブルにあり、RTTIに依存しているように見えます。-fno-rttiを使用すると、簡単なテストでそれらを削除できますが、かなり核となるソリューションです。

私は誰もが思いついたより良い解決策を受け入れてうれしいです!

9
Adam Bowen

デフォルトの可視性属性と-fvisibility = hiddenの使用は、-fvisibility-inlines-hiddenで拡張する必要があります。

Stdlibエクスポートを非表示にすることも忘れてください。理由は このGCCバグ を参照してください。

また、特定のヘッダーにすべてのパブリックシンボルがある場合は、属性を使用する代わりに、それらを#pragma GCC visibility Push(default)#pragma GCC visibility popでラップできます。クロスプラットフォームライブラリを作成している場合でも、Windowsを統合する手法については 共有ライブラリのエクスポートされたシンボルの制御 をご覧くださいDLLおよびLinux DSOエクスポート戦略。

8
joshperry

Ulrich Drepperが、Linux/Unixの 共有ライブラリの作成 の(すべて?)側面に関するエッセイを書いたことに注意してください。これは、他の多くのトピックの中でエクスポートされたシンボルの制御をカバーしています。

これは、共有ライブラリからホワイトリストの関数のみをエクスポートする方法を明確にするのに非常に便利でした。

7
grrussel

プライベート部分を匿名の名前空間でラップすると、どちらも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
6
catwalk

一般に、複数のLinuxおよびUnixシステムにわたって、ここでの答えは、リンク時にここでは答えがないということです。これは、ld.soがどのように機能するかについてかなり基本的です。

これは、かなり労働集約的な代替案につながります。たとえば、STLの名前をstdではなく_STLに変更して、STLとの競合を回避し、名前空間high、low、およびbetweenを使用して、シンボルが他の人のシンボルと競合しないようにします。

これがあなたが気に入らない解決策です:

  1. 公開されたAPIのみを使用して小さな.soを作成します。
  2. Dlopenを使用して実際の実装を開き、dlsymとリンクします。

RTLD_GLOBALを使用しない限り、特定の機密性がない場合でも完全に絶縁されます。-シンボリックも望ましい場合があります。

3
bmargulies

実際、ELF構造には、「symtab」と「dynsym」の2つのシンボルテーブルがあります->これを参照してください: ライブラリにシンボル名を非表示にする

0
vtomazzi