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

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

このコミットは、Go言語のツールチェインの一部であるliblinkライブラリ内の、実行されることのないコードを削除するものです。具体的には、src/liblink/asm6.csrc/liblink/asm8.cという2つのファイルから、外部リンキングモード(LinkExternal)とスレッドローカルストレージ(TLS)に関連する不要なコードが削除されました。これらのファイルはコンパイラ/アセンブラによって使用されるものであり、リンカによって呼び出されることはないため、特定の条件分岐内のコードが到達不能であったことが判明しました。

コミット

commit f4ecfaa442ffdcab83bf63b2d40c67290d13f618
Author: Ian Lance Taylor <iant@golang.org>
Date:   Mon Apr 7 22:12:26 2014 -0700

    liblink: remove code that is never executed
    
    This code tests linkmode == LinkExternal but is only invoked
    by the compiler/assembler, not the linker.
    
    Update #7164
    
    LGTM=rsc
    R=rsc, dave
    CC=golang-codereviews
    https://golang.org/cl/85080043

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

https://github.com/golang/go/commit/f4ecfaa442ffdcab83bf63b2d40c67290d13f618

元コミット内容

--- a/src/liblink/asm6.c
+++ b/src/liblink/asm6.c
@@ -2415,21 +2415,6 @@ putrelv:
  		r = addrel(ctxt->cursym);
  		*r = rel;
  		r->off = ctxt->curp->pc + ctxt->andptr - ctxt->and;
-	} else if(ctxt->iself && ctxt->linkmode == LinkExternal && a->type == D_INDIR+D_FS
-		&& ctxt->headtype != Hopenbsd) {
-		Reloc *r;
-		LSym *s;
-		
-		r = addrel(ctxt->cursym);
-		r->off = ctxt->curp->pc + ctxt->andptr - ctxt->and;
-		r->add = a->offset - ctxt->tlsoffset;
-		r->xadd = r->add;
-		r->siz = 4;
-		r->type = D_TLS;
-		s = linklookup(ctxt, "runtime.tlsgm", 0);
-		r->sym = s;
-		r->xsym = s;
-		v = 0;
  	}
  		
  	put4(ctxt, v);
