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

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

このコミットは、Go言語のリンカである6l(amd64アーキテクチャ向け)が、Plan 9オペレーティングシステムの64-bit版に対応するための修正を含んでいます。具体的には、Plan 9の64-bit版が採用する2メガバイトのメモリページサイズへの対応、32-bitと64-bit Plan 9のロジック修正(64-bitをデフォルトとする)、そしてシンボルテーブル生成ロジックの追加が主な変更点です。

変更されたファイルは以下の通りです。

  • src/cmd/6l/asm.c: アセンブリコードの生成とリンカの出力処理に関連する部分。
  • src/cmd/6l/obj.c: オブジェクトファイルの読み込みとリンカの初期設定に関連する部分。
  • src/cmd/ld/data.c: リンクされるデータの配置に関連する部分。
  • src/cmd/ld/symtab.c: シンボルテーブルの生成と書き込みに関連する部分。

コミット

commit 154c84cdacd00a12da7ec44fa34a5c7d01823827
Author: Akshat Kumar <seed@mail.nanosouffle.net>
Date:   Tue May 29 12:32:42 2012 -0400

    cmd/6l: Fixes for 64-bit Plan 9
    
    Plan 9 versions for amd64 have 2 megabyte pages.
    This also fixes the logic for 32-bit vs 64-bit Plan 9,
    making 64-bit the default, and adds logic to generate
    a symbols table.
    
    R=golang-dev, rsc, rminnich, ality, 0intro
    CC=golang-dev, john
    https://golang.org/cl/6218046
---
 src/cmd/6l/asm.c    | 23 +++++++++++++++++++----\
 src/cmd/6l/obj.c    | 12 ++++++------
 src/cmd/ld/data.c   |  2 +-\
 src/cmd/ld/symtab.c |  9 +++++++--
 4 files changed, 33 insertions(+), 13 deletions(-)

diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c
index 7939b10e30..8d8c6d725c 100644
--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -715,7 +715,8 @@ asmb(void)\
  	ElfPhdr *ph, *pph;\
  	ElfShdr *sh;\
  	Section *sect;\
-\tint o;\
+\tSym *sym;\
+\tint i, o;\
 \
  	if(debug['v'])\
  		Bprint(&bso, "%5.2f asmb\\n", cputime());\
@@ -763,6 +764,7 @@ asmb(void)\
  	default:\
  		diag("unknown header type %d", HEADTYPE);\
  	case Hplan9x32:\
+\tcase Hplan9x64:\
  	case Helf:\
  		break;\
  	case Hdarwin:\
@@ -798,7 +800,7 @@ asmb(void)\
  		Bflush(&bso);\
  		switch(HEADTYPE) {\
  		default:\
-\t\tcase Hplan9x32:\
+\t\tcase Hplan9x64:\
  		case Helf:\
  			debug['s'] = 1;\
  			symo = HEADR+segtext.len+segdata.filelen;\
@@ -833,6 +835,19 @@ asmb(void)\
  			\t\tdwarfemitdebugsections();\
  			\t}\
  			\tbreak;\
+\t\tcase Hplan9x64:\
+\t\t\tasmplan9sym();\
+\t\t\tcflush();\
+\
+\t\t\tsym = lookup("pclntab", 0);\
+\t\t\tif(sym != nil) {\
+\t\t\t\tlcsize = sym->np;\
+\t\t\t\tfor(i=0; i < lcsize; i++)\
+\t\t\t\t\tcput(sym->p[i]);\
+\t\t\t\t\
+\t\t\t\tcflush();\
+\t\t\t}\
+\t\t\tbreak;\
  		case Hwindows:\
  			if(debug['v'])\
  			       Bprint(&bso, "%5.2f dwarf\\n", cputime());\
