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

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

このコミットは、Go言語のリンカの一部である src/liblink/obj8.c ファイルに対する変更です。このファイルは、Goのツールチェーンにおいて、386アーキテクチャ(Intel 80386互換プロセッサ)向けのオブジェクトコード生成とリンク処理に関連する部分を扱っています。特に、Plan 9オペレーティングシステム上での外部レジスタアクセスに関する問題を修正しています。

コミット

commit 52ee63f544a1dc5ef4b69a4638c99cbdfae34b42
Author: Anthony Martin <ality@pbrane.org>
Date:   Mon Dec 9 18:48:44 2013 -0500

    liblink: fix extern register accesses on Plan 9 (386)
    
    R=golang-dev, 0intro, rsc
    CC=golang-dev
    https://golang.org/cl/39680044

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

https://github.com/golang/go/commit/52ee63f544a1dc5ef4b69a4638c99cbdfae34b42

元コミット内容

liblink: fix extern register accesses on Plan 9 (386)

R=golang-dev, 0intro, rsc
CC=golang-dev
https://golang.org/cl/39680044

変更の背景

このコミットの背景には、Go言語のリンカ(liblink)がPlan 9オペレーティングシステム上で386アーキテクチャをターゲットとする際に発生していた、外部(extern)レジスタアクセスに関するバグが存在していました。

Goのツールチェーンは、その設計思想においてPlan 9オペレーティングシステムの影響を強く受けています。特に、Goのアセンブリ言語はPlan 9のアセンブラに似た構文とセマンティクスを持っています。このような環境下で、特定のレジスタ(D_AXからD_DIなど)への外部アクセスが正しく処理されない問題が発生していました。

元のコードには、この問題に対処しようとしたと思われるTODOコメント付きのブロックが存在しましたが、それがコメントアウトされており、機能していませんでした。このコミットは、そのコメントアウトされたコードを修正し、有効化することで、Plan 9 (386) 環境での外部レジスタアクセスを正しく処理できるようにすることを目的としています。具体的には、_tosシンボルへの参照を適切に解決することで、レジスタへの間接アクセスが正しく行われるように修正しています。

前提知識の解説

liblinkはGo言語のビルドツールチェーンにおけるリンカの役割を担っています。Goのコンパイラは、直接機械語を生成するのではなく、Goアセンブリ言語に似た中間表現(「semi-abstract instruction set」と呼ばれることもあります)を生成します。liblinkはこの中間表現を受け取り、最終的な実行可能バイナリを生成する際に、実際の機械語命令への変換(命令選択)や、シンボルの解決、ライブラリのリンクなどを行います。

Plan 9

Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Go言語の設計者の一部はPlan 9の開発にも携わっており、Goのツールチェーン、特にアセンブリ言語の設計にはPlan 9の思想が色濃く反映されています。Plan 9のアセンブラは、一般的なx86アセンブラとは異なる独自の構文とレジスタの命名規則を持っています。

386アーキテクチャ

Intel 80386は、1985年にリリースされた32ビットのx86プロセッサです。このアーキテクチャは、現代のx86-64アーキテクチャの基礎を築きました。Go言語のツールチェーンは、32ビットおよび64ビットのx86アーキテクチャをサポートしており、このコミットは特に32ビット版(386)に焦点を当てています。

Goアセンブリにおけるレジスタとアドレッシングモード

Goのアセンブリ言語は、Plan 9アセンブラの影響を受けており、一般的なIntel構文とは異なる表記を使用します。

  • D_INDIR: これは「間接アドレッシングモード」を示します。オペランドが直接データではなく、データが格納されているメモリのアドレスを指定することを意味します。
  • D_GS: x86アーキテクチャにおけるセグメントレジスタの一つであるGSレジスタを指します。セグメントレジスタは、メモリセグメントのベースアドレスを保持するために使用されます。
  • D_AX, D_DI: これらはx86アーキテクチャの汎用レジスタを指します。
    • AX (Accumulator Register): 演算結果を格納するためによく使われます。32ビット環境ではEAXに対応します。
    • DI (Destination Index Register): 文字列操作やメモリ操作で、目的地のポインタとしてよく使われます。32ビット環境ではEDIに対応します。 Goの内部表現では、これらのレジスタがD_AXD_DIのように表現されることがあります。

ctxt->plan9tos

ctxtはリンカのコンテキスト構造体(Link構造体)を指します。ctxt->plan9tosは、このコンテキスト構造体内のフィールドであり、Plan 9固有の_tos(Top Of Stack)シンボルへの参照を保持するために使用されます。Plan 9環境では、スタックのトップを指す特定のシンボルが存在し、これへのアクセスが正しく行われる必要があります。

技術的詳細

このコミットが修正している問題は、Plan 9 (386) 環境において、外部レジスタへのアクセスが正しく行われないというものです。Goのリンカは、アセンブリコード内のレジスタアクセスを処理する際に、特定の変換や最適化を行います。特に、D_INDIR+D_GSのような間接アドレッシングモードと、D_AXからD_DIまでの汎用レジスタへのアクセスが組み合わされた場合に問題が発生していました。

