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

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

このコミットは、Go言語の標準ライブラリ debug/elf パッケージにおける機能拡張とバグ修正に関するものです。具体的には、ELF (Executable and Linkable Format) ファイルのヘッダからエントリポイント情報を File 構造体に公開する変更が含まれています。

コミット

commit 875f34fd4962935d8e0e17d030b91e4c4feb4f08
Author: Matthew Horsnell <matthew.horsnell@gmail.com>
Date:   Mon May 21 23:29:30 2012 -0400

    debug/elf: Expose entry point from Header in File struct.
    Fixes #3470.

    R=rsc, golang-dev
    CC=golang-dev
    https://golang.org/cl/6195074

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

https://github.com/golang/go/commit/875f34fd4962935d8e0e17d030b91e4c4feb4f08

元コミット内容

debug/elf: Header からエントリポイントを File 構造体に公開する。 Issue #3470 を修正。

変更の背景

このコミットの背景には、Go言語の debug/elf パッケージがELFファイルから情報を適切に抽出し、利用者に提供する能力の向上が挙げられます。ELFファイルは、Unix系システムにおける実行可能ファイル、共有ライブラリ、オブジェクトファイルなどの標準フォーマットであり、そのヘッダにはプログラムの実行開始アドレス(エントリポイント)など、重要なメタデータが含まれています。

元の debug/elf パッケージでは、ELFファイルのヘッダ情報は FileHeader 構造体として内部的に読み込まれていましたが、その中の Entry フィールド(エントリポイント)が、パッケージの主要なインターフェースである File 構造体を通じて直接アクセス可能ではありませんでした。

Issue #3470 は、このエントリポイント情報が File 構造体から直接取得できないことに対する要望またはバグ報告であったと推測されます。デバッグツールやシステムプログラミングにおいて、プログラムのエントリポイントは非常に重要な情報であり、これを簡単に取得できないことは、パッケージの利便性を損なうものでした。このコミットは、この情報へのアクセスを容易にすることで、debug/elf パッケージの機能性と使いやすさを向上させることを目的としています。

前提知識の解説

ELF (Executable and Linkable Format)

ELFは、Unix系オペレーティングシステム(Linux、FreeBSD、Solarisなど)で広く使用されている、実行可能ファイル、オブジェクトコード、共有ライブラリ、コアダンプなどの標準ファイルフォーマットです。WindowsのPE (Portable Executable) フォーマットに相当します。

ELFファイルは、主に以下のセクションで構成されます。

  1. ELF Header: ファイルの先頭に位置し、ELFファイルの基本的な情報(マジックナンバー、クラス(32/64ビット)、データエンコーディング、ABI、バージョン、エントリポイントアドレス、プログラムヘッダテーブルのオフセットとサイズ、セクションヘッダテーブルのオフセットとサイズなど)を含みます。
  2. Program Header Table (プログラムヘッダテーブル): 実行可能ファイルや共有ライブラリに存在し、プログラムのロード方法に関する情報(セグメントのメモリ上の位置、サイズ、パーミッションなど)を記述します。各エントリは「セグメント」を定義します。
  3. Section Header Table (セクションヘッダテーブル): オブジェクトファイルや共有ライブラリに存在し、リンク時に必要な情報(コード、データ、シンボルテーブル、リロケーション情報など)を記述します。各エントリは「セクション」を定義します。
  4. Sections (セクション): 実際のコードやデータが格納される領域です。.text (コード), .data (初期化済みデータ), .bss (初期化されていないデータ), .rodata (読み取り専用データ) などがあります。

エントリポイント (Entry Point)

エントリポイントとは、プログラムが実行を開始するメモリ上のアドレスのことです。オペレーティングシステムがプログラムをメモリにロードした後、CPUはこのエントリポイントアドレスから命令の実行を開始します。ELFヘッダには、このエントリポイントのアドレスが e_entry フィールドとして格納されています。デバッガやプロファイラ、あるいはカスタムローダなどを開発する際には、このエントリポイント情報が不可欠となります。

Go言語の debug/elf パッケージ