@@ -848,7 +863,7 @@ asmb(void)\
  	cseek(0L);\
  	switch(HEADTYPE) {\
  	default:\
-\tcase Hplan9x32:\t/* plan9 */\
+\tcase Hplan9x64:\t/* plan9 */\
  		magic = 4*26*26+7;\
  		magic |= 0x00008000;\t\t/* fat header */\
  		lputb(magic);\t\t\t/* magic */\
@@ -862,7 +877,7 @@ asmb(void)\
  		lputb(lcsize);\t\t\t/* line offsets */\
  		vputb(vl);\t\t\t/* va of entry */\n \t\tbreak;\
-\tcase Hplan9x64:\t/* plan9 */\
+\tcase Hplan9x32:\t/* plan9 */\
  		magic = 4*26*26+7;\
  		lputb(magic);\t\t\t/* magic */\
  		lputb(segtext.filelen);\t\t/* sizes */
diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c
index 64d1730842..cfce2111b8 100644
--- a/src/cmd/6l/obj.c
+++ b/src/cmd/6l/obj.c
@@ -58,8 +58,8 @@ Header headers[] = {\
  };\
  \
  /*\
- *\t-Hplan9x32 -T4136 -R4096\tis plan9 64-bit format\
- *\t-Hplan9 -T4128 -R4096\t\tis plan9 32-bit format\
+ *\t-Hplan9x32 -T4128 -R4096\tis plan9 32-bit format\
+ *\t-Hplan9 -T0x200028 -R0x200000\tis plan9 64-bit format\
  *\t-Helf -T0x80110000 -R4096\tis ELF32\
  *\t-Hdarwin -Tx -Rx\t\tis apple MH-exec\
  *\t-Hlinux -Tx -Rx\t\t\tis linux elf-exec\
@@ -164,7 +164,7 @@ main(int argc, char *argv[])\
  		diag("unknown -H option");\
  		errorexit();\
  	case Hplan9x32:\t/* plan 9 */\
-\t\tHEADR = 32L+8L;\
+\t\tHEADR = 32L;\
  \t\tif(INITTEXT == -1)\
  \t\t\tINITTEXT = 4096+HEADR;\
  \t\tif(INITDAT == -1)\
@@ -173,13 +173,13 @@ main(int argc, char *argv[])\
  \t\t\tINITRND = 4096;\
  \t\tbreak;\
  \tcase Hplan9x64:\t/* plan 9 */\
-\t\tHEADR = 32L;\
+\t\tHEADR = 32L + 8L;\
  \t\tif(INITTEXT == -1)\
-\t\t\tINITTEXT = 4096+32;\
+\t\t\tINITTEXT = 0x200000+HEADR;\
  \t\tif(INITDAT == -1)\
  \t\t\tINITDAT = 0;\
  \t\tif(INITRND == -1)\
-\t\t\tINITRND = 4096;\
+\t\t\tINITRND = 0x200000;\
  \t\tbreak;\
  \tcase Helf:\t/* elf32 executable */\
  \t\tHEADR = rnd(52L+3*32L, 16);\
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c
index 4eff24024e..1f64a84708 100644
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -1058,7 +1058,7 @@ address(void)\
  	segdata.filelen = 0;\
  	if(HEADTYPE == Hwindows)\
  		segdata.fileoff = segtext.fileoff + rnd(segtext.len, PEFILEALIGN);\
-\tif(HEADTYPE == Hplan9x32)\
+\tif(HEADTYPE == Hplan9x64 || HEADTYPE == Hplan9x32)\
  		segdata.fileoff = segtext.fileoff + segtext.filelen;\
  	data = nil;\
  	noptr = nil;\
diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c
index 359a658e74..c43051a392 100644
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -125,7 +125,7 @@ asmelfsym(void)\
  static void\
  putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go)\
  {\
-\tint i;\
+\tint i, l;\
 \
  	USED(go);\
  	USED(ver);\
@@ -144,6 +144,11 @@ putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go)\
  	case 'z':\
  	case 'Z':\
  	case 'm':\
+\t\tl = 4;\
+\t\tif(HEADTYPE == Hplan9x64 && !debug['8']) {\
+\t\t\tlputb(addr>>32);\
+\t\t\tl = 8;\
+\t\t}\
  \t\tlputb(addr);\
  \t\tcput(t+0x80); /* 0x80 is variable length */\
 \
@@ -164,7 +169,7 @@ putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go)\
  \t\t\t\tcput(s[i]);\
  \t\t\tcput(0);\
  \t\t}\
-\t\tsymsize += 4 + 1 + i + 1;\
+\t\tsymsize += l + 1 + i + 1;\
  \t\tbreak;\
  \tdefault:\
  \t\treturn;\

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

