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

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

このコミットは、Go言語の標準ライブラリdebug/plan9objパッケージのAPIクリーンアップを目的としています。plan9objパッケージは、Plan 9オペレーティングシステムで使用されるa.out実行可能ファイルの解析をサポートします。この変更により、内部構造の非公開化、冗長な構造体の削除、主要なヘッダ情報のFileHeaderへの統合、マジックナンバーの定数化、およびドキュメントの改善が行われました。

コミット

commit b28aa1f1ecfd54178e4074670a4f2fa708ef0cf0
Author: David du Colombier <0intro@gmail.com>
Date:   Tue May 20 10:56:50 2014 -0700

    debug/plan9obj: cleanup api
    
    - Don't export Prog structure.
    - Remove ProgHeader and ExecTable structures.
    - Add Magic, Bss and Entry fields in FileHeader.
    - Replace ?_MAGIC variables with constants.
    - Ignore final EOF from ReadAt.
    - Improve documentation.
    
    Fixes #7989.
    
    LGTM=rsc
    R=rsc, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/91400044

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

https://github.com/golang/go/commit/b28aa1f1ecfd54178e4074670a4f2fa708ef0cf0

元コミット内容

このコミットは、debug/plan9objパッケージのAPIを整理し、よりクリーンで使いやすいものにすることを目的としています。具体的には以下の変更が含まれます。

  • Prog構造体をエクスポートしないように変更。
  • ProgHeaderおよびExecTable構造体を削除。
  • FileHeader構造体にMagicBssEntryフィールドを追加。
  • マジックナンバーを表す?_MAGIC変数を定数に置き換え。
  • ReadAtからの最終的なEOFを無視する処理を追加。
  • ドキュメントの改善。

この変更は、Go issue #7989を修正するものです。

変更の背景

このコミットの主な背景は、debug/plan9objパッケージのAPIの整理と改善です。既存のAPIには、外部に公開する必要のない内部構造体(ProgProgHeaderExecTable)が含まれており、これらがAPIの複雑性を増していました。また、a.outファイルのヘッダ情報の一部がFileHeaderに直接含まれておらず、マジックナンバーの扱いも一貫性がありませんでした。

Go言語の設計原則として、APIはシンプルで、必要なものだけを公開し、内部実装の詳細は隠蔽することが推奨されます。このコミットは、これらの原則に沿ってplan9objパッケージのAPIを洗練し、利用者がより直感的に扱えるようにすることを意図しています。

Fixes #7989という記述から、このコミットが特定のバグや問題の解決に関連していることが示唆されます。GoのChange List (CL) 91400044によると、この変更はlinux-386ビルダを一時的に壊したという情報もあり、APIのクリーンアップがビルドシステムに影響を与える可能性があったことを示しています。これは、Goのツールチェインがplan9objパッケージに依存しているため、そのAPI変更が広範囲に影響を及ぼす可能性があったことを示唆しています。

前提知識の解説

Plan 9 a.out フォーマット

a.outは、Unix系システムで古くから使われていた実行可能ファイルフォーマットの名称ですが、Plan 9オペレーティングシステムにおけるa.outは、伝統的なUnixのa.outとは異なる独自のフォーマットです。Go言語は、そのルーツの一部にPlan 9の影響を受けており、debug/plan9objパッケージは、GoプログラムがPlan 9のa.outファイルを解析するための機能を提供します。

Plan 9 a.outファイルは、以下の主要なセクションで構成されます。

  1. ヘッダ (Header): カーネルがバイナリファイルをメモリにロードし、実行するために必要なパラメータ(テキスト、データ、BSSセグメントのサイズ、エントリポイントなど)を含みます。
  2. プログラムテキスト (Text Segment): マシンコードと読み取り専用データを含みます。
  3. データ (Data Segment): 初期化されたデータを含み、書き込み可能なメモリにロードされます。
  4. シンボルテーブル (Symbol Table): リンカが名前付き変数や関数のアドレスを相互参照するために使用するレコードを含みます。
  5. PC/SPオフセットテーブル (MC68020のみ): MC68020実行可能ファイルに特有のセクションで、プログラムの場所に応じたスタックフレームポインタのオフセットをエンコードします。
  6. PC/行番号テーブル (PC/Line Number Table): プログラムカウンタ (PC) の値とソースコードの行番号のマッピングを提供し、デバッグに役立ちます。

