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

[インデックス 18711] ファイルの概要

このコミットは、Go言語のリンカ (cmd/ld) 内の src/cmd/ld/pcln.c ファイルにおける、Plan 9環境でのコンパイル時に発生していた警告を修正するものです。具体的には、diag 関数への引数の渡し方がフォーマット文字列と一致していなかったために発生していた「more arguments than format INT」という警告を解消しています。

コミット

  • コミットハッシュ: f6088060924fa5b6ee26aa8fd134ccf189f1143f
  • 作者: David du Colombier 0intro@gmail.com
  • コミット日時: 2014年3月3日 月曜日 08:14:27 +0100

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/f6088060924fa5b6ee26aa8fd134ccf189f1143f

元コミット内容

cmd/ld: fix warning on Plan 9

warning: src/cmd/ld/pcln.c:184 more arguments than format INT

LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews
https://golang.org/cl/69870047

変更の背景

この変更の背景には、Go言語のツールチェインが様々なプラットフォームで正しく動作することの重要性があります。特に、Go言語はPlan 9オペレーティングシステムから多くの影響を受けており、そのツールチェインの一部はPlan 9環境でもコンパイル・実行されることが期待されます。

報告された警告「warning: src/cmd/ld/pcln.c:184 more arguments than format INT」は、src/cmd/ld/pcln.c の184行目において、diag 関数が使用しているフォーマット文字列と、実際に渡されている引数の数や型が一致していないことを示しています。このような警告は、コンパイラが潜在的なバグや未定義の動作を検出した際に発行するものであり、特にPlan 9環境のコンパイラが厳密であったために顕在化したと考えられます。

この警告は、直接的なクラッシュや誤動作を引き起こすものではないかもしれませんが、コードの品質を低下させ、将来的な問題の温床となる可能性があります。そのため、開発者はこのような警告を真摯に受け止め、修正することが求められます。このコミットは、その品質向上の一環として行われました。

前提知識の解説

Go言語のリンカ (cmd/ld)

cmd/ld はGo言語のビルドプロセスにおけるリンカです。Goのソースコードはまずコンパイラによってオブジェクトファイルに変換され、その後、これらのオブジェクトファイルと必要なライブラリが cmd/ld によって結合され、実行可能なバイナリが生成されます。リンカは、関数呼び出しのアドレス解決、グローバル変数の配置、そして実行に必要なメタデータ(例えば、このコミットで関連するpclntabのようなプログラムカウンタと行番号の対応テーブル)の生成など、多岐にわたる役割を担っています。

pcln.c とプログラムカウンタ/行番号テーブル

pcln.c は、Goの実行可能ファイルに埋め込まれるプログラムカウンタ (PC) と行番号 (line number) の対応テーブル(pclntab)を生成する役割を担っています。このテーブルは、デバッグ時やプロファイリング時に、実行中のコードのアドレスから元のソースコードのファイル名や行番号を特定するために非常に重要です。例えば、パニック発生時のスタックトレース表示や、go tool pprof によるプロファイリング結果の可視化に利用されます。

Plan 9 オペレーティングシステム

Plan 9 from Bell Labsは、ベル研究所で開発された分散型オペレーティングシステムです。Go言語の設計者の一部(Rob Pike, Ken Thompsonなど)はPlan 9の開発にも深く関わっており、Go言語の設計思想やツールチェインにはPlan 9の影響が色濃く反映されています。例えば、Goの標準ライブラリにおけるI/Oモデルや、ファイルシステムを介したリソースへのアクセス方法などは、Plan 9の哲学と共通する部分が多く見られます。

Plan 9のコンパイラやツールチェインは、特定のコーディングスタイルや慣習に対して厳密なチェックを行うことで知られています。今回の警告も、Plan 9環境のコンパイラが、C言語のprintf系関数のフォーマット文字列と引数の不一致を厳しく検出した結果として発生したものです。

diag 関数

diag 関数は、Goのツールチェイン内で使用される診断メッセージ出力用の関数であると推測されます。C言語におけるprintf関数と同様に、フォーマット文字列とそれに続く可変個の引数を受け取り、エラーメッセージや警告メッセージを標準エラー出力などに出力する役割を担っています。このような診断関数は、コンパイラやリンカが処理中に遭遇した問題(例えば、不正な入力、内部エラー、警告事項など)を開発者やユーザーに通知するために不可欠です。

printf フォーマット文字列の不一致警告

C言語のprintfファミリーの関数(printf, fprintf, sprintfなど)は、第一引数にフォーマット文字列を取り、その後の引数をフォーマット文字列の指示に従って解釈し、出力します。フォーマット文字列内の%d%s%fなどの変換指定子は、対応する引数の型と数を期待します。