https://github.com/golang/go/commit/154c84cdacd00a12da7ec44fa34a5c7d01823827

元コミット内容

このコミットは、Go言語の6lリンカ(amd64アーキテクチャ向け)に対する修正です。主な目的は、64-bit版Plan 9オペレーティングシステムへの対応を改善することです。

具体的には以下の3点が含まれます。

  1. Plan 9 AMD64版の2メガバイトページへの対応: Plan 9の64-bitバージョンが2メガバイトのメモリページを使用することに対応するための修正。
  2. 32-bit vs 64-bit Plan 9のロジック修正と64-bitのデフォルト化: 32-bitと64-bitのPlan 9ターゲットを区別するリンカ内部のロジックを修正し、64-bit版をデフォルトのターゲットとして扱うように変更。
  3. シンボルテーブル生成ロジックの追加: 実行ファイルにシンボルテーブルを生成するロジックを追加。これはデバッグやプロファイリングに不可欠な情報を提供します。

変更の背景

この変更の背景には、Go言語が様々なプラットフォームで動作することを目指しているという設計思想があります。Plan 9は、ベル研究所で開発された分散オペレーティングシステムであり、Go言語の設計者の一部がその開発に関わっていたという歴史的経緯から、Go言語にとって重要なターゲットプラットフォームの一つです。

当時のPlan 9の64-bit版(amd64アーキテクチャ向け)は、メモリ管理において2メガバイトの大きなページサイズを採用していました。これは、従来の4キロバイトページと比較して、TLB(Translation Lookaside Buffer)ミスを減らし、大規模なメモリ空間を効率的に管理するための最適化です。しかし、Goリンカがこの新しいページサイズに対応していなければ、生成される実行ファイルがPlan 9上で正しく動作しない、あるいはパフォーマンスが低下する可能性がありました。

また、32-bitと64-bitのPlan 9ターゲットをリンカが適切に区別し、デフォルトで64-bitを優先するようにすることは、現代の主流である64-bitシステムへの対応を強化し、開発者がより簡単に64-bit Plan 9向けのGoプログラムをビルドできるようにするために必要でした。

さらに、実行ファイルにシンボルテーブルを含めることは、デバッグ作業において極めて重要です。シンボルテーブルは、プログラム内の変数名、関数名、その他の識別子と、それらがメモリ上のどこに配置されているかという情報を提供します。これにより、デバッガはソースコードレベルでのデバッグを可能にし、開発者がプログラムの挙動を詳細に分析できるようになります。このコミット以前は、Plan 9向けのGoバイナリにはシンボルテーブルが適切に生成されていなかった可能性があり、その機能を追加することで、Plan 9上でのGo開発の利便性とデバッグ能力が向上しました。

前提知識の解説

Plan 9 オペレーティングシステム

Plan 9 from Bell Labsは、1980年代半ばにベル研究所で開発された分散オペレーティングシステムです。UNIXの設計思想を継承しつつ、その限界を克服することを目指して開発されました。Plan 9の最も特徴的な概念は「すべてがファイルである」というUNIXの哲学をさらに推し進め、「すべてがファイルシステムを通じてアクセス可能である」という点です。ネットワーク上のリソース(プロセス、デバイス、サービスなど)もすべてファイルとして表現され、9Pプロトコルという独自のファイルプロトコルを通じて透過的にアクセスできます。

Go言語の設計者であるKen ThompsonやRob Pikeは、Plan 9の開発にも深く関わっていました。このため、Go言語のツールチェイン(特にアセンブラやリンカ)には、Plan 9の設計思想や命名規則(例: 6l8lなど、アーキテクチャを示す数字とl(リンカ)の組み合わせ)が色濃く反映されています。

Go言語のリンカ (6l)

Go言語のビルドプロセスにおいて、リンカは非常に重要な役割を担います。コンパイラによって生成されたオブジェクトファイル(.oファイル)やアーカイブファイル(.aファイル)を結合し、実行可能なバイナリファイルを作成するのがリンカの主な仕事です。

