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

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

このコミットは、Go言語の debug/elf パッケージにおけるGNUバージョンシンボルの処理に関するバグ修正です。具体的には、以前のリビジョンで行われた変更が原因で発生した問題を修正し、ELFファイル内のGNUバージョンシンボルが正しく解析されるようにします。

コミット

commit 5060dded0f9e4994b168123eeebb03f85e6deb20
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Fri Mar 22 01:27:55 2013 +0100

    debug/elf: repair GNU version symbols.
    
    After the revert of revision 9ea9e7e6e0c8
    the related revision 76ff7da868c6 must be reverted too.
    
    Fixes #5102.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/7961044

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

https://github.com/golang/go/commit/5060dded0f9e4994b168123eeebb03f85e6deb20

元コミット内容

このコミットは、debug/elf パッケージにおけるGNUバージョンシンボルの処理を修正するものです。以前のリビジョン 9ea9e7e6e0c8 のリバート後に、関連するリビジョン 76ff7da868c6 もリバートする必要があるという判断に基づいています。これにより、Issue #5102 で報告された問題が修正されます。

変更の背景

このコミットの背景には、Go言語の debug/elf パッケージがELF(Executable and Linkable Format)ファイルを解析する際に、GNUバージョンシンボルの処理に誤りがあったことが挙げられます。ELFファイルはUnix系システムで実行可能ファイル、共有ライブラリ、オブジェクトファイルなどに用いられる標準的なバイナリフォーマットです。共有ライブラリが複数のバージョンを持つシンボルを管理するために、GNUバージョンシンボルという仕組みが利用されます。

以前のリビジョン 9ea9e7e6e0c8 で導入された変更が、このGNUバージョンシンボルの処理に影響を与え、問題を引き起こしたと考えられます。そのリビジョンがリバートされた後も、関連する 76ff7da868c6 の変更が残っていたため、不整合が生じていました。このコミットは、その不整合を解消し、GNUバージョンシンボルの解析を正常に戻すことを目的としています。Issue #5102 は、この問題がユーザーに影響を与えていたことを示唆しています。

前提知識の解説

ELF (Executable and Linkable Format)

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

シンボルとシンボルテーブル

プログラムにおいて、関数名やグローバル変数名などは「シンボル」として扱われます。ELFファイルには、これらのシンボルとそのアドレスなどの情報を格納する「シンボルテーブル」が含まれています。動的リンクされる共有ライブラリの場合、.dynsym セクションに動的に解決されるシンボルが格納されます。

GNUバージョンシンボル

共有ライブラリは、時間の経過とともに進化し、新しい機能が追加されたり、既存の機能が変更されたりすることがあります。この際、古いアプリケーションが新しいライブラリと互換性を保ちつつ、新しいアプリケーションが最新の機能を利用できるようにするために、「シンボルバージョン管理」の仕組みが導入されています。GNUシステムでは、この目的のためにGNUバージョンシンボルが使用されます。

GNUバージョンシンボルは、特定のシンボルがどのライブラリバージョンで導入されたか、または変更されたかを示すメタデータを提供します。これにより、複数のバージョンの同じ関数が単一の共有ライブラリ内に共存できるようになり、後方互換性が維持されます。このバージョン情報は、ELFファイルの特定のセクション(例: .gnu.version, .gnu.version_d, .gnu.version_r)に埋め込まれます。

debug/elf パッケージ (Go言語)

Go言語の標準ライブラリに含まれる debug/elf パッケージは、ELFファイルを読み込み、解析するための機能を提供します。このパッケージを使用することで、GoプログラムはELFファイルのヘッダ、セクション、シンボルテーブルなどの構造にアクセスし、そこから情報を抽出することができます。例えば、インポートされたライブラリや動的シンボルなどの情報を取得する際に利用されます。ただし、このパッケージは悪意のある入力に対して堅牢に設計されているわけではないため、信頼できないELFファイルを解析する際には注意が必要です。

技術的詳細

