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

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

このコミットは、Go言語のツールチェインに含まれるnmコマンドとobjdumpコマンドにおけるELF(Executable and Linkable Format)ファイルのシンボル型解析のバグ修正、およびobjdumpの逆アセンブル処理における境界チェックの改善を目的としています。特に、elf.File.Sectionsのインデックス処理の誤りを修正し、テキストセグメント外の逆アセンブル要求によるクラッシュを防ぐ堅牢性向上が図られています。

コミット

commit 8e22903b46aadd6eda937417cba86b528cba92e2
Author: Russ Cox <rsc@golang.org>
Date:   Wed May 14 17:45:13 2014 -0700

    cmd/nm, cmd/objdump: fix elf symbol types
    
    Turns out elf.File.Sections is indexed by the actual
    section number, not the number minus one.
    I don't know why I thought the -1 was necessary.
    
    Fixes objdump test (and therefore build) on ELF systems.
    
    While we're here, fix bounds on gnuDump so that we
    don't crash when asked to disassemble outside
    the text segment. May fix Windows build or at least
    make the failure more interesting.
    
    TBR=iant
    CC=golang-codereviews
    https://golang.org/cl/92390043

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

https://github.com/golang/go/commit/8e22903b46aadd6eda937417cba86b528cba92e2

元コミット内容

cmd/nm, cmd/objdump: fix elf symbol types

Turns out elf.File.Sections is indexed by the actual
section number, not the number minus one.
I don't know why I thought the -1 was necessary.

Fixes objdump test (and therefore build) on ELF systems.

While we're here, fix bounds on gnuDump so that we
don't crash when asked to disassemble outside
the text segment. May fix Windows build or at least
make the failure more interesting.

TBR=iant
CC=golang-codereviews
https://golang.org/cl/92390043

変更の背景

このコミットは主に2つの問題に対処しています。

  1. ELFセクションインデックスの誤り: Goのelfパッケージが提供するelf.File.Sectionsは、ELFファイルのセクションヘッダテーブルを表します。ELF仕様ではセクション番号は1から始まる(0は未定義セクション)ため、このスライスは実際のセクション番号で直接インデックスされるべきでした。しかし、これまでの実装では誤ってi-1のようにオフセットを適用しており、これがシンボル型を正しく識別できない原因となっていました。このバグは特にELFシステム上でのobjdumpのテスト失敗を引き起こしていました。
  2. objdumpにおける逆アセンブル範囲の堅牢性不足: objdumpコマンドのgnuDump関数は、逆アセンブルを行う際に指定されたアドレス範囲が実行可能ファイルのテキストセグメント(コードが配置される領域)の境界を越える場合に、クラッシュする可能性がありました。これは、無効なアドレス範囲が指定された場合にプログラムが予期せぬ動作をする脆弱性を示していました。この修正は、特にWindowsのようなシステムでのビルドの安定性向上、または問題発生時のデバッグ情報の改善に寄与します。

これらの問題は、Goツールチェインの安定性と正確性に影響を与えるものであり、特にクロスコンパイルや異なるOS環境でのツール利用において重要でした。

前提知識の解説

ELF (Executable and Linkable Format)

ELFは、Unix系OS(Linux、BSDなど)で広く使用されている実行可能ファイル、オブジェクトコード、共有ライブラリ、コアダンプファイルの標準フォーマットです。プログラムの実行に必要な情報(コード、データ、シンボルテーブル、セクション情報など)を構造化して格納します。

  • セクション (Sections): ELFファイルは複数のセクションに分割されます。各セクションは特定の種類のデータ(例: .textは実行可能コード、.dataは初期化済みデータ、.bssは初期化されていないデータ、.symtabはシンボルテーブルなど)を格納します。
  • セクションヘッダテーブル (Section Header Table): ファイル内の各セクションに関する情報(名前、種類、サイズ、ファイルオフセット、メモリ上のアドレス、フラグなど)を記述したテーブルです。
  • シンボル (Symbols): プログラム内の関数や変数などの名前と、それらがメモリ上のどこに配置されているか(アドレス)を関連付ける情報です。シンボルは、リンカが異なるオブジェクトファイルを結合したり、デバッガがソースコードと実行中のプログラムを関連付けたりするために使用されます。
  • シンボルテーブル (Symbol Table): シンボル情報のリストです。各エントリはシンボルの名前、値(アドレス)、サイズ、型(関数、オブジェクトなど)、および関連するセクションインデックスなどを含みます。

