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

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

このコミットは、Go言語のツールチェインの一部であるcmd/5lリンカにおける、Plan 9オペレーティングシステム向けの実行可能ファイルのヘッダが不正になる問題を修正するものです。具体的には、テキストセグメントのサイズ計算と、Plan 9バイナリにおけるpclntab(PC-lineテーブル)の扱いに関するバグが修正されています。

コミット

commit 1a9a63961b03e386af000ed83fde5fca325c6bd1
Author: Anthony Martin <ality@pbrane.org>
Date:   Sat Jan 12 03:13:55 2013 -0800

    cmd/5l: fix invalid executable header on Plan 9
    
    R=minux.ma, lucio.dere
    CC=golang-dev
    https://golang.org/cl/7094048

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

https://github.com/golang/go/commit/1a9a63961b03e386af000ed83fde5fca325c6bd1

元コミット内容

cmd/5l: fix invalid executable header on Plan 9

このコミットメッセージは簡潔に、cmd/5l(ARMアーキテクチャ向けGoリンカ)がPlan 9オペレーティングシステム用の実行可能ファイルを生成する際に、不正なヘッダが出力される問題を修正したことを示しています。

変更の背景

Go言語は、その初期からPlan 9オペレーティングシステムをサポートしていました。cmd/5lは、ARMアーキテクチャ(Goの内部では5で表現される)向けのGoプログラムをリンクし、実行可能ファイルを生成する役割を担っています。

このコミットが行われた2013年当時、Goのツールチェインはまだ活発に開発されており、様々なプラットフォームやアーキテクチャへの対応が改善されていました。Plan 9向けのバイナリ生成において、リンカが生成する実行可能ファイルのヘッダ情報に誤りがあり、その結果、Plan 9システム上で正しくロード・実行できない問題が発生していました。

具体的には、実行可能ファイルのヘッダには、プログラムのコードセグメント(テキストセグメント)のサイズなど、OSがプログラムをメモリにロードするために必要なメタデータが含まれています。このサイズ情報が誤っていると、OSはメモリ割り当てや実行開始アドレスの特定に失敗し、プログラムの起動が不可能になります。

また、Goのバイナリには、デバッグやプロファイリング、スタックトレースの生成に不可欠なpclntab(PC-lineテーブル)という情報が含まれています。このテーブルは、プログラムカウンタ(PC)の値とソースコードのファイル名・行番号をマッピングする役割を果たします。Plan 9向けのバイナリ生成プロセスにおいて、このpclntabが適切に処理され、実行可能ファイルに組み込まれていなかった可能性も背景にあります。

このコミットは、これらの問題を解決し、Plan 9上でのGoプログラムの実行可能性を確保することを目的としています。

前提知識の解説

  • Go言語のツールチェイン: Go言語は、コンパイラ、リンカ、アセンブラなどを含む独自のツールチェインを持っています。cmd/5lはそのリンカの一つで、ARMアー32ビットアーキテクチャ(GoではGOARCH=arm、内部的には5)向けの実行可能ファイルを生成します。
  • リンカ (Linker): コンパイラによって生成されたオブジェクトファイル(機械語コード)を結合し、必要なライブラリとリンクして、最終的な実行可能ファイルを生成するプログラムです。リンカは、コード、データ、シンボル情報などを配置し、実行可能ファイルのヘッダを構築します。
  • 実行可能ファイルヘッダ: 実行可能ファイルの先頭に位置するデータ構造で、オペレーティングシステムがプログラムをメモリにロードし、実行を開始するために必要な情報(例: エントリポイントのアドレス、コードセグメントとデータセグメントのサイズ、メモリレイアウト、シンボルテーブルの位置など)を含んでいます。ヘッダの形式はOSやアーキテクチャによって異なります(例: LinuxのELF、WindowsのPE、macOSのMach-O、Plan 9のa.out派生形式)。
  • Plan 9: ベル研究所で開発された分散オペレーティングシステムです。ファイルシステムを中心としたユニークな設計思想を持ち、Go言語の設計にも影響を与えたと言われています。Goは初期からPlan 9をサポートしており、Goのソースコード内にはPlan 9関連のコードが多数存在します。
  • pclntab (PC-line table): Goの実行可能ファイルに含まれる重要なデバッグ情報の一つです。プログラムカウンタ(PC)のアドレスと、対応するソースコードのファイル名、行番号、関数名などの情報をマッピングします。これにより、パニック発生時のスタックトレース表示や、プロファイリングツールでの関数ごとの実行時間分析などが可能になります。
  • HEADTYPE: Goリンカの内部で使用される変数で、ターゲットとするオペレーティングシステムとアーキテクチャに応じた実行可能ファイルの形式を識別します。例えば、Hplan9x32は32ビットARMアーキテクチャ向けのPlan 9バイナリを、iselfはELF形式(Linuxなどで使用)を指します。
  • segtext.filelentextsize:
    • segtext: 実行可能ファイルのテキスト(コード)セグメントを表す構造体または変数。
    • segtext.filelen: テキストセグメントがファイル内で占める実際のバイト長。
    • textsize: 以前使用されていた可能性のある、テキストセグメントのサイズを示す変数。このコミットでは、textsizeが正確なファイルサイズを反映していない、または冗長であると判断され、segtext.filelenに置き換えられています。