歴史的に、Go言語のツールチェインは、特定のアーキテクチャごとに異なるコマンド名を持っていました。例えば、x86-64(amd64)アーキテクチャ向けのリンカは6l、コンパイラは6g、アセンブラは6aと呼ばれていました。しかし、Go 1.5以降、これらのアーキテクチャ固有のツールはgo toolコマンドの下に統合され、現在ではgo tool linkとして呼び出されます。このコミットが行われた2012年当時は、まだ6lというコマンド名が使われていました。

リンカは、コードとデータをメモリ上のどこに配置するかを決定し、外部参照(他のモジュールやライブラリの関数や変数への参照)を解決します。また、実行ファイルのヘッダ情報(OSがプログラムをロードするために必要な情報)を生成し、必要に応じてデバッグ情報やシンボルテーブルを埋め込みます。

メモリページとページサイズ

現代のオペレーティングシステムでは、仮想記憶という技術が広く使われています。これは、物理メモリ(RAM)のサイズに縛られずに、より大きなメモリ空間をプログラムに提供する仕組みです。仮想記憶は、メモリを「ページ」と呼ばれる固定サイズのブロックに分割して管理します。プログラムがメモリにアクセスする際、CPUのMMU(Memory Management Unit)が仮想アドレスを物理アドレスに変換します。

ページサイズは、このページの大きさを示します。一般的なページサイズは4キロバイトですが、AMD64のような64-bitアーキテクチャでは、2メガバイトや1ギガバイトといったより大きなページサイズ(「ラージページ」または「ヒュージページ」と呼ばれることもあります)をサポートしています。

大きなページサイズを使用する利点はいくつかあります。

  • TLBミスの削減: TLBは、仮想アドレスから物理アドレスへの変換情報をキャッシュするCPU内の高速なメモリです。ページサイズが大きいほど、同じ量のメモリ空間をカバーするために必要なTLBエントリの数が減り、TLBミス(TLBに情報がないために変換が遅くなる現象)が減少します。これにより、メモリアクセス性能が向上します。
  • ページテーブルのサイズの削減: ページサイズが大きいと、OSが管理する必要があるページテーブルのエントリ数が減り、メモリ使用量を削減できます。

しかし、大きなページサイズには欠点もあります。

  • 内部フラグメンテーションの増加: ページサイズが大きいと、ページ内の未使用領域が増える可能性があり、メモリの無駄が生じやすくなります。
  • メモリ割り当ての粒度: メモリ割り当ての最小単位が大きくなるため、細かいメモリ管理が難しくなる場合があります。

Plan 9の64-bit版が2メガバイトページを採用したことは、Goリンカがそのメモリレイアウトとアラインメントの要件を正しく理解し、対応する必要があることを意味します。

シンボルテーブル

シンボルテーブルは、コンパイルおよびリンクの過程で生成されるデータ構造であり、プログラム内の様々な「シンボル」(変数名、関数名、クラス名など)に関する情報を含んでいます。具体的には、シンボル名、その型、スコープ、そしてメモリ上のアドレスなどの情報が格納されます。

シンボルテーブルの主な用途は以下の通りです。

  • リンキング: リンカはシンボルテーブルを使用して、異なるオブジェクトファイル間で参照されるシンボル(例えば、あるファイルで定義された関数が別のファイルで呼び出される場合)を解決し、最終的な実行ファイルを作成します。
  • デバッグ: デバッガはシンボルテーブルを利用して、実行中のプログラムのメモリ上のアドレスをソースコードの行番号や変数名にマッピングします。これにより、開発者はソースコードレベルでブレークポイントを設定したり、変数の値を検査したりすることが可能になります。
  • プロファイリング: プロファイラはシンボルテーブルを使って、プログラムのどの部分(関数など)がCPU時間やメモリを多く消費しているかを特定します。

Go言語のバイナリには、デフォルトでデバッグ用のシンボルテーブルが埋め込まれています。ただし、バイナリサイズを削減するために、リンカオプション(例: -s-w)を使ってシンボル情報を除去することも可能です。このコミットは、Plan 9向けのバイナリにシンボルテーブルを生成するロジックを追加することで、Plan 9上でのGoプログラムのデバッグ可能性を向上させるものです。

HEADTYPE (Hplan9x32, Hplan9x64)

