[インデックス 15085] ファイルの概要
このコミットは、Goランタイムとリンカ(cmd/ld
)におけるコードの**位置独立性(Position-Independent Code, PIC)**を向上させるための変更を導入しています。具体的には、スタックアンワインド処理とガベージコレクション(GC)命令の動作を、シンボルの動的な再配置に対応するように修正しています。
コミット
commit 44cf814d5002faf92bce26d21c3bf676d6a2c581
Author: Elias Naur <elias.naur@gmail.com>
Date: Fri Feb 1 11:24:49 2013 -0800
runtime, cmd/ld: make code more position-independent
Change the stack unwinding code to compensate for the dynamic
relocation of symbols.
Change the gc instruction GC_CALL to use a relative offset instead of
an absolute address.
R=golang-dev
CC=golang-dev
https://golang.org/cl/7248048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/44cf814d5002faf92bce26d21c3bf676d6a2c581
元コミット内容
このコミットの元の内容は以下の通りです。
- コミットメッセージ:
runtime, cmd/ld: make code more position-independent
- 詳細:
- スタックアンワインドコードを、シンボルの動的な再配置を補償するように変更。
- GC命令
GC_CALL
を絶対アドレスの代わりに相対オフセットを使用するように変更。
変更の背景
この変更の背景には、Goプログラムがより柔軟なメモリ配置で実行できるようにするという目的があります。特に、共有ライブラリや動的にロードされるモジュールにおいて、コードがロードされるメモリ上のアドレスが実行時まで確定しない場合があります。このような環境で正しく動作するためには、コードが絶対アドレスに依存せず、どこにロードされても機能する位置独立性を持つ必要があります。
Goの初期のバージョンでは、一部の内部処理(特にスタックアンワインドやGC関連のメタデータ)が絶対アドレスに依存している可能性がありました。これにより、特定の環境(例えば、ASLR (Address Space Layout Randomization) が有効なシステムや、動的リンクを使用するケース)での互換性やセキュリティが制限されることがありました。
このコミットは、これらの依存関係を解消し、Goバイナリの移植性と堅牢性を高めることを目指しています。
前提知識の解説
1. 位置独立コード (Position-Independent Code, PIC)
PICは、メモリ上の任意のアドレスにロードされても正しく実行できる機械語コードのことです。これは、コード内のアドレス参照を絶対アドレスではなく、プログラムカウンタ(PC)からの相対オフセットや、グローバルオフセットテーブル(GOT)/プロシージャリンケージテーブル(PLT)のような間接的な参照メカニズムを使用することで実現されます。
- 絶対アドレス参照: コードが特定の固定メモリ位置を直接参照する場合、そのコードは位置独立ではありません。ロードアドレスが変わると、参照が不正になります。
- 相対アドレス参照: コードが現在のプログラムカウンタからのオフセットでアドレスを参照する場合、ロードアドレスが変わってもオフセットは変わらないため、コードは位置独立になります。
PICは、共有ライブラリ(DLLや.soファイル)の作成に不可欠です。これにより、複数のプロセスが同じライブラリの単一のコピーをメモリにロードし、異なるアドレス空間にマッピングしても正しく動作できます。
2. 動的な再配置 (Dynamic Relocation)
プログラムが実行される前に、リンカはコード内のシンボル参照(関数や変数のアドレス)を解決します。静的リンクの場合、これらのアドレスはビルド時に固定されます。しかし、動的リンクの場合、共有ライブラリ内のシンボルの実際のアドレスは、プログラムがロードされるまで決定されません。
動的な再配置は、プログラムのロード時に、ローダーが共有ライブラリ内のシンボル参照を、そのライブラリが実際にロードされたメモリ上のアドレスに合わせて修正するプロセスです。これにより、プログラムは実行時に必要な共有ライブラリの関数やデータにアクセスできるようになります。
3. スタックアンワインド (Stack Unwinding)
スタックアンワインドは、プログラムの実行中に、現在の関数呼び出しから呼び出し元の関数へとスタックフレームを遡っていくプロセスです。これは主に以下の目的で使用されます。
- 例外処理: 例外が発生した際に、適切な例外ハンドラを見つけるためにスタックを遡ります。
- デバッグ: コールスタック(関数呼び出しの履歴)を表示するために使用されます。
- プロファイリング: 実行パスを追跡し、パフォーマンスボトルネックを特定するために使用されます。
- ガベージコレクション: GCがスタック上のポインタをスキャンし、到達可能なオブジェクトを特定するために使用されることがあります。
スタックアンワインドが正しく機能するためには、各スタックフレームのサイズ、リターンアドレス、および保存されたレジスタの値に関する正確な情報が必要です。これらの情報は、コンパイラによって生成されるメタデータ(アンワインド情報)に格納されます。
4. ガベージコレクション (Garbage Collection, GC) と GC_CALL
命令
Goは自動メモリ管理(ガベージコレクション)を採用しています。GCは、プログラムが動的に割り当てたメモリのうち、もはや到達不可能になった(使用されていない)メモリを自動的に解放するプロセスです。
GoのGCは、オブジェクトグラフを辿ることで到達可能なオブジェクトを特定します。このプロセスには、スタック上のポインタをスキャンすることも含まれます。Goランタイムは、GCがスタックをスキャンする際に使用する特別な命令やメタデータを持っています。
GC_CALL
は、GoのGCがスタックをアンワインドする際に遭遇する可能性のある、特定の関数呼び出しパターンを示す内部的なGC命令の一つです。この命令は、GCがスタックフレームを正しく解釈し、ポインタを識別するために必要な情報を提供します。
技術的詳細
このコミットは、主に以下の2つの領域で位置独立性を強化しています。
-
スタックアンワインドにおけるシンボル参照の修正:
- 以前のスタックアンワインドコードは、関数エントリポイントなどのシンボルを絶対アドレスで参照していた可能性があります。
- 動的に再配置されるバイナリでは、これらの絶対アドレスは実行時に変化するため、スタックアンワインドが失敗する原因となります。
- このコミットでは、
src/pkg/runtime/symtab.c
のdofunc
関数において、関数のエントリポイント (f->entry
) を設定する際に、シンボルの値 (sym->value
) に加えて、reloffset
というグローバルなオフセットを加算するように変更しています。これにより、実行時にバイナリがロードされるベースアドレスの変動を吸収し、f->entry
が常に正しい絶対アドレスを指すようにします。
-
GC_CALL
命令の相対オフセット化:src/cmd/ld/data.c
はGoリンカの一部であり、GC関連のメタデータを生成する役割を担っています。- 以前は、
GC_CALL
命令が参照するターゲット(おそらくGCサブルーチンや型情報)を絶対アドレスでエンコードしていました。 - このコミットでは、
gcaddsym
関数内でGC_CALL
命令に続くアドレスをaddaddrplus
からaddaddrpcrelplus
に変更しています。addaddrplus
は絶対アドレスを扱う関数です。addaddrpcrelplus
は、プログラムカウンタ(PC)からの相対オフセットを計算し、それをエンコードする関数です。
- これにより、
GC_CALL
命令が参照するアドレスが、バイナリのロードアドレスに依存しない相対オフセットとして格納されるようになります。 src/pkg/runtime/mgc0.c
のscanblock
関数では、GC_CALL
命令を処理する際に、以前はpc[2]
を直接ターゲットアドレスとして使用していましたが、変更後は(uintptr*)((byte*)pc + (uintptr)pc[2])
とすることで、現在のPCからの相対オフセットとして解釈し、正しいターゲットアドレスを計算するように修正されています。src/pkg/runtime/mgc0.h
では、GC_CALL
の引数がobjgc
(絶対ポインタ) からobjgcrel
(相対オフセット) に変更されたことを示すコメントが追加されています。
これらの変更により、Goバイナリは、動的にロードされる環境やASLRが有効なシステムにおいても、スタックアンワインドやガベージコレクションが正しく機能するようになります。
コアとなるコードの変更箇所
src/cmd/ld/data.c
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -951,7 +949,7 @@ gcaddsym(Sym *gc, Sym *s, int32 off)
//print("gcaddsym: %s %d %s\n", s->name, s->size, gotype->name);
adduintxx(gc, GC_CALL, PtrSize);
adduintxx(gc, off, PtrSize);
- addaddrplus(gc, decodetype_gc(gotype), 1*PtrSize);
+ addaddrpcrelplus(gc, decodetype_gc(gotype), 4*PtrSize);
} else {
//print("gcaddsym: %s %d <unknown type>\n", s->name, s->size);
for(a = -off&(PtrSize-1); a+PtrSize<=s->size; a+=PtrSize) {
src/pkg/runtime/mgc0.c
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -663,7 +663,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
// Stack push.
*stack_ptr-- = stack_top;
stack_top = (Frame){1, 0, stack_top.b + pc[1], pc+3 /*return address*/};
- pc = (uintptr*)pc[2]; // target of the CALL instruction
+ pc = (uintptr*)((byte*)pc + (uintptr)pc[2]); // target of the CALL instruction
continue;
case GC_MAP_PTR:
src/pkg/runtime/mgc0.h
--- a/src/pkg/runtime/mgc0.h
+++ b/src/pkg/runtime/mgc0.h
@@ -12,6 +12,7 @@
// Meaning of arguments:
// off Offset (in bytes) from the start of the current object
// objgc Pointer to GC info of an object
+// objgcrel Offset to GC info of an object
// len Length of an array
// elemsize Size (in bytes) of an element
// size Size (in bytes)
@@ -21,7 +22,7 @@ enum {
GC_APTR, // Pointer to an arbitrary object. Args: (off)
GC_ARRAY_START, // Start an array with a fixed length. Args: (off, len, elemsize)
GC_ARRAY_NEXT, // The next element of an array. Args: none
-\tGC_CALL, // Call a subroutine. Args: (off, objgc)
+\tGC_CALL, // Call a subroutine. Args: (off, objgcrel)
GC_MAP_PTR, // Go map. Args: (off, MapType*)
GC_STRING, // Go string. Args: (off)
GC_EFACE, // interface{}. Args: (off)
src/pkg/runtime/symtab.c
--- a/src/pkg/runtime/symtab.c
+++ b/src/pkg/runtime/symtab.c
@@ -93,6 +93,7 @@ walksymtab(void (*fn)(Sym*))
static Func *func;
static int32 nfunc;
+extern byte reloffset[];
static byte **fname;
static int32 nfname;
@@ -118,7 +119,7 @@ dofunc(Sym *sym)
}
f = &func[nfunc++];
f->name = runtime·gostringnocopy(sym->name);
- f->entry = sym->value;
+ f->entry = sym->value + (uint64)reloffset;
if(sym->symtype == 'L' || sym->symtype == 'l')
f->frame = -sizeof(uintptr);
break;
コアとなるコードの解説
src/cmd/ld/data.c
の変更
gcaddsym
関数は、GCが使用するシンボル情報を追加する役割を担っています。GC_CALL
命令を生成する際に、以前はaddaddrplus(gc, decodetype_gc(gotype), 1*PtrSize)
を使用して、gotype
のGC情報への絶対アドレスをエンコードしていました。- 変更後、
addaddrpcrelplus(gc, decodetype_gc(gotype), 4*PtrSize)
を使用するようになりました。addaddrpcrelplus
は、指定されたシンボル(ここではdecodetype_gc(gotype)
が返すGC型情報)への参照を、現在のコード位置からの相対オフセットとしてエンコードします。4*PtrSize
は、おそらくGC_CALL
命令自体のサイズと、それに続くオフセット値のサイズを考慮した、PCからのオフセット計算の基準点を示す値です。これにより、リンカが生成するGCメタデータが位置独立になります。
src/pkg/runtime/mgc0.c
の変更
scanblock
関数は、GCがメモリブロックをスキャンし、ポインタを識別する主要なロジックを含んでいます。GC_CALL
命令を処理する部分で、以前はpc = (uintptr*)pc[2];
となっていました。これは、pc[2]
が直接ターゲットの絶対アドレスを保持していることを前提としていました。- 変更後、
pc = (uintptr*)((byte*)pc + (uintptr)pc[2]);
となりました。- これは、
pc[2]
が現在のプログラムカウンタpc
からの相対オフセットとして解釈されることを意味します。 ((byte*)pc + (uintptr)pc[2])
は、現在の命令ポインタpc
に相対オフセットpc[2]
を加算することで、GC_CALL
がジャンプすべき実際のターゲットアドレスを計算しています。これにより、ランタイムがGC命令を解釈する際も位置独立性が保たれます。
- これは、
src/pkg/runtime/mgc0.h
の変更
- このヘッダーファイルは、GC命令の定義と引数の意味を記述しています。
GC_CALL
のコメントがCall a subroutine. Args: (off, objgc)
からCall a subroutine. Args: (off, objgcrel)
に変更されました。- これは、
GC_CALL
命令の2番目の引数が、絶対ポインタ (objgc
) ではなく、相対オフセット (objgcrel
) を表すようになったことを明確に示しています。
src/pkg/runtime/symtab.c
の変更
dofunc
関数は、シンボルテーブルを走査し、Goの関数に関する情報を構築する際に呼び出されます。- 関数のエントリポイント
f->entry
を設定する際に、以前はf->entry = sym->value;
と、シンボルの値(絶対アドレス)を直接使用していました。 - 変更後、
f->entry = sym->value + (uint64)reloffset;
となりました。reloffset
は、バイナリがロードされたベースアドレスからのオフセットを補償するためのグローバル変数(またはリンカによって設定される値)であると推測されます。- この加算により、
f->entry
は、バイナリがメモリ上のどこにロードされても、その関数の正しい絶対エントリポイントを指すようになります。これは、スタックアンワインドやデバッグツールが関数アドレスを正確に解決するために重要です。
これらの変更は、Goのバイナリがより動的な環境(特に共有ライブラリとして使用される場合やASLRが有効なシステム)で適切に動作するための基盤を強化しています。
関連リンク
- GoのIssueトラッカーやCL (Change List) システム: https://golang.org/cl/7248048
参考にした情報源リンク
- Position-Independent Code (PIC) の概念:
- 動的リンクと再配置:
- スタックアンワインド:
- Goのガベージコレクションに関する一般的な情報:
- Goの公式ドキュメントやブログ記事(特定のバージョンに依存しない一般的な概念)
- Goのソースコード(
src/pkg/runtime
ディレクトリ)
- Goのリンカに関する情報:
- Goのソースコード(
src/cmd/ld
ディレクトリ) - Goのコンパイラとリンカの内部に関する技術記事やプレゼンテーション
- Goのソースコード(
[インデックス 15085] ファイルの概要
このコミットは、Goランタイムとリンカ(cmd/ld
)におけるコードの**位置独立性(Position-Independent Code, PIC)**を向上させるための変更を導入しています。具体的には、スタックアンワインド処理とガベージコレクション(GC)命令の動作を、シンボルの動的な再配置に対応するように修正しています。
コミット
commit 44cf814d5002faf92bce26d21c3bf676d6a2c581
Author: Elias Naur <elias.naur@gmail.com>
Date: Fri Feb 1 11:24:49 2013 -0800
runtime, cmd/ld: make code more position-independent
Change the stack unwinding code to compensate for the dynamic
relocation of symbols.
Change the gc instruction GC_CALL to use a relative offset instead of
an absolute address.
R=golang-dev
CC=golang-dev
https://golang.org/cl/7248048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/44cf814d5002faf92bce26d21c3bf676d6a2c581
元コミット内容
このコミットの元の内容は以下の通りです。
- コミットメッセージ:
runtime, cmd/ld: make code more position-independent
- 詳細:
- スタックアンワインドコードを、シンボルの動的な再配置を補償するように変更。
- GC命令
GC_CALL
を絶対アドレスの代わりに相対オフセットを使用するように変更。
変更の背景
この変更の背景には、Goプログラムがより柔軟なメモリ配置で実行できるようにするという目的があります。特に、共有ライブラリや動的にロードされるモジュールにおいて、コードがロードされるメモリ上のアドレスが実行時まで確定しない場合があります。このような環境で正しく動作するためには、コードが絶対アドレスに依存せず、どこにロードされても機能する位置独立性を持つ必要があります。
Goの初期のバージョンでは、一部の内部処理(特にスタックアンワインドやGC関連のメタデータ)が絶対アドレスに依存している可能性がありました。これにより、特定の環境(例えば、ASLR (Address Space Layout Randomization) が有効なシステムや、動的リンクを使用するケース)での互換性やセキュリティが制限されることがありました。
このコミットは、これらの依存関係を解消し、Goバイナリの移植性と堅牢性を高めることを目指しています。
前提知識の解説
1. 位置独立コード (Position-Independent Code, PIC)
PICは、メモリ上の任意のアドレスにロードされても正しく実行できる機械語コードのことです。これは、コード内のアドレス参照を絶対アドレスではなく、プログラムカウンタ(PC)からの相対オフセットや、グローバルオフセットテーブル(GOT)/プロシージャリンケージテーブル(PLT)のような間接的な参照メカニズムを使用することで実現されます。
- 絶対アドレス参照: コードが特定の固定メモリ位置を直接参照する場合、そのコードは位置独立ではありません。ロードアドレスが変わると、参照が不正になります。
- 相対アドレス参照: コードが現在のプログラムカウンタからのオフセットでアドレスを参照する場合、ロードアドレスが変わってもオフセットは変わらないため、コードは位置独立になります。
PICは、共有ライブラリ(DLLや.soファイル)の作成に不可欠です。これにより、複数のプロセスが同じライブラリの単一のコピーをメモリにロードし、異なるアドレス空間にマッピングしても正しく動作できます。
2. 動的な再配置 (Dynamic Relocation)
プログラムが実行される前に、リンカはコード内のシンボル参照(関数や変数のアドレス)を解決します。静的リンクの場合、これらのアドレスはビルド時に固定されます。しかし、動的リンクの場合、共有ライブラリ内のシンボルの実際のアドレスは、プログラムがロードされるまで決定されません。
動的な再配置は、プログラムのロード時に、ローダーが共有ライブラリ内のシンボル参照を、そのライブラリが実際にロードされたメモリ上のアドレスに合わせて修正するプロセスです。これにより、プログラムは実行時に必要な共有ライブラリの関数やデータにアクセスできるようになります。
3. スタックアンワインド (Stack Unwinding)
スタックアンワインドは、プログラムの実行中に、現在の関数呼び出しから呼び出し元の関数へとスタックフレームを遡っていくプロセスです。これは主に以下の目的で使用されます。
- 例外処理: 例外が発生した際に、適切な例外ハンドラを見つけるためにスタックを遡ります。
- デバッグ: コールスタック(関数呼び出しの履歴)を表示するために使用されます。
- プロファイリング: 実行パスを追跡し、パフォーマンスボトルネックを特定するために使用されます。
- ガベージコレクション: GCがスタック上のポインタをスキャンし、到達可能なオブジェクトを特定するために使用されることがあります。
スタックアンワインドが正しく機能するためには、各スタックフレームのサイズ、リターンアドレス、および保存されたレジスタの値に関する正確な情報が必要です。これらの情報は、コンパイラによって生成されるメタデータ(アンワインド情報)に格納されます。
4. ガベージコレクション (Garbage Collection, GC) と GC_CALL
命令
Goは自動メモリ管理(ガベージコレクション)を採用しています。GCは、プログラムが動的に割り当てたメモリのうち、もはや到達不可能になった(使用されていない)メモリを自動的に解放するプロセスです。
GoのGCは、オブジェクトグラフを辿ることで到達可能なオブジェクトを特定します。このプロセスには、スタック上のポインタをスキャンすることも含まれます。Goランタイムは、GCがスタックをスキャンする際に使用する特別な命令やメタデータを持っています。
GC_CALL
は、GoのGCがスタックをアンワインドする際に遭遇する可能性のある、特定の関数呼び出しパターンを示す内部的なGC命令の一つです。この命令は、GCがスタックフレームを正しく解釈し、ポインタを識別するために必要な情報を提供します。
技術的詳細
このコミットは、主に以下の2つの領域で位置独立性を強化しています。
-
スタックアンワインドにおけるシンボル参照の修正:
- 以前のスタックアンワインドコードは、関数エントリポイントなどのシンボルを絶対アドレスで参照していた可能性があります。
- 動的に再配置されるバイナリでは、これらの絶対アドレスは実行時に変化するため、スタックアンワインドが失敗する原因となります。
- このコミットでは、
src/pkg/runtime/symtab.c
のdofunc
関数において、関数のエントリポイント (f->entry
) を設定する際に、シンボルの値 (sym->value
) に加えて、reloffset
というグローバルなオフセットを加算するように変更しています。これにより、実行時にバイナリがロードされるベースアドレスの変動を吸収し、f->entry
が常に正しい絶対アドレスを指すようにします。
-
GC_CALL
命令の相対オフセット化:src/cmd/ld/data.c
はGoリンカの一部であり、GC関連のメタデータを生成する役割を担っています。- 以前は、
GC_CALL
命令が参照するターゲット(おそらくGCサブルーチンや型情報)を絶対アドレスでエンコードしていました。 - このコミットでは、
gcaddsym
関数内でGC_CALL
命令に続くアドレスをaddaddrplus
からaddaddrpcrelplus
に変更しています。addaddrplus
は絶対アドレスを扱う関数です。addaddrpcrelplus
は、プログラムカウンタ(PC)からの相対オフセットを計算し、それをエンコードする関数です。
- これにより、
GC_CALL
命令が参照するアドレスが、バイナリのロードアドレスに依存しない相対オフセットとして格納されるようになります。 src/pkg/runtime/mgc0.c
のscanblock
関数では、GC_CALL
命令を処理する際に、以前はpc[2]
を直接ターゲットアドレスとして使用していましたが、変更後は(uintptr*)((byte*)pc + (uintptr)pc[2])
とすることで、現在のPCからの相対オフセットとして解釈し、正しいターゲットアドレスを計算するように修正されています。src/pkg/runtime/mgc0.h
では、GC_CALL
の引数がobjgc
(絶対ポインタ) からobjgcrel
(相対オフセット) に変更されたことを示すコメントが追加されています。
これらの変更により、Goバイナリは、動的にロードされる環境やASLRが有効なシステムにおいても、スタックアンワインドやガベージコレクションが正しく機能するようになります。
コアとなるコードの変更箇所
src/cmd/ld/data.c
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -951,7 +949,7 @@ gcaddsym(Sym *gc, Sym *s, int32 off)
//print("gcaddsym: %s %d %s\n", s->name, s->size, gotype->name);
adduintxx(gc, GC_CALL, PtrSize);
adduintxx(gc, off, PtrSize);
- addaddrplus(gc, decodetype_gc(gotype), 1*PtrSize);
+ addaddrpcrelplus(gc, decodetype_gc(gotype), 4*PtrSize);
} else {
//print("gcaddsym: %s %d <unknown type>\n", s->name, s->size);
for(a = -off&(PtrSize-1); a+PtrSize<=s->size; a+=PtrSize) {
src/pkg/runtime/mgc0.c
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -663,7 +663,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
// Stack push.
*stack_ptr-- = stack_top;
stack_top = (Frame){1, 0, stack_top.b + pc[1], pc+3 /*return address*/};
- pc = (uintptr*)pc[2]; // target of the CALL instruction
+ pc = (uintptr*)((byte*)pc + (uintptr)pc[2]); // target of the CALL instruction
continue;
case GC_MAP_PTR:
src/pkg/runtime/mgc0.h
--- a/src/pkg/runtime/mgc0.h
+++ b/src/pkg/runtime/mgc0.h
@@ -12,6 +12,7 @@
// Meaning of arguments:
// off Offset (in bytes) from the start of the current object
// objgc Pointer to GC info of an object
+// objgcrel Offset to GC info of an object
// len Length of an array
// elemsize Size (in bytes) of an element
// size Size (in bytes)
@@ -21,7 +22,7 @@ enum {
GC_APTR, // Pointer to an arbitrary object. Args: (off)
GC_ARRAY_START, // Start an array with a fixed length. Args: (off, len, elemsize)
GC_ARRAY_NEXT, // The next element of an array. Args: none
-\tGC_CALL, // Call a subroutine. Args: (off, objgc)
+\tGC_CALL, // Call a subroutine. Args: (off, objgcrel)
GC_MAP_PTR, // Go map. Args: (off, MapType*)
GC_STRING, // Go string. Args: (off)
GC_EFACE, // interface{}. Args: (off)
src/pkg/runtime/symtab.c
--- a/src/pkg/runtime/symtab.c
+++ b/src/pkg/runtime/symtab.c
@@ -93,6 +93,7 @@ walksymtab(void (*fn)(Sym*))
static Func *func;
static int32 nfunc;
+extern byte reloffset[];
static byte **fname;
static int32 nfname;
@@ -118,7 +119,7 @@ dofunc(Sym *sym)
}
f = &func[nfunc++];
f->name = runtime·gostringnocopy(sym->name);
- f->entry = sym->value;
+ f->entry = sym->value + (uint64)reloffset;
if(sym->symtype == 'L' || sym->symtype == 'l')
f->frame = -sizeof(uintptr);
break;
コアとなるコードの解説
src/cmd/ld/data.c
の変更
gcaddsym
関数は、GCが使用するシンボル情報を追加する役割を担っています。GC_CALL
命令を生成する際に、以前はaddaddrplus(gc, decodetype_gc(gotype), 1*PtrSize)
を使用して、gotype
のGC情報への絶対アドレスをエンコードしていました。- 変更後、
addaddrpcrelplus(gc, decodetype_gc(gotype), 4*PtrSize)
を使用するようになりました。addaddrpcrelplus
は、指定されたシンボル(ここではdecodetype_gc(gotype)
が返すGC型情報)への参照を、現在のコード位置からの相対オフセットとしてエンコードします。4*PtrSize
は、おそらくGC_CALL
命令自体のサイズと、それに続くオフセット値のサイズを考慮した、PCからのオフセット計算の基準点を示す値です。これにより、リンカが生成するGCメタデータが位置独立になります。
src/pkg/runtime/mgc0.c
の変更
scanblock
関数は、GCがメモリブロックをスキャンし、ポインタを識別する主要なロジックを含んでいます。GC_CALL
命令を処理する部分で、以前はpc = (uintptr*)pc[2];
となっていました。これは、pc[2]
が直接ターゲットの絶対アドレスを保持していることを前提としていました。- 変更後、
pc = (uintptr*)((byte*)pc + (uintptr)pc[2]);
となりました。- これは、
pc[2]
が現在のプログラムカウンタpc
からの相対オフセットとして解釈されることを意味します。 ((byte*)pc + (uintptr)pc[2])
は、現在の命令ポインタpc
に相対オフセットpc[2]
を加算することで、GC_CALL
がジャンプすべき実際のターゲットアドレスを計算しています。これにより、ランタイムがGC命令を解釈する際も位置独立性が保たれます。
- これは、
src/pkg/runtime/mgc0.h
の変更
- このヘッダーファイルは、GC命令の定義と引数の意味を記述しています。
GC_CALL
のコメントがCall a subroutine. Args: (off, objgc)
からCall a subroutine. Args: (off, objgcrel)
に変更されました。- これは、
GC_CALL
命令の2番目の引数が、絶対ポインタ (objgc
) ではなく、相対オフセット (objgcrel
) を表すようになったことを明確に示しています。
src/pkg/runtime/symtab.c
の変更
dofunc
関数は、シンボルテーブルを走査し、Goの関数に関する情報を構築する際に呼び出されます。- 関数のエントリポイント
f->entry
を設定する際に、以前はf->entry = sym->value;
と、シンボルの値(絶対アドレス)を直接使用していました。 - 変更後、
f->entry = sym->value + (uint64)reloffset;
となりました。reloffset
は、バイナリがロードされたベースアドレスからのオフセットを補償するためのグローバル変数(またはリンカによって設定される値)であると推測されます。- この加算により、
f->entry
は、バイナリがメモリ上のどこにロードされても、その関数の正しい絶対エントリポイントを指すようになります。これは、スタックアンワインドやデバッグツールが関数アドレスを正確に解決するために重要です。
これらの変更は、Goのバイナリがより動的な環境(特に共有ライブラリとして使用される場合やASLRが有効なシステム)で適切に動作するための基盤を強化しています。
関連リンク
- GoのIssueトラッカーやCL (Change List) システム: https://golang.org/cl/7248048
参考にした情報源リンク
- Position-Independent Code (PIC) の概念:
- 動的リンクと再配置:
- スタックアンワインド:
- Goのガベージコレクションに関する一般的な情報:
- Goの公式ドキュメントやブログ記事(特定のバージョンに依存しない一般的な概念)
- Goのソースコード(
src/pkg/runtime
ディレクトリ)
- Goのリンカに関する情報:
- Goのソースコード(
src/cmd/ld
ディレクトリ) - Goのコンパイラとリンカの内部に関する技術記事やプレゼンテーション
- Goのソースコード(