[インデックス 14799] ファイルの概要
このコミットは、Go言語のリンカ(cmd/ld
)におけるシンボルテーブル(symtab)とELF(Executable and Linkable Format)生成コードのポータブル化に関するものです。複数のアーキテクチャ(ARM, AMD64, 386)で重複していたELF生成ロジックとシンボルテーブル生成ロジックを共通のコードベースに集約し、リンカの保守性と将来的な拡張性を向上させています。
コミット
commit d37c572ad54921319653a4abdd749221f7779bb5
Author: Russ Cox <rsc@golang.org>
Date: Sun Jan 6 14:32:45 2013 -0500
cmd/ld: move symtab, ELF generation to portable code
More cleanup in preparation for fixing issue 4069.
This CL replaces the three nearly identical copies of the
asmb ELF code with a single asmbelf function in elf.c.
In addition to the ELF code movement, remove the elfstr
array in favor of a simpler lookup, and identify sections by
name throughout instead of computing fragile indices.
The CL also replaces the three nearly identical copies of the
genasmsym code with a single genasmsym function in lib.c.
The ARM linker still compiles and generates binaries,
but I haven't tested the binaries. They may not work.
R=ken2
CC=golang-dev
https://golang.org/cl/7062047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d37c572ad54921319653a4abdd749221f7779bb5
元コミット内容
cmd/ld: move symtab, ELF generation to portable code
この変更は、リンカのシンボルテーブルとELF生成コードを、よりポータブルな(特定のアーキテクチャに依存しない)形に移行するものです。これは、Go言語のリンカのコードベースを整理し、将来的なバグ修正や機能追加を容易にするための準備作業の一環として行われました。特に、issue 4069の修正に向けたクリーンアップが目的とされています。
具体的には、以下の主要な変更が含まれます。
- 3つの異なるアーキテクチャ(ARM, AMD64, 386)のリンカに存在していた、ほぼ同一のELFアセンブルコードを、
elf.c
内の単一のasmbelf
関数に置き換えました。 - ELFコードの移動に加えて、
elfstr
配列を廃止し、よりシンプルなルックアップメカニズムを採用しました。これにより、セクションを脆弱なインデックスではなく名前で識別するように変更されました。 - 同様に、3つの異なるアーキテクチャのリンカに存在していた、ほぼ同一の
genasmsym
(アセンブリシンボル生成)コードを、lib.c
内の単一のgenasmsym
関数に置き換えました。
この変更により、ARMリンカは引き続きバイナリをコンパイルおよび生成しますが、生成されたバイナリのテストは行われていないため、動作しない可能性があるという注意書きがあります。
変更の背景
このコミットの主な背景は、Go言語のリンカのコードベースにおける重複の解消と、将来的なメンテナンス性の向上です。特に、コミットメッセージに明記されている「fixing issue 4069」への準備作業として位置づけられています。
Go言語のリンカは、異なるCPUアーキテクチャ(例: ARM、AMD64、386)向けに実行可能ファイルを生成する必要があります。歴史的に、これらのアーキテクチャ間で共通する処理であっても、それぞれのアーキテクチャ固有のリンカコード内に類似またはほぼ同一のコードが複製されている箇所がありました。これは、コードの重複を招き、バグ修正や機能追加の際に複数の場所で同じ変更を行う必要が生じるため、開発効率と保守性を低下させます。
ELF(Executable and Linkable Format)ファイルの生成や、シンボルテーブルの管理は、多くのアーキテクチャで共通の概念に基づいています。しかし、実装の詳細がアーキテクチャごとにわずかに異なるため、それぞれのリンカで個別に実装されていました。このコミットは、これらの共通部分を抽出し、一元化されたポータブルなコードとしてsrc/cmd/ld/elf.c
とsrc/cmd/ld/lib.c
に移動することで、コードベースの健全性を高めることを目指しています。
「issue 4069」は、Goのバグトラッカーにおける特定の課題を指します。このコミットがその修正の「準備」であることから、リンカの既存の構造がその問題の解決を妨げていたか、あるいは解決を非常に困難にしていたことが示唆されます。コードの重複を解消し、よりモジュール化された構造にすることで、将来的にその問題に対処しやすくなるという意図があります。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念について基本的な知識が必要です。
-
リンカ (Linker):
- リンカは、コンパイラによって生成されたオブジェクトファイル(機械語コードとデータを含む)を結合し、実行可能なプログラムやライブラリを作成するソフトウェアツールです。
- 複数のオブジェクトファイルやライブラリから必要なコードやデータを集め、それらの間の参照(例: 関数呼び出し、変数アクセス)を解決し、最終的な実行可能ファイルのメモリレイアウトを決定します。
- 静的リンカと動的リンカ(ランタイムリンカ)があります。Goのリンカは主に静的リンキングを行います。
-
ELF (Executable and Linkable Format):
- ELFは、Unix系システム(Linux、FreeBSD、NetBSD、OpenBSDなど)で広く使用されている、実行可能ファイル、オブジェクトファイル、共有ライブラリ、およびコアダンプファイルの標準的なファイル形式です。
- ELFファイルは、ヘッダ、プログラムヘッダテーブル(実行時情報)、セクションヘッダテーブル(リンク時情報)、および様々なセクション(コード、データ、シンボルテーブルなど)で構成されます。
- セクション (Section): ELFファイル内の論理的なブロックで、コード(
.text
)、初期化済みデータ(.data
)、初期化されていないデータ(.bss
)、読み取り専用データ(.rodata
)、シンボルテーブル(.symtab
)、文字列テーブル(.strtab
)など、様々な種類の情報を含みます。 - シンボルテーブル (Symbol Table): プログラム内の関数名、変数名などのシンボルとそのアドレス、サイズ、型などの情報が格納されているテーブルです。リンカはこれを利用して、異なるオブジェクトファイル間の参照を解決します。
- 動的リンキング (Dynamic Linking): 実行時に共有ライブラリをロードし、リンクする仕組みです。これにより、ディスクスペースの節約やメモリの共有が可能になります。動的リンキングには、
.interp
(インタプリタ)、.dynsym
(動的シンボルテーブル)、.dynstr
(動的文字列テーブル)、.rel
/.rela
(再配置情報)、.got
(グローバルオフセットテーブル)、.plt
(プロシージャリンケージテーブル)などのセクションが関与します。
-
Go言語のリンカ (
cmd/ld
):- Go言語のツールチェインの一部であり、Goプログラムをビルドする際に使用されます。
- クロスコンパイルをサポートしており、異なるアーキテクチャ向けのバイナリを生成できます。
- このコミットが対象としているのは、Goのリンカの内部実装、特にELFファイルの生成とシンボルテーブルの管理方法です。
-
elfstr
配列とセクションの識別:- ELFファイルを生成する際、リンカは様々なセクション(例:
.text
,.data
,.symtab
)を扱います。これらのセクションは、ファイル内で特定のインデックスを持つか、名前で識別されます。 elfstr
配列は、おそらくセクション名とそれに対応する内部的なインデックスやオフセットを管理するために使用されていたデータ構造です。このコミットでは、インデックスに依存する「脆弱な」方法から、セクション名を直接使用する、より堅牢な方法への移行が行われています。
- ELFファイルを生成する際、リンカは様々なセクション(例:
技術的詳細
このコミットの技術的な核心は、Goリンカのアーキテクチャ固有のELF生成およびシンボルテーブル生成ロジックを、共通のポータブルなコードに集約することです。これにより、コードの重複が大幅に削減され、リンカの保守性と拡張性が向上します。
-
ELF生成コードの共通化 (
asmbelf
関数):- 以前は、
src/cmd/5l/asm.c
(ARM),src/cmd/6l/asm.c
(AMD64),src/cmd/8l/asm.c
(386) の各ファイルに、ELFバイナリをアセンブルするための類似したコードブロック(おそらくdoelf
関数やasmb
関数の一部)が存在していました。 - このコミットでは、これらの重複するロジックが
src/cmd/ld/elf.c
内の単一のasmbelf
関数に抽出されました。これにより、各アーキテクチャのasm.c
ファイルからは、asmbelf
を呼び出すだけのシンプルなコードに置き換えられます。 asmbelf
関数は、ELFヘッダ、プログラムヘッダ、セクションヘッダなどの生成、および動的リンキングに必要なセクション(.interp
,.dynsym
,.dynstr
,.rel
/.rela
,.got
,.plt
など)のセットアップを担当します。
- 以前は、
-
シンボルテーブル生成コードの共通化 (
genasmsym
関数):- 同様に、各アーキテクチャの
asm.c
ファイルには、アセンブリシンボルを生成するためのgenasmsym
関数が重複して存在していました。 - このロジックは
src/cmd/ld/lib.c
内の単一のgenasmsym
関数に統合されました。この関数は、プログラム内の様々なシンボル(テキスト、データ、BSS、ファイルなど)を走査し、リンカの内部シンボルテーブルに登録する役割を担います。
- 同様に、各アーキテクチャの
-
elfstr
配列の廃止とセクション名の直接利用:- 以前のリンカでは、ELFセクションの名前(例: ".text", ".data")を管理するために
elfstr
という配列が使用されていたようです。この配列は、セクション名に対応するインデックスを保持し、セクションを識別するためにそのインデックスが使われていました。 - しかし、インデックスはセクションの追加や削除によって容易に変化するため、「脆弱なインデックス」と表現されています。このコミットでは、
elfstr
配列を廃止し、セクションをその名前で直接参照する、より堅牢なメカニズムに移行しました。これは、lookup
関数などを用いてシンボルテーブルからセクション名を直接検索する形になったことを意味します。これにより、コードの可読性と堅牢性が向上します。
- 以前のリンカでは、ELFセクションの名前(例: ".text", ".data")を管理するために
-
Sym
構造体の変更:src/cmd/5l/l.h
,src/cmd/6l/l.h
,src/cmd/8l/l.h
のヘッダファイルでは、Sym
構造体にelfsym
とsect
という新しいフィールドが追加されています。elfsym
: ELFシンボルに関連する情報(おそらくELFシンボルテーブル内のインデックスなど)を格納するために使用されます。sect
: シンボルが属するセクションへのポインタを格納します。これにより、シンボルとセクションの関連付けがより明確になり、セクション名によるルックアップが容易になります。
-
span.c
の変更:src/cmd/5l/span.c
では、.text
セクションの追加と、各テキストシンボル(関数など)がそのセクションに属することを明示的に設定する変更が行われています。これは、シンボルとセクションの関連付けを強化し、共通化されたELF生成コードが正確な情報を利用できるようにするためです。
これらの変更は、Goリンカの内部構造を大幅に簡素化し、将来的な開発においてアーキテクチャ間の差異を吸収しやすくすることを目的としています。特に、ELFファイルの複雑な構造を扱うコードが共通化されたことで、新しいアーキテクチャのサポートや既存のアーキテクチャのELF関連のバグ修正が容易になります。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に以下のファイルに集中しています。
-
src/cmd/5l/asm.c
,src/cmd/6l/asm.c
,src/cmd/8l/asm.c
:- これらのファイルから、重複していたELF生成ロジック(
doelf
関数やasmb
関数内のELF関連コードブロック)とgenasmsym
関数の実装が削除されました。 - 代わりに、
asmb
関数内でiself
フラグが設定されている場合に、asmbelfsetup()
関数(elf.c
に移動)とasmbelf(symo)
関数(elf.c
に移動)が呼び出されるようになりました。 elfstr
配列の定義と使用が削除されました。
- これらのファイルから、重複していたELF生成ロジック(
-
src/cmd/ld/elf.c
:- このファイルに、各アーキテクチャから移動された共通のELF生成ロジックが
asmbelf
関数として追加されました。 doelf
関数内の大部分のロジック(ELFヘッダ、プログラムヘッダ、セクションヘッダの生成、動的リンキング関連セクションのセットアップなど)がここに集約されています。elfstr
配列の代わりに、セクション名を直接使用する新しいルックアップメカニズムが実装されました。shsym
やphsh
といったヘルパー関数も、このファイルに移動または共通化されました。
- このファイルに、各アーキテクチャから移動された共通のELF生成ロジックが
-
src/cmd/ld/lib.c
:- このファイルに、各アーキテクチャから移動された共通のシンボルテーブル生成ロジックが
genasmsym
関数として追加されました。 genasmsym
関数は、プログラム内のすべてのシンボルを走査し、その種類(テキスト、データ、BSSなど)に応じて適切なシンボル情報をリンカの内部構造に登録します。
- このファイルに、各アーキテクチャから移動された共通のシンボルテーブル生成ロジックが
-
src/cmd/5l/l.h
,src/cmd/6l/l.h
,src/cmd/8l/l.h
:struct Sym
定義に、int32 elfsym;
とstruct Section* sect;
の2つの新しいフィールドが追加されました。これらは、シンボルとELFセクションの関連付けを強化するために使用されます。
-
src/cmd/5l/span.c
:span
関数内で、.text
セクションが明示的に追加され、各テキストシンボル(関数)がこのセクションに割り当てられるようになりました。
コアとなるコードの解説
このコミットの主要な変更は、リンカのアーキテクチャ固有のコードから共通部分を抽出し、src/cmd/ld/elf.c
とsrc/cmd/ld/lib.c
に集約した点です。
src/cmd/ld/elf.c
におけるasmbelf
関数(旧doelf
関数の一部)の役割:
以前は、各アーキテクチャのasm.c
ファイル内に、ELFファイルの構造(ヘッダ、プログラムヘッダ、セクションヘッダなど)を構築し、動的リンキングに必要なセクション(.interp
, .dynsym
, .dynstr
, .rel
, .plt
, .got
など)をセットアップするロジックが重複して存在していました。
新しいasmbelf
関数は、これらの共通ロジックをカプセル化します。
- ELFヘッダ (
ElfEhdr
) の初期化: ELFファイルの基本的な情報(マジックナンバー、クラス、データエンコーディング、バージョン、OS ABI、タイプ、マシンアーキテクチャ、エントリポイントなど)を設定します。 - プログラムヘッダ (
ElfPhdr
) の生成: 実行可能ファイルのメモリレイアウトを記述します。これには、コードセグメント、データセグメント、動的リンキング情報、スタック情報などが含まれます。特に、PT_PHDR
(プログラムヘッダ自体)、PT_INTERP
(動的リンカのパス)、PT_LOAD
(ロード可能なセグメント)、PT_DYNAMIC
(動的リンキング情報)、PT_TLS
(スレッドローカルストレージ)、PT_GNU_STACK
(スタックの実行保護)などが設定されます。 - セクションヘッダ (
ElfShdr
) の生成: リンク時に使用されるファイル内の論理的なブロック(.text
,.data
,.bss
,.symtab
,.strtab
, 動的リンキング関連セクションなど)を記述します。- 重要な変更点として、セクションを識別するために
elfstr
配列のインデックスではなく、セクション名を直接使用するようになりました。これは、lookup(".shstrtab", 0)
でセクション名文字列テーブルを取得し、addstring
でセクション名を追加することで実現されます。これにより、セクションの追加や削除によるインデックスのずれを心配する必要がなくなります。
- 重要な変更点として、セクションを識別するために
- 動的リンキング関連セクションのセットアップ:
.dynsym
(動的シンボルテーブル),.dynstr
(動的文字列テーブル),.rel
/.rela
(再配置エントリ),.got
(グローバルオフセットテーブル),.plt
(プロシージャリンケージテーブル),.hash
(ハッシュテーブル),.gnu.version
,.gnu.version_r
などのセクションが、それぞれの役割に応じて適切に初期化され、リンカの内部シンボルに紐付けられます。
src/cmd/ld/lib.c
におけるgenasmsym
関数の役割:
この関数は、リンカが最終的なバイナリに含めるシンボル情報を収集し、シンボルテーブルを構築する役割を担います。
- 以前は各アーキテクチャの
asm.c
に存在していましたが、シンボル収集のロジックはアーキテクチャに依存しないため、lib.c
に移動されました。 genasmsym
は、etext
(テキストセグメントの終わり)シンボルから始まり、リンカが認識しているすべてのシンボル(hash
テーブルやallsym
リストを通じてアクセス可能)を走査します。- 各シンボルについて、その型(テキスト、データ、BSS、定数、ファイルなど)を判別し、対応するシンボル情報(名前、型、アドレス、サイズ、バージョン、Go型情報など)をリンカの内部シンボルテーブルに登録します。
- 特に、関数の自動変数やパラメータに関する情報も収集し、デバッグ情報やスタックフレームの解析に利用できるようにします。
これらの共通化された関数は、各アーキテクチャのリンカ(5l
, 6l
, 8l
)から呼び出されることで、コードの重複を排除し、リンカ全体の構造をよりクリーンで保守しやすいものにしています。
関連リンク
- Go Issue 4069: https://github.com/golang/go/issues/4069 (このコミットが準備作業として言及しているissue)
- Go CL 7062047: https://golang.org/cl/7062047 (このコミットに対応するGerrit Change-List)
参考にした情報源リンク
- ELF (Executable and Linkable Format) - Wikipedia: https://ja.wikipedia.org/wiki/Executable_and_Linkable_Format
- リンカ (Linker) - Wikipedia: https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%B3%E3%82%AB
- Go言語のリンカの仕組み (Go linker internals) に関する一般的な情報源 (例: Goの公式ドキュメント、ブログ記事など)
- Goのソースコード: https://github.com/golang/go
- Goのリンカに関するブログ記事や解説 (一般的な情報源として、特定の記事を指すものではありませんが、Goのリンカの動作を理解する上で役立ちます)
(注: 上記の参考情報源リンクは、一般的な知識の提供を目的としており、このコミットの解析に直接使用された特定の記事やドキュメントを指すものではありません。)