Go言語の標準ライブラリ debug/elf パッケージは、ELFファイルを解析し、その構造や内容にアクセスするための機能を提供します。このパッケージを使用することで、GoプログラムからELFファイルのヘッダ情報、セクション、セグメント、シンボルテーブルなどを読み取ることができます。これは、クロスプラットフォームなデバッグツール、バイナリ解析ツール、あるいはカスタムローダなどをGoで実装する際に非常に有用です。

パッケージ内の主要な構造体には以下のようなものがあります。

  • File: 開かれたELFファイル全体を表す構造体。このコミットの変更対象です。
  • FileHeader: ELFヘッダの情報を格納する構造体。このコミットで Entry フィールドが追加されました。
  • Section: ELFファイルのセクションを表す構造体。
  • ProgramHeader: ELFファイルのプログラムヘッダ(セグメント)を表す構造体。

技術的詳細

このコミットの技術的詳細は、debug/elf パッケージの内部構造とELFファイルの解析ロジックに焦点を当てています。

  1. FileHeader 構造体への Entry フィールドの追加: ELFヘッダには e_entry というフィールドがあり、これがプログラムのエントリポイントアドレスを保持しています。このコミットでは、Goの debug/elf パッケージ内でELFヘッダを表現する FileHeader 構造体に、この e_entry に対応する Entry uint64 フィールドが追加されました。uint64 型が選択されているのは、32ビットおよび64ビットのELFファイル両方に対応するためです。エントリポイントアドレスは、ELFファイルのクラス(32ビットまたは64ビット)に応じてサイズが異なりますが、uint64 で表現することで両方をカバーできます。

  2. File 構造体へのエントリポイントの公開: File 構造体は、debug/elf パッケージにおいて開かれたELFファイル全体を表現する主要な構造体です。以前は、File 構造体から直接エントリポイントにアクセスする手段がありませんでした。このコミットでは、File 構造体の内部でELFヘッダを読み込んだ際に、そのヘッダから抽出したエントリポイントアドレスを File 構造体の新しいフィールドにコピーするように変更されました。これにより、File オブジェクトを通じて elfFile.Entry のようにエントリポイントに直接アクセスできるようになります。

  3. ELFファイル解析ロジックの更新: NewFile 関数は、io.ReaderAt インターフェースを実装するソース(通常はファイル)からELFファイルを読み込み、*File 構造体を構築する役割を担っています。この関数内で、ELFヘッダを解析し、その情報を FileHeader 構造体にマッピングする処理が行われます。このコミットでは、NewFile 関数がELFヘッダから e_entry フィールドを読み取り、それを FileHeader 構造体の Entry フィールドに設定し、さらにその値を File 構造体の新しい Entry フィールドにコピーするロジックが追加されました。これにより、ELFファイルのパース時にエントリポイント情報が適切に抽出され、File 構造体に格納されるようになります。

  4. テストケースの更新: 機能追加に伴い、既存のテストケース file_test.go が更新されました。具体的には、fileTest 構造体の初期化において、FileHeader のインスタンスにエントリポイントの期待値が追加されました。これにより、debug/elf パッケージがELFファイルからエントリポイントを正しく読み取れることを検証できるようになります。テストデータとして使用されている gcc-386-freebsd-execgcc-amd64-linux-exec のエントリポイントアドレスが、それぞれ 0x80483cc0x4003e0 としてテストケースに明示的に記述されています。

この変更により、debug/elf パッケージの利用者は、ELFファイルのデバッグや解析を行う際に、プログラムのエントリポイントをより簡単に取得できるようになり、ツールの開発が容易になります。

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

src/pkg/debug/elf/file.go

--- a/src/pkg/debug/elf/file.go
+++ b/src/pkg/debug/elf/file.go
@@ -31,6 +31,7 @@ type FileHeader struct {
 	ByteOrder  binary.ByteOrder
 	Type       Type
 	Machine    Machine
+	Entry      uint64
 }
 
 // A File represents an open ELF file.
@@ -240,6 +241,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 		}
 		f.Type = Type(hdr.Type)
 		f.Machine = Machine(hdr.Machine)
