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

[インデックス 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のように、本来は即値である0x50text/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ファイルに集中しています。

  1. validSymTypeマップの追加:

    +var validSymType = map[rune]bool{
    +	'T': true,
    +	't': true,
    +	'D': true,
    +	'd': true,
    +	'B': true,
    +	'b': true,
    +}
    

    このマップは、有効なシンボルタイプ(テキスト、データ、BSS)を定義します。

  2. 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形式のオブジェクトファイルを扱うためのロジックを含んでいます。

  1. validSymTypeマップ: このマップは、GoのobjdumpがPlan 9オブジェクトファイルから読み取るべき「有効な」シンボルタイプを明示的に定義しています。

    • 'T''t'は、それぞれグローバルおよびローカルのテキスト(コード)セグメントシンボルを表します。
    • 'D''d'は、それぞれグローバルおよびローカルの初期化済みデータセグメントシンボルを表します。
    • 'B''b'は、それぞれグローバルおよびローカルのBSS(未初期化データ)セグメントシンボルを表します。 これらのシンボルタイプは、プログラムの実行可能な部分やデータ部分に直接関連しており、逆アセンブル結果に意味のある情報を提供します。
  2. 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オブジェクトファイル逆アセンブルに関する問題であったことが強く示唆されます。
  • Go cmd/objdumpのドキュメント (Goの公式ドキュメントやソースコードリポジトリ内)
  • Plan 9のオブジェクトファイル形式に関する情報 (9p.ioなど)

参考にした情報源リンク

  • 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など)