weakシンボル

glibcのソースを見ていたら weakシンボルをやたらと多様していた。
なんとなく言葉は知っていたが、はっきりとわかっていたわけでない
ので改めて確認する。


普通関数を 2つ定義すると多重定義ということで怒られるけど、
そのうちの一つが weakシンボルであれば weakシンボルでない
方がリンクされる。weakシンボルの関数しかなければそれが使われる。


例えば

/* foo.c */
#include <stdio.h>

void func(void)
{
    printf("I'm %s\n", __FUNCTION__);
}

int main (void)
{
    func();
    return 0;
}

/* bar.c */
void func(void)
{
    printf("I'm %s\n", __FUNCTION__);
}

こんな風に定義して二つのファイルをコンパイルするとエラーとなって
しまう。

% gcc foo.c bar.c
ld: duplicate symbol _func in /var/folders/hB/hBvE9Tc7FLq+yh1HQlk9W++++TI/-Tmp-//ccNkiB55.o and /var/folders/hB/hBvE9Tc7FLq+yh1HQlk9W++++TI/-Tmp-//cc934PdJ.o
collect2: ld returned 1 exit status

そこで一方の関数を weakシンボルにしてみる。これは GCCアトリビュート
を使って実現ができる。

/* foo.c */
#include <stdio.h>

void func() __attribute__((weak)); /* 追加 */

void func(void)
{
    printf("I'm weak %s\n", __FUNCTION__);
}

int main (void)
{
    func();
    return 0;
}

/* bar.c */
void func(void)
{
    printf("I'm %s\n", __FUNCTION__);
}

これで再度コンパイルしてみるとエラーが出なくなる。
実行してみると weakシンボルでない方の結果が得られる。

% gcc foo.c bar.c
% ./a.out
I'm func

foo.c 単体でコンパイルした場合は weakシンボルのものが採用される。

% gcc foo.c
% ./a.out
I'm weak func

具体的にどういうときに使えばいいかわからないけど、関数のオーバロードと
いうかユーザが新たに定義しなかったらデフォルトのものを使いますよ、と
いうようなときに使うのかなと思います。


glibcのソースではアーキテクチャ非依存ですませることができる関数に
weakシンボルが使われていた。アーキテクチャ依存の最適化をした関数を
利用したい場合そちらを使わせるということなんだろうなと認識しています。
memsetとか memcpyとかそのあたりで利用されていた。


しかし glibcのソースとか解読が難しすぎる。ディレクトリの構成から
して意味不明だし。weakシンボルの他に aliasという関数に別名を付けると
いう機能も多用していた。う〜ん、読むのが難しいな〜。