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

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

コミット

commit b735eeb323ecae3769e094045e7a908390e1f32f
Author: Ian Lance Taylor <iant@golang.org>
Date:   Wed Mar 27 16:00:58 2013 -0700

    cmd/ld: fix OpenBSD (third try)
    
    On OpenBSD don't mark runtime.{g,m} as STT_TLS symbols.
    
    R=golang-dev, dave
    CC=golang-dev
    https://golang.org/cl/7867046

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

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

元コミット内容

cmd/ld: fix OpenBSD (third try)

On OpenBSD don't mark runtime.{g,m} as STT_TLS symbols.

変更の背景

このコミットは、Go言語のリンカ(cmd/ld)におけるOpenBSD環境での特定のバグを修正するためのものです。具体的には、Goランタイムの内部シンボルであるruntime.gruntime.mが、OpenBSD上でスレッドローカルストレージ(TLS)シンボルとして誤ってマークされることによって発生する問題を解決します。

Goプログラムは、実行時にゴルーチン(g)とOSスレッド(m)を管理するための内部構造体を持っています。これらの構造体は、Goランタイムの非常に重要な部分であり、通常は特定のメモリ領域に配置されます。しかし、OpenBSDのリンカがこれらのシンボルをSTT_TLS(Symbol Type Thread Local Storage)として扱ってしまうと、Goランタイムが期待する動作と異なる挙動を引き起こし、プログラムのクラッシュや予期せぬエラーにつながる可能性がありました。

この問題は、GoのビルドシステムがOpenBSDをターゲットとする際に、リンカがシンボルテーブルを生成する方法に起因していました。特に、外部リンカを使用するLinkExternalモードにおいて、この誤ったTLSシンボルとしてのマーク付けが発生していました。このコミットは、この問題を「3度目の試み」として修正するものであり、過去に同様の修正が試みられたものの、完全には解決されていなかったことを示唆しています。

前提知識の解説

Go言語のリンカ (cmd/ld)

Go言語のコンパイラツールチェーンの一部であり、Goのソースコードから生成されたオブジェクトファイルを結合して実行可能ファイルを生成する役割を担います。リンカは、プログラムが必要とするすべての関数やデータが正しく配置され、参照されるようにシンボルを解決します。

スレッドローカルストレージ (TLS)

各スレッドが独自のコピーを持つことができるデータ領域です。これにより、グローバル変数のようにアクセスできるが、スレッド間で競合しないデータを作成できます。TLSは、スレッド固有のコンテキスト情報(例:エラーコード、スレッドID)を格納するためによく使用されます。シンボルテーブルにおいてSTT_TLSとしてマークされたシンボルは、リンカによってTLS領域に配置されるべきものとして扱われます。

runtime.gruntime.m

Goランタイムの内部で使われる重要な構造体です。

  • runtime.g: ゴルーチン(goroutine)を表す構造体です。Goの並行処理の基本単位であり、軽量なスレッドのようなものです。各ゴルーチンは独自のスタックを持ち、runtime.g構造体はそのゴルーチンの状態やスタック情報などを管理します。
  • runtime.m: OSスレッド(machine)を表す構造体です。Goランタイムは、OSスレッド上でゴルーチンを実行します。runtime.m構造体は、OSスレッドの状態、現在のゴルーチン、スケジューラ関連の情報などを管理します。

これらのシンボルは、Goランタイムが内部的に使用するものであり、通常はTLSとして扱われるべきではありません。

LinkExternal

Goのビルドモードの一つで、Goのリンカが生成したオブジェクトファイルを、システムのCリンカ(例:gccclang)と組み合わせて最終的な実行可能ファイルを生成するモードです。このモードは、Cgoを使用する場合や、特定のシステムライブラリにリンクする必要がある場合によく使用されます。内部リンカ(LinkInternal)とは異なり、外部リンカの挙動に依存するため、OS固有の問題が発生しやすい傾向があります。

HEADTYPE