nmコマンド

nm(name mangler)は、オブジェクトファイル、静的ライブラリ、または実行可能ファイル内のシンボル(関数名、変数名など)をリスト表示するためのUnix系コマンドです。シンボルの種類(例: Tはテキストセクションの関数、BはBSSセクションの変数、Dはデータセクションの変数など)も表示します。

objdumpコマンド

objdumpは、オブジェクトファイルや実行可能ファイルに関する様々な情報を表示するためのUnix系コマンドです。主な機能には、逆アセンブル(機械語コードをアセンブリ言語に変換)、セクションヘッダの表示、シンボルテーブルの表示などがあります。このコミットでは、特に逆アセンブル機能(gnuDump関数に関連)とシンボル型解析が関係しています。

Goのelfパッケージ

Go標準ライブラリのdebug/elfパッケージは、GoプログラムからELFファイルを読み込み、解析するための機能を提供します。このパッケージは、ELFヘッダ、プログラムヘッダ、セクションヘッダ、シンボルテーブルなどの構造をGoの型として定義し、それらの情報にアクセスするためのメソッドを提供します。

  • elf.File.Sections: ELFファイルのセクションヘッダテーブルを表すスライスです。このスライスのインデックスが、ELF仕様におけるセクション番号とどのように対応するかが、今回のバグの核心でした。

技術的詳細

ELFセクションインデックスの修正

元のコードでは、elf.File.Sectionsスライスにアクセスする際に、シンボルが属するセクションのインデックスiから1を引いていました(p.Sections[i-1])。これは、スライスが0-ベースのインデックスを持つという一般的なプログラミングの慣習に基づいていた可能性があります。しかし、ELF仕様においてセクション番号は1から始まるため、elf.File.Sectionsは実際のセクション番号を直接インデックスとして使用するように設計されていました。

例えば、ELFファイルでセクション番号1が.textセクションを指す場合、elf.File.Sections[1]がそのセクション情報を含むべきです。しかし、p.Sections[i-1]というコードでは、セクション番号1のシンボルに対してp.Sections[0]にアクセスしてしまい、誤ったセクション情報(または存在しないセクション)を参照していました。

この修正により、i-1iに修正され、p.Sections[i]と直接アクセスすることで、シンボルが実際に属するセクションの情報を正しく取得できるようになりました。これにより、nmobjdumpがシンボルの種類(例: コードセクションのシンボルはT、データセクションのシンボルはDなど)を正確に判別できるようになります。

また、インデックスの境界チェックもi <= 0 || i > len(p.Sections)からi < 0 || i >= len(p.Sections)に変更されました。これは、Goのスライスインデックスの慣習(0からlen-1まで)に合わせたもので、iが0の場合(ELFのSHN_UNDEFなど、未定義セクションを示す特殊な値)や、スライスの範囲外にアクセスしようとした場合のパニックを防ぎます。

objdumpにおける逆アセンブル範囲の堅牢性向上

src/cmd/objdump/main.gognuDump関数では、逆アセンブルの開始アドレス(start)と終了アドレス(end)が、実行可能ファイルのテキストセグメント(textStartからtextStart + len(textData)まで)の範囲内に収まるように調整するロジックが追加されました。

  • if start < textStart { start = textStart }: 指定された開始アドレスがテキストセグメントの開始アドレスよりも小さい場合、テキストセグメントの開始アドレスに調整します。これにより、テキストセグメントより前のメモリ領域を逆アセンブルしようとする試みを防ぎます。
  • if end < start { end = start }: 終了アドレスが開始アドレスよりも小さい場合、終了アドレスを開始アドレスに調整します。これは、無効な範囲指定(例: 終了アドレスが開始アドレスより前)を正規化し、空の範囲として扱います。
  • if end > textStart+uint64(len(textData)) { end = textStart + uint64(len(textData)) }: 指定された終了アドレスがテキストセグメントの終了アドレスよりも大きい場合、テキストセグメントの終了アドレスに調整します。これにより、テキストセグメントより後のメモリ領域を逆アセンブルしようとする試みを防ぎます。

