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

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

このコミットは、Go言語のリンカである cmd/6l におけるOpenBSD環境でのビルド問題を修正するものです。具体的には、OpenBSD上でスレッドローカルストレージ (TLS) の再配置 (relocation) が誤って生成されるのを回避することで、ビルドが正常に完了するようにします。

コミット

commit e7fc9a5c570a3b8031dc7478c42c1c65cd70c719
Author: Ian Lance Taylor <iant@golang.org>
Date:   Wed Mar 27 14:32:51 2013 -0700

    cmd/6l: fix OpenBSD build
    
    Avoid generating TLS relocations on OpenBSD.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/7641055
---
 src/cmd/6l/span.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/cmd/6l/span.c b/src/cmd/6l/span.c
index b0b666f04c..460a34f2fe 100644
--- a/src/cmd/6l/span.c
+++ b/cmd/6l/span.c
@@ -881,7 +881,8 @@ putrelv:
 		r = addrel(cursym);
 		*r = rel;
 		r->off = curp->pc + andptr - and;
-	} else if(iself && linkmode == LinkExternal && a->type == D_INDIR+D_FS) {
+	} else if(iself && linkmode == LinkExternal && a->type == D_INDIR+D_FS
+		&& HEADTYPE != Hopenbsd) {
 		Reloc *r;
 		Sym *s;
 		

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

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

元コミット内容

上記の「コミット」セクションに記載されている内容が、このコミットの元々の内容です。

変更の背景

この変更の背景には、Go言語のプログラムをOpenBSD環境でビルドする際に発生していた問題があります。Goのリンカ cmd/6l は、実行ファイルを生成する過程で、プログラム内のシンボル参照を解決し、必要に応じて再配置(relocation)情報を生成します。再配置とは、プログラムがロードされる際に、メモリ上の特定のアドレスを修正する必要があることを示す情報です。

問題は、OpenBSDが特定の種類のTLS (Thread-Local Storage) 再配置をサポートしていない、または異なる方法で処理するため、GoリンカがOpenBSD向けに不適切なTLS再配置を生成してしまうことにありました。これにより、OpenBSD上でGoプログラムをビルドしようとすると、リンカエラーが発生したり、生成された実行ファイルが正しく動作しなかったりする可能性がありました。

このコミットは、OpenBSD環境でのビルドの安定性と互換性を確保するために、OpenBSD向けにビルドする際にはTLS再配置の生成を明示的に回避するようにリンカの動作を変更することを目的としています。

前提知識の解説

1. リンカ (Linker)

リンカは、コンパイラによって生成された複数のオブジェクトファイル(コンパイルされたソースコード)とライブラリを結合し、実行可能なプログラムやライブラリを生成するソフトウェアツールです。リンカの主な役割は以下の通りです。

  • シンボル解決 (Symbol Resolution): あるオブジェクトファイルで定義された関数や変数を、別のオブジェクトファイルから参照している場合、リンカはその参照を解決し、正しいメモリ位置にリンクします。
  • 再配置 (Relocation): コンパイル時には、関数や変数の最終的なメモリ上のアドレスは確定していません。リンカは、プログラムがロードされる際にこれらのアドレスを修正するための情報(再配置エントリ)を生成します。これにより、プログラムはメモリ上のどこにロードされても正しく実行できます。
  • 実行ファイルの生成: 最終的に、リンカはすべてのオブジェクトファイルとライブラリを結合し、オペレーティングシステムが実行できる形式(例: ELF, PE, Mach-O)の実行ファイルを生成します。

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

TLSは、マルチスレッドプログラミングにおいて、各スレッドがそれぞれ独立した変数を持つためのメカニズムです。通常、グローバル変数や静的変数はすべてのスレッドで共有されますが、TLS変数を使用すると、同じ名前の変数であっても、各スレッドが独自のコピーを持つことができます。これにより、スレッド間のデータ競合を防ぎ、スレッドセーフなプログラミングを容易にします。

TLS変数は、通常、特定のレジスタ(例: FS または GS セグメントレジスタ)をベースとしたオフセットでアクセスされます。このアクセス方法が、オペレーティングシステムやアーキテクチャによって異なるため、リンカはTLS変数へのアクセスを正しく解決するための特別な再配置を生成する必要があります。

3. 再配置 (Relocation) の種類

再配置には様々な種類がありますが、TLSに関連する再配置は、スレッドローカル変数へのアクセスを解決するために使用されます。例えば、D_INDIR+D_FS のようなシンボルタイプは、FS レジスタを介した間接的なアクセスに関連する再配置を示唆しています。

4. ELF (Executable and Linkable Format)

ELFは、Unix系オペレーティングシステム(Linux, BSDなど)で広く使用されている実行ファイル、オブジェクトファイル、共有ライブラリの標準フォーマットです。ELFファイルは、プログラムコード、データ、シンボルテーブル、再配置情報など、実行に必要なすべての情報を含んでいます。

5. cmd/6l

cmd/6l は、Go言語のツールチェインに含まれるリンカの一つで、64ビットx86アーキテクチャ(AMD64)向けのGoプログラムをリンクするために使用されます。Goのリンカは、一般的なシステムリンカとは異なり、Go独自のランタイムと密接に連携するように設計されています。

6. OpenBSD

OpenBSDは、セキュリティを重視して開発されているUnix系オペレーティングシステムです。その設計思想から、他のOSとは異なるシステムコールやメモリ管理、セキュリティメカニズムを採用している場合があります。これが、特定の再配置タイプに対するサポートの違いとして現れることがあります。

技術的詳細

このコミットが修正しているのは、src/cmd/6l/span.c ファイル内の再配置処理ロジックです。span.c は、Goリンカがオブジェクトファイル内のシンボルと再配置情報を処理し、最終的な実行ファイルに配置する役割を担っています。

問題の箇所は、putrelv というラベルの付いたコードブロック内にあります。このブロックは、様々な種類の再配置を処理し、必要に応じて Reloc 構造体を作成してリンカの内部データ構造に追加します。

変更前のコードでは、以下の条件が満たされた場合に特定の再配置が生成されていました。

} else if(iself && linkmode == LinkExternal && a->type == D_INDIR+D_FS) {

この条件は、以下のことを意味します。

  • iself: 生成される実行ファイルがELF形式であること。
  • linkmode == LinkExternal: 外部リンカ(Goのリンカではなく、システムのリンカ)を使用するモードであること。Goは通常、独自のリンカを使用しますが、Cgoなど外部のコードをリンクする場合には外部リンカを使用することがあります。
  • a->type == D_INDIR+D_FS: 再配置の対象となるシンボルが、FS レジスタを介した間接的なアクセス(TLS変数へのアクセスによく見られるパターン)に関連するタイプであること。

この条件が真の場合、リンカはTLSに関連する再配置を生成していました。しかし、OpenBSDではこの種のTLS再配置の扱いが他のOSと異なるため、問題が発生していました。OpenBSDは、TLSの実現方法が他のシステム(特にLinux)とは異なり、特定のTLS再配置タイプを期待しないか、あるいは異なる形式を要求します。GoリンカがOpenBSDの期待しない形式のTLS再配置を生成してしまうと、リンカがエラーを報告したり、生成されたバイナリが実行時にクラッシュしたりする原因となります。

このコミットは、この条件に && HEADTYPE != Hopenbsd という追加のチェックを導入することで、OpenBSD向けにビルドする際にはこの特定のTLS再配置の生成を回避するようにします。HEADTYPE は、Goのビルドシステムがターゲットとするオペレーティングシステムを示す定数であり、Hopenbsd はOpenBSDを指します。

これにより、OpenBSD環境でのビルド時に不適切なTLS再配置が生成されることがなくなり、ビルドプロセスが正常に完了し、生成された実行ファイルがOpenBSD上で正しく動作するようになります。

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

--- a/src/cmd/6l/span.c
+++ b/src/cmd/6l/span.c
@@ -881,7 +881,8 @@ putrelv:
 		r = addrel(cursym);
 		*r = rel;
 		r->off = curp->pc + andptr - and;
-	} else if(iself && linkmode == LinkExternal && a->type == D_INDIR+D_FS) {
+	} else if(iself && linkmode == LinkExternal && a->type == D_INDIR+D_FS
+		&& HEADTYPE != Hopenbsd) {
 		Reloc *r;
 		Sym *s;
 		

コアとなるコードの解説

変更は src/cmd/6l/span.c ファイルの putrelv セクションにあります。

元のコードでは、以下の条件が満たされた場合にTLS関連の再配置を生成していました。

iself && linkmode == LinkExternal && a->type == D_INDIR+D_FS

この条件は、ELF形式の実行ファイルを生成し、外部リンカを使用するモードであり、かつシンボルが D_INDIR+D_FS タイプ(TLS変数への間接アクセスを示す)である場合に真となります。

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

iself && linkmode == LinkExternal && a->type == D_INDIR+D_FS && HEADTYPE != Hopenbsd

  • HEADTYPE: これは、Goのビルドシステムがターゲットとするオペレーティングシステムを示すマクロまたは定数です。
  • Hopenbsd: これは、ターゲットOSがOpenBSDであることを示す値です。

したがって、追加された条件 && HEADTYPE != Hopenbsd は、「ターゲットOSがOpenBSDではない場合」というチェックを追加します。

この変更により、リンカは以下のようになります。

  1. ターゲットOSがOpenBSDの場合 (HEADTYPE == Hopenbsd): HEADTYPE != Hopenbsd の条件が偽となるため、たとえ他の条件 (iself, linkmode == LinkExternal, a->type == D_INDIR+D_FS) が真であっても、この else if ブロック内のコードは実行されません。結果として、OpenBSD向けにビルドする際には、この特定のTLS再配置が生成されなくなります。
  2. ターゲットOSがOpenBSDではない場合 (HEADTYPE != Hopenbsd): HEADTYPE != Hopenbsd の条件が真となるため、元の条件 (iself && linkmode == LinkExternal && a->type == D_INDIR+D_FS) が真であれば、このブロック内のコードが実行され、TLS再配置が生成されます。これは、他のOSではこのTLS再配置が正しく処理されるため、問題ありません。

このシンプルな条件追加によって、OpenBSD特有のTLS再配置の問題が回避され、GoプログラムのOpenBSD上でのビルドが安定するようになりました。

関連リンク

参考にした情報源リンク