元のコードには、このケースを処理するためのロジックがコメントアウトされた状態で存在していました。このロジックは、p->from.type == D_INDIR+D_GSGSセグメントレジスタを介した間接アクセス)かつp->to.type >= D_AX && p->to.type <= D_DI(汎用レジスタへの書き込み)という条件を満たす場合に適用されるべきものでした。

問題の核心は、この処理がplan9_tosというグローバル変数に依存していた点です。しかし、このplan9_tosが適切に初期化されていないか、あるいはコンテキスト固有の_tosシンボルへの参照が正しく取得されていないために、レジスタアクセスが失敗していました。

このコミットでは、コメントアウトされていたコードを有効化し、plan9_tosグローバル変数への直接的な依存を排除しています。代わりに、リンカのコンテキスト(ctxt)から_tosシンボルを動的にルックアップし、その結果をctxt->plan9tosに格納して使用するように変更しています。これにより、_tosシンボルへの参照が常に現在のリンクコンテキストに適切に解決され、Plan 9 (386) 環境での外部レジスタアクセスが正しく行われるようになります。

具体的には、appendp(ctxt, p)で新しいプログラム命令(Prog)を作成し、元の命令のfromオペランドを新しい命令のfromにコピーします。そして、新しい命令のfrom.typeD_INDIR + p->to.typeに設定することで、レジスタへの間接アクセスを表現します。元の命令pAMOVL(32ビット移動命令)に変換され、p->from.typeD_EXTERN(外部シンボル)に、p->from.symctxt->plan9tosに設定されます。これにより、_tosシンボルを介したレジスタへの間接的なデータ移動が正しく行われるようになります。

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

diff --git a/src/liblink/obj8.c b/src/liblink/obj8.c
index e744abe55e..e22a1b912f 100644
--- a/src/liblink/obj8.c
+++ b/src/liblink/obj8.c
@@ -271,10 +271,11 @@ progedit(Link *ctxt, Prog *p)
 			}
 		}
 	}
-	/* TODO 
  	if(ctxt->headtype == Hplan9) {
  		if(p->from.type == D_INDIR+D_GS
  		&& p->to.type >= D_AX && p->to.type <= D_DI) {
+			if(ctxt->plan9tos == nil)
+				ctxt->plan9tos = linklookup(ctxt, "_tos", 0);
  			q = appendp(ctxt, p);
  			q->from = p->from;
  			q->from.type = D_INDIR + p->to.type;
@@ -282,11 +283,10 @@ progedit(Link *ctxt, Prog *p)
  			q->as = p->as;
  			p->as = AMOVL;
  			p->from.type = D_EXTERN;
-			p->from.sym = plan9_tos;
+			p->from.sym = ctxt->plan9tos;
  			p->from.offset = 0;
  		}
  	}\n-	*/
 }\n 
 static Prog*

コアとなるコードの解説

このコミットのコアとなる変更は、src/liblink/obj8.cファイルのprogedit関数内にあります。

  1. コメントアウトの解除: 元のコードでは、Plan 9 (386) 環境での特定のレジスタアクセスを処理するためのロジックが、/* TODO ... */というコメントブロックによって完全にコメントアウトされていました。このコミットでは、このコメントブロックが解除され、内部のロジックが有効化されています。

  2. ctxt->plan9tosの初期化:

    +			if(ctxt->plan9tos == nil)
    +				ctxt->plan9tos = linklookup(ctxt, "_tos", 0);
    

    これが最も重要な変更点です。以前のコメントアウトされたコードでは、plan9_tosというグローバル変数(またはそれに類するもの)が使われていたと推測されます。しかし、この修正では、リンカのコンテキストctxtに属するplan9tosフィールドを使用するように変更されています。 if(ctxt->plan9tos == nil)というチェックは、_tosシンボルへの参照がまだ解決されていない場合にのみ、linklookup関数を呼び出してシンボルをルックアップし、ctxt->plan9tosに格納することを意味します。linklookup(ctxt, "_tos", 0)は、リンカのコンテキスト内で_tosという名前のシンボルを探し、そのシンボルへのポインタを返します。これにより、_tosシンボルへの参照が動的に、かつコンテキストに依存して解決されるようになります。

  3. p->from.symの変更:

    -			p->from.sym = plan9_tos;
    +			p->from.sym = ctxt->plan9tos;
    

    この行は、上記のctxt->plan9tosの初期化と密接に関連しています。元のコード(コメントアウトされていた部分)では、p->from.symplan9_tosというシンボルに直接設定されていました。しかし、この修正により、p->from.symは、現在のリンカコンテキストで適切にルックアップされた_tosシンボルへの参照であるctxt->plan9tosに設定されるようになりました。これにより、外部レジスタアクセスが_tosシンボルを介して正しく行われるようになります。

この変更により、Plan 9 (386) 環境におけるGoプログラムのリンキング時に、GSセグメントレジスタを介した間接的なレジスタアクセスが正しく処理され、実行時の問題が解消されることが期待されます。

関連リンク

このコミットは、GoのIssueトラッカーやデザインドキュメントに直接関連する特定のリンクは見つかりませんでした。しかし、Goのリンカとアセンブリに関する一般的な情報は以下のリンクで参照できます。

参考にした情報源リンク