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

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

このコミットは、Go言語のリンカ(cmd/5l, cmd/6l, cmd/8l)において、OpenBSDプラットフォーム向けにELFバイナリを生成する際に、PT_TLS (Thread-Local Storage) プログラムヘッダの出力を停止する変更を導入しています。これは、OpenBSDの動的リンカであるld.so(1)PT_TLSをサポートしておらず、このヘッダを含むELFバイナリのロードを拒否するという問題に対処するためのものです。Goランタイムのcgo部分で適切にTLSが処理されるため、リンカレベルでのPT_TLSの生成は不要と判断されました。

コミット

commit e6ca125f14d0f677205d3247f26da60ab8069b9c
Author: Joel Sing <jsing@google.com>
Date:   Fri Dec 21 01:27:50 2012 +1100

    cmd/[568]l: do not generate PT_TLS on openbsd
    
    The OpenBSD ld.so(1) does not currently support PT_TLS and refuses
    to load ELF binaries that contain PT_TLS sections. Do not emit PT_TLS
    sections - we will handle this appropriately in runtime/cgo instead.
    
    R=golang-dev, minux.ma, iant
    CC=golang-dev
    https://golang.org/cl/6846064

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

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

元コミット内容

Go言語のリンカ(5l, 6l, 8l はそれぞれARM、x86-64、x86アーキテクチャに対応するリンカ)が、ELFバイナリを生成する際に、Thread-Local Storage (TLS) のオフセットが存在する場合にPT_TLSタイプのプログラムヘッダを生成していました。

変更の背景

OpenBSDの動的リンカであるld.so(1)は、当時(2012年時点)PT_TLSプログラムヘッダをサポートしていませんでした。このため、GoのリンカがPT_TLSヘッダを含むELFバイナリを生成すると、OpenBSDシステム上でそのバイナリをロードしようとした際に、ld.so(1)がロードを拒否し、実行不可能となる問題が発生していました。

Go言語はクロスプラットフォーム対応を重視しており、各OSの特性やリンカの挙動に合わせて適切なバイナリを生成する必要があります。この問題はOpenBSD上でのGoバイナリの実行を妨げるものであったため、修正が必要とされました。コミットメッセージにもあるように、TLSの扱いはGoのruntime/cgoパッケージで適切に処理されるため、リンカがPT_TLSヘッダを生成する必要はないと判断されました。

前提知識の解説

ELF (Executable and Linkable Format)

ELFは、Unix系システムで広く使われている実行可能ファイル、オブジェクトファイル、共有ライブラリ、コアダンプファイルの標準フォーマットです。ELFファイルは、ヘッダ、プログラムヘッダテーブル、セクションヘッダテーブル、そして様々なセクションから構成されます。

  • プログラムヘッダテーブル (Program Header Table): 実行時にシステムローダ(動的リンカ)がどのようにメモリにファイルをマッピングするかを記述します。各エントリは「プログラムヘッダ」と呼ばれ、セグメントのタイプ(例: PT_LOAD, PT_TLS)、オフセット、サイズ、メモリ上のアドレス、アクセス権限などを定義します。
  • セクションヘッダテーブル (Section Header Table): リンク時にリンカがどのようにファイルを扱うかを記述します。各エントリは「セクションヘッダ」と呼ばれ、セクションの名前、タイプ、アドレス、サイズなどを定義します。

PT_TLS (Program Header Type: Thread-Local Storage)

PT_TLSはELFプログラムヘッダの一種で、スレッドローカルストレージ (TLS) セグメントを記述するために使用されます。TLSは、各スレッドが独自のデータコピーを持つことを可能にするメカニズムで、グローバル変数や静的変数をスレッドごとに独立して管理するために利用されます。PT_TLSヘッダは、TLS初期化イメージのサイズ、アライメント、および実行時にTLSブロックがどのように配置されるかに関する情報を提供します。

ld.so(1)

ld.so(1)は、Unix系システムにおける動的リンカ/ローダのコマンド名です。実行可能ファイルが起動される際に、必要な共有ライブラリをメモリにロードし、シンボル解決を行う役割を担います。ld.so(1)はELFファイルのプログラムヘッダテーブルを読み取り、それに基づいてメモリマッピングや初期化処理を行います。OSやそのバージョンによって、サポートするELFヘッダのタイプや挙動が異なることがあります。

Go言語のリンカ (cmd/5l, cmd/6l, cmd/8l)

