[インデックス 19435] ファイルの概要
このコミットは、Go言語のツールチェインの一部であるcmd/objdump
コマンドにおける、Plan 9オペレーティングシステム向けのオブジェクトファイルの逆アセンブルに関する問題を修正します。具体的には、逆アセンブラがテキスト、データ、またはBSSセグメントに属さないシンボルを誤って解釈し、その結果、命令内の小さな即値(immediate value)がシンボルアドレスとして表示されてしまう問題を解決します。
コミット
commit eb34288ad17bd624cfb4f40a3ab3095698624d95
Author: Anthony Martin <ality@pbrane.org>
Date: Wed May 21 23:24:38 2014 +0200
cmd/objdump: fix dissasembly of Plan 9 object files
Ignore symbols that aren't text, data, or bss since they cause
problems when dissassembling instructions with small immediate
values.
Before:
build.go:142 0x10ee 83ec50 SUBL $text/template/parse.autotmp_1293(SB), SP
After:
build.go:142 0x10ee 83ec50 SUBL $0x50, SP
Fixes #7947.
LGTM=rsc
R=rsc, 0intro
CC=golang-codereviews
https://golang.org/cl/93520045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/eb34288ad17bd624cfb4f40a3ab3095698624d95
元コミット内容
このコミットは、cmd/objdump
ツールがPlan 9オブジェクトファイルを逆アセンブルする際に発生する問題を修正します。問題は、テキスト、データ、またはBSS(Block Started by Symbol)セグメントに属さないシンボルを無視しないために発生していました。これにより、命令内の小さな即値が誤ってシンボルとして解釈され、逆アセンブル結果が不正確になるというものでした。
具体的な例として、SUBL $text/template/parse.autotmp_1293(SB), SP
のように、本来は即値である0x50
がtext/template/parse.autotmp_1293(SB)
というシンボルとして表示されてしまう現象が挙げられています。このコミットは、このような誤ったシンボル解釈を排除し、正しい即値表示(例: SUBL $0x50, SP
)を実現します。
この修正は、Go issue #7947を解決します。
変更の背景
Go言語のcmd/objdump
は、Goバイナリを逆アセンブルし、機械語命令を人間が読めるアセンブリコードに変換するツールです。このツールは、パフォーマンス分析、コンパイラの最適化の理解、および低レベルのデバッグに不可欠です。
Plan 9は、ベル研究所で開発された分散型オペレーティングシステムであり、Go言語の開発に大きな影響を与えました。Goのツールチェインは、Plan 9の設計思想を多く取り入れています。そのため、cmd/objdump
はPlan 9形式のオブジェクトファイルも処理できるように設計されています。
しかし、従来のcmd/objdump
のPlan 9オブジェクトファイル処理ロジックには欠陥がありました。Plan 9のオブジェクトファイルには、コード(テキスト)、初期化済みデータ(データ)、未初期化データ(BSS)以外の様々な種類のシンボルが含まれることがあります。逆アセンブラがこれらの非コード/データ/BSSシンボルを適切にフィルタリングせずに処理しようとすると、特に命令内の小さな即値(例: 0x50
)が、たまたまその値を持つ無関係なシンボルとして誤って解釈される可能性がありました。
この誤解釈は、逆アセンブル結果の正確性を損ない、開発者がコードの実際の動作を理解する上で混乱を招いていました。コミットメッセージに示されているように、SUBL $text/template/parse.autotmp_1293(SB), SP
という命令がSUBL $0x50, SP
と表示されるべきところを、SUBL $text/template/parse.autotmp_1293(SB), SP
と表示されるのは、まさにこの問題の典型例です。0x50
という即値が、text/template/parse.autotmp_1293(SB)
というシンボルのアドレスと一致してしまったために、逆アセンブラが誤った解釈をしてしまったのです。
この問題はGo issue #7947として報告されており、このコミットはその問題を解決するために導入されました。また、src/cmd/objdump/objdump_test.go
内のPlan 9環境でのテストスキップロジックが削除されていることから、この修正によって以前の回避策が不要になったことが示唆されます。
前提知識の解説
cmd/objdump
: Go言語の標準ツールチェインに含まれるコマンドラインツールで、Goの実行可能バイナリを逆アセンブルするために使用されます。これにより、コンパイルされた機械語命令をアセンブリ言語の形式で表示し、プログラムの低レベルな動作を分析できます。- Plan 9オブジェクトファイル: Plan 9オペレーティングシステムで使用される実行可能ファイルおよびオブジェクトファイルの形式です。Go言語のツールチェインは、その設計においてPlan 9の影響を強く受けており、Plan 9形式のオブジェクトファイルを扱うためのサポートが含まれています。
- シンボル (Symbol): プログラム内の特定のメモリ位置(関数、変数、セクションなど)を識別するための名前です。オブジェクトファイルには、これらのシンボルとそのアドレスをマッピングするシンボルテーブルが含まれています。
- テキストシンボル (
T
,t
): 実行可能なコード(命令)が格納されているセグメントに関連するシンボルです。大文字のT
はグローバルシンボル、小文字のt
はローカルシンボルを示します。 - データシンボル (
D
,d
): 初期化されたデータが格納されているセグメントに関連するシンボルです。大文字のD
はグローバルシンボル、小文字のd
はローカルシンボルを示します。 - BSSシンボル (
B
,b
): 初期化されていないデータ(ゼロ初期化されるグローバル変数など)が格納されているセグメントに関連するシンボルです。大文字のB
はグローバルシンボル、小文字のb
はローカルシンボルを示します。 - その他のシンボル: Plan 9オブジェクトファイルには、上記以外にも、ローカル変数、関数パラメータ、ソースファイル履歴、ファイル名コンポーネントなど、様々な目的のシンボルタイプが存在します(例:
a
,p
,z
,f
など)。
- テキストシンボル (
- 即値 (Immediate Value): アセンブリ言語の命令において、オペランドとして直接命令コード内に埋め込まれる定数データのことです。例えば、
ADD EAX, 5
という命令では、5
が即値です。 - 逆アセンブラの課題: 逆アセンブラは、バイナリデータがコードなのかデータなのかを区別する「コード/データ分離問題」に直面します。特に、即値がたまたまメモリアドレスや他のシンボルの値と一致する場合、逆アセンブラがその即値をシンボルとして誤って解釈し、不正確な出力をしてしまうことがあります。これは、シンボル情報が不完全であったり、シンボルテーブルにコードやデータ以外の無関係な情報が含まれていたりする場合に顕著になります。
技術的詳細
このコミットの技術的な核心は、cmd/objdump
がPlan 9オブジェクトファイルからシンボルを読み込む際に、関連性のないシンボルをフィルタリングするという点にあります。
以前の実装では、plan9Symbols
関数がPlan 9オブジェクトファイルから読み込んだすべてのシンボルを処理しようとしていました。しかし、Plan 9のオブジェクトファイル形式は、実行可能なコードやデータとは直接関係のない、デバッグ情報やリンカの内部情報などを示す様々なシンボルタイプを含んでいます。これらのシンボルは、逆アセンブルのコンテキストでは意味を持たないだけでなく、むしろ誤解釈の原因となっていました。
特に問題となったのは、命令内の小さな即値が、たまたまこれらの無関係なシンボルのアドレスと一致してしまった場合です。逆アセンブラは、その即値をシンボルへの参照として解釈し、本来の数値ではなくシンボル名を表示してしまいました。これは、逆アセンブル結果の可読性と正確性を著しく低下させます。
このコミットでは、src/cmd/objdump/plan9obj.go
ファイルにvalidSymType
という新しいマップを導入しました。このマップは、逆アセンブルにおいて意味のあるシンボルタイプ(テキストセグメントのT
, t
、データセグメントのD
, d
、BSSセグメントのB
, b
)のみをtrue
として定義しています。
plan9Symbols
関数内でシンボルを処理するループの前に、!validSymType[s.Type]
というチェックが追加されました。これにより、シンボルのタイプがvalidSymType
マップに定義されていない(つまり、テキスト、データ、BSS以外の)場合、そのシンボルは完全にスキップされ、逆アセンブル処理の対象から外されます。
この変更により、逆アセンブラは、本来処理すべきコードやデータに関連するシンボルのみを考慮するようになります。その結果、小さな即値が誤って無関係なシンボルとして解釈される問題が解消され、逆アセンブル結果が正確かつ意図通りに表示されるようになりました。
また、src/cmd/objdump/objdump_test.go
からPlan 9環境でのテストスキップロジックが削除されたことは、この修正が問題の根本原因を解決し、以前はテストをパスできなかった状況が改善されたことを示しています。
コアとなるコードの変更箇所
変更は主にsrc/cmd/objdump/plan9obj.go
ファイルに集中しています。
-
validSymType
マップの追加:+var validSymType = map[rune]bool{ + 'T': true, + 't': true, + 'D': true, + 'd': true, + 'B': true, + 'b': true, +}
このマップは、有効なシンボルタイプ(テキスト、データ、BSS)を定義します。
-
plan9Symbols
関数内のシンボルフィルタリングロジックの追加:plan9Symbols
関数内の2つのループ(addrs
スライスを構築するループと、syms
スライスを構築するループ)のそれぞれに、以下のフィルタリング条件が追加されました。// 最初のループ (addrsを構築) for _, s := range plan9Syms { + if !validSymType[s.Type] { + continue + } addrs = append(addrs, s.Value) } // 2番目のループ (symを構築) for _, s := range plan9Syms { + if !validSymType[s.Type] { + continue + } sym := Sym{Addr: s.Value, Name: s.Name, Code: rune(s.Type)} // ... (後続のロジック) }
これにより、
plan9Syms
から読み込まれたシンボルのうち、validSymType
で定義されていないタイプを持つものは、以降の処理から除外されます。
また、src/cmd/objdump/objdump_test.go
からは、Plan 9環境でのテストスキップに関する以下の行が削除されました。
--- a/src/cmd/objdump/objdump_test.go
+++ b/src/cmd/objdump/objdump_test.go
@@ -155,10 +155,6 @@ var armNeed = []string{
// can handle that one.
func TestDisasm(t *testing.T) {
- if runtime.GOOS == "plan9" {
- t.Skip("skipping test; see http://golang.org/issue/7947")
- }
-
tmp, exe := buildObjdump(t)
defer os.RemoveAll(tmp)
コアとなるコードの解説
src/cmd/objdump/plan9obj.go
は、Goのobjdump
ツールがPlan 9形式のオブジェクトファイルを扱うためのロジックを含んでいます。
-
validSymType
マップ: このマップは、Goのobjdump
がPlan 9オブジェクトファイルから読み取るべき「有効な」シンボルタイプを明示的に定義しています。'T'
と't'
は、それぞれグローバルおよびローカルのテキスト(コード)セグメントシンボルを表します。'D'
と'd'
は、それぞれグローバルおよびローカルの初期化済みデータセグメントシンボルを表します。'B'
と'b'
は、それぞれグローバルおよびローカルのBSS(未初期化データ)セグメントシンボルを表します。 これらのシンボルタイプは、プログラムの実行可能な部分やデータ部分に直接関連しており、逆アセンブル結果に意味のある情報を提供します。
-
plan9Symbols
関数内のフィルタリング:plan9Symbols
関数は、Plan 9オブジェクトファイルからシンボル情報を抽出し、Sym
構造体のスライスとして返します。この関数内で、元のコードはplan9obj.NewFile(f)
から得られたすべてのシンボル(plan9Syms
)を処理していました。 追加されたif !validSymType[s.Type] { continue }
という行は、この処理フローの重要な変更点です。s.Type
は、現在のシンボルのタイプ(文字)を示します。validSymType[s.Type]
は、そのシンボルタイプがvalidSymType
マップのキーとして存在し、かつその値がtrue
であるかをチェックします。!
演算子により、もしシンボルタイプがvalidSymType
マップに存在しない(つまり、有効なタイプではない)場合、条件がtrue
となり、continue
文が実行されます。continue
文は、現在のループの残りの処理をスキップし、次のイテレーションに進みます。 これにより、テキスト、データ、BSS以外のシンボルは、addrs
スライス(シンボルアドレスのソートされたリスト)の構築時にも、最終的なsym
構造体のスライス構築時にも含まれなくなります。
このフィルタリングによって、逆アセンブラは、本来のコードやデータとは無関係なシンボル情報に惑わされることなく、純粋に実行可能な命令やデータに関連するシンボルのみを考慮して逆アセンブルを実行できるようになりました。結果として、命令内の即値が誤ってシンボルとして解釈される問題が解消され、逆アセンブル結果の正確性と信頼性が向上しました。
関連リンク
- Go issue #7947 (このコミットによって修正された問題):
- 直接的なGitHubのIssueページは見つかりませんでしたが、コミットメッセージとテストコードの変更から、
cmd/objdump
のPlan 9オブジェクトファイル逆アセンブルに関する問題であったことが強く示唆されます。
- 直接的なGitHubのIssueページは見つかりませんでしたが、コミットメッセージとテストコードの変更から、
- Go
cmd/objdump
のドキュメント (Goの公式ドキュメントやソースコードリポジトリ内)- https://pkg.go.dev/cmd/objdump (Go 1.22以降のドキュメント)
- Plan 9のオブジェクトファイル形式に関する情報 (9p.ioなど)
- http://9p.io/sys/doc/a.out.html (Plan 9
a.out
形式の公式ドキュメント)
- http://9p.io/sys/doc/a.out.html (Plan 9
参考にした情報源リンク
- Go issue #7947に関する情報 (Web検索結果に基づく推測)
- Plan 9 object file symbol typesに関するWeb検索結果 (9p.ioのドキュメントなど)
- Go
cmd/objdump
に関するWeb検索結果 (go.dev, GitHub Gistなど) - Disassembler immediate values problem with symbolsに関するWeb検索結果 (Stack Exchange, Redditなど)