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()が利用されました。

先にリンクされた方が、シンボル解決時に先に参照されるようです。