Go言語のツールチェインには、各アーキテクチャに対応する独自のリンカが含まれています。これらはGoのソースコードから実行可能なバイナリを生成する際に使用されます。

  • cmd/5l: ARMアーキテクチャ向け
  • cmd/6l: x86-64 (AMD64) アーキテクチャ向け
  • cmd/8l: x86 (386) アーキテクチャ向け

これらのリンカは、Goのコンパイラが生成したオブジェクトファイルを結合し、最終的な実行可能ファイルを生成します。この過程で、ELFヘッダやプログラムヘッダテーブルも構築されます。

runtime/cgo

runtime/cgoは、GoプログラムがC言語のコード(またはC互換の外部ライブラリ)を呼び出すためのメカニズムを提供するGoのパッケージです。CGOを使用する場合、GoランタイムはCコードが期待する環境(例えば、スレッドローカルストレージの管理方法)を考慮する必要があります。このコミットの背景にあるように、OpenBSDのld.so(1)PT_TLSをサポートしない場合でも、runtime/cgoがTLSを適切に処理する代替手段を提供していることを示唆しています。

技術的詳細

このコミットの技術的詳細なポイントは、GoリンカがELFバイナリを生成する際の条件分岐にあります。

Goのリンカは、TLSセグメントが存在する場合(tlsoffset != 0)にPT_TLSプログラムヘッダを生成するように設計されていました。しかし、OpenBSDのld.so(1)がこのヘッダを解釈できないため、OpenBSD向けにビルドする際にはこのヘッダの生成を抑制する必要があります。

変更は、既存のif(tlsoffset != 0)条件に&& HEADTYPE != Hopenbsdという追加条件を加えることで実現されています。

  • tlsoffset: Thread-Local Storageのオフセットを示す変数。これが0でない場合、TLSセグメントが存在することを示します。
  • HEADTYPE: ビルドターゲットのOSタイプを示す定数。HopenbsdはOpenBSDプラットフォームを指します。

この変更により、リンカは以下のロジックに従います。

  1. TLSオフセットが0でない(TLSセグメントが存在する)
  2. かつ、ターゲットOSがOpenBSDではない

上記2つの条件が同時に満たされる場合にのみ、PT_TLSプログラムヘッダが生成されます。これにより、OpenBSD向けにビルドされたGoバイナリにはPT_TLSヘッダが含まれなくなり、ld.so(1)によるロード拒否の問題が回避されます。

TLSの実際の管理は、リンカではなくGoのruntime/cgoレイヤーで適切に行われるため、このリンカレベルでのPT_TLSヘッダの抑制は機能的な問題を引き起こしません。これは、Goが各プラットフォームの特性に合わせて柔軟なランタイムとツールチェインを提供している良い例です。

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

変更は、Goリンカの各アーキテクチャ (5l, 6l, 8l) のアセンブラコード生成部分 (asm.c) に共通して適用されています。

src/cmd/5l/asm.c (ARMリンカ)

--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -1015,7 +1015,10 @@ asmb(void)\
 			phsh(ph, sh);\
 
 			// .tbss (optional) and TLS phdr