このコミットは、src/pkg/debug/elf/file.go ファイル内の gnuVersion メソッドのロジックを修正しています。このメソッドは、ELFファイル内のGNUバージョンシンボルを処理する際に使用されます。

GNUバージョンシンボルは、通常、シンボルテーブルのエントリと関連付けられています。gnuVersym 配列は、各シンボルに対応するバージョン情報を格納しており、各エントリは2バイトで構成されます。

元のコードでは、シンボルテーブルのインデックス i を直接2倍して gnuVersym 配列のオフセットを計算していました (i = i * 2)。しかし、これはGNUバージョンシンボルの構造と一致していなかった可能性があります。

修正後のコードでは、インデックス i に1を加算してから2倍しています (i = (i + 1) * 2)。この変更は、gnuVersym 配列内のバージョン情報が、シンボルテーブルのインデックスに対してオフセットされていることを示唆しています。具体的には、シンボルテーブルの最初のシンボル(インデックス0)に対応するバージョン情報が、gnuVersym 配列のオフセット2(つまり (0 + 1) * 2)から始まることを意味している可能性があります。

このオフセットの調整により、debug/elf パッケージがELFファイルからGNUバージョンシンボルを正しく読み取り、解析できるようになります。これにより、以前のリビジョンで導入された不正確なオフセット計算が修正され、ELFファイルのシンボル解決における正確性が向上します。

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

変更は src/pkg/debug/elf/file.go ファイルの gnuVersion 関数内の一行です。

--- a/src/pkg/debug/elf/file.go
+++ b/src/pkg/debug/elf/file.go
@@ -718,7 +718,7 @@ func (f *File) gnuVersionInit(str []byte) {
 // which came from offset i of the symbol table.
 func (f *File) gnuVersion(i int, sym *ImportedSymbol) {
 	// Each entry is two bytes.
-	i = i * 2
+	i = (i + 1) * 2
 	if i >= len(f.gnuVersym) {
 		return
 	}

コアとなるコードの解説

gnuVersion 関数は、i で指定されたシンボルテーブルのオフセットに対応するGNUバージョンシンボルを取得しようとします。

  • // Each entry is two bytes. というコメントが示すように、gnuVersym 配列の各エントリは2バイトのサイズを持っています。
  • 変更前の i = i * 2 は、シンボルテーブルのインデックス i をそのまま2倍して、gnuVersym 配列内の対応する位置を計算していました。これは、gnuVersym のインデックスがシンボルテーブルのインデックスと直接対応しているという仮定に基づいています。
  • 変更後の i = (i + 1) * 2 は、シンボルテーブルのインデックス i に1を加算してから2倍しています。この +1 のオフセットは、gnuVersym 配列の構造が、シンボルテーブルのインデックスに対して1つずれていることを示唆しています。例えば、シンボルテーブルの0番目のエントリに対応するバージョン情報が、gnuVersym 配列の2番目のバイトから始まる場合(インデックス1 * 2 = 2)、この修正が適切になります。これは、ELFのバージョンシンボル情報が、シンボルテーブルの最初のいくつかのエントリ(例えば、STN_UNDEFSTN_LOCAL などの特殊なシンボル)をスキップして始まる場合に発生する可能性があります。

この修正により、gnuVersym 配列から正しいバージョン情報が取得されるようになり、ELFファイルの解析におけるGNUバージョンシンボルの処理が正確になります。

関連リンク

参考にした情報源リンク

  • ELF (Executable and Linkable Format) の概要に関する情報源
  • GNUバージョンシンボルに関する技術解説記事
  • Go言語の debug/elf パッケージの公式ドキュメントおよびソースコード
  • GitHubのGo言語リポジトリのコミット履歴 (ただし、特定のコミット 9ea9e7e6e0c8 および 76ff7da868c6 の詳細は直接特定できませんでした)
  • Issue #5102 に関する情報 (Go言語の公式リポジトリでは直接見つかりませんでしたが、コミットメッセージに記載されているため、過去に存在した問題であると推測されます)