more arguments than format INT」という警告は、フォーマット文字列が期待する整数型 (INT) の引数の数よりも、実際に渡されている引数の数が多い場合に発生します。これは、コンパイラが静的に検出できる一般的なプログラミングエラーの一つです。このような不一致は、以下のような問題を引き起こす可能性があります。

  • 未定義の動作 (Undefined Behavior): C標準では、フォーマット文字列と引数の不一致は未定義の動作を引き起こすとされています。これは、プログラムが予期せぬ結果を生じたり、クラッシュしたり、あるいは一見正しく動作しているように見えても、将来的に問題を引き起こす可能性があることを意味します。
  • セキュリティ上の脆弱性: 特に、フォーマット文字列がユーザー入力によって制御される場合、フォーマット文字列の脆弱性(format string vulnerability)として知られるセキュリティホールにつながる可能性があります。今回のケースは固定のフォーマット文字列であるため、直接的なセキュリティリスクは低いですが、警告自体は潜在的な脆弱性を示唆するものです。
  • デバッグの困難さ: 誤った引数が渡されることで、出力されるメッセージが意図しないものとなり、デバッグを困難にする可能性があります。

この警告は、コードの堅牢性と信頼性を確保するために、修正されるべき重要な問題と見なされます。

技術的詳細

このコミットで修正された問題は、src/cmd/ld/pcln.cpclntab 関数内のエラーチェックロジックにありました。具体的には、pcfile テーブル内のファイル番号が有効な範囲内にあるかを検証する部分です。

元のコードでは、diag 関数が以下のように呼び出されていました。

diag("bad file number in pcfile: %d not in range [1, %d]\\n", it.value, 1, ctxt->nhistfile);

ここで、フォーマット文字列は "%d not in range [1, %d]\\n" です。このフォーマット文字列は、2つの整数型 (%d) の引数を期待しています。しかし、実際に diag 関数に渡されている引数は it.value1ctxt->nhistfile の3つです。

Plan 9のコンパイラは、この引数の数の不一致を厳密に検出し、「more arguments than format INT」という警告を発していました。つまり、フォーマット文字列が期待する2つの%dに対して、3つの整数が渡されているため、余分な引数1が存在していると判断されたのです。

この警告を修正するために、開発者はフォーマット文字列の意図と引数の数を一致させる必要がありました。メッセージの意図は「it.value[1, ctxt->nhistfile] の範囲内にない」ということを伝えることでした。フォーマット文字列の [1, %d] の部分が、あたかも 1ctxt->nhistfile の両方を引数として受け取るかのように見えますが、実際には [1, はリテラル文字列であり、%d の部分だけが引数に対応します。

したがって、1 というリテラル値をフォーマット文字列に直接埋め込むことで、引数の数をフォーマット文字列の期待値と一致させることができます。

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

--- a/src/cmd/ld/pcln.c
+++ b/src/cmd/ld/pcln.c
@@ -181,7 +181,7 @@ pclntab(void)
 				// Sanity check the new numbering
 				for(pciterinit(&it, &pcln->pcfile); !it.done; pciternext(&it)) {
 					if(it.value < 1 || it.value > ctxt->nhistfile) {
-						diag("bad file number in pcfile: %d not in range [1, %d]\\n", it.value, 1, ctxt->nhistfile);
+						diag("bad file number in pcfile: %d not in range [1, %d]\\n", it.value, ctxt->nhistfile);
 					errorexit();
 					}
 				}

コアとなるコードの解説

変更は src/cmd/ld/pcln.c の184行目(変更前)にあります。

変更前:

diag("bad file number in pcfile: %d not in range [1, %d]\\n", it.value, 1, ctxt->nhistfile);

この行では、diag 関数に以下の引数が渡されていました。

  1. フォーマット文字列: "bad file number in pcfile: %d not in range [1, %d]\\n"
  2. 第一の %d に対応する引数: it.value
  3. 第二の %d に対応する引数: 1 (これは誤り)
  4. 余分な引数: ctxt->nhistfile (これは第二の %d に対応すべきだった)

問題は、フォーマット文字列内の [1, %d] の部分です。開発者は 1ctxt->nhistfile の両方を引数として渡す意図があったかもしれませんが、C言語のprintf系関数のフォーマット文字列の解釈では、[1, は単なるリテラル文字列として扱われ、%d の部分だけが引数に対応します。したがって、このフォーマット文字列は2つの整数引数 (%d が2回出現するため) を期待していました。しかし、実際には it.value, 1, ctxt->nhistfile の3つの引数が渡されており、1 が余分な引数となっていました。

変更後:

diag("bad file number in pcfile: %d not in range [1, %d]\\n", it.value, ctxt->nhistfile);

修正後のコードでは、diag 関数に渡される引数から 1 が削除されました。

  1. フォーマット文字列: "bad file number in pcfile: %d not in range [1, %d]\\n"
  2. 第一の %d に対応する引数: it.value
  3. 第二の %d に対応する引数: ctxt->nhistfile

これにより、フォーマット文字列が期待する2つの整数引数 (it.valuectxt->nhistfile) と、実際に渡される引数の数が一致するようになりました。結果として、Plan 9環境でのコンパイル時に発生していた「more arguments than format INT」という警告が解消されました。この修正は、コードの論理的な意味を変えることなく、コンパイラの警告を解消し、コードの健全性を向上させるものです。

関連リンク

参考にした情報源リンク