[インデックス 16843] ファイルの概要
このコミットは、Go言語のリンカである cmd/ld において、Plan 9オペレーティングシステム上で発生していたコンパイラ警告を修正するものです。具体的には、未使用変数の警告と、printfスタイルのフォーマット文字列と引数の型不一致に関する警告が対象となっています。
コミット
commit b015af41171f00c5f6b263aafd73df6c4e58443a
Author: David du Colombier <0intro@gmail.com>
Date: Mon Jul 22 17:33:41 2013 -0400
cmd/ld: fix warnings on Plan 9
src/cmd/ld/lib.c:1379 set and not used: p
src/cmd/ld/lib.c:1426 format mismatch 6llux INT, arg 3
src/cmd/ld/lib.c:1437 format mismatch 6llux INT, arg 3
src/cmd/ld/lib.c:1456 format mismatch 6llux INT, arg 3
src/cmd/ld/lib.c:1477 format mismatch 6llux INT, arg 3
src/cmd/ld/lib.c:1459 set and not used: started
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/11615044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b015af41171f00c5f6b263aafd73df6c4e58443a
元コミット内容
このコミットは、src/cmd/ld/lib.c ファイルに対する変更であり、以下の警告を修正することを目的としています。
src/cmd/ld/lib.c:1379 set and not used: psrc/cmd/ld/lib.c:1426 format mismatch 6llux INT, arg 3src/cmd/ld/lib.c:1437 format mismatch 6llux INT, arg 3src/cmd/ld/lib.c:1456 format mismatch 6llux INT, arg 3src/cmd/ld/lib.c:1477 format mismatch 6llux INT, arg 3src/cmd/ld/lib.c:1459 set and not used: started
これらの警告は、GoのリンカがPlan 9環境でコンパイルされる際に発生していました。
変更の背景
Go言語は、その初期からPlan 9オペレーティングシステムの影響を強く受けており、一部のツールチェインはPlan 9のCコンパイラやライブラリを使用して構築されていました。cmd/ld はGoのリンカであり、Goプログラムのコンパイル済みオブジェクトファイルを結合して実行可能ファイルを生成する役割を担っています。
このコミットが作成された2013年当時、Goのツールチェインは様々なプラットフォームをサポートしていましたが、特定のプラットフォーム(この場合はPlan 9)でのコンパイル時に、標準Cとは異なるコンパイラの挙動や型定義の違いにより警告が発生することがありました。これらの警告は、直接的なバグを引き起こすものではないかもしれませんが、コードの品質を低下させ、将来的な問題の兆候となる可能性があるため、修正が望ましいとされていました。
特に、set and not used 警告は、変数が宣言され値が代入されたものの、その後のコードで一度も参照されていないことを示します。これは冗長なコードや、意図しないロジックの誤りを示唆する可能性があります。また、format mismatch 警告は、printfのような可変引数関数において、フォーマット指定子と実際に渡される引数の型が一致しない場合に発生します。これは未定義動作を引き起こす可能性があり、特に異なるアーキテクチャやコンパイラでコンパイルされた場合に、予期せぬ出力やクラッシュにつながる可能性があります。
このコミットは、これらの警告を解消し、Plan 9上での cmd/ld のビルドをクリーンに保つことを目的としています。
前提知識の解説
Plan 9 from Bell Labs
Plan 9は、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、「すべてをファイルとして扱う」という原則を徹底しています。ネットワーク上のリソース(ファイル、デバイス、サービスなど)もすべてファイルシステムとして表現され、標準的なファイル操作インターフェースを通じてアクセスできます。Go言語の設計には、Plan 9の思想(特に並行性モデルやモジュール性)が色濃く反映されています。Plan 9のCコンパイラは、標準Cとは異なる独自の拡張や型定義を持つことがあり、それが移植性の問題を引き起こすことがあります。
Goリンカ (cmd/ld)
Go言語のビルドプロセスにおいて、リンカ (cmd/ld) は非常に重要な役割を担います。Goのソースコードはまずコンパイラ (cmd/compile) によってオブジェクトファイルに変換され、その後、リンカがこれらのオブジェクトファイルと必要なランタイムライブラリを結合して、最終的な実行可能ファイルを生成します。リンカは、シンボルの解決、アドレスの再配置、実行可能ファイルのフォーマット(ELF, Mach-O, PEなど)への変換などを行います。cmd/ld はGo言語で書かれていますが、その内部ではC言語で書かれた低レベルのルーチンや、OS固有のシステムコールを呼び出す部分も存在します。
C言語におけるコンパイラ警告
C言語のコンパイラは、プログラムの潜在的な問題や非効率なコードに対して警告を発することがよくあります。
set and not used(設定されたが使用されていない): 変数が宣言され、値が代入されたにもかかわらず、その後のコードでその変数の値が一度も読み取られない場合に発生します。これは、プログラマの意図しない冗長なコード、あるいはロジックの誤りを示唆する可能性があります。format mismatch(フォーマット不一致):printf,sprintf,fprintfなどのフォーマット文字列を使用する関数において、フォーマット指定子(例:%d,%s,%llx)と、それに対応する引数の実際の型が一致しない場合に発生します。これはC言語の可変引数関数の性質上、コンパイル時に厳密な型チェックが行われにくいため、実行時に未定義動作(例: メモリ破壊、クラッシュ、誤った出力)を引き起こす可能性があります。
vlong 型と 6llux フォーマット指定子
vlong: Plan 9のCコンパイラにおける非標準の型で、通常は標準Cのlong long型に相当します。これは64ビット整数を表すために使用されます。%llux:printfファミリー関数で使用されるフォーマット指定子です。ll:long long型の引数であることを示します。u:unsigned(符号なし)であることを示します。x: 16進数形式で出力することを示します。 したがって、%lluxは「符号なし64ビット整数を16進数で出力する」ことを意味します。
警告 format mismatch 6llux INT は、%llux が期待する unsigned long long 型の引数に対して、実際には int 型(通常32ビット)の引数が渡されていることを示しています。これは、特に32ビットシステムと64ビットシステムが混在する環境で問題となりやすいです。
技術的詳細
このコミットは、src/cmd/ld/lib.c 内の2種類の警告を修正しています。
-
未使用変数
pの修正 (addvarint関数内):addvarint関数は、可変長整数(varint)をシンボルに書き込むためのものです。元のコードでは、pというポインタが*p++ = v;という行でインクリメントされていましたが、このインクリメント後のpの値は関数内でそれ以上使用されていませんでした。C言語では、*p++ = v;は「pが指すアドレスにvを代入し、その後pをインクリメントする」という意味です。警告は、このインクリメント操作自体が不要であり、pの最終的な値が使われていないことを指摘していました。 -
フォーマット不一致警告の修正 (
funcpctab関数内):funcpctab関数は、Goの関数におけるPC-値テーブル(プログラムカウンタと値のマッピング)を生成する際に、デバッグ情報を出力するためにBprint関数(Plan 9のfmtライブラリに似たI/O関数)を使用しています。 警告format mismatch 6llux INT, arg 3は、Bprintの呼び出しにおいて、第3引数(p->pcまたはfunc->value+func->size)がint型であるにもかかわらず、フォーマット文字列%6lluxがunsigned long long型を期待しているために発生していました。p->pcはプログラムカウンタを表す値であり、通常はポインタサイズと同じか、それ以上の大きさを持つ整数型で表現されます。しかし、Plan 9の特定のコンパイル環境では、これが32ビットのintとして扱われていた可能性があります。 修正は、これらの引数を明示的に(vlong)にキャストすることで行われています。vlongはPlan 9における64ビット整数型であり、これによりBprintが期待するunsigned long long型と引数の型が一致し、警告が解消されます。 -
未使用変数
startedの修正 (funcpctab関数内):funcpctab関数内のstarted変数は、PC-値テーブルの処理が開始されたかどうかを追跡するために使用されていました。元のコードではif(!started) started = 1; else { ... }というロジックがあり、startedが0の場合に1に設定されていました。しかし、このstarted = 1;という代入自体が、その後のstartedの値の直接的な使用に繋がっていませんでした。つまり、startedのブール値としての状態は参照されていましたが、代入された1という値そのものが使われていなかったため、コンパイラが警告を発していました。 修正は、if(!started) started = 1; else { ... }をif(started) { ... }に変更することで行われています。これにより、startedが1に設定されるロジックが削除され、startedの状態チェックのみが行われるようになり、警告が解消されました。この変更は、ロジックの意図を変えることなく、コードをより簡潔にしています。
これらの修正は、コードのセマンティクスを変更することなく、コンパイラの警告を解消し、コードベースの健全性を向上させるものです。
コアとなるコードの変更箇所
src/cmd/ld/lib.c ファイルにおいて、以下の変更が行われました。
addvarint関数 (行 1379):--- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -1376,7 +1376,7 @@ addvarint(Sym *s, uint32 val)\n \tp = s->p + s->np - n;\n \tfor(v = val; v >= 0x80; v >>= 7)\n \t\t*p++ = v | 0x80;\n-\t*p++ = v;\n+\t*p = v;\n }\n ```funcpctab関数内のBprint呼び出し (行 1426, 1437, 1456, 1477):--- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -1423,7 +1423,7 @@ funcpctab(Sym *dst, Sym *func, char *desc, int32 (*valfunc)(Sym*, int32, Prog*,\n \t\tif(val == oldval && started) {\n \t\t\tval = valfunc(func, val, p, 1, arg);\n \t\t\tif(debug['O'])\n-\t\t\t\tBprint(&bso, "%6llux %6s %P\\n", p->pc, "", p);\n+\t\t\t\tBprint(&bso, "%6llux %6s %P\\n", (vlong)p->pc, "", p);\n \t\t\tcontinue;\n \t\t}\n ``` (他の箇所も同様に `p->pc` や `func->value+func->size` に `(vlong)` キャストが追加されています)funcpctab関数内のstarted変数のロジック (行 1459):--- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -1453,11 +1453,9 @@ funcpctab(Sym *dst, Sym *func, char *desc, int32 (*valfunc)(Sym*, int32, Prog*,\n \t\t// where the 0x80 bit indicates that the integer continues.\n \n \t\tif(debug['O'])\n-\t\t\tBprint(&bso, "%6llux %6d %P\\n", p->pc, val, p);\n+\t\t\tBprint(&bso, "%6llux %6d %P\\n", (vlong)p->pc, val, p);\n \n-\t\tif(!started)\n-\t\t\tstarted = 1;\n-\t\telse {\n+\t\tif(started) {\n \t\t\taddvarint(dst, (p->pc - pc) / MINLC);\n \t\t\tpc = p->pc;\n \t\t}\n ```
コアとなるコードの解説
-
addvarint関数におけるpの修正: 元のコード*p++ = v;は、vをpが指すメモリ位置に書き込んだ後、pをインクリメントしていました。しかし、この関数内でpのインクリメント後の値は使用されていませんでした。これは、pが指す次のメモリ位置に何かを書き込む意図がないにもかかわらず、ポインタを動かしてしまっていたことを意味します。 修正後の*p = v;は、vをpが指すメモリ位置に書き込むだけで、pをインクリメントしません。これにより、pが「設定されたが使用されていない」という警告が解消されます。この変更は、addvarintの機能(可変長整数の最後のバイトを書き込む)に影響を与えません。 -
funcpctab関数におけるBprintの引数キャスト:Bprint関数は、%6lluxというフォーマット指定子を使用しており、これはunsigned long long型(64ビット符号なし整数)の引数を期待します。しかし、p->pcやfunc->value+func->sizeといった値は、Plan 9の特定のコンパイル環境ではint型(32ビット整数)として扱われることがありました。 この型不一致は、特に64ビットの値を32ビットのレジスタにロードしようとしたり、その逆を行ったりする際に、データの切り捨てや誤った解釈を引き起こす可能性があります。 修正では、これらの引数を明示的に(vlong)にキャストしています。vlongはPlan 9における64ビット整数型であり、これによりBprintに渡される引数が期待される型と一致し、コンパイラ警告が解消されます。これは、異なるアーキテクチャやコンパイラ間での移植性を高める上でも重要な修正です。 -
funcpctab関数におけるstarted変数のロジック修正: 元のコードif(!started) started = 1; else { ... }は、startedがfalseの場合にtrueに設定し、それ以降はelseブロックを実行するというロジックでした。しかし、started = 1;という代入自体が、その後のコードでstartedの値そのものとして利用されることはなく、単にstartedのブール値としての状態がチェックされるだけでした。 修正後のif(started) { ... }は、startedが既にtrueである場合にのみブロック内の処理を実行します。これにより、startedを1に設定する冗長な代入が削除され、コードが簡潔になります。この変更は、started変数の意図された動作(最初のイテレーションをスキップし、2回目以降のイテレーションで特定の処理を行う)を維持しつつ、未使用変数の警告を解消しています。
これらの変更は、Goのリンカの堅牢性と移植性を向上させ、クリーンなビルドを保証するために行われました。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語のリンカ (
cmd/ld) のソースコード: https://github.com/golang/go/tree/master/src/cmd/ld - Plan 9 from Bell Labs: https://9p.io/plan9/
参考にした情報源リンク
- Go Change-list 11615044: https://golang.org/cl/11615044
- C言語の
printfフォーマット指定子に関するドキュメント (例: cppreference.com): https://en.cppreference.com/w/c/io/fprintf - C言語のコンパイラ警告に関する一般的な情報 (例: GCCのドキュメント): https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
- Plan 9のC言語に関する情報 (例: Plan 9 from Bell Labsのドキュメント): https://9p.io/plan9/doc/compiler.html (一般的な情報源として)