2013/11/22(Fri)
○[386BSD(の子孫)ソースコードシリーズ (334)][な阪関無] sys/cdefs.hとは何ですか? (その9)
@まえがき
飽きた、 引き続きNetBSD1.3までに追加されたマクロです。
@_C_LABEL()マクロ
昔の環境のlibcにおいてシンボルは全てアンスコが付いてました。テニスの下着の方でないアンダースコアすなわち「_」です。
NetBSD 1.3.3だとこんな感じ。
$ uname -sr
NetBSD 1.3.3
$ nm /usr/lib/libc.so.12.20 | grep vfprintf
0004adb4 T _vfprintf
なぜこんなことやってんのかちゅーと、みんな大好き Linker & Loadersの「1.6 リンクの実例」に
大域シンボルの先頭にはそれぞれアンダースコアが付加されている。この理由については第5章で説明する
と書かれてます。
んでんでんで、興奮を抑えきれずに
淫夢5章をめくると「5.3.1 CとFortranの単純な名前の変形」にすっげーわかりにくい説明があります、ザックリ要約すると
- 元々 Unix のライブラリはアセンブラで書かれて動いていた
- そこに後から C や Fortran で書かれたライブラリが誕生した
- これら3つをチャンポンで使うと大域シンボルが衝突して宇宙ヤバイ
- OS/360とか今となっては誰も知らん環境について熱く語ってるのが余計なんだよなぁ…
ということです、カレー臭くさい(確信)。
@name mangling の導入
そんでこの大域シンボル衝突を回避するためにどうしたらいいか、と頭悩ませた結果
そうだ、名前をまんぐり返そう(真剣)
という事になったわけですやね。
ルールとしては
- アセンブラではそのまんまん foo → foo
- Cでは前にアンスコ付けて foo → _foo
- Fortranでは前後にアンスコ付けて foo → _foo_
というようになっとります、C++のネームマンドリルより簡単ですな。
@_C_LABEL() マクロの使用例
アンダースコア付への変換はCコンパイラが勝手にやるので通常は意識しません。ただしアセンブラソースや淫乱アセンブラからCの大域シンボル参照する時とかには手マンする必要があります。
例えば こ↑こ↓の2nd stage boot loaderのコードですが
…
movzbl %dl, %edx
push %ebp /* high 32 bits of first sector */
push %ebx /* first sector of bios partition */
push %edx /* bios disk */
call boot2 /* C bootstrap code */
…
boot2 は C ABI なのでアンスコ付けてやらんとならんわけですが
call _boot2 /* C bootstrap code */
わざわざ手マンせず _C_LABEL() マグロを使って↓と書くわけですな
call _C_LABEL(boot2) /* C bootstrap code */
つーかタイプ量10倍になっとるじゃんとお思いでしょうが
- 将来的にアンダースコアつける以外のまんぐり返しが必要になった場合もコード書き直し不要
- Cの名前空間であることが、ソース読む人間に一目瞭然である
- エディタの検索機能でのひっかけ易くなる
ちゅー意味があるわけでして。
@加藤良三、ライブラリインタフェースをCに統一
Unixは移植性の為にプログラミング言語Cで書き直されることになります *1、 4th Editionあたりでその作業はほぼ完了し、アセンブラで書かれたライブラリ絶滅しました。もはやアセンブラとそれ以外を区別するための前アンスコは不要になったのです。
そうなると前アンスコ撲滅したくなるのが人情、特にシンボル名8文字制限(当時)のうち常に1文字アンスコに奪われるのは嫌だもんげ。しかしながら実行するには
- Cコンパイラを修正してアンスコを付けないようにする
- その上すべてのライブラリを再コンパイル
ちゅー難題が待っております、これ現実的には無理なんで長らく放置されることになります。
@成し遂げたぜ。
まずCコンパイラの修正は gcc に -fleading-underscore/-fno-leading-underscore というスイッチが実装されname mangle有り無しを切り替え可能になりました。
あとはライブラリを全て再コンパイルするタイミングなのですが、これはもっと時代が後になります。バイナリフォーマットをa.outからELFへスイッチするタイミングであれば互換性は全部切り捨てられるわけで、それが選ばれました。
ちなみにELF化されたNetBSD 1.5.3だとこんな感じ。
$ uname -sr
NetBSD 1.5.3
$ nm /usr/lib/libc.so.12.62.1 | grep vfprintf
00041ef0 T vfprintf
前アンスコが無くなったのがお分かり頂けますでしょうか?
現在のコードはこんな感じ
#ifdef __LEADING_UNDERSCORE
#define _C_LABEL(x) __CONCAT(_,x)
#else
#define _C_LABEL(x) x
#endif
-fleading-underscoreの場合は前アンスコあり、-fno-leading-underscoreの場合はなしで_C_LABEL()は展開されます。
Fortranも前アンスコは不要になりました、ただし後アンスコア健在なはず。詳しくはそこら歩いてる数学屋をとっ捕まえて聞いてください。
@次回予告
ヒッ