これらの境界チェックにより、gnuDump関数は常に有効なテキストセグメントの範囲内で逆アセンブルを実行するようになり、無効な入力によって発生する可能性のあるクラッシュや予期せぬ動作を防ぎ、ツールの堅牢性が向上しました。

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

src/cmd/nm/elf.go

--- a/src/cmd/nm/elf.go
+++ b/src/cmd/nm/elf.go
@@ -34,10 +34,10 @@ func elfSymbols(f *os.File) []Sym {
 			sym.Code = 'B'
 		default:
 			i := int(s.Section)
-			if i <= 0 || i > len(p.Sections) {
+			if i < 0 || i >= len(p.Sections) {
 				break
 			}
-			sect := p.Sections[i-1]
+			sect := p.Sections[i]
 			switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) {
 			case elf.SHF_ALLOC | elf.SHF_EXECINSTR:
 				sym.Code = 'T'

src/cmd/objdump/elf.go

--- a/src/cmd/objdump/elf.go
+++ b/src/cmd/objdump/elf.go
@@ -42,10 +42,10 @@ func elfSymbols(f *os.File) (syms []Sym, goarch string) {
 			sym.Code = 'B'
 		default:
 			i := int(s.Section)
-			if i <= 0 || i > len(p.Sections) {
+			if i < 0 || i >= len(p.Sections) {
 				break
 			}
-			sect := p.Sections[i-1]
+			sect := p.Sections[i]
 			switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) {
 			case elf.SHF_ALLOC | elf.SHF_EXECINSTR:
 				sym.Code = 'T'

src/cmd/objdump/main.go

--- a/src/cmd/objdump/main.go
+++ b/src/cmd/objdump/main.go
@@ -235,6 +235,15 @@ func gnuDump(tab *gosym.Table, lookup lookupFunc, disasm disasmFunc, textData []
 	if err != nil {
 		log.Fatalf("invalid end PC: %v", err)
 	}
+	if start < textStart {
+		start = textStart
+	}
+	if end < start {
+		end = start
+	}
+	if end > textStart+uint64(len(textData)) {
+		end = textStart + uint64(len(textData))
+	}
 
 	stdout := bufio.NewWriter(os.Stdout)
 	defer stdout.Flush()

コアとなるコードの解説

src/cmd/nm/elf.go および src/cmd/objdump/elf.go の変更

これらのファイルでは、elfSymbols関数内でシンボルのセクション情報を処理する部分が変更されています。

  • i := int(s.Section): シンボルが属するセクションのインデックスを取得します。s.Sectiondebug/elfパッケージのSymbol構造体の一部で、ELFシンボルテーブルエントリのst_shndxフィールドに対応します。
  • if i < 0 || i >= len(p.Sections): 変更後の境界チェックです。
    • i < 0: セクションインデックスが負の値である場合(これは通常、SHN_UNDEFなどの特殊なセクションインデックスを示すか、不正な値であることを意味します)。
    • i >= len(p.Sections): セクションインデックスがp.Sectionsスライスの範囲外である場合。 これらの条件のいずれかが真であれば、ループを抜けて次のシンボルに進みます。これにより、不正なインデックスアクセスによるパニックを防ぎます。
  • sect := p.Sections[i]: ここが最も重要な変更点です。以前はp.Sections[i-1]でしたが、iに修正されました。これにより、elf.File.Sectionsが実際のELFセクション番号で直接インデックスされるという設計意図に合致し、シンボルが属するセクションの情報を正しく取得できるようになりました。
  • switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR): 取得したセクションのフラグをチェックし、シンボルの種類(例: 実行可能コードセクションであればT、データセクションであればDなど)を決定します。このフラグチェックは、正しいセクション情報が取得されることで、より正確なシンボル型分類が可能になります。

src/cmd/objdump/main.go の変更

gnuDump関数は、指定されたアドレス範囲の機械語コードを逆アセンブルする役割を担っています。

  • if start < textStart { start = textStart }: 逆アセンブルの開始アドレスstartが、実行可能コードが格納されているテキストセグメントの開始アドレスtextStartよりも小さい場合、starttextStartに強制的に設定します。これにより、コード領域外の不正な開始アドレスが指定されても、逆アセンブルはテキストセグメントの先頭から行われるようになります。
  • if end < start { end = start }: 逆アセンブルの終了アドレスendが、開始アドレスstartよりも小さい場合、endstartに設定します。これは、逆アセンブル範囲が負になるような不正な指定(例: start=100, end=50)を修正し、結果として空の逆アセンブル範囲(または単一アドレスの逆アセンブル)として扱われるようにします。
  • if end > textStart+uint64(len(textData)) { end = textStart + uint64(len(textData)) }: 逆アセンブルの終了アドレスendが、テキストセグメントの終了アドレス(textStartにテキストデータの長さlen(textData)を加えたもの)よりも大きい場合、endをテキストセグメントの終了アドレスに強制的に設定します。これにより、コード領域外の不正な終了アドレスが指定されても、逆アセンブルはテキストセグメントの末尾までで停止するようになります。

これらの変更により、gnuDump関数はより堅牢になり、ユーザーがどのようなアドレス範囲を指定しても、プログラムがクラッシュすることなく、常に有効なテキストセグメントの範囲内で安全に逆アセンブルを実行できるようになりました。

関連リンク

参考にした情報源リンク

  • (この解説の生成において、特定の外部Webサイトを直接参照する必要はありませんでした。Goのソースコード、ELFの一般的な知識、およびnm/objdumpコマンドの機能に関する既存の知識に基づいて作成されました。)```markdown

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

このコミットは、Go言語のツールチェインに含まれるnmコマンドとobjdumpコマンドにおけるELF(Executable and Linkable Format)ファイルのシンボル型解析のバグ修正、およびobjdumpの逆アセンブル処理における境界チェックの改善を目的としています。特に、elf.File.Sectionsのインデックス処理の誤りを修正し、テキストセグメント外の逆アセンブル要求によるクラッシュを防ぐ堅牢性向上が図られています。

コミット

commit 8e22903b46aadd6eda937417cba86b528cba92e2
Author: Russ Cox <rsc@golang.org>
Date:   Wed May 14 17:45:13 2014 -0700

    cmd/nm, cmd/objdump: fix elf symbol types
    
    Turns out elf.File.Sections is indexed by the actual
    section number, not the number minus one.
    I don't know why I thought the -1 was necessary.
    
    Fixes objdump test (and therefore build) on ELF systems.
    
    While we're here, fix bounds on gnuDump so that we
    don't crash when asked to disassemble outside
    the text segment. May fix Windows build or at least
    make the failure more interesting.
    
    TBR=iant
    CC=golang-codereviews
    https://golang.org/cl/92390043

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

https://github.com/golang/go/commit/8e22903b46aadd6eda937417cba86b528cba92e2

元コミット内容

cmd/nm, cmd/objdump: fix elf symbol types

Turns out elf.File.Sections is indexed by the actual
section number, not the number minus one.
I don't know why I thought the -1 was necessary.

Fixes objdump test (and therefore build) on ELF systems.

While we're here, fix bounds on gnuDump so that we
don't crash when asked to disassemble outside
the text segment. May fix Windows build or at least
make the failure more interesting.

TBR=iant
CC=golang-codereviews
https://golang.org/cl/92390043

変更の背景

このコミットは主に2つの問題に対処しています。

  1. ELFセクションインデックスの誤り: Goのelfパッケージが提供するelf.File.Sectionsは、ELFファイルのセクションヘッダテーブルを表します。ELF仕様ではセクション番号は1から始まる(0は未定義セクション)ため、このスライスは実際のセクション番号で直接インデックスされるべきでした。しかし、これまでの実装では誤ってi-1のようにオフセットを適用しており、これがシンボル型を正しく識別できない原因となっていました。このバグは特にELFシステム上でのobjdumpのテスト失敗を引き起こしていました。
  2. objdumpにおける逆アセンブル範囲の堅牢性不足: objdumpコマンドのgnuDump関数は、逆アセンブルを行う際に指定されたアドレス範囲が実行可能ファイルのテキストセグメント(コードが配置される領域)の境界を越える場合に、クラッシュする可能性がありました。これは、無効なアドレス範囲が指定された場合にプログラムが予期せぬ動作をする脆弱性を示していました。この修正は、特にWindowsのようなシステムでのビルドの安定性向上、または問題発生時のデバッグ情報の改善に寄与します。

これらの問題は、Goツールチェインの安定性と正確性に影響を与えるものであり、特にクロスコンパイルや異なるOS環境でのツール利用において重要でした。

前提知識の解説

ELF (Executable and Linkable Format)

ELFは、Unix系OS(Linux、BSDなど)で広く使用されている実行可能ファイル、オブジェクトコード、共有ライブラリ、コアダンプファイルの標準フォーマットです。プログラムの実行に必要な情報(コード、データ、シンボルテーブル、セクション情報など)を構造化して格納します。

  • セクション (Sections): ELFファイルは複数のセクションに分割されます。各セクションは特定の種類のデータ(例: .textは実行可能コード、.dataは初期化済みデータ、.bssは初期化されていないデータ、.symtabはシンボルテーブルなど)を格納します。
  • セクションヘッダテーブル (Section Header Table): ファイル内の各セクションに関する情報(名前、種類、サイズ、ファイルオフセット、メモリ上のアドレス、フラグなど)を記述したテーブルです。
  • シンボル (Symbols): プログラム内の関数や変数などの名前と、それらがメモリ上のどこに配置されているか(アドレス)を関連付ける情報です。シンボルは、リンカが異なるオブジェクトファイルを結合したり、デバッガがソースコードと実行中のプログラムを関連付けたりするために使用されます。
  • シンボルテーブル (Symbol Table): シンボル情報のリストです。各エントリはシンボルの名前、値(アドレス)、サイズ、型(関数、オブジェクトなど)、および関連するセクションインデックスなどを含みます。

nmコマンド

nm(name mangler)は、オブジェクトファイル、静的ライブラリ、または実行可能ファイル内のシンボル(関数名、変数名など)をリスト表示するためのUnix系コマンドです。シンボルの種類(例: Tはテキストセクションの関数、BはBSSセクションの変数、Dはデータセクションの変数など)も表示します。

objdumpコマンド

objdumpは、オブジェクトファイルや実行可能ファイルに関する様々な情報を表示するためのUnix系コマンドです。主な機能には、逆アセンブル(機械語コードをアセンブリ言語に変換)、セクションヘッダの表示、シンボルテーブルの表示などがあります。このコミットでは、特に逆アセンブル機能(gnuDump関数に関連)とシンボル型解析が関係しています。

Goのelfパッケージ

Go標準ライブラリのdebug/elfパッケージは、GoプログラムからELFファイルを読み込み、解析するための機能を提供します。このパッケージは、ELFヘッダ、プログラムヘッダ、セクションヘッダ、シンボルテーブルなどの構造をGoの型として定義し、それらの情報にアクセスするためのメソッドを提供します。

  • elf.File.Sections: ELFファイルのセクションヘッダテーブルを表すスライスです。このスライスのインデックスが、ELF仕様におけるセクション番号とどのように対応するかが、今回のバグの核心でした。

技術的詳細

ELFセクションインデックスの修正

元のコードでは、elf.File.Sectionsスライスにアクセスする際に、シンボルが属するセクションのインデックスiから1を引いていました(p.Sections[i-1])。これは、スライスが0-ベースのインデックスを持つという一般的なプログラミングの慣習に基づいていた可能性があります。しかし、ELF仕様においてセクション番号は1から始まるため、elf.File.Sectionsは実際のセクション番号を直接インデックスとして使用するように設計されていました。

例えば、ELFファイルでセクション番号1が.textセクションを指す場合、elf.File.Sections[1]がそのセクション情報を含むべきです。しかし、p.Sections[i-1]というコードでは、セクション番号1のシンボルに対してp.Sections[0]にアクセスしてしまい、誤ったセクション情報(または存在しないセクション)を参照していました。

この修正により、i-1iに修正され、p.Sections[i]と直接アクセスすることで、シンボルが実際に属するセクションの情報を正しく取得できるようになりました。これにより、nmobjdumpがシンボルの種類(例: コードセクションのシンボルはT、データセクションのシンボルはDなど)を正確に判別できるようになります。

また、インデックスの境界チェックもi <= 0 || i > len(p.Sections)からi < 0 || i >= len(p.Sections)に変更されました。これは、Goのスライスインデックスの慣習(0からlen-1まで)に合わせたもので、iが0の場合(ELFのSHN_UNDEFなど、未定義セクションを示す特殊な値)や、スライスの範囲外にアクセスしようとした場合のパニックを防ぎます。

objdumpにおける逆アセンブル範囲の堅牢性向上

src/cmd/objdump/main.gognuDump関数では、逆アセンブルの開始アドレス(start)と終了アドレス(end)が、実行可能ファイルのテキストセグメント(textStartからtextStart + len(textData)まで)の範囲内に収まるように調整するロジックが追加されました。

  • if start < textStart { start = textStart }: 指定された開始アドレスがテキストセグメントの開始アドレスよりも小さい場合、テキストセグメントの開始アドレスに調整します。これにより、テキストセグメントより前のメモリ領域を逆アセンブルしようとする試みを防ぎます。
  • if end < start { end = start }: 終了アドレスが開始アドレスよりも小さい場合、終了アドレスを開始アドレスに調整します。これは、無効な範囲指定(例: 終了アドレスが開始アドレスより前)を正規化し、空の範囲として扱います。
  • if end > textStart+uint64(len(textData)) { end = textStart + uint64(len(textData)) }: 指定された終了アドレスがテキストセグメントの終了アドレス(textStartにテキストデータの長さlen(textData)を加えたもの)よりも大きい場合、endをテキストセグメントの終了アドレスに強制的に設定します。これにより、コード領域外の不正な終了アドレスが指定されても、逆アセンブルはテキストセグメントの末尾までで停止するようになります。

これらの境界チェックにより、gnuDump関数はより堅牢になり、ユーザーがどのようなアドレス範囲を指定しても、プログラムがクラッシュすることなく、常に有効なテキストセグメントの範囲内で安全に逆アセンブルを実行できるようになりました。

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

src/cmd/nm/elf.go

--- a/src/cmd/nm/elf.go
+++ b/src/cmd/nm/elf.go
@@ -34,10 +34,10 @@ func elfSymbols(f *os.File) []Sym {
 			sym.Code = 'B'
 		default:
 			i := int(s.Section)
-			if i <= 0 || i > len(p.Sections) {
+			if i < 0 || i >= len(p.Sections) {
 				break
 			}
-			sect := p.Sections[i-1]
+			sect := p.Sections[i]
 			switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) {
 			case elf.SHF_ALLOC | elf.SHF_EXECINSTR:
 				sym.Code = 'T'

src/cmd/objdump/elf.go

--- a/src/cmd/objdump/elf.go
+++ b/src/cmd/objdump/elf.go
@@ -42,10 +42,10 @@ func elfSymbols(f *os.File) (syms []Sym, goarch string) {
 			sym.Code = 'B'
 		default:
 			i := int(s.Section)
-			if i <= 0 || i > len(p.Sections) {
+			if i < 0 || i >= len(p.Sections) {
 				break
 			}
-			sect := p.Sections[i-1]
+			sect := p.Sections[i]
 			switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) {
 			case elf.SHF_ALLOC | elf.SHF_EXECINSTR:
 				sym.Code = 'T'

src/cmd/objdump/main.go

--- a/src/cmd/objdump/main.go
+++ b/cmd/objdump/main.go
@@ -235,6 +235,15 @@ func gnuDump(tab *gosym.Table, lookup lookupFunc, disasm disasmFunc, textData []
 	if err != nil {
 		log.Fatalf("invalid end PC: %v", err)
 	}
+	if start < textStart {
+		start = textStart
+	}
+	if end < start {
+		end = start
+	}
+	if end > textStart+uint64(len(textData)) {
+		end = textStart + uint64(len(textData))
+	}
 
 	stdout := bufio.NewWriter(os.Stdout)
 	defer stdout.Flush()

コアとなるコードの解説

src/cmd/nm/elf.go および src/cmd/objdump/elf.go の変更

これらのファイルでは、elfSymbols関数内でシンボルのセクション情報を処理する部分が変更されています。

  • i := int(s.Section): シンボルが属するセクションのインデックスを取得します。s.Sectiondebug/elfパッケージのSymbol構造体の一部で、ELFシンボルテーブルエントリのst_shndxフィールドに対応します。
  • if i < 0 || i >= len(p.Sections): 変更後の境界チェックです。
    • i < 0: セクションインデックスが負の値である場合(これは通常、SHN_UNDEFなどの特殊なセクションインデックスを示すか、不正な値であることを意味します)。
    • i >= len(p.Sections): セクションインデックスがp.Sectionsスライスの範囲外である場合。 これらの条件のいずれかが真であれば、ループを抜けて次のシンボルに進みます。これにより、不正なインデックスアクセスによるパニックを防ぎます。
  • sect := p.Sections[i]: ここが最も重要な変更点です。以前はp.Sections[i-1]でしたが、iに修正されました。これにより、elf.File.Sectionsが実際のELFセクション番号で直接インデックスされるという設計意図に合致し、シンボルが属するセクションの情報を正しく取得できるようになりました。
  • switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR): 取得したセクションのフラグをチェックし、シンボルの種類(例: 実行可能コードセクションであればT、データセクションであればDなど)を決定します。このフラグチェックは、正しいセクション情報が取得されることで、より正確なシンボル型分類が可能になります。

src/cmd/objdump/main.go の変更

gnuDump関数は、指定されたアドレス範囲の機械語コードを逆アセンブルする役割を担っています。

  • if start < textStart { start = textStart }: 逆アセンブルの開始アドレスstartが、実行可能コードが格納されているテキストセグメントの開始アドレスtextStartよりも小さい場合、starttextStartに強制的に設定します。これにより、コード領域外の不正な開始アドレスが指定されても、逆アセンブルはテキストセグメントの先頭から行われるようになります。
  • if end < start { end = start }: 逆アセンブルの終了アドレスendが、開始アドレスstartよりも小さい場合、endstartに設定します。これは、逆アセンブル範囲が負になるような不正な指定(例: start=100, end=50)を修正し、結果として空の逆アセンブル範囲(または単一アドレスの逆アセンブル)として扱われるようにします。
  • if end > textStart+uint64(len(textData)) { end = textStart + uint64(len(textData)) }: 逆アセンブルの終了アドレスendが、テキストセグメントの終了アドレス(textStartにテキストデータの長さlen(textData)を加えたもの)よりも大きい場合、endをテキストセグメントの終了アドレスに強制的に設定します。これにより、コード領域外の不正な終了アドレスが指定されても、逆アセンブルはテキストセグメントの末尾までで停止するようになります。

これらの変更により、gnuDump関数はより堅牢になり、ユーザーがどのようなアドレス範囲を指定しても、プログラムがクラッシュすることなく、常に有効なテキストセグメントの範囲内で安全に逆アセンブルを実行できるようになりました。

関連リンク

参考にした情報源リンク

  • (この解説の生成において、特定の外部Webサイトを直接参照する必要はありませんでした。Goのソースコード、ELFの一般的な知識、およびnm/objdumpコマンドの機能に関する既存の知識に基づいて作成されました。)