HEADTYPEは、Goリンカの内部でターゲットとなるオペレーティングシステムとアーキテクチャを識別するために使用される定数です。リンカは、このHEADTYPEの値に基づいて、生成する実行ファイルのフォーマット(ELF、Mach-O、PE、Plan 9独自のフォーマットなど)や、メモリレイアウト、ヘッダ構造などを決定します。

  • Hplan9x32: 32-bit Plan 9向けのターゲットを示す内部定数。
  • Hplan9x64: 64-bit Plan 9向けのターゲットを示す内部定数。

これらの定数は、GoのクロスコンパイルにおいてGOOS=plan9GOARCH=386またはGOARCH=amd64を設定した際に、リンカ内部で対応するHEADTYPEにマッピングされ、適切なバイナリが生成されるように制御されます。このコミットでは、これらのHEADTYPEの扱いが修正され、特にHplan9x64が正しく処理されるように変更されています。

技術的詳細

このコミットは、Goリンカの複数のファイルにわたる変更を通じて、64-bit Plan 9への対応を強化しています。

src/cmd/6l/asm.c の変更

このファイルは、リンカが最終的な実行ファイルをアセンブル(構築)する際の主要なロジックを含んでいます。

  • asmb関数の変更:
    • Sym *sym; int i, o; の追加: シンボルテーブル関連の処理のために、Symポインタsymとループ変数iが追加されています。
    • case Hplan9x64: の追加: asmb関数内のswitch(HEADTYPE)文において、Hplan9x64Hplan9x32Helfと同様に認識されるように追加されました。これにより、64-bit Plan 9ターゲットがリンカによって適切に処理されるようになります。
    • シンボルテーブル生成ロジックの追加: switch(HEADTYPE)文の内部で、Hplan9x64の場合に以下の処理が追加されました。
      • asmplan9sym();: Plan 9形式のシンボルテーブルを生成する関数が呼び出されます。
      • cflush();: バッファをフラッシュします。
      • sym = lookup("pclntab", 0);: pclntab(Program Counter Line Table)というシンボルを検索します。これはGoのランタイムが使用する、プログラムカウンタとソースコードの行番号のマッピング情報を含むテーブルです。
      • if(sym != nil) { ... }: pclntabシンボルが見つかった場合、その内容(sym->p)をバイナリに書き込みます。これにより、Goのランタイムがデバッグ情報にアクセスできるようになります。
    • HEADTYPEの入れ替え: ファイルのヘッダ情報を書き込む部分で、Hplan9x32Hplan9x64caseが入れ替わっています。これは、コメントと実際のHEADTYPEの対応を修正し、Hplan9x64がデフォルトのPlan 9ターゲットとして扱われるようにするためと考えられます。具体的には、以前Hplan9x32が処理していたロジックがHplan9x64に、Hplan9x64が処理していたロジックがHplan9x32に割り当てられています。これは、リンカの内部で32-bitと64-bitのPlan 9ターゲットの識別子が逆転していたか、あるいはデフォルトの挙動を変更するために意図的に行われた可能性があります。

src/cmd/6l/obj.c の変更

このファイルは、リンカの初期化とオブジェクトファイルの処理に関連する設定を含んでいます。

  • コメントの修正:
    • -Hplan9x32 -T4136 -R4096 is plan9 64-bit format-Hplan9 -T4128 -R4096 is plan9 32-bit format のコメントが、それぞれ -Hplan9x32 -T4128 -R4096 is plan9 32-bit format-Hplan9 -T0x200028 -R0x200000 is plan9 64-bit format に修正されています。これは、リンカの内部で使われるHEADTYPEと、それに対応するPlan 9の32-bit/64-bitフォーマットの記述が正しくなるように調整されたものです。特に、64-bit Plan 9のテキストセクションの開始アドレス(T)とラウンドアップサイズ(R)が、2MBページサイズを反映した値(0x2000280x200000)に変更されています。
  • main関数の変更:
    • Hplan9x32の場合のHEADR(ヘッダサイズ)が32L+8Lから32Lに減少しています。
    • Hplan9x64の場合のHEADR32Lから32L+8Lに増加しています。
    • Hplan9x64の場合のINITTEXT(テキストセクションの初期アドレス)が4096+32から0x200000+HEADRに変更されています。0x200000は2MB(2 * 1024 * 1024バイト)に相当し、これはPlan 9の64-bit版が2MBページを使用するという情報と一致します。
    • Hplan9x64の場合のINITRND(セクションのアラインメントサイズ)が4096から0x200000に変更されています。これも2MBページサイズへの対応です。