diff --git a/src/liblink/asm8.c b/src/liblink/asm8.c
index 2e4bc709e8..4d209cbdfa 100644
--- a/src/liblink/asm8.c
+++ b/src/liblink/asm8.c
@@ -1728,14 +1728,6 @@ vaddr(Link *ctxt, Addr *a, Reloc *r)\n 	return v;\n }\n \n-static int\n-istls(Link *ctxt, Addr *a)\n-{\n-\tif(ctxt->headtype == Hlinux || ctxt->headtype == Hnacl)\n-\t\treturn a->index == D_GS;\n-\treturn a->type == D_INDIR+D_GS;\n-}\n-\n static void\n asmand(Link *ctxt, Addr *a, int r)\n {\n@@ -1857,20 +1849,6 @@ putrelv:\n  		r = addrel(ctxt->cursym);\n  		*r = rel;\n  		r->off = ctxt->curp->pc + ctxt->andptr - ctxt->and;\n-	} else if(ctxt->iself && ctxt->linkmode == LinkExternal && istls(ctxt, a) && ctxt->headtype != Hopenbsd) {\n-\t\tReloc *r;\n-\t\tLSym *s;\n-\n-\t\tr = addrel(ctxt->cursym);\n-\t\tr->off = ctxt->curp->pc + ctxt->andptr - ctxt->and;\n-\t\tr->add = a->offset - ctxt->tlsoffset;\n-\t\tr->xadd = r->add;\n-\t\tr->siz = 4;\n-\t\tr->type = D_TLS;\n-\t\ts = linklookup(ctxt, "runtime.tlsgm", 0);\n-\t\tr->sym = s;\n-\t\tr->xsym = s;\n-\t\tv = 0;\n \t}\n \n \tput4(ctxt, v);\n```

## 変更の背景

このコミットの背景には、Go言語のビルドプロセスにおけるコードの役割分担の明確化と最適化があります。Goのツールチェインは、コンパイラ、アセンブラ、リンカといった複数のコンポーネントで構成されています。`src/liblink/asm6.c`と`src/liblink/asm8.c`は、それぞれ386アーキテクチャとAMD64アーキテクチャ向けのアセンブリコード生成に関連するファイルであり、主にコンパイラやアセンブラによって利用されます。

コミットメッセージによると、これらのファイル内に`linkmode == LinkExternal`という条件をテストするコードブロックが存在していました。`LinkExternal`は、Goのリンカが外部のCリンカ(例: `gcc`)を利用して最終的な実行ファイルを生成するモードを指します。しかし、このコードブロックはコンパイラ/アセンブラのコンテキストでしか呼び出されず、リンカのコンテキストでは呼び出されないため、`linkmode == LinkExternal`という条件が真になることはありませんでした。

したがって、このコードブロックは決して実行されることのない「デッドコード」であり、コードベースの複雑性を増し、メンテナンスの負担となるだけでなく、潜在的なバグの原因にもなりかねません。このコミットは、このような不要なコードを特定し、削除することで、コードベースの健全性を向上させることを目的としています。

## 前提知識の解説

このコミットを理解するためには、以下のGo言語のビルドプロセスと関連技術に関する知識が必要です。

*   **Go言語のビルドプロセス**:
    Goのソースコードは、まずGoコンパイラによってアセンブリコードに変換されます。次に、Goアセンブラがこのアセンブリコードをオブジェクトファイルに変換します。最後に、Goリンカがこれらのオブジェクトファイルと必要なライブラリを結合して、実行可能ファイルを生成します。このプロセスは、Goのツールチェインによって自動的に管理されます。

*   **`liblink`**:
    `liblink`は、Goのツールチェイン内で使用される内部ライブラリで、主にアセンブリコードの生成とオブジェクトファイルの処理を担当します。`asm6.c`と`asm8.c`は、この`liblink`の一部であり、それぞれ386(x86)とAMD64(x86-64)アーキテクチャ向けのアセンブリ命令の生成や、シンボル、リロケーションの処理を行います。

*   **`LinkExternal` (外部リンキングモード)**:
    Goのリンカは、GoのコードとC/C++などの他の言語で書かれたコードをリンクする際に、外部リンカ(通常は`gcc`などのシステムリンカ)を利用するモードを持っています。これが`LinkExternal`モードです。このモードでは、Goリンカはオブジェクトファイルを生成し、その後の最終的なリンク処理を外部リンカに委ねます。Goの標準リンカ(内部リンカ)を使用する場合は、`LinkInternal`モードとなります。

*   **TLS (Thread Local Storage)**:
    TLSは、各スレッドが独自のデータコピーを持つことを可能にするメカニズムです。これにより、グローバル変数や静的変数をスレッド間で共有することなく、スレッドごとに独立した状態を保持できます。TLSは、特にマルチスレッドプログラミングにおいて重要であり、OSやアーキテクチャによってその実装方法が異なります。Goのランタイムも、特定の内部データのためにTLSを利用することがあります。

*   **リロケーション (Relocation)**:
    リロケーションとは、コンパイル時やリンク時に、プログラム内のアドレス参照を修正するプロセスです。コンパイラやアセンブラは、コード内のシンボル(変数や関数など)のアドレスを仮のアドレスで生成しますが、最終的なアドレスはリンカが決定します。リロケーションエントリは、リンカがこれらの仮のアドレスを実際のメモリ上のアドレスに修正するために必要な情報を含んでいます。`D_TLS`はTLSに関連するリロケーションタイプの一つです。

*   **`ctxt` (Context)**:
    `liblink`のコードでは、`Link *ctxt`という引数が頻繁に登場します。これはリンキングコンテキストを表す構造体で、現在のリンキング処理に関する様々な情報(例: リンクモード、ターゲットアーキテクチャ、シンボルテーブルなど)を保持しています。

## 技術的詳細

このコミットで削除されたコードは、`src/liblink/asm6.c`と`src/liblink/asm8.c`内の`putrelv`関数の中に存在していました。`putrelv`関数は、アセンブリコードを生成する際に、リロケーション情報を処理し、最終的なバイナリに書き込む役割を担っています。

削除されたコードブロックは、以下の条件で実行されるように記述されていました。

```c
else if(ctxt->iself && ctxt->linkmode == LinkExternal && a->type == D_INDIR+D_FS
    && ctxt->headtype != Hopenbsd) {
    // ... TLS関連のリロケーション処理 ...
}

および

else if(ctxt->iself && ctxt->linkmode == LinkExternal && istls(ctxt, a) && ctxt->headtype != Hopenbsd) {
    // ... TLS関連のリロケーション処理 ...
}

ここで重要なのは、ctxt->linkmode == LinkExternalという条件です。前述の通り、asm6.casm8.cはコンパイラ/アセンブラのフェーズで動作します。このフェーズでは、まだ最終的なリンキングモードが決定されておらず、特に外部リンカを使用するかどうかの判断はリンカの責任範囲です。したがって、コンパイラ/アセンブラのコンテキストでは、ctxt->linkmodeLinkExternalになることはありません。

また、asm8.cから削除されたistls関数も、TLSに関連するアドレスタイプをチェックするためのヘルパー関数でした。この関数も、LinkExternalモードでのTLS処理に関連していたため、デッドコードとして削除されました。

この変更は、コードの実行パスを正確に分析し、到達不能なコードを特定することで、コードベースの品質を向上させる典型的な例です。これにより、コンパイラ/アセンブラのコードがよりシンプルになり、将来的な変更やデバッグが容易になります。

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

src/liblink/asm6.c

putrelv関数内の以下のelse ifブロックが完全に削除されました。

