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

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

このコミットは、Go言語のリンカであるliblinkcmd/5l(ARMアーキテクチャ向けリンカ)において、共有ライブラリ(flag_shared)使用時のTLS(Thread Local Storage)関連の挙動を修正し、以前のコミットで発生した問題を解決するものです。具体的には、R_ARM_TLS_LE32リロケーションからR_ARM_TLS_IE32リロケーションへの移行に伴う間接参照の追加を補償し、$runtime.tlsgm(SB)へのアクセスをruntime.tlsgm(SB)に書き換えることで、共有ライブラリにおけるTLSアクセスを正しく機能させることを目的としています。また、不要になったシンボルルックアップの削除も行われています。

コミット

commit 9ed5995cfe4c217c1291ced9c4a70334383df226
Author: Elias Naur <elias.naur@gmail.com>
Date:   Mon Feb 3 14:49:57 2014 -0800

    liblink, cmd/5l: restore flag_shared
    
    CL 56120043 fixed and cleaned up TLS on ARM after introducing liblink, but
    left flag_shared broken. This CL restores the (unsupported) flag_shared
    behaviour by simply rewriting access to $runtime.tlsgm(SB) with
    runtime.tlsgm(SB), to compensate for the extra indirection when going from
    the R_ARM_TLS_LE32 relocation to the R_ARM_TLS_IE32 relocation.
    
    Also, remove unnecessary symbol lookup left after 56120043.
    
    LGTM=iant
    R=iant, rsc
    CC=golang-codereviews
    https://golang.org/cl/57000043
---
 src/cmd/5l/5.out.h |  2 +-\
 src/liblink/asm5.c |  3 +--
 src/liblink/obj5.c | 18 +++++++++++++++---\
 3 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h
index 99836cb7ff..16348a457e 100644
--- a/src/cmd/5l/5.out.h
+++ b/src/cmd/5l/5.out.h
@@ -279,7 +279,7 @@ enum
 	D_PLT1 = (D_NONE+44), // R_ARM_PLT32, 2nd inst: add ip, ip, #0xNN000
 	D_PLT2 = (D_NONE+45), // R_ARM_PLT32, 3rd inst: ldr pc, [ip, #0xNNN]!
 	D_CALL = (D_NONE+46), // R_ARM_PLT32/R_ARM_CALL/R_ARM_JUMP24, bl xxxxx or b yyyyy
-	D_TLS = (D_NONE+47), // R_ARM_TLS_LE32
+	D_TLS = (D_NONE+47), // R_ARM_TLS_LE32/R_ARM_TLS_IE32
 };
 
 /*
diff --git a/src/liblink/asm5.c b/src/liblink/asm5.c
index d19283a197..02b6e8e465 100644
--- a/src/liblink/asm5.c
+++ b/src/liblink/asm5.c
@@ -1371,11 +1371,10 @@ if(0 /*debug['G']*/) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->na
 			// Its "address" is the offset from the TLS thread pointer
 			// to the thread-local g and m pointers.
 			// Emit a TLS relocation instead of a standard one.
-			// The TLS flag_shared case is not tested and probably now wrong.
 			if(rel->sym == ctxt->gmsym) {
 				rel->type = D_TLS;
 				if(ctxt->flag_shared)
-					rel->add += ctxt->pc - p->pcrel->pc - 8 - rel->siz; // TODO: probably wrong
+					rel->add += ctxt->pc - p->pcrel->pc - 8 - rel->siz;
 				rel->xadd = rel->add;
 				rel->xsym = rel->sym;
 			} else if(ctxt->flag_shared) {
diff --git a/src/liblink/obj5.c b/src/liblink/obj5.c
index cda00b6432..2af23358fd 100644
--- a/src/liblink/obj5.c
+++ b/src/liblink/obj5.c
@@ -167,6 +167,21 @@ progedit(Link *ctxt, Prog *p)\
 		}\
 		break;\
 	}\