これらの変更は、64-bit Plan 9のメモリレイアウトとアラインメント要件に合わせて、リンカが生成するバイナリの構造を調整するために不可欠です。

src/cmd/ld/data.c の変更

このファイルは、リンカがデータセクションを配置する際のロジックを含んでいます。

  • address関数の変更:
    • if(HEADTYPE == Hplan9x32) の条件が if(HEADTYPE == Hplan9x64 || HEADTYPE == Hplan9x32) に変更されています。これは、データセクションのファイルオフセット(segdata.fileoff)の計算ロジックが、32-bit Plan 9だけでなく、64-bit Plan 9にも適用されるように拡張されたことを意味します。これにより、両方のPlan 9ターゲットでデータセクションが正しく配置されるようになります。

src/cmd/ld/symtab.c の変更

このファイルは、リンカがシンボルテーブルを生成し、バイナリに書き込む際のロジックを含んでいます。

  • putplan9sym関数の変更:
    • int i;int i, l; に変更され、lという変数が追加されています。
    • 64-bitアドレスの扱い: case 'z': case 'Z': case 'm': のブロック内で、シンボルのアドレスを書き込む前に、HEADTYPE == Hplan9x64 && !debug['8'] の条件が追加されています。
      • この条件が真の場合(つまり、64-bit Plan 9ターゲットで、かつデバッグオプション8が有効でない場合)、lputb(addr>>32); が呼び出され、アドレスの上位32ビットが先に書き込まれます。これにより、64-bitアドレスが正しく処理されます。
      • 変数lは、アドレスのサイズ(32-bitの場合は4バイト、64-bitの場合は8バイト)を保持するために使用されます。
    • シンボルサイズ計算の修正: symsize += 4 + 1 + i + 1; の行が symsize += l + 1 + i + 1; に変更されています。これは、シンボルテーブルのエントリサイズを計算する際に、アドレスのサイズが固定の4バイトではなく、l(32-bitまたは64-bitに応じて4または8バイト)になるように修正されたことを意味します。これにより、64-bitアドレスを持つシンボルがシンボルテーブル内で正しくサイズ計算され、配置されるようになります。

これらの変更は、64-bit Plan 9環境でGoプログラムが正しくリンクされ、実行され、そしてデバッグ可能になるために不可欠なものです。特に、2MBページサイズへの対応と64-bitアドレスの適切な処理は、このコミットの核心をなす技術的課題でした。

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

src/cmd/6l/asm.c

--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -798,7 +800,7 @@ asmb(void)
  		Bflush(&bso);
  		switch(HEADTYPE) {
  		default:
-		case Hplan9x32:
+		case Hplan9x64:
  		case Helf:
  			debug['s'] = 1;
  			symo = HEADR+segtext.len+segdata.filelen;
@@ -833,6 +835,19 @@ asmb(void)
  					dwarfemitdebugsections();
  				}
  				break;
+		case Hplan9x64:
+			asmplan9sym();
+			cflush();
+
+			sym = lookup("pclntab", 0);
+			if(sym != nil) {
+				lcsize = sym->np;
+				for(i=0; i < lcsize; i++)
+					cput(sym->p[i]);
+				
+				cflush();
+			}
+			break;
  		case Hwindows:
  			if(debug['v'])
  			       Bprint(&bso, "%5.2f dwarf\\n", cputime());