Goのビルドシステムで使われる内部変数で、ターゲットとなるオペレーティングシステムの種類を示します。例えば、HopenbsdはOpenBSDを、HlinuxはLinuxを、HdarwinはmacOSを指します。

symtab.c

Goリンカのソースコードの一部で、シンボルテーブルの生成と管理に関連するロジックが含まれています。このファイルは、オブジェクトファイル内のシンボル情報を解析し、最終的な実行可能ファイルのシンボルテーブルを構築する役割を担います。

技術的詳細

このコミットの技術的な核心は、GoリンカがOpenBSDをターゲットとする際に、runtime.gruntime.mシンボルをSTT_TLSとしてマークしないようにすることです。

Goのランタイムは、runtime.gruntime.mを特定のレジスタ(例:GSレジスタやFSレジスタ)を介してアクセスする、スレッドローカルなデータとして扱います。これは、OSが提供する一般的なTLSメカニズムとは異なる、Go独自の最適化されたアプローチです。

しかし、OpenBSDのリンカは、Goの内部リンカが生成するシンボル情報の一部を誤解釈し、runtime.gruntime.mをOSレベルのTLSシンボルとして扱おうとしました。これにより、リンカがこれらのシンボルをTLSセクションに配置しようとしたり、TLSアクセス用の特別な命令を生成しようとしたりすることで、Goランタイムが期待するメモリレイアウトやアクセス方法と矛盾が生じ、実行時エラーが発生していました。

この修正は、src/cmd/ld/symtab.c内のasmelfsym関数に条件を追加することで行われます。この関数は、ELF形式のシンボルをアセンブルする際に呼び出されます。変更前は、LinkExternalモードであれば常にruntime.m(そしてruntime.gも同様に)のシンボル情報を処理していました。しかし、OpenBSDではこの処理が問題を引き起こすため、HEADTYPE != Hopenbsdという条件を追加することで、OpenBSDの場合にはこの特定のシンボル処理をスキップするようにしました。

これにより、OpenBSD上でGoプログラムを外部リンカでビルドする際に、runtime.gruntime.mが誤ってSTT_TLSシンボルとしてマークされることがなくなり、Goランタイムが正しく機能するようになります。

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

diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c
index 1bf5c58eb7..d8a4645e0d 100644
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -153,7 +153,7 @@ asmelfsym(void)\n 	elfbind = STB_LOCAL;\n 	genasmsym(putelfsym);\n 	\n-\tif(linkmode == LinkExternal) {\n+\tif(linkmode == LinkExternal && HEADTYPE != Hopenbsd) {\n \t\ts = lookup(\"runtime.m\", 0);\n \t\tif(s->sect == nil) {\n \t\t\tcursym = nil;\

コアとなるコードの解説

変更はsrc/cmd/ld/symtab.cファイルのasmelfsym関数内の一行です。

元のコード:

if(linkmode == LinkExternal) {

変更後のコード:

if(linkmode == LinkExternal && HEADTYPE != Hopenbsd) {

この変更は、runtime.m(および関連するruntime.g)シンボルの処理を制御する条件文に、追加のチェックを導入しています。

  • linkmode == LinkExternal: この条件は、Goリンカが外部リンカ(システムにインストールされているCリンカなど)を使用している場合に真となります。この問題は、Goの内部リンカではなく、外部リンカとの連携時に発生するため、この条件は引き続き必要です。
  • HEADTYPE != Hopenbsd: この新しい条件は、ビルドターゲットがOpenBSDではない場合に真となります。つまり、この条件が追加されたことで、linkmode == LinkExternalが真であっても、ターゲットOSがOpenBSDである場合には、このブロック内のコード(runtime.mシンボルの処理)は実行されなくなります

この修正により、OpenBSD環境でGoプログラムを外部リンカでビルドする際に、runtime.gruntime.mが誤ってSTT_TLSシンボルとして扱われることを防ぎます。これにより、OpenBSD上でのGoプログラムの安定性と互換性が向上しました。

関連リンク

参考にした情報源リンク