Goのdebug/plan9objパッケージは、これらのセクションをGoプログラムからアクセスできるようにするための構造体と関数を提供します。

Go言語におけるio.ReaderAtio.SectionReader

  • io.ReaderAt: このインターフェースは、ReadAt(p []byte, off int64) (n int, err error)メソッドを定義します。これは、指定されたオフセットoffからデータを読み込み、pに書き込むことを可能にします。ReadAtは、内部の読み取りオフセットを変更しないため、複数のゴルーチンから同時に安全にアクセスできます。
  • io.SectionReader: io.SectionReaderは、io.ReaderAtio.Seekerインターフェースを実装する構造体です。これは、基となるio.ReaderAtから特定のセクション(オフセットとサイズで定義される範囲)を読み取るためのビューを提供します。このコミットでは、Section構造体のsrフィールドがio.SectionReader型であり、Section.Data()メソッドでs.sr.ReadAt(dat, 0)のように使用されています。

ReadAtの挙動として、要求されたバイト数よりも少ないバイト数を読み込んだ場合でも、エラーがnilであることがあります。これは、ファイルの終端に達した場合に発生し、io.EOFエラーが返される前に部分的な読み込みが成功することがあります。このコミットの「Ignore final EOF from ReadAt」という変更は、このようなケースでio.EOFを適切に処理し、部分的な読み込みが成功した場合はエラーと見なさないようにするためのものです。

技術的詳細

このコミットで行われた技術的な変更は、debug/plan9objパッケージの内部構造と外部APIの両方に影響を与えます。

  1. Prog構造体の非公開化:

    • 変更前はProg構造体がエクスポートされていましたが、変更後は非公開(小文字始まりのprog)になりました。
    • ProgはPlan 9 a.outのプログラムヘッダを表す構造体でしたが、この情報はFileHeaderに統合されるため、外部から直接アクセスする必要がなくなりました。
    • これにより、APIの表面積が減少し、パッケージの利用者はより高レベルの抽象化(FileHeader)を通じて情報にアクセスするようになります。これは、カプセル化の原則に沿った改善です。
  2. ProgHeaderおよびExecTable構造体の削除:

    • ProgHeaderProg構造体と同様にプログラムヘッダ情報を保持していましたが、その役割はFileHeaderに引き継がれました。
    • ExecTableは、様々なアーキテクチャのマジックナンバーとポインタサイズ、ヘッダサイズをマッピングする内部テーブルでした。このテーブルは、マジックナンバーが直接定数として定義されるようになったため、不要になりました。
    • これらの構造体を削除することで、コードの冗長性が排除され、パッケージの内部実装が簡素化されました。
  3. FileHeader構造体へのMagic, Bss, Entryフィールドの追加:

    • 変更前はFileHeaderPtrsz(ポインタサイズ)のみを持っていましたが、変更後にはMagic(マジックナンバー)、Bss(BSSセグメントのサイズ)、Entry(エントリポイントアドレス)が追加されました。
    • これにより、FileHeaderはPlan 9 a.outファイルのヘッダから直接読み取られる主要な情報をより包括的に保持するようになりました。これは、a.outファイルのヘッダ構造をより正確に反映し、利用者がヘッダ情報にアクセスしやすくなります。
  4. ?_MAGIC変数の定数への置き換え:

    • 変更前は、_A_MAGIC, _I_MAGICなどのマジックナンバーがmagic関数を使って動的に生成されていました。
    • 変更後には、Magic386, MagicAMD64, MagicARMといった具体的な定数として定義されました。
    • 定数を使用することで、コードの可読性が向上し、マジックナンバーの意味が明確になります。また、コンパイル時に値が確定するため、実行時のオーバーヘッドがなくなります。
  5. ReadAtからの最終的なEOFの無視:

    • Section.Data()メソッドにおいて、s.sr.ReadAt(dat, 0)の呼び出し後、n == len(dat)の場合にerr = nilと設定する行が追加されました。
    • これは、io.ReaderAtの実装によっては、要求されたバイト数すべてを読み込んだ場合でも、ファイルの終端に達しているとio.EOFを返すことがあるためです。この変更により、期待されるすべてのデータが読み込まれた場合は、io.EOFをエラーとして扱わないようにし、正常な読み込みとして処理します。これにより、Section.Data()の挙動がより堅牢になります。
  6. ドキュメントの改善:

    • コードコメントが修正され、より正確で分かりやすい説明が追加されました。例えば、FileHeaderSectionのコメントが修正されています。
    • formatError構造体が非公開化され、その役割がコメントで明確に説明されました。これは、エラー型が内部的なものであることを示し、APIの利用者が不必要に内部エラー型に依存しないように促します。

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

