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

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

このコミットは、Goコンパイラのリンカ(cmd/ld)におけるOpenBSDのビルド問題を修正することを目的としています。具体的には、OpenBSD環境でのスレッドローカルストレージ(TLS)の取り扱いに関するリンカの挙動を調整し、外部リンカを使用する際のTLS関連のリロケーション処理からOpenBSDを除外することで、ビルドエラーを回避します。

コミット

commit ed890e7414c1aa7040a5ff6bef6dffa2767b6df5
Author: Russ Cox <rsc@golang.org>
Date:   Tue Apr 15 15:52:23 2014 -0400

    cmd/ld: attempt at fixing openbsd build
    
    OpenBSD is excluded from all the usual thread-local storage
    code, not just emitting the tbss section in the external link .o
    but emitting a PT_TLS section in an internally-linked executable.
    I assume it just has no proper TLS support. Exclude it here too.
    
    TBR=iant
    CC=golang-codereviews
    https://golang.org/cl/87900045

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

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

元コミット内容

cmd/ld: attempt at fixing openbsd build

OpenBSD is excluded from all the usual thread-local storage
code, not just emitting the tbss section in the external link .o
but emitting a PT_TLS section in an internally-linked executable.
I assume it just has no proper TLS support. Exclude it here too.

TBR=iant
CC=golang-codereviews
https://golang.org/cl/87900045

変更の背景

このコミットの背景には、OpenBSDにおけるスレッドローカルストレージ(TLS)のサポートの特殊性があります。Goのリンカ(cmd/ld)は、実行ファイルを生成する際に、スレッドローカルストレージに関連するセクション(例: .tbssセクションやPT_TLSセグメント)を適切に処理する必要があります。しかし、OpenBSDは他の多くのUnix系OSとは異なり、標準的なTLSのメカニズムを完全にサポートしていないか、あるいは異なる方法で実装しているため、Goのリンカが生成するTLS関連の構造がOpenBSDのシステムと互換性がないという問題が発生していました。

具体的には、Goのリンカが外部リンカ(linkmode == LinkExternal)を使用し、かつELF形式の実行ファイル(iself)を生成する際に、R_TLS_LE(TLS Local Executable)やR_TLS_IE(TLS Initial Executable)といったTLS関連のリロケーションを処理しようとすると、OpenBSDでは問題が生じていました。これらのリロケーションは、通常、スレッドローカル変数へのアクセスを最適化するために使用されますが、OpenBSDのTLSサポートの欠如または違いにより、リンカがこれらのリロケーションを処理しようとするとビルドが失敗するか、正しくない実行ファイルが生成される可能性がありました。

このコミットは、OpenBSDが「通常のTLSコードから除外されている」という認識に基づいています。つまり、OpenBSDは、外部リンカが生成する.oファイル内の.tbssセクションの出力だけでなく、内部リンカが生成する実行ファイル内のPT_TLSセクションの出力においても、TLS関連の処理から除外されるべきであるという判断がなされました。この変更は、OpenBSDでのGoのビルドを成功させるための試みとして行われました。

前提知識の解説

スレッドローカルストレージ (TLS: Thread-Local Storage)

TLSは、マルチスレッドプログラミングにおいて、各スレッドがそれぞれ独立した変数を持つことを可能にするメカニズムです。通常、グローバル変数や静的変数はプロセス内のすべてのスレッドで共有されますが、TLSを使用すると、同じ名前の変数であっても、各スレッドが独自のインスタンスを持ち、他のスレッドから独立してその値を読み書きできます。これにより、スレッド間の競合状態を避け、コードの並行性を高めることができます。

TLS変数は、コンパイル時やリンク時に特別な処理が必要となり、実行時にはスレッドIDに基づいて適切なメモリ領域が割り当てられます。

外部リンカと内部リンカ

Goのビルドシステムでは、実行ファイルを生成する際に「内部リンカ」と「外部リンカ」のいずれかを使用できます。

  • 内部リンカ: Go言語で書かれたリンカであり、Goのツールチェインの一部として提供されます。Goのコードを直接リンクし、Goランタイムの特性を最大限に活かすように設計されています。
  • 外部リンカ: システムにインストールされているgccなどのCコンパイラに付属するリンカ(例: ld)を指します。Cgoを使用する場合や、特定のシステムライブラリにリンクする必要がある場合など、Goの内部リンカでは対応できない複雑なリンク要件がある場合に利用されます。linkmode == LinkExternalは、この外部リンカを使用する設定を意味します。

ELF (Executable and Linkable Format)

ELFは、Unix系OS(Linux、BSD系など)で広く使用されている実行ファイル、オブジェクトファイル、共有ライブラリの標準的なファイル形式です。プログラムのコード、データ、シンボル、リロケーション情報などがELF形式で格納されます。Goのリンカは、これらのシステムで実行可能なELFファイルを生成します。iselfは、ターゲットシステムがELF形式を使用しているかどうかを示すフラグです。

Goリンカ (cmd/ld)

cmd/ldは、Go言語のビルドツールチェインの一部であるリンカです。Goのソースコードから生成されたオブジェクトファイルを結合し、実行可能なバイナリやライブラリを生成する役割を担います。このリンカは、シンボルの解決、リロケーションの適用、セクションの配置など、リンク処理のすべての側面を管理します。

R_TLS_LE および R_TLS_IE リロケーション

