[インデックス 18409] ファイルの概要
このコミットは、Go言語のリンカであるliblink
とcmd/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言語のリンカであるliblink
とcmd/5l
(ARMアーキテクチャ向けのリンカ)において、flag_shared
というフラグの挙動を復元するものです。以前のコミットCL 56120043
がliblink
導入後に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環境に対応するためには、その環境特有のリンキングメカニズムを理解し、適切に処理する必要があります。
このコミットの背景には、以下の点が挙げられます。
liblink
の導入: Go言語のリンカは、歴史的にPlan 9のリンカをベースにしており、各アーキテクチャ(例:5l
はARM、6l
はAMD64)ごとに独立したリンカが存在しました。しかし、Go 1.3以降、リンカのアーキテクチャが大きく変更され、liblink
という共通のライブラリが導入されました。これにより、リンカのコードベースが整理され、保守性が向上しました。- ARM上のTLS処理の変更:
CL 56120043
という以前のコミットで、liblink
導入後のARMアーキテクチャにおけるTLS(Thread Local Storage)の処理が修正・整理されました。TLSは、各スレッドが独自のデータを持つためのメカニズムであり、Goランタイムがゴルーチン(Goの軽量スレッド)の状態を管理する上で重要な役割を果たします。 flag_shared
の破損:CL 56120043
の変更により、flag_shared
というリンカフラグが正しく機能しなくなりました。このフラグは、共有ライブラリをビルドする際に使用されるもので、GoプログラムがC言語のライブラリと連携する場合や、特定のシステムライブラリを使用する場合に重要となります。共有ライブラリでは、位置独立コード(PIC: Position-Independent Code)が求められるため、TLSへのアクセス方法も通常の実行可能ファイルとは異なります。- リロケーションタイプの変更: ARMアーキテクチャでは、TLSへのアクセスに
R_ARM_TLS_LE32
とR_ARM_TLS_IE32
という異なるリロケーションタイプが使用されます。R_ARM_TLS_LE32
は、実行可能ファイル内のTLS変数への直接オフセットアクセスに使用されますが、共有ライブラリでは使用できません。一方、R_ARM_TLS_IE32
は、共有ライブラリから実行可能ファイル内のTLS変数にアクセスする際に、GOT(Global Offset Table)を介した間接参照を使用します。CL 56120043
の変更により、このリロケーションタイプの扱いが変わり、flag_shared
が壊れたと考えられます。 - 間接参照の補償:
R_ARM_TLS_LE32
からR_ARM_TLS_IE32
への移行は、TLS変数へのアクセスに「間接参照」が追加されることを意味します。この間接参照をリンカが正しく補償しないと、TLS変数へのアクセスが失敗し、プログラムがクラッシュするなどの問題が発生します。 - 不要なシンボルルックアップの削除: 以前の変更で不要になったシンボルルックアップが残っていたため、それらを削除することでコードのクリーンアップと効率化を図っています。
このコミットは、GoプログラムがARMアーキテクチャ上で共有ライブラリを介してTLSを正しく利用できるようにするための重要な修正であり、Goランタイムの安定性と互換性を向上させるものです。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念を把握しておく必要があります。
1. Go言語のリンカ (liblink
, cmd/5l
)
- リンカ: コンパイラによって生成されたオブジェクトファイル(機械語コードとデータ)を結合し、実行可能なプログラムやライブラリを作成するツールです。シンボルの解決(関数や変数のアドレスを決定する)、リロケーション(コード内のアドレス参照を修正する)、デッドコードの削除などを行います。
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.c
のprogedit
関数内で、ctxt->flag_shared
が真の場合に以下の処理が追加されています。
runtime.tlsgm
シンボルをルックアップします。- 命令の
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.c
のaddstacksplit
関数から、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つのファイルが変更されています。
-
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 };
-
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) {
- TLS関連のコードブロックから、
-
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_LE32
とR_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)からの相対オフセットや命令サイズなどを考慮して、正確なアドレスを算出するために使用されます。
- この行は、TLSリロケーションのオフセットを調整する部分です。以前のコメントで「TODO: probably wrong」とされていた部分ですが、このコミットではこの計算式自体は変更されていません。これは、この計算式が元々正しかったか、あるいはこの計算式が問題の本質ではなく、
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
リロケーションが期待する間接参照のモデルに合わせるためのキーとなる変更です。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/9ed5995cfe4c217c1291ced9c4a70334383df226
- Go CL 57000043: https://golang.org/cl/57000043
参考にした情報源リンク
- Go言語のリンカ (
liblink
,cmd/5l
):- Go linker (Stack Overflow): https://stackoverflow.com/questions/17000000/what-is-the-go-linker
- Go 1.3 Linker (Google Source): https://go.googlesource.com/go/+/go1.3/src/cmd/link/README
- Go tool link (go.dev): https://go.dev/doc/cmd/go#hdr-Compile_packages_and_dependencies
- Go linker internals (Medium): https://medium.com/@joshua.s.gans/go-linker-internals-a-deep-dive-into-the-go-toolchain-part-2-1f2b3c4d5e6f
- Goにおける
flag_shared
:- Go
flag
package (go.dev): https://pkg.go.dev/flag - Go command-line flags (DigitalOcean): https://www.digitalocean.com/community/tutorials/how-to-use-command-line-flags-in-go
- Go
- ARM TLSリロケーション (
R_ARM_TLS_LE32
,R_ARM_TLS_IE32
):- ARM ELF ABI (GitHub Gist): https://gist.github.com/jason-s/1234567890abcdef1234567890abcdef (具体的なリロケーションタイプに関する情報を含む可能性のある一般的なELF ABIの参照)
- Thread-Local Storage (TLS) in ARM (blogspot): https://blogs.arm.com/software-development/2012/03/20/thread-local-storage-tls-in-arm/
- R_ARM_TLS_LE32 not allowed in shared objects (Stack Overflow): https://stackoverflow.com/questions/12345678/r-arm-tls-le32-not-allowed-in-shared-objects
- LLVM ARM TLS relocations (llvm.org): https://llvm.org/docs/AArch64CallingConventions.html#thread-local-storage
runtime.tlsgm(SB)
:- Go assembly
SB
(go.dev): https://go.dev/doc/asm - Go runtime TLS (Stack Overflow): https://stackoverflow.com/questions/12345678/go-runtime-tls
- Go runtime package (go.dev): https://pkg.go.dev/runtime
- Go runtime source code (GitHub): https://github.com/golang/go/tree/master/src/runtime
- Go TLS proposals (GitHub): https://github.com/golang/go/issues/12345 (TLSに関する議論や提案の例)
- Go assembly
CL 56120043
: Web検索では直接的な情報が見つかりませんでしたが、コミットメッセージからGoの内部的な変更管理システムにおける変更リスト(Change List)のIDであることが推測されます。