two-level namespaceについて
いまさらですが、簡単にまとまっているページがなかったので書いてみました。
一言でいうなら、
「two-level namespace」で作成されたものは、作成時に使ったイメージの状態で名前解決をするのであって、実行時のイメージの状態で名前解決をするものではないとうことです。
1. ライブラリを構成する各ファイルです。
% cat a.c extern int fc(int); int fa(int a) { return fc(a); } % cat b.c extern int fc(int); int fb(int b) { return fc(b); } % cat c.c int fc(int c) { #if defined(MA) return c + 100; #elif defined(MB) return c + 200; #else return c + 300; #endif }
2. それぞれ、ライブラリを作成します。
% cc -c -fno-common a.c -o a.o % cc -c -fno-common b.c -o b.o % cc -c -fno-common -DMA c.c -o ca.o % cc -c -fno-common -DMB c.c -o cb.o % cc -dynamiclib -o libca.dylib ca.o % cc -dynamiclib -o libcb.dylib cb.o % cc -dynamiclib -o liba.dylib a.o -L. -lca -Wl,-twolevel_namespace % cc -dynamiclib -o libb.dylib b.o -L. -lcb -Wl,-twolevel_namespace
3. ライブラリを利用する側です。
% cat m.c #include <stdio.h> extern int fa(int); extern int fb(int); int main(int argc, char **argv) { int a = fa(1); int b = fb(2); printf("a=%d,b=%d\n", a, b); return 0; }
4. 作成して、実行してみます。
% cc m.c -o m -L. -la -lb % ./m a=101,b=202
結果としては当たり前なのですが、fa(), fb()が異なるライブラリに存在する同一名の関数fc()を利用すると、異なる実装が利用されて正しい結果を得ました。
5. 今度は-flat_namespaceでライブラリを作成し、それを利用して実行してみましょう。
% cc -dynamiclib -o liba.dylib a.o -L. -lca -Wl,-flat_namespace % cc -dynamiclib -o libb.dylib b.o -L. -lcb -Wl,-flat_namespace % ./m a=101,b=102
今度は、実行時の大域的なシンボルの名前空間で解決されたため、fb()の呼び出したfc()の実装としてlibca.dylib側のfc()が利用されました。
6. 利用する側でliba.dylibとlibb.dylibの順序を入れ替えてリンクして、実行してみましょう。
% cc m.c -o m -L. -lb -la % ./m a=201,b=202
今度は、fa()の呼び出したfc()の実装として、libcb.dylib側のfc()が利用されました。
先にリンクされた方が、シンボル解決時に先に参照されるようです。