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

[インデックス 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_LE32R_ARM_TLS_IE32)を必要としていました。

このコミットでは、liblink内のARM関連のコード(src/liblink/asm5.csrc/liblink/obj5.c)が修正され、linkmodeのチェックを削除し、代わりにctxt->gmsymruntime.tlsgmシンボルへのポインタ)が設定されているかどうかでTLS関連の処理を分岐するように変更されています。

また、src/cmd/ld/data.cでは、STLSBSS(Thread Local Storage BSSセクション)シンボルへの参照が、無効なアドレス(1)を持つように設定されています。これは、これらのシンボルが誤って使用された場合に、アラインメントエラーなどによって即座にクラッシュするようにするためです。特にARMでは、アラインメントされていないアドレスへのアクセスがフォルトを引き起こすため、1という値が選ばれています。

src/pkg/runtime/asm_arm.sでは、runtime.save_gmruntime.load_gmというTLS関連のランタイム関数にコメントが追加されています。これらの関数は、MRC命令(Coprocessor Register Transfer)を使用してTLSレジスタから値をフェッチし、g(goroutine構造体)とm(machine構造体)レジスタのTLSコピーを更新します。追加されたコメントは、liblinkがこれらのMRC命令の後にR0レジスタを調整する命令を追加し、8(R0)12(R0)がTLSコピーを指すようにする「魔法のような」挙動について言及しています。このコミットは、この「魔法」をlinkmodeに依存しない形に修正しています。

具体的には、obj5.caddstacksplit関数内で、runtime.tlsgmシンボルへのアクセスに関するコードが変更されています。以前はctxt->linkmode == LinkExternalという条件で分岐していましたが、この条件が削除され、ctxt->gmsymが設定されているかどうかで処理が継続されるようになっています。これにより、コンパイル時にlinkmodeが不明な場合でも、TLS関連の命令生成が正しく行われるようになります。

さらに、ARMアセンブリコードにおける条件コードのマジックナンバー14が、より意味のある定数C_SCOND_NONEに置き換えられています。これはコードの可読性と保守性を向上させるための変更です。

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

このコミットで変更された主要なファイルとコードの変更点は以下の通りです。

  1. src/cmd/ld/data.c:

    • dodata関数内で、STLSBSSタイプのシンボルに対して、s->value = 1を設定するロジックが追加されました。これは、これらのシンボルが誤って使用された場合にフォルトを発生させるためです。
    • address関数内で、シンボルのvaluesect->vaddrを加算する際に、sym->sect != nilのチェックが追加されました。
  2. src/liblink/asm5.c:

    • asmout関数のシグネチャからLSym *gmsym引数が削除され、ctxt->gmsymが直接使用されるようになりました。
    • span5関数内で、gmsymローカル変数が削除され、ctxt->gmsymが使用されるようになりました。
    • 条件コードのマジックナンバー14C_SCOND_NONEに置き換えられました。
  3. src/liblink/obj5.c:

    • addstacksplit関数内で、ctxt->linkmode == LinkExternalのチェックが削除され、ctxt->gmsymが設定されているかどうかでTLS関連の処理が継続されるようになりました。
    • TLS関連の命令生成ロジック(AMOVW, AADD, ASUB命令の生成)において、条件コードのマジックナンバー14C_SCOND_NONEに置き換えられました。
    • ARET命令の条件チェックで、マジックナンバー14C_SCOND_NONEに置き換えられました。
  4. src/pkg/runtime/asm_arm.s:

    • runtime.save_gmruntime.load_gm関数に、liblinkMRC命令の後にR0レジスタを調整する「魔法のような」挙動に関するコメントが追加されました。

コアとなるコードの解説

このコミットの主要な変更は、liblinkがARMアーキテクチャのTLS変数を扱う際に、linkmodeというリンカ固有の情報に依存しないようにすることです。

以前のコードでは、runtime.tlsgmシンボルへの参照を解決する際に、現在のlinkmodeLinkExternal(動的リンクに相当)であるかどうかをチェックしていました。このチェックに基づいて、TLS変数のオフセットを直接埋め込むか(R_ARM_TLS_LE32)、またはGOTを介してアクセスするか(R_ARM_TLS_IE32)を決定していました。

しかし、liblinkがコンパイラに統合されたことで、コンパイル時にはlinkmodeが不明になりました。このため、linkmodeに依存するコードはビルドエラーを引き起こすか、誤ったコードを生成する可能性がありました。

このコミットでは、linkmodeのチェックを削除し、代わりにctxt->gmsymというコンテキスト内のシンボルポインタが設定されているかどうかで処理を分岐するように変更しました。ctxt->gmsymruntime.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)をさらに調整してgmレジスタのTLSコピーにアクセスできるようにする「魔法」について開発者に注意を促しています。このコミットは、この「魔法」がlinkmodeに依存しないように修正されたことを示唆しています。

全体として、このコミットはGoツールチェインの内部的な整合性を高め、liblinkの統合に伴うARMアーキテクチャでのTLS関連のバグを修正し、将来的な保守性を向上させるための重要な変更です。

関連リンク

参考にした情報源リンク

  • 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) の概念に関する一般的なプログラミングリソース