Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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: 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

これらの警告は、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種類の警告を修正しています。

  1. 未使用変数 p の修正 (addvarint 関数内): addvarint 関数は、可変長整数(varint)をシンボルに書き込むためのものです。元のコードでは、p というポインタが *p++ = v; という行でインクリメントされていましたが、このインクリメント後の p の値は関数内でそれ以上使用されていませんでした。C言語では、*p++ = v; は「p が指すアドレスに v を代入し、その後 p をインクリメントする」という意味です。警告は、このインクリメント操作自体が不要であり、p の最終的な値が使われていないことを指摘していました。

  2. フォーマット不一致警告の修正 (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 型であるにもかかわらず、フォーマット文字列 %6lluxunsigned long long 型を期待しているために発生していました。 p->pc はプログラムカウンタを表す値であり、通常はポインタサイズと同じか、それ以上の大きさを持つ整数型で表現されます。しかし、Plan 9の特定のコンパイル環境では、これが32ビットの int として扱われていた可能性があります。 修正は、これらの引数を明示的に (vlong) にキャストすることで行われています。vlong はPlan 9における64ビット整数型であり、これにより Bprint が期待する unsigned long long 型と引数の型が一致し、警告が解消されます。

  3. 未使用変数 started の修正 (funcpctab 関数内): funcpctab 関数内の started 変数は、PC-値テーブルの処理が開始されたかどうかを追跡するために使用されていました。元のコードでは if(!started) started = 1; else { ... } というロジックがあり、started0 の場合に 1 に設定されていました。しかし、この started = 1; という代入自体が、その後の started の値の直接的な使用に繋がっていませんでした。つまり、started のブール値としての状態は参照されていましたが、代入された 1 という値そのものが使われていなかったため、コンパイラが警告を発していました。 修正は、if(!started) started = 1; else { ... }if(started) { ... } に変更することで行われています。これにより、started1 に設定されるロジックが削除され、started の状態チェックのみが行われるようになり、警告が解消されました。この変更は、ロジックの意図を変えることなく、コードをより簡潔にしています。

これらの修正は、コードのセマンティクスを変更することなく、コンパイラの警告を解消し、コードベースの健全性を向上させるものです。

コアとなるコードの変更箇所

src/cmd/ld/lib.c ファイルにおいて、以下の変更が行われました。

  1. 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    ```
    
    
  2. 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)` キャストが追加されています)
    
    
  3. 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    ```
    
    

コアとなるコードの解説

  1. addvarint 関数における p の修正: 元のコード *p++ = v; は、vp が指すメモリ位置に書き込んだ後、p をインクリメントしていました。しかし、この関数内で p のインクリメント後の値は使用されていませんでした。これは、p が指す次のメモリ位置に何かを書き込む意図がないにもかかわらず、ポインタを動かしてしまっていたことを意味します。 修正後の *p = v; は、vp が指すメモリ位置に書き込むだけで、p をインクリメントしません。これにより、p が「設定されたが使用されていない」という警告が解消されます。この変更は、addvarint の機能(可変長整数の最後のバイトを書き込む)に影響を与えません。

  2. funcpctab 関数における Bprint の引数キャスト: Bprint 関数は、%6llux というフォーマット指定子を使用しており、これは unsigned long long 型(64ビット符号なし整数)の引数を期待します。しかし、p->pcfunc->value+func->size といった値は、Plan 9の特定のコンパイル環境では int 型(32ビット整数)として扱われることがありました。 この型不一致は、特に64ビットの値を32ビットのレジスタにロードしようとしたり、その逆を行ったりする際に、データの切り捨てや誤った解釈を引き起こす可能性があります。 修正では、これらの引数を明示的に (vlong) にキャストしています。vlong はPlan 9における64ビット整数型であり、これにより Bprint に渡される引数が期待される型と一致し、コンパイラ警告が解消されます。これは、異なるアーキテクチャやコンパイラ間での移植性を高める上でも重要な修正です。

  3. funcpctab 関数における started 変数のロジック修正: 元のコード if(!started) started = 1; else { ... } は、startedfalse の場合に true に設定し、それ以降は else ブロックを実行するというロジックでした。しかし、started = 1; という代入自体が、その後のコードで started の値そのものとして利用されることはなく、単に started のブール値としての状態がチェックされるだけでした。 修正後の if(started) { ... } は、started が既に true である場合にのみブロック内の処理を実行します。これにより、started1 に設定する冗長な代入が削除され、コードが簡潔になります。この変更は、started 変数の意図された動作(最初のイテレーションをスキップし、2回目以降のイテレーションで特定の処理を行う)を維持しつつ、未使用変数の警告を解消しています。

これらの変更は、Goのリンカの堅牢性と移植性を向上させ、クリーンなビルドを保証するために行われました。

関連リンク

参考にした情報源リンク