[インデックス 18316] ファイルの概要
このコミットは、Go言語のツールチェインにおけるliblink
(リンカのコア部分)がARMアーキテクチャにおいてlinkmode
を使用しないように変更するものです。これにより、cgo
バイナリが独自のTLS(Thread Local Storage)変数を持つ場合に発生していたバグが修正されます。
コミット
commit dab127baf5a0bae92c289d6fa754f7a7c08745d3
Author: Russ Cox <rsc@golang.org>
Date: Tue Jan 21 19:46:34 2014 -0500
liblink: remove use of linkmode on ARM
Now that liblink is compiled into the compilers and assemblers,
it must not refer to the "linkmode", since that is not known until
link time. This CL makes the ARM support no longer use linkmode,
which fixes a bug with cgo binaries that contain their own TLS
variables.
The x86 code must also remove linkmode; that is issue 7164.
Fixes #6992.
R=golang-codereviews, iant
CC=golang-codereviews
https://golang.org/cl/55160043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dab127baf5a0bae92c289d6fa754f7a7c08745d3
元コミット内容
liblink: remove use of linkmode on ARM
liblink
がコンパイラやアセンブラにコンパイルされるようになったため、リンク時まで不明な"linkmode"
を参照してはならない。この変更は、ARMサポートがlinkmode
を使用しないようにすることで、独自のTLS変数を含むcgo
バイナリのバグを修正する。
x86コードもlinkmode
を削除する必要がある。これはissue 7164である。
Fixes #6992.
変更の背景
この変更の背景には、Goツールチェインの進化があります。以前は、liblink
は独立したライブラリとして存在し、リンカが最終的なバイナリを生成する際に使用されていました。しかし、Goのビルドプロセスが最適化され、liblink
の機能がコンパイラ(5c
, 6c
, 8c
など)やアセンブラ(5a
, 6a
, 8a
など)に直接組み込まれるようになりました。
この統合により、コンパイル時やアセンブル時にlinkmode
(リンクの種類、例えば静的リンクか動的リンクかなど)に関する情報が利用できなくなりました。linkmode
は最終的なリンク時に決定される情報であるため、コンパイラやアセンブラの段階でこれに依存するコードがあると問題が発生します。
特に、cgo
(C言語との相互運用機能)を使用してCのコードをGoプログラムに組み込む場合、Cのコードが独自のTLS(Thread Local Storage)変数を定義することがあります。ARMアーキテクチャでは、これらのTLS変数の扱いがlinkmode
に依存する形で実装されていました。liblink
がコンパイラに組み込まれたことで、この依存関係がビルドエラーや実行時エラーを引き起こすようになったため、linkmode
への依存を解消する必要が生じました。
このコミットは、ARMアーキテクチャにおけるこの問題を解決することを目的としています。同様の問題がx86アーキテクチャでも存在し、それはIssue 7164として追跡されています。
前提知識の解説
このコミットを理解するためには、以下の概念を把握しておく必要があります。
- Goツールチェイン: Go言語のプログラムをビルド、テスト、実行するためのツール群(コンパイラ、アセンブラ、リンカなど)。
liblink
: Goリンカのコア部分を構成するライブラリ。Go 1.2以降、このライブラリの機能がコンパイラやアセンブラに統合され、ビルドプロセスが効率化されました。linkmode
: リンクの種類を示すモード。静的リンク(すべてのライブラリが最終バイナリに組み込まれる)や動的リンク(共有ライブラリが実行時にロードされる)などがあります。この情報は、最終的なバイナリを生成するリンカの段階で決定されます。- TLS (Thread Local Storage): スレッドごとに独立したデータを保持するためのメカニズム。各スレッドは、TLS変数に対して独自のコピーを持ち、他のスレッドのTLS変数に影響を与えることなくアクセスできます。OSやアーキテクチャによってTLSの実装方法は異なります。
cgo
: GoプログラムからC言語のコードを呼び出すためのGoの機能。cgo
を使用すると、GoとCのコードを混在させることができます。CのコードがTLS変数を使用する場合、GoのランタイムとCのランタイムの間でTLSの管理に互換性が必要になることがあります。- ARMアーキテクチャ: モバイルデバイスや組み込みシステムで広く使用されているRISCベースのプロセッサアーキテクチャ。ARMプロセッサは、特定の命令セットやレジスタの使用方法、メモリ管理ユニット(MMU)の動作など、独自の特性を持っています。TLSのアクセス方法もアーキテクチャ固有の命令(例:
MRC
命令)を使用することがあります。 - リロケーション (Relocation): リンカがプログラム内のシンボル参照を、最終的なメモリ上のアドレスに解決するプロセス。コンパイル時には、関数や変数の正確なアドレスは不明なため、プレースホルダーが置かれ、リンカがそのプレースホルダーを実際のメモリアドレスに置き換えます。
R_ARM_TLS_LE32
: ARMアーキテクチャにおけるTLS関連のリロケーションタイプの一つ。TLS変数のオフセットを直接埋め込む方式(Local Executable)。R_ARM_TLS_IE32
: ARMアーキテクチャにおけるTLS関連のリロケーションタイプの一つ。GOT(Global Offset Table)を介してTLS変数にアクセスする方式(Initial Executable)。共有ライブラリで使用されることが多いです。- GOT (Global Offset Table): 動的リンクされたプログラムで使用されるデータ構造。共有ライブラリ内の関数や変数への参照を解決するために使用されます。GOTエントリは、実行時に実際のメモリアドレスに更新されます。
C_SCOND_NONE
: ARMアセンブリにおける条件コード(Condition Code)の一つ。命令が常に実行されることを意味します。以前はマジックナンバー14
が使用されていましたが、可読性と保守性の向上のために定数に置き換えられました。
技術的詳細
このコミットの核心は、Goのリンカ(liblink
)がARMアーキテクチャでTLS変数を扱う際に、linkmode
への依存を排除することです。
Goのビルドプロセスでは、liblink
がコンパイラとアセンブラに統合されたため、コンパイル時には最終的なリンクモード(静的リンクか動的リンクか)が不明になりました。しかし、ARMアーキテクチャにおけるTLS変数のアクセスコードは、このlinkmode
に依存していました。具体的には、runtime.tlsgm
シンボルへの参照が、静的リンクと動的リンクで異なるリロケーションタイプ(R_ARM_TLS_LE32
とR_ARM_TLS_IE32
)を必要としていました。
このコミットでは、liblink
内のARM関連のコード(src/liblink/asm5.c
とsrc/liblink/obj5.c
)が修正され、linkmode
のチェックを削除し、代わりにctxt->gmsym
(runtime.tlsgm
シンボルへのポインタ)が設定されているかどうかでTLS関連の処理を分岐するように変更されています。
また、src/cmd/ld/data.c
では、STLSBSS
(Thread Local Storage BSSセクション)シンボルへの参照が、無効なアドレス(1
)を持つように設定されています。これは、これらのシンボルが誤って使用された場合に、アラインメントエラーなどによって即座にクラッシュするようにするためです。特にARMでは、アラインメントされていないアドレスへのアクセスがフォルトを引き起こすため、1
という値が選ばれています。
src/pkg/runtime/asm_arm.s
では、runtime.save_gm
とruntime.load_gm
というTLS関連のランタイム関数にコメントが追加されています。これらの関数は、MRC
命令(Coprocessor Register Transfer)を使用してTLSレジスタから値をフェッチし、g
(goroutine構造体)とm
(machine構造体)レジスタのTLSコピーを更新します。追加されたコメントは、liblink
がこれらのMRC
命令の後にR0レジスタを調整する命令を追加し、8(R0)
と12(R0)
がTLSコピーを指すようにする「魔法のような」挙動について言及しています。このコミットは、この「魔法」をlinkmode
に依存しない形に修正しています。
具体的には、obj5.c
のaddstacksplit
関数内で、runtime.tlsgm
シンボルへのアクセスに関するコードが変更されています。以前はctxt->linkmode == LinkExternal
という条件で分岐していましたが、この条件が削除され、ctxt->gmsym
が設定されているかどうかで処理が継続されるようになっています。これにより、コンパイル時にlinkmode
が不明な場合でも、TLS関連の命令生成が正しく行われるようになります。
さらに、ARMアセンブリコードにおける条件コードのマジックナンバー14
が、より意味のある定数C_SCOND_NONE
に置き換えられています。これはコードの可読性と保守性を向上させるための変更です。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルとコードの変更点は以下の通りです。
-
src/cmd/ld/data.c
:dodata
関数内で、STLSBSS
タイプのシンボルに対して、s->value = 1
を設定するロジックが追加されました。これは、これらのシンボルが誤って使用された場合にフォルトを発生させるためです。address
関数内で、シンボルのvalue
にsect->vaddr
を加算する際に、sym->sect != nil
のチェックが追加されました。
-
src/liblink/asm5.c
:asmout
関数のシグネチャからLSym *gmsym
引数が削除され、ctxt->gmsym
が直接使用されるようになりました。span5
関数内で、gmsym
ローカル変数が削除され、ctxt->gmsym
が使用されるようになりました。- 条件コードのマジックナンバー
14
がC_SCOND_NONE
に置き換えられました。
-
src/liblink/obj5.c
:addstacksplit
関数内で、ctxt->linkmode == LinkExternal
のチェックが削除され、ctxt->gmsym
が設定されているかどうかでTLS関連の処理が継続されるようになりました。- TLS関連の命令生成ロジック(
AMOVW
,AADD
,ASUB
命令の生成)において、条件コードのマジックナンバー14
がC_SCOND_NONE
に置き換えられました。 ARET
命令の条件チェックで、マジックナンバー14
がC_SCOND_NONE
に置き換えられました。
-
src/pkg/runtime/asm_arm.s
:runtime.save_gm
とruntime.load_gm
関数に、liblink
がMRC
命令の後にR0レジスタを調整する「魔法のような」挙動に関するコメントが追加されました。
コアとなるコードの解説
このコミットの主要な変更は、liblink
がARMアーキテクチャのTLS変数を扱う際に、linkmode
というリンカ固有の情報に依存しないようにすることです。
以前のコードでは、runtime.tlsgm
シンボルへの参照を解決する際に、現在のlinkmode
がLinkExternal
(動的リンクに相当)であるかどうかをチェックしていました。このチェックに基づいて、TLS変数のオフセットを直接埋め込むか(R_ARM_TLS_LE32
)、またはGOTを介してアクセスするか(R_ARM_TLS_IE32
)を決定していました。
しかし、liblink
がコンパイラに統合されたことで、コンパイル時にはlinkmode
が不明になりました。このため、linkmode
に依存するコードはビルドエラーを引き起こすか、誤ったコードを生成する可能性がありました。
このコミットでは、linkmode
のチェックを削除し、代わりにctxt->gmsym
というコンテキスト内のシンボルポインタが設定されているかどうかで処理を分岐するように変更しました。ctxt->gmsym
はruntime.tlsgm
シンボルを指しており、このシンボルがリンカによって解決されるべきTLS変数であることを示します。これにより、linkmode
の知識がなくても、TLS関連の命令生成を適切に行うことができるようになりました。
また、src/cmd/ld/data.c
でのSTLSBSS
シンボルへのs->value = 1
の設定は、TLS BSSセクションのシンボルが誤って参照された場合に、実行時エラーを意図的に引き起こすための防御的な措置です。特にARMでは、アラインメントされていないアドレスへのアクセスがハードウェア例外を引き起こすため、1
という値は効果的です。
src/pkg/runtime/asm_arm.s
に追加されたコメントは、liblink
が生成するコードの複雑さ、特にTLSレジスタ(C13)からフェッチした値(R0)をさらに調整してg
とm
レジスタのTLSコピーにアクセスできるようにする「魔法」について開発者に注意を促しています。このコミットは、この「魔法」がlinkmode
に依存しないように修正されたことを示唆しています。
全体として、このコミットはGoツールチェインの内部的な整合性を高め、liblink
の統合に伴うARMアーキテクチャでのTLS関連のバグを修正し、将来的な保守性を向上させるための重要な変更です。
関連リンク
- Go Issue 6992: https://code.google.com/p/go/issues/detail?id=6992 (このコミットによって修正されたバグ)
- Go Issue 7164: https://code.google.com/p/go/issues/detail?id=7164 (x86アーキテクチャにおける同様の問題)
- Go CL 55160043: https://golang.org/cl/55160043 (このコミットのコードレビューページ)
参考にした情報源リンク
- Go言語の公式ドキュメント (Goツールチェイン、cgo、ランタイムに関する情報)
- ARMアーキテクチャリファレンスマニュアル (TLS、リロケーションタイプに関する情報)
- ELF (Executable and Linkable Format) 仕様 (リロケーションタイプに関する情報)
- Goのソースコード (特に
src/cmd/ld
,src/liblink
,src/pkg/runtime
ディレクトリ) - GoのIssueトラッカー (関連するバグ報告や議論)
- Goのコードレビューシステム (変更の意図や議論の詳細)
- Thread-Local Storage (TLS) の概念に関する一般的なプログラミングリソース