ELF形式において、リロケーションは、プログラムがロードされる際にシンボル参照を正しいメモリ位置に解決するための情報です。TLSに関連するリロケーションタイプにはいくつかあり、このコミットで言及されているのは以下の2つです。

  • R_TLS_LE (TLS Local Executable): スレッドローカル変数へのアクセスに使用されるリロケーションタイプの一つです。これは、実行ファイル内で定義されたTLS変数にアクセスする際に、その変数がスレッドのTLSブロック内のどこにあるかを解決するために使用されます。
  • R_TLS_IE (TLS Initial Executable): これもTLS変数へのアクセスに関連するリロケーションタイプですが、通常は共有ライブラリからTLS変数にアクセスする場合に用いられます。実行ファイルがロードされる際に、共有ライブラリ内のTLS変数のアドレスを解決するために使用されます。

これらのリロケーションは、TLS変数のアドレスを効率的に計算するために、コンパイラやリンカによって生成されます。

技術的詳細

このコミットの技術的な核心は、GoのリンカがTLS関連のリロケーション(R_TLS_LER_TLS_IE)を処理する際に、OpenBSDプラットフォームを特別扱いすることにあります。

Goのリンカは、relocsym関数内で様々なリロケーションタイプを処理します。TLS関連のリロケーションであるR_TLS_LER_TLS_IEの場合、通常は外部リンカ(linkmode == LinkExternal)を使用し、かつターゲットがELF形式(iself)である場合に、特定の処理(r->done = 0; r->sym = ctxt->gmsym; r->xsym = ctxt->gmsym;など)を行います。この処理は、Goランタイムのruntime.tlsgシンボル(GoルーチンのTLS領域へのポインタ)を介してTLS変数にアクセスするための準備です。

しかし、OpenBSDはTLSのサポートが他のシステムと異なるため、この標準的な処理が問題を引き起こしていました。コミットメッセージにあるように、「OpenBSDは通常のTLSコードから除外されている」という状況です。これは、OpenBSDが.tbssセクション(スレッドローカルな初期化されていないデータ)の出力や、内部リンカが生成する実行ファイル内のPT_TLSセグメント(TLS領域を記述するプログラムヘッダ)の出力においても、他のシステムとは異なる挙動を示すことを意味します。

この問題を解決するため、変更は既存の条件式に&& HEADTYPE != Hopenbsdという条件を追加しています。

  • HEADTYPEは、GoのビルドシステムでターゲットOSの実行ファイル形式を識別するために使用される定数です。HopenbsdはOpenBSDプラットフォームを指します。

この追加により、linkmode == LinkExternal && iselfという条件が真であっても、ターゲットOSがOpenBSDである場合は、TLS関連のリロケーションに対する特別な処理を行わないようにリンカの挙動が変更されます。これにより、OpenBSDのTLSサポートの特殊性によって引き起こされるビルドエラーや実行時の問題を回避し、OpenBSD上でのGoプログラムのビルドを成功させることが可能になります。

この変更は、OpenBSDがTLSを全くサポートしていないか、あるいはGoのリンカが期待するような標準的な方法でサポートしていないという仮定に基づいています。そのため、GoのリンカはOpenBSDに対しては、TLS関連の最適化や特別な処理を適用せず、より基本的な方法でリンク処理を進めることになります。

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

src/cmd/ld/data.c ファイルにおいて、以下の2箇所が変更されています。

--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -184,7 +184,7 @@ relocsym(LSym *s)
 			o = r->add;
 			break;
 		case R_TLS_LE:
-			if(linkmode == LinkExternal && iself) {
+			if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd) {
 				r->done = 0;
 				r->sym = ctxt->gmsym;
 				r->xsym = ctxt->gmsym;
@@ -198,7 +198,7 @@ relocsym(LSym *s)
 			break;
 
 		case R_TLS_IE:
-			if(linkmode == LinkExternal && iself) {
+			if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd) {
 				r->done = 0;
 				r->sym = ctxt->gmsym;
 				r->xsym = ctxt->gmsym;

コアとなるコードの解説

変更は、relocsym関数内のR_TLS_LER_TLS_IEという2つのcaseブロックにあります。

元々のコードでは、以下の条件が真の場合にTLS関連のリロケーション処理が行われていました。

if(linkmode == LinkExternal && iself) {
    // TLS関連のリロケーション処理
}

この条件は、「外部リンカを使用しており(linkmode == LinkExternal)、かつターゲットがELF形式である(iself)」ことを意味します。

このコミットでは、この条件に&& HEADTYPE != Hopenbsdという追加の条件が加えられました。

if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd) {
    // TLS関連のリロケーション処理
}

この変更により、TLS関連のリロケーション処理が実行されるのは、以下のすべての条件が真である場合に限定されます。

  1. linkmode == LinkExternal: 外部リンカを使用している。
  2. iself: ターゲットがELF形式である。
  3. HEADTYPE != Hopenbsd: ターゲットOSがOpenBSDではない。

つまり、ターゲットOSがOpenBSDである場合(HEADTYPE == Hopenbsd)、たとえ外部リンカを使用し、ELF形式であっても、このTLS関連の特別なリロケーション処理はスキップされることになります。これにより、OpenBSDのTLSサポートの特殊性によって引き起こされるリンカの問題が回避され、OpenBSD上でのGoのビルドが正常に完了するようになります。

この修正は、OpenBSDがGoのリンカが期待するような標準的なTLSの挙動を示さないため、そのプラットフォームではTLS関連の最適化や特定の処理を無効にすることが最善であるという判断に基づいています。

関連リンク

参考にした情報源リンク

  • Goのコミットメッセージ: ed890e7414c1aa7040a5ff6bef6dffa2767b6df5
  • Goのソースコード: src/cmd/ld/data.c
  • 一般的なリンカとELFに関する知識
  • スレッドローカルストレージに関する一般的な知識