+		f.Entry = uint64(hdr.Entry)
 		if v := Version(hdr.Version); v != f.Version {
 			return nil, &FormatError{0, "mismatched ELF version", v}
 		}
@@ -258,6 +260,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 		}
 		f.Type = Type(hdr.Type)
 		f.Machine = Machine(hdr.Machine)
+		f.Entry = uint64(hdr.Entry)
 		if v := Version(hdr.Version); v != f.Version {
 			return nil, &FormatError{0, "mismatched ELF version", v}
 		}

src/pkg/debug/elf/file_test.go

--- a/src/pkg/debug/elf/file_test.go
+++ b/src/pkg/debug/elf/file_test.go
@@ -24,7 +24,7 @@ type fileTest struct {
 var fileTests = []fileTest{
 	{
 		"testdata/gcc-386-freebsd-exec",
-		FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386},
+		FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386, 0x80483cc},
 		[]SectionHeader{
 			{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0},
 			{".interp", SHT_PROGBITS, SHF_ALLOC, 0x80480d4, 0xd4, 0x15, 0x0, 0x0, 0x1, 0x0},
@@ -67,7 +67,7 @@ var fileTests = []fileTest{
 	},
 	{
 		"testdata/gcc-amd64-linux-exec",
-		FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64},
+		FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64, 0x4003e0},
 		[]SectionHeader{
 			{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0},
 			{".interp", SHT_PROGBITS, SHF_ALLOC, 0x400200, 0x200, 0x1c, 0x0, 0x0, 0x1, 0x0},

コアとなるコードの解説

src/pkg/debug/elf/file.go の変更点

  1. FileHeader 構造体への Entry フィールド追加:

    type FileHeader struct {
    	ByteOrder  binary.ByteOrder
    	Type       Type
    	Machine    Machine
    	Entry      uint64 // <-- 追加
    }
    

    FileHeader はELFファイルのヘッダ情報をGoの構造体として表現します。ここに追加された Entry uint64 フィールドは、ELFヘッダの e_entry フィールド(プログラムのエントリポイントアドレス)を格納するためのものです。uint64 型は、32ビットおよび64ビットのELFファイル両方のアドレスを表現できます。

  2. NewFile 関数内での Entry フィールドへの値の代入: NewFile 関数は、ELFファイルを読み込み、*File 構造体を構築する際に、ELFヘッダから情報を抽出します。変更箇所は、32ビットELFヘッダ (hdr32) と64ビットELFヘッダ (hdr) の両方を処理する部分にあります。

    // 32ビットELFヘッダの場合
    f.Type = Type(hdr32.Type)
    f.Machine = Machine(hdr32.Machine)
    f.Entry = uint64(hdr32.Entry) // <-- 追加
    
    // 64ビットELFヘッダの場合
    f.Type = Type(hdr.Type)
    f.Machine = Machine(hdr.Machine)
    f.Entry = uint64(hdr.Entry) // <-- 追加
    

    これらの行は、それぞれ32ビットおよび64ビットのELFヘッダから読み取った Entry フィールドの値を、新しく追加された File 構造体の Entry フィールドに uint64 型にキャストして代入しています。これにより、File オブジェクトがELFファイルのエントリポイントアドレスを保持するようになります。

src/pkg/debug/elf/file_test.go の変更点

  1. fileTests 配列内の FileHeader 初期化の変更:
    -		FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386},
    +		FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386, 0x80483cc},
    
    -		FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64},
    +		FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64, 0x4003e0},
    
    fileTests は、異なるELFファイルのテストデータとその期待される FileHeader の値を含む配列です。この変更では、既存の FileHeader の初期化リストの末尾に、対応するELFファイルのエントリポイントアドレス(0x80483cc0x4003e0)が追加されました。これにより、テストフレームワークがELFファイルを解析した結果得られる FileHeader.Entry の値が、これらの期待値と一致するかどうかを検証できるようになります。これは、新しい機能が正しく実装され、既存のELFファイルからエントリポイントを正確に抽出できることを保証するための重要なテストです。

これらの変更により、debug/elf パッケージはELFファイルのエントリポイント情報をより完全に抽出し、利用者に提供できるようになりました。

関連リンク

参考にした情報源リンク