技術的詳細

このコミットの主要な変更点は、src/cmd/5l/asm.cファイル内のasmb関数に集中しています。asmb関数は、リンカの最終段階で実行可能ファイルの各セクションをアセンブルし、ヘッダ情報やシンボルテーブルなどを書き込む役割を担っています。

  1. HEADTYPEによる処理の分岐: 変更前は、ELF形式(iself)の処理が直接記述されていましたが、変更後はswitch(HEADTYPE)文が導入され、ターゲットOSのタイプに応じて処理が分岐するようになりました。これにより、異なるOS向けのバイナリ生成ロジックが明確に分離され、保守性が向上しています。

  2. Plan 9 (Hplan9x32) 向けのpclntab処理の追加: case Hplan9x32:ブロックが新設され、Plan 9向けの特別な処理が追加されました。

    • asmplan9sym(): Plan 9固有のシンボル情報をアセンブルする関数が呼び出されます。
    • pclntabの明示的な書き込み: lookup("pclntab", 0)によってpclntabシンボルが検索され、その内容(sym->p)とサイズ(sym->nplcsizeに格納)が実行可能ファイルに直接書き込まれるようになりました。これは、Plan 9バイナリがpclntabを正しく含むために不可欠なステップです。以前は、この情報が適切にヘッダに組み込まれていなかったか、または誤った方法で処理されていた可能性があります。
  3. textsizeからsegtext.filelenへの置き換え: 複数の箇所で、テキストセグメントのサイズを示すために使用されていたグローバル変数textsizeが、segtext.filelenに置き換えられました。

    • 実行可能ファイルのヘッダに書き込まれるテキストセグメントのサイズ情報 (lputl(textsize+HEADR)lputl(segtext.filelen+HEADR) に変更)。
    • Plan 9向けのヘッダ情報 (lput(textsize)lput(segtext.filelen) に変更)。
    • デバッグ出力 (print("textsize=%d\\n", textsize)print("textsize=%d\\n", segtext.filelen) に変更)。
    • 合計サイズ計算 (textsize+segdata.len+symsize+lcsizesegtext.filelen+segdata.len+symsize+lcsize に変更)。

    この変更は、textsizeがリンカの処理過程で常に正確なテキストセグメントの最終的なファイルサイズを反映していなかったためと考えられます。segtext.filelenは、実際にファイルに書き込まれるテキストセグメントの長さを正確に表しており、これにより実行可能ファイルのヘッダに正しいサイズ情報が記録されるようになります。

  4. src/cmd/5l/l.hからのtextsizeの削除: textsize変数が不要になったため、ヘッダファイルsrc/cmd/5l/l.hからその宣言が削除されました。これは、コードのクリーンアップと、冗長な変数の排除を意味します。

これらの変更により、cmd/5lはPlan 9向けの実行可能ファイルを生成する際に、正しいヘッダ情報(特にテキストセグメントのサイズ)とpclntabを適切に含めることができるようになり、Plan 9システム上でのGoバイナリの互換性と信頼性が向上しました。

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

src/cmd/5l/asm.c

--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -546,18 +548,34 @@ asmb(void)\
 			break;
 		}
 	cseek(symo);
-	if(iself) {
-		if(debug['v'])
-			Bprint(&bso, "%5.2f elfsym\n", cputime());
-		asmelfsym();
+	switch(HEADTYPE) {
+	default:
+		if(iself) {
+			if(debug['v'])
+				Bprint(&bso, "%5.2f elfsym\n", cputime());
+			asmelfsym();
+			cflush();
+			cwrite(elfstrdat, elfstrsize);
+	
+			if(debug['v'])
+				Bprint(&bso, "%5.2f dwarf\n", cputime());
+			dwarfemitdebugsections();
+		}
+		break;
+	case Hplan9x32:
+		asmplan9sym();
 		cflush();
-		cwrite(elfstrdat, elfstrsize);
 
-		if(debug['v'])
-			Bprint(&bso, "%5.2f dwarf\n", cputime());
-		dwarfemitdebugsections();
+		sym = lookup("pclntab", 0);
+		if(sym != nil) {
+			lcsize = sym->np;
+			for(i=0; i < lcsize; i++)
+				cput(sym->p[i]);
+
+			cflush();
+		}
+		break;
 	}
-	cflush();
 	}
 
 	cursym = nil;