-	} else if(ctxt->iself && ctxt->linkmode == LinkExternal && a->type == D_INDIR+D_FS
-		&& ctxt->headtype != Hopenbsd) {
-		Reloc *r;
-		LSym *s;
-		
-		r = addrel(ctxt->cursym);
-		r->off = ctxt->curp->pc + ctxt->andptr - ctxt->and;
-		r->add = a->offset - ctxt->tlsoffset;
-		r->xadd = r->add;
-		r->siz = 4;
-		r->type = D_TLS;
-		s = linklookup(ctxt, "runtime.tlsgm", 0);
-		r->sym = s;
-		r->xsym = s;
-		v = 0;
- 	}

このブロックは、ELF形式のバイナリで外部リンキングモードが有効な場合に、D_FSレジスタ(TLSベースアドレスを指すことが多い)に関連するTLSリロケーションを処理しようとしていました。

src/liblink/asm8.c

  1. istls関数が完全に削除されました。

    -static int
    -istls(Link *ctxt, Addr *a)
    -{
    -	if(ctxt->headtype == Hlinux || ctxt->headtype == Hnacl)
    -		return a->index == D_GS;
    -	return a->type == D_INDIR+D_GS;
    -}
    

    この関数は、アドレスがTLSに関連するかどうかをチェックするためのものでした。

  2. putrelv関数内の以下のelse ifブロックが完全に削除されました。

    -	} else if(ctxt->iself && ctxt->linkmode == LinkExternal && istls(ctxt, a) && ctxt->headtype != Hopenbsd) {
    -		Reloc *r;
    -		LSym *s;
    -
    -		r = addrel(ctxt->cursym);
    -		r->off = ctxt->curp->pc + ctxt->andptr - ctxt->and;
    -		r->add = a->offset - ctxt->tlsoffset;
    -		r->xadd = r->add;
    -		r->siz = 4;
    -		r->type = D_TLS;
    -		s = linklookup(ctxt, "runtime.tlsgm", 0);
    -		r->sym = s;
    -		r->xsym = s;
    -		v = 0;
    - 	}
    

    このブロックも、ELF形式のバイナリで外部リンキングモードが有効な場合に、TLSに関連するリロケーションを処理しようとしていました。asm6.cの削除箇所と類似していますが、istls関数を使用している点が異なります。

コアとなるコードの解説

削除されたコードは、Goのコンパイラ/アセンブラが生成するオブジェクトファイル内のリロケーションエントリを処理するロジックの一部でした。特に、D_TLSタイプのリロケーションは、スレッドローカルストレージへのアクセスを解決するためにリンカが必要とする情報です。

しかし、これらのコードブロックがlinkmode == LinkExternalという条件に依存していたことが問題でした。Goのビルドプロセスにおいて、asm6.casm8.cが関与するフェーズ(コンパイル/アセンブル)では、まだ外部リンカを使用するかどうかの決定は行われていません。この決定は、より後のリンカフェーズで行われます。

したがって、コンパイラ/アセンブラが動作している間は、ctxt->linkmodeは決してLinkExternalにはなりません。このため、これらのelse ifブロック内のコードは論理的に到達不可能であり、実行されることはありませんでした。

コードの削除は、以下の理由で正当化されます。

  1. デッドコードの排除: 実行されないコードは、コードベースの肥大化を招き、理解を困難にします。
  2. メンテナンス性の向上: 不要なコードがなくなることで、将来の機能追加やバグ修正の際に、開発者が考慮すべき範囲が狭まります。
  3. 潜在的なバグの回避: 実行されないコードであっても、その中に論理的な誤りや依存関係の不整合が含まれている場合、将来的にコードパスが変更された際に予期せぬ問題を引き起こす可能性があります。

このコミットは、Goツールチェインの内部構造に対する深い理解に基づいた、クリーンアップと最適化の一例と言えます。

関連リンク

  • Go言語の公式ドキュメント: https://golang.org/doc/
  • Goのツールチェインに関する一般的な情報: https://go.dev/doc/
  • Goのリンカに関する議論(一般的な情報源): Goのリンカは非常に複雑なため、特定の公開ドキュメントは少ないですが、Goのソースコード自体が最も正確な情報源です。

参考にした情報源リンク

  • Go言語のソースコード (特に src/cmd/link および src/liblink ディレクトリ)
  • Go言語のコミット履歴とコードレビューシステム (Gerrit): https://go.googlesource.com/go/+/refs/heads/master
  • GoのIssue Tracker (ただし、#7164は公開情報が見つからなかったため、このコミットメッセージからの推測に基づいています): https://github.com/golang/go/issues
  • ELF (Executable and Linkable Format) の仕様に関する一般的な情報源 (TLSやリロケーションの理解のため)
  • スレッドローカルストレージ (TLS) に関する一般的な情報源
  • Goのビルドプロセスに関する技術ブログや記事 (一般的な理解のため)