+\
+\tif(ctxt->flag_shared) {\
+\t\t// Shared libraries use R_ARM_TLS_IE32 instead of \
+\t\t// R_ARM_TLS_LE32, replacing the link time constant TLS offset in\
+\t\t// runtime.tlsgm with an address to a GOT entry containing the \
+\t\t// offset. Rewrite $runtime.tlsgm(SB) to runtime.tlsgm(SB) to\
+\t\t// compensate.\
+\t\tif(ctxt->gmsym == nil)\
+\t\t\tctxt->gmsym = linklookup(ctxt, \"runtime.tlsgm\", 0);\
+\
+\t\tif(p->from.type == D_CONST && p->from.name == D_EXTERN && p->from.sym == ctxt->gmsym)\
+\t\t\tp->from.type = D_OREG;\
+\t\tif(p->to.type == D_CONST && p->to.name == D_EXTERN && p->to.sym == ctxt->gmsym)\
+\t\t\tp->to.type = D_OREG;\
+\t}\
 }\
 \
 static Prog*\
@@ -225,8 +240,6 @@ addstacksplit(Link *ctxt, LSym *cursym)\
 \tif(ctxt->symmorestack[0] == nil)\
 \t\tctxt->symmorestack[0] = linklookup(ctxt, \"runtime.morestack\", 0);\
 \t\
-\tif(ctxt->gmsym == nil)\
-\t\tctxt->gmsym = linklookup(ctxt, \"runtime.tlsgm\", 0);\
 \tq = nil;\
 \t\
 \tctxt->cursym = cursym;\
@@ -302,7 +315,6 @@ addstacksplit(Link *ctxt, LSym *cursym)\
 \t * strip NOPs\
 \t * expand RET\
 \t * expand BECOME pseudo\
-\t * fixup TLS\
 \t */\
 \n\tfor(p = cursym->text; p != nil; p = p->link) {\

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

https://github.com/golang/go/commit/9ed5995cfe4c217c1291ced9c4a70334383df226

元コミット内容

このコミットは、Go言語のリンカであるliblinkcmd/5l(ARMアーキテクチャ向けのリンカ)において、flag_sharedというフラグの挙動を復元するものです。以前のコミットCL 56120043liblink導入後にARM上でのTLS(Thread Local Storage)を修正・整理しましたが、その際にflag_sharedの挙動が壊れてしまいました。このコミットは、R_ARM_TLS_LE32リロケーションからR_ARM_TLS_IE32リロケーションへの移行に伴う追加の間接参照を補償するために、$runtime.tlsgm(SB)へのアクセスをruntime.tlsgm(SB)に書き換えることで、この(サポートされていない)flag_sharedの挙動を復元します。また、CL 56120043後に残っていた不要なシンボルルックアップも削除しています。

変更の背景

Go言語のリンカは、プログラムを最終的な実行可能ファイルに結合する重要な役割を担っています。特に、異なるアーキテクチャやOS環境に対応するためには、その環境特有のリンキングメカニズムを理解し、適切に処理する必要があります。

このコミットの背景には、以下の点が挙げられます。

  1. liblinkの導入: Go言語のリンカは、歴史的にPlan 9のリンカをベースにしており、各アーキテクチャ(例: 5lはARM、6lはAMD64)ごとに独立したリンカが存在しました。しかし、Go 1.3以降、リンカのアーキテクチャが大きく変更され、liblinkという共通のライブラリが導入されました。これにより、リンカのコードベースが整理され、保守性が向上しました。
  2. ARM上のTLS処理の変更: CL 56120043という以前のコミットで、liblink導入後のARMアーキテクチャにおけるTLS(Thread Local Storage)の処理が修正・整理されました。TLSは、各スレッドが独自のデータを持つためのメカニズムであり、Goランタイムがゴルーチン(Goの軽量スレッド)の状態を管理する上で重要な役割を果たします。
  3. flag_sharedの破損: CL 56120043の変更により、flag_sharedというリンカフラグが正しく機能しなくなりました。このフラグは、共有ライブラリをビルドする際に使用されるもので、GoプログラムがC言語のライブラリと連携する場合や、特定のシステムライブラリを使用する場合に重要となります。共有ライブラリでは、位置独立コード(PIC: Position-Independent Code)が求められるため、TLSへのアクセス方法も通常の実行可能ファイルとは異なります。
  4. リロケーションタイプの変更: ARMアーキテクチャでは、TLSへのアクセスにR_ARM_TLS_LE32R_ARM_TLS_IE32という異なるリロケーションタイプが使用されます。R_ARM_TLS_LE32は、実行可能ファイル内のTLS変数への直接オフセットアクセスに使用されますが、共有ライブラリでは使用できません。一方、R_ARM_TLS_IE32は、共有ライブラリから実行可能ファイル内のTLS変数にアクセスする際に、GOT(Global Offset Table)を介した間接参照を使用します。CL 56120043の変更により、このリロケーションタイプの扱いが変わり、flag_sharedが壊れたと考えられます。
  5. 間接参照の補償: R_ARM_TLS_LE32からR_ARM_TLS_IE32への移行は、TLS変数へのアクセスに「間接参照」が追加されることを意味します。この間接参照をリンカが正しく補償しないと、TLS変数へのアクセスが失敗し、プログラムがクラッシュするなどの問題が発生します。
  6. 不要なシンボルルックアップの削除: 以前の変更で不要になったシンボルルックアップが残っていたため、それらを削除することでコードのクリーンアップと効率化を図っています。

このコミットは、GoプログラムがARMアーキテクチャ上で共有ライブラリを介してTLSを正しく利用できるようにするための重要な修正であり、Goランタイムの安定性と互換性を向上させるものです。

前提知識の解説

このコミットを理解するためには、以下の技術的な概念を把握しておく必要があります。

  • リンカ: コンパイラによって生成されたオブジェクトファイル(機械語コードとデータ)を結合し、実行可能なプログラムやライブラリを作成するツールです。シンボルの解決(関数や変数のアドレスを決定する)、リロケーション(コード内のアドレス参照を修正する)、デッドコードの削除などを行います。
  • cmd/5l: Go言語の初期のリンカの一つで、ARMアーキテクチャ(Plan 9の命名規則で「5」)向けのリンカでした。Goツールチェーンは、6l(AMD64)、8l(i386)など、ターゲットアーキテクチャごとに異なるリンカを持っていました。
  • liblink: Go 1.3以降、リンカの内部構造が大きく変更され、共通のリンキングロジックがliblinkというライブラリに集約されました。これにより、各アーキテクチャ固有のリンカはliblinkを呼び出す形になり、コードの重複が減り、保守性が向上しました。

2. Thread Local Storage (TLS)

  • TLSの概念: 各スレッドが自分専用のデータ領域を持つためのメカニズムです。グローバル変数とは異なり、TLSに格納されたデータはスレッド間で共有されず、各スレッドが独立したコピーを持ちます。
  • GoにおけるTLS: Go言語のゴルーチンはOSのスレッドとは異なり、Goランタイムが管理する軽量な並行処理単位です。Goランタイム自体は、内部的にOSのスレッドを使用し、ゴルーチンをそれらのスレッドにマッピングします。Goランタイムは、ゴルーチン固有の状態(gポインタなど)をOSのスレッドローカルストレージに格納することがあります。これは、Cgo(GoからC言語のコードを呼び出す機能)を使用する場合や、特定のシステムコールを扱う場合に特に重要になります。
  • runtime.tlsgm(SB): Goランタイムの内部シンボルで、TLSに関連するグローバルなメモリ領域または関数を指します。SBはGoアセンブリにおける「Static Base」を意味し、グローバルシンボルへの参照を示します。runtime.tlsgmは、Goランタイムがスレッドローカルなg(ゴルーチン構造体)とm(OSスレッド構造体)ポインタを管理するために使用するTLSオフセットを保持していると考えられます。

3. ARMアーキテクチャにおけるTLSリロケーションタイプ

ELF(Executable and Linkable Format)バイナリ形式では、プログラムのロード時にアドレスを解決するための「リロケーション」という仕組みがあります。TLSアクセスに関連するARM固有のリロケーションタイプには、主に以下の2つがあります。

  • R_ARM_TLS_LE32 (Local Executable 32-bit):

    • 用途: 実行可能ファイル(メインプログラム)内で定義されたTLS変数に、同じ実行可能ファイル内のコードから直接アクセスする場合に使用されます。
    • 動作: スレッドポインタ($tp)からのオフセットとしてTLS変数のアドレスを解決します。
    • 制限: 共有ライブラリ(ダイナミックライブラリ)では使用できません。共有ライブラリは位置独立コード(PIC)である必要があり、TLS変数の正確なオフセットをリンク時に決定できないためです。R_ARM_TLS_LE32を含む共有ライブラリをリンクしようとすると、通常リンカエラーになります。
  • R_ARM_TLS_IE32 (Initial Executable 32-bit):

    • 用途: 共有ライブラリ内のコードが、メインの実行可能ファイル内で定義されたTLS変数にアクセスする場合に使用されます。
    • 動作: 直接オフセットを使用する代わりに、GOT(Global Offset Table)にエントリを割り当てます。このGOTエントリは、実行時にR_ARM_TLS_TPOFF32などのリロケーションによって、正しいTLS変数へのオフセットを指すように動的に解決されます。
    • 利点: GOTを介した間接アクセスにより、共有ライブラリが位置独立性を保ちながら、メイン実行可能ファイルのTLS変数にアクセスできるようになります。

4. flag_shared

Goリンカにおけるflag_sharedは、共有ライブラリをビルドするためのフラグです。このフラグが有効な場合、リンカは共有ライブラリの要件(例: 位置独立コードの生成)を満たすように動作を変更します。TLSアクセスに関しても、共有ライブラリではR_ARM_TLS_LE32ではなくR_ARM_TLS_IE32のような間接的なリロケーションタイプを使用する必要があります。

技術的詳細

このコミットの技術的な核心は、ARMアーキテクチャにおけるTLSアクセスのリロケーションタイプ変更に伴う「間接参照の追加」をリンカが正しく処理するように修正することにあります。

以前のCL 56120043は、liblinkの導入後、ARM上のTLS処理を改善しました。しかし、この改善はflag_shared(共有ライブラリビルド)のシナリオを考慮しておらず、結果としてflag_sharedが壊れてしまいました。

問題の根源は、TLS変数へのアクセス方法がR_ARM_TLS_LE32からR_ARM_TLS_IE32に変わったことにあります。

  • R_ARM_TLS_LE32: これは「Local Executable」リロケーションであり、TLS変数のアドレスがスレッドポインタからの直接オフセットとして解決されます。これは、実行可能ファイル内でTLS変数にアクセスする場合には効率的ですが、共有ライブラリでは位置独立性(PIC)の要件を満たせません。
  • R_ARM_TLS_IE32: これは「Initial Executable」リロケーションであり、共有ライブラリからメイン実行可能ファイル内のTLS変数にアクセスする場合に使用されます。このリロケーションは、GOT(Global Offset Table)を介した間接参照を導入します。つまり、TLS変数のオフセットがGOTエントリに格納され、コードはそのGOTエントリを介してTLS変数にアクセスします。これにより、共有ライブラリは位置独立性を保つことができます。

CL 56120043の変更により、GoリンカはARM上でTLSアクセスを処理する際に、共有ライブラリのコンテキストでもR_ARM_TLS_IE32のような間接的なアプローチを期待するようになりました。しかし、リンカが生成するコードは、この「間接参照の追加」を考慮していなかったため、$runtime.tlsgm(SB)(TLSオフセットを保持するシンボル)へのアクセスが正しく解決されなくなりました。

このコミットは、この問題を解決するために、リンカがflag_sharedが有効な場合に、$runtime.tlsgm(SB)へのアクセスをruntime.tlsgm(SB)に「書き換える」というアプローチを取っています。

具体的には、src/liblink/obj5.cprogedit関数内で、ctxt->flag_sharedが真の場合に以下の処理が追加されています。

  1. runtime.tlsgmシンボルをルックアップします。
  2. 命令のfromオペランドまたはtoオペランドがD_CONSTタイプで、かつruntime.tlsgmシンボルを参照している場合、そのタイプをD_OREG(オフセットレジスタ)に変更します。

これは、リンカが$runtime.tlsgm(SB)という形式の参照(定数オフセットとして扱われる)を、runtime.tlsgm(SB)という形式の参照(GOTを介した間接参照として扱われる)に変換することで、R_ARM_TLS_IE32リロケーションが期待する形式に合わせることを意味します。これにより、共有ライブラリがTLS変数にアクセスする際に追加される間接参照が正しく補償され、プログラムが正常に動作するようになります。

また、src/liblink/asm5.cでは、// The TLS flag_shared case is not tested and probably now wrong.というコメントが削除されており、これはこのコミットによってflag_sharedのTLS処理が修正されたことを示唆しています。

さらに、src/liblink/obj5.caddstacksplit関数から、ctxt->gmsym = linklookup(ctxt, "runtime.tlsgm", 0);という行が削除されています。これは、progedit関数内でgmsymのルックアップが行われるようになったため、この場所でのルックアップが不要になったことを意味します。これにより、コードの重複が解消され、クリーンアップが図られています。

最後に、src/cmd/5l/5.out.hでは、D_TLSリロケーションタイプがR_ARM_TLS_LE32だけでなくR_ARM_TLS_IE32もカバーするようになったことがコメントで明示されています。これは、リンカがこれらの両方のTLSリロケーションタイプを処理できるようになったことを示しています。

これらの変更により、GoリンカはARMアーキテクチャ上で共有ライブラリをビルドする際に、TLSアクセスを正しく処理できるようになり、Goプログラムの柔軟性と互換性が向上しました。

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

このコミットでは、主に以下の3つのファイルが変更されています。

  1. src/cmd/5l/5.out.h:

    • D_TLSというリロケーションタイプのコメントが更新され、R_ARM_TLS_LE32だけでなくR_ARM_TLS_IE32もこのタイプで処理されることが明示されました。
    --- a/src/cmd/5l/5.out.h
    +++ b/src/cmd/5l/5.out.h
    @@ -279,7 +279,7 @@ enum
     	D_PLT1 = (D_NONE+44), // R_ARM_PLT32, 2nd inst: add ip, ip, #0xNN000
     	D_PLT2 = (D_NONE+45), // R_ARM_PLT32, 3rd inst: ldr pc, [ip, #0xNNN]!
     	D_CALL = (D_NONE+46), // R_ARM_PLT32/R_ARM_CALL/R_ARM_JUMP24, bl xxxxx or b yyyyy
    -	D_TLS = (D_NONE+47), // R_ARM_TLS_LE32
    +	D_TLS = (D_NONE+47), // R_ARM_TLS_LE32/R_ARM_TLS_IE32
     };
    
  2. src/liblink/asm5.c:

    • TLS関連のコードブロックから、// The TLS flag_shared case is not tested and probably now wrong.というコメントが削除されました。これは、flag_sharedのTLS処理がこのコミットで修正されたことを示唆しています。
    --- a/src/liblink/asm5.c
    +++ b/src/liblink/asm5.c
    @@ -1371,11 +1371,10 @@ if(0 /*debug['G']*/) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->na
     			// Its "address" is the offset from the TLS thread pointer
     			// to the thread-local g and m pointers.
     			// Emit a TLS relocation instead of a standard one.
    -			// The TLS flag_shared case is not tested and probably now wrong.
     			if(rel->sym == ctxt->gmsym) {
     				rel->type = D_TLS;
     				if(ctxt->flag_shared)
    -					rel->add += ctxt->pc - p->pcrel->pc - 8 - rel->siz; // TODO: probably wrong
    +					rel->add += ctxt->pc - p->pcrel->pc - 8 - rel->siz;
     				rel->xadd = rel->add;
     				rel->xsym = rel->sym;
     			} else if(ctxt->flag_shared) {
    
  3. src/liblink/obj5.c:

    • progedit関数内に、flag_sharedが有効な場合のTLSシンボル(runtime.tlsgm(SB))の処理を追加しました。具体的には、$runtime.tlsgm(SB)形式の参照をruntime.tlsgm(SB)形式に書き換えるロジックが追加されています。
    • addstacksplit関数から、不要になったruntime.tlsgmシンボルのルックアップ処理が削除されました。
    --- a/src/liblink/obj5.c
    +++ b/src/liblink/obj5.c
    @@ -167,6 +167,21 @@ progedit(Link *ctxt, Prog *p)\
     		}\
     		break;\
     	}\
    +\
    +\tif(ctxt->flag_shared) {\
    +\t\t// Shared libraries use R_ARM_TLS_IE32 instead of \
    +\t\t// R_ARM_TLS_LE32, replacing the link time constant TLS offset in\
    +\t\t// runtime.tlsgm with an address to a GOT entry containing the \
    +\t\t// offset. Rewrite $runtime.tlsgm(SB) to runtime.tlsgm(SB) to\
    +\t\t// compensate.\
    +\t\tif(ctxt->gmsym == nil)\
    +\t\t\tctxt->gmsym = linklookup(ctxt, \"runtime.tlsgm\", 0);\
    +\
    +\t\tif(p->from.type == D_CONST && p->from.name == D_EXTERN && p->from.sym == ctxt->gmsym)\
    +\t\t\tp->from.type = D_OREG;\
    +\t\tif(p->to.type == D_CONST && p->to.name == D_EXTERN && p->to.sym == ctxt->gmsym)\
    +\t\t\tp->to.type = D_OREG;\
    +\t}\
     }\
     \
     static Prog*\
    @@ -225,8 +240,6 @@ addstacksplit(Link *ctxt, LSym *cursym)\
     	if(ctxt->symmorestack[0] == nil)\
     		ctxt->symmorestack[0] = linklookup(ctxt, \"runtime.morestack\", 0);\
     	\
    -	if(ctxt->gmsym == nil)\
    -		ctxt->gmsym = linklookup(ctxt, \"runtime.tlsgm\", 0);\
     	q = nil;\
     	\
     	ctxt->cursym = cursym;\
    @@ -302,7 +315,6 @@ addstacksplit(Link *ctxt, LSym *cursym)\
     	 * strip NOPs\
     	 * expand RET\
     	 * expand BECOME pseudo\
    -	 * fixup TLS\
     	 */\
     \n\tfor(p = cursym->text; p != nil; p = p->link) {\
    

コアとなるコードの解説

src/cmd/5l/5.out.h の変更

  • D_TLS = (D_NONE+47), // R_ARM_TLS_LE32/R_ARM_TLS_IE32:
    • この変更は、リンカが内部的に使用するリロケーションタイプD_TLSが、ARMアーキテクチャにおける2つの主要なTLSリロケーションタイプ、R_ARM_TLS_LE32R_ARM_TLS_IE32の両方を表すようになったことを示しています。
    • 以前はR_ARM_TLS_LE32のみがコメントされていましたが、共有ライブラリのサポートを強化するためにR_ARM_TLS_IE32もこの内部タイプで処理されるようになったことを明示しています。これは、リンカがこれらの異なるTLSアクセスパターンを統一的に扱うための準備です。

src/liblink/asm5.c の変更

  • コメントの削除:
    • // The TLS flag_shared case is not tested and probably now wrong.というコメントが削除されました。
    • このコメントは、以前のコードがflag_shared(共有ライブラリビルド)におけるTLS処理が正しくない可能性を示唆していました。このコミットによってその問題が修正されたため、この警告コメントは不要になりました。これは、このコミットがflag_sharedのTLS挙動を修正したことの直接的な証拠です。
  • rel->add += ctxt->pc - p->pcrel->pc - 8 - rel->siz;:
    • この行は、TLSリロケーションのオフセットを調整する部分です。以前のコメントで「TODO: probably wrong」とされていた部分ですが、このコミットではこの計算式自体は変更されていません。これは、この計算式が元々正しかったか、あるいはこの計算式が問題の本質ではなく、obj5.cでのシンボル書き換えが主要な修正点であることを示唆しています。この行は、TLSオフセットの計算において、プログラムカウンタ(PC)からの相対オフセットや命令サイズなどを考慮して、正確なアドレスを算出するために使用されます。

src/liblink/obj5.c の変更

  • progedit 関数内の追加ロジック:

    • if(ctxt->flag_shared) ブロックが追加されました。これは、リンカが共有ライブラリをビルドしている場合にのみ、以下の特殊なTLS処理を実行することを示します。
    • // Shared libraries use R_ARM_TLS_IE32 instead of // R_ARM_TLS_LE32, replacing the link time constant TLS offset in // runtime.tlsgm with an address to a GOT entry containing the // offset. Rewrite $runtime.tlsgm(SB) to runtime.tlsgm(SB) to // compensate.:
      • このコメントは、変更の目的を明確に説明しています。共有ライブラリはR_ARM_TLS_LE32ではなくR_ARM_TLS_IE32を使用し、これはリンク時定数であるruntime.tlsgmのTLSオフセットを、GOTエントリのアドレスに置き換えることを意味します。この変更は、$runtime.tlsgm(SB)へのアクセスをruntime.tlsgm(SB)に書き換えることで、この間接参照を補償します。
    • if(ctxt->gmsym == nil) ctxt->gmsym = linklookup(ctxt, "runtime.tlsgm", 0);:
      • runtime.tlsgmシンボルがまだルックアップされていない場合、ここでルックアップしてctxt->gmsymに格納します。これにより、後続の処理でこのシンボルを参照できるようになります。
    • if(p->from.type == D_CONST && p->from.name == D_EXTERN && p->from.sym == ctxt->gmsym) p->from.type = D_OREG;:
      • 現在の命令(p)のfromオペランドが、D_CONSTタイプ(定数オフセット)で、かつ外部シンボル(D_EXTERN)としてruntime.tlsgmを参照している場合、そのタイプをD_OREG(オフセットレジスタ)に変更します。
      • これは、$runtime.tlsgm(SB)という形式の参照(定数として扱われる)を、runtime.tlsgm(SB)という形式の参照(GOTを介した間接参照として扱われる)に変換する核心部分です。D_OREGは、レジスタからのオフセットアクセスを示すことが多く、ここではGOTエントリを介した間接アクセスを表現するために使用されます。
    • if(p->to.type == D_CONST && p->to.name == D_EXTERN && p->to.sym == ctxt->gmsym) p->to.type = D_OREG;:
      • 同様に、命令のtoオペランドがruntime.tlsgmを参照している場合も、そのタイプをD_OREGに変更します。これにより、runtime.tlsgmが命令のターゲットとして使用される場合も正しく処理されます。
  • addstacksplit 関数からの削除:

    • if(ctxt->gmsym == nil) ctxt->gmsym = linklookup(ctxt, "runtime.tlsgm", 0); の行が削除されました。
    • この行は、以前はaddstacksplit関数内でruntime.tlsgmシンボルをルックアップしていましたが、progedit関数内でこのルックアップが集中して行われるようになったため、重複を避けるために削除されました。これはコードの整理と効率化に貢献します。
    • * fixup TLSというコメントも削除されています。これは、TLSの修正がprogedit関数内の新しいロジックによって行われるようになったため、この場所での「fixup TLS」の記述が不要になったことを示します。

これらの変更は、GoリンカがARMアーキテクチャ上で共有ライブラリをビルドする際に、TLSアクセスを正しく処理するための重要な修正です。特に、$runtime.tlsgm(SB)からruntime.tlsgm(SB)への書き換えは、R_ARM_TLS_IE32リロケーションが期待する間接参照のモデルに合わせるためのキーとなる変更です。

関連リンク

参考にした情報源リンク