@@ -581,7 +599,7 @@ asmb(void)\
 			 - 8) / 4);		/* BL - entry code */
 
 		lputl(0xef000011);		/* SWI - exit code */
-		lputl(textsize+HEADR);		/* text size */
+		lputl(segtext.filelen+HEADR);		/* text size */
 		lputl(segdata.filelen);			/* data size */
 		lputl(0);			/* sym size */
 
@@ -601,7 +619,7 @@ asmb(void)\
 		break;
 	case Hplan9x32:	/* plan 9 */
 		lput(0x647);			/* magic */
-		lput(textsize);			/* sizes */
+		lput(segtext.filelen);			/* sizes */
 		lput(segdata.filelen);
 		lput(segdata.len - segdata.filelen);
 		lput(symsize);			/* nsyms */
@@ -626,12 +644,12 @@ asmb(void)\
 	}\n\
 	cflush();
 	if(debug['c']){
-		print("textsize=%d\n", textsize);
+		print("textsize=%d\n", segtext.filelen);
 		print("datsize=%ulld\n", segdata.filelen);
 		print("bsssize=%ulld\n", segdata.len - segdata.filelen);
 		print("symsize=%d\n", symsize);
 		print("lcsize=%d\n", lcsize);
-		print("total=%lld\n", textsize+segdata.len+symsize+lcsize);
+		print("total=%lld\n", segtext.filelen+segdata.len+symsize+lcsize);
 	}
 }
 

src/cmd/5l/l.h

--- a/src/cmd/5l/l.h
+++ b/src/cmd/5l/l.h
@@ -304,7 +304,6 @@ EXTERN	char*	rpath;
 EXTERN	uint32	stroffset;
 EXTERN	int32	symsize;
 EXTERN	Sym*	textp;
-EXTERN	int32	textsize;
 EXTERN	int	version;
 EXTERN	char	xcmp[C_GOK+1][C_GOK+1];
 EXTERN	Prog	zprg;

コアとなるコードの解説

src/cmd/5l/asm.c の変更点

  1. asmb 関数内の switch(HEADTYPE) ブロック:

    • 以前はif(iself)という条件分岐でELF形式の処理を行っていましたが、これをswitch(HEADTYPE)に置き換え、より汎用的な構造にしました。
    • default:ケースには、既存のELF関連の処理(asmelfsym(), dwarfemitdebugsections()など)が移動されました。これにより、他のHEADTYPEが追加された場合でも、既存のELF処理に影響を与えずに新しいケースを追加できるようになります。
    • case Hplan9x32: の追加:
      • asmplan9sym(): Plan 9固有のシンボルテーブルを生成する関数を呼び出します。
      • sym = lookup("pclntab", 0);: pclntabという名前のシンボルをリンカのシンボルテーブルから検索します。このシンボルは、Goコンパイラが生成したPC-lineテーブルのデータを含んでいます。
      • if(sym != nil) { ... }: pclntabシンボルが見つかった場合、その内容をファイルに書き込みます。
        • lcsize = sym->np;: sym->nppclntabのバイトサイズを表します。これをlcsize変数に格納します。
        • for(i=0; i < lcsize; i++) cput(sym->p[i]);: sym->ppclntabのデータへのポインタであり、このループでその内容をバイト単位で出力ファイルに書き込みます。
        • cflush();: バッファリングされた出力をファイルにフラッシュします。 このpclntabの明示的な書き込みは、Plan 9バイナリがデバッグ情報を正しく含むために重要です。
  2. textsize から segtext.filelen への置き換え:

    • lputl(textsize+HEADR)lputl(segtext.filelen+HEADR) に変更された箇所は、実行可能ファイルのヘッダに書き込まれるテキストセグメントのサイズを修正しています。HEADRはヘッダの固定サイズ部分を表す定数です。この変更により、ヘッダに書き込まれるテキストセグメントのサイズが、実際にファイルに書き込まれるセグメントの長さと一致するようになります。
    • case Hplan9x32: 内の lput(textsize)lput(segtext.filelen) に変更された箇所も同様に、Plan 9固有のヘッダにおけるテキストセグメントのサイズ情報を修正しています。
    • デバッグ出力 (print文) や合計サイズ計算の箇所でも、textsizesegtext.filelenに置き換えられています。これは、デバッグ情報や内部計算においても、より正確なテキストセグメントのサイズを使用するためです。

src/cmd/5l/l.h の変更点

  • EXTERN int32 textsize; の行が削除されました。これは、textsize変数がもはや使用されなくなったため、リンカのグローバル変数リストから削除されたことを意味します。これにより、コードの冗長性が減り、保守性が向上します。

これらの変更は、GoリンカがPlan 9向けの実行可能ファイルを生成する際の正確性を大幅に向上させ、特にヘッダの整合性とデバッグ情報の完全性を確保しています。

関連リンク

参考にした情報源リンク