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

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

このコミットは、Go言語のツールチェインの一部である cmd/5l におけるバグ修正を扱っています。具体的には、src/cmd/5l/asm.c ファイル内のアセンブラコード生成ロジックにおける PLD 命令の処理に関する問題が修正されています。

コミット

commit 6a5660f1606716c2c68cdf804de1292a520279b0
Author: Russ Cox <rsc@golang.org>
Date:   Wed May 30 17:01:25 2012 -0400

    cmd/5l: fix PLD
    
    Was missing break.
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/6250078

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

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

元コミット内容

cmd/5l: fix PLD Was missing break.

このコミットは、cmd/5l というGo言語のリンカ(またはアセンブラの一部)において、PLD 命令の処理に関するバグを修正するものです。問題は、コード内の break ステートメントが欠落していたことに起因していました。

変更の背景

Go言語のコンパイラおよびリンカは、異なるアーキテクチャ(例: ARM、x86)向けにバイナリを生成します。cmd/5l はARMアーキテクチャ向けのリンカ(またはアセンブラ)に関連するコンポーネントです。

PLD (Preload Data) 命令は、ARMアーキテクチャにおいて、メモリからデータを事前にキャッシュにロードするようプロセッサに指示するための命令です。これは、将来的にアクセスされる可能性のあるデータを事前にフェッチしておくことで、メモリレイテンシを隠蔽し、プログラムのパフォーマンスを向上させるために使用されます。

このコミットが行われた2012年当時、Go言語はまだ比較的新しい言語であり、様々なアーキテクチャへの対応やツールチェインの安定化が活発に進められていました。cmd/5l のような低レベルのツールにおけるバグは、生成されるバイナリの正確性やパフォーマンスに直接影響を与えるため、早期の修正が求められます。

今回のバグは、PLD 命令を処理するコードパスにおいて、特定のケースで break ステートメントが欠落していたために発生しました。これにより、PLD 命令の処理が完了した後も、意図せず次の命令の処理ロジックにフォールスルーしてしまい、誤ったアセンブリコードが生成される可能性がありました。

前提知識の解説

cmd/5l

cmd/5l は、Go言語のツールチェインにおけるARMアーキテクチャ向けのリンカ(またはアセンブラの一部)です。Go言語のコンパイラは、ソースコードを中間表現に変換し、その後、各アーキテクチャ固有のアセンブラやリンカが最終的な実行可能バイナリを生成します。5l5 は、ARMアーキテクチャの古い名称である ARMv5 に由来しています。Goのツールチェインでは、8l (x86-64), 6l (x86-32), 5l (ARM) のように、アーキテクチャごとに異なるリンカが使われていました。

PLD (Preload Data) 命令

PLD は ARM アーキテクチャの命令セットの一部です。その目的は、指定されたメモリアドレスのデータをプロセッサのキャッシュにプリロードすることです。これにより、実際にそのデータが必要になったときに、メインメモリからのロードではなく、高速なキャッシュからデータが取得できるようになり、プログラムの実行速度が向上します。これは、特にデータアクセスパターンが予測可能なループ処理などで有効です。

C言語における switch-casebreak

C言語(Go言語のツールチェインの多くはC言語で書かれています)の switch ステートメントでは、case ラベルに一致するブロックが実行された後、明示的に break ステートメントがない限り、次の case ブロックに処理がフォールスルー(fall-through)します。これは意図的な動作として利用されることもありますが、多くの場合、各 case の処理を独立させるためには break が必要です。break がないと、ある case の処理が完了した後、意図しない次の case の処理まで実行されてしまい、論理的な誤りやバグを引き起こす可能性があります。

技術的詳細

このコミットの技術的な核心は、src/cmd/5l/asm.c ファイル内の switch ステートメントにおける break の欠落です。

asm.c は、アセンブリ命令を処理し、機械語に変換するロジックを含んでいます。PLD 命令に対応する case ブロック内で、PLD 命令のオペランド(オフセットなど)を適切に処理した後、break ステートメントがありませんでした。

これにより、PLD 命令の処理が完了した後、プログラムの実行フローは switch ステートメント内の次の case ブロック(この場合は case 96: /* UNDEF */)にフォールスルーしていました。UNDEF は未定義命令を意味し、通常は到達してはならないコードパスです。

このフォールスルーによって、PLD 命令の処理後に UNDEF 命令の処理ロジックが誤って実行される可能性がありました。これは、生成されるアセンブリコードの誤りや、リンカの予期せぬ動作、最悪の場合、生成されたバイナリのクラッシュや不正な動作につながる可能性があります。

修正は非常にシンプルで、PLD 命令の処理を行う case ブロックの最後に break; を追加するだけです。これにより、PLD 命令の処理が完了すると、switch ステートメントから適切に抜け出し、意図しないフォールスルーが防止されます。

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

--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -1791,6 +1791,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
  		to1 |= (-p->from.offset) & 0xfff;
  	} else
  		to1 |= p->from.offset & 0xfff;
+		break;
  	case 96:	/* UNDEF */
  		// This is supposed to be something that stops execution.
  		// It's not supposed to be reached, ever, but if it is, we'd

コアとなるコードの解説

変更は src/cmd/5l/asm.c ファイルの1792行目に追加された break; ステートメントです。

このコードスニペットは、ARMアセンブリ命令のエンコーディングの一部を処理している switch ステートメントの内部にあると考えられます。p->from.offset は、命令のオペランドとして使用されるオフセット値を表しています。

変更前のコードでは、PLD 命令に対応する case ブロックの処理(to1 にオフセット値を設定する部分)が完了した後、break がなかったため、次の case 96: /* UNDEF */ のコードブロックに処理が継続してしまっていました。

case 96: /* UNDEF */ のコメントにあるように、このブロックは「実行を停止させるべきもの」であり、「決して到達すべきではない」場所です。しかし、PLDcase からのフォールスルーによって、この UNDEF 処理が誤って実行される可能性がありました。

break; の追加により、PLD 命令の処理が完了すると、switch ステートメント全体から抜け出すようになり、UNDEF 命令の処理ロジックへの意図しないフォールスルーが完全に防止されます。これにより、PLD 命令が正しくアセンブルされ、生成されるバイナリの正確性が保証されます。

関連リンク

参考にした情報源リンク