@@ -848,7 +863,7 @@ asmb(void)
  	cseek(0L);
  	switch(HEADTYPE) {
  	default:
-	case Hplan9x32:	/* plan9 */
+	case Hplan9x64:	/* plan9 */
  		magic = 4*26*26+7;
  		magic |= 0x00008000;		/* fat header */
  		lputb(magic);			/* magic */
@@ -862,7 +877,7 @@ asmb(void)
  		lputb(lcsize);			/* line offsets */
  		vputb(vl);			/* va of entry */
  		break;
-	case Hplan9x64:	/* plan9 */
+	case Hplan9x32:	/* plan9 */
  		magic = 4*26*26+7;
  		lputb(magic);			/* magic */
  		lputb(segtext.filelen);		/* sizes */

src/cmd/6l/obj.c

--- a/src/cmd/6l/obj.c
+++ b/src/cmd/6l/obj.c
@@ -58,8 +58,8 @@ Header headers[] = {
 };
 
 /*
- *	-Hplan9x32 -T4136 -R4096	is plan9 64-bit format
- *	-Hplan9 -T4128 -R4096		is plan9 32-bit format
+ *	-Hplan9x32 -T4128 -R4096	is plan9 32-bit format
+ *	-Hplan9 -T0x200028 -R0x200000	is plan9 64-bit format
  *	-Helf -T0x80110000 -R4096	is ELF32
  *	-Hdarwin -Tx -Rx		is apple MH-exec
  *	-Hlinux -Tx -Rx			is linux elf-exec
@@ -164,7 +164,7 @@ main(int argc, char *argv[])
  		diag("unknown -H option");
  		errorexit();
  	case Hplan9x32:	/* plan 9 */
-		HEADR = 32L+8L;
+		HEADR = 32L;
  		if(INITTEXT == -1)
  			INITTEXT = 4096+HEADR;
  		if(INITDAT == -1)
@@ -173,13 +173,13 @@ main(int argc, char *argv[])
  			INITRND = 4096;
  		break;
  	case Hplan9x64:	/* plan 9 */
-		HEADR = 32L;
+		HEADR = 32L + 8L;
  		if(INITTEXT == -1)
-			INITTEXT = 4096+32;
+			INITTEXT = 0x200000+HEADR;
  		if(INITDAT == -1)
  			INITDAT = 0;
  		if(INITRND == -1)
-			INITRND = 4096;
+			INITRND = 0x200000;
  		break;
  	case Helf:	/* elf32 executable */
  		HEADR = rnd(52L+3*32L, 16);

src/cmd/ld/symtab.c

--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -125,7 +125,7 @@ asmelfsym(void)
 static void
 putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go)
 {
-	int i;
+	int i, l;
 
 	USED(go);
 	USED(ver);
@@ -144,6 +144,11 @@ putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go)
 	case 'z':
 	case 'Z':
 	case 'm':
+		l = 4;
+		if(HEADTYPE == Hplan9x64 && !debug['8']) {
+			lputb(addr>>32);
+			l = 8;
+		}
 		lputb(addr);
 		cput(t+0x80); /* 0x80 is variable length */
 
@@ -164,7 +169,7 @@ putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go)
 			cput(s[i]);
 			cput(0);
 		}
-		symsize += 4 + 1 + i + 1;
+		symsize += l + 1 + i + 1;
 		break;
 	default:
 		return;

コアとなるコードの解説

src/cmd/6l/asm.c の解説

このファイルの変更は、主に64-bit Plan 9バイナリの最終的なアセンブルと、シンボルテーブルの埋め込みに関するものです。

  • switch(HEADTYPE)の変更: 以前はHplan9x32がデフォルトのPlan 9ターゲットとして扱われていましたが、この変更によりHplan9x64がその位置を占めるようになりました。これは、64-bit Plan 9がより一般的なターゲットとなることを反映しています。
  • Hplan9x64ケースの追加とシンボルテーブル生成:
    • asmplan9sym(); は、Plan 9固有のシンボルフォーマットでシンボルテーブルを生成する関数です。これにより、Plan 9環境でGoバイナリのシンボル情報が利用可能になります。
    • sym = lookup("pclntab", 0); とそれに続くループは、Goのランタイムが使用するpclntab(Program Counter Line Table)という特殊なシンボルを検索し、その内容をバイナリに直接書き込んでいます。pclntabは、実行時のスタックトレースやプロファイリングにおいて、プログラムカウンタのアドレスから対応するソースコードのファイル名と行番号を特定するために非常に重要です。このテーブルをバイナリに含めることで、64-bit Plan 9上でのGoプログラムのデバッグとプロファイリング能力が大幅に向上します。
  • ヘッダ書き込みロジックのHEADTYPE入れ替え: ファイルの先頭に書き込まれる実行ファイルのヘッダ情報(マジックナンバー、セクションサイズなど)を決定するswitch文で、Hplan9x32Hplan9x64の処理が入れ替わっています。これは、リンカが内部的にこれらの定数をどのように解釈し、どのフォーマットを生成するかというデフォルトの挙動を調整するためのものです。これにより、Hplan9x64が64-bit Plan 9の「fat header」形式(0x00008000フラグを持つ)を生成し、Hplan9x32が32-bit Plan 9のヘッダを生成するように修正されています。

