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

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

このコミットは、Go言語のリンカである cmd/5l (ARM), cmd/6l (x86-64), cmd/8l (x86) におけるエラーメッセージバッファのサイズを増やす変更です。具体的には、エラーメッセージを格納するための内部バッファ buf のサイズが STRINGSZ (200バイト) から 1024 バイトに拡張されています。これにより、ファイル名を含む長いエラーメッセージが切り詰められることなく表示されるようになります。

コミット

commit 6c99b5c0d31b21d0163bd1d7ee0e248e19a0cba3
Author: Russ Cox <rsc@golang.org>
Date:   Thu Jul 11 22:49:15 2013 -0400

    cmd/5l, cmd/6l, cmd/8l: increase error buffer size
    
    STRINGSZ (200) is fine for lines generated by things like
    instruction dumps, but an error containing a couple file
    names can easily exceed that, especially on Macs with
    the ridiculous default $TMPDIR.
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/11199043

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

https://github.com/golang/go/commit/6c99b5c0d31b21d0163bd1d7ee0e248e19a0cba3

元コミット内容

cmd/5l, cmd/6l, cmd/8l: increase error buffer size

STRINGSZ (200) is fine for lines generated by things like
instruction dumps, but an error containing a couple file
names can easily exceed that, especially on Macs with
the ridiculous default $TMPDIR.

R=ken2
CC=golang-dev
https://golang.org/cl/11199043

変更の背景

この変更の背景には、Go言語のリンカが生成するエラーメッセージが、特定の環境下でバッファオーバーフローを起こし、メッセージが途中で切り詰められてしまう問題がありました。特に、ファイルパスが長くなる傾向にあるmacOS環境の $TMPDIR (一時ディレクトリ) などで、複数のファイル名を含むエラーメッセージが STRINGSZ で定義された200バイトのバッファに収まりきらないケースが発生していました。

STRINGSZ は、命令ダンプのような比較的短い文字列を扱う場合には十分なサイズでしたが、エラーメッセージのように動的に内容が変化し、かつファイルパスなどの長い文字列を含む可能性がある場合には不十分でした。エラーメッセージが途中で切れてしまうと、ユーザーは問題の根本原因を特定しにくくなり、デバッグの効率が低下します。この問題を解決し、より分かりやすいエラーメッセージを提供するために、エラーバッファのサイズを増やす必要がありました。

前提知識の解説

Go言語のツールチェインとリンカ

Go言語のビルドプロセスは、ソースコードをコンパイルし、最終的に実行可能なバイナリを生成する一連のツールチェインによって構成されています。このツールチェインには、コンパイラ (go tool compile)、アセンブラ (go tool asm)、そしてリンカ (go tool link) などが含まれます。

  • コンパイラ: Goのソースコードをアセンブリコードに変換します。
  • アセンブラ: アセンブリコードをオブジェクトファイルに変換します。
  • リンカ: 複数のオブジェクトファイルやライブラリを結合し、最終的な実行可能バイナリを生成します。リンカは、プログラムが参照する外部関数やデータのアドレスを解決し、それらを適切に配置する役割を担います。

cmd/5l, cmd/6l, cmd/8l

これらは、Go言語の初期のツールチェインにおけるリンカのコマンド名です。Go言語はクロスプラットフォーム開発を強く意識しており、異なるCPUアーキテクチャ向けにバイナリを生成できます。

  • cmd/5l: ARMアーキテクチャ向けのリンカです。
  • cmd/6l: x86-64 (AMD64) アーキテクチャ向けのリンカです。現代のほとんどのデスクトップPCやサーバーで広く使われています。
  • cmd/8l: x86 (IA-32) アーキテクチャ向けのリンカです。32ビットシステムで使われます。

これらのリンカは、それぞれ特定のアーキテクチャの命令セットやメモリモデルに合わせて最適化されています。

STRINGSZ

STRINGSZ は、Go言語のツールチェイン内部で使われていた定数で、主に文字列バッファのデフォルトサイズを定義するために使用されていました。このコミットの時点では、その値は200バイトでした。これは、命令ダンプのような固定長または比較的短い文字列の表示には十分でしたが、可変長で長いパス名などを含むエラーメッセージには不十分であることが判明しました。

