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

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

このコミットは、Go言語のリンカ (cmd/ld) に新しいコマンドラインフラグ -tmpdir を追加するものです。このフラグを使用することで、リンカが生成する一時ファイルを指定したディレクトリに残すことが可能になり、デバッグやビルドプロセスの詳細な調査が容易になります。

コミット

commit e982ecacd1920d4314c84ecfca316a9bf0698fd3
Author: Russ Cox <rsc@golang.org>
Date:   Sun Mar 10 12:50:44 2013 -0400

    cmd/ld: add tmpdir flag to preserve temp files
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/7497044

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

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

元コミット内容

cmd/ld: add tmpdir flag to preserve temp files

R=ken2
CC=golang-dev
https://golang.org/cl/7497044

変更の背景

Go言語のビルドプロセスにおいて、リンカ (cmd/ld) は実行可能ファイルを生成する際に、中間的な一時ファイルを生成します。通常、これらのファイルはリンカの処理が完了すると自動的に削除されます。しかし、リンカの動作をデバッグしたり、生成される中間ファイルの内容を詳細に調査したりする際には、これらのファイルが削除されてしまうと不便でした。

このコミットの背景には、開発者やデバッガがリンカの内部動作をより深く理解し、問題の診断を容易にするためのニーズがあったと考えられます。一時ファイルを保持する機能は、特に複雑なビルドの問題や、リンカの最適化、あるいは特定の環境での挙動の違いを調査する際に非常に有用です。

前提知識の解説

Go言語のビルドプロセス

Go言語のプログラムは、通常、go build コマンドによってコンパイルされ、リンクされて実行可能ファイルが生成されます。このプロセスは大きく分けて以下の段階を含みます。

  1. コンパイル: Goのソースコード (.go ファイル) は、コンパイラ (cmd/compile) によってアセンブリコードに変換され、オブジェクトファイル (.o ファイル) が生成されます。
  2. アセンブル: オブジェクトファイルは、アセンブラによって機械語に変換されます。
  3. リンク: リンカ (cmd/ld) は、生成されたオブジェクトファイル、Goランタイムライブラリ、およびその他の必要なライブラリを結合し、単一の実行可能ファイルを生成します。この際、シンボル解決、アドレスの再配置、デバッグ情報の埋め込みなどが行われます。

cmd/ld (Goリンカ)

cmd/ld は、Go言語のツールチェインにおけるリンカです。C言語の ld コマンドと同様に、複数のオブジェクトファイルやライブラリを結合して実行可能ファイルや共有ライブラリを生成する役割を担います。Goのリンカは、Goプログラムの特性(例えば、静的リンクがデフォルトであること、ガベージコレクション、goroutineスケジューラなど)を考慮して設計されています。

一時ファイル

ソフトウェアのビルドプロセスでは、多くの場合、中間的な処理結果を一時ファイルとしてディスクに書き出すことがあります。リンカも同様に、シンボルテーブル、再配置情報、デバッグ情報など、最終的な実行可能ファイルに組み込まれる前の様々なデータを一時ファイルとして利用することがあります。これらのファイルは通常、処理が完了すると自動的に削除され、ユーザーからは見えません。

コマンドラインフラグ

コマンドラインフラグ(またはオプション)は、プログラムの実行時にその挙動を制御するために使用される引数です。Goのツールチェインでは、多くのコマンドが -flagname の形式でフラグを受け入れます。例えば、go build -o myapp は、出力ファイル名を myapp に指定するフラグです。

技術的詳細

このコミットは、Goリンカ (cmd/ld) の内部に、一時ファイルを保持するための新しいメカニズムを導入します。具体的には、以下の変更が行われています。

  1. tmpdir フラグの追加: リンカの各アーキテクチャ固有のエントリポイント (src/cmd/5l/obj.c, src/cmd/6l/obj.c, src/cmd/8l/obj.c はそれぞれARM、x86-64、x86のリンカに対応) に、-tmpdir という新しいコマンドラインフラグが追加されました。このフラグは文字列引数を取り、一時ファイルを保存するディレクトリのパスを指定します。
  2. 一時ディレクトリ管理の変更: src/cmd/ld/lib.c はリンカの共通ライブラリであり、一時ディレクトリの作成とクリーンアップを担当するロジックが含まれています。
    • 以前は、tmpdir 変数は static char *tmpdir; としてローカルに宣言され、リンカが起動すると常に新しい一時ディレクトリを作成し、atexit(rmtemp) を使ってプログラム終了時にそのディレクトリを削除するように設定されていました。
    • 変更後、tmpdir 変数は EXTERN char* tmpdir; として src/cmd/ld/lib.h で外部変数として宣言され、他のファイルからアクセス可能になりました。
    • hostlinksetup 関数内で、tmpdirnil (Goにおける null に相当) である場合にのみ、新しい一時ディレクトリを作成し、クリーンアップを設定するようになりました。これにより、-tmpdir フラグで明示的にディレクトリが指定された場合(tmpdirnil でなくなる)は、リンカが自動的に一時ディレクトリを作成したり削除したりしないようになります。つまり、ユーザーが指定したディレクトリはリンカによって削除されません。
  3. flagstr の利用: コマンドラインフラグを解析するために flagstr 関数が使用されています。これは、文字列型のフラグを定義し、その値を指定された変数に格納するためのGoの標準的なフラグパーシングメカニズムの一部です。

この変更により、リンカはデフォルトでは引き続き一時ファイルを自動的にクリーンアップしますが、ユーザーが -tmpdir /path/to/my/temp のようにフラグを指定すると、指定されたディレクトリに一時ファイルが生成され、リンカの終了後もそのファイルが残るようになります。これは、デバッグや詳細な分析を行う際に非常に役立ちます。

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

