web-dev-qa-db-ja.com

共有ライブラリをリンクするときのシンボルの可視性の制限

一部のプラットフォームでは、共有ライブラリの外部シンボルのリストをリンカーに提供する必要があります。ただし、ほとんどのunixishシステムでは必要ありません。すべての非静的シンボルはデフォルトで使用できます。

GNUツールチェーンは、オプションで可視性を明示的に宣言されたシンボルのみに制限できることを理解しています。GNU ld?

44
rafl

GNU ldはELFプラットフォームでそれを行うことができます。

リンカバージョンのスクリプトでこれを行う方法は次のとおりです。

_/* foo.c */
int foo() { return 42; }
int bar() { return foo() + 1; }
int baz() { return bar() - 1; }

gcc -fPIC -shared -o libfoo.so foo.c && nm -D libfoo.so | grep ' T '
_

デフォルトでは、すべてのシンボルがエクスポートされます。

_0000000000000718 T _fini
00000000000005b8 T _init
00000000000006b7 T bar
00000000000006c9 T baz
00000000000006ac T foo
_

bar()baz()のみをエクスポートするとします。 「バージョンスクリプト」を作成する_libfoo.version_:

_FOO {
  global: bar; baz; # explicitly list symbols to be exported
  local: *;         # hide everything else
};
_

それをリンカーに渡します。

_gcc -fPIC -shared -o libfoo.so foo.c -Wl,--version-script=libfoo.version
_

エクスポートされたシンボルを確認します。

_nm -D libfoo.so | grep ' T '
00000000000005f7 T bar
0000000000000609 T baz
_
66

これを行う最も簡単な方法は、gccオプションに_-fvisibility=hidden_を追加し、(__attribute__((visibility("default")))によって)コードでいくつかのシンボルの可視性を明示的に公開することだと思います。ドキュメント here を参照してください。

Ldリンカースクリプトによってそれを達成する方法があるかもしれませんが、私はそれについて多くを知りません。

38
jpalecek

エクスポートされた関数を呼び出したり、エクスポートされたグローバルを使用したりするために生成されたコードは、エクスポートされていないコードよりも効率的ではありません。間接的なレベルがさらに高くなります。これは、可能性があるコンパイル時にエクスポートされるすべての関数に適用されます。 gccは、リンカスクリプトによって後でアンエクスポートされる関数に対して、追加の間接参照を生成します。したがって、visibility属性を使用すると、リンカースクリプトよりも優れたコードが生成されます。

7
user188731

Libtoolを使用している場合、Employed Russianの回答によく似た別のオプションがあります。

彼の例を使用すると、次のようになります。

cat export.sym
bar
baz

次に、次のオプションを指定してlibtoolを実行します。

libtool -export-symbols export.sym ...

-export-symbolsを使用する場合、デフォルトではすべてのシンボルがエクスポートされず、export.symのシンボルのみがエクスポートされることに注意してください(したがって、このアプローチでは、libfoo.versionの「local:*」行は実際には暗黙的です)。

1
aaa90210