このコミットは主に以下の3つのファイルに影響を与えています。

  1. src/pkg/debug/plan9obj/file.go:

    • FileHeader構造体にMagic, Bss, Entryフィールドが追加され、PtrszPtrSizeにリネームされました。
    • ProgHeader構造体とProg構造体が削除されました。
    • Section.Data()メソッドでReadAt後のEOF処理が追加されました。
    • FormatErrorが非公開のformatErrorにリネームされました。
    • parseMagic関数のシグネチャが変更され、ExecTableへの依存がなくなりました。
    • NewFile関数内でFileHeaderの初期化ロジックが大幅に変更され、削除された構造体の代わりに新しいFileHeaderフィールドが使用されるようになりました。
    • walksymtab関数やnewTable関数で、f.Ptrszf.PtrSizeに更新されました。
  2. src/pkg/debug/plan9obj/file_test.go:

    • fileTests内のFileHeaderの初期化が、新しいFileHeader構造体のフィールドに合わせて更新されました。これにより、テストが新しいAPIと整合するように修正されました。
  3. src/pkg/debug/plan9obj/plan9obj.go:

    • bytesおよびencoding/binaryのインポートが削除されました。これは、magic関数やExecTableが不要になったためです。
    • hsize定数と_HDR_MAGIC定数が削除されました。
    • magic関数が削除されました。
    • _A_MAGICなどのマジックナンバー変数が削除され、代わりにMagic386, MagicAMD64, MagicARMといった定数が直接定義されました。
    • ExecTable構造体とexectab変数が削除されました。

コアとなるコードの解説

このコミットのコアとなる変更は、debug/plan9objパッケージのデータ構造とそれらを扱うロジックの根本的な再設計にあります。

  • FileHeaderの強化: 以前はPtrszしか持たなかったFileHeaderが、MagicBssEntryといったa.outヘッダの主要な情報を直接保持するようになりました。これにより、a.outファイルの基本的な属性にアクセスするために複数の構造体を横断する必要がなくなり、APIがより直感的になりました。
  • 内部構造の隠蔽と簡素化: ProgProgHeaderExecTableといった構造体が削除または非公開化されたことで、パッケージの内部実装が外部に漏れることがなくなり、APIの利用者はよりクリーンなインターフェースに集中できるようになりました。これは、GoのAPI設計におけるカプセル化の原則を強く反映しています。
  • マジックナンバーの定数化: マジックナンバーを動的に生成するのではなく、明確な定数として定義することで、コードの可読性と保守性が向上しました。これにより、異なるアーキテクチャのマジックナンバーが何を表しているのかが一目でわかるようになります。
  • ReadAtの堅牢性向上: Section.Data()におけるEOF処理の改善は、io.ReaderAtの特定の挙動に対する防御的なプログラミングであり、ファイルの終端での部分的な読み込みがエラーとして誤って報告されるのを防ぎます。これにより、パッケージの信頼性が向上します。

全体として、このコミットはdebug/plan9objパッケージをよりGoらしいAPI設計に近づけ、内部の複雑さを減らし、外部から利用する際のシンプルさと堅牢性を高めることを目的としています。

関連リンク

参考にした情報源リンク