このコミットで変更された主要なファイルは以下の通りです。

  • src/cmd/5l/obj.c: ARMアーキテクチャ向けリンカのメイン関数に -tmpdir フラグの定義を追加。
  • src/cmd/6l/obj.c: x86-64アーキテクチャ向けリンカのメイン関数に -tmpdir フラグの定義を追加。また、flag_shared フラグの定義順序が変更されていますが、これは機能的な変更ではありません。
  • src/cmd/8l/obj.c: x86アーキテクチャ向けリンカのメイン関数に -tmpdir フラグの定義を追加。
  • src/cmd/ld/lib.c: リンカの共通ライブラリ。一時ディレクトリの管理ロジックが変更され、tmpdir 変数の宣言が削除され、外部変数として扱われるようになりました。また、tmpdirnil の場合にのみ一時ディレクトリを作成し、クリーンアップを設定する条件が追加されました。
  • src/cmd/ld/lib.h: リンカの共通ヘッダファイル。tmpdir 変数が EXTERN char* tmpdir; として外部変数宣言されました。また、segsym セグメントの宣言が削除されていますが、これはこのコミットの主要な目的とは直接関係なく、おそらく別の変更の一部か、クリーンアップの一環です。

コアとなるコードの解説

src/cmd/5l/obj.c, src/cmd/6l/obj.c, src/cmd/8l/obj.c における変更

これらのファイルでは、リンカのメイン関数内で新しいコマンドラインフラグ -tmpdir が追加されています。

// 変更前 (例: src/cmd/5l/obj.c)
// flagcount("s", "disable symbol table", &debug['s']);

// 変更後 (例: src/cmd/5l/obj.c)
flagstr("tmpdir", "leave temporary files in this directory", &tmpdir);
// flagcount("s", "disable symbol table", &debug['s']);
  • flagstr("tmpdir", "leave temporary files in this directory", &tmpdir);
    • flagstr: Goのフラグパーシングライブラリの一部で、文字列型のコマンドラインフラグを定義するために使用されます。
    • 第一引数 "tmpdir": フラグの名前です。コマンドラインで -tmpdir として指定されます。
    • 第二引数 "leave temporary files in this directory": フラグの短い説明です。ヘルプメッセージなどに表示されます。
    • 第三引数 &tmpdir: フラグの値が格納される変数のアドレスです。この tmpdir 変数は、src/cmd/ld/lib.h で外部変数として宣言されています。

src/cmd/ld/lib.c における変更

このファイルでは、一時ディレクトリの管理ロジックが変更されています。

// 変更前
// static char *tmpdir; // ローカル変数として宣言されていた

static void
rmtemp(void)
{
	// ... (一時ディレクトリ削除ロジック)
}

void
hostlinksetup(void)
{
	// ...
	// create temporary directory and arrange cleanup
	// TODO: Add flag to specify tempdir, which is then not cleaned up.
	tmpdir = mktempdir(); // 常に新しい一時ディレクトリを作成
	atexit(rmtemp);       // 常に終了時に削除を設定
	// ...
}

// 変更後
// tmpdir の宣言は lib.h に移動

static void
rmtemp(void)
{
	// ... (一時ディレクトリ削除ロジック)
}

void
hostlinksetup(void)
{
	// ...
	if(tmpdir == nil) { // tmpdir が nil の場合のみ
		tmpdir = mktempdir(); // 新しい一時ディレクトリを作成
		atexit(rmtemp);       // 終了時に削除を設定
	}
	// ...
}
  • static char *tmpdir; の削除: tmpdir 変数がこのファイル内でローカルに宣言されていたものが削除され、src/cmd/ld/lib.h で外部変数として宣言されるようになりました。これにより、コマンドラインフラグからこの変数に値を設定できるようになります。
  • if(tmpdir == nil) 条件の追加: hostlinksetup 関数内で、一時ディレクトリの作成とクリーンアップの設定が if(tmpdir == nil) という条件ブロック内に移動しました。
    • tmpdir == nil: これは、ユーザーがコマンドラインで -tmpdir フラグを指定しなかった場合(つまり、tmpdir 変数に値が設定されていない場合)に真となります。
    • mktempdir(): 一時ディレクトリを作成し、そのパスを返します。
    • atexit(rmtemp): プログラムが正常終了する際に rmtemp 関数を呼び出すように登録します。rmtemp 関数は、作成された一時ディレクトリを削除する役割を担います。

この変更により、ユーザーが -tmpdir フラグでパスを指定した場合、tmpdirnil ではなくなるため、リンカは自動的に一時ディレクトリを作成したり、そのディレクトリを削除したりしなくなります。これにより、ユーザーが指定した一時ファイルが保持されるようになります。

src/cmd/ld/lib.h における変更

// 変更前
// EXTERN	char*\tinterpreter;
// EXTERN	Segment	segsym;
// EXTERN	Segment segdwarf;

// 変更後
EXTERN	char*\tinterpreter;
EXTERN	char*\ttmpdir; // 新しく追加
// EXTERN	Segment	segsym; // 削除
EXTERN	Segment	segdwarf;
  • EXTERN char* tmpdir; の追加: tmpdir 変数が外部変数として宣言されました。これにより、リンカの異なる部分(例えば、各アーキテクチャの obj.c ファイル)からこの変数にアクセスし、値を設定できるようになります。
  • EXTERN Segment segsym; の削除: segsym セグメントの宣言が削除されています。これはこのコミットの主要な目的とは直接関係ありませんが、リンカの内部構造に関する別の変更の一部である可能性があります。

関連リンク

  • Go言語のリンカに関するドキュメント (Goの公式ドキュメントやツールチェインのソースコードが参考になります)
  • Go言語のビルドプロセスに関する情報

参考にした情報源リンク