[インデックス 13139] ファイルの概要
このコミットは、Go言語のリンカ (cmd/ld
) における ldelf.c
ファイルの変更に関するものです。具体的には、Linux/ARM環境でのビルド問題を修正するために、ldelf()
関数内にシンボルチェックのガードを追加しています。
コミット
commit e0b0f62d96b37dd78811159d6ddd9819f374d603
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed May 23 11:36:24 2012 +0800
cmd/ld: fix Linux/ARM build
CL 5823055 removed a line introduced in Linux/ARM cgo support.
Because readsym() now returns nil for "$a", "$d" mapping symbols,
no matter the settings of `needSym', we still have to guard against
them in ldelf().
R=golang-dev, dave, rsc
CC=golang-dev
https://golang.org/cl/6220073
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e0b0f62d96b37dd78811159d6ddd9819f374d603
元コミット内容
このコミットは、Go言語のリンカ (cmd/ld
) におけるLinux/ARM環境でのビルド問題を修正するものです。以前の変更である CL 5823055
によって、Linux/ARMのcgoサポートで導入された特定の行が削除されました。その結果、readsym()
関数が "$a" や "$d" といったマッピングシンボルに対して nil
を返すようになり、needSym
の設定に関わらず、ldelf()
関数内でこれらのシンボルに対するガードが必要になったため、そのガードを追加する変更です。
変更の背景
この変更の背景には、Go言語のリンカがELF(Executable and Linkable Format)形式のバイナリを処理する際の特定の挙動と、ARMアーキテクチャにおけるシンボルの扱いの複雑さがあります。
Go言語のリンカ (cmd/ld
) は、Goプログラムをコンパイルして実行可能なバイナリを生成する際に重要な役割を担います。特に、C言語のコードとGo言語のコードを連携させるcgoを使用する場合、リンカは両方の言語のシンボルを適切に解決し、リンクする必要があります。
問題の発端は、以前の変更である CL 5823055
にあります。この変更は「cmd/ld, cmd/6l, cmd/8l: fix hidden/local symbol import for ELF systems」と題されており、ELFシステムにおける隠しシンボルやローカルシンボルのインポートに関する問題を修正することを目的としていました。この CL 5823055
の導入により、Linux/ARM cgoサポートで以前導入されていた特定のコード行が削除されました。
この行の削除が、readsym()
関数の挙動に影響を与えました。readsym()
はシンボルテーブルからシンボルを読み取る関数ですが、CL 5823055
以降、ARMアーキテクチャ特有の "$a" や "$d" といったマッピングシンボルに対して nil
を返すようになりました。これらのシンボルは、ARMのThumbモードとARMモード間の切り替えや、データとコードの区別など、特定のアーキテクチャ的特性を扱うために使用されることがあります。
readsym()
が nil
を返すようになったにもかかわらず、ldelf()
関数(ELFファイルをロードして解析するリンカの主要な部分)では、これらのシンボルに対する適切なチェックが不足していました。その結果、リンカが予期せぬ nil
シンボルを処理しようとして、Linux/ARM環境でのビルドが失敗する問題が発生しました。
このコミットは、このビルド問題を解決するために、ldelf()
関数内に明示的なガード(チェック)を追加し、readsym()
が nil
を返す可能性のある "$a" や "$d" マッピングシンボルを適切にスキップするようにすることで、リンカの堅牢性を向上させています。
前提知識の解説
このコミットを理解するためには、以下の前提知識が役立ちます。
- Go言語のリンカ (
cmd/ld
): Go言語のコンパイラツールチェーンの一部であり、Goのソースコードから実行可能なバイナリを生成する際に、オブジェクトファイルやライブラリを結合(リンク)する役割を担います。cmd/ld
は、Goプログラムの実行に必要なすべてのコードとデータを一つのファイルにまとめ上げます。 - ELF (Executable and Linkable Format): Unix系システム(Linuxを含む)で広く使用されている、実行可能ファイル、オブジェクトコード、共有ライブラリ、コアダンプの標準ファイル形式です。ELFファイルは、ヘッダ、プログラムヘッダテーブル、セクションヘッダテーブル、そして実際のデータ(コード、データ、シンボルテーブルなど)で構成されます。
- ARMアーキテクチャ: スマートフォン、タブレット、組み込みシステムなどで広く利用されているRISC(Reduced Instruction Set Computer)ベースのプロセッサアーキテクチャです。ARMプロセッサは、その電力効率の高さから、モバイルデバイスで特に普及しています。
- cgo: Go言語の機能の一つで、C言語のコードをGoプログラムから呼び出したり、GoのコードをC言語から呼び出したりすることを可能にします。cgoを使用すると、既存のCライブラリをGoプロジェクトで再利用したり、パフォーマンスが重要な部分をCで記述したりすることができます。cgoを使用する際には、GoとCのリンケージ規約の違いをリンカが適切に処理する必要があります。
- シンボル: プログラム内の関数、変数、ラベルなどの名前付きエンティティを指します。リンカはシンボルを使用して、異なるオブジェクトファイル間で参照される関数や変数のアドレスを解決します。
- シンボルテーブル: ELFファイル内に含まれるデータ構造で、プログラム内のシンボルとそのアドレス、型、サイズなどの情報が格納されています。リンカはシンボルテーブルを参照して、シンボル解決を行います。
readsym()
: リンカの内部関数で、ELFファイルのシンボルテーブルからシンボル情報を読み取る役割を担います。この関数は、特定のシンボルを検索したり、シンボルテーブル全体を走査したりするために使用されます。ldelf()
: リンカの内部関数で、ELFファイルをロードし、その構造を解析する主要な部分です。シンボルテーブルの読み込み、セクションの処理、再配置エントリの適用など、ELFファイルのリンクに必要な多くの処理を行います。- "$a" および "$d" マッピングシンボル: ARMアーキテクチャ特有のシンボルで、主にコードとデータの区別、またはARM命令セットとThumb命令セット間の切り替えを示すために使用されます。
$a
シンボルは、続くコードがARM命令セットで記述されていることを示します。$d
シンボルは、続くデータがデータセクションであることを示します。 これらは、リンカがコードとデータを正しく配置し、プロセッサが適切なモードで実行できるようにするために重要です。
技術的詳細
このコミットの技術的詳細は、GoリンカのELF処理におけるシンボル解決のロジックに深く関わっています。
CL 5823055
の変更により、readsym()
関数が特定の条件下で、特にARMアーキテクチャの "$a" や "$d" といったマッピングシンボルに対して nil
を返すようになりました。これは、readsym()
が「不要なシンボルをスキップする」という内部ロジックを持つためです。しかし、ldelf()
関数内では、readsym()
が nil
を返す可能性があるという前提が十分に考慮されていませんでした。
ldelf()
関数は、ELFファイルのシンボルテーブルを走査し、各シンボルを処理します。通常、readsym()
が有効なシンボル情報を返すことを期待して処理を進めます。しかし、readsym()
が nil
を返した場合、ldelf()
はその nil
シンボルを処理しようとし、これが問題を引き起こしました。
このコミットは、ldelf()
関数内のシンボル処理ループに、以下のガードを追加することでこの問題を解決しています。
// even when we pass needSym == 1 to readsym, it might still return nil to skip some unwanted symbols
if(sym.sym == S)
continue;
ここで、sym.sym == S
は、読み取られたシンボルが nil
であるか、またはリンカがスキップすべき特定の種類のシンボルである場合に真となります。S
は、Goリンカの内部でシンボルが有効でないことを示すために使用される定数であると推測されます。この条件が真の場合、continue
ステートメントによって現在のシンボルの処理をスキップし、次のシンボルへとループを進めます。
この変更により、readsym()
が "$a" や "$d" のようなマッピングシンボルに対して nil
を返した場合でも、ldelf()
はそれらを安全に無視し、ビルドプロセスが中断されることなく続行できるようになります。これは、リンカがELFファイルを正しく解析し、ARM環境でのcgoを含むGoプログラムのビルドを成功させるために不可欠な修正です。
コアとなるコードの変更箇所
変更は src/cmd/ld/ldelf.c
ファイルにあります。
--- a/src/cmd/ld/ldelf.c
+++ b/src/cmd/ld/ldelf.c
@@ -575,6 +575,9 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
}
if(sym.shndx >= obj->nsect || sym.shndx == 0)
continue;
+ // even when we pass needSym == 1 to readsym, it might still return nil to skip some unwanted symbols
+ if(sym.sym == S)
+ continue;
sect = obj->sect+sym.shndx;
if(sect->sym == nil) {
diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type);
コアとなるコードの解説
追加されたコードは以下の3行です。
// even when we pass needSym == 1 to readsym, it might still return nil to skip some unwanted symbols
if(sym.sym == S)
continue;
このコードブロックは、ldelf()
関数内のシンボル処理ループの中に挿入されています。
// even when we pass needSym == 1 to readsym, it might still return nil to skip some unwanted symbols
これはコメントであり、このコードが追加された理由を説明しています。readsym()
関数にneedSym == 1
を渡した場合でも、readsym()
が一部の不要なシンボルをスキップするためにnil
を返す可能性があることを示しています。これは、readsym()
の内部ロジックが、特定のシンボル(この場合はARMの "$a", "$d" マッピングシンボル)を意図的に無視するように設計されていることを示唆しています。if(sym.sym == S)
これは条件文です。sym
は現在処理中のシンボルを表す構造体(またはそれに類するもの)であり、sym.sym
はそのシンボルの種類や状態を示すフィールドであると推測されます。S
は、Goリンカの内部で「スキップすべきシンボル」や「無効なシンボル」を示すために使用される定数であると考えられます。したがって、この条件は「もし現在のシンボルがスキップすべきシンボルであるならば」という意味になります。continue;
これはループ制御文です。if
文の条件が真(つまり、シンボルがスキップすべきものである)の場合、このcontinue
ステートメントが実行され、現在のループの残りの処理をスキップして、次のシンボルへと処理を移します。
この変更により、readsym()
が nil
を返すような特定のARMマッピングシンボルが ldelf()
によって安全に無視されるようになり、リンカがこれらのシンボルを不適切に処理しようとしてクラッシュするのを防ぎます。結果として、Linux/ARM環境でのGoプログラムのビルドが正常に完了するようになります。
関連リンク
- Go Gerrit Change-Id: https://golang.org/cl/6220073