-			if(tlsoffset != 0) {
+			// Do not emit PT_TLS for OpenBSD since ld.so(1) does
+			// not currently support it. This is handled
+			// appropriately in runtime/cgo.
+			if(tlsoffset != 0 && HEADTYPE != Hopenbsd) {
 				ph = newElfPhdr();
 				ph->type = PT_TLS;
 				ph->flags = PF_R;

src/cmd/6l/asm.c (x86-64リンカ)

--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -1116,7 +1116,10 @@ asmb(void)\
 			/*
 			 * Thread-local storage segment (really just size).
 			 */
-			if(tlsoffset != 0) {
+			// Do not emit PT_TLS for OpenBSD since ld.so(1) does
+			// not currently support it. This is handled
+			// appropriately in runtime/cgo.
+			if(tlsoffset != 0 && HEADTYPE != Hopenbsd) {
 				ph = newElfPhdr();
 				ph->type = PT_TLS;
 				ph->flags = PF_R;

src/cmd/8l/asm.c (x86リンカ)

--- a/src/cmd/8l/asm.c
+++ b/src/cmd/8l/asm.c
@@ -1179,7 +1179,10 @@ asmb(void)\
 			/*
 			 * Thread-local storage segment (really just size).\
 			 */
-			if(tlsoffset != 0) {
+			// Do not emit PT_TLS for OpenBSD since ld.so(1) does
+			// not currently support it. This is handled
+			// appropriately in runtime/cgo.
+			if(tlsoffset != 0 && HEADTYPE != Hopenbsd) {
 				ph = newElfPhdr();
 				ph->type = PT_TLS;
 				ph->flags = PF_R;

コアとなるコードの解説

上記の各ファイルにおいて、asmb() 関数内でELFプログラムヘッダを構築するロジックの一部が変更されています。

元のコードでは、tlsoffset が0でない(つまり、スレッドローカルストレージが必要な場合)には無条件に PT_TLS タイプのプログラムヘッダを生成していました。

if(tlsoffset != 0) {
    ph = newElfPhdr();
    ph->type = PT_TLS;
    ph->flags = PF_R;
    // ... その他の設定 ...
}

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

if(tlsoffset != 0 && HEADTYPE != Hopenbsd) {
    ph = newElfPhdr();
    ph->type = PT_TLS;
    ph->flags = PF_R;
    // ... その他の設定 ...
}

これにより、ビルドターゲットがOpenBSD (HEADTYPE == Hopenbsd) である場合には、tlsoffset が0でなくても PT_TLS ヘッダは生成されなくなります。コメントにも明記されている通り、OpenBSDのld.so(1)PT_TLSをサポートしないため、このヘッダの生成を抑制し、TLSの扱いはruntime/cgoに委ねるという方針が示されています。

この変更は、Goのクロスコンパイルとマルチプラットフォーム対応の柔軟性を示すものであり、特定のOSのリンカの制約に対応するために、ビルドプロセスの一部を条件分岐させている点が重要です。

関連リンク

参考にした情報源リンク

  • ELF (Executable and Linkable Format) の仕様に関するドキュメント (例: System V Application Binary Interface)
  • ld.so(1) のmanページ (OpenBSDのmanページを参照)
  • Thread-Local Storage (TLS) に関する一般的な情報
  • Go言語のリンカの内部構造に関するドキュメントやソースコード (Goの公式ドキュメントやGoのソースコードリポジトリ)
  • Goのruntime/cgoパッケージに関するドキュメントI have provided the detailed explanation as requested.
# [インデックス 14695] ファイルの概要

このコミットは、Go言語のリンカ(`cmd/5l`, `cmd/6l`, `cmd/8l`)において、OpenBSDプラットフォーム向けにELFバイナリを生成する際に、`PT_TLS` (Thread-Local Storage) プログラムヘッダの出力を停止する変更を導入しています。これは、OpenBSDの動的リンカである`ld.so(1)`が`PT_TLS`をサポートしておらず、このヘッダを含むELFバイナリのロードを拒否するという問題に対処するためのものです。Goランタイムの`cgo`部分で適切にTLSが処理されるため、リンカレベルでの`PT_TLS`の生成は不要と判断されました。

## コミット

commit e6ca125f14d0f677205d3247f26da60ab8069b9c Author: Joel Sing jsing@google.com Date: Fri Dec 21 01:27:50 2012 +1100

cmd/[568]l: do not generate PT_TLS on openbsd

The OpenBSD ld.so(1) does not currently support PT_TLS and refuses
to load ELF binaries that contain PT_TLS sections. Do not emit PT_TLS
sections - we will handle this appropriately in runtime/cgo instead.

R=golang-dev, minux.ma, iant
CC=golang-dev
https://golang.org/cl/6846064

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

[https://github.com/golang/go/commit/e6ca125f14d0f677205d3247f26da60ab8069b9c](https://github.com/golang/go/commit/e6ca125f14d0f677205d3247f26da60ab8069b9c)

## 元コミット内容

Go言語のリンカ(`5l`, `6l`, `8l` はそれぞれARM、x86-64、x86アーキテクチャに対応するリンカ)が、ELFバイナリを生成する際に、Thread-Local Storage (TLS) のオフセットが存在する場合に`PT_TLS`タイプのプログラムヘッダを生成していました。

## 変更の背景

OpenBSDの動的リンカである`ld.so(1)`は、当時(2012年時点)`PT_TLS`プログラムヘッダをサポートしていませんでした。このため、Goのリンカが`PT_TLS`ヘッダを含むELFバイナリを生成すると、OpenBSDシステム上でそのバイナリをロードしようとした際に、`ld.so(1)`がロードを拒否し、実行不可能となる問題が発生していました。

Go言語はクロスプラットフォーム対応を重視しており、各OSの特性やリンカの挙動に合わせて適切なバイナリを生成する必要があります。この問題はOpenBSD上でのGoバイナリの実行を妨げるものであったため、修正が必要とされました。コミットメッセージにもあるように、TLSの扱いはGoの`runtime/cgo`パッケージで適切に処理されるため、リンカが`PT_TLS`ヘッダを生成する必要はないと判断されました。

## 前提知識の解説

### ELF (Executable and Linkable Format)

ELFは、Unix系システムで広く使われている実行可能ファイル、オブジェクトファイル、共有ライブラリ、コアダンプファイルの標準フォーマットです。ELFファイルは、ヘッダ、プログラムヘッダテーブル、セクションヘッダテーブル、そして様々なセクションから構成されます。

*   **プログラムヘッダテーブル (Program Header Table)**: 実行時にシステムローダ(動的リンカ)がどのようにメモリにファイルをマッピングするかを記述します。各エントリは「プログラムヘッダ」と呼ばれ、セグメントのタイプ(例: `PT_LOAD`, `PT_TLS`)、オフセット、サイズ、メモリ上のアドレス、アクセス権限などを定義します。
*   **セクションヘッダテーブル (Section Header Table)**: リンク時にリンカがどのようにファイルを扱うかを記述します。各エントリは「セクションヘッダ」と呼ばれ、セクションの名前、タイプ、アドレス、サイズなどを定義します。

### PT_TLS (Program Header Type: Thread-Local Storage)

`PT_TLS`はELFプログラムヘッダの一種で、スレッドローカルストレージ (TLS) セグメントを記述するために使用されます。TLSは、各スレッドが独自のデータコピーを持つことを可能にするメカニズムで、グローバル変数や静的変数をスレッドごとに独立して管理するために利用されます。`PT_TLS`ヘッダは、TLS初期化イメージのサイズ、アライメント、および実行時にTLSブロックがどのように配置されるかに関する情報を提供します。

### ld.so(1)

`ld.so(1)`は、Unix系システムにおける動的リンカ/ローダのコマンド名です。実行可能ファイルが起動される際に、必要な共有ライブラリをメモリにロードし、シンボル解決を行う役割を担います。`ld.so(1)`はELFファイルのプログラムヘッダテーブルを読み取り、それに基づいてメモリマッピングや初期化処理を行います。OSやそのバージョンによって、サポートするELFヘッダのタイプや挙動が異なることがあります。

### Go言語のリンカ (`cmd/5l`, `cmd/6l`, `cmd/8l`)

Go言語のツールチェインには、各アーキテクチャに対応する独自のリンカが含まれています。これらはGoのソースコードから実行可能なバイナリを生成する際に使用されます。
*   `cmd/5l`: ARMアーキテクチャ向け
*   `cmd/6l`: x86-64 (AMD64) アーキテクチャ向け
*   `cmd/8l`: x86 (386) アーキテクチャ向け

これらのリンカは、Goのコンパイラが生成したオブジェクトファイルを結合し、最終的な実行可能ファイルを生成します。この過程で、ELFヘッダやプログラムヘッダテーブルも構築されます。

### runtime/cgo

`runtime/cgo`は、GoプログラムがC言語のコード(またはC互換の外部ライブラリ)を呼び出すためのメカニズムを提供するGoのパッケージです。CGOを使用する場合、GoランタイムはCコードが期待する環境(例えば、スレッドローカルストレージの管理方法)を考慮する必要があります。このコミットの背景にあるように、OpenBSDの`ld.so(1)`が`PT_TLS`をサポートしない場合でも、`runtime/cgo`がTLSを適切に処理する代替手段を提供していることを示唆しています。

## 技術的詳細

このコミットの技術的詳細なポイントは、GoリンカがELFバイナリを生成する際の条件分岐にあります。

Goのリンカは、TLSセグメントが存在する場合(`tlsoffset != 0`)に`PT_TLS`プログラムヘッダを生成するように設計されていました。しかし、OpenBSDの`ld.so(1)`がこのヘッダを解釈できないため、OpenBSD向けにビルドする際にはこのヘッダの生成を抑制する必要があります。

変更は、既存の`if(tlsoffset != 0)`条件に`&& HEADTYPE != Hopenbsd`という追加条件を加えることで実現されています。
*   `tlsoffset`: Thread-Local Storageのオフセットを示す変数。これが0でない場合、TLSセグメントが存在することを示します。
*   `HEADTYPE`: ビルドターゲットのOSタイプを示す定数。`Hopenbsd`はOpenBSDプラットフォームを指します。

この変更により、リンカは以下のロジックに従います。
1.  TLSオフセットが0でない(TLSセグメントが存在する)
2.  かつ、ターゲットOSがOpenBSDではない

上記2つの条件が同時に満たされる場合にのみ、`PT_TLS`プログラムヘッダが生成されます。これにより、OpenBSD向けにビルドされたGoバイナリには`PT_TLS`ヘッダが含まれなくなり、`ld.so(1)`によるロード拒否の問題が回避されます。

TLSの実際の管理は、リンカではなくGoの`runtime/cgo`レイヤーで適切に行われるため、このリンカレベルでの`PT_TLS`ヘッダの抑制は機能的な問題を引き起こしません。これは、Goが各プラットフォームの特性に合わせて柔軟なランタイムとツールチェインを提供している良い例です。

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

変更は、Goリンカの各アーキテクチャ (`5l`, `6l`, `8l`) のアセンブラコード生成部分 (`asm.c`) に共通して適用されています。

### `src/cmd/5l/asm.c` (ARMリンカ)

```diff
--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -1015,7 +1015,10 @@ asmb(void)\
 			phsh(ph, sh);\
 
 			// .tbss (optional) and TLS phdr
-			if(tlsoffset != 0) {
+			// Do not emit PT_TLS for OpenBSD since ld.so(1) does
+			// not currently support it. This is handled
+			// appropriately in runtime/cgo.
+			if(tlsoffset != 0 && HEADTYPE != Hopenbsd) {
 				ph = newElfPhdr();
 				ph->type = PT_TLS;
 				ph->flags = PF_R;

src/cmd/6l/asm.c (x86-64リンカ)

--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -1116,7 +1116,10 @@ asmb(void)\
 			/*
 			 * Thread-local storage segment (really just size).
 			 */
-			if(tlsoffset != 0) {
+			// Do not emit PT_TLS for OpenBSD since ld.so(1) does
+			// not currently support it. This is handled
+			// appropriately in runtime/cgo.
+			if(tlsoffset != 0 && HEADTYPE != Hopenbsd) {
 				ph = newElfPhdr();
 				ph->type = PT_TLS;
 				ph->flags = PF_R;

src/cmd/8l/asm.c (x86リンカ)

--- a/src/cmd/8l/asm.c
+++ b/src/cmd/8l/asm.c
@@ -1179,7 +1179,10 @@ asmb(void)\
 			/*
 			 * Thread-local storage segment (really just size).\
 			 */
-			if(tlsoffset != 0) {
+			// Do not emit PT_TLS for OpenBSD since ld.so(1) does
+			// not currently support it. This is handled
+			// appropriately in runtime/cgo.
+			if(tlsoffset != 0 && HEADTYPE != Hopenbsd) {
 				ph = newElfPhdr();
 				ph->type = PT_TLS;
 				ph->flags = PF_R;

コアとなるコードの解説

上記の各ファイルにおいて、asmb() 関数内でELFプログラムヘッダを構築するロジックの一部が変更されています。

元のコードでは、tlsoffset が0でない(つまり、スレッドローカルストレージが必要な場合)には無条件に PT_TLS タイプのプログラムヘッダを生成していました。

if(tlsoffset != 0) {
    ph = newElfPhdr();
    ph->type = PT_TLS;
    ph->flags = PF_R;
    // ... その他の設定 ...
}

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

if(tlsoffset != 0 && HEADTYPE != Hopenbsd) {
    ph = newElfPhdr();
    ph->type = PT_TLS;
    ph->flags = PF_R;
    // ... その他の設定 ...
}

これにより、ビルドターゲットがOpenBSD (HEADTYPE == Hopenbsd) である場合には、tlsoffset が0でなくても PT_TLS ヘッダは生成されなくなります。コメントにも明記されている通り、OpenBSDのld.so(1)PT_TLSをサポートしないため、このヘッダの生成を抑制し、TLSの扱いはruntime/cgoに委ねるという方針が示されています。

この変更は、Goのクロスコンパイルとマルチプラットフォーム対応の柔軟性を示すものであり、特定のOSのリンカの制約に対応するために、ビルドプロセスの一部を条件分岐させている点が重要です。

関連リンク

参考にした情報源リンク

  • ELF (Executable and Linkable Format) の仕様に関するドキュメント (例: System V Application Binary Interface)
  • ld.so(1) のmanページ (OpenBSDのmanページを参照)
  • Thread-Local Storage (TLS) に関する一般的な情報
  • Go言語のリンカの内部構造に関するドキュメントやソースコード (Goの公式ドキュメントやGoのソースコードリポジトリ)
  • Goのruntime/cgoパッケージに関するドキュメント