Posted at

LinuxのC言語共有ライブラリでの、公開するAPI制限方法

More than 1 year has passed since last update.


共有ライブラリで公開するAPIへの悩み

私が共有ライブラリを作成する際に、ずっと悩んでいたことがあります。

libraryの規模が大きくなると、当然中をファイル分けし、ライブラリ内でAPIを定義して開発を行うと思います。

この時ずっと何か方法がないかな~と悩んでいたことがあります。

普通にやると、こうなっちゃうんですよね。

作成された共有ライブラリで利用可能なAPIの中に、ライブラリ内で定義したヘッダーAPIも含まれてしまう

公開ヘッダーは指定しますが、強引に内部で使っているヘッダーを読み込めば、そのAPIも使われてしまう。

特にgithubでライブラリ公開なんてするのに、意図しないAPIを好きに使えてしまうなんて、ちょっとダサいな~

ということで期待せずに調べてみました。


共有ライブラリで公開するAPIは制限出来る!

出来ないことなんてないんだ、そうGNUならね!

というわけで、なんとLinuxの既存機能で公開制限ができるみたい。

参考:http://0xcc.net/blog/archives/000108.html

例えばこんなライブラリ公開ヘッダーがあったとします。(以前作った時間計測ライブラリ)


timetestlog.h

void * timetestlog_init(char *delimiter, size_t maxloglen, unsigned long maxstoresize);

int timetestlog_store_printf(void * handle, const char *format, ...);
void timetestlog_exit(void * handle);

ライブラリ作成者としてはこのAPIだけを使ってほしいところ。

このライブラリを開発する際に、こんなAPIを使っていたとします。


testfile.h

int testfunction(void);



通常のコンパイル

これで普通にコンパイルすると、testfile.hをincludeしちゃえばtestfunctionが使えちゃいます。

公開されているAPIはnm -Dコマンドやobjdump -tで確認できます。

コマンド実行結果を見ると、testfunctionが入っていますね。それ以外にも余計なAPIがちらほら

Linux env$ nm -D .libs/libtimelog.so | grep " T "

0000000000000dec T _fini
00000000000007d8 T _init
0000000000000dc0 T testfunction
0000000000000d90 T timetestlog_exit
0000000000000b10 T timetestlog_init
0000000000000c90 T timetestlog_store_printf

Linux env$ objdump -t .libs/libtimelog.so | grep " g"

0000000000000dc0 g F .text 000000000000002c testfunction
0000000000202068 g .data 0000000000000000 _edata
0000000000000dec g F .fini 0000000000000000 _fini
0000000000000c90 g F .text 00000000000000fc timetestlog_store_printf
0000000000000b10 g F .text 000000000000017f timetestlog_init
0000000000202070 g .bss 0000000000000000 _end
0000000000202068 g .bss 0000000000000000 __bss_start
0000000000000d90 g F .text 0000000000000022 timetestlog_exit
00000000000007d8 g F .init 0000000000000000 _init


--version-scriptによるAPI制限

公開APIを制限するには"--version-script"を使います。このようにversion-scriptと公開APIを定義した設定ファイル(ここではlibtimelog.map)を指定します。

LDFLAGS+=-Wl,--version-script,libtimelog.map

設定ファイルにはglobalに公開したいAPIを、localにそれ以外(*)を指定します。


libtimelog.map

{

global:
timetestlog_init;
timetestlog_store_printf;
timetestlog_exit;
local: *;
};

上記の設定ファイルを指定してビルドをしたライブラリの公開APIがこちら。

最小限のAPIだけが公開されていることがわかります。

Linux env$ nm -D .libs/libtimelog.so | grep " T "

0000000000000cb0 T timetestlog_exit
0000000000000a30 T timetestlog_init
0000000000000bb0 T timetestlog_store_printf

Linux env$ objdump -t .libs/libtimelog.so | grep " g"
0000000000000bb0 g F .text 00000000000000fc timetestlog_store_printf
0000000000000a30 g F .text 000000000000017f timetestlog_init
0000000000000cb0 g F .text 0000000000000022 timetestlog_exit


まとめ

--version-scriptを使えば、公開APIを指定することが出来ます。

今までは公開されるAPIのことを気にして、本当は分けたいファイルを纏めたりしていましたが、

これで気兼ねなく好きなファイル構成でライブラリを作ることが出来ますね。