diag 関数

diag 関数は、Go言語のツールチェイン内で診断メッセージ(エラー、警告など)を出力するために使用される内部関数です。この関数は printf スタイルのフォーマット文字列と可変引数を受け取り、それらを整形して標準エラー出力などに書き出します。このコミットでは、diag 関数内で使用される一時バッファ buf のサイズが変更されています。

技術的詳細

このコミットの技術的な詳細は、Go言語のリンカ (cmd/5l, cmd/6l, cmd/8l) のソースコードにおけるエラーメッセージ処理の改善にあります。

各リンカの list.c ファイルには、診断メッセージを出力するための diag 関数が実装されています。この diag 関数内で、フォーマットされたメッセージを一時的に格納するために char buf[STRINGSZ] という固定サイズのバッファが使用されていました。

問題は、STRINGSZ が200バイトと定義されていたことです。エラーメッセージが、例えば以下のような形式で出力される場合を考えます。

error: could not open file /path/to/very/long/directory/name/another/long/directory/name/source_file.go and /path/to/another/very/long/directory/name/another/long/directory/name/target_file.go

このようなメッセージは、特にmacOSのように一時ディレクトリのパスが非常に長くなる環境では、容易に200バイトを超過してしまいます。バッファサイズを超過すると、diag 関数はメッセージの残りの部分を切り捨ててしまい、ユーザーには不完全なエラーメッセージしか表示されません。これはデバッグを困難にし、ユーザーエクスペリエンスを損ないます。

このコミットでは、この問題を解決するために、buf のサイズを STRINGSZ から 1024 バイトに増やしています。1024バイトは、ほとんどの一般的なエラーメッセージ、特にファイルパスを含む場合でも、十分な長さを確保できるサイズです。これにより、エラーメッセージが途中で切り詰められる可能性が大幅に低減され、より完全で有用な診断情報がユーザーに提供されるようになります。

この変更は、Go言語のツールチェインの堅牢性とユーザーフレンドリーさを向上させるための、小さなしかし重要な改善と言えます。

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

変更は src/cmd/5l/list.c, src/cmd/6l/list.c, src/cmd/8l/list.c の3つのファイルにわたっています。それぞれのファイルで、diag 関数内のローカル変数 buf の宣言が変更されています。

--- a/src/cmd/5l/list.c
+++ b/src/cmd/5l/list.c
@@ -473,7 +473,7 @@ Oconv(Fmt *fp)
 void
 diag(char *fmt, ...)
 {
-	char buf[STRINGSZ], *tn, *sep;
+	char buf[1024], *tn, *sep;
 	va_list arg;
 
 	tn = "";

同様の変更が src/cmd/6l/list.csrc/cmd/8l/list.c にも適用されています。

コアとなるコードの解説

上記のコードスニペットは、diag 関数の定義の一部を示しています。

  • void diag(char *fmt, ...): これは diag 関数のシグネチャです。fmt はフォーマット文字列(printf と同様の形式)で、... は可変引数リストを示します。
  • char buf[STRINGSZ], *tn, *sep;: 変更前の行です。ここで buf という名前の char 型の配列が宣言されており、そのサイズは STRINGSZ で定義されていました。STRINGSZ はこの時点では200でした。
  • char buf[1024], *tn, *sep;: 変更後の行です。buf のサイズが 1024 に直接ハードコードされています。これにより、diag 関数が内部でメッセージを整形する際に使用するバッファの容量が、200バイトから1024バイトに拡張されました。

この変更により、diag 関数が生成するエラーメッセージが、より長い文字列(特にファイルパスなど)を含んでいても、途中で切り詰められることなく完全にバッファに収まるようになります。これは、エラーメッセージの完全性と有用性を向上させるための直接的な修正です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード
  • Gitのコミット履歴と差分表示
  • 一般的なリンカの動作に関する情報
  • C言語における可変引数関数 (stdarg.h) に関する情報