src/cmd/6l/obj.c の解説

このファイルの変更は、リンカがPlan 9の32-bit/64-bitターゲットを初期化する際のメモリレイアウトとアラインメントに関する設定を調整しています。

  • コメントの修正: リンカのコマンドラインオプションと、それが対応するPlan 9のフォーマットに関するコメントが修正されています。特に、64-bit Plan 9のテキストセクションの開始アドレス(-Tオプションで指定される値)が0x200028(2MB + 40バイト)に、ラウンドアップサイズ(-Rオプションで指定される値)が0x200000(2MB)に変更されている点が重要です。これは、64-bit Plan 9が2MBページを使用するという事実をリンカが認識し、それに合わせてセクションを配置する必要があるためです。
  • HEADR, INITTEXT, INITRND の調整:
    • Hplan9x32の場合のHEADR(ヘッダサイズ)が32L+8Lから32Lに減少し、Hplan9x64の場合のHEADR32Lから32L+8Lに増加しています。これは、32-bitと64-bitのPlan 9バイナリのヘッダ構造の違いを正確に反映するための調整です。
    • Hplan9x64の場合のINITTEXT(テキストセクションの開始仮想アドレス)が4096+32から0x200000+HEADRに変更されています。これは、64-bit Plan 9が2MBページを使用するため、テキストセクションが2MBの境界にアラインされるように設定されたことを意味します。0x200000は2MBを表す16進数です。
    • Hplan9x64の場合のINITRND(セクションのアラインメントサイズ)が4096から0x200000に変更されています。これも同様に、2MBページサイズに合わせたアラインメントを強制するための変更です。

これらの変更により、Goリンカは64-bit Plan 9環境で、2MBページを考慮した適切なメモリレイアウトで実行ファイルを生成できるようになります。

src/cmd/ld/symtab.c の解説

このファイルの変更は、シンボルテーブルの生成において、特に64-bitアドレスを正しく処理するためのものです。

  • putplan9sym関数の変更: この関数は、個々のシンボル情報をPlan 9形式のシンボルテーブルに書き込む役割を担っています。
    • int l; の追加: l変数は、シンボルのアドレスが32-bit(4バイト)か64-bit(8バイト)かを示すために導入されました。
    • 64-bitアドレスの書き込みロジック: if(HEADTYPE == Hplan9x64 && !debug['8']) の条件が追加されています。
      • HEADTYPE == Hplan9x64: ターゲットが64-bit Plan 9であることを確認します。
      • !debug['8']: デバッグオプション8が有効でないことを確認します。このオプションは、おそらくシンボル情報の詳細度を制御するためのもので、通常は64-bitアドレスをフルで書き込む必要がある場合にこの条件が真になります。
      • この条件が真の場合、lputb(addr>>32); が呼び出され、シンボルの64-bitアドレスの上位32ビットが先に書き込まれます。その後にlputb(addr); で下位32ビットが書き込まれることで、64-bitアドレス全体が正しく記録されます。lの値も8に設定され、アドレスが8バイトであることを示します。
    • シンボルサイズ計算の修正: symsize += l + 1 + i + 1; の変更は、シンボルテーブルのエントリサイズを計算する際に、アドレスのサイズが固定の4バイトではなく、実際のサイズ(l)を使用するように修正されたことを意味します。これにより、64-bitアドレスを持つシンボルがシンボルテーブル内で正しくサイズ計算され、シンボルテーブルの破損を防ぎます。

これらの変更は、64-bit Plan 9環境で生成されるGoバイナリのシンボルテーブルが、64-bitアドレスを正確に表現し、デバッグツールがその情報を正しく解釈できるようにするために不可欠です。

関連リンク

参考にした情報源リンク